Apply minimum touch target sizes of 44px (iOS) or 48px (Material Design)
Use touch-action property to prevent unwanted touch gestures
Control tap highlight color with -webkit-tap-highlight-color
Provide touch feedback using :active state styling
Separate touch and mouse interactions with @media (hover: hover)
Prevent text selection on UI elements with user-select
Understand gesture considerations for swipe, pinch, and long-press
Introduction to Touch Interactions
Touch interactions differ fundamentally from mouse interactions. Fingers are larger and less precise than cursor pointers, users can't see what's behind their finger while tapping, and there's no hover state. Designing for touch requires larger targets, immediate visual feedback, and consideration of natural gestures.
Mobile users expect smooth, responsive interfaces that feel natural to touch. Proper touch target sizing, feedback, and gesture support are essential for creating professional mobile experiences. CSS provides several properties specifically designed to optimize touch interactions.
Minimum Touch Target Sizes
The most critical rule for touch interfaces is adequate target size. Apple recommends minimum 44x44px targets, while Google's Material Design suggests 48x48px. Smaller targets lead to frustrating mistaps and poor user experience.
The touch-action property controls which touch gestures the browser handles. Use it to prevent double-tap zoom, disable panning, or allow only specific gestures.
/* Prevent double-tap zoom on buttons */
button {
touch-action: manipulation;
/* Allows single tap, disables double-tap zoom */
}
/* Disable all touch gestures */
.disabled {
touch-action: none;
/* User can't pan, zoom, or interact */
}
/* Allow only vertical scrolling */
.vertical-scroll {
touch-action: pan-y;
/* Horizontal panning and zooming disabled */
}
/* Allow only horizontal scrolling */
.horizontal-scroll {
touch-action: pan-x;
/* Vertical panning and zooming disabled */
}
/* Allow panning but not zooming */
.pan-only {
touch-action: pan-x pan-y;
/* Or use 'pan' shorthand */
}
/* Default browser behavior */
.default-touch {
touch-action: auto;
}
/* Common use cases */
/* Prevent zoom on interactive elements */
button,
a,
.clickable {
touch-action: manipulation;
}
/* Carousel that only scrolls horizontally */
.carousel {
touch-action: pan-x;
overflow-x: auto;
}
/* Modal overlay - no scrolling */
.modal-overlay {
touch-action: none;
}
Tap Highlight Color
Mobile browsers add a highlight color when elements are tapped. Control or remove this with -webkit-tap-highlight-color. Remove the default highlight when you provide your own touch feedback.
/* Remove tap highlight (most common) */
button,
a,
.clickable {
-webkit-tap-highlight-color: transparent;
}
/* Custom tap highlight color */
.button {
-webkit-tap-highlight-color: rgba(37, 99, 235, 0.2);
/* Light blue highlight */
}
/* Remove globally */
* {
-webkit-tap-highlight-color: transparent;
}
/* Then add custom :active states for feedback */
button:active {
background: #1e40af;
transform: scale(0.98);
}
/* Remove from links but keep focus outline */
a {
-webkit-tap-highlight-color: transparent;
}
a:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
Touch Feedback with :active State
Since touch devices don't have hover states, the :active state is critical for feedback. Provide immediate visual response when users tap elements.
Use the hover media query to apply hover effects only on devices with true hover capability. This prevents hover states from appearing on touch devices and getting "stuck."
/* Base button styles (all devices) */
button {
background: #3b82f6;
transition: background 0.2s;
}
/* Hover effects ONLY on hover-capable devices */
@media (hover: hover) {
button:hover {
background: #1d4ed8;
}
}
/* Touch feedback for ALL devices */
button:active {
background: #1e40af;
transform: scale(0.95);
}
/* Card example */
.card {
transition: transform 0.3s, box-shadow 0.3s;
}
/* Hover effect only on desktop */
@media (hover: hover) {
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
}
/* Touch devices get simpler feedback */
@media (hover: none) {
.card:active {
opacity: 0.9;
}
}
/* Link underlines */
a {
text-decoration: none;
}
/* Underline on hover (desktop) */
@media (hover: hover) {
a:hover {
text-decoration: underline;
}
}
/* Touch devices show underline on active */
@media (hover: none) {
a:active {
text-decoration: underline;
}
}
user-select: Preventing Text Selection
Touch interactions often accidentally select text. Use user-select to prevent selection on UI elements while keeping it enabled for content.
/* Prevent selection on buttons */
button,
.button {
user-select: none;
-webkit-user-select: none;
}
/* Navigation links shouldn't be selected */
nav a {
user-select: none;
-webkit-user-select: none;
}
/* Draggable elements */
.draggable {
user-select: none;
-webkit-user-select: none;
cursor: grab;
}
.draggable:active {
cursor: grabbing;
}
/* UI controls */
.slider,
.toggle,
.tabs {
user-select: none;
-webkit-user-select: none;
}
/* Keep selection enabled for content */
article p,
.content {
user-select: text; /* Default, but explicit */
}
/* Code blocks should select all on single tap */
code,
pre {
user-select: all;
-webkit-user-select: all;
}
/* Prevent callout menu on long press (iOS) */
img,
a img {
-webkit-touch-callout: none;
}
Gesture Considerations
Beyond basic taps, mobile users expect natural gestures like swipe, pinch, and long-press. Use CSS to enable or enhance these interactions.