# PomodoroCard

> A pomodoro with focus, short and long breaks. usePomodoro runs the cycle drift-free, PomodoroCard shows the ring, dots and linked task, and BreakLockScreen is the full-screen break.

Source: https://docs.nasaqui.com/components/pomodoro

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/pomodoro.json
```

Focus in sessions, rest in between. `usePomodoro` runs the classic cycle (25 minutes of focus, a 5 minute break, and a 15
minute break after every fourth session) and returns one controller. `PomodoroCard` shows it: cycle dots, the timer ring
with the phase inside, start, pause, skip and stop, the task the session is for, and today against a daily goal.
`BreakLockScreen` covers the page during a break with the countdown and a stretch or water suggestion.

## When to use

- A focus timer in a health, wellbeing or productivity product.
- Forcing a real rest: the break screen traps focus so it is hard to click past.

## When not to use

- Tracking billable hours: use [`TimeTracker`](https://docs.nasaqui.com/components/time-tracker).
- Locking the app for security: use [`LockScreen`](https://docs.nasaqui.com/components/lock-screen) and [`IdleLock`](https://docs.nasaqui.com/components/idle-lock).
- A plain countdown: use [`useCountdownTimer`](https://docs.nasaqui.com/components/countdown).

## Import

```tsx
import { BreakLockScreen, PomodoroCard, usePomodoro } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { BreakLockScreen, PomodoroCard, usePomodoro } from "@fadymondy/nasaq/web";

export function Focus() {
  const pomodoro = usePomodoro({ onEvent: (event) => api.saveSession(event) });
  return (
    <>
      <PomodoroCard pomodoro={pomodoro} task={{ id: "t1", title: "Write the report" }} />
      <BreakLockScreen
        open={pomodoro.onBreak}
        phase={pomodoro.phase === "longBreak" ? "longBreak" : "shortBreak"}
        seconds={pomodoro.seconds}
        fraction={pomodoro.fraction}
        cycle={pomodoro.cycle}
        cycles={pomodoro.cycles}
        onPostpone={(minutes) => pomodoro.postpone(minutes * 60_000)}
        onSkip={pomodoro.skip}
      />
    </>
  );
}
```

## Anatomy

```
PomodoroCard                    data-slot="pomodoro-card", data-phase, data-status
├─ CycleDots                    sessions in the set
├─ TimerRing + TimerReadout     phase icon, mm:ss and the phase word
├─ Start / Pause, Skip, Stop
├─ task                         data-slot="pomodoro-task" (a picker when `tasks` is given)
└─ today                        data-slot="pomodoro-today", Progress against the daily goal
BreakLockScreen                 data-slot="break-lock-screen", data-phase, data-confirming
├─ title and message
├─ TimerRing + CycleDots
├─ suggestion                   data-slot="break-suggestion"
├─ Postpone, Skip break
└─ confirm                      data-slot="break-lock-confirm"
```

## API

### `usePomodoro(options): PomodoroController`

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `config?` | `Partial<PomodoroConfig>` | 25 / 5 / 15 min, 4 sessions | `focusMs`, `shortBreakMs`, `longBreakMs`, `cyclesBeforeLongBreak`, `autoStartBreaks` (default true), `autoStartFocus` (default false). |
| `initialCompleted?` | `number` | `0` | Sessions already finished today. |
| `initialState?` | `PomodoroState` | fresh | Restore a saved state. |
| `speed?` | `number` | `1` | Faster than real time, for demos. Do not persist state made with it. |
| `onEvent?` | `(event: PomodoroEvent) => void` | none | A phase `completed`, `skipped`, `stopped` or `postponed`, with `startedAt`, `endedAt`, `plannedMs`, `spentMs`. Save sessions here. |
| `onStateChange?` | `(state: PomodoroState) => void` | none | Every change. Persist it and pass it back as `initialState` after a reload. |

The controller has `state`, `config`, `phase` (`"focus" \| "shortBreak" \| "longBreak"`), `status`, `cycle`, `cycles`, `completed`,
`remainingMs`, `seconds`, `fraction` (share left, 1 to 0), `onBreak`, and the actions `start()`, `pause()`, `resume()`,
`toggle()`, `skip()`, `stop()`, `postpone(ms)`.

Rules: a focus that ends counts as a session and starts the break at once. Skipping focus goes to a break without counting.
After the last session in a set the break is long, and the set restarts after it. Postponing a break gives extra focus time
that does not count as a new session; the break is owed again afterwards. `stop()` keeps today's count. Phases that start
by themselves begin at the previous phase's end, so a late tick shortens nothing.

### `PomodoroCard`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `pomodoro` | `PomodoroController` | required | From `usePomodoro`. |
| `dailyTarget?` | `number` | `8` | Goal in sessions. `0` hides the day counter. |
| `task?` | `PomodoroTask \| null` | none | `{ id, title, project? }` the session is for. |
| `tasks?` | `readonly PomodoroTask[]` | none | Turns the task line into a picker. |
| `onTaskChange?` | `(task: PomodoroTask \| null) => void` | none | Picker change. |
| `focusMinutesToday?` | `number` | sessions x focus length | Minutes focused today. |
| `title?` | `ReactNode` | "Pomodoro" | Heading. |
| `labels?` | `PomodoroLabels` | built-in en/ar | String overrides. |
| `className?` | `string` | none | Merged onto the card (max width 28rem by default). |

### `BreakLockScreen`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | required | Usually `pomodoro.onBreak`. |
| `phase` | `"shortBreak" \| "longBreak"` | required | Picks the title and colour. |
| `seconds`, `fraction` | `number` | required | Time left and the share left for the ring. |
| `cycle?`, `cycles?`, `completed?` | `number` | `0`, `4`, `cycle` | Dots and the message. |
| `suggestions?` | `readonly BreakSuggestion[]` | stretch, water, eyes, walk | `{ id, kind, title, description? }`. |
| `postponeMinutes?` | `number` | `5` | Minutes a postponement adds. |
| `onPostpone?` | `(minutes: number) => void` | none | Shows the Postpone button. |
| `onSkip` | `() => void` | required | Called after the person confirms. |
| `confirmSkip?` | `boolean` | `true` | Ask before skipping. |
| `nextTask?` | `string` | none | Shown as "Next up". |
| `paused?` | `boolean` | `false` | Dashes the ring. |
| `labels?` | `PomodoroLabels` | built-in | String overrides. |

## Examples

### Persist across reloads

```tsx
const saved = JSON.parse(localStorage.getItem("pomodoro") ?? "null") ?? undefined;
const pomodoro = usePomodoro({ initialState: saved, onStateChange: (s) => localStorage.setItem("pomodoro", JSON.stringify(s)) });
```

### Auto-start everything, four-minute long break

```tsx
usePomodoro({ config: { autoStartFocus: true, longBreakMs: 4 * 60_000 } });
```

### Arabic

```tsx
<NasaqProvider locale="ar">
  <PomodoroCard pomodoro={pomodoro} />
