fix(ui): namespace carry-meter accent token to avoid shadcn contrast regression

A bare --accent in src/index.css silently overrode shadcn's own
--accent/--accent-foreground pairing (near-white background paired with
near-black text) with the design contract's dark teal, without touching
the paired foreground. That dropped the computed contrast ratio on every
outline/ghost button, select item, badge link and dialog close button
that uses bg-accent/text-accent-foreground to 3.23:1 -- below WCAG AA's
4.5:1 minimum for text.

Renamed the token to --budget-accent everywhere it's declared and
consumed (src/index.css, src/ui/components/PostTable.tsx's carry meter),
leaving shadcn's own --accent/--accent-foreground pair untouched. The
other five contract tokens (--paper, --ink, --rule, --surplus,
--overspend) don't collide with any shadcn token name in
styles/globals.css, so they are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5 committed 2026-09-01 20:39:52 +02:00
1 parent dd612565cb
commit b278dcaf12
2 files changed
+17 -9

No files matched your search

+11 -3
View File
@@ -4,14 +4,22 @@
* Design contract tokens (Tasks 18-23).
* See .superpowers/sdd/2026-09-01-budget-app/design-contract.md
*
* Deliberately unlayered (not inside @layer base) so it wins the cascade
* over the shadcn defaults imported above, which are also unlayered.
* Namespaced as --budget-accent (not --accent): shadcn already defines a
* bare --accent/--accent-foreground pair in styles/globals.css for hover
* backgrounds on outline/ghost buttons, select items, badges, and the
* dialog close button. A same-named override here would repaint that
* background without repainting its paired foreground, breaking contrast
* on every one of those controls. None of the other five tokens below
* collide with a shadcn token name (checked against styles/globals.css).
*
* Unlayered (not inside @layer base) so these predictably win the cascade
* over anything layered added later, without needing !important.
*/
:root {
--paper: #f2f4f6; /* cool statement-paper ground, NOT cream */
--ink: #16212b; /* ink navy, primary text */
--rule: #d8dee4; /* hairlines, table borders */
--accent: #0f6e63; /* muted teal: links, primary actions, spend fill */
--budget-accent: #0f6e63; /* muted teal: links, primary actions, spend fill */
--surplus: #2f6f4e; /* carried-forward surplus */
--overspend: #a6402b; /* ochre-red: negative balances only */
+6 -6
View File
@@ -20,11 +20,11 @@ interface Props {
*
* Segments, left to right, sized against a per-row scale of
* (carried-in surplus + max(allocation, charges)):
* - --surplus : carried-in surplus (omitted when carriedIn <= 0)
* - --accent : spend within this month's allocation, filled from the
* track's left edge
* - --rule : the unspent remainder of the allocation track
* - --overspend: spend beyond the allocation, past the track's end
* - --surplus : carried-in surplus (omitted when carriedIn <= 0)
* - --budget-accent : spend within this month's allocation, filled from
* the track's left edge
* - --rule : the unspent remainder of the allocation track
* - --overspend : spend beyond the allocation, past the track's end
*/
function carryMeterBackground(figures: PostMonthFigures): string {
const surplus = Math.max(figures.carriedIn, 0);
@@ -41,7 +41,7 @@ function carryMeterBackground(figures: PostMonthFigures): string {
return (
`linear-gradient(to right, ` +
`var(--surplus) 0%, var(--surplus) ${pSurplus}%, ` +
`var(--accent) ${pSurplus}%, var(--accent) ${pFillEnd}%, ` +
`var(--budget-accent) ${pSurplus}%, var(--budget-accent) ${pFillEnd}%, ` +
`var(--rule) ${pFillEnd}%, var(--rule) ${pTrackEnd}%, ` +
`var(--overspend) ${pTrackEnd}%, var(--overspend) 100%)`
);