KQ-1256 · FOCUS/MOON
Focus Moon
A do-not-disturb toggle that eclipses its own sun: pressed, a moon slides across it on springs.snap, whose one overshoot flashes the last bead of sun on the far limb before the corona comes up on snap, the face turns to night and stars twinkle in on snap in a cascade; while focusing and on screen the corona breathes from one phase value. Holding the button extends the end time in grid steps, a tick per step as the changed digits roll on snap, and letting go sets it; pressed off, the moon slides away on an exit tween and the sunlight blooms back, settling on springs.drift. Space and Enter toggle it, the Up and Down arrows move the end a step, and times come only from the now prop.
Mara Okafor
Gaugeworks · review at 16:00
notifications on · press to focus for 60 min
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/focus-moon
Props
- pressed / defaultPressed
- boolean
- default false
- Controlled and initial state; true while notifications are held.
- onPressedChange
- (pressed: boolean) => void
- default —
- Fires from the tap, the hold or the key that changed it, with the new state.
- until / defaultUntil
- Date | number
- default now plus minutes once it starts
- Controlled and initial end of the focus.
- onUntilChange
- (until: Date) => void
- default —
- Fires when a tap, a released hold or an arrow key sets a new end time.
- now
- Date | number
- default —
- The current time. Pass it from your clock so server and client agree; without it a press reads the clock when it happens.
- minutes
- number
- default 60
- How long a tap focuses for, in minutes.
- step
- number
- default 30
- What one hold step or arrow key adds, in minutes; steps land on this grid (15:07 to 15:30).
- maxMinutes
- number
- default 480
- The furthest a hold can push the end, in minutes from now.
- holdDelay
- number
- default 450
- How long a press must be held before it starts extending, in ms.
- holdInterval
- number
- default 420
- Time between steps while held, in ms.
- stars
- number
- default 5
- How many stars come out around the eclipse, 0 to 8.
- glow
- number
- default 0.6
- Strength of the light, 0 to 1: the sun's bloom, the corona and its breathing.
- label
- string
- default "Do not disturb"
- The button's accessible name and its words while off.
- focusLabel
- string
- default "Focusing until"
- The words before the end time while on.
- format
- (date: Date) => string
- default 24-hour "15:00" in timeZone
- How the end time is written.
- timeZone
- string
- default "UTC"
- The IANA time zone the default format writes in.
- compact
- boolean
- default false
- Icon only; the name stays and the end time moves to the description and title.
- size
- "sm" | "md" | "lg"
- default "md"
- Pill height 32, 40 or 48px with a 24, 30 or 36px sky.
- sun
- string
- default oklch(from var(--warn) 0.84 0.13 h)
- The sun, the corona and the bloom, any CSS colour.
- night
- string
- default oklch(from var(--accent) 0.27 0.07 h)
- The night the face turns while focusing, any CSS colour.
- sound
- boolean
- default false
- A chime at totality (lower when it ends) and a rising tick per hold step or arrow key. Only after the visitor acts.
- disabled
- boolean
- default false
- Greys the toggle out and ignores presses, holds and keys.
- className
- string
- default —
- Extra classes for the root, which wraps the button.
| Prop | Type | Default | Description |
|---|---|---|---|
| pressed / defaultPressed | boolean | false | Controlled and initial state; true while notifications are held. |
| onPressedChange | (pressed: boolean) => void | — | Fires from the tap, the hold or the key that changed it, with the new state. |
| until / defaultUntil | Date | number | now plus minutes once it starts | Controlled and initial end of the focus. |
| onUntilChange | (until: Date) => void | — | Fires when a tap, a released hold or an arrow key sets a new end time. |
| now | Date | number | — | The current time. Pass it from your clock so server and client agree; without it a press reads the clock when it happens. |
| minutes | number | 60 | How long a tap focuses for, in minutes. |
| step | number | 30 | What one hold step or arrow key adds, in minutes; steps land on this grid (15:07 to 15:30). |
| maxMinutes | number | 480 | The furthest a hold can push the end, in minutes from now. |
| holdDelay | number | 450 | How long a press must be held before it starts extending, in ms. |
| holdInterval | number | 420 | Time between steps while held, in ms. |
| stars | number | 5 | How many stars come out around the eclipse, 0 to 8. |
| glow | number | 0.6 | Strength of the light, 0 to 1: the sun's bloom, the corona and its breathing. |
| label | string | "Do not disturb" | The button's accessible name and its words while off. |
| focusLabel | string | "Focusing until" | The words before the end time while on. |
| format | (date: Date) => string | 24-hour "15:00" in timeZone | How the end time is written. |
| timeZone | string | "UTC" | The IANA time zone the default format writes in. |
| compact | boolean | false | Icon only; the name stays and the end time moves to the description and title. |
| size | "sm" | "md" | "lg" | "md" | Pill height 32, 40 or 48px with a 24, 30 or 36px sky. |
| sun | string | oklch(from var(--warn) 0.84 0.13 h) | The sun, the corona and the bloom, any CSS colour. |
| night | string | oklch(from var(--accent) 0.27 0.07 h) | The night the face turns while focusing, any CSS colour. |
| sound | boolean | false | A chime at totality (lower when it ends) and a rising tick per hold step or arrow key. Only after the visitor acts. |
| disabled | boolean | false | Greys the toggle out and ignores presses, holds and keys. |
| className | string | — | Extra classes for the root, which wraps the button. |