Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Breathing Exercise Dialog Block for Solid

A calm app's home screen with its guided-breath dialog shown open over a drawn Today card: three patterns, three round counts, a circle that grows and shrinks inside a filling ring, Pause and Resume, and a finished session that lands on the card.

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

Give people a minute of slow breathing without a video or a soundtrack, with this breathing exercise dialog for Solid with Ark UI. It opens from the home screen of Slowtide, a calm app: the header holds an outline Button, and the screen it opens over is drawn under a soft scrim inside a hairline frame with a plus mark on each corner, with soft arcs and one Today card. The Dialog picks a pattern with a Toggle Group (Box 4-4-4-4, Calming 4-7-8, Coherent 5-5), the rounds with a Select (3, 5 or 8) and writes the pattern as words, such as In 4, hold 7, out 8. Start, the only Shine Button, runs a bounded session: a circle grows as you breathe in, stays for a hold and shrinks as you breathe out, inside a Circular Progress ring that fills for each phase, beside three stacked labels, Breathe in, Hold and Breathe out, with the seconds counted down. Pause stops it where it is and Resume carries on. When the last round ends the dialog says Session complete and the Today card behind it shows one more session. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same state. For meditation, wellbeing, focus and sleep apps.

FAQ

Why is the dialog already open, and what do I keep?

So you can see it without a click. On load the dialog is a copy drawn in place over the home screen: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Start a session button in the header opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same BreatheFields component and share the block's state, so what you set in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

How does the session run, and does it loop?

It runs for exactly the rounds you chose and then stops. While a session is running, one 100 ms interval adds to an elapsed counter; momentAt turns the elapsed time into the phase, how far through it you are, the seconds left and the circle's size, using a smooth ease. The circle follows with a 100 ms CSS transition, so it moves smoothly. The interval is removed when you pause, finish or close, so nothing runs in the background. The pattern is plain data in PATTERNS: change the seconds, or add one.

What happens with reduced motion?

The circle does not scale. The ring still fills for each phase, the three labels still step, the seconds are still counted down, and the pattern is written out as words, so the session works without watching anything grow. The circle, ring and labels are decoration and hidden from assistive technology; the start, the pause and the end are announced in a polite status message, and the pattern is in text.

Why can I not change the pattern while it runs?

A session is for one pattern and one count. The pattern and rounds controls are disabled from Start until the session ends, so the circle never changes its mind mid-breath. Closing the dialog, with Escape, Close or the close button, stops the session and puts it back to ready; the Today card keeps its count. After Session complete, Start again begins a new one.

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

Everything. A finished session adds one to the Today card on the drawn screen, and nothing is tracked or saved. A session you stop does not count. In your app record a completed session from the effect that ends it, with the pattern and rounds, and decide whether partial sessions count. Add sound or haptics there too, with the same on and off choice you give elsewhere.

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus is trapped while it is open, and Escape and the close button return focus to the header button. The trigger is the button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus; Escape inside it and its close button close it and put focus back on the header button. Start, Pause and Resume are one button position, so focus stays where you pressed it.

How does it look on a phone?

The copy shown on load docks to the foot of the frame as a bottom sheet with Close and Start pinned below a scrolling body. The three labels move under the circle. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.