Skip to content

Color budget Variance and YTD Util. by sign / threshold (today they render in plain text) #58

@studert

Description

@studert

Problem

Two prominent budget KPIs render as plain neutral text regardless of whether the value is favorable, neutral, or alarming:

  • "Variance" on /budget — currently shows -$28,743.84 in the same color as "Total Budget" and "Allocated".
  • "YTD Budget Util." on the dashboard — currently shows 108% in default text style even though the value is over 100% (i.e. budget already overrun for the year).

Other parts of the app already use semantic color: the Budget Alert banner, the over-100% workspace progress bars on /claude. The dashboard tiles and the FY card on /budget should match that vocabulary so an admin scanning the page can spot trouble at a glance — currently a 108% overspend looks identical to a 50% utilisation.

Evidence

Browser smoke test on /budget and / (2026-04-28). Screenshots saved under C:\Users\stude\AppData\Local\Temp\smoke-budget.png and smoke-dashboard.png (locally — feel free to recreate via the steps in the verification section).

Implicated components (find via grep on "Variance" and "YTD Budget"):

  • /budget summary card — likely under src/app/budget/ or src/components/.
  • Dashboard KPI tile — likely under src/app/page.tsx or src/components/dashboard*.

Proposed approach

  1. Decide the thresholds — propose:
    • Utilisation < 80% → default/muted-foreground.
    • 80%–100% → amber (text-amber-500 / shadcn warning palette).
    • > 100% → destructive red (text-destructive).
    • Variance: positive (under-spend) → muted/neutral; negative (over-spend) → destructive.
  2. Extract the threshold logic into one small helper (e.g. src/lib/budget-color.ts) that returns a Tailwind class string. Reuse it on both the dashboard KPI tile and the /budget FY card.
  3. Update both call sites to apply the class to the value's wrapping span.
  4. Add appropriate aria-label (e.g. \"Variance: -$28,743.84 (over budget)\") so screen-reader users get the semantic info that sighted users get from color — required for a11y and the reason this issue is also tagged a11y.
  5. Add a Vitest unit test for the helper covering boundaries (79.9, 80, 99.9, 100, 100.1).

Acceptance criteria

  • Variance value is colored red when negative, neutral when positive.
  • YTD Util. tile is colored amber at ≥80% and red at >100%.
  • Both elements include an aria-label describing the status in words, not only color.
  • One shared helper drives both call sites.
  • Unit tests cover the helper.
  • pnpm lint && pnpm typecheck && pnpm test pass.

Verification

  1. With seed data showing a 108% YTD utilisation: visit / → tile is red and screen-reader announces "over budget". Visit /budget → Variance row is red.
  2. Manually set utilisation to 75% (e.g. by inserting a smaller billed_costs total in dev) → tile becomes neutral.
  3. Manually set utilisation to 90% → tile becomes amber.

Metadata

Metadata

Assignees

No one assigned

    Labels

    a11yAccessibilityarea:uxUI / UX bugs and regressionsenhancementNew feature or requestpriority:lowNice to have

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions