xtablo-source/.planning/phases/13-design-system-foundation/13-05-PLAN.md
Arthur Belleville 593bb5bf20
fix(13): apply checker revisions to plans 04 and 05
- 13-04: add 13-03 to depends_on, bump wave 3→4 (form-field.css must exist before tailwind.input.css manifest is written)
- 13-05: bump wave 4→5 to follow 13-04; clear overclaimed requirements (DS-01–DS-09 → []); extend Task 1 automated verify with grep checks for RegisterCatalogRoute, build tag, and catalog page heading

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-16 13:24:46 +02:00

16 KiB
Raw Blame History

phase plan type wave depends_on files_modified autonomous requirements must_haves
13-design-system-foundation 05 execute 5
13-03
13-04
backend/internal/web/ui/catalog/catalog.templ
backend/internal/web/ui/catalog/examples.go
backend/internal/web/catalog_route_catalog.go
backend/internal/web/catalog_route_stub.go
backend/internal/web/router.go
backend/justfile
false
truths artifacts key_links
go run -tags catalog ./cmd/web serves GET /ui-catalog and returns HTTP 200
The catalog page renders all 11 component sections with section headings matching DS-XX requirement IDs
The default (no -tags catalog) build does not include the catalog route
The developer can visually verify all component variants before Phase 14 begins
go build ./... (without -tags catalog) succeeds — no undefined symbol linker errors
go test ./... passes — no regressions
path provides contains
backend/internal/web/ui/catalog/catalog.templ Single-page catalog layout with sidebar nav and component sections Component Catalog
path provides contains
backend/internal/web/ui/catalog/examples.go Typed Example structs for each component variant buttonExamples
path provides contains
backend/internal/web/catalog_route_catalog.go Build-tagged catalog route registration //go:build catalog
path provides contains
backend/internal/web/catalog_route_stub.go No-op stub for production builds //go:build !catalog
from to via pattern
backend/internal/web/router.go RegisterCatalogRoute unconditional call in NewRouter RegisterCatalogRoute
from to via pattern
backend/internal/web/catalog_route_catalog.go backend/internal/web/ui/catalog/catalog.templ catalogPageHandler() templ.Handler render catalog

Phase Goal

As a developer, I want to run just catalog and view all 11 component types rendered with every variant on a single page, so that I can visually sign off on token values and component shapes before Phase 14 begins applying the design system to real app views.

Create the build-tag-gated /ui-catalog route with a single-page component catalog. The catalog shows all component variants with their templ call annotations. A human checkpoint at the end gates progression to Phase 14.

Purpose: Visual sign-off is the final gate before Phases 1417 apply the design system to real app views. The catalog makes component shapes and token choices visible before any per-view work begins.

