# ResetPasswordForm

> Choose a new password with a confirmation field, strength meter and optional policy checklist, then a Password changed or Link expired screen; en and ar built in.

Source: https://docs.nasaqui.com/components/reset-password-form

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/reset-password-form.json
```

Two password fields: new and confirm. It checks length, match and (with `rules`) a policy, then calls
`onSubmit({ password })`. On success it swaps to a "Password changed" screen with a Sign in button. Return
`{ expired: true }` for a used or old link and it shows "This link has expired" with a Send a new link button.

## When to use

- The page a reset email links to.

## When not to use

- Changing a password while signed in: use a change-password form that asks for the current one.

## Import

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

## Quick start

```tsx
import { ResetPasswordForm } from "@fadymondy/nasaq/web";

export function Reset({ token }: { token: string }) {
  return (
    <ResetPasswordForm
      rules
      signIn="/login"
      requestLink="/forgot-password"
      onSubmit={async ({ password }) => {
        const res = await api.reset(token, password);
        if (res.expired) return { expired: true };
      }}
    />
  );
}

declare const api: { reset(token: string, password: string): Promise<{ expired: boolean }> };
```

## Anatomy

```
ResetPasswordForm                data-slot="reset-password-form", data-state="idle" | "success" | "expired"
├─ idle: form                    noValidate
│  ├─ error summary              role="alert"
│  ├─ Field: password            autocomplete="new-password", strength meter, hint or rule checklist
│  ├─ Field: confirm             autocomplete="new-password"
│  └─ submit Button
└─ success / expired
   ├─ icon                       success or warning soft circle
   ├─ role="status"              h2 (focused, tabIndex -1) + body
   └─ Button                     Sign in / Send a new link (a link when given a URL)
```

## API

**ResetPasswordForm**: every `div` prop except `onSubmit` and `children`, plus:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `onSubmit` | `(values: { password }) => Promise<ResetPasswordResult> \| ResetPasswordResult` | required | Resolve nothing on success, `{ error, fieldErrors }` to stay on the form, or `{ expired: true }` for a dead link. |
| `minPasswordLength` | `number` | `8`, or 12 with `rules` | Checked before `onSubmit`. |
| `rules` | `boolean \| PasswordPolicy` | | Show the requirement checklist and require every rule. See [`password-input`](https://docs.nasaqui.com/components/password-input). |
| `defaultState` | `"idle" \| "success" \| "expired"` | `"idle"` | Start on a screen, e.g. `"expired"` when the token was checked on load. |
| `state` / `onStateChange` | `ResetPasswordState` | | Controlled screen. |
| `signIn` | `string \| () => void` | | The Sign in button on success: a URL renders a link. Hidden when omitted. |
| `requestLink` | `string \| () => void` | | The Send a new link button on an expired link. Hidden when omitted. |
| `labels` | `Partial<ResetPasswordFormLabels>` | English or Arabic | `passwordHint` uses `{min}`. |

`fieldErrors` keys: `password`, `confirm`.

## Examples

**Token checked on load**

```tsx
import { ResetPasswordForm } from "@fadymondy/nasaq/web";

export function Reset({ valid }: { valid: boolean }) {
  return <ResetPasswordForm defaultState={valid ? "idle" : "expired"} requestLink="/forgot-password" onSubmit={async () => {}} />;
}
```

**Longer minimum**

```tsx
import { ResetPasswordForm } from "@fadymondy/nasaq/web";

export function Strict() {
  return <ResetPasswordForm minPasswordLength={12} onSubmit={async () => {}} />;
}
```

## Accessibility

- The form is `noValidate` and validates itself, so messages are yours to localise and read in one place. Every field
  has a visible `<label>`; an invalid field gets `aria-invalid` and its message is linked with `aria-describedby`.
- On a failed submit focus moves to the first invalid field. When the failure is for the whole form (`error`) the
  summary at the top of the form (`role="alert"`) is focused instead, so it is announced.
- The submit button shows a spinner and is `aria-busy` while `onSubmit` runs; a second submit is ignored.
- Both fields use `autocomplete="new-password"`; the strength meter is `role="meter"` with a live level word.
- When the form swaps to the success or expired screen, focus moves to its heading, which sits in a `role="status"`
  region, so the outcome is announced and the next action is one Tab away.

## RTL & i18n

- Built-in English and Arabic strings follow the Nasaq locale (`useNasaq`). Pass `labels` to change any of them or to
  add another language; keys you omit keep the built-in text.
- Layout uses logical properties, so it mirrors under `dir="rtl"` with no extra work.


## Styling & tokens

- Built from `Field`, `PasswordInput`, `Button` and `Alert`; tokens only. The success icon uses `bg-nq-success-soft`
  and the expired icon `bg-nq-warning-soft`.

## Do / Don't

- Do sign the person in or send them to sign-in on success.
- Do invalidate the link once used.
- Don't echo the password back.
- Don't skip the confirmation field.

## Related

- [`auth-layout`](https://docs.nasaqui.com/components/auth-layout)
- [`forgot-password-form`](https://docs.nasaqui.com/components/forgot-password-form)
- [`login-form`](https://docs.nasaqui.com/components/login-form)
- [`password-input`](https://docs.nasaqui.com/components/password-input)

## Lab

https://docs.nasaqui.com/?path=/docs/components-auth-reset-password-form--docs

## Code

### React

```tsx
import { ResetPasswordForm } from "@fadymondy/nasaq/web";

