Skip to content

[2026-02-12] Visual Hierarchy (Calorie Focus)

Task Metadata

  • 📆 Date: 2026-02-12
  • 🚥 Status: Ready for Review
  • Beads Issue: 2512_genai_food_tracking-1kb

Objective

Goal: Define, implement, and verify the dashboard progress tracker changes so calories stand out more than other nutrients (which were previously equal size/weight), while keeping supporting nutrient visibility.

  • Trigger: Phase 2 roadmap item "Visual Hierarchy (Calorie Focus)" is active and now tracked in beads.
  • Constraints: Preserve existing data model and avoid regressions in Guest/Auth flows.

Technical Strategy

Use this work log to capture approach decisions, the intended UI hierarchy design, and a clear implementation plan before coding.

  • Key Decisions:
    • Scope boundary: apply hierarchy changes to the Daily Totals progress tracker (DailyTotalsVertical + ProgressBar) rather than broader dashboard cards/sections.
    • Non-goal: restructuring Meal List, Pending Meal card, or tracker page layout outside totals/progress display.
    • Calories remain permanently expanded/full-detail (label + value/target visible).
    • Secondary rows use independent space-efficiency toggles for macros and extended nutrients:
      • Expanded (default): current detailed view (labels + numeric text).
      • Collapsed: compressed bars with no labels or numeric tracking text.
    • Prioritize calorie total, target, and remaining/over state as the first-read information.
    • Keep protein/carbs/fat visible as secondary context, not competing with the calorie signal.
    • Validate hierarchy across desktop and mobile breakpoints before UI implementation.

Crowding-Control Options for Collapsed State

  • Option A (Approved): Adaptive Compact Labels
  • In collapsed mode, secondary bars always render.
  • Hide numeric values for secondary rows.
  • Core macros always show compact labels (P/C/F); extended nutrient labels show only on larger widths (sm+/md+) and hide on tight screens.
  • Pros: Preserves information scent with minimal clutter.
  • Cons: Slightly more breakpoint logic.
  • Option B: No Secondary Text in Collapsed Mode
  • In collapsed mode, secondary rows are color/progress only, no labels/values.
  • Pros: Maximum visual focus on calories.
  • Cons: Lowest scanability for macro identification.
  • Option C: Inline Legend Above Compact Bars
  • Keep collapsed bars text-free, add a compact color legend row above them.
  • Pros: Better discoverability than pure no-text bars.
  • Cons: Adds extra UI element and vertical overhead.
  • Decision (2026-02-12): Option A approved.

Testing Strategy

  • Must Test: DailyTotalsVertical metric ordering and emphasis; expanded/collapsed toggle behavior; ProgressBar style rendering for primary vs secondary rows; compact text visibility rules by breakpoint; mobile and desktop readability.
  • Skip: Backend/API changes (not in scope for this design task).

Risk Analysis

  • Potential Regressions: Over-compressing secondary nutrient visibility, ambiguous label behavior in compact mode, or breakpoint-specific text clipping.
  • Deferred Risk (Won't Fix Yet): Collapsed mode still renders all secondary rows; very large nutrient sets may still create vertical crowding (tracked in 2512_genai_food_tracking-e6s).
  • Security Implications: None (design and frontend presentation scope).
  • Files to Modify:
    • frontend/src/pages/Dashboard.tsx
    • frontend/src/features/dashboard/components/DailyTotalsVertical.tsx
    • frontend/src/components/ProgressBar.tsx
    • frontend/src/features/dashboard/components/__tests__/DailyTotalsExtended.test.tsx
    • frontend/src/components/__tests__/ProgressBar.test.tsx
    • docs/architecture/ux_definitions.md
    • docs/project_management/index.md
    • docs/project_management/logs/features/2026-02-12_visual_hierarchy_calorie_focus.md

1. Daily Totals Metric Priority

  • File: frontend/src/features/dashboard/components/DailyTotalsVertical.tsx
  • Intent: Ensure calories remain the visually dominant primary metric while macros are rendered as secondary rows.

Keep calories as the first and visually dominant metric in the totals component, with macros rendered as secondary rows.

// frontend/src/features/dashboard/components/DailyTotalsVertical.tsx
const primaryMetric = {
  key: "calories",
  label: displayLabel || "Calories",
  current: totals.calories,
  target: targets.calories,
  emphasis: "primary",
};

const secondaryMetrics = [
  { key: "protein", label: "Protein", current: totals.protein, target: targets.protein, emphasis: "secondary" },
  { key: "carbs", label: "Carbs", current: totals.carbs, target: targets.carbs, emphasis: "secondary" },
  { key: "fat", label: "Fat", current: totals.fat, target: targets.fat, emphasis: "secondary" },
];

// Add stable test hooks for hierarchy assertions
<div data-testid={`daily-metric-row-${metric.key}`} data-metric-key={metric.key}>...</div>

2. Expand/Collapse Control Design & Placement

  • File: frontend/src/features/dashboard/components/DailyTotalsVertical.tsx
  • Intent: Add clear, low-friction compact controls so macros and extended nutrients can be collapsed independently.

Place both compact toggles beside the settings button in the "Daily Totals" header.

// frontend/src/features/dashboard/components/DailyTotalsVertical.tsx
<div className="flex gap-2">
  <button
    aria-label="Compact macros"
    aria-pressed={isMacroCompact}
    title={isMacroCompact ? "Expand macros" : "Compact macros"}
    className="p-2 text-zinc-500 rounded-lg hover:bg-zinc-100 transition-colors active:scale-95"
    onClick={() => setIsMacroCompact((v) => !v)}
  >
    {isMacroCompact ? <ChevronDown size={18} /> : <ChevronUp size={18} />}
  </button>
  <button
    aria-label="Compact nutrients"
    aria-pressed={isExtendedCompact}
    title={isExtendedCompact ? "Expand nutrients" : "Compact nutrients"}
    className="p-2 text-zinc-500 rounded-lg hover:bg-zinc-100 transition-colors active:scale-95"
    onClick={() => setIsExtendedCompact((v) => !v)}
  >
    {isExtendedCompact ? <ChevronDown size={18} /> : <ChevronUp size={18} />}
  </button>
  <button ...>...</button>
</div>

3. Compact State Tracking

  • File: frontend/src/features/dashboard/components/DailyTotalsVertical.tsx
  • Intent: Track macro and nutrient compact states independently and persist user preference across reloads with user-scoped keys.
// frontend/src/pages/Dashboard.tsx
const preferenceScopeKey = user?.isGuest ? "guest" : (user?.username || "auth");
<DailyTotalsVertical preferenceScopeKey={preferenceScopeKey} ... />

// frontend/src/features/dashboard/components/DailyTotalsVertical.tsx
const macroStorageKey = `daily_totals_macro_compact_v1:${preferenceScopeKey}`;
const extendedStorageKey = `daily_totals_extended_compact_v1:${preferenceScopeKey}`;
const [isMacroCompact, setIsMacroCompact] = useState<boolean>(...);
const [isExtendedCompact, setIsExtendedCompact] = useState<boolean>(...);

useEffect(() => {
  localStorage.setItem(macroStorageKey, String(isMacroCompact));
  localStorage.setItem(extendedStorageKey, String(isExtendedCompact));
}, [macroStorageKey, extendedStorageKey, isMacroCompact, isExtendedCompact]);

const isCompact = metric.emphasis === "macro" ? isMacroCompact : isExtendedCompact;

4. ProgressBar Compact Text Rules

  • File: frontend/src/components/ProgressBar.tsx
  • Intent: Support compact secondary rows with optional label-only text and no numeric value text.

Add lightweight text-mode controls for compact rendering.

// frontend/src/components/ProgressBar.tsx
interface ProgressBarProps {
  current: number;
  target: number;
  baseColor: string;
  className?: string;
  infoMode?: "full" | "label-only" | "none";
  compactLabel?: string;
}

5. Test Coverage for Toggle, State, and Styling

  • File: frontend/src/features/dashboard/components/__tests__/DailyTotalsExtended.test.tsx
  • Intent: Assert calorie-first order, toggle placement/state transitions, and collapsed text rules.
// frontend/src/features/dashboard/components/__tests__/DailyTotalsExtended.test.tsx
const rows = screen.getAllByTestId(/daily-metric-row-/);
expect(rows[0]).toHaveAttribute("data-metric-key", "calories");
expect(rows[0]).toHaveClass("h-8");
expect(rows[1]).toHaveClass("h-6");

const macroToggle = screen.getByRole("button", { name: /compact macros/i });
const nutrientToggle = screen.getByRole("button", { name: /compact nutrients/i });
expect(macroToggle).toHaveAttribute("aria-pressed", "false");
expect(nutrientToggle).toHaveAttribute("aria-pressed", "false");
fireEvent.click(macroToggle);
expect(macroToggle).toHaveAttribute("aria-pressed", "true");
expect(nutrientToggle).toHaveAttribute("aria-pressed", "false");
expect(rows[1]).toHaveClass("h-3");
expect(localStorage.getItem("daily_totals_macro_compact_v1:testuser")).toBe("true");

Critique & Gaps

  • Resolved - Scope clarification: Goal now explicitly targets the progress tracker hierarchy (not whole-dashboard layout), which removes the previous scope mismatch.
  • Deferred (Won't Fix Yet) - Secondary row budget: Active/archived extended nutrients can still crowd vertical space because collapsed mode keeps all rows visible. Follow-up issue: 2512_genai_food_tracking-e6s.
  • Addressed - Color-only interpretation risk: Option A now keeps core macro compact labels (P/C/F) in collapsed mode.
  • Addressed - Test executability: Design now includes explicit data-testid/data-metric-key hooks for hierarchy assertions.
  • Medium - Coverage gaps: Readability claims include desktop/mobile behavior, but no viewport-specific test strategy is currently documented.
  • Addressed - State persistence scope: Compact mode storage keys are scoped by user context (guest vs username) and split by metric group.
  • Medium - Accessibility under-specified: Styling focus currently favors height/shadow changes; text legibility and contrast criteria still need explicit measurable standards.
  • Addressed - Toggle semantics: Control now specifies aria-pressed and explicit toggle label.
  • Addressed - Process-state drift: Beads issue status is aligned to planning phase (open) while log remains Draft.

Gap Analysis

  • Gap 1: Define exact breakpoint/text rules for approved Option A (when extended nutrient labels render vs hide).
  • Gap 2: Add concrete accessibility acceptance criteria (contrast, minimum text size, first-read clarity threshold).
  • Gap 3: Document viewport-specific validation steps (mobile and desktop) for compact labels.
  • Gap 4: Deferred: row-budget/overflow behavior for high nutrient counts tracked in 2512_genai_food_tracking-e6s.

1. Acceptance Criteria Hardening

Add explicit success criteria before implementation:

  • Calories are visually first-read in less than 1 second on first glance.
  • Secondary macros remain visible without scrolling on common mobile viewports.
  • Collapsed secondary rows remove numeric text; macros keep compact labels (P/C/F), and extended labels follow approved breakpoint visibility rules.
  • Extended nutrient handling follows approved Option A behavior and does not displace primary calorie signal.
  • Over-target state remains readable and unambiguous.

2. Regression Safeguard

Define a pre/post screenshot checklist (desktop + mobile) to prevent accidental reversion of hierarchy during future UI refactors.

Execution Plan

Approval Gate Passed

User approval was received before implementation. Execution proceeded against the approved Option A scope.

  • Step 1: Create and activate work log with issue linkage.
  • Step 2: Finalize proposed dashboard hierarchy design and rationale.
  • Step 3: Select collapsed-state strategy (Option A approved).
  • Step 4: Address clear design risks (toggle semantics, scoped persistence, executable test hooks) and mark row-budget concern deferred.
  • Step 5: Finalize implementation plan, rollout sequencing, accessibility criteria, and viewport-specific test coverage.
  • Verify: Confirm roadmap/index and work log statuses are aligned with protocol states.

Execution Notes

Infrastructure & Structural Hardening

  • 2026-02-12: Created work log and set status to Draft.
  • Linked this work to beads issue 2512_genai_food_tracking-1kb (Open).
  • Updated project index to include this work log with Draft status and issue reference.
  • 2026-02-12 (Decision): Approved collapsed-state Option A (adaptive compact labels; no secondary numeric text in collapsed mode).
  • 2026-02-12 (Deferred): Created follow-up issue 2512_genai_food_tracking-e6s for collapsed-mode row-budget/overflow behavior (won't fix yet).

Architecture Refinement (Optional)

  • Shifted scope definition from generic "UI polish" to explicit information hierarchy outcomes (primary calorie signal + secondary macro context).

Implementation & Verification

  • 2026-02-12 (Execute): Implemented independent compact behavior in DailyTotalsVertical with:
    • persistent, user-scoped compact states (daily_totals_macro_compact_v1:<scope> and daily_totals_extended_compact_v1:<scope>);
    • calorie row always full-detail and visually emphasized (h-8, shadow);
    • collapsed rows with no labels or numeric text, zero-gap compact stacking, and animated expand/collapse transitions.
    • progress bars remain rounded in expanded mode; only collapsed secondary bars use a right-angled edge.
  • 2026-02-12 (Execute): Iterated compact controls UX based on review feedback:
    • replaced one shared toggle with independent macro/nutrient toggles;
    • moved toggles onto section dividers (Macros divider between calories and macros; Nutrients divider above extended rows);
    • reduced control visual weight and section padding to avoid increasing tracker footprint.
  • 2026-02-12 (Fix): Corrected shaped-bar rendering regression in ProgressBar (bars disappeared due conflicting positioning classes in inner shape wrapper); restored rendering and re-verified tests.
  • 2026-02-12 (Execute): Added separate compact controls near settings for macros and nutrients with aria-pressed semantics.
  • 2026-02-12 (Execute): Wired preference scope from Dashboard (guest vs username/auth).
  • 2026-02-12 (Execute): Extended ProgressBar info-mode handling used by compact state (full/label-only/none).
  • 2026-02-12 (Verify): Added/updated frontend tests:
    • DailyTotalsExtended.test.tsx for ordering, compact toggle behavior, persistence, and registry edge cases.
    • ProgressBar.test.tsx for info-mode rendering behavior.
  • 2026-02-12 (Docs): Updated docs/architecture/ux_definitions.md to reflect calorie-first hierarchy and secondary compact-mode behavior as the current UX state.
  • 2026-02-12 (Verify): Passed:
    • npm --workspace frontend run typecheck
    • npm --workspace frontend test

Deviation from Plan (Optional)

  • Deviation: Initial draft log was created before expanded hardening sections were added to the template.
  • Reason: Template uplift happened immediately after log creation.
  • Impact: No delivery risk; this log now aligns with the updated template and protocol state model.

User Approval & Key Learnings

Key Learnings

  • Calorie-first hierarchy can be emphasized without removing macro visibility by separating primary vs secondary information density.
  • Option A is viable with low-risk UI changes when compact state is persisted per user scope.

Context Memory (AI-Only)

Summary for Future Context

Visual hierarchy implementation is complete and test-verified for the approved scope: calories are permanently full-detail and visually dominant; secondary metrics support persisted compact mode with macro shorthand and no numeric text. High nutrient-count row capping remains deferred in 2512_genai_food_tracking-e6s.