Output:

  • backend/internal/web/ui/catalog/catalog.templ (layout + section rendering)
  • backend/internal/web/ui/catalog/examples.go (typed examples per component)
  • backend/internal/web/catalog_route_catalog.go (//go:build catalog)
  • backend/internal/web/catalog_route_stub.go (//go:build !catalog)
  • router.go wired with unconditional RegisterCatalogRoute call
  • justfile catalog target added

<execution_context> @/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.claude/get-shit-done/workflows/execute-plan.md @/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/phases/13-design-system-foundation/13-CONTEXT.md @.planning/phases/13-design-system-foundation/13-RESEARCH.md @.planning/phases/13-design-system-foundation/13-PATTERNS.md @.planning/phases/13-design-system-foundation/13-UI-SPEC.md @.planning/phases/13-design-system-foundation/13-03-SUMMARY.md @.planning/phases/13-design-system-foundation/13-04-SUMMARY.md From backend/internal/web/router.go: func NewRouter(pinger Pinger, staticFS fs.FS, deps AuthDeps, tabloDeps TablosDeps, taskDeps TasksDeps, etapeDeps EtapesDeps, eventDeps EventsDeps, discussionDeps DiscussionDeps, planningDeps PlanningDeps, fileDeps FilesDeps, csrfKey []byte, env string, trustedOrigins ...string) (http.Handler, error) // Add RegisterCatalogRoute(r) call inside this function, after all protected routes.

Build tag file pair pattern: catalog_route_catalog.go — //go:build catalog — defines RegisterCatalogRoute(r chi.Router) that mounts handler catalog_route_stub.go — //go:build !catalog — defines RegisterCatalogRoute(r chi.Router) {} no-op

Catalog layout (from 13-UI-SPEC.md): Sidebar 240px fixed, right: main content fluid Section order: alphabetical — badge, button, card, empty-state, form-field, icon-button, input, modal, select, table, textarea Each section heading: "{ComponentName} — {DS-req}" (e.g. "Button — DS-02") Modal section renders panel-only (no backdrop wrapper — Pitfall 7)

Catalog copywriting (from 13-UI-SPEC.md): Page title: "Component Catalog" Nav heading: "Components" Empty state demo title: "Nothing here yet" Empty state demo body: "Add your first item to get started." Modal demo title: "Confirm action" Form error demo: "This field is required." Form hint demo: "Enter a value between 1 and 100."

Task 1: Create catalog package (catalog.templ + examples.go + route files + router wiring) backend/internal/web/ui/catalog/catalog.templ, backend/internal/web/ui/catalog/examples.go, backend/internal/web/catalog_route_catalog.go, backend/internal/web/catalog_route_stub.go, backend/internal/web/router.go - go-backend/internal/web/ui/catalog/catalog.templ (structure reference — go-backend uses static file generation but the templ layout structure is the model) - go-backend/internal/web/ui/catalog/examples.go (Example struct pattern and per-component example functions) - backend/internal/web/router.go (find where to insert RegisterCatalogRoute call — after all protected routes, before healthz handler if any) - 13-PATTERNS.md (catalog.templ, examples.go, catalog_route_catalog.go + stub sections) - 13-RESEARCH.md (Catalog Route — Build Tag Pattern section, Pitfall 4, Pitfall 7) - 13-UI-SPEC.md (Catalog Page Contract section — layout, section order, copywriting) Step 1 — Create backend/internal/web/ui/catalog/ directory and catalog.templ: Package declaration: "package catalog". The catalog page uses a fake shell with Tailwind utilities (flex, min-h-screen, w-60, etc.) — no import of real app.css (per D-A04). Layout: two-column flex with sidebar (w-60, fixed, border-r) and main content area (flex-1, p-8). Sidebar nav: heading "Components" + anchor links in alphabetical order: #badge, #button, #card, #empty-state, #form-field, #icon-button, #input, #modal, #select, #table, #textarea. Each section in main content:
with

{ComponentName} — {DS-req}

followed by the component examples from the examples list. Each example: rendered component +
 block showing the Snippet string.
    Modal section: render the ui-modal-panel div directly without the ui-modal-backdrop wrapper
    (Pitfall 7 — backdrop position:fixed would overlay the page).
    Page title: .
    Import path for ui components: "backend/internal/web/ui".
Step 2 — Create backend/internal/web/ui/catalog/examples.go:
Package declaration: "package catalog".
Define type Example struct { Title string; Preview templ.Component; Snippet string }.
Define example functions for each component:
- badgeExamples() — returns examples for info, warning, success, danger, primary variants
- buttonExamples() — returns examples for solid/default, solid/danger, soft/neutral, ghost variants (all size MD)
- cardExamples() — returns one example with body content using textBody helper
- emptyStateExamples() — returns one example with title "Nothing here yet", description "Add your first item to get started."
- formFieldExamples() — returns example with Label, Hint, Error using "This field is required." and "Enter a value between 1 and 100."
- iconButtonExamples() — returns examples for ghost/neutral (plus icon) and solid/danger (trash icon)
- inputExamples() — returns examples for text type, email type, with placeholder
- modalExamples() — returns one example rendering ModalProps{Title: "Confirm action"} BUT wraps it to render only the ui-modal-panel (not the backdrop)
- selectExamples() — returns one example with a few options
- tableExamples() — returns one example with a simple head/body
- textareaExamples() — returns one example with placeholder and rows

Add a componentFunc helper (adapted from go-backend examples.go):
func componentFunc(f func(ctx context.Context, w io.Writer) error) templ.Component.
Imports: "context", "io", "github.com/a-h/templ", "backend/internal/web/ui".

Step 3 — Create backend/internal/web/catalog_route_catalog.go:
First line: //go:build catalog
Package declaration: package web
Import: "github.com/go-chi/chi/v5"
Define RegisterCatalogRoute(r chi.Router) that calls r.Get("/ui-catalog", catalogPageHandler()).
Define catalogPageHandler() http.HandlerFunc that renders catalog.templ using templ.Handler.
Import path for catalog: "backend/internal/web/ui/catalog".

Step 4 — Create backend/internal/web/catalog_route_stub.go:
First line: //go:build !catalog
Package declaration: package web
Import: "github.com/go-chi/chi/v5"
Define RegisterCatalogRoute(r chi.Router) {} // no-op in production builds.

Step 5 — Update backend/internal/web/router.go:
Add a single unconditional call RegisterCatalogRoute(r) inside NewRouter, after all authenticated
routes are registered and before the function returns. In the non-catalog build, the stub's
no-op satisfies the symbol — no linker error.

Run: cd backend && go build ./... — must succeed (without -tags catalog — stub satisfies symbol).
Run: cd backend && go build -tags catalog ./... — must succeed (catalog build).
Run: cd backend && just generate && go test ./... -count=1 — must be green.
cd backend && go build ./... && go build -tags catalog ./... && just generate && go test ./... -count=1 && grep 'RegisterCatalogRoute' internal/web/router.go && grep '^//go:build catalog' internal/web/catalog_route_catalog.go && grep 'Component Catalog' internal/web/ui/catalog/catalog.templ - go build ./... (without catalog tag) succeeds — no linker errors - go build -tags catalog ./... succeeds - just generate && go test ./... -count=1 is green - backend/internal/web/catalog_route_catalog.go starts with "//go:build catalog" - backend/internal/web/catalog_route_stub.go starts with "//go:build !catalog" - router.go contains "RegisterCatalogRoute(r)" - catalog.templ contains "Component Catalog" - catalog.templ contains each section anchor: "#badge", "#button", "#card", "#input" - examples.go contains "func buttonExamples()" - examples.go contains "func modalExamples()" - The modal example does NOT use "ui-modal-backdrop" as a wrapper (renders panel-only) Catalog package created; route files registered; router wired; build succeeds with and without catalog tag; tests green Task 2: Visual sign-off — run catalog and verify all 11 component sections A dev-only catalog route at /ui-catalog that renders all 11 component types with all variants. The catalog is gated by //go:build catalog so it never ships in production. The justfile has a "catalog" target that runs: just generate && go run -tags catalog ./cmd/web Developer visits http://localhost:8080/ui-catalog to review all components. 1. Run the catalog server: cd backend && just generate && go run -tags catalog ./cmd/web (or: just catalog if the target is wired)
2. Visit http://localhost:8080/ui-catalog in a browser.

3. Verify the sidebar shows all 11 section links in alphabetical order:
   badge, button, card, empty-state, form-field, icon-button, input, modal, select, table, textarea

4. For each section, verify:
   - Section heading includes the DS requirement number (e.g. "Button — DS-02")
   - All declared variants are rendered (e.g. button: solid/default, solid/danger, soft/neutral, ghost)
   - Each variant shows a templ call annotation below the rendered component

5. Verify specific components:
   - Button: ghost variant renders with transparent background (no colored fill)
   - Badge: primary variant renders with purple-tinted background (different from info blue)
   - Card: header and body regions render as separate bordered sections
   - Modal: modal panel is visible (no full-page backdrop overlay)
   - EmptyState: icon circle + title + description render correctly
   - Select: dropdown control renders with chevron indicator
   - IconButton: ghost variant has no background, solid variant has filled background

6. Check browser console — no JavaScript errors.

7. Confirm token values look correct:
   - Brand purple (#804eec) on primary buttons and ghost button text
   - Pill-shaped badges with subtle colored borders
   - Cards with rounded corners and shadow

8. Type "approved" if all 11 sections look correct, or describe any issues to address.
Type "approved" to proceed to Phase 14, or describe specific visual issues (component, variant, observed vs expected appearance) so they can be fixed before Phase 14 begins.

<threat_model>

Trust Boundaries

Boundary Description
Catalog route → production binary //go:build catalog tag ensures the route is never compiled into production; production binary built without -tags catalog
Catalog page → browser All catalog content is developer-authored static data (no user input rendered on catalog page)

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-13-05-01 Information Disclosure /ui-catalog route in production mitigate //go:build catalog + //go:build !catalog stub ensures the route symbol is a no-op in default builds. Production deployment uses go build ./... (no catalog tag). Verify with: go build ./... followed by strings ./cmd/web/main-binary
T-13-05-02 Elevation of Privilege catalog route in dev accept Catalog is dev-only; no auth required is intentional for a local development tool; serves static component previews only, no user data
T-13-05-03 Information Disclosure examples.go static strings accept Demo strings ("Nothing here yet", "Confirm action", etc.) are hard-coded display text; no secrets, no user data
</threat_model>
After this plan completes (pre-checkpoint): - cd backend && go build ./... — must succeed (no catalog tag, stub satisfies symbol) - cd backend && go build -tags catalog ./... — must succeed - cd backend && go test ./... -count=1 — must be green - grep 'RegisterCatalogRoute' backend/internal/web/router.go — must match - grep '^//go:build catalog$' backend/internal/web/catalog_route_catalog.go — must match - grep '^//go:build !catalog$' backend/internal/web/catalog_route_stub.go — must match

After checkpoint approval:

  • Developer has visually confirmed all 11 component sections on /ui-catalog
  • Token values (brand purple, badge tones, card shadow) match expectations
  • Phase 14 can begin

<success_criteria>

  1. go build ./... and go build -tags catalog ./... both succeed
  2. go test ./... is green with no regressions
  3. /ui-catalog serves all 11 component sections with variants and templ annotations
  4. Production build (no -tags catalog) excludes catalog route — RegisterCatalogRoute is a no-op
  5. Developer has approved the visual output via the checkpoint — all 11 sections confirmed correct </success_criteria>
After completion, create `.planning/phases/13-design-system-foundation/13-05-SUMMARY.md`