</NasaqProvider>
```

## Accessibility

| Key | Action |
| --- | --- |
| Tab / Shift+Tab | Move between the buttons; inside the break screen focus stays trapped. |
| Enter or Space | Press the focused button. |
| Escape | On the break screen: open "Skip this break?". Pressed again, it goes back to resting. It never skips by itself. |

- The break screen is a modal dialog: the page behind is inert, focus is trapped, outside clicks do nothing. Only `onSkip`
  after the confirmation, or your own `open={false}`, removes it.
- The card announces "Focus started" / "Break started" in a polite live region. The readout does not announce every second.
- The ring dashes when paused and each phase has its own icon and word: state is not conveyed by colour alone.
- `prefers-reduced-motion` stops the ring, fade and suggestion pulse.
- Localise `labels` for languages other than English and Arabic.

## RTL & i18n

- English and Arabic copy is built in. The timer is always left-to-right; Play and Skip icons mirror in RTL.
- Numbers use Western digits in both languages.

## Styling & tokens

Tokens only (`bg-primary`, `stroke-nq-success`, `bg-nq-success-soft`, `bg-background`). Target `data-phase` and `data-status` on
the card and `data-phase`, `data-confirming` on the break screen. The screen sits at `z-[100]`.

## Do / Don't

- Do save sessions in `onEvent`, not by watching the controller.
- Do keep the break screen for real breaks; let people postpone and skip with a confirm rather than trapping them.
- Don't use the break screen for security locks; it is not authentication.
- Don't persist state created with `speed` other than 1.

## Related

- [`Countdown`](https://docs.nasaqui.com/components/countdown)
- [`FocusStatus`](https://docs.nasaqui.com/components/focus-status)
- [`TimeTracker`](https://docs.nasaqui.com/components/time-tracker)
- [`LockScreen`](https://docs.nasaqui.com/components/lock-screen)

## Lab

`https://docs.nasaqui.com/?path=/docs/components-productivity-pomodoro-card--docs`

