<!-- Protocore Design System — Tag -->
# Tag

- **Category:** Data Display (`data-display`)
- **Slug:** `data-display/tag`
- **Status:** stable
- **Platforms:** web, mobile
- **Import:** `import { Tag } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/data-display/tag

> A static, as-typed metadata label in muted mono — optionally bordered or copy-on-click.

## Tag vs Badge vs Chip

The system separates these by **case and type family** (see the table in CONVENTIONS §3):

- **Tag** — *metadata*, rendered **as typed** in muted **mono**. It's a value or a key: `region:eu-central-1`, `v2.14.0`, a commit sha. Static, or copy-on-click. It never carries a status hue.
- **Badge** — *status*. Sentence-case **sans** on a tone tint. Use it for a row's state (Settled, Failed), not for a label.
- **Chip** — *an interactive filter*. Mono **UPPERCASE** that toggles selected. If clicking it filters the view, it's a Chip, not a Tag.

If you're tempted to give a Tag a `tone`, you probably want a Badge. If you're tempted to make a Tag toggle a filter, you want a Chip.

## Mobile (React Native)

**Preview.** `@protocore/pds-mobile` ships the React Native sibling of **Tag**. It mirrors the web API where React Native allows; the package is a **preview** with no device-level QA yet, so pin it and expect small changes.

Import it from the mobile package (not `@protocore/pds`), inside a `<PdsProvider>` — there is no stylesheet, so `style` (a `ViewStyle`) replaces `className` and every value comes from the theme:

```tsx
import { Tag } from "@protocore/pds-mobile";
```

**Parity with web.** A static metadata label — mono, as-typed, muted, optional border.

- React Native has no `navigator.clipboard`: a copyable tag takes `copyValue` and calls `onCopy(value)` so **you** perform the write (e.g. RN/Expo `Clipboard`). Web copies directly.
- Pressing a copyable tag briefly shows a "copied" skin.

```tsx
<Tag bordered copyValue="prod-eu-1" onCopy={(v) => Clipboard.setString(v)}>
  prod-eu-1
</Tag>
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `bordered` | `boolean` | no | `false` | Draw a 1px faint border around the tag. |
| `className` | `string` | no | — | — |
| `copyValue` | `string` | no | — | When set, the tag becomes a button that copies this value to the clipboard and briefly shows a "Copied" state. |
| `style` | `CSSProperties` | no | — | — |

## Examples

### Basics

A Tag renders its children **verbatim** in muted mono — no case transform. That's what makes it right for machine metadata: regions, tiers, versions, keys.

```tsx
import { Tag, Stack } from "@protocore/pds";

// Tags render exactly as typed — no case transform — so they suit keys, shas,
// regions, and other machine metadata.
export default function TagBasics() {
  return (
    <Stack direction="row" gap={2} align="center" wrap>
      <Tag>region:eu-central-1</Tag>
      <Tag>tier:standard</Tag>
      <Tag>v2.14.0</Tag>
      <Tag>internal</Tag>
    </Stack>
  );
}
```

### Bordered

By default a Tag is borderless and quiet. Add `bordered` for a faint 1px outline when tags sit on a busy surface or need to read as discrete chips in a dense row.

```tsx
import { Tag, Stack } from "@protocore/pds";

// `bordered` adds a faint 1px outline — use it when tags sit on a busy surface
// and need a little more definition than the plain muted variant.
export default function TagBordered() {
  return (
    <Stack direction="column" gap={4}>
      <Stack direction="row" gap={2} align="center" wrap>
        <Tag>plain</Tag>
        <Tag>compute</Tag>
        <Tag>ledger</Tag>
      </Stack>
      <Stack direction="row" gap={2} align="center" wrap>
        <Tag bordered>bordered</Tag>
        <Tag bordered>compute</Tag>
        <Tag bordered>ledger</Tag>
      </Stack>
    </Stack>
  );
}
```

## Usage

**Do**

- Use Tags for machine metadata — keys, ids, regions, versions — rendered exactly as they appear on the wire.
- Reach for `copyValue` on anything an operator would paste into a terminal or ticket.
- Keep tags borderless in quiet contexts; add `bordered` only when they need more separation.
- Prefer a CodeRef when the value is a long identifier that benefits from middle-truncation.

**Don't**

- Don't give a Tag a status colour — that's a Badge's job.
- Don't use a Tag as a filter control — a non-copy Tag is inert; use Chip.
- Don't uppercase or title-case tag text; the whole point is as-typed fidelity.
- Don't cram a full sentence into a Tag — it's a label, not prose.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Tab` | Moves focus to a copyable Tag (one with `copyValue`). Plain tags are not focusable. |
| `Enter / Space` | Copies the value to the clipboard and announces "Copied" via an aria-live region. |

**Notes**

- A plain Tag is a non-interactive `<span>` — screen readers read its text and move on.
- A copyable Tag exposes `role="button"` with an `aria-label` of "Copy <value>", so its purpose is announced even though the visible text is the value itself.
- The transient "Copied" confirmation is placed in an `aria-live="polite"` region so assistive tech hears the result of the copy.

## Related

`badge`, `role-chip`, `code-ref`, `env-badge`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
