Skip to content

Inputs

ChipGroup

Single choice among a few short options as chips that wrap, with a roving tabindex, a check icon and a field error.

<script>
  import { ChipGroup } from "@codeatlasdev/ui";
  let size = $state("");
</script>

<ChipGroup
  label="Sales team size"
  name="teamSize"
  bind:value={size}
  items={[{ value: "1", label: "Just me" }, { value: "2-5", label: "2 to 5" }]}
  error={errors.teamSize}
/>

Use it for one choice among 3 to 8 short options. RadioGroup is a vertical list or cards (too tall for short options) and SegmentedControl never wraps, so it overflows a 390px screen.

The group is a real radiogroup of native radios. Tab enters on the selected chip (or the first one) and leaves on the next Tab; the arrows, Home and End move focus and selection; Space and Enter select. Disabled chips are skipped. Below sm (600px) the chips sit in three equal columns, so five short options are two rows and never exceed the container; from sm they take their own width and wrap freely. The selected chip has a brand outline and fill plus a check icon, so colour is never the only signal, and every chip is at least 44px tall.

error renders role="alert" under the group and sets aria-invalid and aria-describedby on it; description is announced through aria-describedby too. name makes the value part of FormData on a plain form submit, and required blocks a native submit until a chip is picked. A visible label names the group; with hideLabel or without label, pass aria-label.

#720

Wrapping chips

Five short options: two rows at 390px, one row of chips on desktop.

Sales team size

Reference #720 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { ChipGroup } from '@codeatlasdev/ui';

  let size = $state('2-5');

  const sizes = [
    { value: '1', label: 'Just me' },
    { value: '2-5', label: '2 to 5' },
    { value: '6-20', label: '6 to 20' },
    { value: '21-50', label: '21 to 50' },
    { value: '50+', label: '50+' },
  ];
</script>

<ChipGroup label="Sales team size" items={sizes} bind:value={size} />
#721

Required choice with error

Inside a native form: submit without a selection to see the alert.

Sales team size

Reference #721 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Button, ChipGroup } from '@codeatlasdev/ui';

  let size = $state('');
  let error = $state<string | undefined>();

  const sizes = [
    { value: '1', label: 'Just me' },
    { value: '2-5', label: '2 to 5' },
    { value: '6-20', label: '6 to 20' },
  ];

  function submit(e: SubmitEvent) {
    e.preventDefault();
    error = size ? undefined : 'Choose a team size.';
  }
</script>

<form onsubmit={submit} novalidate>
  <ChipGroup
    label="Sales team size"
    name="teamSize"
    items={sizes}
    bind:value={size}
    {error}
    required
    onchange={() => (error = undefined)}
  />
  <Button type="submit" variant="filled">Continue</Button>
</form>

Props

PropTypeDefaultDescription
itemsChipGroupItem[]–Options: { value, label, disabled? }.
valuestring''Bindable selected value. '' means nothing selected.
labelstring–Visible title and accessible name of the group.
hideLabelbooleanfalseKeeps label for assistive tech only.
descriptionstring–Helper text under the group (aria-describedby).
errorstring–Field-level error: role="alert", aria-invalid and aria-describedby on the group, red outline on the unselected chips.
namestring–Native name of the radios, so the value is in FormData on a plain form submit. Unset: an internal id groups them.
requiredbooleanfalseNative required: a form cannot submit until a chip is picked. Adds an asterisk to the label.
disabledbooleanfalseDisables the whole group.
aria-labelstring–Accessible name when there is no visible label.
classstring–Extra classes on the root.
onchange(value: string) => void–Called when the selection changes.