## Code

### React

```tsx
import { BreakLockScreen, PomodoroCard, usePomodoro } from "@fadymondy/nasaq/web";

export function Focus() {
  const pomodoro = usePomodoro({ onEvent: (event) => api.saveSession(event) });
  return (
    <>
      <PomodoroCard pomodoro={pomodoro} task={{ id: "t1", title: "Write the report" }} />
      <BreakLockScreen
        open={pomodoro.onBreak}
        phase={pomodoro.phase === "longBreak" ? "longBreak" : "shortBreak"}
        seconds={pomodoro.seconds}
        fraction={pomodoro.fraction}
        cycle={pomodoro.cycle}
        cycles={pomodoro.cycles}
        onPostpone={(minutes) => pomodoro.postpone(minutes * 60_000)}
        onSkip={pomodoro.skip}
      />
    </>
  );
}
```

### shadcn

```tsx
import { BreakLockScreen, PomodoroCard, usePomodoro } from "@/components/ui/pomodoro";

export function Focus() {
  const pomodoro = usePomodoro({ onEvent: (event) => api.saveSession(event) });
  return (
    <>
      <PomodoroCard pomodoro={pomodoro} task={{ id: "t1", title: "Write the report" }} />
      <BreakLockScreen
        open={pomodoro.onBreak}
        phase={pomodoro.phase === "longBreak" ? "longBreak" : "shortBreak"}
        seconds={pomodoro.seconds}
        fraction={pomodoro.fraction}
        cycle={pomodoro.cycle}
        cycles={pomodoro.cycles}
        onPostpone={(minutes) => pomodoro.postpone(minutes * 60_000)}
        onSkip={pomodoro.skip}
      />
    </>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import { NqBreakLockScreen, NqPomodoroCard, usePomodoro } from "@fadymondy/nasaq/vue";
import { ref } from "vue";

const tasks = [
  { id: "t1", title: "Write the release notes", project: "Nasaq" },
  { id: "t2", title: "Review the booking flow" },
];
const task = ref<(typeof tasks)[number] | null>(tasks[0]!);
// Short durations so the break screen can be seen quickly.
const pomodoro = usePomodoro({ config: { focusMs: 20_000, shortBreakMs: 10_000 }, initialCompleted: 2 });
</script>

<template>
  <NqPomodoroCard :pomodoro="pomodoro" :tasks="tasks" :task="task" @task-change="task = $event" />
  <NqBreakLockScreen
    :open="pomodoro.onBreak.value"
    :phase="pomodoro.phase.value === 'longBreak' ? 'longBreak' : 'shortBreak'"
    :seconds="pomodoro.seconds.value"
    :fraction="pomodoro.fraction.value"
    :cycle="pomodoro.cycle.value"
    :cycles="pomodoro.cycles.value"
    :completed="pomodoro.completed.value"
    :next-task="task?.title"
    postpone
    @skip="pomodoro.skip()"
    @postponed="pomodoro.postpone($event * 60_000)"
  />
</template>
```

### Blade

```blade
<x-nq::pomodoro :config="['focusMs' => 20000, 'shortBreakMs' => 10000]" :completed="2" :tasks="[['id' => 't1', 'title' => 'Write the release notes'], ['id' => 't2', 'title' => 'Review the booking flow']]" task-id="t1">
    <x-nq::pomodoro.break-lock-screen postpone />
</x-nq::pomodoro>
```

### HTML + Alpine

