From afc1ee424d8e64bc860c4958f4f12390d2a88fce Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 22 Sep 2026 09:11:26 -0300 Subject: [PATCH 1/6] feat(chip): add semantic variants, a solid fill and a status dot The legacy `.chip` is the only thing in the app that can express a status pill, a solid brand pill or a removable one, which is what keeps its 46 call sites from moving. This gives Chip the colours they need. Variants are semantic, so a chip says what it means rather than which colour it is, and each resolves to bg-surface-*/text-* token utilities. `solid` is the one filled variant, on --color-surface-action with text-white, because there is no inverse-text token yet (5.93:1, AA but not AAA). Note the app has a second, darker solid (bg-primary900, BetaFlag and PlanBasedAccess) that this deliberately does not cover: which of the two survives is a design decision. ChipDot is the leading dot on a status chip, in currentColor so it follows the variant with nothing to wire, and aria-hidden since the label carries meaning. Shape comes from the Figma tags frame, which we were off: it specifies a 6px radius on a fixed 24px height, against rounded-sm (4px) and no explicit height here. The frame's 8px vertical padding is not applied, being an artefact of a height override that would leave 8px for a 12px label. Co-Authored-By: Claude Opus 5 (1M context) --- frontend/web/components/base/Chip/Chip.scss | 25 +++++++++++++ frontend/web/components/base/Chip/Chip.tsx | 36 +++++++++++++++++-- frontend/web/components/base/Chip/ChipDot.tsx | 6 ++++ frontend/web/components/base/Chip/index.ts | 1 + 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 frontend/web/components/base/Chip/ChipDot.tsx diff --git a/frontend/web/components/base/Chip/Chip.scss b/frontend/web/components/base/Chip/Chip.scss index 854427ce3f98..3407d57ff58d 100644 --- a/frontend/web/components/base/Chip/Chip.scss +++ b/frontend/web/components/base/Chip/Chip.scss @@ -13,13 +13,38 @@ border-color: var(--color-border-action); } + // Status colours carry their meaning in the fill, so an outline only adds + // noise. Transparent rather than removed, so a status chip is the same + // height as a bordered one beside it. + &--success, + &--warning, + &--danger, + &--info, + &--muted, + &--solid { + border-color: transparent; + } + + // Status dot. currentColor, so it follows the variant with nothing to wire. + &__dot { + width: 6px; + height: 6px; + border-radius: var(--radius-full); + background: currentColor; + flex-shrink: 0; + } + // Sizes (default is the base above). &--sm { padding: 3px 8px; font-size: 0.75rem; } + // 24px is fixed in the tags frame, so it is stated rather than left to derive + // from the inherited line-height. The frame's 8px vertical padding is an + // artefact of a height override on the auto-layout and is not applied. &--xs { + height: 24px; padding: 1px 6px; font-size: 0.6875rem; } diff --git a/frontend/web/components/base/Chip/Chip.tsx b/frontend/web/components/base/Chip/Chip.tsx index 296219372dda..e381e5d9a064 100644 --- a/frontend/web/components/base/Chip/Chip.tsx +++ b/frontend/web/components/base/Chip/Chip.tsx @@ -5,7 +5,18 @@ import { colorIconSecondary } from 'common/theme/tokens' import './Chip.scss' export type ChipSize = 'default' | 'sm' | 'xs' -export type ChipVariant = 'neutral' | 'accent' +export type ChipVariant = + | 'neutral' + | 'accent' + | 'success' + | 'warning' + | 'danger' + | 'info' + | 'muted' + | 'solid' + // The caller supplies the colour through className. Used by tags, whose + // colour is a user's decorative choice rather than a semantic role. + | 'none' export type ChipProps = { children: ReactNode @@ -30,7 +41,24 @@ export type ChipProps = { // bg + text come from token utilities; the variant border lives in Chip.scss. const VARIANT_UTILITIES: Record = { accent: 'bg-surface-action-subtle text-action', + + danger: 'bg-surface-danger text-danger', + + info: 'bg-surface-info text-info', + + muted: 'bg-surface-muted text-secondary', + neutral: 'bg-surface-subtle text-default', + // No utilities: a caller-supplied colour class would otherwise have to beat + // these on source order alone, which a reordered stylesheet would break. + none: '', + // The one filled variant. `text-white` rather than a token because there is + // no inverse-text token yet; white on --color-surface-action is 5.93:1, so AA + // but not AAA. Note the app has a second, darker solid (`bg-primary900`, used + // by BetaFlag and PlanBasedAccess) that this deliberately does not cover. + solid: 'bg-surface-action text-white', + success: 'bg-surface-success text-success', + warning: 'bg-surface-warning text-warning', } // Token-based chip primitive. Uses `ds-chip` rather than the legacy `.chip` @@ -58,10 +86,12 @@ const Chip = ({