Parent
#196 — PRD: Angular web client for SubVora (parity minus reminders)
What to build
The shell-level polish pass: the app usable from 360px to a wide desktop, reachable entirely by keyboard and screen reader, and locked down with a content security policy. Grouped as one slice because all three are cross-cutting passes over the shell and the finished screens rather than separate user-visible features.
Implementation Steps
- Responsive shell — make the side nav a persistent rail above ~900px and a drawer below it; verify every screen down to 360px, with tables scrolling inside their own
overflow-x: auto container so the page body never scrolls sideways.
- Narrow-screen tables — collapse the subscriptions table to stacked cards (or hide secondary columns) at narrow widths so the primary information — name, cost, due state — stays readable without horizontal scrolling.
- Keyboard and screen reader — every control labelled; visible focus rings throughout; row menus and dialogs reachable and dismissable by keyboard with focus returned to the trigger; snackbars announced via
aria-live; the overdue state conveyed by text, not colour alone.
- Content security policy — add a strict CSP
<meta> to index.html: default-src 'self', the API origin in connect-src, the catalog logo host in img-src, and no third-party script sources. [HITL] — confirm the catalogLogoUrl host before locking img-src, or logos silently stop loading.
- Verify the budget — confirm the production build still fits the bundle budget from slice 3 and record the resulting sizes on the issue.
- Manual sweep — walk every screen once by keyboard only at 360px and at desktop width, and note anything unreachable on the issue.
Agent Routing
agent_routing:
complexity_hint: medium
required_capability: balanced
parallel_safe: false
cost_preference: balanced
speed_preference: balanced
ownership_scope:
- src/SubVora.Web/src/app/layout/shell.component.ts
- src/SubVora.Web/src/app/features/**
- src/SubVora.Web/src/index.html
- src/SubVora.Web/src/styles.scss
verification:
- cd src/SubVora.Web && npx ng build --configuration production
- cd src/SubVora.Web && npx ng test --no-watch
Technical Context Snapshot
Current stack in scope
- UI: Angular (20+) standalone components with signals and built-in control flow, Angular Material (Material 3) as the only component library, SCSS. Static SPA — no SSR, no service worker.
- State: signal-backed injectable stores, one per domain area, mirroring the MAUI ViewModel split in
src/SubVora.Mobile/ViewModels one-to-one. No NgRx.
- API access: hand-written models plus one service per API controller under
src/SubVora.Web/src/app/core/api. Enums travel as JSON strings (JsonStringEnumConverter in Program.cs), so TypeScript string-literal unions are exact.
- Backend consumed unchanged: ASP.NET Core
net10.0, /api/v1/, JWT bearer in the Authorization header, tokens in JSON bodies (no cookies).
- Tests: Angular CLI unit-test builder (Vitest runner; Karma is deprecated) with
HttpTestingController. Stores, interceptors, mappers and utils only — no component-DOM or browser automation.
Dependencies in scope
- Reuse:
@angular/*, @angular/material, rxjs, and the utilities already added under src/SubVora.Web/src/app/core. No chart library, no date library, no HTTP wrapper library.
- New dependency additions allowed for this slice: no. If a dependency looks unavoidable, stop and raise it on the issue rather than adding it.
Architecture alignment
- Preserve the repo's load-bearing rules (CLAUDE.md): burn-rate maths is server-side and counts cycles, never days; currency conversion is a read-time projection and stored amounts are never overwritten; nothing advances
next_billing_date on a timer; provider matching stays one SQL query; the mobile SQLite cache stays a read-only mirror.
- There is deliberately no shared DTO project. Web models mirror the API's JSON contract by convention — a contract change means editing both sides.
create-git-issue provides routing hints only and assigns no concrete agent or model.
run-with-it remains the final runtime routing authority.
Integration touchpoints
- The CSP must allow the API origin for XHR and the catalog logo host for images; getting either wrong breaks the app silently in production.
- No new endpoints or contracts; presentation and headers only.
- Accessibility is a baseline requirement, not a nice-to-have: labels, focus, keyboard reachability and non-colour-only status are in scope even though an automated axe gate is not.
Acceptance criteria
Blocked by
Parent
#196 — PRD: Angular web client for SubVora (parity minus reminders)
What to build
The shell-level polish pass: the app usable from 360px to a wide desktop, reachable entirely by keyboard and screen reader, and locked down with a content security policy. Grouped as one slice because all three are cross-cutting passes over the shell and the finished screens rather than separate user-visible features.
Implementation Steps
overflow-x: autocontainer so the page body never scrolls sideways.aria-live; the overdue state conveyed by text, not colour alone.<meta>toindex.html:default-src 'self', the API origin inconnect-src, the catalog logo host inimg-src, and no third-party script sources.[HITL]— confirm thecatalogLogoUrlhost before lockingimg-src, or logos silently stop loading.Agent Routing
Technical Context Snapshot
Current stack in scope
src/SubVora.Mobile/ViewModelsone-to-one. No NgRx.src/SubVora.Web/src/app/core/api. Enums travel as JSON strings (JsonStringEnumConverterinProgram.cs), so TypeScript string-literal unions are exact.net10.0,/api/v1/, JWT bearer in theAuthorizationheader, tokens in JSON bodies (no cookies).HttpTestingController. Stores, interceptors, mappers and utils only — no component-DOM or browser automation.Dependencies in scope
@angular/*,@angular/material,rxjs, and the utilities already added undersrc/SubVora.Web/src/app/core. No chart library, no date library, no HTTP wrapper library.Architecture alignment
next_billing_dateon a timer; provider matching stays one SQL query; the mobile SQLite cache stays a read-only mirror.create-git-issueprovides routing hints only and assigns no concrete agent or model.run-with-itremains the final runtime routing authority.Integration touchpoints
Acceptance criteria
Blocked by