export function Reset({ token }: { token: string }) {
  return (
    <ResetPasswordForm
      rules
      signIn="/login"
      requestLink="/forgot-password"
      onSubmit={async ({ password }) => {
        const res = await api.reset(token, password);
        if (res.expired) return { expired: true };
      }}
    />
  );
}

declare const api: { reset(token: string, password: string): Promise<{ expired: boolean }> };
```

### shadcn

```tsx
import { ResetPasswordForm } from "@/components/ui/reset-password-form";

export function Reset({ token }: { token: string }) {
  return (
    <ResetPasswordForm
      rules
      signIn="/login"
      requestLink="/forgot-password"
      onSubmit={async ({ password }) => {
        const res = await api.reset(token, password);
        if (res.expired) return { expired: true };
      }}
    />
  );
}

declare const api: { reset(token: string, password: string): Promise<{ expired: boolean }> };
```

### Vue

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

const token = new URLSearchParams(location.search).get("token") ?? "";

// Your API call. Resolve nothing for success, or { expired: true } when the link was used or is too old.
async function reset({ password }: ResetPasswordValues) {
  const res = await fetch("/api/reset", { method: "POST", body: JSON.stringify({ token, password }) });
  if (res.status === 410) return { expired: true as const };
}
</script>

<template>
  <div class="w-80">
    <NqResetPasswordForm rules sign-in="/login" request-link="/forgot-password" :on-submit="reset" />
  </div>
</template>
```

### Blade

```blade
{{-- Your API call. Resolve nothing for success, or { expired: true } when the link was used or is too old. --}}
<div class="w-80">
    <x-nq::reset-password-form rules sign-in="/login" request-link="/forgot-password"
        x-on:nq-reset-password="$event.detail.waitUntil(new Promise((done) => setTimeout(() => done($event.detail.password.includes('expired') ? { expired: true } : undefined), 300)))" />
</div>
```

### HTML + Alpine

