Deck
Browse the catalog

Build / Atoms

Tag

  • atom
  • play-fn

A user-authored label on a connection, and the filter control for it.

Distinct from Badge: a Badge is system-assigned status you read, a Tag is user data you can remove or filter by. Tags are how a holder makes captured connections findable later, so filtering state is exposed via aria-pressed rather than implied by color.

import { Tag } from '@meetcard/deck'

Example

<Tag onRemove={() => remove('follow-up')}>follow-up</Tag> <Tag onToggle={toggle} selected={isOn}>SaaSConf</Tag>

Stories

Every preview below is the real component, rendered in this page from the same story Storybook runs. The width control resizes each preview's container; CSS media queries still follow the browser window, so breakpoint behaviour is worth checking in Storybook.

Read-only: a label on a connection.

Default
Source
const Default = () => <Tag>follow-up</Tag>;

Removable. The tag's text is folded into the button's name, so a screen reader hears which one goes.

Removable
Source
const Removable = () => <Tag onRemove={fn()}>follow-up</Tag>;

As a filter, the tag becomes a toggle button exposing aria-pressed.

Filter
Source
const Filter = function Render(args) {
  const [selected, setSelected] = useState<string[]>(['SaaSConf'])
  const toggle = (tag: string) =>
    setSelected((current) =>
      current.includes(tag)
        ? current.filter((t) => t !== tag)
        : [...current, tag],
    )

  return (
    <Stack direction="row" gap={8} wrap>
      {['SaaSConf', 'follow-up', 'investor', 'warm'].map((tag) => (
        <Tag
          {...args}
          key={tag}
          onToggle={() => toggle(tag)}
          selected={selected.includes(tag)}
        >
          {tag}
        </Tag>
      ))}
    </Stack>
  )
};

A connection's tag list, as it appears on a contact record.

Tag List
Source
const TagList = function Render() {
  const [tags, setTags] = useState(['SaaSConf', 'follow-up', 'investor'])
  return (
    <Stack direction="row" gap={8} wrap>
      {tags.map((tag) => (
        <Tag
          key={tag}
          onRemove={() => setTags((t) => t.filter((x) => x !== tag))}
        >
          {tag}
        </Tag>
      ))}
    </Stack>
  )
};

sm for inline and dense lists; md on its own.

Sizes
Source
const Sizes = () => <Stack direction="row" gap={8} align="center">
    <Tag size="sm">small
              </Tag>
    <Tag size="md">medium
              </Tag>
</Stack>;

Props

PropTypeDefaultDescription
childrenrequiredReactNode——
onRemove() => void—Renders a remove control. Omit for a read-only tag.
onToggle() => void—Turns the tag into a filter toggle. With this set the tag renders as a real button exposing aria-pressed, so its state is announced.
removeLabelstring'Remove tag'Label for the remove control; the tag text is appended automatically.
selectedbooleanfalseSelected state for a filter tag. Only meaningful with onToggle.
size'sm' | 'md''md'—

Also accepts everything from Omit, so id, data-* and event handlers pass straight through.

Open in Storybookfor controls, the a11y panel and the interaction runner.