GitHub

Chip Group

Single-select pill toggles — a horizontal row where exactly one chip is active. Each option carries a tone (neutral, neutral-strong, brand, or amber) that colors its active state. Arrow keys move, Enter or Space select. Form-associated; emits a composed change event.

Stable wc import '@esa/ecology/esa-chip-group';

Preview

Set the options property to a list of { value, label, tone? }. Exactly one chip is active, tracked by value.

<esa-chip-group label="Status filter" value="open"></esa-chip-group>

<script>
  document.querySelector('esa-chip-group').options = [
    { value: 'all', label: 'All', tone: 'neutral' },
    { value: 'open', label: 'Open', tone: 'brand' },
    { value: 'flagged', label: 'Flagged', tone: 'amber' },
  ];
</script>

Tones

Each option's tone sets the palette of its active state, mapped to Ecology semantic tokens. neutral is the default.

{ value: 'n',  label: 'Neutral',        tone: 'neutral' }
{ value: 'ns', label: 'Neutral strong', tone: 'neutral-strong' }
{ value: 't',  label: 'Brand',           tone: 'brand' }
{ value: 'a',  label: 'Amber',          tone: 'amber' }

Sizes

Chips follow the shared form size scale.

<esa-chip-group size="xs" ...></esa-chip-group>
<esa-chip-group size="sm" ...></esa-chip-group>
<esa-chip-group size="md" ...></esa-chip-group>
<esa-chip-group size="lg" ...></esa-chip-group>

API

PropTypeDefaultDescription
options EsaChipOption[] | string [] Array of {'{ value, label, tone? }'}, or a JSON string of the same when set as an attribute. tone is one of neutral, neutral-strong, brand, amber.
value string '' The selected option value. Exactly one chip is active at a time.
size 'xs' | 'sm' | 'md' | 'lg' 'md' Chip size on the shared form scale.
name string '' Field name the selected value is submitted under.
label string '' Accessible label applied to the radiogroup (aria-label).

Events

EventTypeDefaultDescription
change CustomEvent<{ value: string }> Fired when the selection changes via click or keyboard. Composed and bubbling; detail.value is the newly selected value.

Accessibility

  • The host carries role="radiogroup" labeled by label (aria-label).
  • Each chip is a role="radio" with aria-checked; only the active chip is in the tab order (roving tabindex).
  • Arrow keys move selection with wrap-around; Home and End jump to the ends; selection follows arrow focus per the WAI-ARIA radio pattern.
  • Enter and Space select the focused chip.
  • The change event is composed and bubbles, so it can be caught on the host or an ancestor.

Theming surface

Every public token this component reads, extracted from its source at build time. The Resolves to column walks each token's real lineage — component → semantic → primitive → raw value — so you can see exactly where re-pointing it lands. A spoke re-skins by overriding these in its theme-<slug>.css; component internals are never edited. No hook for what you need? File it with /request-lego.

TokenTierResolves to
--focus-ring-color component #65ba74 via --color-border-focus--color-grass-8
--focus-ring-width component 2px
--form-font-size-lg component clamp(0.875rem, 0.77rem + 0.52vw, 1.125rem) via --type-size-300
--form-font-size-md component clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) via --type-size-200
--form-font-size-sm component clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --type-size-100
--form-font-size-xs component clamp(0.5rem, 0.44rem + 0.3vw, 0.625rem) via --type-size-050
--form-padding-x-lg component 1rem via --spacing-400
--form-padding-x-md component 0.75rem via --spacing-300
--form-padding-x-sm component 0.625rem via --spacing-250
--form-padding-x-xs component 0.5rem via --spacing-200
--color-border semantic #cecece via --color-gray-7
--color-border-strong semantic #bbbbbb via --color-gray-8
--color-primary-border semantic #b2ddb5 via --color-grass-6
--color-primary-strong semantic #2a7e3b via --color-grass-11
--color-primary-subtle semantic #f5fbf5 via --color-grass-2
--color-surface semantic #fcfcfc via --color-gray-1
--color-surface-sunken semantic #f0f0f0 via --color-gray-3
--color-text-primary semantic #202020 via --color-gray-12
--color-text-secondary semantic #646464 via --color-gray-11
--color-text-tertiary semantic #646464 via --color-gray-11
--color-warning-border semantic #f3d673 via --color-status-warning-border--color-yellow-6
--color-warning-strong semantic #ab6400 via --color-status-warning-strong--color-yellow-11
--color-warning-subtle semantic #fefbe9 via --color-status-warning-subtle--color-yellow-2
--transition-fast semantic 150ms ease
--radius-100 primitive 0.25rem
--spacing-100 primitive 0.25rem
--spacing-150 primitive 0.375rem
--spacing-200 primitive 0.5rem