```html
<div class="w-80">
    <div data-slot="reset-password-form" x-data="nqResetPasswordForm(JSON.parse('{\u0022minLength\u0022:12,\u0022policy\u0022:{\u0022minLength\u0022:12,\u0022require\u0022:[\u0022upper\u0022,\u0022lower\u0022,\u0022digit\u0022,\u0022symbol\u0022]},\u0022state\u0022:\u0022idle\u0022,\u0022names\u0022:[\u0022password\u0022,\u0022confirm\u0022],\u0022fieldLabels\u0022:{\u0022password\u0022:\u0022New password\u0022,\u0022confirm\u0022:\u0022Confirm new password\u0022},\u0022errorTitle\u0022:\u0022Fix these to reset your password\u0022,\u0022failed\u0022:\u0022Something went wrong. Try again.\u0022,\u0022labels\u0022:{\u0022password\u0022:\u0022New password\u0022,\u0022passwordHint\u0022:\u0022At least {min} characters.\u0022,\u0022confirm\u0022:\u0022Confirm new password\u0022,\u0022submit\u0022:\u0022Reset password\u0022,\u0022passwordShort\u0022:\u0022Use at least {min} characters.\u0022,\u0022passwordWeak\u0022:\u0022Meet every requirement below.\u0022,\u0022confirmMismatch\u0022:\u0022The passwords do not match.\u0022,\u0022errorTitle\u0022:\u0022Fix these to reset your password\u0022,\u0022failed\u0022:\u0022Something went wrong. Try again.\u0022,\u0022successTitle\u0022:\u0022Password changed\u0022,\u0022successBody\u0022:\u0022You can now sign in with your new password.\u0022,\u0022signIn\u0022:\u0022Sign in\u0022,\u0022expiredTitle\u0022:\u0022This link has expired\u0022,\u0022expiredBody\u0022:\u0022Reset links work once and only for a short time. Ask for a new one.\u0022,\u0022requestLink\u0022:\u0022Send a new link\u0022}}'))" x-bind:data-state="state"
    x-on:nq-reset-password="$event.detail.waitUntil(new Promise((done) =&gt; setTimeout(() =&gt; done($event.detail.password.includes(&#039;expired&#039;) ? { expired: true } : undefined), 300)))" class="w-full">
    <form novalidate x-show="state === 'idle'"  x-bind:aria-busy="pending ? 'true' : null" x-on:submit.prevent="onSubmit()" x-on:input="clear($event.target.name)"
         class="flex w-full flex-col gap-4">
        <div tabindex="-1" class="sr-only" x-show="! problems()"></div>
<div tabindex="-1" data-slot="auth-error-summary" class="outline-none" x-show="problems()" style="display: none">
    <div data-slot="alert" data-tone="danger" role="alert" 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-title" class="text-label text-foreground" x-text="summaryTitle()"></div>
            <div data-slot="alert-description" class="text-body-sm text-muted-foreground" x-show="! error && entries().length" style="display: none">
                <ul class="flex list-disc flex-col gap-0.5 ps-4">
                    <template x-for="e in entries()" :key="e.name">
                        <li><button type="button" class="text-start underline underline-offset-2" x-on:click="focusField(e.name)" x-text="(e.label ? e.label + ': ' : '') + e.message"></button></li>
                    </template>
                </ul>
            </div>
        </div>
    </div>
</div>
        <div data-slot="field" x-data="nqField(false)" x-modelable="invalid" x-id="['nq-field']"
         data-valid     x-model="bad.password" class="flex flex-col gap-1.5">
    <label data-slot="field-label"  for="nq-reset-000001-password" class="text-label text-foreground data-disabled:opacity-50">New password</label>
            <div data-slot="password-input" x-data="nqPasswordInput('', JSON.parse('{\u0022visible\u0022:false,\u0022score\u0022:null,\u0022rules\u0022:{\u0022minLength\u0022:12,\u0022require\u0022:[\u0022upper\u0022,\u0022lower\u0022,\u0022digit\u0022,\u0022symbol\u0022]},\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="value" x-bind:type="shown ? &#039;text&#039; : &#039;password&#039;" type="password" value="" autocapitalize="none" autocorrect="off" spellcheck="false" id="nq-reset-000001-password" name="password" autocomplete="new-password" aria-describedby="nq-reset-000001-password-hint nq-reset-000001-password-error" x-bind:aria-invalid="bad.password ? &#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 data-slot="password-input-strength" data-score="0" class="flex flex-col gap-1 opacity-60" x-bind="strengthBox">
            <div data-slot="meter" role="meter" aria-label="Password strength" aria-valuemin="0" aria-valuemax="4" aria-valuenow="0"
                data-tone="danger" x-bind="meter" class="flex w-full flex-col gap-1.5">
                <div data-slot="meter-track" class="relative block h-1 w-full overflow-hidden rounded-full bg-nq-surface-soft">
                    <div data-slot="meter-indicator" style="inset-inline-start:0;width:0%" x-bind="meterFill"
                        class="block h-full rounded-full transition-[width] duration-300 ease-nq motion-reduce:transition-none bg-nq-danger"></div>
                </div>
            </div>
            <div class="flex items-baseline justify-between gap-3 text-caption text-muted-foreground">
                <span>Password strength</span>
                <span aria-live="polite" class="text-foreground" x-text="levelText()"></span>
            </div>
        </div>
                <ul data-slot="password-input-rules" aria-label="Password requirements" class="grid gap-1 sm:grid-cols-2">
                            <li  x-bind="rule('length')"
                    class="flex items-center gap-1.5 text-caption transition-colors duration-150 ease-nq text-muted-foreground">
                    <svg x-show="met('length')" x-cloak="1" style="display: none" aria-hidden="true" class="size-3.5 shrink-0" 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="M20 6 9 17l-5-5"/>
</svg>                    <svg x-show="!met('length')" x-cloak="1" style="" aria-hidden="true" class="size-3.5 shrink-0" 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 12h14"/>
</svg>                    <span>At least 12 characters</span>
                    <span class="sr-only" x-text="met('length') ? ', met' : ', not met'">, not met</span>
                </li>
                            <li  x-bind="rule('upper')"
                    class="flex items-center gap-1.5 text-caption transition-colors duration-150 ease-nq text-muted-foreground">
                    <svg x-show="met('upper')" x-cloak="1" style="display: none" aria-hidden="true" class="size-3.5 shrink-0" 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="M20 6 9 17l-5-5"/>
</svg>                    <svg x-show="!met('upper')" x-cloak="1" style="" aria-hidden="true" class="size-3.5 shrink-0" 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 12h14"/>
</svg>                    <span>An uppercase letter</span>
                    <span class="sr-only" x-text="met('upper') ? ', met' : ', not met'">, not met</span>
                </li>
                            <li  x-bind="rule('lower')"
                    class="flex items-center gap-1.5 text-caption transition-colors duration-150 ease-nq text-muted-foreground">
                    <svg x-show="met('lower')" x-cloak="1" style="display: none" aria-hidden="true" class="size-3.5 shrink-0" 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="M20 6 9 17l-5-5"/>
</svg>                    <svg x-show="!met('lower')" x-cloak="1" style="" aria-hidden="true" class="size-3.5 shrink-0" 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 12h14"/>
</svg>                    <span>A lowercase letter</span>
                    <span class="sr-only" x-text="met('lower') ? ', met' : ', not met'">, not met</span>
                </li>
                            <li  x-bind="rule('digit')"
                    class="flex items-center gap-1.5 text-caption transition-colors duration-150 ease-nq text-muted-foreground">
                    <svg x-show="met('digit')" x-cloak="1" style="display: none" aria-hidden="true" class="size-3.5 shrink-0" 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="M20 6 9 17l-5-5"/>
</svg>                    <svg x-show="!met('digit')" x-cloak="1" style="" aria-hidden="true" class="size-3.5 shrink-0" 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 12h14"/>
</svg>                    <span>A number</span>
                    <span class="sr-only" x-text="met('digit') ? ', met' : ', not met'">, not met</span>
                </li>
                            <li  x-bind="rule('symbol')"
                    class="flex items-center gap-1.5 text-caption transition-colors duration-150 ease-nq text-muted-foreground">
                    <svg x-show="met('symbol')" x-cloak="1" style="display: none" aria-hidden="true" class="size-3.5 shrink-0" 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="M20 6 9 17l-5-5"/>
</svg>                    <svg x-show="!met('symbol')" x-cloak="1" style="" aria-hidden="true" class="size-3.5 shrink-0" 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 12h14"/>
</svg>                    <span>A symbol</span>
                    <span class="sr-only" x-text="met('symbol') ? ', met' : ', not met'">, not met</span>
                </li>
                    </ul>
    </div>
            <div data-slot="field-error" role="alert" x-show="invalid"  style="display: none"  id="nq-reset-000001-password-error" class="text-caption text-nq-danger-text"><span x-text="fe.password"></span></div>
</div>
        <div data-slot="field" x-data="nqField(false)" x-modelable="invalid" x-id="['nq-field']"
         data-valid     x-model="bad.confirm" class="flex flex-col gap-1.5">
    <label data-slot="field-label"  for="nq-reset-000001-confirm" class="text-label text-foreground data-disabled:opacity-50">Confirm new 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="value" x-bind:type="shown ? &#039;text&#039; : &#039;password&#039;" type="password" value="" autocapitalize="none" autocorrect="off" spellcheck="false" id="nq-reset-000001-confirm" name="confirm" autocomplete="new-password" aria-describedby="nq-reset-000001-confirm-error" x-bind:aria-invalid="bad.confirm ? &#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 data-slot="field-error" role="alert" x-show="invalid"  style="display: none"  id="nq-reset-000001-confirm-error" class="text-caption text-nq-danger-text"><span x-text="fe.confirm"></span></div>
</div>
        <button data-slot="button"
     type="submit"                         x-bind:disabled="pending" x-bind:data-disabled="pending ? &#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>
            Reset password</button>
    </form>
    <div class="flex w-full flex-col gap-4 text-start" x-show="state !== 'idle'"  style="display: none" >
        <div class="flex size-10 items-center justify-center rounded-full bg-nq-success-soft text-nq-success-text"
            x-bind:class="state === 'success' ? 'bg-nq-success-soft text-nq-success-text' : 'bg-nq-warning-soft text-nq-warning-text'">
            <svg x-show="state === 'success'" aria-hidden="true" style="" 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">
  <circle cx="12" cy="12" r="10"/>
  <path d="m9 12 2 2 4-4"/>
</svg>            <svg x-show="state === 'expired'" aria-hidden="true" style="display: none" 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="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
  <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
</svg>        </div>
        <div role="status" class="flex flex-col gap-1.5">
            <h2 data-slot="reset-password-title" tabindex="-1" class="text-h3 text-foreground outline-none" x-text="state === 'success' ? 'Password changed' : 'This link has expired'">Password changed</h2>
            <p class="text-body-sm text-muted-foreground" x-text="state === 'success' ? 'You can now sign in with your new password.' : 'Reset links work once and only for a short time. Ask for a new one.'">You can now sign in with your new password.</p>
        </div>
                    <div class="contents" x-show="state === 'success'"  style="display: none" >
                                    <a data-slot="button"
     href="/login"                         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">
        Sign in</a>
                            </div>
                            <div class="contents" x-show="state === 'expired'"  style="display: none" >
                                    <a data-slot="button"
     href="/forgot-password"                         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">
        Send a new link</a>
                            </div>
            </div>
</div>
</div>
```
