Tessina UI
Tessina UI
GitHubIntroduction
InstallationUsageTheming
Components
ButtonIconButtonLinkSpinnerBadgeAvatarStatusChipShortcutSkeletonSurfaceProgressMeterRating
LabelFieldFieldsetCheckboxRadioSwitchSliderSelectComboboxSearchTextareaNumberFieldDate PickerTime PickerOTP InputFile UploadCalendar
AlertBannerToastTooltip
TabsAccordionCollapsibleBreadcrumbPaginationStepperSegmentedControlButtonGroupToggleButtonToggleGroupToolbarNavigation MenuMenubarBottom NavSidebar
Split ButtonFABDropdown MenuContextMenuCommandPopoverHoverCard
ContainerStackFlexGridAspectRatioSpacerCardCarouselDividerScroll Area
Table
ChatBubblePromptInputCodeBlock
ModalAlertDialogDrawerAction SheetTop Header MobileTop Header DesktopEmptyStateForm
Contributing
ComponentsEssentials

Chip

Interactive label pills for filtering, tagging, and selection. Four variants, five intents, five sizes, six rounded options, optional remove button, leading icon/avatar/status slots, and RTL support.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { Chip, ChipGroup } from "@tessinaui/ui";
<ChipGroup>
  <Chip>Label</Chip>
  <Chip selected>Selected</Chip>
  <Chip onRemove={() => {}}>Removable</Chip>
  <Chip leadingIcon={<Tag />}>With icon</Chip>
</ChipGroup>

Showcase

Variants

VariantDescription
outlineBorder + white background. Default. Matches Figma spec.
softFilled with a subtle secondary background. No border.
solidFilled with the intent colour (or secondary for none).
ghostNo border, no background. Minimal emphasis.

API Reference

Chip

PropTypeDefaultDescription
variant"outline" | "soft" | "solid" | "ghost""outline"Visual fill style
intent"none" | "error" | "warning" | "success" | "info""none"Semantic colour — applied when selected
size"xs" | "sm" | "md" | "lg" | "xl""md"Text size and padding
rounded"none" | "sm" | "md" | "lg" | "xl" | "full""full"Border radius
selectedbooleanfalseActive/toggled state — fills with intent colour
disabledbooleanfalseDisables interaction
dir"ltr" | "rtl""ltr"Text direction
leadingIconReactNode—Icon before label (controlled by showLeadingIcon)
leadingAvatarChipAvatarDef—Avatar before label — picks one leading slot
leadingStatusStatusVariant—Coloured status dot before label
showLeadingIconbooleantrueShow or hide leadingIcon / leadingStatus
trailingIconReactNode—Icon after label (ignored when onRemove is provided)
onRemove(e: MouseEvent) => void—Renders an × remove button when provided
onClickMouseEventHandler—Click handler — typically used to toggle selected
classNamestring—Extra classes on the root element

ChipAvatarDef

FieldTypeDescription
srcstringAvatar image URL
initialsstring1-2 char fallback when no src
statusStatusVariantStatus dot on the avatar (online/away/busy/…)

ChipGroup

Container that wraps multiple chips with consistent spacing.

PropTypeDefaultDescription
gap"sm" | "md" | "lg""md"Gap between chips (4px / 6px / 8px)
wrapbooleantrueWrap chips to multiple lines
dir"ltr" | "rtl""ltr"Text direction

Leading Slot Priority

When multiple leading props are provided, the first match wins:

  1. leadingAvatar (avatar always renders if defined)
  2. leadingStatus (status dot — requires showLeadingIcon !== false)
  3. leadingIcon (icon — requires showLeadingIcon !== false)

Remove Button

When onRemove is provided, the chip renders as two sibling <button> elements inside a pill container:

<Chip onRemove={(e) => removeTag(tag)}>
  TypeScript
</Chip>

The remove button fires onRemove and stops click propagation so it does not trigger onClick (the toggle handler).

Toggle Pattern

const [selected, setSelected] = useState(false);

<Chip
  selected={selected}
  onClick={() => setSelected((s) => !s)}
>
  Filter
</Chip>

Filter Group

const [active, setActive] = useState<string[]>([]);

function toggle(tag: string) {
  setActive((prev) =>
    prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]
  );
}

<ChipGroup>
  {tags.map((tag) => (
    <Chip
      key={tag}
      selected={active.includes(tag)}
      onClick={() => toggle(tag)}
    >
      {tag}
    </Chip>
  ))}
</ChipGroup>

Accessibility

  • Renders as <button type="button"> with aria-pressed reflecting the selected state.
  • When onRemove is provided, the main area and remove button are separate focusable <button> elements — fully keyboard navigable.
  • The remove button has aria-label="Remove".
  • disabled applies pointer-events-none and opacity-50 to all interactive areas.
  • Touch targets meet WCAG 2.1 AA (≥ 44×44 px) at md, lg, xl sizes via min-h-[44px].
  • Leading icon and trailing icon containers have aria-hidden="true" — labels provide the accessible text.

Status

A small circular status indicator for user presence (online, away, busy, neutral) and feedback states (success, info, warning, error). Five sizes from 8 px to 24 px. Used as a standalone badge or embedded inside Avatar.

Shortcut

Keyboard shortcut badges for documenting and displaying key combinations. Includes a standalone Key component for individual keys and a Shortcut wrapper for combos with configurable separators and RTL support.

On this page

PlaygroundInstallationUsageShowcaseVariantsAPI ReferenceChipChipAvatarDefChipGroupLeading Slot PriorityRemove ButtonToggle PatternFilter GroupAccessibility