\| undefined` | | |
| orientation | `{ horizontal: string; vertical: string; }` | | |
| spacing | `complex` | | |
| data-testid | `string \| undefined` | | |
| decorative | `boolean \| undefined` | | |
| mx | `complex` | `2` | |
## Explorer
---
url: https://wallarm.github.io/design-system/components/select.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Select
Holds one answer, or several, chosen from a list — below about six options expose them instead with `Radio` or `SegmentedControl`, and reach for `DropdownMenu` when the items run commands rather than set a value. The trigger is composed rather than configured: `SelectButton` for a button-shaped trigger, `SelectInput` for one that looks like a field.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Select) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-select--overview)
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
export const Basic = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
isItemDisabled: item => item.value === 'angular',
});
return (
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
## Usage
```tsx
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
```
## Examples
### Sizes
The same height scale as the other fields, so a select sits in a row of inputs without
breaking the line.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectContent, SelectInput, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const Sizes = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
});
return (
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
### Different buttons
The trigger is yours to shape — labelled, icon-led, or value-only. Whatever it looks like,
it must still read as something that opens.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const DifferentButtons = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
});
return (
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
### Multiple
`multiple` collects several answers, and the trigger summarises them. Once past a couple of
selections a summary beats a list, which is what `WithTags` is for.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectContent, SelectInput, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
import { VStack } from '@wallarm-org/design-system/Stack';
export const Multiple = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
});
return (
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
### Multiple with icons
Icons carry the option's identity into the closed trigger, which matters more in a
multi-select where the text is compressed into a summary.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectContent, SelectInput, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
import { VStack } from '@wallarm-org/design-system/Stack';
export const MultipleWithIcons = () => {
const collection = createListCollection({
items: skills,
});
return (
{collection.items.map(skill => {
const { icon: Icon } = skill;
return (
{Icon && }
{skill.label}
);
})}
{collection.items.map(skill => {
const { icon: Icon } = skill;
return (
{Icon && }
{skill.label}
);
})}
{collection.items.map(skill => {
const { icon: Icon } = skill;
return (
{Icon && }
{skill.label}
);
})}
);
};
```
### Grouped
`SelectGroup` with `SelectGroupLabel` splits a long list into named sections. Group when
the reader would otherwise scan, not merely because the options have categories.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectClearTrigger, SelectContent, SelectGroup, SelectGroupLabel, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
export const Grouped = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
groupBy: item => item.category ?? '',
});
return (
{collection.group().map(([category, group], index) => (
{category}
{index === 0 && Clear all }
{group.map(skill => (
{skill.label}
))}
))}
);
};
```
### Disabled
The whole control is out, trigger included. Disabling individual options instead keeps the
list honest about what exists.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
export const Disabled = () => {
const collection = createListCollection({
items: skills,
});
return (
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
### Loading
`loading` on the root, for a list still being fetched. It holds the trigger's shape so the
layout doesn't jump when the options land.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
export const Loading = () => {
const collection = createListCollection({
items: skills,
});
return (
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
### With select input
`SelectInput` makes the trigger look like a text field, which suits a select sitting in a
form beside real inputs rather than in a toolbar.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectContent, SelectInput, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
import { VStack } from '@wallarm-org/design-system/Stack';
export const WithSelectInput = () => {
const collection = createListCollection({
items: skills,
});
return (
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
### With separator
`SelectSeparator` divides kinds of option — a destructive or clear-all entry from the real
choices — where a group label would be overkill.
```tsx
import { Fragment } from 'react';
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner, SelectSeparator } from '@wallarm-org/design-system/Select';
export const WithSeparator = () => {
const collection = createListCollection({
items: skills,
});
return (
{collection.items.map((skill, index) => (
{skill.label}
{index === 3 && }
))}
);
};
```
### With icons
`SelectValueIcon` in the option and the trigger, so the chosen option keeps its icon once
the menu closes.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
export const WithIcons = () => {
const collection = createListCollection({
items: skills,
});
return (
{collection.items.map(({ icon: Icon, ...skill }) => (
{Icon && }
{skill.label}
))}
);
};
```
### With footer
`SelectFooter` pins content below the scrolling list, for a persistent escape like managing
the options themselves.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Button } from '@wallarm-org/design-system/Button';
import { SquareArrowOutUpRight } from '@wallarm-org/design-system/icons';
import { Select, SelectButton, SelectClearTrigger, SelectContent, SelectFooter, SelectGroup, SelectGroupLabel, SelectOption, SelectOptionDescription, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
import { HStack } from '@wallarm-org/design-system/Stack';
export const WithFooter = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
groupBy: item => item.category ?? '',
});
return (
{collection.group().map(([category, group], index) => (
{category}
{index === 0 && Clear all }
{group.map(skill => (
{skill.label}
{skill.description && (
{skill.description}
)}
))}
))}
See all
Cancel
Apply
);
};
```
### With search
`SelectSearchInput` with the `useSelectSearch` hook, plus `SelectEmptyState` for when
nothing matches. Add search once the list outgrows a glance — roughly ten options.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Button } from '@wallarm-org/design-system/Button';
import { SquareArrowOutUpRight } from '@wallarm-org/design-system/icons';
import { Select, SelectButton, SelectClearTrigger, SelectContent, SelectEmptyState, SelectFooter, SelectGroup, SelectGroupLabel, SelectHeader, SelectOption, SelectOptionDescription, SelectOptionIndicator, SelectOptionText, SelectPositioner, SelectSearchInput, useSelectSearch } from '@wallarm-org/design-system/Select';
import { HStack } from '@wallarm-org/design-system/Stack';
export const WithSearch = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
groupBy: item => item.category ?? '',
itemToString: item => item.label,
});
const { searchValue, onSearchChange, filteredCollection } = useSelectSearch(collection);
return (
{filteredCollection.size === 0 ? (
) : (
filteredCollection.group().map(([category, group], index) => (
{category}
{index === 0 && Clear all }
{group.map(skill => (
{skill.label}
{skill.description && (
{skill.description}
)}
))}
))
)}
See all
Cancel
Apply
);
};
```
### With form field
Inside `Field`, so the label and description come from `FieldLabel` and `FieldDescription`
rather than from the select.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Field, FieldContent, FieldLabel, FieldSet } from '@wallarm-org/design-system/Field';
import { Select, SelectButton, SelectContent, SelectInput, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
export const WithFormField = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
});
return (
Single with SelectInput
{collection.items.map(skill => (
{skill.label}
))}
Single with SelectButton
{collection.items.map(skill => (
{skill.label}
))}
Multiple with SelectInput
{collection.items.map(skill => (
{skill.label}
))}
Multiple with SelectButton
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
### With tags
Selections render as removable tags inside the field, which is the honest shape for
multi-select when the reader needs to see and undo each choice individually.
```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Select, SelectContent, SelectInput, SelectOption, SelectPositioner } from '@wallarm-org/design-system/Select';
import { Tag } from '@wallarm-org/design-system/Tag';
export const WithTags = () => {
const collection = createListCollection({
items: skillsWithoutIcons,
});
return (
{collection.items.map(skill => (
{skill.label}
))}
);
};
```
## Props
| Prop | Type | Default | Description |
| ----------------------- | --------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| form | `string \| undefined` | | The associate form of the underlying select. |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string[] \| undefined` | | The initial default value of the select when rendered. Use when you don't need to control the value of the select. |
| autoFocus | `boolean \| undefined` | | |
| id | `string` | | The unique identifier of the machine. |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| onSelect | `((details: SelectionDetails) => void) \| undefined` | | Function called when an item is selected |
| disabled | `boolean \| undefined` | `false` | Whether the select is disabled |
| name | `string \| undefined` | | The `name` attribute of the underlying select. |
| value | `string[] \| undefined` | | The controlled keys of the selected items |
| asChild | `boolean \| undefined` | | Use the provided child element as the default rendered element, combining their props and behavior. |
| ids | `ElementIds \| undefined` | | The ids of the elements in the select. Useful for composition. |
| defaultHighlightedValue | `string \| null \| undefined` | | The initial value of the highlighted item when opened. Use when you don't need to control the highlighted value of the select. |
| highlightedValue | `string \| null \| undefined` | | The controlled key of the highlighted item |
| onHighlightChange | `complex` | | The callback fired when the highlighted item changes. |
| loopFocus | `boolean \| undefined` | | Whether to loop the keyboard navigation through the options |
| closeOnSelect | `boolean \| undefined` | | Whether the select should close after an item is selected |
| open | `boolean \| undefined` | | Whether the select menu is open |
| onOpenChange | `((details: OpenChangeDetails) => void) \| undefined` | | Function called when the popup is opened |
| defaultOpen | `boolean \| undefined` | | Whether the select's open state is controlled by the user |
| composite | `boolean \| undefined` | | Whether the select is a composed with other composite widgets like tabs or combobox |
| onPointerDownOutside | `complex` | | Function called when the pointer is pressed down outside the component |
| onFocusOutside | `complex` | | Function called when the focus is moved outside the component |
| onInteractOutside | `complex` | | Function called when an interaction happens outside the component |
| present | `boolean \| undefined` | | Whether the node is present (controlled by the user) |
| onExitComplete | `VoidFunction \| undefined` | | Function called when the animation ends in the closed state |
| onEnterComplete | `VoidFunction \| undefined` | | Function called when the animation ends in the open state |
| immediate | `boolean \| undefined` | | Whether to synchronize the present change immediately or defer it to the next frame |
| autoComplete | `string \| undefined` | | The autocomplete attribute for the hidden select. Enables browser autofill (e.g. "address-level1" for state). |
| readOnly | `boolean \| undefined` | | Whether the select is read-only |
| required | `boolean \| undefined` | | Whether the select is required |
| invalid | `boolean \| undefined` | | Whether the select is invalid |
| onValueChange | `((details: ValueChangeDetails) => void) \| undefined` | | The callback fired when the selected item changes. |
| multiple | `boolean \| undefined` | | Whether to allow multiple selection |
| collection \* | `ListCollection` | | The collection of items |
| translations | `IntlTranslations \| undefined` | | Specifies the localized strings that identifies the accessibility elements and their states |
| scrollToIndexFn | `((details: ScrollToIndexDetails) => void) \| undefined` | | Function to scroll to a specific index |
| deselectable | `boolean \| undefined` | | Whether the value can be cleared by clicking the selected item. **Note:** this is only applicable for single selection |
| skipAnimationOnMount | `boolean \| undefined` | | Whether to allow the initial presence animation. |
| hideMode | `"display-none" \| "activity"` | | How to hide content when mounted but not present. - `'display-none'`: HTML `hidden` attribute. Effects stay alive. - `'activity'`: React 19 ``. Effects pause. Requires React 19+. |
| loading | `boolean \| undefined` | `false` | |
| positioning | `PositioningOptions \| undefined` | | Override default floating positioning (merged with DS defaults). |
| data-testid | `string \| undefined` | | |
### SelectEmptyState
| Prop | Type | Default | Description |
| ----------- | ----------- | ------------ | ----------- |
| description | `ReactNode` | `No results` | |
### SelectFooter
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | --------- | ----------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| variant | `{ default: string; actions: string; }` | `default` | |
| data-testid | `string \| undefined` | | |
### SelectOptionHint
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| data-testid | `string \| undefined` | | |
### SelectPositioner
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| contentProps | `complex` | | Props for the content element (the `listbox` panel inside the positioner) — for a ref, an inline measurement such as a locked width, or `data-*` attributes. `className` on the positioner itself already lands on the content, and overrides its default sizes (`min-w-240 max-w-320 max-h-(--available-height)`) through tailwind-merge. |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| asChild | `boolean \| undefined` | | Use the provided child element as the default rendered element, combining their props and behavior. |
### SelectSearchInput
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ------------------------------------------------------------------------------- |
| value \* | `string` | | |
| onChange \* | `(value: string) => void` | | |
| onClear | `(() => void) \| undefined` | | Called when the built-in clear button empties the field, before `onChange('')`. |
| title | `string \| undefined` | | |
| pattern | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| name | `string \| undefined` | | |
| alt | `string \| undefined` | | |
| height | `string \| number \| undefined` | | |
| src | `string \| undefined` | | |
| width | `string \| number \| undefined` | | |
| autoComplete | `HTMLInputAutoCompleteAttribute \| undefined` | | |
| maxLength | `number \| undefined` | | |
| minLength | `number \| undefined` | | |
| placeholder | `string \| undefined` | | |
| readOnly | `boolean \| undefined` | | |
| required | `boolean \| undefined` | | |
| checked | `boolean \| undefined` | | |
| step | `string \| number \| undefined` | | |
| accept | `string \| undefined` | | |
| max | `string \| number \| undefined` | | |
| min | `string \| number \| undefined` | | |
| multiple | `boolean \| undefined` | | |
| data-testid | `string \| undefined` | | |
### SelectSeparator
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
## Explorer
---
url: https://wallarm.github.io/design-system/components/slider.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Slider
Sets an approximate, bounded value by dragging along a track — reach for `NumberInput` when the exact figure matters, since a slider is faster to move than it is to land. A slider should always show its value: pair it with `SliderInput` for entry, or `SliderValue` for a readout. It implies a continuum, so a few fixed choices belong in `SegmentedControl`, `Radio` or `Select`, and on/off in `Switch`.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Slider) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-slider--overview)
```tsx
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const Basic = () => (
);
```
## Usage
```tsx
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
```
## Examples
### Range
Two entries in the value make two thumbs; render one `SliderThumb` per entry. The thumbs
cannot cross, so the pair always reads as a valid range.
```tsx
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const Range = () => (
);
```
### Ticks
`SliderMarks` draws ticks along the scale. Keep `step` aligned to the marks, or the handle
lands between them and the ticks stop meaning anything.
```tsx
import { Slider, SliderControl, SliderMarks, SliderThumb } from '@wallarm-org/design-system/Slider';
export const Ticks = () => (
);
```
### Ticks without snapping
The same marks used purely for orientation, with `step` finer than their spacing. Choose this
when the marks are a guide rather than the only valid answers.
```tsx
import { Slider, SliderControl, SliderMarks, SliderThumb } from '@wallarm-org/design-system/Slider';
export const TicksWithoutSnapping = () => (
);
```
### Labeled
Marks carrying text, for a scale whose steps are named rather than numeric. The handle snaps
to the labelled positions and announces the label rather than the number, so keep it to a
handful of rungs.
```tsx
import { Slider, SliderControl, SliderMarks, SliderThumb } from '@wallarm-org/design-system/Slider';
export const Labeled = () => (
);
```
### With tooltip
`tooltip` on a `SliderThumb` shows the value while dragging, then hides it. It suits a value
only checked mid-gesture; anything needed afterwards wants a persistent readout instead,
never both.
```tsx
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const WithTooltip = () => (
);
```
### With input
`SliderInput` is the precision escape hatch: the track for approximate moves, the field for
an exact figure. Typing commits on blur or Enter rather than live, so clamping never fights a
half-typed number.
```tsx
import { Slider, SliderControl, SliderInput, SliderThumb } from '@wallarm-org/design-system/Slider';
export const WithInput = () => (
);
```
### Range with input
A field at each end, low on the left and high on the right. Neither crosses the other, so the
fields cannot produce a range the track could not.
```tsx
import { Slider, SliderControl, SliderInput, SliderThumb } from '@wallarm-org/design-system/Slider';
export const RangeWithInput = () => (
);
```
### Disabled
The whole control dims and stops responding, handle included. The value stays legible,
because a locked setting is usually one the reader still needs to read.
```tsx
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const Disabled = () => (
);
```
### In field
Wrapped in `Field`, which is the form-field shape. The slider reads that context, so the
label and error attach without being passed anything.
```tsx
import { Field, FieldDescription, FieldLabel } from '@wallarm-org/design-system/Field';
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const InField = () => (
Risk threshold
Approximate — fine-tune the exact value later.
);
```
### Field with error
`invalid` on the `Field` cascades to the handle. The message is `FieldError`'s job, and it
matters here because a slider cannot show a wrong value the way a text field can.
```tsx
import { Field, FieldError, FieldLabel } from '@wallarm-org/design-system/Field';
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const FieldWithError = () => (
Risk threshold
Enter a value between 0 and 100.
);
```
### Field with value
The value beside the label, which is the readout Figma draws for a slider in a form — value
beside label is the drawn default, not one option among several.
```tsx
import { useState } from 'react';
import { Field, FieldLabel } from '@wallarm-org/design-system/Field';
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const FieldWithValue = () => {
const [value, setValue] = useState([40]);
return (
Risk threshold
{value[0]}
);
};
```
### Range field with value
Both ends beside the label, comma-separated. For a range this is the only way to read the
value without hovering each thumb.
```tsx
import { useState } from 'react';
import { Field, FieldLabel } from '@wallarm-org/design-system/Field';
import { Slider, SliderControl, SliderThumb } from '@wallarm-org/design-system/Slider';
export const RangeFieldWithValue = () => {
const [value, setValue] = useState([20, 80]);
return (
Risk range
{value.join(', ')}
);
};
```
### With value readout
`SliderValue` reads the slider's own context, so a live readout needs no controlled state of
its own.
```tsx
import { Slider, SliderControl, SliderThumb, SliderValue } from '@wallarm-org/design-system/Slider';
export const WithValueReadout = () => (
);
```
## Props
| Prop | Type | Default | Description |
| ---------------------- | ------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| error | `boolean \| undefined` | `false` | Error state → maps to Ark `invalid` (repaints the handle; the message is the Field's job). |
| onValueChange | `((value: number[]) => void) \| undefined` | | Fires live during drag with the current value array (`[n]` single, `[low, high]` range). |
| onValueChangeEnd | `((value: number[]) => void) \| undefined` | | Fires once on drag release with the committed value array. |
| form | `string \| undefined` | | The associate form of the underlying input element. |
| defaultValue | `number[] \| undefined` | `[50]` | The initial value of the slider when rendered. Use when you don't need to control the value of the slider. |
| disabled | `boolean \| undefined` | | Whether the slider is disabled |
| name | `string \| undefined` | | The name associated with each slider thumb (when used in a form) |
| value | `number[] \| undefined` | | The controlled value of the slider |
| readOnly | `boolean \| undefined` | | Whether the slider is read-only |
| step | `number \| undefined` | | The step value of the slider |
| max | `number \| undefined` | | The maximum value of the slider |
| min | `number \| undefined` | | The minimum value of the slider |
| getAriaValueText | `((details: ValueTextDetails) => string) \| undefined` | | Function that returns a human readable value for the slider thumb |
| minStepsBetweenThumbs | `number \| undefined` | | The minimum permitted steps between multiple thumbs. `minStepsBetweenThumbs` \* `step` should reflect the gap between the thumbs. - `step: 1` and `minStepsBetweenThumbs: 10` => gap is `10` - `step: 10` and `minStepsBetweenThumbs: 2` => gap is `20` |
| thumbCollisionBehavior | `"none" \| "push" \| "swap" \| undefined` | | Controls how thumbs behave when they collide during pointer interactions. - `none` (default): Thumbs cannot move past each other; excess movement is ignored. - `push`: Thumbs push each other without restoring their previous positions when dragged back. - `swap`: Thumbs swap places when dragged past each other. |
| data-testid | `string \| undefined` | | |
### SliderInput
| Prop | Type | Default | Description |
| -------------- | ------------------------------------------------------ | ------- | ------------------------------------------------------------ |
| index | `number \| undefined` | `0` | Which thumb this box is bound to (0 = single/low, 1 = high). |
| title | `string \| undefined` | | |
| pattern | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| type | `HTMLInputTypeAttribute \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| name | `string \| undefined` | | |
| alt | `string \| undefined` | | |
| height | `string \| number \| undefined` | | |
| src | `string \| undefined` | | |
| width | `string \| number \| undefined` | | |
| autoComplete | `HTMLInputAutoCompleteAttribute \| undefined` | | |
| maxLength | `number \| undefined` | | |
| minLength | `number \| undefined` | | |
| placeholder | `string \| undefined` | | |
| readOnly | `boolean \| undefined` | | |
| required | `boolean \| undefined` | | |
| checked | `boolean \| undefined` | | |
| step | `string \| number \| undefined` | | |
| accept | `string \| undefined` | | |
| max | `string \| number \| undefined` | | |
| min | `string \| number \| undefined` | | |
| multiple | `boolean \| undefined` | | |
### SliderMarks
| Prop | Type | Default | Description |
| -------- | -------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| marks \* | `SliderMark[]` | | Each entry places a tick at `value`; an optional `label` renders below it (and drives `aria-valuetext` for ordinal scales). Align `step` to the marks so drag and arrow-keys snap onto them. |
### SliderThumb
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------- |
| index | `number \| undefined` | `0` | Which thumb this is (0 = single/low, 1 = high). |
| tooltip | `boolean \| undefined` | `false` | Show an on-drag value bubble above this thumb (DS Tooltip, opened by drag). |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| name | `string \| undefined` | | |
### SliderValue
| Prop | Type | Default | Description |
| ----- | --------------------- | ------- | ------------------------------------------------------------------------------------ |
| index | `number \| undefined` | | Show one thumb's value (range). Omit to show all (single → "50", range → "20 – 80"). |
---
url: https://wallarm.github.io/design-system/components/switch.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Switch
Turns one setting on or off, and takes effect the moment it is flipped — reach for `Checkbox` when the answer is submitted with a form, and `ToggleButton` when the control belongs in a toolbar rather than a settings list. Because there is no Save to undo it, avoid a switch for anything destructive or slow.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Switch) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-switch--overview)
```tsx
import { Switch, SwitchControl, SwitchLabel } from '@wallarm-org/design-system/Switch';
export const Basic = () => (
Enable notifications
);
```
## Usage
```tsx
import { Switch, SwitchControl, SwitchLabel } from '@wallarm-org/design-system/Switch';
```
## Examples
### Checked
The on state. The track fills with brand and the thumb travels, so on and off differ by
both colour and position.
```tsx
import { Switch, SwitchControl, SwitchLabel } from '@wallarm-org/design-system/Switch';
export const Checked = () => (
Enable notifications
);
```
### Disabled
Dimmed and not clickable, in both states. A disabled switch still shows where the setting
stands, which is the point of showing it at all.
```tsx
import { VStack } from '@wallarm-org/design-system/Stack';
import { Switch, SwitchControl, SwitchLabel } from '@wallarm-org/design-system/Switch';
export const Disabled = () => (
Enable notifications
Enable notifications
);
```
### With description
`SwitchDescription` adds a second line and switches the row to a grid, so the text aligns
with the label rather than the track.
```tsx
import { Switch, SwitchControl, SwitchDescription, SwitchLabel } from '@wallarm-org/design-system/Switch';
export const WithDescription = () => (
Enable notifications
Receive notifications about updates and new features.
);
```
### Multiple
Several switches in a column. They are independent settings, not a choice between
options — if only one may be on, that is a `Radio` group.
```tsx
import { Info } from '@wallarm-org/design-system/icons';
import { VStack } from '@wallarm-org/design-system/Stack';
import { Switch, SwitchControl, SwitchDescription, SwitchLabel } from '@wallarm-org/design-system/Switch';
import { Tooltip, TooltipContent, TooltipTrigger } from '@wallarm-org/design-system/Tooltip';
export const Multiple = () => (
Email notifications{' '}
Additional information
Receive email notifications about important updates.
Push notifications{' '}
Additional information
Get push notifications on your mobile device.
SMS notifications{' '}
Additional information
Receive SMS notifications for critical alerts.
);
```
### Accessibility mode
`a11yMode` draws a tick or a cross inside the track, so the state survives being read
without colour. Worth turning on wherever the setting matters more than the aesthetic.
```tsx
import { VStack } from '@wallarm-org/design-system/Stack';
import { Switch, SwitchControl, SwitchLabel } from '@wallarm-org/design-system/Switch';
export const AccessibilityMode = () => (
Enable notifications
Enable notifications
);
```
## Props
| Prop | Type | Default | Description |
| --------------- | -------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| a11yMode | `boolean \| undefined` | `false` | |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| htmlFor | `string \| undefined` | | |
| label | `string \| undefined` | | Specifies the localized strings that identifies the accessibility elements and their states |
| disabled | `boolean \| undefined` | | Whether the switch is disabled. |
| name | `string \| undefined` | | The name of the input field in a switch (Useful for form submission). |
| value | `string \| number \| undefined` | | The value of switch input. Useful for form submission. |
| ids | `ElementIds \| undefined` | | The ids of the elements in the switch. Useful for composition. |
| readOnly | `boolean \| undefined` | | Whether the switch is read-only |
| required | `boolean \| undefined` | | If `true`, the switch input is marked as required, |
| onCheckedChange | `((details: CheckedChangeDetails) => void) \| undefined` | | Function to call when the switch is clicked. |
| invalid | `boolean \| undefined` | | If `true`, the switch is marked as invalid. |
| checked | `boolean \| undefined` | | The controlled checked state of the switch |
| asChild | `boolean \| undefined` | | Use the provided child element as the default rendered element, combining their props and behavior. |
| data-testid | `string \| undefined` | | |
### SwitchDescription
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### SwitchLabel
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| asChild | `boolean \| undefined` | | Use the provided child element as the default rendered element, combining their props and behavior. |
## Explorer
---
url: https://wallarm.github.io/design-system/components/textarea.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Textarea
Multi-line free text, for answers that run to sentences — reach for `Input` when one line will do, since a tall box tells the reader to write more than you want. Like `Input`, it reads `Field` context for its label, description and error state.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Textarea) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-textarea--overview)
```tsx
import { Textarea } from '@wallarm-org/design-system/Textarea';
```
## Usage
```tsx
import { Textarea } from '@wallarm-org/design-system/Textarea';
```
## Examples
### Sizes
The same 36 / 32 / 24px scale as `Input`, applied to the single-row height, so a textarea
lines up with the fields around it before it grows.
```tsx
import { HStack } from '@wallarm-org/design-system/Stack';
import { Textarea } from '@wallarm-org/design-system/Textarea';
export const Sizes = () => (
);
```
### Focused
Focus is a ring outside the border, as on every other field, so the box doesn't shift when
the reader tabs into it.
```tsx
import { Textarea } from '@wallarm-org/design-system/Textarea';
```
### Disabled
Dimmed and not focusable. Say elsewhere what would enable it.
```tsx
import { Textarea } from '@wallarm-org/design-system/Textarea';
```
### With value
A filled box. Text starts at the top rather than centring, which is why the height you
choose reads as an invitation.
```tsx
import { Textarea } from '@wallarm-org/design-system/Textarea';
```
### With error
`error` reddens the border and sets `aria-invalid`; the message is `FieldError`'s job.
```tsx
import { Textarea } from '@wallarm-org/design-system/Textarea';
```
### Auto resize
Passing `maxRows` turns auto-resize on: the box starts at `minRows` and grows with the
text until it hits the cap, then scrolls. Without `maxRows` the height is fixed.
```tsx
import { Textarea } from '@wallarm-org/design-system/Textarea';
export const AutoResize = () => (
);
```
### With footer
Inside an `InputGroup` with a `block-end` addon, which is how the composer pattern is
built — auto-resize plus a toolbar that stays attached as the box grows.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemText, DropdownMenuLabel, DropdownMenuShortcut, DropdownMenuTrigger } from '@wallarm-org/design-system/DropdownMenu';
import { ArrowUp, Settings2 } from '@wallarm-org/design-system/icons';
import { InputGroup, InputGroupAddon } from '@wallarm-org/design-system/InputGroup';
import { HStack } from '@wallarm-org/design-system/Stack';
import { Switch, SwitchControl } from '@wallarm-org/design-system/Switch';
import { Text } from '@wallarm-org/design-system/Text';
import { Textarea } from '@wallarm-org/design-system/Textarea';
export const WithFooter = () => {
const toolItems = Array.from({ length: 6 }, (_, i) => ({
id: String(i + 1),
tool: '{tool-name}',
desc: 'Description',
enabled: true,
}));
return (
Always allowed in this conversation
{toolItems.map(item => (
{item.tool}
{item.desc}
))}
);
};
```
## Props
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------------------------------- | --------- | ----------- |
| minRows | `number \| undefined` | | |
| maxRows | `number \| undefined` | | |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| disabled | `boolean \| undefined` | `false` | |
| name | `string \| undefined` | | |
| value | `string \| number \| readonly string[] \| undefined` | | |
| autoComplete | `string \| undefined` | | |
| maxLength | `number \| undefined` | | |
| minLength | `number \| undefined` | | |
| placeholder | `string \| undefined` | | |
| readOnly | `boolean \| undefined` | | |
| required | `boolean \| undefined` | | |
| size | `{ small: string; medium: string; default: string; 'inline-edit': string; }` | `default` | |
| error | `{ true: string; false: string; }` | `false` | |
| data-testid | `string \| undefined` | | |
---
url: https://wallarm.github.io/design-system/components/calendar.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Calendar
The month grid for choosing a date the reader needs to see in context — a weekday, a distance from today — where `DateInput` is for a date they already know. It is its own popover, so compose a trigger beside it rather than wrapping it in one, and take React Aria date objects rather than a JavaScript `Date`.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Calendar) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-date-calendar--overview)
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const Single = () => (
Select date
);
```
## Usage
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
```
## Examples
### Range
A start and an end in one grid, with the span drawn between them. Two months show at once so
a range crossing a boundary can be chosen without paging.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const Range = () => (
Select date range
);
```
### Range with presets
`CalendarPresets` alongside the grid. The shipped defaults cover the ranges people actually
ask for — last week, this month, last 90 days — so reach for them before writing your own.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, DEFAULT_RANGE_PRESETS, CalendarBody, CalendarContent, CalendarGrids, CalendarPresets, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeWithPresets = () => (
Select date range
);
```
### Range with input
`CalendarInputHeader` puts a typed field above the grid, so the same popover serves both a
reader who knows the dates and one who needs to look.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarInputHeader, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeWithInput = () => (
Select date range
);
```
### Range with footer
`CalendarFooter` holds Reset and Apply, which turns the grid into a deliberate commit —
nothing changes underneath the reader until they apply it.
```tsx
import { useState } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarApplyButton, CalendarBody, CalendarContent, CalendarFooter, CalendarFooterControls, CalendarGrids, CalendarKeyboardHints, CalendarResetButton, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeWithFooter = () => {
const [value, setValue] = useState([]);
return (
Select date range
setValue([])} />
);
};
```
### Range full featured
Presets, input header and footer together, which is the shape a filter bar wants. Everything
else on this page is a subset of it.
```tsx
import { useState } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, DEFAULT_RANGE_PRESETS, CalendarApplyButton, CalendarBody, CalendarContent, CalendarFooter, CalendarFooterControls, CalendarGrids, CalendarInputHeader, CalendarKeyboardHints, CalendarPresets, CalendarResetButton, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeFullFeatured = () => {
const [value, setValue] = useState([]);
return (
Select date range
setValue([])} />
);
};
```
### Single with input
The typed header on a single calendar, for the same reason as the range: the reader chooses
how to answer.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarInputHeader, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const SingleWithInput = () => (
Select date
);
```
### Single with date time
`showTime` adds a time to the date, for a moment rather than a day. Ask for it only where the
time genuinely changes the result.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarInputHeader, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const SingleWithDateTime = () => (
Select date and time
);
```
### Single with presets
Single-date presets — today, yesterday, first of the month — which answer the questions a
single date usually stands for.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarPresets, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const SingleWithPresets = () => (
Select date
);
```
### Range with disabled dates
`isDateUnavailable` greys out dates that cannot be chosen. Show them rather than hiding them,
so the reader can see the shape of what is allowed.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeWithDisabledDates = () => (
{
const dayOfWeek = new Date(date.year, date.month - 1, date.day).getDay();
return dayOfWeek === 0 || dayOfWeek === 6;
}}
>
Select date range (weekends disabled)
);
```
### Range disallow disabled
A range refusing to span an unavailable date, which needs controlled mode. Use it where
crossing the gap would produce a period the system cannot honour.
```tsx
import { useState } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeDisallowDisabled = () => {
const [value, setValue] = useState([]);
const isWeekend = (date: DateValue) => {
const dayOfWeek = new Date(date.year, date.month - 1, date.day).getDay();
return dayOfWeek === 0 || dayOfWeek === 6;
};
return (
Range cannot span weekends
);
};
```
### Range controlled
Owning the value, which is what presets, Apply and disallowed spans all require — the
uncontrolled form is only for the simplest case.
```tsx
import { useState } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, DEFAULT_RANGE_PRESETS, CalendarApplyButton, CalendarBody, CalendarContent, CalendarFooter, CalendarFooterControls, CalendarGrids, CalendarInputHeader, CalendarKeyboardHints, CalendarPresets, CalendarResetButton, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeControlled = () => {
const [value, setValue] = useState([]);
const [open, setOpen] = useState(false);
const getButtonLabel = () => {
if (!value || value.length === 0 || !value[0]) return 'Select date range';
if (value.length === 1 || !value[1]) return value[0].toString();
return `${value[0].toString()} - ${value[1].toString()}`;
};
return (
{getButtonLabel()}
setValue([])} />
setOpen(false)} />
);
};
```
### Single custom presets
Your own preset list, for domain periods the defaults do not cover. Keep the labels as plain
as the shipped ones.
```tsx
import { getLocalTimeZone, today } from '@internationalized/date';
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarPresetItem, CalendarPresets, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const SingleCustomPresets = () => {
const now = today(getLocalTimeZone()) as unknown as DateValue;
return (
Select date
);
};
```
### Range readonly
A range shown but not editable, for a period the reader needs to read back rather than
change.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeReadonly = () => (
View date range
);
```
### Range readonly with presets
The same, with presets rendered but inert — worth seeing, because a live-looking preset that
does nothing is worse than none.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, DEFAULT_RANGE_PRESETS, CalendarBody, CalendarContent, CalendarGrids, CalendarPresets, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const RangeReadonlyWithPresets = () => (
View date range
);
```
### Single readonly
A single date shown but not editable.
```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Calendar, CalendarBody, CalendarContent, CalendarGrids, CalendarTrigger } from '@wallarm-org/design-system/Calendar';
import { Calendar as CalendarIcon } from '@wallarm-org/design-system/icons';
export const SingleReadonly = () => (
View date
);
```
## Props
| Prop | Type | Default | Description |
| ---------------------------- | ---------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | `CalendarType \| undefined` | `single` | Calendar type: 'single' for one month, 'range' for two months (date range selection) |
| showTime | `boolean \| undefined` | `false` | Show time selection (affects input placeholder text) |
| showKeyboardHints | `boolean \| undefined` | `true` | Show keyboard shortcut hints in presets and footer |
| value | `DateValue[] \| undefined` | | Controlled value - array of selected dates |
| defaultValue | `DateValue[] \| undefined` | | Default value for uncontrolled mode |
| onChange | `complex` | | Callback fired when selected value changes |
| minDate | `DateValue \| undefined` | | Minimum selectable date |
| maxDate | `DateValue \| undefined` | | Maximum selectable date |
| isDateUnavailable | `complex` | | Function to determine if a specific date should be unavailable/disabled. DateValue has year, month (1-12), and day properties. To get day of week, use: new Date(date.year, date.month - 1, date.day).getDay() where Sunday = 0, Saturday = 6. |
| disallowDisabledDatesInRange | `boolean \| undefined` | `false` | When true, prevents selecting a range that contains any unavailable dates. Requires controlled mode (value + onChange props) to work properly. |
| open | `boolean \| undefined` | | Controlled open state of the calendar popover |
| defaultOpen | `boolean \| undefined` | | Default open state for uncontrolled mode |
| onOpenChange | `((open: boolean) => void) \| undefined` | | Callback fired when popover open state changes |
| closeOnSelect | `boolean \| undefined` | `true` | Whether to close calendar on date selection. Default: true |
| readonly | `boolean \| undefined` | `false` | Whether calendar is readonly (display only, no date selection allowed). Default: false |
| focusedValue | `DateValue \| undefined` | | Controlled focused date — navigates the calendar view to this date |
### CalendarApplyButton
| Prop | Type | Default | Description |
| -------------- | -------------------------------------------------------------------------- | --------- | -------------------------------------- |
| onClick | `(() => void) \| undefined` | | Callback fired before closing calendar |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| color | `string \| undefined` | `brand` | |
| onChange | `ChangeEventHandler \| undefined` | | |
| type | `"button" \| "submit" \| "reset" \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| name | `string \| undefined` | | |
| value | `string \| number \| readonly string[] \| undefined` | | |
| asChild | `boolean \| undefined` | | |
| size | `{ small: string; medium: string; large: string; 'inline-edit': string; }` | `small` | |
| data-testid | `string \| undefined` | | |
| variant | `{ primary: string; outline: string; ghost: string; secondary: string; }` | `primary` | |
| loading | `{ true: string; }` | | |
| as | `C \| undefined` | | |
| fullWidth | `{ true: string; false: string; }` | | |
### CalendarPresetItem
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ------------------------------------------------- |
| label \* | `string` | | Display label for the preset |
| value \* | `PresetValue` | | Preset value - dates array or named preset string |
| shortcut | `string \| undefined` | | Keyboard shortcut key |
| showShortcut | `boolean \| undefined` | `true` | Whether to display the shortcut badge |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| name | `string \| undefined` | | |
| data-testid | `string \| undefined` | | |
### CalendarPresets
| Prop | Type | Default | Description |
| ------------- | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
| presets | `PresetConfig[] \| undefined` | | Array of preset configurations. Defaults to DEFAULT_SINGLE_PRESETS or DEFAULT_RANGE_PRESETS based on calendar type |
| showShortcuts | `boolean \| undefined` | `true` | Whether to show keyboard shortcuts next to presets |
### CalendarResetButton
| Prop | Type | Default | Description |
| -------------- | -------------------------------------------------------------------------- | --------- | ---------------------------------------------------------- |
| onClick | `(() => void) \| undefined` | | Callback fired after reset (in addition to clearing value) |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| color | `string \| undefined` | `neutral` | |
| onChange | `ChangeEventHandler \| undefined` | | |
| type | `"button" \| "submit" \| "reset" \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| name | `string \| undefined` | | |
| value | `string \| number \| readonly string[] \| undefined` | | |
| asChild | `boolean \| undefined` | | |
| size | `{ small: string; medium: string; large: string; 'inline-edit': string; }` | `small` | |
| data-testid | `string \| undefined` | | |
| variant | `{ primary: string; outline: string; ghost: string; secondary: string; }` | `ghost` | |
| loading | `{ true: string; }` | | |
| as | `C \| undefined` | | |
| fullWidth | `{ true: string; false: string; }` | | |
### CalendarTrigger
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ---------------------------------------------- |
| asChild | `boolean \| undefined` | `true` | Additional props passed to the trigger wrapper |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| data-testid | `string \| undefined` | | |
---
url: https://wallarm.github.io/design-system/components/date-input.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# DateInput
A date typed segment by segment, for a date the reader knows — pair it with `Calendar` when they need to see the month to choose. It takes React Aria date objects rather than a JavaScript `Date`, and segment order and hour cycle come from `DateFormatProvider` app-wide rather than per input.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/DateInput) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-date-dateinput--overview)
```tsx
import { DateInput } from '@wallarm-org/design-system/DateInput';
export const Basic = () => {
return ;
};
```
## Usage
```tsx
import { DateInput } from '@wallarm-org/design-system/DateInput';
```
## Examples
### With icon
The calendar icon is decoration here, not a trigger — this field opens nothing. Reach for
`Calendar` when the icon should open a picker.
```tsx
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const WithIcon = () => (
);
```
### States
Default, disabled and error side by side. `error` reddens the border and marks the field
invalid; the reason belongs in `FieldError`.
```tsx
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const States = () => (
);
```
### Sizes
The same 36 / 32 / 24px scale as the other fields, so a date sits in a row of inputs without
breaking the line.
```tsx
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const Sizes = () => (
);
```
### Filled
A field holding a value, next to one holding a placeholder. The clear affordance only appears
once there is something to clear.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { VStack } from '@wallarm-org/design-system/Stack';
export const Filled = () => (
);
```
### Granularity
`granularity` decides how many segments there are, from a day down to a second. Ask for the
smallest unit you will actually use — every extra segment is another thing to tab through.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const Granularity = () => (
);
```
### With field components
Inside `Field`, which supplies the label and description. The field does not read that
context, so check the labelling rather than assuming it wired itself.
```tsx
import { useState } from 'react';
import { CalendarDate, getLocalTimeZone, today } from '@wallarm-org/design-system';
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { Field, FieldAction, FieldDescription, FieldError, FieldLabel } from '@wallarm-org/design-system/Field';
import { VStack } from '@wallarm-org/design-system/Stack';
export const WithFieldComponents = () => {
// With `granularity='day'` the runtime value is always a CalendarDate —
// onChange gets cast on the way in so app state stays narrowly typed.
const [value, setValue] = useState(null);
const [errorValue, setErrorValue] = useState(today(getLocalTimeZone()));
const handleSetNow = () => {
setValue(today(getLocalTimeZone()));
};
return (
Label
Set now
setValue(v as CalendarDate | null)}
/>
This is an input description.
Label
setErrorValue(v as CalendarDate | null)}
/>
An error message.
);
};
```
### Date order comparison
Day-first against month-first, each in its own scoped provider. This is an app-wide decision,
not a per-field one — the comparison exists to show what the provider changes.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const DateOrderComparison = () => (
);
```
### Hour cycle by context
12-hour against 24-hour, again from the provider. There is no per-input escape hatch, which
is deliberate: a form mixing both would be unreadable.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const HourCycleByContext = () => (
);
```
### Read only
`readOnly` keeps the value legible and removes every edit affordance. Different from
disabled, which says the control exists but is unavailable.
```tsx
import { DateInput } from '@wallarm-org/design-system/DateInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const ReadOnly = () => (
);
```
## Props
| Prop | Type | Default | Description |
| ---------------- | -------------------------------- | --------- | ---------------------------------------------------------------------------- |
| showIcon | `boolean \| undefined` | `true` | Whether to show the leading icon. Default: true. |
| error | `boolean \| undefined` | `false` | Whether the input has an error state. Sets `aria-invalid` and error styling. |
| disabled | `boolean \| undefined` | `false` | Whether the input is disabled. Prevents user interaction. |
| readOnly | `boolean \| undefined` | `false` | Whether the input is read-only. Segments are focusable but not editable. |
| placeholder | `string \| undefined` | | Placeholder shown when no value is selected. |
| size | `TemporalInputSize \| undefined` | `default` | Visual size. Default: 'default' (36px). |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| data-testid | `string \| undefined` | | |
| value | `DateValue \| null \| undefined` | | |
| defaultValue | `DateValue \| null \| undefined` | | |
| onChange | `complex` | | |
| minValue | `DateValue \| undefined` | | |
| maxValue | `DateValue \| undefined` | | |
| placeholderValue | `DateValue \| undefined` | | |
| name | `string \| undefined` | | |
| isRequired | `boolean \| undefined` | | Marks the field as required in assistive tech and HTML form validation. |
| granularity | `"day" \| undefined` | `day` | |
| showTimeDropdown | `undefined` | | |
| timeStep | `undefined` | `30` | |
---
url: https://wallarm.github.io/design-system/components/date-range-input.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# DateRangeInput
Two dates in one field, for a period the reader knows — pair it with `Calendar` in range mode when they need to see the months to choose. Same rules as `DateInput`: React Aria date objects, and order and hour cycle from `DateFormatProvider` rather than per input.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/DateRangeInput) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-date-daterangeinput--overview)
```tsx
import { DateRangeInput, type DateRangeInputProps } from '@wallarm-org/design-system/DateRangeInput';
export const Basic = args => {
return ;
};
```
## Usage
```tsx
import { DateRangeInput, type DateRangeInputProps } from '@wallarm-org/design-system/DateRangeInput';
```
## Examples
### With icon
Decoration again, not a trigger — reach for `Calendar` when the icon should open a picker.
```tsx
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const WithIcon = () => (
);
```
### States
Default, disabled and error. The error applies to the range as a whole, since a start without
an end is not yet an answer.
```tsx
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const States = () => (
);
```
### Sizes
The shared height scale. A range is wider than a single date, so check it against the column
it lives in.
```tsx
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const Sizes = () => (
);
```
### Filled
A complete range beside an empty one, which is the clearest way to see how much width the
filled state needs.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { VStack } from '@wallarm-org/design-system/Stack';
export const Filled = () => (
);
```
### Granularity
Both ends share one granularity. Add time only when the period genuinely turns on it — a
report covering part of a day rather than whole days.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const Granularity = () => (
);
```
### Date order comparison
Day-first against month-first from the provider, doubly worth seeing here because an
ambiguous range is twice as easy to misread.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const DateOrderComparison = () => (
);
```
### Hour cycle by context
12-hour against 24-hour from the provider, with no per-input override.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const HourCycleByContext = () => (
);
```
### Read only
Both ends legible, no edit affordances.
```tsx
import { DateRangeInput } from '@wallarm-org/design-system/DateRangeInput';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const ReadOnly = () => (
);
```
## Props
| Prop | Type | Default | Description |
| ---------------- | -------------------------------- | ------- | ---------------------------------------------------------------------------- |
| showIcon | `boolean \| undefined` | | Whether to show the leading icon. Default: true. |
| error | `boolean \| undefined` | | Whether the input has an error state. Sets `aria-invalid` and error styling. |
| disabled | `boolean \| undefined` | | Whether the input is disabled. Prevents user interaction. |
| readOnly | `boolean \| undefined` | | Whether the input is read-only. Segments are focusable but not editable. |
| placeholder | `string \| undefined` | | Placeholder shown when no value is selected. |
| size | `TemporalInputSize \| undefined` | | Visual size. Default: 'default' (36px). |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| data-testid | `string \| undefined` | | |
| value | `complex` | | Controlled value — range with start and end dates. |
| defaultValue | `complex` | | Default value for uncontrolled mode. |
| onChange | `complex` | | Callback fired when the range value changes. Receives `null` on clear. |
| granularity | `"day" \| undefined` | | |
| showTimeDropdown | `undefined` | | |
| timeStep | `undefined` | | |
### DateRangeEndValue
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| data-testid | `string \| undefined` | | |
### DateRangeStartValue
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| data-testid | `string \| undefined` | | |
---
url: https://wallarm.github.io/design-system/components/time-input.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# TimeInput
A time typed segment by segment, the companion to `DateInput` — `showTimeDropdown` adds a list of times for when the reader would rather pick than type. It takes React Aria time objects, and hour cycle comes from `DateFormatProvider` app-wide rather than per input.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/TimeInput) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/inputs-date-timeinput--overview)
```tsx
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const Basic = () => {
return ;
};
```
## Usage
```tsx
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
```
## Examples
### States
Default, disabled and error, matching `DateInput` so a form containing both stays consistent.
```tsx
import { Time } from '@wallarm-org/design-system';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const States = () => (
);
```
### Icon
A clock icon as decoration. It opens nothing on its own — the dropdown is a separate opt-in.
```tsx
import { Time } from '@wallarm-org/design-system';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const Icon = () => (
);
```
### Sizes
The shared height scale, so a time field lines up beside a date field.
```tsx
import { Time } from '@wallarm-org/design-system';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const Sizes = () => (
);
```
### Filled
A held value beside a placeholder, with the clear affordance appearing only once there is a
value.
```tsx
import { Time } from '@wallarm-org/design-system';
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { VStack } from '@wallarm-org/design-system/Stack';
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const Filled = () => (
);
```
### Granularity
Hour, minute or second. Drop to `hour` where minutes would imply a precision the system does
not honour.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const Granularity = () => (
);
```
### Time dropdown steps
`timeStep` sets the spacing of the offered times — fifteen, thirty or sixty minutes. Coarser
steps make the list scannable; the reader can still type an exact time.
```tsx
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const TimeDropdownSteps = () => (
);
```
### Read only
Value legible, every edit affordance gone, as on `DateInput`.
```tsx
import { Time } from '@wallarm-org/design-system';
import { DateFormatProvider } from '@wallarm-org/design-system/DateFormatProvider';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { TimeInput } from '@wallarm-org/design-system/TimeInput';
export const ReadOnly = () => (
);
```
## Props
| Prop | Type | Default | Description |
| ---------------- | ----------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------- |
| showIcon | `boolean \| undefined` | `true` | Whether to show the leading icon. Default: true. |
| error | `boolean \| undefined` | `false` | Whether the input has an error state. Sets `aria-invalid` and error styling. |
| disabled | `boolean \| undefined` | `false` | Whether the input is disabled. Prevents user interaction. |
| readOnly | `boolean \| undefined` | `false` | Whether the input is read-only. Segments are focusable but not editable. |
| placeholder | `string \| undefined` | | Placeholder shown when no value is selected. |
| size | `TemporalInputSize \| undefined` | `default` | Visual size. Default: 'default' (36px). |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| data-testid | `string \| undefined` | | |
| value | `TimeValue \| null \| undefined` | | |
| defaultValue | `TimeValue \| null \| undefined` | | |
| onChange | `complex` | | |
| minValue | `TimeValue \| undefined` | | |
| maxValue | `TimeValue \| undefined` | | |
| placeholderValue | `TimeValue \| undefined` | | |
| name | `string \| undefined` | | |
| isRequired | `boolean \| undefined` | | Marks the field as required in assistive tech and HTML form validation. |
| showTimeDropdown | `boolean \| undefined` | | Show dropdown for time selection with arrow navigation. |
| timeStep | `number \| undefined` | `30` | Time interval in minutes for dropdown options. Default: 30. |
| granularity | `TimeInputGranularity \| undefined` | `minute` | Smallest editable unit. - 'hour': hours only - 'minute': hours and minutes (default) - 'second': hours, minutes, and seconds |
---
url: https://wallarm.github.io/design-system/components/accordion.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Accordion
Stacks sections behind their own headers and opens each on demand — reach for `Tabs` when a few primary sections get switched between often, and leave content on the page when people read most of it anyway. `primary` and `secondary` are inline rows for grouping; `section` is the bordered panel, and the only variant with room for `AccordionActions`.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Accordion) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/data-display-accordion--overview)
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Text } from '@wallarm-org/design-system/Text';
export const Primary = () => (
Title
{sampleText}
Title
{sampleText}
);
```
## Usage
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Text } from '@wallarm-org/design-system/Text';
```
## Examples
### Secondary
A 24px row in small medium-weight muted type with no hover fill, for dense or
nested groups where the header should sit behind the content it labels.
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Text } from '@wallarm-org/design-system/Text';
export const Secondary = () => (
Title
{sampleText}
Title
{sampleText}
);
```
### Section
`section` draws each item as a bordered panel row — title left, chevron right —
and the title takes inline content, here a `NumericBadge` carrying a count.
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { NumericBadge } from '@wallarm-org/design-system/NumericBadge';
import { Text } from '@wallarm-org/design-system/Text';
export const Section = () => (
Title 2
{sampleText}
Title 2
{sampleText}
);
```
### Section with actions
`AccordionActions` renders beside the trigger rather than inside it, so buttons in
the header act on the section without toggling it open.
```tsx
import { Accordion, AccordionActions, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Button } from '@wallarm-org/design-system/Button';
import { NumericBadge } from '@wallarm-org/design-system/NumericBadge';
import { Text } from '@wallarm-org/design-system/Text';
export const SectionWithActions = () => (
Title 2
Delete
Edit
{sampleText}
Title 2
Delete
Edit
{sampleText}
);
```
### Long title primary
Titles truncate to one line rather than wrapping — the row is a fixed height, so
reword a long header instead of relying on the ellipsis.
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Text } from '@wallarm-org/design-system/Text';
export const LongTitlePrimary = () => (
{longTitleText}
{sampleText}
);
```
### Long title secondary
The same 320px column at `secondary` size, where smaller type and no row padding
leave a little more of the title visible before the ellipsis.
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Text } from '@wallarm-org/design-system/Text';
export const LongTitleSecondary = () => (
{longTitleText}
{sampleText}
);
```
### Multiple
`multiple` keeps several sections open at once, for when people reference sections
on their own terms — not when the sections have to be read side by side, which is
a layout rather than an accordion.
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Text } from '@wallarm-org/design-system/Text';
export const Multiple = () => (
Section A
{sampleText}
Section B
{sampleText}
Section C
{sampleText}
);
```
### Disabled
`disabled` on a single `AccordionItem` fades the row and blocks its toggle while the
rest stay live; the same prop on `Accordion` locks every row at once.
```tsx
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@wallarm-org/design-system/Accordion';
import { Text } from '@wallarm-org/design-system/Text';
export const Disabled = () => (
Available
{sampleText}
Disabled item
{sampleText}
);
```
## Props
| Prop | Type | Default | Description |
| ------------- | ------------------------------- | --------- | -------------------------------------------------------------------- |
| variant | `AccordionVariant \| undefined` | `primary` | Visual variant of the accordion |
| multiple | `boolean \| undefined` | `false` | Whether multiple items can be expanded at the same time |
| collapsible | `boolean \| undefined` | `true` | Whether an expanded item can be collapsed (when `multiple` is false) |
| value | `string[] \| undefined` | | Controlled expanded item values |
| defaultValue | `string[] \| undefined` | | Initial expanded item values for uncontrolled usage |
| onValueChange | `complex` | | Callback fired when expanded items change |
| disabled | `boolean \| undefined` | | Disable all interaction |
| data-testid | `string \| undefined` | | |
### AccordionActions
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AccordionContent
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| asChild | `boolean \| undefined` | | Use the provided child element as the default rendered element, combining their props and behavior. |
### AccordionItem
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| value \* | `string` | | The value of the accordion item. |
| disabled | `boolean \| undefined` | | Whether the accordion item is disabled. |
| asChild | `boolean \| undefined` | | Use the provided child element as the default rendered element, combining their props and behavior. |
### AccordionTrigger
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| name | `string \| undefined` | | |
| value | `string \| number \| readonly string[] \| undefined` | | |
| asChild | `boolean \| undefined` | | Use the provided child element as the default rendered element, combining their props and behavior. |
## Explorer
---
url: https://wallarm.github.io/design-system/components/attribute.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Attribute
Pairs one field's label with its value on a detail page — reach for `Table` when the same fields repeat across many objects, and `Field` when the value is being collected rather than read. The value slot takes whichever display component fits the data and hosts `InlineEdit` when the field is editable in place, while `AttributeActions` hangs a per-value menu off it.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Attribute) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/data-display-attribute--overview)
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Text } from '@wallarm-org/design-system/Text';
export const Default = () => (
Request ID
abc-123-def-456
);
```
## Usage
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Text } from '@wallarm-org/design-system/Text';
```
## Examples
### With description
`AttributeLabelDescription` adds a line that stays under the label, for a field
whose name does not explain itself.
```tsx
import { Attribute, AttributeLabel, AttributeLabelDescription, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Text } from '@wallarm-org/design-system/Text';
export const WithDescription = () => (
Created at
The time when the request was first received
April 9, 2026, 14:32
);
```
### With info
`AttributeLabelInfo` puts the same explanation behind a hover tooltip instead,
and reads correctly on either side of the label text.
```tsx
import { Attribute, AttributeLabel, AttributeLabelInfo, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Text } from '@wallarm-org/design-system/Text';
export const WithInfo = () => (
Request ID
Unique identifier assigned to each incoming request
abc-123-def-456
Unique identifier assigned to each incoming request
Request ID
abc-123-def-456
);
```
### With link
The label row takes inline content of its own, here a `Link` out to whatever
explains the field — the value slot stays reserved for the data.
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Link } from '@wallarm-org/design-system/Link';
import { Text } from '@wallarm-org/design-system/Text';
export const WithLink = () => (
Source
View docs
API Gateway
);
```
### Empty
Four ways a missing value can read: composed by hand, `isEmpty` on its own —
which swaps the value for an em dash and ignores whatever children it had — and
`isEmpty` alongside an `AttributeEmptyDescription`, which appears only in that
state and can carry the link out to the fix.
```tsx
import { Attribute, AttributeEmptyDescription, AttributeLabel, AttributeLabelDescription, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Link } from '@wallarm-org/design-system/Link';
import { Text } from '@wallarm-org/design-system/Text';
export const Empty = () => (
Region
Manual composition
Region
This text is replaced by the em-dash placeholder
Owner
Not yet assigned
SSO provider
Not connected —{' '}
set up integration
);
```
### Loading
`loading` skeletons both slots and ignores the children, so a panel can be laid
out before its data lands; it wins over `isEmpty`.
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
export const Loading = () => (
);
```
### Composition
The display components sanctioned inside a value — `Badge`, `FormatDateTime`,
`OverflowList` of `Tag`s, `Ip` and `IpList`, `Link`, `InlineCodeSnippet`, `Code` —
each formatting the data itself rather than receiving a pre-built string.
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Badge } from '@wallarm-org/design-system/Badge';
import { Code } from '@wallarm-org/design-system/Code';
import { InlineCodeSnippet } from '@wallarm-org/design-system/CodeSnippet';
import { FormatDateTime } from '@wallarm-org/design-system/FormatDateTime';
import { Ip, IpAddress, IpCountry, IpList, IpProvider } from '@wallarm-org/design-system/Ip';
import { Link } from '@wallarm-org/design-system/Link';
import { OverflowList } from '@wallarm-org/design-system/OverflowList';
import { Tag } from '@wallarm-org/design-system/Tag';
import { Text } from '@wallarm-org/design-system/Text';
export const Composition = () => (
Status
Blocked
First seen
Attack type
{item} }
/>
Sessions
3 sessions
Users
{item} }
/>
IPs
142.198.167.52
Azure
34.74.73.20
AWS
34.74.73.20
GCP
10.0.0.1
192.168.1.1
Source IP
142.198.167.52
Azure
Created at
Documentation
View full report
Payload (inline)
Payload (code)
{'{ "action": "login", "user_id": 42 }'}
);
```
### With actions
`AttributeActions` turns the value into a `DropdownMenu` trigger: the row picks up
a hover surface and the menu carries the actions that belong to that one value.
```tsx
import { Attribute, AttributeActions, AttributeActionsTarget, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Badge } from '@wallarm-org/design-system/Badge';
import { Code } from '@wallarm-org/design-system/Code';
import { InlineCodeSnippet } from '@wallarm-org/design-system/CodeSnippet';
import { FormatDateTime } from '@wallarm-org/design-system/FormatDateTime';
import { Ip, IpAddress, IpCountry, IpList, IpProvider } from '@wallarm-org/design-system/Ip';
import { Link } from '@wallarm-org/design-system/Link';
import { OverflowList } from '@wallarm-org/design-system/OverflowList';
import { Tag } from '@wallarm-org/design-system/Tag';
import { Text } from '@wallarm-org/design-system/Text';
export const WithActions = () => (
Status
Blocked
{renderActionsItems()}
First seen
{renderActionsItems()}
Attack type
{item} }
/>
{renderActionsItems()}
Sessions
3 sessions
{renderActionsItems()}
Users
{item} }
/>
{renderActionsItems()}
IPs
142.198.167.52
Azure
34.74.73.20
AWS
34.74.73.20
GCP
10.0.0.1
192.168.1.1
{renderActionsItems()}
Source IP
142.198.167.52
Azure
{renderActionsItems()}
Created at
{renderActionsItems()}
Documentation
View full report
{renderActionsItems()}
Payload (inline)
{renderActionsItems()}
Payload (code)
{'{ "action": "login", "user_id": 42 }'}
{renderActionsItems()}
);
```
### Horizontal
`orientation='horizontal'` sets the label beside the value in a cell clamped
between 100 and 256px, so values line up down a panel.
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Badge } from '@wallarm-org/design-system/Badge';
export const Horizontal = () => (
);
```
### Horizontal truncation
Horizontal cells clip with an ellipsis rather than wrapping — the label at its set
width, 100px unless you widen it, and the value once its own content overruns.
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Text } from '@wallarm-org/design-system/Text';
export const HorizontalTruncation = () => (
Short
Fits in 256px label cell
This label text is much longer than 256 pixels and must be truncated
Value
Users
artem@acme.com, uxd@acme.com, ops@acme.com, security@acme.com, admin@acme.com
);
```
### Horizontal composition
The same value components in the horizontal layout, where a wide one such as
`IpList type='horizontal'` needs its row to span both columns.
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Badge } from '@wallarm-org/design-system/Badge';
import { FormatDateTime } from '@wallarm-org/design-system/FormatDateTime';
import { Ip, IpAddress, IpCountry, IpList, IpProvider } from '@wallarm-org/design-system/Ip';
import { OverflowList } from '@wallarm-org/design-system/OverflowList';
import { Tag } from '@wallarm-org/design-system/Tag';
import { Text } from '@wallarm-org/design-system/Text';
export const HorizontalComposition = () => (
Status
Blocked
First seen
Attack type
{item} }
/>
Last seen
Sessions
3 sessions
Users
{item} }
/>
IPs
142.198.167.52
Azure
34.74.73.20
AWS
34.74.73.20
GCP
);
```
### Horizontal loading
The horizontal skeleton is a 16px line against the vertical 24px, matching the
single-line row it stands in for.
```tsx
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
export const HorizontalLoading = () => (
);
```
### Horizontal empty
The same four cases side by side, with `width={160}` on the label so the
descriptions have somewhere to sit.
```tsx
import { Attribute, AttributeEmptyDescription, AttributeLabel, AttributeLabelDescription, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Link } from '@wallarm-org/design-system/Link';
import { Text } from '@wallarm-org/design-system/Text';
export const HorizontalEmpty = () => (
Region
Manual composition
Region
This text is replaced by the em-dash placeholder
Owner
Not yet assigned
SSO provider
Not connected —{' '}
set up integration
);
```
### Horizontal with actions
By default the value keeps its own interactivity: a link, or a snippet's copy
button, still does its own job, and the menu opens from the space around it.
```tsx
export const HorizontalWithActions = () => (
{renderActionsAttributes(false)}
);
```
### Horizontal with actions menu only
`disableNestedInteractive` renders the value inert, so a click anywhere on the row
opens the menu — for values with nothing worth clicking of their own.
```tsx
export const HorizontalWithActionsMenuOnly = () => (
{renderActionsAttributes(true)}
);
```
### With inline edit
`Attribute` > `AttributeValue` > `InlineEdit` is the sanctioned nesting for an
editable field — the value slot supplies the overflow room the editor needs, so
never add your own.
```tsx
export const WithInlineEdit = () => ;
```
### Horizontal with inline edit
The same editor in a horizontal row, where it takes the width of the value cell
rather than the panel.
```tsx
export const HorizontalWithInlineEdit = () => (
);
```
### Mixed
A plain value, an actions target and an inline editor stacked in one column: the
text has to start at the same place in all three, which is what the value slot's
7px pull is for — it moves the hover box left, not the text.
```tsx
export const Mixed = () => ;
```
### Horizontal mixed
The same three hosts horizontally, where the label cell fixes the start of every
value and the pull has to fit inside the 12px gap between the two cells.
```tsx
export const HorizontalMixed = () => ;
```
## Props
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| loading | `boolean \| undefined` | `false` | Show skeleton placeholders instead of children |
| isEmpty | `boolean \| undefined` | `false` | When true, `AttributeValue` renders the em-dash placeholder instead of its children, and any `AttributeEmptyDescription` inside the tree renders (it returns `null` otherwise). Ignored while `loading` is true (skeleton wins). |
| orientation | `AttributeOrientation \| undefined` | `vertical` | Layout direction of the label and value. - `vertical` (default): label above value - `horizontal`: label on the left, value on the right |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| data-testid | `string \| undefined` | | |
### AttributeActions
| Prop | Type | Default | Description |
| ----------------- | ---------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| open | `boolean \| undefined` | | |
| defaultOpen | `boolean \| undefined` | | |
| onOpenChange | `((open: boolean) => void) \| undefined` | | |
| modal | `boolean \| undefined` | | |
| anchorPoint | `{ x: number; y: number; } \| undefined` | | Virtual anchor point for positioning without a trigger element |
| positioning | `PositioningOptions \| undefined` | | Override default positioning config |
| highlightedValue | `string \| null \| undefined` | | Programmatically control which item is highlighted |
| onHighlightChange | `((details: HighlightChangeDetails) => void) \| undefined` | | Callback when the highlighted item changes (e.g. on mouse hover) |
| closeOnSelect | `boolean \| undefined` | | Whether selecting an item closes the menu (default true) |
| onRequestDismiss | `((event: LayerDismissEvent) => void) \| undefined` | | Called when another layer asks this menu to dismiss — e.g. a layer below it in the stack is removed. Call `event.preventDefault()` to keep the menu open. |
| ids | `ElementIds \| undefined` | | Override the trigger element's id. Pass the same id to another compound component's `ids.trigger` (e.g. `Tooltip`, `Popover`) to compose their triggers on one element via nested `asChild` without an extra wrapper node — otherwise the outer trigger's id wins and the other component can't find its own anchor. |
| data-testid | `string \| undefined` | | |
### AttributeActionsContent
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AttributeActionsItem
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------------- | ------- | ----------------------------------------------------------------- |
| inset | `boolean \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| onSelect | `(() => void) \| undefined` | | |
| value | `string \| undefined` | | Explicit value for programmatic highlighting via highlightedValue |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| variant | `{ default: string; brand: string; destructive: string; }` | | |
| data-testid | `string \| undefined` | | |
### AttributeActionsTarget
| Prop | Type | Default | Description |
| ------------------------ | ---------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| disableNestedInteractive | `boolean \| undefined` | `false` | When true, descendants are rendered `inert` — they cannot be clicked, focused, or read by assistive tech as actionable. Any interaction with the target (mouse or keyboard) opens the AttributeActions dropdown. Default `false` — descendants keep their own behavior; clicking them does not open the dropdown. The dropdown opens only when the surrounding target area is clicked. |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AttributeEmptyDescription
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AttributeLabel
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| width | `number \| undefined` | `MIN_LABEL_WIDTH` | Width of the label cell in horizontal orientation, in pixels. Defaults to MIN_LABEL_WIDTH. Clamped to \[MIN_LABEL_WIDTH, MAX_LABEL_WIDTH]. Ignored in vertical orientation. |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AttributeLabelDescription
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AttributeLabelInfo
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AttributeValue
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
## Explorer
---
url: https://wallarm.github.io/design-system/components/avatar.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Avatar
A person's photo on a rounded plate, falling back to initials or an icon while it loads, when it fails, or when there is none. It is decorative beside a visible name; standing alone, give it `aria-label` so it reads as an image. Add `AvatarContent` with `AvatarName` (and an optional `AvatarDescription`) for the user item: plate and name in one row. To let people change it, make the avatar the `FileUploadTrigger` (`asChild`) and give it `as='button'` — picking only previews, and your form uploads.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Avatar) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/data-display-avatar--overview)
```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
export const Basic = () => (
);
```
## Usage
```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
```
## Examples
### Sizes
`sm` (20px) and `md` (24px, the nav-rail plate) are the compact sizes for user items; `lg` (32px,
the default) is for rows and forms. Each row shows a photo, initials and the icon, then the same
three inside a Branded frame.
```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const Sizes = () => (
{SIZES.map(size => (
))}
);
```
### With name
The user item (Figma 12557:5856): the plate and the name in one row, 6px apart. The name is
`text-sm` at every size and truncates with an ellipsis when the row is narrow.
```tsx
import { Avatar, AvatarContent, AvatarFallback, AvatarImage, AvatarName } from '@wallarm-org/design-system/Avatar';
import { VStack } from '@wallarm-org/design-system/Stack';
export const WithName = () => (
{(['sm', 'md'] as const).map(size => (
Artem Miskevich
Artem Miskevich
Maximilian Alexander-Montgomery
))}
);
```
### With description
`AvatarDescription` adds a secondary line under the name — the email in the account menu. Both lines truncate.
```tsx
import { Avatar, AvatarContent, AvatarDescription, AvatarFallback, AvatarImage, AvatarName } from '@wallarm-org/design-system/Avatar';
import { VStack } from '@wallarm-org/design-system/Stack';
export const WithDescription = () => (
Artem Miskevich
admin@wallarm.com
Artem Miskevich
admin@wallarm.com
Maximilian Alexander-Montgomery
maximilian.alexander-montgomery@wallarm.com
);
```
### Fallback
Initials are the first letters of the first and last word of `name`; without a name the person icon shows, whether the photo is missing or failed to load.
```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { HStack } from '@wallarm-org/design-system/Stack';
export const Fallback = () => (
);
```
### Custom icon
`icon` replaces the default person glyph whenever `name` gives no initials.
```tsx
import { Avatar, AvatarFallback } from '@wallarm-org/design-system/Avatar';
import { Activity } from '@wallarm-org/design-system/icons';
export const CustomIcon = () => (
);
```
### Branded
Inside a Branded frame the empty plate takes the brand tint and stroke, initials and the icon take the brand colour, and a photo is unchanged.
```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { HStack } from '@wallarm-org/design-system/Stack';
export const Branded = () => (
);
```
### Click to upload
Click the avatar, or focus it and press Enter, to pick a photo — hover and focus show the edit
icon, and the pick only previews until your form saves.
```tsx
import { Avatar, AvatarFallback, AvatarImage, AvatarOverlay } from '@wallarm-org/design-system/Avatar';
import { FileUpload, FileUploadError, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';
export const ClickToUpload = () => {
const { src, files, setFiles } = useAvatarUpload();
return (
);
};
```
### With actions
The settings-row pattern: explicit Remove and Replace buttons beside a plain avatar, with the accepted formats and size spelled out.
```tsx
import { Avatar, AvatarFallback, AvatarImage } from '@wallarm-org/design-system/Avatar';
import { Button } from '@wallarm-org/design-system/Button';
import { FieldDescription, FieldLabel } from '@wallarm-org/design-system/Field';
import { FileUpload, FileUploadError, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';
import { RefreshCcw, Trash2 } from '@wallarm-org/design-system/icons';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const WithActions = () => {
const { src, files, setFiles, remove } = useAvatarUpload(PHOTO);
return (
Avatar
PNG, JPEG, or WebP · up to 512 KB
{src && (
)}
);
};
```
### Uploading
While the new photo uploads, disable the trigger and force the overlay with `visible` so it holds a `Loader`.
```tsx
import { Avatar, AvatarFallback, AvatarImage, AvatarOverlay } from '@wallarm-org/design-system/Avatar';
import { FileUpload, FileUploadTrigger } from '@wallarm-org/design-system/FileUpload';
import { Loader } from '@wallarm-org/design-system/Loader';
export const Uploading = () => (
);
```
## Props
| Prop | Type | Default | Description |
| -------------- | ------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------- |
| onStatusChange | `((details: StatusChangeDetails) => void) \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| size | `{ sm: string; md: string; lg: string; }` | `lg` | |
| data-testid | `string \| undefined` | | |
| as | `"span" \| undefined` | | Root tag. 'button' makes the avatar itself interactive (e.g. inside FileUploadTrigger asChild). |
| asChild | `false \| undefined` | `false` | Render the single child element as the root instead of `as`; takes precedence over it. |
| type | `"button" \| "submit" \| "reset" \| undefined` | | |
| disabled | `boolean \| undefined` | | |
| name | `string \| undefined` | | |
| value | `string \| number \| readonly string[] \| undefined` | | |
### AvatarContent
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AvatarDescription
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AvatarFallback
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------- |
| name | `string \| undefined` | | Initials are taken from this: first letter of the first and of the last word (first word only at `sm`). |
| icon | `ComponentType \| undefined` | | Shown when there are no children and `name` gives no initials. |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AvatarImage
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| alt | `string \| undefined` | | |
| height | `string \| number \| undefined` | | |
| loading | `"eager" \| "lazy" \| undefined` | | |
| src | `string \| undefined` | | |
| width | `string \| number \| undefined` | | |
### AvatarName
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### AvatarOverlay
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | -------------------------------------------------------------------------------- |
| visible | `boolean \| undefined` | `false` | Keep it shown regardless of hover — e.g. a `Loader` while the new photo uploads. |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
---
url: https://wallarm.github.io/design-system/components/card.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# Card
Holds one self-contained thing — a metric, an integration, a saved view — in a block you can compare against its neighbours in a grid; reach for `Table` when the same fields repeat down a list, and for a `Heading` when you only mean to section a page. Passing any handler turns the whole card into the target: it becomes focusable and picks up hover and pressed overlays, while clicks on a link or button inside it stay with that control.
[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Card) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/data-display-card--overview)
```tsx
import { type MouseEvent, useState } from 'react';
import { Badge } from '@wallarm-org/design-system/Badge';
import { Button } from '@wallarm-org/design-system/Button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@wallarm-org/design-system/Card';
export const Basic = () => {
// `data-last-click` records which target handled the click. It renders nothing,
// and it is what the click-isolation e2e asserts on — the story used to prove
// the same thing by writing to the console.
const [lastClick, setLastClick] = useState<'card' | 'button' | 'none'>('none');
return (
{
onCardClick(e);
setLastClick('card');
}}
data-last-click={lastClick}
>
Card title
Badge
Card description
) => {
onButtonClick(e);
setLastClick('button');
}}
>
Button
);
};
```
## Usage
```tsx
import { Badge } from '@wallarm-org/design-system/Badge';
import { Button } from '@wallarm-org/design-system/Button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@wallarm-org/design-system/Card';
```
## Examples
### Variants
`primary` sits on the page surface and `secondary` on a tinted fill, each shown
plain, with a handler, and disabled — where the card fades and stops answering
the pointer.
```tsx
import { Badge } from '@wallarm-org/design-system/Badge';
import { Button } from '@wallarm-org/design-system/Button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@wallarm-org/design-system/Card';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const Variants = () => (
Primary card
Badge
This is a primary card variant
Button
Secondary card
Badge
This is a secondary card variant
Button
Primary card
Badge
This is a primary card variant with onClick event
Button
Secondary card
Badge
This is a secondary card variant with onClick event
Button
Primary card
Badge
This is a disabled primary card variant
Button
Secondary card
Badge
This is a disabled secondary card variant
Button
);
```
### Various content
What the block tolerates: body copy that wraps, a `CodeSnippet`, a whole `Field`,
a `CardTitle` with an `icon`, and a header standing on its own with nothing under it.
```tsx
import { Badge } from '@wallarm-org/design-system/Badge';
import { Button } from '@wallarm-org/design-system/Button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@wallarm-org/design-system/Card';
import { CodeSnippetActions, CodeSnippetCode, CodeSnippetContent, CodeSnippetCopyButton, CodeSnippetRoot } from '@wallarm-org/design-system/CodeSnippet';
import { Field, FieldDescription, FieldLabel } from '@wallarm-org/design-system/Field';
import { CircleDashed } from '@wallarm-org/design-system/icons';
import { Input } from '@wallarm-org/design-system/Input';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
export const VariousContent = () => (
Card title
Badge
Card description Very long! Even if it goes to the second line, it won't break anything.
It works perfectly! Can you see? Amazing yeah?
Button
Card title
Button
Card title
Badge
Label
Input description
Button
}>Card title
Card description.
}>Card title
);
```
## Props
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | --------- | ----------- |
| asChild | `boolean \| undefined` | `false` | |
| disabled | `boolean \| undefined` | `false` | |
| onClick | `MouseEventHandler \| undefined` | | |
| color | `{ primary: string; secondary: string; }` | `primary` | |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
| data-testid | `string \| undefined` | | |
### CardContent
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| asChild | `boolean \| undefined` | `false` | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### CardFooter
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| asChild | `boolean \| undefined` | `false` | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### CardHeader
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| asChild | `boolean \| undefined` | `false` | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| title | `string \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
### CardTitle
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------- | ------- | ----------- |
| icon | `ReactNode` | | |
| title | `string \| undefined` | | |
| defaultChecked | `boolean \| undefined` | | |
| defaultValue | `string \| number \| readonly string[] \| undefined` | | |
| autoFocus | `boolean \| undefined` | | |
| id | `string \| undefined` | | |
| tabIndex | `number \| undefined` | | |
| rel | `string \| undefined` | | |
| onChange | `ChangeEventHandler \| undefined` | | |
## Explorer
---
url: https://wallarm.github.io/design-system/components/code-editor.md
---
> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.
# CodeEditor
An editable code surface that is visually the same component as `CodeSnippet` and reuses its header, tabs, actions and show-more — reach for `CodeSnippet` whenever the code is only read, since a page that never renders `CodeEditorContent` never loads the editor engine. Colours come from the same `CodeSnippetAdapterProvider` (plain text without one), so a read-only editor matches the snippet except for a native thin scrollbar, a caret and selection drawn by the editor, and an HTTP JSON body that stays coloured when `Content-Type` has parameters or the body has blank lines. The adapter re-tokenizes the whole document after each pause in typing, which stays within a frame up to about 2 000 lines with Prism; longer documents still edit smoothly, their colours just settle a moment later. Like the snippet, the editor sits inside a `display: contents` wrapper that keeps it mounted across fullscreen, so space editors with their own `className` (for example `mt-16`) or a `gap` on the parent — not `space-*`, `divide-*` or child selectors on the parent. `lines`, `folds`, `completions` and `schema` are compared by identity, so memoise them or hoist them to module constants — a new value on every render reconfigures the editor. Autocomplete suggests language keywords and words already in the document for every language; HTTP methods and headers and JSON Schema values have their own contextual suggestions. Pass `completions` to add application-specific results. Syntax errors are best-effort: JSON reports the `JSON.parse` message, JavaScript and TypeScript use Babel, Lua uses luaparse (first error only), YAML and Python use their Lezer parsers, which still flag a few valid Python forms such as `lambda a, /, b` and parenthesised `with` items. `lines` and `folds` stay on their line numbers while typing; to keep a decoration on a piece of text, recompute it from `value` (for example `useMemo(() => linesFor(value), [value])`, or a `folds` function such as `getHttpFolds`). The editor injects `