‮Skip to content‬
uiify

Combobox

‮Single- or multi-value ARIA combobox with filtering.‬

‮Browsers‬
Chrome 114+Firefox 125+Safari 17+
‮Tier‬
2
‮Bundle compressed‬
5.18 kB
‮Forced layout budget‬
0 ms
‮Axe budget‬
0
‮SSR compatible‬
‮Yes‬
‮Published contract‬
‮Test harness‬

‮Playground‬

Combobox

Default

Apple
Banana
Cherry
Date

Multiple

ReactUI
React
UI
TypeScript
Accessibility
Design systems

Open

Apple
Banana
Cherry
Date

Large

"use client";

import { Combobox } from "@gunkin/uiify";

const skills = [
  { label: "React", value: "React" },
  { label: "UI", value: "UI" },
  { label: "TypeScript", value: "TypeScript" },
  { label: "Accessibility", value: "Accessibility" },
];

export default function Example() {
  return (
    <div className="">
      <Combobox.Root multiple defaultValue={["React", "UI"]} items={skills}>
        <Combobox.Control className="">
          <Combobox.Tags className="" />
          <Combobox.Input className="" aria-label="Add tag" placeholder="Add tag" />
        </Combobox.Control>
        <Combobox.Content className="">
          {(items) =>
            items.length === 0 ? (
              <Combobox.Empty>Nothing matched. Try another search.</Combobox.Empty>
            ) : (
              items.map((item) => <Combobox.Item className="" item={item} key={item.value} />)
            )
          }
        </Combobox.Content>
      </Combobox.Root>
    </div>
  );
}
/* Combobox — text input + listbox surface + option rows. The active option is
   tracked via aria-activedescendant, surfaced here as [data-highlighted]. */

@layer components {
  [data-uiify-combobox] {
    position: relative;
    inline-size: 100%;
    max-inline-size: 18rem;
  }

  [data-uiify-combobox] [data-part="control"] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-xs);
    inline-size: 100%;
    min-block-size: 2.5rem;
    padding-block: var(--space-sm);
    padding-inline: var(--space-md);
    border: var(--border-width) solid var(--input);
    border-radius: var(--radius-md);
    corner-shape: squircle;
    background-color: var(--card);
    color: var(--foreground);
    transition: var(--transition-colors);
  }

  [data-uiify-combobox] [data-part="control"]:focus-within {
    border-color: var(--primary);
  }

  [data-uiify-combobox] [data-part="control"][data-disabled],
  [data-uiify-combobox] [data-part="control"]:has([data-part="input"]:disabled) {
    opacity: 0.5;
    cursor: not-allowed;
  }

  [data-uiify-combobox] [data-part="control"] [data-part="tags"] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-xs);
  }

  [data-uiify-combobox] [data-part="control"] [data-part="tag"] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding-block: var(--space-xs);
    padding-inline: var(--space-sm);
    border-radius: var(--radius-xs);
    corner-shape: squircle;
    background-color: var(--accent);
    color: var(--accent-foreground);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
  }

  [data-uiify-combobox] [data-part="control"] [data-part="tag-remove"] {
    display: inline-grid;
    place-items: center;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    flex: 0 0 1.25rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius-xs);
    corner-shape: squircle;
    background: transparent;
    color: inherit;
    font: inherit;
    line-height: 1;
    cursor: pointer;
  }

  [data-uiify-combobox] [data-part="control"] [data-part="tag-remove"]:hover,
  [data-uiify-combobox] [data-part="control"] [data-part="tag-remove"]:focus-visible {
    background-color: var(--primary);
    color: var(--primary-foreground);
  }

  [data-uiify-combobox] [data-part="input"] {
    inline-size: 100%;
    padding-block: var(--space-sm);
    padding-inline: var(--space-md);
    border: var(--border-width) solid var(--input);
    border-radius: var(--radius-md);
    corner-shape: squircle;
    background-color: var(--card);
    color: var(--foreground);
    font-size: var(--uiify-control-text);
    transition: var(--transition-colors);
  }

  [data-uiify-combobox] [data-part="input"]:focus-visible {
    border-color: var(--primary);
  }

  [data-uiify-combobox] [data-part="control"] [data-part="input"] {
    flex: 1 1 5rem;
    min-inline-size: 5rem;
    inline-size: auto;
    padding-block: var(--space-xs);
    padding-inline: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    outline: none;
    box-shadow: none;
  }

  [data-uiify-combobox] [data-part="content"]:where(:not([popover]), :popover-open) {
    display: flex;
  }

  [data-uiify-combobox] [data-part="content"] {
    flex-direction: column;
    min-inline-size: 12rem;
    max-block-size: min(24rem, calc(100dvb - 2rem));
    overflow-block: auto;
    overscroll-behavior: contain;
    padding: var(--space-xs);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    corner-shape: squircle;
    background-color: var(--card);
    color: var(--foreground);
    box-shadow: var(--shadow-popover);
    transform-origin: var(--transform-origin, top);
  }

  [data-uiify-combobox] [data-part="content"][data-state="closed"] {
    display: none;
  }

  [data-uiify-combobox] [data-part="item"] {
    display: flex;
    align-items: center;
    inline-size: 100%;
    padding-block: var(--space-sm);
    padding-inline: var(--space-md);
    border: 0;
    border-radius: calc(var(--radius-lg) - var(--space-xs));
    corner-shape: squircle;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    text-align: start;
    cursor: pointer;
    user-select: none;
  }

  [data-uiify-combobox] [data-part="item"]:hover,
  [data-uiify-combobox] [data-part="item"]:focus-visible,
  [data-uiify-combobox] [data-part="item"][data-highlighted],
  [data-uiify-combobox] [data-part="item"][aria-selected="true"],
  [data-uiify-combobox] [data-part="item"][data-selected] {
    background-color: var(--accent);
    color: var(--accent-foreground);
    outline: none;
  }

  [data-uiify-combobox] [data-part="item"][aria-disabled="true"],
  [data-uiify-combobox] [data-part="item"][data-disabled] {
    opacity: 0.5;
    pointer-events: none;
  }

  [data-uiify-combobox] [data-part="empty"] {
    padding: var(--space-sm) var(--space-md);
    color: var(--muted-foreground);
    font-size: var(--text-sm);
  }

  @media (prefers-reduced-motion: no-preference) {
    [data-uiify-combobox] [data-part="content"] {
      transition: var(--transition-presence);
    }

    [data-uiify-combobox] [data-part="content"][popover]:not(:popover-open) {
      opacity: 0;
      scale: var(--presence-scale);
    }

    @starting-style {
      [data-uiify-combobox] [data-part="content"]:popover-open {
        opacity: 0;
        scale: var(--presence-scale);
      }
    }
  }
}
"use client";