```html
<div data-slot="pomodoro-root" x-data="nqPomodoro(JSON.parse('{\u0022config\u0022:{\u0022focusMs\u0022:20000,\u0022shortBreakMs\u0022:10000},\u0022completed\u0022:2,\u0022dailyTarget\u0022:8,\u0022tasks\u0022:[{\u0022id\u0022:\u0022t1\u0022,\u0022title\u0022:\u0022Write the release notes\u0022},{\u0022id\u0022:\u0022t2\u0022,\u0022title\u0022:\u0022Review the booking flow\u0022}],\u0022taskId\u0022:\u0022t1\u0022,\u0022postponeMinutes\u0022:5,\u0022confirmSkip\u0022:true}'))" class="contents">
    <div data-slot="pomodoro-card" x-bind:data-phase="st.phase" x-bind:data-status="st.timer.status" class="flex flex-col gap-4 rounded-card border border-border bg-card py-4 text-card-foreground w-full max-w-md"><div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto]"><h2 data-slot="card-title" class="text-label text-foreground flex items-center gap-2"><svg aria-hidden="true" class="size-4 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <line x1="10" x2="14" y1="2" y2="2"/>
  <line x1="12" x2="15" y1="14" y2="11"/>
  <circle cx="12" cy="14" r="8"/>
</svg>                Pomodoro</h2></div>
        <div data-slot="card-content" class="px-4 flex flex-col items-center gap-5"><div data-slot="cycle-dots" role="img"  :aria-label="cyclesLabel()"  class="inline-flex items-center gap-2">
                                <span :data-state="dotState(0)" :class="dotClass(0)"></span>
                                        <span :data-state="dotState(1)" :class="dotClass(1)"></span>
                                        <span :data-state="dotState(2)" :class="dotClass(2)"></span>
                                        <span :data-state="dotState(3)" :class="dotClass(3)"></span>
            </div>
            <div data-slot="timer-ring" data-tone="primary"  x-bind="ringRoot(224)"     class="relative inline-flex shrink-0 items-center justify-center">
    <svg aria-hidden="true" viewBox="0 0 224 224" class="absolute inset-0 size-full -rotate-90">
        <circle cx="112" cy="112" r="106" fill="none" stroke-width="12" class="stroke-nq-line" />
        <circle data-slot="timer-ring-arc" cx="112" cy="112" r="106" fill="none" stroke-width="12" stroke-linecap="round"
             x-bind="arc(224, 12)"             class="stroke-primary transition-[stroke-dashoffset,stroke] duration-500 ease-linear motion-reduce:transition-none" />
    </svg>
    <div class="relative flex flex-col items-center justify-center gap-1 text-center"><span x-bind:class="toneText()" class="text-primary">
                    <svg x-show="is('focus')" aria-hidden="true" class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M12 18V5"/>
  <path d="M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4"/>
  <path d="M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5"/>
  <path d="M17.997 5.125a4 4 0 0 1 2.526 5.77"/>
  <path d="M18 18a4 4 0 0 0 2-7.464"/>
  <path d="M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517"/>
  <path d="M6 18a4 4 0 0 1-2-7.464"/>
  <path d="M6.003 5.125a4 4 0 0 0-2.526 5.77"/>
</svg>                    <svg x-show="is('shortBreak')" x-cloak="1" style="display: none" aria-hidden="true" class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M10 2v2"/>
  <path d="M14 2v2"/>
  <path d="M16 8a1 1 0 0 1 1 1v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1h14a4 4 0 1 1 0 8h-1"/>
  <path d="M6 2v2"/>
</svg>                    <svg x-show="is('longBreak')" x-cloak="1" style="display: none" aria-hidden="true" class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M19 9V6a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v3"/>
  <path d="M3 16a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-5a2 2 0 0 0-4 0v1.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V11a2 2 0 0 0-4 0z"/>
  <path d="M5 18v2"/>
  <path d="M19 18v2"/>
</svg>                </span>
                <time data-slot="timer-readout" role="timer" aria-live="off" dir="ltr" aria-label="Focus" x-bind:aria-label="phaseName()" datetime="PT0M20S" x-bind:datetime="iso()" x-text="clock()"
                    class="font-medium leading-none tabular-nums text-foreground text-[clamp(2rem,9vw,3rem)]">00:20</time>
                <span x-bind:class="toneText()" x-text="stateWord()" class="text-label text-primary">Focus · Ready</span></div>
</div>
            <div class="flex flex-wrap items-center justify-center gap-2">
                <button data-slot="button"
     type="button"                         x-show="idle() || paused()" x-on:click="toggle()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--nq-action)_88%,var(--nq-fg))] h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"/>
</svg>                    <span x-text="startLabel()">Start focus</span></button>
                <button data-slot="button"
     type="button"                         x-show="running()" x-cloak="x-cloak" style="display: none" x-on:click="toggle()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect x="14" y="3" width="5" height="18" rx="1"/>
  <rect x="5" y="3" width="5" height="18" rx="1"/>
</svg>                    Pause</button>
                <button data-slot="button"
     type="button"                         x-on:click="skip()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M21 4v16"/>
  <path d="M6.029 4.285A2 2 0 0 0 3 6v12a2 2 0 0 0 3.029 1.715l9.997-5.998a2 2 0 0 0 .003-3.432z"/>
</svg>                    Skip</button>
                <button data-slot="button"
     type="button"                         x-show="hasProgress()" x-cloak="x-cloak" style="display: none" x-on:click="stop()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect width="18" height="18" x="3" y="3" rx="2"/>
</svg>                    Stop</button>
            </div>
                            <div data-slot="pomodoro-task" class="flex w-full flex-col gap-1.5">
                    <span class="text-caption text-muted-foreground">Working on</span>
                    <div data-slot="native-select" class="relative w-full min-w-0">
    <select           class="w-full min-w-0 appearance-none rounded-control border border-input bg-card ps-3 pe-9 text-body text-foreground min-h-[var(--nq-touch-min,0px)] transition-colors duration-150 ease-nq outline-none focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px] h-control" aria-label="Link a task" x-on:change="pickTask($event.target.value)">
                    <option value=""  >No task linked</option>
                            <option value="t1"   selected >Write the release notes</option>
                    <option value="t2"  >Review the booking flow</option>
    </select>
    <svg aria-hidden="true" class="pointer-events-none absolute end-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m6 9 6 6 6-6"/>
</svg></div>
                </div>
                            <div data-slot="pomodoro-today" class="flex w-full flex-col gap-1.5">
                    <div class="flex items-baseline justify-between gap-3 text-body-sm">
                        <span class="text-label text-foreground">Today</span>
                        <span class="text-muted-foreground tabular-nums" x-text="sessionsText()">2 of 8 sessions</span>
                    </div>
                    <div data-slot="progress" data-tone="success" role="progressbar" aria-label="Today" aria-valuemin="0" aria-valuemax="100"
                        aria-valuenow="25" x-bind:aria-valuenow="progressPct()" class="h-1.5 w-full overflow-hidden rounded-full bg-nq-line">
                        <span class="block h-full rounded-full bg-nq-success transition-[width] duration-300 ease-nq motion-reduce:transition-none" style="width: 25%" x-bind:style="{ width: progressPct() + '%' }"></span>
                    </div>
                    <span class="text-caption text-muted-foreground tabular-nums" x-text="focusText()">1 min focused</span>
                </div>
                        <p role="status" class="sr-only" x-text="announce()"></p></div></div>
    <template x-teleport="body">
    <div data-slot="break-lock-screen" x-bind:data-phase="breakPhase()" x-bind:data-confirming="confirming ? '' : null" x-nq-presence="onBreak()" x-trap.noscroll="onBreak()"
        role="dialog" aria-modal="true" tabindex="-1" aria-labelledby="nq-break-title" aria-describedby="nq-break-description"
        x-on:keydown.escape.prevent.stop="escape()" x-cloak style="display: none"
        class="fixed inset-0 z-[100] flex flex-col items-center justify-center gap-8 overflow-y-auto bg-background p-6 text-center text-foreground outline-none transition-opacity duration-300 ease-nq motion-reduce:transition-none data-starting-style:opacity-0 data-ending-style:opacity-0">
        <h2 id="nq-break-title" class="text-h1 text-foreground" x-text="breakTitle()"></h2>
        <p id="nq-break-description" class="max-w-md text-body text-muted-foreground" x-text="breakBody()"></p>
        <div data-slot="break-lock-confirm" x-show="confirming" x-cloak style="display: none" class="flex flex-col gap-2 sm:flex-row">
            <button data-slot="button"
     type="button"                         data-keep-resting="data-keep-resting" x-on:click="keepResting()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--nq-action)_88%,var(--nq-fg))] h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M10 2v2"/>
  <path d="M14 2v2"/>
  <path d="M16 8a1 1 0 0 1 1 1v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1h14a4 4 0 1 1 0 8h-1"/>
  <path d="M6 2v2"/>
</svg>                Keep resting</button>
            <button data-slot="button"
     type="button"                         data-confirm-skip="data-confirm-skip" x-on:click="confirmedSkip()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M21 4v16"/>
  <path d="M6.029 4.285A2 2 0 0 0 3 6v12a2 2 0 0 0 3.029 1.715l9.997-5.998a2 2 0 0 0 .003-3.432z"/>
</svg>                Skip anyway</button>
        </div>
        <div x-show="!confirming" class="flex flex-col items-center gap-8">
            <div data-slot="timer-ring" x-bind:data-paused="paused() ? '' : null" class="relative inline-flex shrink-0 items-center justify-center" style="width: 260px; height: 260px; max-width: 100%">
                <svg aria-hidden="true" viewBox="0 0 260 260" class="absolute inset-0 size-full -rotate-90">
                    <circle cx="130" cy="130" r="123" fill="none" stroke-width="14" class="stroke-nq-line" />
                    <circle data-slot="timer-ring-arc" cx="130" cy="130" r="123" fill="none" stroke-width="14" stroke-linecap="round" x-bind="arc(260, 14, true)"
                        class="transition-[stroke-dashoffset,stroke] duration-500 ease-linear motion-reduce:transition-none" />
                </svg>
                <div class="relative flex flex-col items-center justify-center gap-1 text-center">
                    <time data-slot="timer-readout" role="timer" aria-live="off" dir="ltr" x-bind:aria-label="breakName()" x-bind:datetime="breakIso()" x-text="breakClock()"
                        class="font-medium leading-none tabular-nums text-foreground text-[clamp(3rem,14vw,5.5rem)]">00:00</time>
                    <span class="text-label" x-bind:class="breakToneText()" x-text="breakName()"></span>
                </div>
            </div>
            <div data-slot="cycle-dots" role="img" x-bind:aria-label="cyclesLabel()" class="inline-flex items-center gap-2">
                <template x-for="i in cfg.cyclesBeforeLongBreak" x-bind:key="i"><span x-bind:data-state="dotStateRest(i - 1)" x-bind:class="dotClassRest(i - 1)"></span></template>
            </div>
            <div data-slot="break-suggestion" role="group" aria-label="A suggestion for this break"
                class="flex w-full max-w-md items-center gap-3 rounded-card border border-border bg-card p-4 text-start">
                <span class="grid size-10 shrink-0 place-items-center rounded-full bg-nq-success-soft text-nq-success-text motion-safe:animate-pulse">
                    <svg x-show="ideaIcon('sparkles')" aria-hidden="true" class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/>
  <path d="M20 2v4"/>
  <path d="M22 4h-4"/>
  <circle cx="4" cy="20" r="2"/>
</svg>                    <svg x-show="ideaIcon('droplets')" x-cloak="1" style="display: none" aria-hidden="true" class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M7 16.3c2.2 0 4-1.83 4-4.05 0-1.16-.57-2.26-1.71-3.19S7.29 6.75 7 5.3c-.29 1.45-1.14 2.84-2.29 3.76S3 11.1 3 12.25c0 2.22 1.8 4.05 4 4.05z"/>
  <path d="M12.56 6.6A10.97 10.97 0 0 0 14 3.02c.5 2.5 2 4.9 4 6.5s3 3.5 3 5.5a6.98 6.98 0 0 1-11.91 4.97"/>
</svg>                    <svg x-show="ideaIcon('eye')" x-cloak="1" style="display: none" aria-hidden="true" class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/>
  <circle cx="12" cy="12" r="3"/>
</svg>                    <svg x-show="ideaIcon('footprints')" x-cloak="1" style="display: none" aria-hidden="true" class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0Z"/>
  <path d="M20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0Z"/>
  <path d="M16 17h4"/>
  <path d="M4 13h4"/>
</svg>                </span>
                <span class="flex min-w-0 flex-1 flex-col gap-0.5">
                    <span class="text-label text-foreground" x-text="ideaTitle()"></span>
                    <span class="text-body-sm text-muted-foreground" x-text="ideaBody()"></span>
                </span>
                <button data-slot="button"
     type="button"                         data-another-idea="data-another-idea" aria-label="Another idea" title="Another idea" x-on:click="anotherIdea()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
  <path d="M21 3v5h-5"/>
  <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
  <path d="M8 16H3v5"/>
</svg></button>
            </div>
            <p class="text-body-sm text-muted-foreground" x-show="taskTitle()" x-text="nextText()"></p>
            <div class="flex flex-col gap-2 sm:flex-row">
                                    <button data-slot="button"
     type="button"                         data-postpone="data-postpone" x-on:click="postpone()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <span x-text="postponeLabel()"></span></button>
                                <button data-slot="button"
     type="button"                         data-skip-break="data-skip-break" x-on:click="askSkip()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover h-[calc(var(--nq-control)+8px)] px-5 text-body">
        <svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M21 4v16"/>
  <path d="M6.029 4.285A2 2 0 0 0 3 6v12a2 2 0 0 0 3.029 1.715l9.997-5.998a2 2 0 0 0 .003-3.432z"/>
</svg>                    Skip break</button>
            </div>
        </div>
    </div>
</template>
</div>
```
