# IdleLock

> Locks the app after a period of inactivity: a Still there countdown dialog first, then your LockScreen over the inert app.

Source: https://docs.nasaqui.com/components/idle-lock

## Install

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

Wraps the app so it locks itself after inactivity. Ten seconds of warning is not enough for a person who stepped away,
so the "Still there?" dialog counts down first and only its buttons answer it. Then your `LockScreen` covers the app,
which stays mounted but `inert` and hidden from assistive tech. The timer is a courtesy: expire the session on the server too.

## When to use

- An app with sensitive data on shared or unattended screens.
- A "Lock" menu item (controlled `locked`).

## When not to use

- A session that already ended: use [`session-expired`](https://docs.nasaqui.com/components/session-expired).
- Pages that must not lock, such as a live call: set `disabled`.

## Import

```tsx
import { IdleLock, LockScreen } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { IdleLock, LockScreen } from "@fadymondy/nasaq/web";

export function Shell({ children, user }: { children: React.ReactNode; user: { name: string } }) {
  return (
    <IdleLock
      timeoutSeconds={600}
      warningSeconds={30}
      lockScreen={({ unlock }) => <LockScreen user={user} methods={["password"]} onUnlock={async (attempt) => (await verify(attempt)) ? unlock() : { error: "Wrong password." }} />}
    >
      {children}
    </IdleLock>
  );
}

declare function verify(attempt: unknown): Promise<boolean>;
```

## Anatomy

```
IdleLock                     data-slot="idle-lock"
├─ app                       data-slot="idle-lock-app"  (inert while locked)
├─ IdleWarningDialog         data-slot="idle-warning"   role="timer"
└─ lock overlay              data-slot="idle-lock-screen"
```

## API

**IdleLock**: `div` props plus `children`, `lockScreen` (a node, or `({ unlock }) => node`), `timeoutSeconds` (300),
`warningSeconds` (30, `0` for none), `locked` / `defaultLocked` / `onLockedChange(locked, reason)`, `disabled`, `unmountWhenLocked`, `labels`.

**useIdleLock**: `{ timeoutSeconds, warningSeconds, disabled, onIdle }` returns `{ phase, secondsLeft, stayActive, reset }`.
Build your own UI on it. It re-checks when the tab becomes visible, so a sleeping laptop locks on wake.

**IdleWarningDialog**: `open`, `secondsLeft`, `warningSeconds`, `onStay`, `onLockNow`, `labels`.

## Examples

**A manual lock button**

```tsx
import { Button, IdleLock } from "@fadymondy/nasaq/web";
import { useState } from "react";

export function App() {
  const [locked, setLocked] = useState(false);
  return (
    <IdleLock locked={locked} onLockedChange={setLocked} lockScreen={<p>Locked</p>}>
      <Button onClick={() => setLocked(true)}>Lock</Button>
    </IdleLock>
  );
}
```

## Accessibility

- The countdown is a `role="timer"`, announced every ten seconds and in the last five, not every tick.
- The warning is an alert dialog with focus on "Stay signed in". Input during the warning does not cancel it.
- The locked app is `inert` and `aria-hidden`, so focus and screen readers cannot reach it.

## RTL & i18n

- English and Arabic built in; pass `labels`. The countdown is always left to right with Latin digits.

## Styling & tokens

- Tokens only. The overlay is `z-[60]`, above dialogs.

## Do / Don't

- Do end the session on the server as well.
- Do use `unmountWhenLocked` when the page content is confidential.
- Don't set a warning longer than the timeout (it is clamped).

## Related

- [`lock-screen`](https://docs.nasaqui.com/components/lock-screen)
- [`session-expired`](https://docs.nasaqui.com/components/session-expired)

## Lab

https://docs.nasaqui.com/?path=/docs/components-auth-pages-idle-lock--docs

## Code

### React

```tsx
import { IdleLock, LockScreen } from "@fadymondy/nasaq/web";

export function Shell({ children, user }: { children: React.ReactNode; user: { name: string } }) {
  return (
    <IdleLock
      timeoutSeconds={600}
      warningSeconds={30}
      lockScreen={({ unlock }) => <LockScreen user={user} methods={["password"]} onUnlock={async (attempt) => (await verify(attempt)) ? unlock() : { error: "Wrong password." }} />}
    >
      {children}
    </IdleLock>
  );
}

declare function verify(attempt: unknown): Promise<boolean>;
```

### shadcn

```tsx
import { IdleLock } from "@/components/ui/idle-lock";
import { LockScreen } from "@/components/ui/lock-screen";

export function Shell({ children, user }: { children: React.ReactNode; user: { name: string } }) {
  return (
    <IdleLock
      timeoutSeconds={600}
      warningSeconds={30}
      lockScreen={({ unlock }) => <LockScreen user={user} methods={["password"]} onUnlock={async (attempt) => (await verify(attempt)) ? unlock() : { error: "Wrong password." }} />}
    >
      {children}
    </IdleLock>
  );
}

declare function verify(attempt: unknown): Promise<boolean>;
```

### Vue

```vue
<script setup lang="ts">
import { NqIdleLock, NqLockScreen } from "@fadymondy/nasaq/vue";

const user = { name: "Nour Adel" };

// Your API call. Resolve { error } for a wrong password.
async function verify(secret: string): Promise<boolean> {
  const res = await fetch("/api/unlock", { method: "POST", body: JSON.stringify({ secret }) });
  return res.ok;
}
</script>

<template>
  <NqIdleLock :timeout-seconds="600" :warning-seconds="30">
    <p>The app</p>
    <template #lockScreen="{ unlock }">
      <NqLockScreen
        :user="user"
        :methods="['password']"
        :on-unlock="async (attempt) => ((await verify(attempt.secret)) ? unlock() : { error: 'Wrong password.' })"
      />
    </template>
  </NqIdleLock>
</template>
```

### Blade

```blade
{{-- Your listener checks the password and then calls unlock(); the lock-screen here accepts "123456" for the docs. --}}
<x-nq::idle-lock :timeout-seconds="600" :warning-seconds="30">
    <x-slot:lock-screen>
        <x-nq::lock-screen :user="['name' => 'Nour Adel']" :methods="['password']" now="2026-09-29T09:00:00"
            x-on:nq-lock-unlock="$event.detail.waitUntil(new Promise((done) => setTimeout(() => { $event.detail.secret === '123456' ? unlock() : null; done($event.detail.secret === '123456' ? undefined : { error: 'Wrong password.' }); }, 100)))" />
    </x-slot:lock-screen>
    <p>The app</p>
    <x-nq::button type="button" variant="secondary" x-on:click="lock()">Lock</x-nq::button>
</x-nq::idle-lock>
```

### HTML + Alpine

```html
<div data-slot="idle-lock" x-data="nqIdleLock(JSON.parse('{\u0022timeoutSeconds\u0022:600,\u0022warningSeconds\u0022:30,\u0022locked\u0022:false,\u0022disabled\u0022:false,\u0022description\u0022:\u0022You have been inactive, so the app will lock in {time} to protect your data.\u0022}'))" x-modelable="isLocked"
    x-bind:data-locked="isLocked ? '' : null" class="contents">
            <div data-slot="idle-lock-app" class="contents" x-bind:inert="isLocked ? '' : null" x-bind:aria-hidden="isLocked ? 'true' : null"><p>The app</p>
    <button data-slot="button"
     type="button"                         x-on:click="lock()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border 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 border-border bg-card text-foreground hover:bg-nq-hover h-control px-[var(--nq-control-pad)]">
        Lock</button></div>
    <template x-teleport="body">
        <div data-slot="idle-warning-portal">
            <div data-slot="alert-dialog-backdrop" x-nq-presence="warning" class="fixed inset-0 z-50 bg-nq-fg/15 dark:bg-nq-bg/60 transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0"></div>
            <div data-slot="idle-warning" role="alertdialog" aria-modal="true" tabindex="-1" aria-labelledby="nq-idle-000002-title" aria-describedby="nq-idle-000002-description"
                x-nq-presence="warning" x-trap.noscroll="warning"
                class="fixed inset-0 z-50 m-auto grid h-fit w-[calc(100%-2rem)] max-w-md gap-4 rounded-floating border border-border bg-popover p-6 text-popover-foreground outline-none max-h-[calc(100dvh-2rem)] overflow-y-auto transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">
                <div data-slot="alert-dialog-header" class="flex flex-col gap-1.5 text-start">
                    <h2 data-slot="alert-dialog-title" id="nq-idle-000002-title" class="text-h3 text-foreground flex items-center gap-2">
                        <svg aria-hidden="true" class="size-5 text-nq-warning-text" 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 2h4"/>
  <path d="M12 14v-4"/>
  <path d="M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6"/>
  <path d="M9 17H4v5"/>
</svg>                        Still there?
                    </h2>
                    <p data-slot="alert-dialog-description" id="nq-idle-000002-description" class="text-body-sm text-muted-foreground" x-text="description">You have been inactive, so the app will lock in 0:30 to protect your data.</p>
                </div>
                <div class="flex flex-col gap-2">
                    <p dir="ltr" role="timer" x-bind:aria-live="live" aria-label="Time left" class="text-center text-h2 text-foreground tabular-nums" x-text="clock"></p>
                    <div data-slot="progress" data-tone="warning" role="progressbar"     aria-valuemin="0" aria-valuemax="100"  aria-valuenow="100"     aria-valuetext="100%" data-complete x-data="nqProgress(JSON.parse('{\u0022value\u0022:100,\u0022min\u0022:0,\u0022max\u0022:100,\u0022locale\u0022:\u0022en\u0022,\u0022fixedText\u0022:false}'))" x-modelable="value" x-effect="sync(percent)"
    aria-label="Time left" class="flex w-full flex-col gap-1.5">
        <div data-slot="progress-track" data-complete class="relative block w-full overflow-hidden rounded-full bg-nq-surface-soft h-1">
        <div data-slot="progress-indicator" data-complete style="inset-inline-start:0;width:100%"
            class="block h-full rounded-full transition-[width] duration-300 ease-nq motion-reduce:transition-none bg-nq-warning"></div>
    </div>
</div>
                </div>
                <div data-slot="alert-dialog-footer" class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
                    <button data-slot="button"
     type="button"                         x-on:click="lock()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border 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 border-border bg-card text-foreground hover:bg-nq-hover h-control px-[var(--nq-control-pad)]">
        <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">
  <circle cx="12" cy="16" r="1"/>
  <rect x="3" y="10" width="18" height="12" rx="2"/>
  <path d="M7 10V7a5 5 0 0 1 10 0v3"/>
</svg>Lock now</button>
                    <button data-slot="button"
     type="button"                         autofocus="autofocus" x-on:click="stay()" 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 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--nq-action)_88%,var(--nq-fg))] h-control px-[var(--nq-control-pad)]">
        Stay signed in</button>
                </div>
            </div>
        </div>
    </template>
            <template x-if="isLocked">
            <div data-slot="idle-lock-screen" class="fixed inset-0 z-[60] overflow-y-auto bg-background"><div data-slot="lock-screen" data-reason="locked" x-data="nqLockScreen(JSON.parse('{\u0022methods\u0022:[\u0022password\u0022],\u0022defaultMethod\u0022:\u0022password\u0022,\u0022pinLength\u0022:6,\u0022requireCode\u0022:false,\u0022maxAttempts\u0022:5,\u0022lockoutSeconds\u0022:30,\u0022now\u0022:\u00222026-09-29T09:00:00\u0022,\u0022locale\u0022:\u0022en\u0022,\u0022accounts\u0022:[],\u0022failed\u0022:\u0022Something went wrong. Try again.\u0022,\u0022labels\u0022:{\u0022passwordRequired\u0022:\u0022Enter your password.\u0022,\u0022codeRequired\u0022:\u0022Enter all {length} digits of the code.\u0022,\u0022wrongPin\u0022:\u0022That PIN is not right. {left} tries left.\u0022,\u0022lockout\u0022:\u0022Too many attempts. Try again in {time}.\u0022,\u0022entered\u0022:\u0022{count} of {length} digits entered\u0022,\u0022failed\u0022:\u0022Something went wrong. Try again.\u0022}}'))"
    x-bind:data-method="method" x-bind:aria-busy="pending ? 'true' : null"
    x-on:nq-lock-unlock="$event.detail.waitUntil(new Promise((done) =&gt; setTimeout(() =&gt; { $event.detail.secret === &#039;123456&#039; ? unlock() : null; done($event.detail.secret === &#039;123456&#039; ? undefined : { error: &#039;Wrong password.&#039; }); }, 100)))" class="relative isolate flex min-h-dvh flex-col overflow-hidden bg-muted text-foreground">
    <div aria-hidden="true" data-slot="lock-screen-wallpaper" class="absolute inset-0 -z-10">
                <div class="absolute inset-0 bg-background/55 backdrop-blur-sm"></div>
    </div>
            <div data-slot="lock-screen-clock" class="flex flex-col items-center gap-1 px-4 pt-10 text-center sm:pt-14">
            <time dir="ltr" class="text-[clamp(3rem,10vw,5rem)] leading-none font-light tabular-nums text-foreground" x-text="time"></time>
            <p class="text-body text-muted-foreground first-letter:uppercase" x-text="date"></p>
        </div>
    <main class="mx-auto flex w-full max-w-sm flex-1 flex-col items-center justify-center gap-5 px-4 py-8 text-center">
                    <div data-slot="lock-screen-mark"><svg data-slot="product-mark" viewBox="0 0 100 100" width="28" height="28"  aria-hidden="true"  shape-rendering="crispEdges"  class="shrink-0">
            <g class="dark:hidden">
                            <rect x="0" y="20" width="20" height="20" fill="#15694A" />
                            <rect x="80" y="20" width="20" height="20" fill="#15694A" />
                            <rect x="20" y="40" width="20" height="20" fill="#15694A" />
                            <rect x="60" y="40" width="20" height="20" fill="#15694A" />
                            <rect x="40" y="60" width="20" height="20" fill="#C9A227" />
                    </g>
        <g class="hidden dark:block">
                            <rect x="0" y="20" width="20" height="20" fill="#4CC495" />
                            <rect x="80" y="20" width="20" height="20" fill="#4CC495" />
                            <rect x="20" y="40" width="20" height="20" fill="#4CC495" />
                            <rect x="60" y="40" width="20" height="20" fill="#4CC495" />
                            <rect x="40" y="60" width="20" height="20" fill="#C9A227" />
                    </g>
    </svg>
</div>
                <div class="flex flex-col items-center gap-2">
            <span data-slot="avatar" x-data="nqAvatar(0)"
    class="inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-10 text-label rounded-full">
                    <span data-slot="avatar-fallback" class="flex size-full items-center justify-center" x-bind="fallback"
             role="img" aria-label="Nour Adel" >NA</span>
    </span>
            <div class="flex flex-col gap-0.5">
                <h1 class="text-h3 text-foreground">Nour Adel</h1>
                            </div>
        </div>
        <div class="flex flex-col items-center gap-1">
            <p class="inline-flex items-center gap-1.5 text-label text-foreground">
                 <svg aria-hidden="true" class="size-4" 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">
  <circle cx="12" cy="16" r="1"/>
  <rect x="3" y="10" width="18" height="12" rx="2"/>
  <path d="M7 10V7a5 5 0 0 1 10 0v3"/>
</svg>                 Locked
            </p>
            <p class="text-body-sm text-muted-foreground">Enter your credentials to continue.</p>
        </div>
        <div class="flex w-full flex-col items-center gap-4" x-on:keydown="onKey($event)">
                            <template x-if="method === 'password'">
                    <form novalidate data-slot="lock-screen-password" class="flex w-full flex-col gap-3 text-start" x-on:submit.prevent="submitPassword()">
                        <div data-slot="field" x-data="nqField(false)" x-modelable="invalid" x-id="['nq-field']"
         data-valid     x-model="bad.secret" class="flex flex-col gap-1.5">
    <label data-slot="field-label"  for="nq-lock-000001-secret" class="text-label text-foreground data-disabled:opacity-50">Password</label>
                            <div data-slot="password-input" x-data="nqPasswordInput('', JSON.parse('{\u0022visible\u0022:false,\u0022score\u0022:null,\u0022rules\u0022:null,\u0022levels\u0022:[\u0022Very weak\u0022,\u0022Weak\u0022,\u0022Fair\u0022,\u0022Good\u0022,\u0022Strong\u0022]}'))" x-modelable="value"
    class="flex w-full flex-col gap-2">
    <div role="group" data-slot="input-group"
    class="group/input-group flex h-control min-h-[var(--nq-touch-min,0px)] w-full min-w-0 items-center overflow-hidden rounded-control border border-input bg-card text-body text-foreground transition-colors duration-150 ease-nq focus-within:border-nq-focus focus-within:outline-1 focus-within:outline-nq-focus has-[[data-invalid]]:border-nq-danger has-[[aria-invalid=true]]:border-nq-danger has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50"><input data-slot="input-group-input"     x-model="password" x-bind:type="shown ? &#039;text&#039; : &#039;password&#039;" type="password" value="" autocapitalize="none" autocorrect="off" spellcheck="false" id="nq-lock-000001-secret" name="secret" autofocus="autofocus" autocomplete="current-password" x-bind:disabled="locked()" x-bind:aria-invalid="hasMessage() ? &#039;true&#039; : null" class="h-full min-w-0 flex-1 border-0 bg-transparent px-3 text-body text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed pointer-coarse:text-[16px]" />
        <div data-slot="input-group-addon" data-align="end"
    class="flex h-full shrink-0 items-center gap-1.5 text-body-sm text-muted-foreground [&_svg]:size-4 order-last ps-1 pe-1.5"><button data-slot="password-input-toggle"
     type="button"                         x-bind="toggle" aria-label="Show password" aria-pressed="false" 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 x-show="shown" x-cloak="1" style="display: none" 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.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/>
  <path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/>
  <path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/>
  <path d="m2 2 20 20"/>
</svg>                <svg x-show="!shown" x-cloak="1" style="" 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="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></button></div></div>
        </div>
</div>
                                                <button data-slot="button"
     type="submit"                         x-bind:disabled="locked()" x-bind:data-disabled="locked() ? &#039;&#039; : null" x-bind:aria-busy="pending ? &#039;true&#039; : null" 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">
        <template x-if="pending"><svg data-slot="spinner" aria-hidden="true" class="size-4 animate-spin motion-reduce:animate-none" 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 12a9 9 0 1 1-6.219-8.56"/>
</svg></template>
                            Unlock</button>
                    </form>
                </template>
            <div class="min-h-5 w-full" aria-live="polite">
                <p role="alert" class="text-caption text-nq-danger-text" style="display: none" x-show="showPlain()" x-text="message()"></p>
                <div data-slot="alert" data-tone="danger" role="alert"
        style="display: none" x-show="showAlert()" class="relative grid grid-cols-[auto_1fr_auto] items-start gap-x-3 rounded-card border p-3 text-start border-nq-danger/30 bg-nq-danger-soft">
    <svg aria-hidden="true" data-slot="alert-icon" class="mt-0.5 size-4 text-nq-danger-text" 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">
  <circle cx="12" cy="12" r="10"/>
  <path d="m15 9-6 6"/>
  <path d="m9 9 6 6"/>
</svg>    <div data-slot="alert-body" class="flex min-w-0 flex-col gap-0.5">
                            <div data-slot="alert-description" class="text-body-sm text-foreground"><span x-text="message()"></span></div>
            </div>
    </div>
            </div>
        </div>
                    </main>
    </div></div>
        </template>
    </div>
```
