State label component
The status pill for a code-hosting entity (issue or pull request) whose value is drawn from a per-entity state machine, not a free tag. Distinct from a notification badge (count/dot) and a chip (user-editable filter): a state label is read-only, single-valued, and semantically colored by state. Modeled after GitHub Primer StateLabel. Used by Koder Flow issue lists, PR headers, dashboards, and any surface that shows VCS entity status.
Status: v0.1.0 — Draft. Promoted from the GitHub Primer parity scan (metadocsstack #092). Live URL once rendered:
kds.koder.dev/<locale>/components/components-state-label.html. Source: https://primer.style/components/state-label
R1 — What it is (and is not)
A state label is a read-only pill bound to ONE value from a fixed, entity-specific state machine (R2). It is NOT:
- a badge (
specs/components/badges.kmd) — that signals a count/dot; - a chip (
specs/components/chips.kmd) — that is user-editable / afilter input;
- a free-text tag/label (issue labels are a separate, multi-valued,
user-defined concept).
There is exactly one state label per entity instance.
R2 — State machines (closed sets)
The valid values and their semantics depend on the entity:
Issue
| State | Meaning | Semantic color | Icon |
|---|---|---|---|
Open |
Active issue | success (open-green) | issue-opened |
Closed — completed |
Resolved as done | done (purple) | issue-closed |
Closed — not planned |
Closed without action | neutral (grey) | skip/circle-slash |
Pull request
| State | Meaning | Semantic color | Icon |
|---|---|---|---|
Draft |
Not yet ready for review | neutral (grey) | git-pull-request-draft |
Open |
Ready / in review | success (open-green) | git-pull-request |
Merged |
Merged into target | done (purple) | git-merge |
Closed |
Closed unmerged | danger (red) | git-pull-request-closed |
No other values exist. The state is derived from entity data, never set by hand in the view.
R3 — Anatomy
- Pill container with a leading icon + a text label.
- Sized in two scales:
small(inline, in lists/tables) anddefault(entity header). Both keep the icon+label pair.
- Fill uses the state's semantic color at the established surface
contrast; the label text meets WCAG AA on that fill.
R4 — Color is never the only signal
Each state is distinguished by icon + text label, not color alone (WCAG 1.4.1). The icon shape per row in R2 is mandatory; a state label without its icon is non-conformant.
R5 — Tokens
All fills, text, and icon colors come from Verge semantic tokens (specs/themes/verge.kmd) — --kds-color-open, --kds-color-done, --kds-color-danger, --kds-color-neutral (or their canonical names). No raw hex. Light/dark are handled by the token layer, not by the component.
R6 — Accessibility
- The label renders its state in words; the icon is decorative
(
aria-hidden) since the text carries meaning. - When used as the accessible name of an entity row, the state is part of
that row's label ("Issue #214, Open").
- Non-interactive: a state label is not a button/link. If a surface makes
the surrounding row clickable, the click target is the row, not the pill.
R7 — i18n
The visible state label is translated per specs/i18n/contract.kmd ("Open"→"Aberto", "Merged"→"Mesclado", etc.). The underlying state KEY (used in code/filters) stays stable and language-invariant.
Não-escopo
- User-defined issue labels (multi-valued, colored tags) — separate.
- The state-transition rules themselves (owned by Koder Flow backend).
- Review-decision states (approved / changes-requested) — those belong to
the PR review surface (
specs/components/review spec, #138).