Content
- One or two words, sentence case: "Accepted", "No reply yet", "Fundraising".
- The same word for the same status everywhere in Bridge.
The soft color label for status, category and counts. Not a button; not a pill.
import { … } from "@brdgapp/ui/components/tag"Tag is Bridge's label: the small soft-coloured box that says what something is or what state it's in — an intro's status, a category, a stage, a count. It is the one label in Bridge; never hand-make a <span> chip. The usual mistakes are making it a pill (it is 4px, never round) and using color alone to carry meaning.
onClick, for a label that also opens a menu, such as a category you can change.One of each
Bridge has one button, one label, one menu, one tooltip, one confirm dialog and one table. Before building a control, look for it here. Never hand-make a second version (a <button> with pill classes, a <span> chip, a new kebab), even if it looks the same today: copies drift.
The look comes from props
A component's appearance comes only from its own options (variant, size, color). className on a package component is for layout (mt-4, shrink-0, w-full), never for font size, color, padding, radius or hover. If the look needs a className, the component or the chosen option is wrong: fix that instead.
Never by color alone
Status and meaning are never carried by color alone. A colored tag has a word; a status color comes with an icon or text; a chart says in words what it shows.
Labels are boxes, controls are pills
Tags and badges have 4px corners and never become pills. Pills (rounded-full) are for things you press: buttons, filter chips, search fields, segmented controls.
Sentence case
Buttons, titles, headings, tabs, labels and statuses use sentence case: only the first word and names get a capital. "Make intro", "Intro forwarded", "Stop sharing".
Box. 4px corners, a tint fill and a matching border, from one token family per color.
Icon. Optional, before the label. 14px at sm, 16px at md.
Label. SemiBold, 0.4px letter spacing. A tag is as wide as its label; to cut a long label with an ellipsis, limit the width (className="max-w-full" inside a narrow cell).
Trailing slot. Optional, after the label, such as a chevron.
Bridge's intro-status colors, plus gray for categories.
sm for tables and dense lists; md where tags stand alone.
A label that opens a menu becomes a button.
Cut with an ellipsis instead of wrapping.
| Rest | Hover | Focus | Pressed | |
|---|---|---|---|---|
| Grey | ||||
| Blue | ||||
| Green | ||||
| Yellow | ||||
| Red | ||||
| Purple |
| State | What happens |
|---|---|
| Rest | Tint fill, border and text from the color's token family. |
| Hover (clickable only) | Darkens slightly. |
| Pressed (clickable only) | Fades to 60%. |
| Focus (clickable only) | 2px blue ring. |
onClick it's a plain <span>: not focusable, not announced as a control.onClick it renders a real button through Base UI, so it gets keyboard and focus behavior.Do
Map each meaning to one color and keep it the same everywhere: accepted is always green.Don't
Pick a color because it looks nice next to its neighbor.Do
Keep labels to one or two words.Don't
Put a sentence in a tag. Write it as text beside it.| Keys | Action |
|---|---|
| EnterSpace | Activates a clickable tag. |
Tag
The label.
| Prop | Type | Default | Description |
|---|---|---|---|
color | "grey" | "blue" | "green" | "yellow" | "red" | "purple" | "grey" | Each maps to one token family: neutral, info, success, warning, error, purple. |
size | "sm" | "md" | "sm" | 12px for dense surfaces and tables; 14px where tags stand alone. |
icon | ReactNode | — | A bare Lucide icon before the label. |
trailing | ReactNode | — | Something after the label, such as a chevron. |
onClick | () => void | — | Makes the tag a button. |
className | string | — | Layout only. Merged last. |
title | string | — | Deprecated: removed in 2.0. Use Tip for an explanation; a native title tooltip is slow, unstyled and missing on touch. Kept so Bridge OS could move over unchanged. |
…other attributes | id, aria-*, data-*, event handlers | — | Passed to the tag. The component's own props win where they overlap. |
tagVariants
The class recipe behind Tag.
| Prop | Type | Default | Description |
|---|
What it's built from and where it's used, read from the code. When you change it, check these too.
Where the code still differs from the system. Follow the system, not the gap.
pending_auto_decline shows yellow where the intros app shows orange.components.md → Tag / Badge / RatingPill (2026-10-29).src/components/ui/tag.tsx and intros-table.tsx (status → color map).