You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[0.0.1B] Define and implement the Loop UI design system
Objective
Create a consistent visual and interaction system for the new shell and future production workflows.
Why this is not cosmetic
One thing here is correctness, not taste: severity mapping. loupe-preflight emits three severities — error, warning, info (profile.schema.json:69) — plus check statuses that are neither pass nor fail (not_inspected, budget-exceeded, not_applicable). If each surface invents its own colours, an incomplete check will eventually render as a clean pass somewhere. #133 refuses to certify in that state; the UI must not contradict it.
So the design system's load-bearing deliverable is a canonical state mapping, and everything else is consistency work.
Step-by-step
1. Tokens
Pdf4QtLibGui/loupe/loupetokens.h plus a documented table in docs/LOUPE_DESIGN_SYSTEM.md. Tokens are semantic, never raw colour names at call sites:
namespaceloupe::tokens
{
// Spacing — 4px base grid.inlineconstexprint SpaceXs = 4, SpaceS = 8, SpaceM = 12, SpaceL = 16, SpaceXl = 24;
// Semantic roles resolve per theme; call sites never hardcode a colour.enumclassColorRole
{
SurfaceBase, SurfacePanel, SurfaceOverlay,
TextPrimary, TextSecondary, TextDisabled,
SeverityError, SeverityWarning, SeverityInfo,
StateIncomplete, // NOT a severity — see below
StateNotChecked,
FocusRing, DestructiveAction
};
QColor color(ColorRole role, LoupeTheme theme);
}
2. The canonical state mapping
One table, one implementation, consumed by finding cards, the report dock, canvas overlays, the Inspector (#127), and the status bar:
/// Single source of truth for finding/check presentation./// Every surface calls this; none derives its own colour.
LoupeStateVisual resolveStateVisual(const PreflightFinding* finding,
const PreflightCheckStatus* status,
const PreflightDecision* decision);
The two rules worth enforcing in code review: incomplete never renders as success, and waived never renders as passed. Both are the visual form of guarantees #133 and #126 make in data.
Shape and hatching carry the distinction alongside colour, so the mapping survives colour-blindness and greyscale printing — required by the #25 baseline anyway.
3. Components
Reusable implementations, each with a fixture exercising every state:
The "restriction excluded all" empty state matters: with #125, an operator can scope a profile to nothing and get zero findings. That must read as nothing was inspected, not as clean.
4. Theme and high-DPI
Light and dark defined together; every role has both values, contrast-checked.
Icons as SVG so they scale — the fork already requires Qt ≥ 6.11.1 for Qt SVG security fixes (AGENTS.md), so SVG is a settled dependency.
Verify at 100% / 150% / 200% scaling.
5. Adoption
New Loupe surfaces (#193, #195, #196, #127) use the system. Inherited PDF4QT dialogs are adapted, hidden, or explicitly deferred — tracked in the #191 manifest rather than left ambiguous. Do not restyle inherited dialogs that ADR-005 may stop shipping; that is wasted work.
Acceptance criteria
Design tokens and component rules are documented in docs/LOUPE_DESIGN_SYSTEM.md.
Severity and finding states have exactly one canonical visual mapping, implemented once in resolveStateVisual and used by every surface.
Incomplete inspection never renders with success treatment; waived never renders as passed. Both asserted by test.
A visual/state regression set runs in CI or release verification.
Test strategy
State-mapping test: table-driven over every (severity, check status, decision) combination; assert no incomplete or waived combination resolves to the success visual. This is the highest-value test here.
Contrast: automated WCAG contrast check over every token pair in both themes.
Colour-independence: render the state set greyscale; assert every state remains distinguishable.
Visual regression: a fixture page rendering every component in every state, captured at 100%/150%/200% in both themes, diffed against committed baselines — reuse the golden-image approach from UnitTests/tst_overprintrendertest.cpp.
Keyboard: focus traversal reaches every interactive element with a visible focus ring.
[0.0.1B] Define and implement the Loop UI design system
Objective
Create a consistent visual and interaction system for the new shell and future production workflows.
Why this is not cosmetic
One thing here is correctness, not taste: severity mapping.
loupe-preflightemits three severities —error,warning,info(profile.schema.json:69) — plus check statuses that are neither pass nor fail (not_inspected, budget-exceeded,not_applicable). If each surface invents its own colours, an incomplete check will eventually render as a clean pass somewhere. #133 refuses to certify in that state; the UI must not contradict it.So the design system's load-bearing deliverable is a canonical state mapping, and everything else is consistency work.
Step-by-step
1. Tokens
Pdf4QtLibGui/loupe/loupetokens.hplus a documented table indocs/LOUPE_DESIGN_SYSTEM.md. Tokens are semantic, never raw colour names at call sites:2. The canonical state mapping
One table, one implementation, consumed by finding cards, the report dock, canvas overlays, the Inspector (#127), and the status bar:
severity: "error"SeverityErrorseverity: "warning"SeverityWarningseverity: "info"SeverityInfoPreflightCheckStatus.status != okStateIncompleteStateNotCheckedThe two rules worth enforcing in code review: incomplete never renders as success, and waived never renders as passed. Both are the visual form of guarantees #133 and #126 make in data.
Shape and hatching carry the distinction alongside colour, so the mapping survives colour-blindness and greyscale printing — required by the #25 baseline anyway.
3. Components
Reusable implementations, each with a fixture exercising every state:
The "restriction excluded all" empty state matters: with #125, an operator can scope a profile to nothing and get zero findings. That must read as nothing was inspected, not as clean.
4. Theme and high-DPI
AGENTS.md), so SVG is a settled dependency.5. Adoption
New Loupe surfaces (#193, #195, #196, #127) use the system. Inherited PDF4QT dialogs are adapted, hidden, or explicitly deferred — tracked in the #191 manifest rather than left ambiguous. Do not restyle inherited dialogs that ADR-005 may stop shipping; that is wasted work.
Acceptance criteria
docs/LOUPE_DESIGN_SYSTEM.md.resolveStateVisualand used by every surface.docs/ACCESSIBILITY_BASELINE.md.Test strategy
(severity, check status, decision)combination; assert no incomplete or waived combination resolves to the success visual. This is the highest-value test here.UnitTests/tst_overprintrendertest.cpp.Dependencies
docs/ACCESSIBILITY_BASELINE.md— accessibility baseline.