import { Combobox } from "@gunkin/uiify";

const skills = [
  { label: "React", value: "React" },
  { label: "UI", value: "UI" },
  { label: "TypeScript", value: "TypeScript" },
  { label: "Accessibility", value: "Accessibility" },
];

export default function Example() {
  return (
    <div className="relative w-full max-w-72">
      <Combobox.Root multiple defaultValue={["React", "UI"]} items={skills}>
        <Combobox.Control
          className="
            flex flex-wrap items-center gap-xs w-full min-h-10 py-sm px-md border-[length:var(--border-width)]
            border-solid border-input rounded-md [corner-shape:squircle] bg-card text-foreground
            [transition:var(--transition-colors)] focus-within:border-primary data-[disabled]:opacity-50
            data-[disabled]:cursor-not-allowed has-[[data-part=input]:disabled]:opacity-50
            has-[[data-part=input]:disabled]:cursor-not-allowed
          "
        >
          <Combobox.Tags
            className="
              flex flex-wrap items-center gap-xs [&>[data-part=tag]]:inline-flex [&>[data-part=tag]]:items-center
              [&>[data-part=tag]]:gap-xs [&>[data-part=tag]]:py-xs [&>[data-part=tag]]:px-sm
              [&>[data-part=tag]]:rounded-xs [&>[data-part=tag]]:[corner-shape:squircle] [&>[data-part=tag]]:bg-accent
              [&>[data-part=tag]]:text-accent-foreground [&>[data-part=tag]]:text-xs [&>[data-part=tag]]:font-medium
              [&_[data-part=tag-remove]]:inline-grid [&_[data-part=tag-remove]]:place-items-center
              [&_[data-part=tag-remove]]:size-5 [&_[data-part=tag-remove]]:flex-none [&_[data-part=tag-remove]]:p-0
              [&_[data-part=tag-remove]]:border-0 [&_[data-part=tag-remove]]:rounded-xs
              [&_[data-part=tag-remove]]:bg-transparent [&_[data-part=tag-remove]]:text-inherit
              [&_[data-part=tag-remove]]:[font:inherit] [&_[data-part=tag-remove]]:leading-none
              [&_[data-part=tag-remove]]:cursor-pointer [&_[data-part=tag-remove]:hover]:bg-primary
              [&_[data-part=tag-remove]:hover]:text-primary-foreground
              [&_[data-part=tag-remove]:focus-visible]:bg-primary
              [&_[data-part=tag-remove]:focus-visible]:text-primary-foreground
            "
          />
          <Combobox.Input
            className="
              w-full py-sm px-md border-[length:var(--border-width)] border-solid border-input rounded-md
              [corner-shape:squircle] bg-card text-foreground [font-size:var(--uiify-control-text)]
              [transition:var(--transition-colors)] focus-visible:border-primary [[data-part=control]_&]:flex-[1_1_5rem]
              [[data-part=control]_&]:[min-inline-size:5rem] [[data-part=control]_&]:[inline-size:auto]
              [[data-part=control]_&]:py-xs [[data-part=control]_&]:px-0 [[data-part=control]_&]:border-0
              [[data-part=control]_&]:rounded-none [[data-part=control]_&]:bg-transparent
              [[data-part=control]_&]:[outline:none] [[data-part=control]_&]:[box-shadow:none]
            "
            aria-label="Add tag"
            placeholder="Add tag"
          />
        </Combobox.Control>
        <Combobox.Content
          className="
            [&[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)]
            [&_[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)]
            [&[popover]:popover-open:not([data-positioning=native])]:[margin:revert]
            [&_[popover]:popover-open:not([data-positioning=native])]:[margin:revert] flex-col [min-inline-size:12rem]
            [max-block-size:min(24rem,calc(100dvb_-_2rem))] overflow-y-auto overscroll-contain p-xs
            border-[length:var(--border-width)] border-solid border-border rounded-lg [corner-shape:squircle] bg-card
            text-foreground shadow-popover [transform-origin:var(--transform-origin,top)] data-[state=closed]:hidden
            [&:where(:not([popover]),:popover-open)]:flex motion-safe:[transition:var(--transition-presence)]
            motion-safe:data-[state=closed]:opacity-0 motion-safe:data-[state=closed]:[scale:var(--presence-scale)]
            motion-safe:starting:[&:popover-open]:opacity-0
            motion-safe:starting:[&:popover-open]:[scale:var(--presence-scale)]
          "
        >
          {(items) =>
            items.length === 0 ? (
              <Combobox.Empty>Nothing matched. Try another search.</Combobox.Empty>
            ) : (
              items.map((item) => (
                <Combobox.Item
                  className="
                    flex items-center w-full py-sm px-md border-0 rounded-[calc(var(--radius-lg)_-_var(--space-xs))]
                    [corner-shape:squircle] bg-transparent text-inherit [font:inherit] text-sm
                    [line-height:var(--leading-normal)] text-start cursor-pointer select-none hover:bg-accent
                    hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground
                    focus-visible:outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground
                    aria-[selected=true]:bg-accent aria-[selected=true]:text-accent-foreground data-[selected]:bg-accent
                    data-[selected]:text-accent-foreground aria-[disabled=true]:opacity-50
                    aria-[disabled=true]:pointer-events-none data-[disabled]:opacity-50
                    data-[disabled]:pointer-events-none
                  "
                  item={item}
                  key={item.value}
                />
              ))
            )
          }
        </Combobox.Content>
      </Combobox.Root>
    </div>
  );
}
/* app.css */

