Zum Inhalt springen
    WingoUI
    KomponentenVorlagenDokuPreiseKI-AgentenBlogChangelog
    1. Startseite
    2. Blog
    3. Mobile UI mit React
    4. Tailwind Touch Manipulation: touch-action für jedes Control
    Read in English
    1. Blog
    2. Mobile UI mit React
    3. Tailwind Touch Manipulation: touch-action für jedes Control
    Mobile UI mit React
    Mobile UI mit React

    Tailwind Touch Manipulation: touch-action für jedes Control

    SR
    Serban Rusu · Gründer von Wingo UI
    13. Okt. 2026 · 9 Min. Lesezeit

    Auf dieser Seite

    0%
    1. Was steuert touch-action eigentlich?
    2. Welche Utilities für Tailwind touch-action gibt es?
    3. Was behebt touch-manipulation auf Buttons?
    4. Wie gehst du mit der Tap Highlight Color in Tailwind um?
    5. Wann nutzt du touch-none in Tailwind?
    6. Wann nutzt du touch-pan-y oder touch-pan-x?
    7. Wie lassen sich Sheets gleichzeitig ziehen und scrollen?
    8. Welche Klasse braucht welches Control?
    9. Wie testest du touch-action?
    10. Wo fängst du am besten an?
    11. Häufige Fragen
      1. Was macht touch-manipulation in Tailwind?
      2. Gibt es auf dem Smartphone noch die 300ms-Verzögerung beim Tippen?
      3. Wann nutze ich touch-none in Tailwind?
      4. Wie entferne ich die Tap Highlight Color in Tailwind?
      5. Kann ich touch-pan-y und touch-pinch-zoom in Tailwind kombinieren?

    Kurz gesagt

    Setz touch-manipulation auf alles, was angetippt wird, also Buttons, Links, Tabs und Stepper: Scrollen und Pinch-Zoom bleiben erhalten, der Doppeltipp-Zoom fällt weg, also zoomen schnelle Taps nie die Seite. Nutze touch-none nur auf Flächen, auf denen du jede Richtung selbst behandelst, etwa bei einem Slider oder einem Signature Pad, und touch-pan-y auf allem, was seitlich gewischt wird, damit ein vertikaler Swipe weiter scrollt. Der Preflight von Tailwind CSS v4 macht das Tap-Highlight bereits transparent, deshalb braucht jedes drückbare Element einen eigenen Pressed-State.

    Veröffentlicht am 13. Okt. 2026

    Tipp auf dem iPhone dreimal schnell auf einen Plus-Button, und die Seite zoomt hinein, statt bis drei zu zählen. Zieh den Griff eines Sliders, und die Seite scrollt mit. Wisch ein Karussell zur Seite, und der ganze Screen rutscht nach oben. Hinter jedem dieser Bugs steckt eine fehlende CSS-Eigenschaft, touch-action, und die Utilities für Tailwind Touch Manipulation setzen sie pro Element: touch-manipulation, touch-none, touch-pan-y und sieben weitere. Dieses Tutorial zeigt, was jeder Wert macht, welches Control welchen braucht und welche Tap-Highlight- und Zoom-Bugs sie beheben. Dazu kommen die Klassen, die wir in Button, Slider und Drawer von Wingo UI ausliefern.

    Der Artikel gehört zu unserer Reihe Mobile UI in React. Der Leitfaden Mobile-first React-Komponenten stellt diese Regeln neben Touch Targets und Safe Areas.

    Was steuert touch-action eigentlich?

    Welche Gesten der Browser selbst übernimmt, wenn ein Finger auf einem Element landet. Standardmäßig gehören Panning (Scrollen) und Pinch-Zoom dem Browser. Sobald er ein Panning startet, hören deine Pointer Events auf: Du bekommst ein pointercancel und kein weiteres pointermove (MDN (opens in a new tab)). Deshalb funktioniert ein Drag auf Basis von Pointer Events mit der Maus und bricht auf dem Smartphone ab, sobald sich der Finger bewegt.

    Drei Regeln aus der Spezifikation erklären die meisten Überraschungen:

    • Der Wert wird nicht vererbt, erreicht die Kinder aber trotzdem. Beginnt eine Geste, bildet der Browser die Schnittmenge aus den touch-action-Werten des berührten Elements und seiner Vorfahren, bis zum nächsten Scroll-Container. Ein touch-manipulation auf einem Button gilt also auch für das Icon darin.
    • Er endet am Scroll-Container. Werte oberhalb des scrollenden Elements gelten nicht für eine Berührung darin. Ein Sheet mit touch-action: none kann trotzdem eine Liste enthalten, die nativ scrollt. Das nutzen wir weiter unten beim Drawer.
    • Er wird beim Start der Geste gelesen. Änderst du ihn mitten im Ziehen, wirkt sich das auf diese Geste nicht mehr aus.

    Welche Utilities für Tailwind touch-action gibt es?

    Zehn, jede für einen Wert (Tailwind-Doku (opens in a new tab)):

    Klassetouch-actionEinsatz
    touch-autoautoder Standard; setzt einen Wert an einem Breakpoint zurück
    touch-manipulationmanipulationButtons, Links, Tabs, Stepper, Toggles
    touch-nonenoneSlider, Signature Pads, Canvas, Karten
    touch-pan-ypan-yalles, was seitlich gewischt wird: Zeilen, Karten, Karussells
    touch-pan-xpan-xvertikal gezogene Elemente in einem horizontalen Scroller
    touch-pan-left, touch-pan-rightpan-left, pan-righthorizontale Gesten in nur eine Richtung
    touch-pan-up, touch-pan-downpan-up, pan-downvertikale Gesten in nur eine Richtung
    touch-pinch-zoompinch-zoomzusammen mit einem Pan-Wert, damit Zoom erhalten bleibt

    Die Pan- und Zoom-Klassen lassen sich kombinieren. In Tailwind CSS 4.3.3 (der Version, die wir im Oktober 2026 geprüft haben) setzt jede eine Variable wie --tw-pan-y, und eine einzige Deklaration liest sie alle: touch-action: var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,). Aus touch-pan-y touch-pinch-zoom wird also pan-y pinch-zoom, eine Fläche zum seitlichen Wischen, auf der man trotzdem zoomen kann. Varianten funktionieren wie gewohnt, zum Beispiel touch-pan-y md:touch-auto.

    Was behebt touch-manipulation auf Buttons?

    Den Doppeltipp-Zoom. manipulation erlaubt Panning und Pinch-Zoom und schaltet den Rest ab, vor allem den Zoom per Doppeltipp. MDN beschreibt den Wert als Alias für pan-x pan-y pinch-zoom und merkt an, dass der Browser Klicks ohne Doppeltipp-Zoom nicht mehr verzögern muss.

    Die 300ms-Verzögerung durch den Doppeltipp-Zoom ist größtenteils Geschichte. Chrome hat sie in Version 32 für Seiten mit width=device-width abgeschafft, Safari folgte mit iOS 9.3 (Beitrag von Chrome (opens in a new tab)). Der Beitrag von WebKit zu schnellem Tippen (opens in a new tab) (Dezember 2015) ergänzt das Detail, das bis heute zählt: Der schnelle Weg gilt nur, solange die Seite auf ihrer anfänglichen Zoomstufe steht. Hat jemand hineingezoomt, wartet Safari wieder auf einen möglichen zweiten Tap. Mit touch-action: manipulation reagieren Taps auf jeder Zoomstufe sofort.

    Der Bug, den du heute noch siehst, ist der Zoom selbst. Der Plus-Button eines Steppers, eine Taste im Taschenrechner oder ein zweimal hintereinander angetipptes „Mehr laden“ kann als Doppeltipp zählen und die Seite zoomen. Willst du den Zoom beim Doppeltippen verhindern, setz die Klasse auf das Control:

    tsx
    "use client";
    import { useState } from "react";
    import { Plus } from "lucide-react";
    export function GuestCount() {
    const [count, setCount] = useState(1);
    return (
    <div className="flex items-center gap-3">
    <span aria-live="polite">{count} Gäste</span>
    <button
    type="button"
    aria-label="Gast hinzufügen"
    onClick={() => setCount((n) => n + 1)}
    className="grid size-11 touch-manipulation select-none place-items-center rounded-full bg-foreground/5 active:bg-foreground/10"
    >
    <Plus aria-hidden className="size-5" />
    </button>
    </div>
    );
    }

    Setz sie auf Controls, nicht auf html oder body. Mit dem Doppeltipp-Zoom auf Text lesen manche Menschen Kleingedrucktes. Schaltest du ihn für die ganze Seite ab, nimmst du ihnen das, nur um ein Problem zu lösen, das allein deine Controls haben.

    Der kostenlose Button von Wingo UI trägt touch-manipulation, select-none und das Zurücksetzen des Tap-Highlights in seinen eigenen Basisklassen. Ein kopierter Button behält sie also in einem Projekt mit beliebigem globalem Stylesheet:

    tsx
    import { Plus } from "lucide-react";
    import { Button } from "@/components/ui/button";
    export function AddGuest({ onAdd }: { onAdd: () => void }) {
    return (
    <Button variant="soft" size="icon" radius="full" aria-label="Gast hinzufügen" onClick={onAdd}>
    <Plus />
    </Button>
    );
    }
    Tipp auf dem Smartphone auf den Button: Er gibt federnd nach und zeigt seinen Ladezustand, ohne seine Breite zu ändern, und schnelle wiederholte Taps zoomen nie die Seite
    $ npx wingo-ui@latest add button
    FreeDoku: Button

    Wie gehst du mit der Tap Highlight Color in Tailwind um?

    Meist, indem du einen Pressed-State ergänzt, denn Tailwind hat das Highlight schon entfernt. -webkit-tap-highlight-color legt das halbtransparente Rechteck fest, das Safari und Chromium über einen angetippten Link oder Button zeichnen. Die Eigenschaft ist nicht standardisiert, wird vererbt und von WebKit und Blink unterstützt, nicht von Firefox (MDN (opens in a new tab)).

    Der Preflight von Tailwind CSS v4 setzt -webkit-tap-highlight-color: transparent auf html (in 4.3.3 erklärt der Kommentar daneben, dass das die Tap-Highlights auf iOS abschaltet). Weil die Eigenschaft vererbt wird, startet jedes Element in einer Tailwind-v4-App ohne Highlight. Das hat einen Preis, den viele übersehen: Ein einfaches <a> oder <button> ohne active:-Style zeigt bei einem Tap überhaupt nichts an.

    In einer Tailwind-v4-App laufen die Regeln also andersherum:

    • Jedes drückbare Element bekommt einen Pressed-State. active:bg-foreground/10, active:scale-[0.97] oder ein Spring-Press wie beim Button.
    • Komponenten, die du teilst, setzen den Wert selbst. Code, der in ein Projekt ohne Preflight kopiert wird oder in eines, das die Eigenschaft zurücksetzt, blitzt wieder grau auf. Nutze in der Komponente die Arbitrary Property [-webkit-tap-highlight-color:transparent] oder definiere einmal eine Utility:
    css
    /* app/globals.css */
    @utility tap-highlight-none {
    -webkit-tap-highlight-color: transparent;
    }
    • Entferne das Highlight nie ohne Ersatz. Hat ein Control keinen Pressed-Style, ist ein sichtbares Tap-Highlight besser als gar nichts.

    Wann nutzt du touch-none in Tailwind?

    Wenn dein Code auf dem Element jede Bewegungsrichtung behandelt und es dich nicht stört, dass ein Swipe, der dort beginnt, nie die Seite scrollt. Das passt zu einem Slider, einem Signature Pad, einem Zeichen-Canvas, einer Karte oder einem Crop-Handle. Für alles Große ist es die falsche Wahl: Ein bildschirmfüllendes Element mit touch-none hält Leute fest, die daran vorbeiscrollen wollen. MDN warnt außerdem, dass none auch den Pinch-Zoom blockiert, auf den Menschen mit Sehschwäche angewiesen sind.

    Der Slider von Wingo UI zeigt, wie du das Element klein hältst. Nur das Root-Element hat touch-none, eine schmale Zeile, die bei Coarse Pointern (Touch) per Pseudo-Element eine Trefferfläche von 44px bekommt. Ein Ziehen darauf scrollt nie die Seite, während der Rest des Formulars normal scrollt:

    tsx
    "use client";
    import { useState } from "react";
    import { Slider } from "@/components/ui/slider";
    export function PriceFilter() {
    const [price, setPrice] = useState([500, 2000]);
    return (
    <Slider
    label="Preis"
    value={price}
    onValueChange={setPrice}
    min={0}
    max={5000}
    step={50}
    locale="en-US"
    format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
    showValueLabel
    />
    );
    }
    Zieh auf dem Smartphone einen Griff: Die Seite bleibt stehen, während die Wertanzeige deinem Finger folgt. Wisch dann neben dem Slider nach oben, und die Seite scrollt
    $ npx wingo-ui@latest add slider
    ProDoku: Slider

    Die Griffe (Thumbs) setzen zusätzlich touch-manipulation, damit auch ein schneller Doppeltipp auf einen Griff nie zoomt.

    Wann nutzt du touch-pan-y oder touch-pan-x?

    Wenn sich dein Element auf einer Achse bewegt und die Seite auf der anderen scrollt. Eine Zeile, die du zum Löschen nach links wischst, eine Karte, die du wegwischst, oder ein Slide im Karussell bekommen alle touch-pan-y: Der Browser behält das vertikale Scrollen, und eine horizontale Bewegung geht an deine Pointer-Handler.

    tsx
    "use client";
    import { useRef, type PointerEvent, type ReactNode } from "react";
    export function SwipeRow({ children, onDelete }: { children: ReactNode; onDelete: () => void }) {
    const startX = useRef<number | null>(null);
    const offset = useRef(0);
    function down(event: PointerEvent<HTMLLIElement>) {
    startX.current = event.clientX;
    event.currentTarget.setPointerCapture(event.pointerId);
    }
    function move(event: PointerEvent<HTMLLIElement>) {
    if (startX.current === null) return;
    offset.current = Math.min(0, event.clientX - startX.current);
    event.currentTarget.style.transform = `translateX(${offset.current}px)`;
    }
    function end(event: PointerEvent<HTMLLIElement>) {
    if (offset.current < -120) onDelete();
    startX.current = null;
    offset.current = 0;
    event.currentTarget.style.transform = "";
    }
    return (
    <li
    onPointerDown={down}
    onPointerMove={move}
    onPointerUp={end}
    onPointerCancel={end}
    className="touch-pan-y select-none"
    >
    {children}
    </li>
    );
    }

    Mit eigenen Pointer-Handlern wie diesen ist die Klasse Pflicht. Ohne sie behandelt der Browser die Berührung als mögliches Scrollen, und eine Bewegung mit auch nur leichter vertikaler Abweichung endet in pointercancel. setPointerCapture ist die andere Hälfte: Die Zeile bekommt weiter Bewegungen, auch wenn der Finger von ihr abrutscht. Die Handler schreiben das transform direkt ins Element, statt bei jeder Bewegung setState aufzurufen. Motion erledigt das für dich. Sein drag="x" schreibt touch-action: pan-y als Inline-Style (pan-x bei drag="y", none bei drag), und ein Inline-Style gewinnt gegen jede Klasse, die du ergänzt. Unsere Tutorials React Swipe to Delete und React Swipe Cards bauen beide Muster.

    touch-pan-x ist der gespiegelte und seltenere Fall: ein vertikales Ziehen, etwa an einem Handle zum Umsortieren, in einer Zeile, die seitlich scrollt. Für sortierbare Listen vergleicht das Tutorial zu dnd kit auf dem Smartphone die Kombinationen aus touch-action und Sensoren, mit denen ein Long Press zieht und ein Swipe trotzdem scrollt.

    Wie lassen sich Sheets gleichzeitig ziehen und scrollen?

    Indem du dich auf die Regel verlässt, dass touch-action am Scroll-Container endet. vaul, die Bibliothek hinter dem kostenlosen Drawer, setzt touch-action: none auf das ganze Sheet. Ein Ziehen irgendwo auf Header oder Footer bewegt also das Sheet. Der Body des Drawers ist ein eigener Scroll-Container (overflow-y-auto overscroll-contain). Für eine Berührung im Body zählen deshalb nur die Werte bis zum Body selbst, und die Liste darin scrollt nativ. vaul bewegt das Sheet erst, wenn die Liste wieder ganz oben ist.

    Tipp auf „Filters“, scroll die Liste im Sheet und zieh dann vom Header nach unten: Die Liste scrollt nativ, und der Header zieht das Sheet
    $ npx wingo-ui@latest add drawer
    FreeDoku: Drawer

    overscroll-contain übernimmt die andere Hälfte: Erreicht die Liste ihr Ende, setzt sich das Scrollen nicht auf die Seite hinter dem Sheet fort. touch-action steuert dieses Scroll Chaining gar nicht, das macht overscroll-behavior. Für ein Sheet, dessen Inhalt eigene horizontale Gesten hat, etwa ein Karussell oder eine Karte, gibst du handleOnly mit, damit es sich nur am Handle ziehen lässt. Der Guide zum React Bottom Sheet (auf Englisch) behandelt Snap Points und die Tastatur.

    Welche Klasse braucht welches Control?

    ControlKlassen
    Button, Link, Tab, Toggle, Menüeintragtouch-manipulation select-none und ein active:-Style
    Stepper oder jeder Button, der wiederholt angetippt wirdtouch-manipulation, hier Pflicht
    Slider, Signature Pad, Canvas, Crop-Handletouch-none auf dem kleinsten Element, das funktioniert
    Zeile, Karte oder Slide, die seitlich gewischt wirdtouch-pan-y (oder drag="x" von Motion)
    Vertikales Ziehen in einem horizontalen Scrollertouch-pan-x
    Scroller in einem Sheet oder Dialogtouch-action unverändert lassen, overscroll-contain ergänzen
    html oder bodynichts; hier nie touch-none oder touch-manipulation

    Wie testest du touch-action?

    Lies zuerst den berechneten Wert: Im Web Inspector von Safari oder in den Chrome DevTools zeigt der Tab Computed den touch-action-Wert des Elements. Der Wert der Eltern zählt ebenfalls, bis zum nächsten Scroller. Der Device Mode von Chrome emuliert Touch. So prüfst du, dass ein Ziehen auf einer Fläche mit touch-none nicht scrollt. Doppeltipp-Zoom und Tap-Highlights brauchen ein echtes Smartphone: Tipp deinen Stepper auf einem iPhone und einem Android-Smartphone schnell hintereinander an, zoom in die Seite hinein und tipp noch einmal.

    In Playwright kannst du in einem Context mit hasTouch: true page.touchscreen.tap() aufrufen. Das reicht, um zu prüfen, dass Klicks feuern. Die Gestenerkennung des Browsers bildet es aber nicht nach, also teste das Zoomverhalten weiter auf einem echten Gerät.

    Wo fängst du am besten an?

    Durchsuch deine Codebase nach onClick auf Elementen ohne touch-manipulation und nach Drag-Handlern ohne touch-*-Klasse. Diese beiden Listen sind meist schon der ganze Fix. Prüf dann, ob jedes drückbare Element einen Pressed-State hat, denn Tailwind v4 hat das Tap-Highlight bereits ausgeblendet. Wenn du lieber mit Komponenten startest, die diese Klassen schon tragen: Button und Drawer sind kostenlos, npx wingo-ui@latest add button drawer. Der Slider gehört zu Wingo UI Pro.

    Komponenten in diesem Artikel

    • Button

      The React button component every screen starts with: five variants, six tones, all sizes, and a loading state that never jumps.

      Free
    • Slider

      A React slider for a number or a range: any number of thumbs, labelled marks, a value bubble, vertical mode, tones and finger-sized thumbs.

      Pro
    • Drawer

      A React drawer and bottom sheet built on vaul: drag to dismiss, snap points, nesting, and a side panel or dialog on desktop.

      Free

    Häufige Fragen

    Was macht touch-manipulation in Tailwind?

    Die Klasse setzt touch-action: manipulation. Der Browser darf scrollen und per Pinch zoomen, andere Gesten wie der Doppeltipp-Zoom sind aus. Ohne Doppeltipp-Zoom muss der Browser nicht auf einen zweiten Tap warten, Taps auf dem Element feuern also sofort.

    Gibt es auf dem Smartphone noch die 300ms-Verzögerung beim Tippen?

    Nicht auf Seiten mit width=device-width auf der anfänglichen Zoomstufe: Chrome hat sie in Version 32 entfernt, Safari mit iOS 9.3. WebKit hat dokumentiert, dass der schnelle Tap nur auf der anfänglichen Zoomstufe greift, und schnelle Taps können weiterhin den Doppeltipp-Zoom auslösen. Genau den schaltet touch-manipulation pro Element ab.

    Wann nutze ich touch-none in Tailwind?

    Nur auf Elementen, auf denen dein Code jede Bewegungsrichtung behandelt, etwa ein Slider, ein Signature Pad, ein Canvas oder eine Karte. Ein Swipe, der dort beginnt, scrollt oder zoomt die Seite nie. Halte solche Elemente also klein oder lass der Seite einen anderen Bereich zum Scrollen.

    Wie entferne ich die Tap Highlight Color in Tailwind?

    In Tailwind CSS v4 musst du das meist nicht: Der Preflight setzt -webkit-tap-highlight-color: transparent auf html, und die Eigenschaft wird vererbt. Ohne Preflight nimmst du die Arbitrary Property [-webkit-tap-highlight-color:transparent] und ergänzt immer einen active- oder Pressed-Style, damit ein Tap sichtbar bleibt.

    Kann ich touch-pan-y und touch-pinch-zoom in Tailwind kombinieren?

    Ja. In Tailwind CSS v4 schreiben die Pan- und Pinch-Utilities eigene CSS-Variablen, die eine einzige touch-action-Deklaration liest. touch-pan-y touch-pinch-zoom wird also zu touch-action: pan-y pinch-zoom.

    • Tailwind CSS
    • Touch
    • Mobile
    • CSS
    • Barrierefreiheit

    Teilen

    SR

    Über den Autor

    Serban Rusu

    Gründer von Wingo UI

    Serban Rusu hat Wingo UI gegründet. Er baut die Komponenten, die CLI und den MCP-Server und schreibt über React-UIs für Smartphones und KI-Agents.

    Mehr von Serban
    Weiterlesen

    Ähnliche Artikel

    Alle Artikel
    Vergleiche und Alternativen

    Chakra UI vs shadcn 2026: Style Props oder Tailwind?

    Chakra UI vs shadcn im Oktober 2026: Style Props auf Emotion oder Tailwind-Code, der dir gehört. Theming, Turbopack, RSC und Smartphones, mit Codebeispielen.

    SRSerban Rusu·16. Okt. 2026·10 Min. Lesezeit
    Vergleiche und Alternativen

    Wingo UI vs shadcn/ui: gleiches Modell, anderer Umfang

    Wingo UI vs shadcn/ui, Stand Oktober 2026: dasselbe Copy-the-Source-Modell, verglichen nach Lizenz, Preis, Mobile-Verhalten, Updates und KI-Agenten.

    SRSerban Rusu·13. Okt. 2026·9 Min. Lesezeit
    Vergleiche und Alternativen

    14 shadcn Alternativen 2026: Preis, Mobile und MCP

    14 shadcn Alternativen im Oktober 2026 geprüft: freie und kostenpflichtige UI-Bibliotheken nach Lizenz, Preis, Mobile und MCP, mit einer Wahl pro Einsatz.

    SRSerban Rusu·9. Okt. 2026·20 Min. Lesezeit
    Newsletter

    Neue Artikel per E-Mail

    Neue Guides, Tutorials und Vergleiche aus dem Wingo UI Blog, sobald sie erscheinen. Die meisten Artikel sind auf Englisch.

    Kein Spam. Du kannst dich jederzeit abmelden.

    WingoUI

    Animierte, konfigurierbare, mobile-first React-Komponenten und Blöcke. Kopier den Quellcode, pass ihn an und lass deinen Coding-Agenten damit bauen.

    Produkt

    • Komponenten
    • Vorlagen
    • Preise
    • KI-Agenten
    • Theme-Generator

    Ressourcen

    • Doku
    • Blog
    • Changelog
    • Changelog-RSS
    • llms.txt

    Rechtliches

    • AGB
    • Datenschutz
    • Rückerstattung
    • Lizenz
    • Impressum

    Komponenten-Kategorien

    • Buttons & Actions
    • Inputs
    • Forms
    • Navigation
    • Overlays
    • Feedback
    • Data Display
    • Tables & Lists
    • Charts & Stats
    • Layout
    • Media
    • AI Kit
    • Text & Effects
    • Mobile
    • Commerce
    • Marketing Sections
    • Blocks
    • Hooks & Utilities

    © 2026 Wingo UI

    Wingo UI