Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Responsive Touch Target Switch Form Block for Radix UI

Three display settings as whole-row switches 56 pixels tall on a phone and 48 on desktop, with a Show touch targets switch that outlines each hit area and a Narrow or Wide control that drops the switch under the text.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Make every row one big target with this responsive switch form for React with Radix UI. A compact header sits over a hairline frame with a plus mark on each corner. In the frame, three display settings of a calendar app (Dark appearance, Compact view and Reduce animations) are each one row whose whole area is the hit area of its Switch: press the text, the word "On" or the switch itself. A band above the rows holds a "Show touch targets" switch; turn it on and each hit area gets a dashed outline while the band prints its typed size ("48 x 288 px per row"; "56 px tall, the full width" on a phone). A Narrow or Wide control in the header sets the width of the frame, and below a 20 rem container the switch moves under the text so nothing crowds, because the rows answer to their own container, not to the screen. Rows keep their height whether a switch is on or off, and the foot counts unsaved changes beside a Shine Button to save them and an outline Button to discard them. It's built from Switch, Segment Group, Button, Shine Button and Guides, with a CSS entrance only. For preferences screens, accessibility settings, kiosk and tablet apps and any list of toggles that is used with a thumb.

FAQ

How does the whole row become the hit area?

Each row is relative and its switch gets static! after:inset-0!, so the switch's invisible hit area stretches to the row instead of to the switch. Focus is drawn around the row with has-focus-visible. The row has a minimum height of 56 px on a phone and 48 px from the small breakpoint, which keeps it above the common 44 to 48 px touch-target guidance.

Why does the switch move under the text?

The frame is an @container and each row uses @min-[20rem]: classes: at 20 rem or wider the text and switch sit side by side, narrower they stack. That makes the rows respond to the space they have, so the same component works in a narrow sidebar or a wide page. Narrow is 288 px, which is under the line; Wide is 480 px, which is over it.

Are the sizes measured?

No, they are typed numbers (WIDTHS and the 48 and 56 px in the readout), so the line is the same on every machine and in tests. If you want the real size, read it with a ResizeObserver on a row and print that instead.

Where did Narrow and Wide go on a phone?

The control is hidden below the small breakpoint: the frame is already the width of the screen, so Narrow is what a phone shows. Rows stack with the switch under the text there, and the readout says "56 px tall, the full width".

What stays local, and how do I connect it for real?

Nothing is sent: Save says "Saved 2 changes in this demo." The touch-target switch and the width are only a view and are not part of what is saved. To make the settings real, send draft from the submit handler, set saved from the answer and show a failure in the status line without losing the draft.

How do I connect it to react-hook-form or TanStack Form?

With react-hook-form, register each setting as a boolean and pass field.value and field.onChange to the switch through Controller; the touch-target switch and the width stay in plain state because they are not form values.