`. Give it an `id` and pass that to the Item's `aria-labelledby`. |
##### Title.Props
Re-export of [Title](#questionnaire) props.
##### Description
Supporting text that describes the item. Renders a `` element.
**Description Props:**
| Prop | Type | Default | Description |
| :----- | :------------------------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| render | `Snippet<[props: HTMLAttributes, state: Record, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
##### Description.Props
Re-export of [Description](#questionnaire) props.
##### Error
The validation message, hidden until the item fails validation. Renders a `` element.
**Error Props:**
| Prop | Type | Default | Description |
| :------- | :-------------------------------------------------------------------------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `Snippet` | - | Defaults to a message that depends on whether the item is required. |
| render | `Snippet<[props: HTMLAttributes, state: QuestionnaireErrorState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Error Data Attributes:**
| Attribute | Type | Description |
| :----------- | :--- | :--------------------------------- |
| data-invalid | - | Present while its item is invalid. |
##### Error.Props
Re-export of [Error](#questionnaire) props.
##### Error.State
```typescript
type QuestionnaireErrorState = {
/** Whether its item is invalid. */
invalid: boolean;
};
```
##### Choice
A fixed answer, with its native control inside. Renders a `` element.
**Choice Props:**
| Prop | Type | Default | Description |
| :------------- | :--------------------------------------------------------------------------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| defaultChecked | `boolean` | - | The state a form reset restores. Defaults to the initial `checked`. |
| checked | `boolean` | - | Bind it to follow and set the selection. |
| value | `string` | - | The value the choice submits. |
| onchange | `ChangeEventHandler \| null` | - | Runs for the native input's change event. |
| disabled | `boolean` | `false` | Whether the choice ignores user interaction. |
| render | `Snippet<[props: HTMLAttributes, state: QuestionnaireChoiceState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Choice Data Attributes:**
| Attribute | Type | Description |
| :------------- | :--- | :----------------------------------------------------------- |
| data-checked | - | Present when the choice is selected. |
| data-unchecked | - | Present when the choice is not selected. |
| data-disabled | - | Present when the choice is disabled. |
| data-invalid | - | Present when its item is invalid. |
| data-shortcut | - | The key that selects the choice. |
| data-type | - | The control type: `radio`, or `checkbox` in a multiple item. |
##### Choice.Props
Re-export of [Choice](#questionnaire) props.
##### Choice.State
```typescript
type QuestionnaireChoiceState = {
/** Whether the choice is selected. */
checked: boolean;
/** Whether the choice or its item is disabled. */
disabled: boolean;
/** Whether its item is invalid. */
invalid: boolean;
/** The key that selects the choice, or null. */
shortcut: string | null;
/** The native control type. A multiple item renders checkboxes. */
type: 'checkbox' | 'radio';
};
```
##### ChoiceInput
The native radio or checkbox of a choice. Renders an ` ` element.
**ChoiceInput Props:**
| Prop | Type | Default | Description |
| :----- | :----------------------------------------------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| render | `Snippet<[props: HTMLInputAttributes, state: QuestionnaireChoiceState]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element and the component's state. |
**ChoiceInput Data Attributes:**
| Attribute | Type | Description |
| :------------- | :--- | :----------------------------------------------------------- |
| data-checked | - | Present when the choice is selected. |
| data-unchecked | - | Present when the choice is not selected. |
| data-disabled | - | Present when the choice is disabled. |
| data-invalid | - | Present when its item is invalid. |
| data-shortcut | - | The key that selects the choice. |
| data-type | - | The control type: `radio`, or `checkbox` in a multiple item. |
##### ChoiceInput.Props
Re-export of [ChoiceInput](#questionnaire) props.
##### ChoiceInput.State
```typescript
type QuestionnaireChoiceInputState = {
/** Whether the choice is selected. */
checked: boolean;
/** Whether the choice or its item is disabled. */
disabled: boolean;
/** Whether its item is invalid. */
invalid: boolean;
/** The key that selects the choice, or null. */
shortcut: string | null;
/** The native control type. A multiple item renders checkboxes. */
type: 'checkbox' | 'radio';
};
```
##### ChoiceLabel
The text of a choice. Renders a `` element.
**ChoiceLabel Props:**
| Prop | Type | Default | Description |
| :----- | :------------------------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| render | `Snippet<[props: HTMLAttributes, state: Record, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
##### ChoiceLabel.Props
Re-export of [ChoiceLabel](#questionnaire) props.
##### ChoiceShortcut
Shows the key that selects a choice. Renders a `` element.
**ChoiceShortcut Props:**
| Prop | Type | Default | Description |
| :------- | :----------------------------------------------------------------------------------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `Snippet` | - | Defaults to the assigned shortcut. |
| render | `Snippet<[props: HTMLAttributes, state: QuestionnaireChoiceShortcutState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**ChoiceShortcut Data Attributes:**
| Attribute | Type | Description |
| :------------ | :--- | :------------------------------- |
| data-shortcut | - | The key that selects the choice. |
##### ChoiceShortcut.Props
Re-export of [ChoiceShortcut](#questionnaire) props.
##### ChoiceShortcut.State
```typescript
type QuestionnaireChoiceShortcutState = {
/** The key that selects the choice, or null. */
shortcut: string | null;
};
```
##### Choices
Groups the answers of an item. Renders a `` element.
**Choices Props:**
| Prop | Type | Default | Description |
| :----- | :---------------------------------------------------------------------------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| render | `Snippet<[props: HTMLAttributes
, state: QuestionnaireChoicesState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Choices Data Attributes:**
| Attribute | Type | Description |
| :------------- | :--- | :---------------------------------------------------------------- |
| data-shortcuts | - | The shortcut mode, `letters` or `numbers`, when shortcuts are on. |
##### Choices.Props
Re-export of [Choices](#questionnaire) props.
##### Choices.State
```typescript
type QuestionnaireChoicesState = {
/** The shortcut mode, or null when shortcuts are off. */
shortcuts: QuestionnaireShortcutMode | null;
};
```
##### Next
Validates the active item and moves to the next one. Renders a `` element.
**Next Props:**
| Prop | Type | Default | Description |
| :------- | :------------------------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `Snippet` | - | Defaults to the action's name, such as "Next". |
| render | `Snippet<[props: HTMLButtonAttributes, state: QuestionnaireNavigationState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Next Data Attributes:**
| Attribute | Type | Description |
| :------------ | :--- | :--------------------------------------------------------------- |
| data-disabled | - | Present when the button is disabled. |
| data-hidden | - | Present when the action does not apply to the active item. |
| data-shortcut | - | `Enter` on an enabled, visible Next or Submit. |
| data-status | - | The active item's status: `unanswered`, `answered` or `skipped`. |
| data-visible | - | Present when the action applies to the active item. |
##### Next.Props
Re-export of [Next](#questionnaire) props.
##### Next.State
```typescript
type QuestionnaireNextState = {
/** Whether the button is disabled. */
disabled: boolean;
/** `Enter` on an enabled, visible Next or Submit. */
shortcut: 'Enter' | null;
/** The active item's status, or null when no item is active. */
status: QuestionnaireItemStatus | null;
/** Whether the action applies to the active item. */
visible: boolean;
};
```
##### Previous
Moves to the previous item. Renders a `` element.
**Previous Props:**
| Prop | Type | Default | Description |
| :------- | :------------------------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `Snippet` | - | Defaults to the action's name, such as "Next". |
| render | `Snippet<[props: HTMLButtonAttributes, state: QuestionnaireNavigationState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Previous Data Attributes:**
| Attribute | Type | Description |
| :------------ | :--- | :--------------------------------------------------------------- |
| data-disabled | - | Present when the button is disabled. |
| data-hidden | - | Present when the action does not apply to the active item. |
| data-shortcut | - | `Enter` on an enabled, visible Next or Submit. |
| data-status | - | The active item's status: `unanswered`, `answered` or `skipped`. |
| data-visible | - | Present when the action applies to the active item. |
##### Previous.Props
Re-export of [Previous](#questionnaire) props.
##### Previous.State
```typescript
type QuestionnairePreviousState = {
/** Whether the button is disabled. */
disabled: boolean;
/** `Enter` on an enabled, visible Next or Submit. */
shortcut: 'Enter' | null;
/** The active item's status, or null when no item is active. */
status: QuestionnaireItemStatus | null;
/** Whether the action applies to the active item. */
visible: boolean;
};
```
##### Progress
Shows the position of the active item among the enabled items. Renders a `` element.
**Progress Props:**
| Prop | Type | Default | Description |
| :------- | :----------------------------------------------------------------------------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `Snippet` | - | Defaults to "Question {current} of {total}". |
| render | `Snippet<[props: HTMLAttributes
, state: QuestionnaireProgressState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Progress Data Attributes:**
| Attribute | Type | Description |
| :----------- | :--- | :--------------------------------------- |
| data-current | - | The 1-based position of the active item. |
| data-first | - | Present when the first item is active. |
| data-last | - | Present when the last item is active. |
| data-total | - | The number of enabled items. |
##### Progress.Props
Re-export of [Progress](#questionnaire) props.
##### Progress.State
```typescript
type QuestionnaireProgressState = {
/** The 1-based position of the active item, or 0 when no item is enabled. */
current: number;
/** Whether the first item is active. */
first: boolean;
/** Whether the last item is active. */
last: boolean;
/** The number of enabled items. */
total: number;
};
```
##### Skip
Skips an optional item. Renders a `` element.
**Skip Props:**
| Prop | Type | Default | Description |
| :------- | :------------------------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `Snippet` | - | Defaults to the action's name, such as "Next". |
| render | `Snippet<[props: HTMLButtonAttributes, state: QuestionnaireNavigationState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Skip Data Attributes:**
| Attribute | Type | Description |
| :------------ | :--- | :--------------------------------------------------------------- |
| data-disabled | - | Present when the button is disabled. |
| data-hidden | - | Present when the action does not apply to the active item. |
| data-shortcut | - | `Enter` on an enabled, visible Next or Submit. |
| data-status | - | The active item's status: `unanswered`, `answered` or `skipped`. |
| data-visible | - | Present when the action applies to the active item. |
##### Skip.Props
Re-export of [Skip](#questionnaire) props.
##### Skip.State
```typescript
type QuestionnaireSkipState = {
/** Whether the button is disabled. */
disabled: boolean;
/** `Enter` on an enabled, visible Next or Submit. */
shortcut: 'Enter' | null;
/** The active item's status, or null when no item is active. */
status: QuestionnaireItemStatus | null;
/** Whether the action applies to the active item. */
visible: boolean;
};
```
##### Submit
Submits the form from the last item. Renders a `` element.
**Submit Props:**
| Prop | Type | Default | Description |
| :------- | :------------------------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| children | `Snippet` | - | Defaults to the action's name, such as "Next". |
| render | `Snippet<[props: HTMLButtonAttributes, state: QuestionnaireNavigationState, children: Snippet \| undefined]>` | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. |
**Submit Data Attributes:**
| Attribute | Type | Description |
| :------------ | :--- | :--------------------------------------------------------------- |
| data-disabled | - | Present when the button is disabled. |
| data-hidden | - | Present when the action does not apply to the active item. |
| data-shortcut | - | `Enter` on an enabled, visible Next or Submit. |
| data-status | - | The active item's status: `unanswered`, `answered` or `skipped`. |
| data-visible | - | Present when the action applies to the active item. |
##### Submit.Props
Re-export of [Submit](#questionnaire) props.
##### Submit.State
```typescript
type QuestionnaireSubmitState = {
/** Whether the button is disabled. */
disabled: boolean;
/** `Enter` on an enabled, visible Next or Submit. */
shortcut: 'Enter' | null;
/** The active item's status, or null when no item is active. */
status: QuestionnaireItemStatus | null;
/** Whether the action applies to the active item. */
visible: boolean;
};
```
#### Additional Types
##### QuestionnaireChoiceDataAttributes
Data attributes of [Choice](#questionnaire).
```typescript
declare namespace QuestionnaireChoiceDataAttributes {
/** The control type: `radio`, or `checkbox` in a multiple item. */
const type: 'data-type';
/** Present when the choice is selected. */
const checked: 'data-checked';
/** Present when the choice is not selected. */
const unchecked: 'data-unchecked';
/** Present when the choice is disabled. */
const disabled: 'data-disabled';
/** Present when its item is invalid. */
const invalid: 'data-invalid';
/** The key that selects the choice. */
const shortcut: 'data-shortcut';
}
```
##### QuestionnaireChoiceDefinition
```typescript
type QuestionnaireChoiceDefinition = {
value: string;
disabled?: boolean;
};
```
##### QuestionnaireChoiceInputDataAttributes
Data attributes of [ChoiceInput](#questionnaire).
```typescript
declare namespace QuestionnaireChoiceInputDataAttributes {
/** The control type: `radio`, or `checkbox` in a multiple item. */
const type: 'data-type';
/** Present when the choice is selected. */
const checked: 'data-checked';
/** Present when the choice is not selected. */
const unchecked: 'data-unchecked';
/** Present when the choice is disabled. */
const disabled: 'data-disabled';
/** Present when its item is invalid. */
const invalid: 'data-invalid';
/** The key that selects the choice. */
const shortcut: 'data-shortcut';
}
```
##### QuestionnaireChoicesDataAttributes
Data attributes of [Choices](#questionnaire).
```typescript
declare namespace QuestionnaireChoicesDataAttributes {
/** The shortcut mode, `letters` or `numbers`, when shortcuts are on. */
const shortcuts: 'data-shortcuts';
}
```
##### QuestionnaireChoiceShortcutDataAttributes
Data attributes of [ChoiceShortcut](#questionnaire).
```typescript
declare namespace QuestionnaireChoiceShortcutDataAttributes {
/** The key that selects the choice. */
const shortcut: 'data-shortcut';
}
```
##### QuestionnaireErrorDataAttributes
Data attributes of [Error](#questionnaire).
```typescript
declare namespace QuestionnaireErrorDataAttributes {
/** Present while its item is invalid. */
const invalid: 'data-invalid';
}
```
##### QuestionnaireInputDataAttributes
Data attributes of [Input](#questionnaire).
```typescript
declare namespace QuestionnaireInputDataAttributes {
/** Present when the input holds text. */
const filled: 'data-filled';
/** Present when the input is empty. */
const empty: 'data-empty';
/** Present when the input is disabled. */
const disabled: 'data-disabled';
/** Present when its item is invalid. */
const invalid: 'data-invalid';
}
```
##### QuestionnaireInputType
```typescript
type QuestionnaireInputType =
| 'date'
| 'datetime-local'
| 'email'
| 'month'
| 'number'
| 'password'
| 'search'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week';
```
##### QuestionnaireItemDataAttributes
Data attributes of [Item](#questionnaire).
```typescript
declare namespace QuestionnaireItemDataAttributes {
/** Present when the item is the active one. */
const active: 'data-active';
/** The answer status: `unanswered`, `answered` or `skipped`. */
const status: 'data-status';
/** Present when the item needs an answer. */
const required: 'data-required';
/** Present when the item takes more than one choice. */
const multiple: 'data-multiple';
/** Present when the item is disabled. */
const disabled: 'data-disabled';
/** Present when the item failed validation or `invalid` is set. */
const invalid: 'data-invalid';
}
```
##### QuestionnaireItemDefinition
```typescript
type QuestionnaireItemDefinition = {
name: string;
required?: boolean;
disabled?: boolean;
choices?: readonly QuestionnaireChoiceDefinition[];
};
```
##### QuestionnaireItemStatus
```typescript
type QuestionnaireItemStatus = 'unanswered' | 'answered' | 'skipped';
```
##### QuestionnaireNavigationProps
```typescript
type QuestionnaireNavigationProps = {
/** Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a snippet that receives the props to spread onto the element, the component's state, and the children to render. */
render?: Snippet<
[
props: HTMLButtonAttributes,
state: QuestionnaireNavigationState,
children: Snippet | undefined
]
>;
/** Defaults to the action's name, such as "Next". */
children?: Snippet;
};
```
##### QuestionnaireNavigationState
```typescript
type QuestionnaireNavigationState = {
/** Whether the button is disabled. */
disabled: boolean;
/** `Enter` on an enabled, visible Next or Submit. */
shortcut: 'Enter' | null;
/** The active item's status, or null when no item is active. */
status: QuestionnaireItemStatus | null;
/** Whether the action applies to the active item. */
visible: boolean;
};
```
##### QuestionnaireNextDataAttributes
Data attributes of [Next](#questionnaire).
```typescript
declare namespace QuestionnaireNextDataAttributes {
/** Present when the action applies to the active item. */
const visible: 'data-visible';
/** Present when the action does not apply to the active item. */
const hidden: 'data-hidden';
/** Present when the button is disabled. */
const disabled: 'data-disabled';
/** `Enter` on an enabled, visible Next or Submit. */
const shortcut: 'data-shortcut';
/** The active item's status: `unanswered`, `answered` or `skipped`. */
const status: 'data-status';
}
```
##### QuestionnairePreviousDataAttributes
Data attributes of [Previous](#questionnaire).
```typescript
declare namespace QuestionnairePreviousDataAttributes {
/** Present when the action applies to the active item. */
const visible: 'data-visible';
/** Present when the action does not apply to the active item. */
const hidden: 'data-hidden';
/** Present when the button is disabled. */
const disabled: 'data-disabled';
/** `Enter` on an enabled, visible Next or Submit. */
const shortcut: 'data-shortcut';
/** The active item's status: `unanswered`, `answered` or `skipped`. */
const status: 'data-status';
}
```
##### QuestionnaireProgressDataAttributes
Data attributes of [Progress](#questionnaire).
```typescript
declare namespace QuestionnaireProgressDataAttributes {
/** The 1-based position of the active item. */
const current: 'data-current';
/** The number of enabled items. */
const total: 'data-total';
/** Present when the first item is active. */
const first: 'data-first';
/** Present when the last item is active. */
const last: 'data-last';
}
```
##### QuestionnaireRootDataAttributes
Data attributes of [Root](#questionnaire).
```typescript
declare namespace QuestionnaireRootDataAttributes {
/** The 1-based position of the active item. */
const current: 'data-current';
/** The number of enabled items. */
const total: 'data-total';
/** Present when the first item is active. */
const first: 'data-first';
/** Present when the last item is active. */
const last: 'data-last';
/** The shortcut mode, `letters` or `numbers`, when shortcuts are on. */
const shortcuts: 'data-shortcuts';
}
```
##### QuestionnaireShortcutMode
```typescript
type QuestionnaireShortcutMode = 'letters' | 'numbers';
```
##### QuestionnaireSkipDataAttributes
Data attributes of [Skip](#questionnaire).
```typescript
declare namespace QuestionnaireSkipDataAttributes {
/** Present when the action applies to the active item. */
const visible: 'data-visible';
/** Present when the action does not apply to the active item. */
const hidden: 'data-hidden';
/** Present when the button is disabled. */
const disabled: 'data-disabled';
/** `Enter` on an enabled, visible Next or Submit. */
const shortcut: 'data-shortcut';
/** The active item's status: `unanswered`, `answered` or `skipped`. */
const status: 'data-status';
}
```
##### QuestionnaireSubmitDataAttributes
Data attributes of [Submit](#questionnaire).
```typescript
declare namespace QuestionnaireSubmitDataAttributes {
/** Present when the action applies to the active item. */
const visible: 'data-visible';
/** Present when the action does not apply to the active item. */
const hidden: 'data-hidden';
/** Present when the button is disabled. */
const disabled: 'data-disabled';
/** `Enter` on an enabled, visible Next or Submit. */
const shortcut: 'data-shortcut';
/** The active item's status: `unanswered`, `answered` or `skipped`. */
const status: 'data-status';
}
```
### Day Picker
A calendar that shows one or more months and selects a single day, several days or a range.
#### Demo
##### Tailwind
This example shows how to implement the component using Tailwind CSS.
```svelte
button]:bg-neutral-950 [&>button]:text-white dark:[&>button]:bg-white dark:[&>button]:text-neutral-950',
outside: 'text-neutral-400'
}}
/>
```
##### CSS Modules
This example shows how to implement the component using CSS Modules.
```css
/* index.module.css */
.Root {
position: relative;
font-size: 0.875rem;
color: oklch(14.5% 0 0deg);
@media (prefers-color-scheme: dark) {
color: white;
}
}
.Caption {
display: flex;
align-items: center;
height: 2rem;
padding-inline: 0.25rem;
font-weight: 500;
}
.Nav {
position: absolute;
top: 0;
right: 0;
display: flex;
gap: 0.25rem;
}
.NavButton,
.DayButton {
width: 2.25rem;
height: 2.25rem;
border: 0;
border-radius: 0.375rem;
background: none;
color: inherit;
font: inherit;
@media (hover: hover) {
&:hover {
background-color: oklch(97% 0 0deg);
@media (prefers-color-scheme: dark) {
background-color: oklch(26.9% 0 0deg);
}
}
}
}
.NavButton {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
fill: currentcolor;
&[aria-disabled='true'] {
opacity: 0.4;
}
}
.Chevron {
width: 1rem;
height: 1rem;
}
.Weekday {
width: 2.25rem;
height: 2.25rem;
font-size: 0.75rem;
font-weight: 400;
color: oklch(55.6% 0 0deg);
}
.Day {
padding: 0;
text-align: center;
}
.DayButton {
&:focus-visible {
outline: 2px solid oklch(14.5% 0 0deg);
outline-offset: 1px;
@media (prefers-color-scheme: dark) {
outline-color: white;
}
}
&:disabled {
opacity: 0.4;
}
}
.Today {
font-weight: 600;
}
.Selected > .DayButton {
background-color: oklch(14.5% 0 0deg);
color: white;
@media (prefers-color-scheme: dark) {
background-color: white;
color: oklch(14.5% 0 0deg);
}
}
```
```svelte
```
#### Installation
Day Picker computes dates with [date-fns](https://date-fns.org) and time zones with [@date-fns/tz](https://github.com/date-fns/tz). Both are optional peer dependencies of `@sveltery/base`, so install them next to it:
```bash
npm install date-fns @date-fns/tz
```
Import Day Picker from its subpath. The `@sveltery/base` root entry does not export it.
#### Anatomy
Import the component and use it as a single part:
```svelte title="Anatomy"
```
Day Picker ports [react-day-picker](https://daypicker.dev) v9.8.1 and takes its props. `bind:selected` holds a `Date` in `single` mode, a `Date[]` in `multiple` mode and a `{ from, to }` range in `range` mode. `bind:month` holds the first displayed month. `defaultSelected` and `defaultMonth` set the starting value of an empty binding. The `root`, `chevron`, `dayButton`, `weekNumber` and `footer` snippets replace react-day-picker's `components`.
#### API reference
##### DayPicker
A calendar that shows one or more months and selects a single day, several days or a range. Renders a `` element.
**DayPicker Props:**
| Prop | Type | Default | Description |
| :-------------------- | :-------------------------------------------------------------------------------------------------- | :-------- | :------------------------------------------------------------------------------------------------------------ |
| hidden | `Matcher \| Matcher[]` | - | Days that are not rendered. |
| ISOWeek | `boolean` | - | Use ISO weeks: Monday first, ISO week numbers. |
| autofocus | `boolean` | - | Focus the focus target when the calendar mounts. |
| captionLayout | `CaptionLayout` | `'label'` | Show the caption as a label, or as month and year dropdowns. |
| chevron | `Snippet<[ChevronSnippetProps]>` | - | Replaces the chevron icon in the navigation buttons and dropdowns. |
| classNames | `Partial
` | - | The class of each part and modifier, replacing `defaultClassNames`. |
| dayButton | `Snippet<[DayButtonSnippetProps]>` | - | Replaces the button inside each selectable day. |
| defaultMonth | `Date` | - | The month shown first when `month` is unset. Read once, at mount. Defaults to the month of `today`. |
| defaultSelected | `Date` | - | The day selected first when `selected` is unset. Read once, at mount. |
| dir | `'ltr' \| 'rtl'` | - | The text direction. It also flips the arrow keys. |
| disableNavigation | `boolean` | - | Disable the navigation buttons and the dropdowns. |
| endMonth | `Date` | - | The latest month the user can navigate to. |
| excludeDisabled | `boolean` | - | Restart the range when it would include a disabled day. |
| firstWeekContainsDate | `1 \| 4` | - | The day of January that the first week must contain. Defaults to the locale's. |
| fixedWeeks | `boolean` | - | Always render six weeks per month. |
| footer | `Snippet` | - | Content shown below the months, announced as a live region. |
| formatters | `Partial` | - | Replace the functions that format the caption, days, weekdays and week numbers. |
| hideNavigation | `boolean` | - | Do not render the navigation buttons. |
| hideWeekdays | `boolean` | - | Do not render the weekday header row. |
| labels | `Partial` | - | Replace the functions that build the ARIA labels. |
| locale | `Locale` | - | A date-fns locale. |
| mode | `'single'` | - | How days are selected: one day, several days or a range. Unset, days are not selectable. |
| modifiers | `Record` | - | Custom modifiers. Each name becomes a `Modifiers` key, and the day gets its class from `modifiersClassNames`. |
| modifiersClassNames | `Record` | - | The class added to a day for each custom or built-in modifier. |
| month | `Date` | - | The first displayed month. Use `bind:month` to read or drive it. |
| navLayout | `'around' \| 'after'` | - | `around` puts the buttons around the caption instead of in a shared nav. |
| nonce | `string` | - | - |
| numberOfMonths | `number` | `1` | How many months to show. |
| onDayBlur | `DayEventHandler` | - | Called when a day loses focus. |
| onDayClick | `DayEventHandler` | - | Called when a day is clicked. |
| onDayFocus | `DayEventHandler` | - | Called when a day gains focus. |
| onDayKeyDown | `DayEventHandler` | - | Called on a key press on a day. |
| onDayMouseEnter | `DayEventHandler` | - | Called when the pointer enters a day. |
| onDayMouseLeave | `DayEventHandler` | - | Called when the pointer leaves a day. |
| onMonthChange | `(month: Date) => void` | - | Called after the displayed month changes, with the new first month. |
| onNextClick | `(month: Date) => void` | - | Called when the next button moves the month. |
| onPrevClick | `(month: Date) => void` | - | Called when the previous button moves the month. |
| onSelect | `(selected: Date \| undefined, triggerDate: Date, modifiers: Modifiers, event: MouseEvent) => void` | - | Called after a click changes the selection, with the new selection. |
| pagedNavigation | `boolean` | - | Move by `numberOfMonths` instead of one month. |
| reverseMonths | `boolean` | - | Show the months in reverse order. |
| root | `Snippet<[RootSnippetProps]>` | - | Replaces the root element. Spread `props` onto it and render `props.children` inside. |
| selected | `Date` | - | Use `bind:selected` to read or drive it. |
| showOutsideDays | `boolean` | - | Show the days of the months before and after. |
| showWeekNumber | `boolean` | - | Show a column of week numbers. |
| startMonth | `Date` | - | The earliest month the user can navigate to. |
| timeZone | `string` | - | An IANA time zone, such as `America/New_York`. Days are computed in it. |
| today | `Date` | - | The day marked as today. Defaults to the current date in `timeZone`. |
| weekNumber | `Snippet<[WeekNumberSnippetProps]>` | - | Replaces the week number cell. |
| weekStartsOn | `WeekStartsOn` | - | The first day of the week, `0` for Sunday. Defaults to the locale's. |
| min | `number` | - | The fewest days that can stay selected. |
| max | `number` | - | The most days that can be selected. |
| disabled | `Matcher \| Matcher[]` | - | Days that can't be selected. |
| required | `boolean` | - | Keep a date selected: clicking the selected day does not clear it. |
**DayPicker Data Attributes:**
| Attribute | Type | Description |
| :------------------- | :---------------------------------- | :------------------------------------------------------------------------ |
| data-selected | - | On a day cell. Present when the day is selected. |
| data-disabled | - | On a day cell. Present when the day is disabled. |
| data-required | - | Present when `required` is set on a selection mode. |
| data-day | - | On a day cell. The day's date, as `yyyy-MM-dd`. |
| data-focused | - | On a day cell. Present when the day has focus. |
| data-hidden | - | On a day cell. Present when the day is hidden. |
| data-mode | `'single' \| 'multiple' \| 'range'` | Present when a selection mode is set. |
| data-month | - | On a day cell. The month of an outside day, as `yyyy-MM`. |
| data-multiple-months | - | Present when more than one month is shown. |
| data-nav-layout | `'around' \| 'after'` | Present when `navLayout` is set. |
| data-outside | - | On a day cell. Present when the day belongs to the month before or after. |
| data-today | - | On a day cell. Present when the day is today. |
| data-week-numbers | - | Present when week numbers are shown. |
##### DayPicker.Props
Re-export of [DayPicker](#day-picker) props.
##### DayPicker.State
```typescript
type DayPickerState = {
focused: CalendarDay;
#lastFocused: CalendarDay;
#input: DayPickerInput;
lib: DateLib;
today: Date;
grid: GridOptions;
#nav: [start: Date | undefined, end: Date | undefined];
navStart: Date;
navEnd: Date;
firstMonth: Date;
months: CalendarMonth[];
days: CalendarDay[];
previousMonth: Date;
nextMonth: Date;
selected: Date | Date[] | DateRange | undefined;
isSelected: (date: Date) => boolean;
#getModifiers: (day: CalendarDay) => Modifiers;
/** The day that takes Tab focus when focus enters the grid. */
focusTarget: CalendarDay;
modifiers: (day: CalendarDay) => Modifiers;
isFocusTarget: (day: CalendarDay) => boolean;
goToMonth: (date: Date) => void;
goToPrevious: () => void;
goToNext: () => void;
focus: (day: CalendarDay) => void;
blur: () => void;
moveFocus: (moveBy: MoveFocusBy, moveDir: MoveFocusDir) => void;
select: (day: CalendarDay, modifiers: Modifiers, event: MouseEvent) => void;
};
```
#### Additional Types
##### CalendarDay
A day in the grid. An outside day belongs to the month before or after `displayMonth`.
```typescript
type CalendarDay = {
date: Date;
displayMonth: Date;
outside: boolean;
/** `yyyy-MM-dd`. */
isoDate: string;
/** `yyyy-MM` of `displayMonth`. */
displayMonthId: string;
/** `yyyy-MM` of `date`. */
dateMonthId: string;
};
```
##### CalendarMonth
```typescript
type CalendarMonth = {
/** The first day of the month. */
date: Date;
weeks: CalendarWeek[];
};
```
##### CalendarWeek
```typescript
type CalendarWeek = {
weekNumber: number;
days: CalendarDay[];
};
```
##### CaptionLayout
```typescript
type CaptionLayout = 'label' | 'dropdown' | 'dropdown-months' | 'dropdown-years';
```
##### ChevronSnippetProps
```typescript
type ChevronSnippetProps = {
orientation: 'left' | 'right' | 'up' | 'down';
class: string;
/** Width and height in pixels. Set to `18` in the caption dropdowns, otherwise unset for a 24px icon. */
size?: number;
/** Set on the chevron of a disabled navigation button. */
disabled?: true;
};
```
##### ClassNames
```typescript
type ClassNames = {};
```
##### DateAfter
Matches the days after `after`.
```typescript
type DateAfter = {
after: Date;
};
```
##### DateBefore
Matches the days before `before`.
```typescript
type DateBefore = {
before: Date;
};
```
##### DateInterval
Matches the days between `after` and `before`, both excluded. When `after` is later than `before`, matches the days outside them.
```typescript
type DateInterval = {
before: Date;
after: Date;
};
```
##### DateLibLike
The formatting surface formatters and labels receive. `DateLib` implements it.
```typescript
type DateLibLike = {
options: DateLibOptions;
format: (date: Date, pattern: string) => string;
};
```
##### DateLibOptions
```typescript
type DateLibOptions = {
/** A date-fns locale. Defaults to `enUS`. */
locale?: Locale;
weekStartsOn?: WeekStartsOn;
firstWeekContainsDate?: 1 | 4;
/** An IANA time zone. Dates are created and compared in it. */
timeZone?: string;
};
```
##### DateRange
A range of dates. `to` is unset while the user has picked only the start.
```typescript
type DateRange = {
from: Date | undefined;
to?: Date;
};
```
##### DayButtonSnippetProps
```typescript
type DayButtonSnippetProps = {
day: CalendarDay;
modifiers: Modifiers;
/** Spread onto the button. Holds the handlers, ARIA, `tabindex` and the attachment that moves focus to it. */
props: Omit & { disabled: true | undefined };
/** The formatted day, such as `12`. */
text: string;
};
```
##### DayEventHandler
```typescript
type DayEventHandler = (date: Date, modifiers: Modifiers, event: E) => void;
```
##### DayFlag
```typescript
type DayFlag = 'disabled' | 'hidden' | 'outside' | 'focused' | 'today';
```
##### DayOfWeek
Matches the days of the week, `0` for Sunday to `6` for Saturday.
```typescript
type DayOfWeek = {
dayOfWeek: number | number[];
};
```
##### DayPickerDataAttributes
Data attributes of [DayPicker](#day-picker).
```typescript
declare namespace DayPickerDataAttributes {
/**
* Present when a selection mode is set.
* @type 'single' | 'multiple' | 'range'
*/
const mode: 'data-mode';
/** Present when `required` is set on a selection mode. */
const required: 'data-required';
/** Present when more than one month is shown. */
const multipleMonths: 'data-multiple-months';
/** Present when week numbers are shown. */
const weekNumbers: 'data-week-numbers';
/**
* Present when `navLayout` is set.
* @type 'around' | 'after'
*/
const navLayout: 'data-nav-layout';
/** On a day cell. The day's date, as `yyyy-MM-dd`. */
const day: 'data-day';
/** On a day cell. The month of an outside day, as `yyyy-MM`. */
const month: 'data-month';
/** On a day cell. Present when the day is selected. */
const selected: 'data-selected';
/** On a day cell. Present when the day is disabled. */
const disabled: 'data-disabled';
/** On a day cell. Present when the day is hidden. */
const hidden: 'data-hidden';
/** On a day cell. Present when the day belongs to the month before or after. */
const outside: 'data-outside';
/** On a day cell. Present when the day has focus. */
const focused: 'data-focused';
/** On a day cell. Present when the day is today. */
const today: 'data-today';
}
```
##### DropdownOption
```typescript
type DropdownOption = {
value: number;
label: string;
disabled: boolean;
};
```
##### Formatters
```typescript
type Formatters = {
formatCaption: (month: Date, lib: DateLibLike) => string;
formatDay: (date: Date, lib: DateLibLike) => string;
formatMonthDropdown: (month: Date, lib: DateLibLike) => string;
formatYearDropdown: (year: Date, lib: DateLibLike) => string;
formatWeekdayName: (weekday: Date, lib: DateLibLike) => string;
formatWeekNumber: (weekNumber: number, lib: DateLibLike) => string;
formatWeekNumberHeader: () => string;
};
```
##### GridOptions
The options that shape the grid and its navigation.
```typescript
type GridOptions = {
numberOfMonths?: number;
fixedWeeks?: boolean;
ISOWeek?: boolean;
/** The earliest month the user can navigate to. */
startMonth?: Date;
/** The latest month the user can navigate to. */
endMonth?: Date;
captionLayout?: CaptionLayout;
pagedNavigation?: boolean;
reverseMonths?: boolean;
disableNavigation?: boolean;
today?: Date;
};
```
##### Labels
```typescript
type Labels = {
labelDayButton: (date: Date, modifiers: Modifiers, lib: DateLibLike) => string;
labelGridcell: (date: Date, modifiers: Modifiers, lib: DateLibLike) => string;
labelGrid: (month: Date, lib: DateLibLike) => string;
labelMonthDropdown: () => string;
labelYearDropdown: () => string;
labelNav: () => string;
labelNext: (month: Date | undefined) => string;
labelPrevious: (month: Date | undefined) => string;
labelWeekday: (weekday: Date, lib: DateLibLike) => string;
labelWeekNumber: (weekNumber: number) => string;
labelWeekNumberHeader: () => string;
};
```
##### Matcher
Describes a set of days, as in react-day-picker's `disabled`, `hidden` and `modifiers` props.
```typescript
type Matcher =
| boolean
| ((date: Date) => boolean)
| Date
| Date[]
| DateRange
| DateBefore
| DateAfter
| DateInterval
| DayOfWeek;
```
##### Mode
```typescript
type Mode = 'single' | 'multiple' | 'range';
```
##### Modifiers
What applies to a day: the built-in flags, the selection states and the custom `modifiers`. `selected` is set only on a selected day, and the range states only while `selected` is a range.
```typescript
type Modifiers = {};
```
##### MoveFocusBy
```typescript
type MoveFocusBy = 'day' | 'week' | 'month' | 'year' | 'startOfWeek' | 'endOfWeek';
```
##### MoveFocusDir
```typescript
type MoveFocusDir = 'before' | 'after';
```
##### MultipleProps
```typescript
type MultipleProps = {
mode: 'multiple';
selected?: Date[];
/** The days selected first when `selected` is unset. Read once, at mount. */
defaultSelected?: Date[];
required?: boolean;
/** The fewest days that can stay selected. */
min?: number;
/** The most days that can be selected. */
max?: number;
onSelect?: (
selected: Date[] | undefined,
triggerDate: Date,
modifiers: Modifiers,
event: MouseEvent
) => void;
};
```
##### NoModeProps
```typescript
type NoModeProps = {
mode?: undefined;
selected?: undefined;
defaultSelected?: undefined;
onSelect?: undefined;
};
```
##### RangeProps
```typescript
type RangeProps = {
mode: 'range';
selected?: DateRange;
/** The range selected first when `selected` is unset. Read once, at mount. */
defaultSelected?: DateRange;
required?: boolean;
/** The fewest nights a range spans. */
min?: number;
/** The most nights a range spans. */
max?: number;
/** Restart the range when it would include a disabled day. */
excludeDisabled?: boolean;
onSelect?: (
selected: DateRange | undefined,
triggerDate: Date,
modifiers: Modifiers,
event: MouseEvent
) => void;
};
```
##### RootSnippetProps
```typescript
type RootSnippetProps = {
/** Spread onto the root element. `children` renders the months and the footer. */
props: HTMLAttributes & { children: Snippet };
};
```
##### SelectionState
```typescript
type SelectionState = 'selected' | 'range_start' | 'range_middle' | 'range_end';
```
##### SingleProps
```typescript
type SingleProps = {
/** How days are selected: one day, several days or a range. Unset, days are not selectable. */
mode: 'single';
/** Use `bind:selected` to read or drive it. */
selected?: Date;
/** The day selected first when `selected` is unset. Read once, at mount. */
defaultSelected?: Date;
/** Keep a date selected: clicking the selected day does not clear it. */
required?: boolean;
/** Called after a click changes the selection, with the new selection. */
onSelect?: (
selected: Date | undefined,
triggerDate: Date,
modifiers: Modifiers,
event: MouseEvent
) => void;
};
```
##### UI
The element keys of `classNames`, with react-day-picker's names.
```typescript
type UI =
| 'root'
| 'chevron'
| 'day'
| 'day_button'
| 'caption_label'
| 'dropdowns'
| 'dropdown'
| 'dropdown_root'
| 'footer'
| 'month_grid'
| 'month_caption'
| 'months_dropdown'
| 'month'
| 'months'
| 'nav'
| 'button_next'
| 'button_previous'
| 'week'
| 'weeks'
| 'weekday'
| 'weekdays'
| 'week_number'
| 'week_number_header'
| 'years_dropdown';
```
##### WeekNumberSnippetProps
```typescript
type WeekNumberSnippetProps = {
week: CalendarWeek;
/** Spread onto the cell. */
props: HTMLThAttributes;
/** The formatted week number, such as `07`. */
text: string;
};
```
##### WeekStartsOn
```typescript
type WeekStartsOn = 0 | 1 | 2 | 3 | 4 | 5 | 6;
```
### Message Scroller
A chat transcript that follows new messages, anchors each turn and keeps the reader's place.
#### Demo
##### Tailwind
This example shows how to implement the component using Tailwind CSS.
```svelte
{#each messages as message (message.id)}
{message.text}
{/each}
Ask
```
##### CSS Modules
This example shows how to implement the component using CSS Modules.
```css
/* index.module.css */
.Container {
display: flex;
flex-direction: column;
gap: 0.75rem;
width: 24rem;
max-width: calc(100vw - 8rem);
}
.Root {
position: relative;
height: 12rem;
border: 1px solid oklch(87% 0 0deg);
border-radius: 0.375rem;
background-color: white;
@media (prefers-color-scheme: dark) {
border-color: oklch(37.1% 0 0deg);
background-color: oklch(14.5% 0 0deg);
}
}
.Viewport {
height: 100%;
overflow-y: auto;
border-radius: inherit;
&[data-pending-scroll] {
visibility: hidden;
}
&:focus-visible {
outline: 2px solid oklch(14.5% 0 0deg);
outline-offset: -1px;
@media (prefers-color-scheme: dark) {
outline-color: white;
}
}
}
.Content {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.75rem;
}
.Item {
max-width: 80%;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
font-size: 0.875rem;
line-height: 1.25rem;
color: oklch(14.5% 0 0deg);
background-color: oklch(97% 0 0deg);
@media (prefers-color-scheme: dark) {
color: white;
background-color: oklch(26.9% 0 0deg);
}
&[data-author='user'] {
align-self: flex-end;
color: white;
background-color: oklch(14.5% 0 0deg);
@media (prefers-color-scheme: dark) {
color: oklch(14.5% 0 0deg);
background-color: white;
}
}
}
.Button {
position: absolute;
bottom: 0.5rem;
left: 50%;
translate: -50% 0;
padding: 0.25rem 0.75rem;
border: 1px solid oklch(87% 0 0deg);
border-radius: 9999px;
font-size: 0.75rem;
color: oklch(14.5% 0 0deg);
background-color: white;
transition: opacity 150ms;
&[data-active='false'] {
opacity: 0;
}
@media (prefers-color-scheme: dark) {
border-color: oklch(37.1% 0 0deg);
color: white;
background-color: oklch(14.5% 0 0deg);
}
}
.Ask {
align-self: flex-start;
padding: 0.375rem 0.875rem;
border: 1px solid oklch(87% 0 0deg);
border-radius: 0.375rem;
font-size: 0.875rem;
color: oklch(14.5% 0 0deg);
background-color: oklch(98.5% 0 0deg);
@media (prefers-color-scheme: dark) {
border-color: oklch(37.1% 0 0deg);
color: white;
background-color: oklch(20.5% 0 0deg);
}
}
```
```svelte
{#each messages as message (message.id)}
{message.text}
{/each}
Ask
```
Message Scroller ports [`@shadcn/react`](https://github.com/shadcn-ui/ui/tree/main/packages/react)'s message scroller. It follows that package's behavior, not Base UI's.
#### Anatomy
Import the component and assemble its parts:
```svelte title="Anatomy"
```
#### Examples
##### Anchoring turns
Set `scrollAnchor` on the row that starts a turn, such as the user's message. When a new anchored row arrives, the viewport places it near the top, with `scrollPreviousItemPeek` pixels of the previous row still visible, and holds it there while the reply streams in below. With `autoScroll`, the viewport follows the end again once the reply fills the space below the anchor.
##### Avoiding a flash on reload
For the `'end'` and `'last-anchor'` opening positions, the root and the viewport have `data-pending-scroll` until the position is applied. Hide the viewport with `visibility: hidden` while it is set, so the layout still exists for the opening scroll:
```css title="Pending scroll"
.Viewport[data-pending-scroll] {
visibility: hidden;
}
```
##### Reading scroll state
`getMessageScroller()` returns the scroller of the closest provider. Call it while a component initializes. Its `scrollable` and `visibility` getters are reactive. Visibility is tracked only while something reads it.
```svelte title="Outline"
Current turn: {scroller.visibility.currentAnchorId ?? 'none'}
scroller.scrollToMessage('m1', { behavior: 'smooth' })}>
Back to the first message
```
#### API reference
##### Root
The frame around the viewport. It mirrors the viewport's scroll-state attributes. Renders a `` element.
**Root Data Attributes:**
| Attribute | Type | Description |
| :------------------ | :-------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| data-autoscrolling | - | Present while a programmatic scroll to the end runs. |
| data-pending-scroll | - | Present until the `'end'` or `'last-anchor'` opening position is applied. Hide the viewport with `visibility: hidden` while it is set. |
| data-scrollable | `'start' \| 'end' \| 'start end'` | The edges the viewport can scroll toward: `start`, `end` or `start end`. Absent when the content fits. Match one edge with `[data-scrollable~="end"]`. |
##### Root.Props
Re-export of [Root](#message-scroller) props.
##### Provider
Owns the scroll state and the behavior props, and provides them to the parts and `getMessageScroller()`. Doesn't render its own HTML element.
**Provider Props:**
| Prop | Type | Default | Description |
| :--------------------- | :------------------------------------- | :------ | :------------------------------------------------------------------------- |
| autoScroll | `boolean` | `false` | Follow new content at the bottom while the viewport is already at the end. |
| defaultScrollPosition | `MessageScrollerDefaultScrollPosition` | `'end'` | Opening position on the first non-empty render, applied once. |
| scrollEdgeThreshold | `number` | `8` | Distance from an edge that still counts as at the start or end. |
| scrollMargin | `number` | `0` | Default margin on the aligned edge for commands and visibility. |
| scrollPreviousItemPeek | `number` | `64` | Extra top margin for a newly anchored row, added to `scrollMargin`. |
##### Provider.Props
Re-export of [Provider](#message-scroller) props.
##### Item
One transcript row: a message, marker, typing row, separator or load-more row. Renders a `
` element.
**Item Props:**
| Prop | Type | Default | Description |
| :----------- | :-------- | :------ | :------------------------------------------------------------------------------- |
| messageId | `string` | - | Stable row id for `scrollToMessage`, visibility and prepend preservation. |
| scrollAnchor | `boolean` | `false` | Marks a turn boundary that newly appended anchors and `last-anchor` restore use. |
**Item Data Attributes:**
| Attribute | Type | Description |
| :----------------- | :------------------ | :----------------------------------- |
| data-message-id | - | The item's `messageId`. |
| data-scroll-anchor | `'true' \| 'false'` | Whether the item is a turn boundary. |
##### Item.Props
Re-export of [Item](#message-scroller) props.
##### Content
The transcript log. Every direct child should be a `MessageScroller.Item`. Renders a `
` element.
**Content Props:**
| Prop | Type | Default | Description |
| :---------- | :------- | :------ | :------------------------------------------------------------------------- |
| spacerClass | `string` | - | Class for the internal tail spacer used when anchoring turns near the top. |
##### Content.Props
Re-export of [Content](#message-scroller) props.
##### Viewport
The scrollable, focusable transcript region. Renders a `
` element.
**Viewport Props:**
| Prop | Type | Default | Description |
| :---------------------- | :-------- | :------ | :--------------------------------------------------------------------- |
| preserveScrollOnPrepend | `boolean` | `true` | Keep the first visible `messageId` row stable when rows are prepended. |
**Viewport Data Attributes:**
| Attribute | Type | Description |
| :------------------ | :-------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| data-autoscrolling | - | Present while a programmatic scroll to the end runs. |
| data-pending-scroll | - | Present until the `'end'` or `'last-anchor'` opening position is applied. Hide the viewport with `visibility: hidden` while it is set. |
| data-scrollable | `'start' \| 'end' \| 'start end'` | The edges the viewport can scroll toward: `start`, `end` or `start end`. Absent when the content fits. Match one edge with `[data-scrollable~="end"]`. |
##### Viewport.Props
Re-export of [Viewport](#message-scroller) props.
##### Button
Scrolls to the start or end of the transcript. It is inert and out of the tab order while there is nothing to scroll toward. Renders a `
` element.
**Button Props:**
| Prop | Type | Default | Description |
| :-------- | :-------------------------------------------------------------------------- | :--------- | :--------------------------------------------------------------------- |
| behavior | `ScrollBehavior` | `'smooth'` | Native scroll behavior when clicked. |
| direction | `MessageScrollerButtonDirection` | `'end'` | Transcript edge to scroll toward. |
| render | `Snippet<[props: HTMLButtonAttributes, state: MessageScrollerButtonState]>` | - | Replaces the default ``. Spread `props` onto the host element. |
**Button Data Attributes:**
| Attribute | Type | Description |
| :---------- | :------------------ | :--------------------------------------------- |
| data-active | `'true' \| 'false'` | Whether the button can scroll toward its edge. |
##### Button.Props
Re-export of [Button](#message-scroller) props.
##### Button.State
```typescript
type MessageScrollerButtonState = {
/** Whether content is hidden toward this button's direction. */
active: boolean;
direction: MessageScrollerButtonDirection;
};
```
#### Additional Types
##### MessageScrollerButtonDataAttributes
Data attributes of [Button](#message-scroller).
```typescript
declare namespace MessageScrollerButtonDataAttributes {
/**
* Whether the button can scroll toward its edge.
* @type 'true' | 'false'
*/
const active: 'data-active';
}
```
##### MessageScrollerButtonDirection
Which transcript edge a scroll button scrolls toward.
```typescript
type MessageScrollerButtonDirection = 'start' | 'end';
```
##### MessageScrollerDefaultScrollPosition
Where a saved transcript opens on the first non-empty render.
```typescript
type MessageScrollerDefaultScrollPosition = 'start' | 'end' | 'last-anchor';
```
##### MessageScrollerItemDataAttributes
Data attributes of [Item](#message-scroller).
```typescript
declare namespace MessageScrollerItemDataAttributes {
/** The item's `messageId`. */
const messageId: 'data-message-id';
/**
* Whether the item is a turn boundary.
* @type 'true' | 'false'
*/
const scrollAnchor: 'data-scroll-anchor';
}
```
##### MessageScrollerRootDataAttributes
Data attributes of [Root](#message-scroller).
```typescript
declare namespace MessageScrollerRootDataAttributes {
/**
* The edges the viewport can scroll toward: `start`, `end` or `start end`. Absent when the content fits. Match one edge with `[data-scrollable~="end"]`.
* @type 'start' | 'end' | 'start end'
*/
const scrollable: 'data-scrollable';
/** Present while a programmatic scroll to the end runs. */
const autoscrolling: 'data-autoscrolling';
/** Present until the `'end'` or `'last-anchor'` opening position is applied. Hide the viewport with `visibility: hidden` while it is set. */
const pendingScroll: 'data-pending-scroll';
}
```
##### MessageScrollerScrollable
Which edges the viewport can still scroll toward.
```typescript
type MessageScrollerScrollable = {
/** Content is hidden above. */
start: boolean;
/** Content is hidden below. */
end: boolean;
};
```
##### MessageScrollerScrollAlign
```typescript
type MessageScrollerScrollAlign = 'start' | 'center' | 'end' | 'nearest';
```
##### MessageScrollerScrollOptions
```typescript
type MessageScrollerScrollOptions = {
/** Viewport edge or center to align the target to. */
align?: MessageScrollerScrollAlign;
/** Native scroll behavior. */
behavior?: ScrollBehavior;
/** Margin on the aligned edge, in pixels. Defaults to the provider `scrollMargin`. */
scrollMargin?: number;
};
```
##### MessageScrollerViewportDataAttributes
Data attributes of [Viewport](#message-scroller).
```typescript
declare namespace MessageScrollerViewportDataAttributes {
/**
* The edges the viewport can scroll toward: `start`, `end` or `start end`. Absent when the content fits. Match one edge with `[data-scrollable~="end"]`.
* @type 'start' | 'end' | 'start end'
*/
const scrollable: 'data-scrollable';
/** Present while a programmatic scroll to the end runs. */
const autoscrolling: 'data-autoscrolling';
/** Present until the `'end'` or `'last-anchor'` opening position is applied. Hide the viewport with `visibility: hidden` while it is set. */
const pendingScroll: 'data-pending-scroll';
}
```
##### MessageScrollerVisibilityState
```typescript
type MessageScrollerVisibilityState = {
/** The anchored turn the reader is in, or null. Stays set after the anchor scrolls above the viewport. */
currentAnchorId: string | null;
/** `messageId` values intersecting the viewport, in document order. */
visibleMessageIds: string[];
};
```
### Resizable
Panels that the user resizes by dragging the separators between them or with the keyboard.
#### Demo
##### Tailwind
This example shows how to implement the component using Tailwind CSS.
```svelte
Sidebar
Content
```
##### CSS Modules
This example shows how to implement the component using CSS Modules.
```css
/* index.module.css */
.Container {
width: 24rem;
max-width: 100%;
height: 12rem;
border: 1px solid oklch(87% 0 0deg);
border-radius: 0.375rem;
@media (prefers-color-scheme: dark) {
border-color: oklch(37.1% 0 0deg);
}
}
.Panel {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 0.875rem;
color: oklch(14.5% 0 0deg);
@media (prefers-color-scheme: dark) {
color: white;
}
}
.Separator {
width: 1px;
background-color: oklch(87% 0 0deg);
outline: 0;
@media (prefers-color-scheme: dark) {
background-color: oklch(37.1% 0 0deg);
}
&[data-separator='hover'],
&[data-separator='active'],
&:focus-visible {
background-color: oklch(55.6% 0 0deg);
}
}
```
```svelte
Sidebar
Content
```
Resizable ports [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) 4.5.8. Its layout engine is copied from upstream, and the parts are Svelte rewrites of its React components.
#### Anatomy
Import the component and assemble its parts:
```svelte title="Anatomy"
```
Panels and separators must be direct children of their group's element.
#### Examples
##### Sizes
`defaultSize`, `minSize`, `maxSize` and `collapsedSize` take a number of pixels or a string with a unit: `%`, `px`, `em`, `rem`, `vh` or `vw`. A string without a unit is a percentage.
```svelte title="Sizes"
Header
Body
```
##### Collapsible panels and methods
A `collapsible` panel collapses when it is resized below its `minSize`, and Enter on the separator toggles it. `bind:this` on a panel gives `collapse()`, `expand()`, `getSize()`, `isCollapsed()` and `resize()`. `bind:this` on a group gives `getLayout()` and `setLayout()`.
```svelte title="Panel methods"
(sidebar?.isCollapsed() ? sidebar.expand() : sidebar?.collapse())}>
Toggle sidebar
Sidebar
Content
```
##### Saving the layout
`useDefaultLayout` reads a saved layout from `localStorage`, or any storage with `getItem` and `setItem`, and returns callbacks that save the layout when it settles. Call it while the component initializes. Give each panel an `id`, so the saved sizes match their panels.
```svelte title="Persistence"
Files
Editor
```
##### Reading an element
Put an attachment on a part to reach its element. The group, the separator and the outer element of a panel take it.
```svelte title="Attachment"
console.log(element.clientWidth)}>
One
Two
```
#### Keyboard
A focused separator resizes the panel before it. The arrow keys along the group's orientation move it by 5%. Home and End move it to its limits, and Enter collapses or expands a collapsible panel.
#### API reference
##### Panel
Wraps resizable content, with optional size constraints and collapsible behavior. Renders a `` element.
**Panel Props:**
| Prop | Type | Default | Description |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------ | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| collapsedSize | `number \| string \| undefined` | `'0%'` | The panel's size when collapsed. |
| collapsible | `boolean \| undefined` | `false` | Whether the panel collapses when it is resized below its `minSize`. |
| defaultSize | `number \| string \| undefined` | - | The size the panel starts with. Without it, the group shares out the free space. |
| maxSize | `number \| string \| undefined` | `'100%'` | The largest size of the panel. |
| minSize | `number \| string \| undefined` | `'0%'` | The smallest size of the panel. |
| onResize | `((panelSize: ResizablePanelSize, id: string \| number \| undefined, prevPanelSize: ResizablePanelSize \| undefined) => void) \| undefined` | - | Called when the panel's size changes, with the new size, the `id` prop and the previous size, which is `undefined` on mount. |
| id | `string \| number \| undefined` | - | Identifies the panel within its group. Falls back to a generated id. Saved layouts use it to match a size to its panel. The element also takes it as `data-testid`. |
| class | `string \| undefined` | - | CSS class for the inner element that holds the content, so the class does not interfere with the flex layout. |
| style | `string \| null \| undefined` | - | Inline styles for the inner element that holds the content. |
**Panel Data Attributes:**
| Attribute | Type | Description |
| :---------- | :------- | :---------------------------------- |
| data-panel | - | Present on every panel. |
| data-testid | `string` | The panel's `id`, for test queries. |
##### Panel.Props
Re-export of [Panel](#resizable) props.
##### Group
Wraps a set of resizable panels, laid out horizontally or vertically. Renders a `
` element.
**Group Props:**
| Prop | Type | Default | Description |
| :---------------------- | :------------------------------------------------- | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| defaultLayout | `ResizableLayout \| undefined` | - | The layout the group starts with, such as one `useDefaultLayout` restored. Read once at mount. |
| disableCursor | `boolean \| undefined` | `false` | Whether to leave the cursor alone. By default, the group sets the cursor to show the resize direction while the pointer hovers or drags an edge. |
| onLayoutChange | `((layout: ResizableLayout) => void) \| undefined` | - | Called while the layout changes. A pointer drag calls it on every move, so prefer `onLayoutChanged` for most cases. |
| onLayoutChanged | `((layout: ResizableLayout) => void) \| undefined` | - | Called once the layout has changed. A pointer drag calls it when the pointer is released. Save layouts from this callback. |
| resizeTargetMinimumSize | `{ coarse: number; fine: number; }` | `{ coarse: 20, fine: 10 }` | The minimum size, in pixels, of the area that starts a resize around a separator or a panel edge, for coarse (touch) and fine (mouse) pointers. |
| disabled | `boolean \| undefined` | `false` | Whether the panels of the group cannot be resized. |
| orientation | `ResizableOrientation \| undefined` | `'horizontal'` | The direction the panels are laid out and resized in. |
| id | `string \| number \| undefined` | - | Identifies the group within the application. Falls back to a generated id. The element also takes it as `data-testid`. |
| children | `Snippet` | - | The panels and separators of the group. |
| style | `string \| null \| undefined` | - | Inline styles for the group element. `display`, `flex-direction`, `flex-wrap` and `overflow` cannot be overridden. |
**Group Data Attributes:**
| Attribute | Type | Description |
| :---------- | :------- | :---------------------------------- |
| data-group | - | Present on every group. |
| data-testid | `string` | The group's `id`, for test queries. |
##### Group.Props
Re-export of [Group](#resizable) props.
##### Separator
A focusable handle between two panels that resizes them with the pointer and the keyboard. Renders a `
` element.
**Separator Props:**
| Prop | Type | Default | Description |
| :---- | :------------------------------ | :------ | :------------------------------------------------------------------------------------------------------------------- |
| id | `string \| number \| undefined` | - | Identifies the separator within its group. Falls back to a generated id. The element also takes it as `data-testid`. |
| style | `string \| null \| undefined` | - | Inline styles for the separator. `flex-grow` and `flex-shrink` cannot be overridden. |
**Separator Data Attributes:**
| Attribute | Type | Description |
| :------------- | :---------------------------------- | :--------------------------------------------------------------------------------------------------------------- |
| data-separator | `'inactive' \| 'hover' \| 'active'` | Whether the pointer is over the separator's hit area (`hover`), dragging it (`active`), or neither (`inactive`). |
| data-testid | `string` | The separator's `id`, for test queries. |
##### Separator.Props
Re-export of [Separator](#resizable) props.
#### Additional Types
##### ResizableGroupActions
```typescript
type ResizableGroupActions = {
/** Get the Group's current layout as a map of Panel id to percentage (0..100) */
getLayout: () => { [panelId: string]: number };
/** Set a new layout for the Group */
setLayout: (layout: { [panelId: string]: number }) => { [id: string]: number };
};
```
##### ResizableGroupDataAttributes
Data attributes of [Group](#resizable).
```typescript
declare namespace ResizableGroupDataAttributes {
/** Present on every group. */
const group: 'data-group';
/**
* The group's `id`, for test queries.
* @type string
*/
const testid: 'data-testid';
}
```
##### ResizableLayout
Map of panel id to its size, as a percentage (0..100) of the group.
```typescript
type ResizableLayout = {
[id: string]: number;
};
```
##### ResizableLayoutStorage
The storage `useDefaultLayout` reads and writes, such as `localStorage`.
```typescript
type ResizableLayoutStorage = {};
```
##### ResizableOrientation
How a group lays out its panels and the direction they resize in. It loosely relates to the `aria-orientation` attribute.
```typescript
type ResizableOrientation = Orientation;
```
##### ResizablePanelActions
```typescript
type ResizablePanelActions = {
/**
* Collapse the Panel to its `collapsedSize`.
*
* ⚠️ This method will do nothing if the Panel is not `collapsible` or if it is already collapsed.
*/
collapse: () => void;
/**
* Expand a collapsed Panel to its most recent size.
*
* ⚠️ This method will do nothing if the Panel is not currently collapsed.
*/
expand: () => void;
/** Get the current size of the Panel in pixels as well as a percentage of the parent group (0..100). */
getSize: () => { asPercentage: number; inPixels: number };
/** The Panel is currently collapsed. */
isCollapsed: () => boolean;
/**
* Update the Panel's size.
*
* Size can be in the following formats:
* - Percentage of the parent Group (0..100)
* - Pixels
* - Relative font units (em, rem)
* - Viewport relative units (vh, vw)
*
* ℹ️ Numeric values are assumed to be pixels. Strings without explicit units are assumed to be percentages (0%..100%). Percentages may also be specified as strings ending with "%" (e.g. "33%") Pixels may also be specified as strings ending with the unit "px". Other units should be specified as strings ending with their CSS property units (e.g. 1rem, 50vh)
*/
resize: (size: number | string) => void;
};
```
##### ResizablePanelDataAttributes
Data attributes of [Panel](#resizable).
```typescript
declare namespace ResizablePanelDataAttributes {
/** Present on every panel. */
const panel: 'data-panel';
/**
* The panel's `id`, for test queries.
* @type string
*/
const testid: 'data-testid';
}
```
##### ResizablePanelSize
A panel's size as a percentage of its group and in pixels.
```typescript
type ResizablePanelSize = {
asPercentage: number;
inPixels: number;
};
```
##### ResizableSeparatorDataAttributes
Data attributes of [Separator](#resizable).
```typescript
declare namespace ResizableSeparatorDataAttributes {
/**
* Whether the pointer is over the separator's hit area (`hover`), dragging it (`active`), or neither (`inactive`).
* @type 'inactive' | 'hover' | 'active'
*/
const separator: 'data-separator';
/**
* The separator's `id`, for test queries.
* @type string
*/
const testid: 'data-testid';
}
```
##### ResizableSizeUnit
The CSS units a panel size can use.
```typescript
type ResizableSizeUnit = SizeUnit;
```
##### UseDefaultLayoutOptions
```typescript
type UseDefaultLayoutOptions = {
/**
* Debounce save operation by the specified number of milliseconds; defaults to 100ms
*
* @deprecated Use the `onLayoutChanged` callback instead; it does not require debouncing
*/
debounceSaveMs?: number;
/**
* For Groups that contain conditionally-rendered Panels, this prop can be used to save and restore multiple layouts.
*
* ℹ️ This prevents layout shift for server-rendered apps.
*
* ⚠️ Panel ids must match the Panels rendered within the Group during mount or the initial layout will be incorrect.
*/
panelIds?: string[] | undefined;
/** Storage implementation; supports localStorage, sessionStorage, and custom implementations. */
storage?: ResizableLayoutStorage;
} & (
| {
/**
* Group id; must be unique in order for layouts to be saved separately.
* @deprecated Use the `id` param instead
*/
groupId: string;
}
| {
/** Uniquely identifies a specific group/layout. */
id: string;
}
);
```
#### External Types
##### Orientation
```typescript
type Orientation = 'horizontal' | 'vertical';
```
##### SizeUnit
```typescript
type SizeUnit = 'px' | '%' | 'em' | 'rem' | 'vh' | 'vw';
```
### Input OTP
A one-time password input that types into one input and draws each character in its own slot.
#### Demo
##### Tailwind
This example shows how to implement the component using Tailwind CSS.
```svelte
{#snippet render({ slots })}
{#each slots as slot, index (index)}
{slot.char ?? slot.placeholderChar}
{#if slot.hasFakeCaret}
{/if}
{/each}
{/snippet}
```
##### CSS Modules
This example shows how to implement the component using CSS Modules.
```css
/* index.module.css */
.Container {
display: flex;
align-items: center;
&:has(:disabled) {
opacity: 0.5;
}
}
.Slots {
display: flex;
align-items: center;
gap: 0.5rem;
}
.Slot {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 2.5rem;
height: 2.5rem;
border: 1px solid oklch(14.5% 0 0deg);
font-size: 1rem;
line-height: 1.5rem;
background-color: white;
color: oklch(14.5% 0 0deg);
@media (prefers-color-scheme: dark) {
border-color: white;
background-color: oklch(14.5% 0 0deg);
color: white;
}
&[data-active] {
outline: 2px solid oklch(14.5% 0 0deg);
outline-offset: -1px;
@media (prefers-color-scheme: dark) {
outline-color: white;
}
}
}
.Caret {
width: 1px;
height: 1.25rem;
background-color: currentcolor;
animation: blink 1s step-end infinite;
}
@keyframes blink {
50% {
opacity: 0;
}
}
```
```svelte
{#snippet render({ slots })}
{#each slots as slot, index (index)}
{slot.char ?? slot.placeholderChar}
{#if slot.hasFakeCaret}
{/if}
{/each}
{/snippet}
```
InputOTP ports [input-otp](https://github.com/guilhermerodz/input-otp) 1.4.2, which Sveltery UI's input OTP is built on. One real `
` sits over the slots, so typing, paste, selection and autofill are the browser's own. For the Base UI part with one input per slot, see [OTP Field](#otp-field).
#### Anatomy
Import the component and draw the slots from its state:
```svelte title="Anatomy"
{#snippet render({ slots })}
{#each slots as slot}
{slot.char}
{/each}
{/snippet}
```
#### Examples
##### Slots as children
Without `render`, `children` renders. A slot component reads the state with `getInputOTPContext()`:
```svelte title="Slot.svelte"
{slot.char}
```
##### Patterns
`pattern` drops input that doesn't match. `REGEXP_ONLY_DIGITS`, `REGEXP_ONLY_CHARS` and `REGEXP_ONLY_DIGITS_AND_CHARS` are exported:
```svelte title="Letters and digits"
```
##### Reading the input
Rest props go to the input, attachments included:
```svelte title="Focus on mount"
input.focus()}>
```
#### API reference
##### Root
Holds the slots and the input that types into them. Rest props go to the input. Renders a `
` element.
**Root Props:**
| Prop | Type | Default | Description |
| :-------------------------- | :------------------------------------ | :----------------- | :---------------------------------------------------------------------------------------------------------------- |
| defaultValue | `string` | `''` | The code an empty `value` starts from. |
| value | `string` | - | The code. |
| containerClass | `ClassValue \| null` | - | Class for the container that holds the slots and the input. |
| maxlength | `number` | - | The number of slots. |
| noScriptCSSFallback | `string \| null` | - | CSS that shows the plain input when JavaScript is off. `null` leaves it out. |
| onComplete | `(value: string) => void` | - | Called with the value when the last slot fills. |
| pasteTransformer | `(pasted: string) => string` | - | Rewrites pasted text before it is inserted. |
| pattern | `string \| RegExp` | - | Every value must match it, for example `REGEXP_ONLY_DIGITS`. Input that doesn't match is dropped. |
| placeholder | `string` | - | Characters shown in the empty slots, one per slot, through `placeholderChar`. |
| pushPasswordManagerStrategy | `'increase-width' \| 'none'` | `'increase-width'` | Widens the input so a password manager's badge sits outside the slots. |
| textAlign | `'left' \| 'center' \| 'right'` | `'left'` | The text alignment of the input. |
| render | `Snippet<[state: InputOTPRootState]>` | - | Renders the slots from the state. Without it, `children` renders and reads the state with `getInputOTPContext()`. |
**Root Data Attributes:**
| Attribute | Type | Description |
| :------------------------------- | :------- | :------------------------------------------------------------- |
| data-input-otp | - | Present on the input. |
| data-input-otp-container | - | Present on the container that holds the slots and the input. |
| data-input-otp-mse | `number` | The end of the selection the slots mirror, set on the input. |
| data-input-otp-mss | `number` | The start of the selection the slots mirror, set on the input. |
| data-input-otp-placeholder-shown | - | Present on the input while the value is empty. |
**Root CSS Variables:**
| Variable | Type | Description |
| :-------------- | :------- | :--------------------------------------------- |
| `--root-height` | `number` | The height of the input, set on the container. |
##### Root.Props
Re-export of [Root](#input-otp) props.
##### Root.State
```typescript
type InputOTPRootState = {
/** One entry per slot. */
slots: InputOTPSlotState[];
/** Whether the input is focused. */
isFocused: boolean;
/** Whether the pointer is over the input and it is not disabled. */
isHovering: boolean;
};
```
#### Additional Types
##### InputOTPRootCssVariables
CSS variables of [Root](#input-otp).
```typescript
declare namespace InputOTPRootCssVariables {
/**
* The height of the input, set on the container.
* @type number
*/
const rootHeight: '--root-height';
}
```
##### InputOTPRootDataAttributes
Data attributes of [Root](#input-otp).
```typescript
declare namespace InputOTPRootDataAttributes {
/** Present on the container that holds the slots and the input. */
const container: 'data-input-otp-container';
/** Present on the input. */
const input: 'data-input-otp';
/** Present on the input while the value is empty. */
const placeholderShown: 'data-input-otp-placeholder-shown';
/**
* The start of the selection the slots mirror, set on the input.
* @type number
*/
const mirrorSelectionStart: 'data-input-otp-mss';
/**
* The end of the selection the slots mirror, set on the input.
* @type number
*/
const mirrorSelectionEnd: 'data-input-otp-mse';
}
```
##### InputOTPSlotState
What one slot renders.
```typescript
type InputOTPSlotState = {
/** Whether the selection covers this slot while the input is focused. */
isActive: boolean;
/** The character in this slot, or `null` when it is empty. */
char: string | null;
/** The `placeholder` character for this slot while the whole value is empty. */
placeholderChar: string | null;
/** Whether this slot is active and empty, so it shows a caret. */
hasFakeCaret: boolean;
};
```
### Command
A command menu that filters, ranks and selects its items.
#### Demo
##### Tailwind
This example shows how to implement the component using Tailwind CSS.
```svelte
No results found.
Calendar
Search Emoji
Calculator
Profile
Billing
```
##### CSS Modules
This example shows how to implement the component using CSS Modules.
```css
/* index.module.css */
.Root {
width: 18rem;
border: 1px solid oklch(14.5% 0 0deg);
background-color: white;
color: oklch(14.5% 0 0deg);
@media (prefers-color-scheme: dark) {
border-color: white;
background-color: oklch(14.5% 0 0deg);
color: white;
}
}
.Input {
box-sizing: border-box;
width: 100%;
height: 2.5rem;
padding: 0 0.75rem;
border: 0;
border-bottom: 1px solid currentcolor;
background-color: transparent;
color: inherit;
font-family: inherit;
font-size: 0.875rem;
outline: none;
}
.List {
max-height: 16rem;
padding: 0.25rem 0;
overflow-y: auto;
}
.Empty {
padding: 1.5rem 0.75rem;
font-size: 0.875rem;
text-align: center;
}
.Group [cmdk-group-heading] {
padding: 0.375rem 0.75rem;
color: oklch(55.6% 0 0deg);
font-size: 0.75rem;
}
.Item {
display: flex;
height: 2rem;
align-items: center;
padding: 0 0.75rem;
cursor: default;
font-size: 0.875rem;
-webkit-user-select: none;
user-select: none;
&[data-disabled='true'] {
color: oklch(55.6% 0 0deg);
}
&[data-selected='true'] {
background-color: oklch(14.5% 0 0deg);
color: white;
@media (prefers-color-scheme: dark) {
background-color: white;
color: oklch(14.5% 0 0deg);
}
}
}
.Separator {
height: 1px;
margin: 0.25rem 0;
background-color: oklch(92.2% 0 0deg);
@media (prefers-color-scheme: dark) {
background-color: oklch(26.9% 0 0deg);
}
}
```
```svelte
No results found.
Calendar
Search Emoji
Calculator
Profile
Billing
```
Command ports [cmdk](https://github.com/pacocoursey/cmdk) 1.1.1. It renders cmdk's elements, `cmdk-*` attributes, roles and ARIA, so styles written for cmdk select the same markup. Sveltery UI's Command is built on it.
#### Anatomy
Import the component and assemble its parts:
```svelte title="Anatomy"
```
#### Filtering and selection
An item's value is its trimmed text unless you pass `value`. Typing filters and ranks items with cmdk's fuzzy scoring. Items that do not match unmount, and the rest render in ranked order. The first matching item is selected, the arrow keys move the selection, and
Enter or a click calls the item's `onSelect` with its value.
A bound `value` that names no item is kept, for example while items load, and selects the first item with that value once one mounts. An empty `value` selects the first item. Use `defaultValue` to start with an item selected without binding it.
`filter(value, search, keywords)` returns a score from 0 to 1. A score of 0 hides the item. The default is the exported `defaultFilter`.
#### Keyboard
| Key | Action |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
|
ArrowDown ,
ArrowUp | Next or previous item. Wraps around with `loop`. |
|
Ctrl +
N or
J ,
Ctrl +
P or
K | Same as ArrowDown and ArrowUp, unless `vimBindings` is false. |
|
Meta with any of the above | Last or first item. |
|
Alt with any of the above | First item of the next or previous group. |
|
Home ,
End | First or last item. |
|
Enter | Calls the selected item's `onSelect`. |
Root ignores a key when a handler called `preventDefault()` or an input method is composing. Disabled items are never selected by the keyboard or the pointer.
#### Examples
##### Server results
For results that you fetch yourself, turn filtering off and render what the server returns. Selection and the keyboard keep working:
```svelte title="Server results"
{#if loading}Fetching... {/if}
{#each results as result (result.id)}
{result.title}
{/each}
Nothing found.
```
##### Reading the state
`getCommandState()` reads the menu's state from any component inside Root. Its fields are reactive:
```svelte title="Result count"
{#if command.search}
{command.count} results
{/if}
```
##### In a dialog
Put Root inside a [Dialog](#dialog). The menu resets its search and selection each time the popup content mounts again. Bind `value` on Root and Input to keep them across openings.
```svelte title="Command dialog"
...
```
##### Where parts render
List and each Group render their parts themselves, in ranked order, and each part leaves only a comment where you write it. So an element you wrap around an Item, Group, Separator, Empty or Loading stays empty: put classes and attributes on the part itself. Other markup written in List or Group renders before its parts. A `{#each}` or `{#if}` around parts is fine.
#### API reference
##### Root
A command menu that filters, ranks and selects its items. Renders a `
` element.
**Root Props:**
| Prop | Type | Default | Description |
| :---------------------- | :--------------------------------------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| label | `string` | - | Accessible label for the command menu, rendered visually hidden. |
| defaultValue | `string` | - | The value of the item selected at first. This is the uncontrolled counterpart of `value`. |
| value | `string` | - | The selected item's value. Bindable. While it is empty, Root selects the first item. A value that names no item is kept, and selects the first item with that value once one mounts.
To start with a value without binding it, use the `defaultValue` prop instead. |
| onValueChange | `(value: string) => void` | - | Called on every change of the selected value, including the menu's own automatic selection. |
| disablePointerSelection | `boolean` | `false` | Stops pointer movement from selecting items. |
| filter | `CommandFilter` | - | Scores an item against the search from 0 (hidden) to 1 (best). Defaults to `defaultFilter`. |
| onkeydown | `KeyboardEventHandler
\| null` | - | Runs before the menu's keyboard handling. Call `event.preventDefault()` to skip it. |
| shouldFilter | `boolean` | `true` | Set to false to turn off filtering and sorting, for example when the items come from a server search. |
| vimBindings | `boolean` | `true` | Enables Ctrl+N/J and Ctrl+P/K. |
| loop | `boolean` | `false` | Wraps the arrow keys around the ends of the list. |
##### Root.Props
Re-export of [Root](#command) props.
##### Input
The search input of the command menu. Renders an ` ` element.
**Input Props:**
| Prop | Type | Default | Description |
| :---- | :------- | :------ | :------------------------------------------------------------------------------------------------------------------ |
| value | `string` | - | The search text. Bindable.
To start with a search without binding it, use the `defaultValue` prop instead. |
##### Input.Props
Re-export of [Input](#command) props.
##### List
The list of items, groups and other parts. Renders a `` element.
**List Props:**
| Prop | Type | Default | Description |
| :---- | :------- | :-------------- | :-------------------------------- |
| label | `string` | `'Suggestions'` | Accessible label for the listbox. |
##### List.Props
Re-export of [List](#command) props.
##### Item
An item in the command menu. Renders a `
` element.
**Item Props:**
| Prop | Type | Default | Description |
| :------------ | :-------------------------------------------- | :------ | :------------------------------------------------------------------------------------------------- |
| value | `string` | - | The item's value. Defaults to its trimmed text content. |
| onclick | `MouseEventHandler
\| null` | - | Runs before the item handles the click. Call `event.preventDefault()` to skip it. |
| forceMount | `boolean` | - | Keeps the item visible whatever the search is. |
| keywords | `readonly string[]` | - | Extra words the search matches against. |
| onSelect | `(value: string) => void` | - | Called with the item's value on click or Enter. |
| onpointermove | `PointerEventHandler \| null` | - | Runs before the item selects itself on pointer movement. Call `event.preventDefault()` to skip it. |
| disabled | `boolean` | `false` | Whether the item ignores the keyboard, the pointer and clicks. |
**Item Data Attributes:**
| Attribute | Type | Description |
| :------------ | :--- | :-------------------------------------------------- |
| data-selected | - | `"true"` when the item is selected, else `"false"`. |
| data-disabled | - | `"true"` when the item is disabled, else `"false"`. |
| data-value | - | The item's value. |
##### Item.Props
Re-export of [Item](#command) props.
##### Group
A group of items with an optional heading. Renders a `` element.
**Group Props:**
| Prop | Type | Default | Description |
| :--------- | :------------------ | :------ | :------------------------------------------------------------ |
| value | `string` | - | The group's `data-value`. Defaults to the heading's text. |
| forceMount | `boolean` | `false` | Keeps the group and its items visible whatever the search is. |
| heading | `string \| Snippet` | - | The group's heading, which also labels its items. |
**Group Data Attributes:**
| Attribute | Type | Description |
| :--------- | :--- | :----------------- |
| data-value | - | The group's value. |
##### Group.Props
Re-export of [Group](#command) props.
##### Separator
A separator between items or groups, removed while searching. Renders a `
` element.
**Separator Props:**
| Prop | Type | Default | Description |
| :----------- | :-------- | :------ | :-------------------------------------------- |
| alwaysRender | `boolean` | `false` | Keeps the separator rendered while searching. |
##### Separator.Props
Re-export of [Separator](#command) props.
##### Empty
Rendered while no item matches the search. Renders a `
` element.
##### Empty.Props
Re-export of [Empty](#command) props.
##### Loading
A progress bar for items that are still loading. Renders a `
` element.
**Loading Props:**
| Prop | Type | Default | Description |
| :------- | :------- | :------------- | :------------------------------------- |
| label | `string` | `'Loading...'` | Accessible label for the progress bar. |
| progress | `number` | - | Loading progress from 0 to 100. |
##### Loading.Props
Re-export of [Loading](#command) props.
#### Additional Types
##### CommandFilter
```typescript
type CommandFilter = (value: string, search: string, keywords?: readonly string[]) => number;
```
##### CommandGroupDataAttributes
Data attributes of [Group](#command).
```typescript
declare namespace CommandGroupDataAttributes {
/** The group's value. */
const value: 'data-value';
}
```
##### CommandItemDataAttributes
Data attributes of [Item](#command).
```typescript
declare namespace CommandItemDataAttributes {
/** `"true"` when the item is selected, else `"false"`. */
const selected: 'data-selected';
/** `"true"` when the item is disabled, else `"false"`. */
const disabled: 'data-disabled';
/** The item's value. */
const value: 'data-value';
}
```
##### CommandState
The command menu's state, read with `getCommandState()` from any descendant of Root.
```typescript
type CommandState = {
search: string;
value: string;
/** How many items match the search, not counting force-mounted ones. 0 until Root mounts. */
count: number;
/** The id of the selected item element. */
selectedId: string | undefined;
};
```
#### Additional types