@layer reset, ui, app, tokens, base, demo, components, utilities, theme;

@import "@gunkin/uiify/styles/reset";

@import "@gunkin/uiify/components/behavior.css";

@import "@gunkin/uiify/styles/tokens";

@import "@gunkin/uiify/styles/tailwind";

@import "tailwindcss/utilities.css";

@theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }

@source inline('relative w-full max-w-72 flex flex-wrap items-center gap-xs min-h-10 py-sm px-md border-[length:var(--border-width)] border-solid border-input rounded-md [corner-shape:squircle] bg-card text-foreground [transition:var(--transition-colors)] focus-within:border-primary data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed has-[[data-part=input]:disabled]:opacity-50 has-[[data-part=input]:disabled]:cursor-not-allowed [&>[data-part=tag]]:inline-flex [&>[data-part=tag]]:items-center [&>[data-part=tag]]:gap-xs [&>[data-part=tag]]:py-xs [&>[data-part=tag]]:px-sm [&>[data-part=tag]]:rounded-xs [&>[data-part=tag]]:[corner-shape:squircle] [&>[data-part=tag]]:bg-accent [&>[data-part=tag]]:text-accent-foreground [&>[data-part=tag]]:text-xs [&>[data-part=tag]]:font-medium [&_[data-part=tag-remove]]:inline-grid [&_[data-part=tag-remove]]:place-items-center [&_[data-part=tag-remove]]:size-5 [&_[data-part=tag-remove]]:flex-none [&_[data-part=tag-remove]]:p-0 [&_[data-part=tag-remove]]:border-0 [&_[data-part=tag-remove]]:rounded-xs [&_[data-part=tag-remove]]:bg-transparent [&_[data-part=tag-remove]]:text-inherit [&_[data-part=tag-remove]]:[font:inherit] [&_[data-part=tag-remove]]:leading-none [&_[data-part=tag-remove]]:cursor-pointer [&_[data-part=tag-remove]:hover]:bg-primary [&_[data-part=tag-remove]:hover]:text-primary-foreground [&_[data-part=tag-remove]:focus-visible]:bg-primary [&_[data-part=tag-remove]:focus-visible]:text-primary-foreground [font-size:var(--uiify-control-text)] focus-visible:border-primary [[data-part=control]_&]:flex-[1_1_5rem] [[data-part=control]_&]:[min-inline-size:5rem] [[data-part=control]_&]:[inline-size:auto] [[data-part=control]_&]:py-xs [[data-part=control]_&]:px-0 [[data-part=control]_&]:border-0 [[data-part=control]_&]:rounded-none [[data-part=control]_&]:bg-transparent [[data-part=control]_&]:[outline:none] [[data-part=control]_&]:[box-shadow:none] [&[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)] [&_[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)] [&[popover]:popover-open:not([data-positioning=native])]:[margin:revert] [&_[popover]:popover-open:not([data-positioning=native])]:[margin:revert] flex-col [min-inline-size:12rem] [max-block-size:min(24rem,calc(100dvb_-_2rem))] overflow-y-auto overscroll-contain p-xs border-border rounded-lg shadow-popover [transform-origin:var(--transform-origin,top)] data-[state=closed]:hidden [&:where(:not([popover]),:popover-open)]:flex motion-safe:[transition:var(--transition-presence)] motion-safe:data-[state=closed]:opacity-0 motion-safe:data-[state=closed]:[scale:var(--presence-scale)] motion-safe:starting:[&:popover-open]:opacity-0 motion-safe:starting:[&:popover-open]:[scale:var(--presence-scale)] border-0 rounded-[calc(var(--radius-lg)_-_var(--space-xs))] bg-transparent text-inherit [font:inherit] text-sm [line-height:var(--leading-normal)] text-start cursor-pointer select-none hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground aria-[selected=true]:bg-accent aria-[selected=true]:text-accent-foreground data-[selected]:bg-accent data-[selected]:text-accent-foreground aria-[disabled=true]:opacity-50 aria-[disabled=true]:pointer-events-none data-[disabled]:pointer-events-none');

@layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }

/* Authored animation and registered properties used by the Tailwind skin.
 * Visual rules belong in utility recipes; only at-rule definitions live here. */
@layer components {
  @keyframes uiify-presence-in {
    from {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@layer components {
  @keyframes uiify-presence-out {
    to {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@property --uiify-spinner-tail {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@property --uiify-spinner-length {
  syntax: "<angle>";
  inherits: false;
  initial-value: 36deg;
}

@layer components {
  @keyframes uiify-spinner-rotate {
    to {
      transform: rotate(360deg);
    }
  }
}

@layer components {
  @keyframes uiify-spinner-orbit {
    0% {
      --uiify-spinner-tail: 0deg;
      --uiify-spinner-length: 36deg;
    }

    50% {
      --uiify-spinner-tail: 90deg;
      --uiify-spinner-length: 324deg;
    }

    100% {
      --uiify-spinner-tail: 360deg;
      --uiify-spinner-length: 36deg;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-pulse {
    50% {
      opacity: 0.45;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-shimmer {
    to {
      background-position: -200% 0;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-reveal {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}

@property --candle-body-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-body-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-slot {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 100%;
}

@property --candle-wick-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-wick-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-x {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@property --line-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-prev-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-count {
  syntax: "<integer>";
  inherits: false;
  initial-value: 1;
}

@property --line-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@layer components {
  @keyframes uiify-otp-input-caret-blink {
    50% {
      opacity: 0;
    }
  }
}

‮API‬

Combobox.Root

‮Prop‬‮Type‬‮Default‬‮Description‬
childrenReactNode—‮Combobox parts: control, tags, input, trigger and content.‬
items*ComboboxItemData[]—‮Full list of options the input filters over.‬
defaultInputValuestring—‮Starting text in the input when the input value is uncontrolled.‬
defaultOpenboolean—‮Whether the list starts open when the open state is uncontrolled.‬
defaultValuestring | readonly string[]—‮Value or values selected on first render when the selection is uncontrolled.‬
multiplebooleanfalse‮Allows selecting and removing multiple values as tags.‬
allowCustomValuebooleanfalse‮Allows typed text that is not in items to become a selected value.‬
addOnPastebooleantrue‮Adds multiple delimited values from pasted text in multiple mode.‬
delimitersreadonly string[][","]‮Characters that commit or split typed values in multiple mode.‬
maxValuesnumber—‮Largest number of values that can be selected in multiple mode.‬
validateValue(value: string) => boolean—‮Rejects a custom value when it returns false.‬
filterFn(items: ComboboxItemData[], inputValue: string) => ComboboxItemData[]—‮Replaces the built-in filter that narrows items as the user types.‬
inputValuestring—‮Text in the input, making the typed value controlled.‬
onInputValueChange(value: string) => void—‮Called with the new text whenever the input changes.‬
onOpenChange(open: boolean, details: OpenChangeDetails) => void—‮Called when the list opens or closes, with the reason for the change.‬
onValueChange((value: string) => void) | ((value: string[]) => void)—‮Called with the selected value or full selected-values array.‬
openboolean—‮Whether the list is open, making the open state controlled.‬
valuestring | readonly string[]—‮Selected value or values, making the selection controlled.‬

Combobox.Control

‮Prop‬‮Type‬‮Default‬‮Description‬
childrenReactNode—‮Tags and input rendered inside the shared visual control.‬
classNamestring—‮Class applied to the control element.‬
renderReactElement | ((props, state) => ReactElement)—‮Replace the rendered element while keeping behavior and accessibility wiring.‬

Combobox.Tags

‮Prop‬‮Type‬‮Default‬‮Description‬
childrenReactNode—‮Content appended after the generated removable tags.‬
classNamestring—‮Class applied to the generated tags container.‬
renderReactElement | ((props, state) => ReactElement)—‮Replace the rendered element while keeping behavior and accessibility wiring.‬

Combobox.Input

‮Prop‬‮Type‬‮Default‬‮Description‬
classNamestring—‮Class applied to the text input.‬
renderReactElement | ((props, state) => ReactElement)—‮Replace the rendered element while keeping behavior and accessibility wiring.‬

Combobox.Trigger

‮Prop‬‮Type‬‮Default‬‮Description‬
childrenReactNode—‮Content of the button that opens the list, such as an icon.‬
classNamestring—‮Class applied to the trigger button.‬
renderReactElement | ((props, state) => ReactElement)—‮Replace the rendered element while keeping behavior and accessibility wiring.‬

Combobox.Content

‮Prop‬‮Type‬‮Default‬‮Description‬
align"start" | "center" | "end"—‮Alignment of the list against the input along the chosen side.‬
children*(filteredItems: ComboboxItemData[]) => ReactNode—‮Render function called with the items left after filtering.‬
classNamestring—‮Class applied to the list popup.‬
side"top" | "right" | "bottom" | "left"—‮Side of the input the list is placed on.‬

Combobox.Item

‮Prop‬‮Type‬‮Default‬‮Description‬
classNamestring—‮Class applied to the option element.‬
item*ComboboxItemData—‮The item this option renders, normally taken from the content callback.‬

Combobox.Empty

‮Prop‬‮Type‬‮Default‬‮Description‬
childrenReactNode—‮Content shown when filtering leaves no items.‬
classNamestring—‮Class applied to the empty-state element.‬

ComboboxItemData

‮Prop‬‮Type‬‮Default‬‮Description‬
disabledboolean—‮Makes this item unselectable.‬
label*string—‮Text shown for the item and matched by the filter.‬
value*string—‮Value reported when the item is selected.‬

‮Keyboard‬

‮Key‬‮Action‬
↓ / ↑‮Open and rove options‬
Enter‮Select‬
Escape‮Close‬
a–z‮Typeahead to filter options‬

‮Accessibility‬

‮ARIA combobox + listbox. Active option is announced via aria-activedescendant. Content exposes open/closed state; options expose highlighted, disabled and (in multiple mode) selected state. Use Empty to explain when no options match. Popup presence motion is disabled when reduced motion is preferred.‬

‮Styling hooks‬

data-statedata-highlighteddata-disableddata-selected