Skip to content

Refresh Badge and StatCard for Dixie dashboard - #35

Merged
dpup merged 1 commit into
mainfrom
dixie-badge-statcard-refresh
Jul 18, 2026
Merged

Refresh Badge and StatCard for Dixie dashboard#35
dpup merged 1 commit into
mainfrom
dixie-badge-statcard-refresh

Conversation

@dpup

@dpup dpup commented Jul 18, 2026

Copy link
Copy Markdown
Member

Refreshes Badge and StatCard to match the Dixie dashboard mock. Two independent deltas, folded into one PR.

Badge

The mock's badges use a soft tinted fill + solid hairline border + no glow, they're roomier, and several carry a monospace count.

  • Restyled light-mode variants (theme.css): white-fill + halved-opacity border + colored glow → soft *-50 tint + solid *-200 border, no glow. Text deepens one step (*-800) to read against the tint. neutral becomes navy-tinted to match the mock; success/info get the same treatment for family consistency. Dark mode unchanged (the mock is light-only).
  • Variant classes own color only. .gp-badge-* no longer bake in padding/gap/radius — those move to the component's base + size classes, so the two can't fight over spacing (the "sizing prop interaction" the mock flagged). Semantic weight stays on the variant: font-semibold for flags (success/warning/error), font-medium for info/neutral.
  • New count / countPosition props (additive, no breaking change): render a font-mono tabular-nums count on the leading edge (flags — "10 flagged") or trailing edge (tags — "other 525").

Deliberate deviation from the handoff: the count is not aria-hidden. It carries meaning ("10 sessions flagged"), so hiding it from screen readers would drop real information. count={0} renders correctly (covered by a test).

StatCard

gp-stat-card baked a grey fill that reads muddy grey-on-white when nested inside a white Card/SectionCard (Metrics card, session-detail stat grids).

  • New surface prop: muted (default — today's filled tile, zero visual change) or plain (transparent, inherits the parent card).
  • Fill control moved fully into the component. .gp-stat-card is now geometry-only; the fill was previously defined twice (in the CSS class and via colorClasses[color].bg) and relied on utility-layer ordering. Now a single surfaceClasses[color][surface] map owns it.
  • Applies across all tiled variants (default/compact/inline/centered) and both colors; variant="display" is chrome-less and ignores surface.

Tests & playground

  • Added Badge.test.tsx (variant classes, count render, count={0}, leading/trailing order) and StatCard.test.tsx (muted default, plain transparent, accent+plain, display-ignores-surface).
  • BadgeDemo gets a "With Count" section; StatCardDemo gets a "Surface: muted vs plain" side-by-side inside white cards so the muddy-vs-clean difference is the visual contract.

Risk

  • Badge delta-1 changes the look of every existing Badge in consuming apps — worth a visual pass on any screen already using badges.
  • StatCard surface="muted" is the default, so existing StatCards are unchanged; plain is opt-in.

npm run typecheck, npm run build, and the new tests all pass.

🤖 Generated with Claude Code

Badge:
- Light-mode variants move from white-fill + colored glow to a soft tint
  + solid hairline border, no glow; text deepens one step and reads
  roomier. neutral becomes navy-tinted to match the mock.
- Variant classes (.gp-badge-*) now own color + semantic weight only;
  geometry moves into the component's base + size classes so the two no
  longer fight over padding.
- New additive `count` / `countPosition` props render a monospace,
  tabular count on the leading (flags) or trailing (tags) edge.

StatCard:
- New `surface` prop: `muted` (default, unchanged filled tile) or
  `plain` (transparent) so tiles nested inside a Card/SectionCard inherit
  the surface instead of reading grey-on-white.
- Fill control moves fully into the component; .gp-stat-card is now
  geometry-only, removing the duplicated background definition.

Adds Badge and StatCard tests plus playground demos for both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Preview deployed! 🚀

View the playground preview at: https://thegpvc.github.io/gp-ui/pr-35/

Last updated: 2026-07-18T21:00:20.239Z

@claude

claude Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review: Refresh Badge and StatCard for Dixie dashboard

Overall this is a clean, well-scoped restyle. Good discipline splitting color/geometry ownership between theme.css and the components, and the tests lock down the new behavior contract (not snapshots) per the repo's testing convention. A few things worth a look before merge.

Design-system conformance

gp-badge-neutral and gp-badge-info nearly collapse in light mode (src/styles/theme.css)
Both now render bg-navy-50 with font-medium text. The only remaining differentiators are the text shade (text-navy-700 for info vs. text-navy-500 for neutral) and the border (border-navy-200 for info vs. border-gray-200 for neutral). CLAUDE.md is explicit: "Distinguish Badge... variant families. Don't collapse them." Two issues here:

  • The border-gray-200 on a bg-navy-50 neutral badge looks like a leftover from the pre-refresh gray-family styling rather than an intentional pairing — every other variant keeps bg/border in the same color family (emerald/emerald, amber/amber, rose/rose, navy/navy for info). Should this be border-navy-200 too, or is neutral meant to stay in the gray family (bg-gray-50) instead of navy-tinted, to keep it visually distinct from info?
  • Separately, text-navy-500 breaks the "deepen one step to *-800" pattern used by success/warning/error, and doesn't match info's text-navy-700 either — worth confirming this was a deliberate choice for the mock rather than a stray value.

Token-first authoring (per CLAUDE.md)

Two new arbitrary-value classes bypass the token system the guide calls out as a review smell:

  • Badge.tsx: sm size uses py-[5px] instead of a scale step (py-1/py-1.5).
  • Badge.tsx: the count span uses text-[11px], which doesn't line up with either badge size's type scale (text-xs/text-sm) and stays fixed regardless of size="md".

If these pixel values are needed to match the mock precisely, consider promoting them to @theme tokens (or documenting why they're one-offs) rather than leaving them as inline arbitrary values — otherwise they'll likely get copy-pasted elsewhere.

Minor a11y note

The count span applies opacity-80 unconditionally across all variant/mode combinations. Most combos have comfortable headroom, but it's worth a quick visual/contrast check on the darker-mode variants (e.g. dark:text-gray-300 on dark:bg-navy-800 at 80% opacity) since the count is deliberately not aria-hidden — good call keeping it in the accessibility tree, just want to make sure the visual contrast matches that intent.

What looks good

  • Moving badge geometry into the component and color/weight into theme.css cleanly resolves the padding/sizing fights the mock flagged.
  • StatCard's surfaceClasses[color][surface] map is a nice fix for the double-defined fill (CSS class + colorClasses[color].bg) — single source of truth now.
  • count={0} handled correctly via count != null rather than truthiness — easy footgun avoided.
  • Test coverage is solid: variant classes, count leading/trailing order, zero-count edge case, StatCard surface × color combinations, and the display variant correctly ignoring surface.
  • Playground demos added for both new features, consistent with the "update the playground" requirement in CLAUDE.md.
  • No security concerns — presentational only, count is rendered as JSX text (auto-escaped), no new data flow.

Nothing here blocks merge; the neutral/info collapse is the one I'd want a second look at before it ships, since it affects every badge instance per the PR description.

@dpup
dpup merged commit d75bc34 into main Jul 18, 2026
4 checks passed
@dpup
dpup deleted the dixie-badge-statcard-refresh branch July 18, 2026 21:19
github-actions Bot added a commit that referenced this pull request Jul 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧹 Preview deployment has been cleaned up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant