---
phase: 13-design-system-foundation
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - backend/internal/web/ui/base.css
  - backend/internal/web/ui/auth.css
  - backend/internal/web/ui/variants.go
  - backend/internal/web/ui/helpers.go
  - backend/tailwind.input.css
autonomous: true
requirements:
  - DS-01

must_haves:
  truths:
    - "base.css contains the full 223-line CSS custom property vocabulary from go-backend"
    - "auth-provider button styles are preserved in auth.css (not lost when button.css is replaced)"
    - "variants.go declares ButtonVariantGhost, BadgeVariantPrimary, IconButtonVariant, IconButtonTone, and SpacingStep enums"
    - "helpers.go declares buttonType, inputType, inputID, and textareaRows helper functions"
    - "tailwind.input.css imports auth.css so the login page retains provider button styling"
    - "go test ./internal/web/ui/... passes (no regressions from enum additions)"
  artifacts:
    - path: "backend/internal/web/ui/base.css"
      provides: "Full CSS custom property token vocabulary"
      contains: "--color-brand-primary"
    - path: "backend/internal/web/ui/auth.css"
      provides: "Auth provider button CSS extracted from button.css"
      contains: ".auth-provider-button"
    - path: "backend/internal/web/ui/variants.go"
      provides: "All variant enums including Ghost, Primary, IconButton, SpacingStep"
      contains: "ButtonVariantGhost"
    - path: "backend/internal/web/ui/helpers.go"
      provides: "Helper functions for templ components"
      contains: "buttonType"
  key_links:
    - from: "backend/tailwind.input.css"
      to: "backend/internal/web/ui/auth.css"
      via: "@import"
      pattern: "auth\\.css"
    - from: "backend/internal/web/ui/variants.go"
      to: "ButtonVariantGhost"
      via: "const block"
      pattern: "ButtonVariantGhost"
---

## Phase Goal

**As a** developer, **I want to** have a complete CSS token vocabulary and Go variant enums in the backend, **so that** every subsequent plan can port component CSS and templ files against a stable foundation without losing any existing styling.

<objective>
Replace the 28-line backend/base.css stub with the full 223-line token vocabulary from go-backend,
extract auth-provider CSS from button.css into auth.css before button.css is replaced,
and extend variants.go + helpers.go with all new enums and helper functions.

Purpose: This is the mandatory prerequisite wave. Without the full token vocabulary, component CSS
ported in Plans 02–04 cannot reference var(--...) tokens. Without auth.css extraction, replacing
button.css in Plan 02 silently destroys the login page's provider button styling.

Output:
- backend/internal/web/ui/base.css — 223-line token vocabulary (verbatim port, per D-T01/T02/T03)
- backend/internal/web/ui/auth.css — auth-provider selectors extracted from current button.css
- backend/internal/web/ui/variants.go — extended with Ghost/Primary variants + new enums
- backend/internal/web/ui/helpers.go — extended with buttonType/inputType/inputID/textareaRows
- backend/tailwind.input.css — auth.css import added (button.css replacement in Plan 02)
</objective>

<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>

<context>
@.planning/ROADMAP.md
@.planning/REQUIREMENTS.md
@.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
</context>

<tasks>

<task type="auto">
  <name>Task 1: Extract auth.css and replace base.css</name>
  <files>
    backend/internal/web/ui/auth.css,
    backend/internal/web/ui/base.css,
    backend/tailwind.input.css
  </files>
  <read_first>
    - backend/internal/web/ui/button.css (lines 121–180 contain the auth-provider CSS to extract verbatim)
    - backend/internal/web/ui/base.css (current 28-line stub to understand what is being replaced)
    - go-backend/internal/web/ui/base.css (the 223-line source to port verbatim)
    - backend/tailwind.input.css (current 4-import manifest to update)
  </read_first>
  <action>
    Step 1 — Create backend/internal/web/ui/auth.css: copy lines 121–180 from the current
    backend/internal/web/ui/button.css verbatim. The first line must be a comment:
    "/* auth.css — sign-in provider controls, extracted from button.css in Phase 13 */".
    The file must contain the selectors: .auth-provider-stack, .auth-provider-button,
    .auth-provider-button:hover, .auth-provider-button:focus-visible,
    .auth-provider-button-disabled, .auth-provider-separator, .auth-provider-separator span,
    .auth-provider-separator em.

    Step 2 — Replace backend/internal/web/ui/base.css entirely with the contents of
    go-backend/internal/web/ui/base.css (verbatim — per D-T01, D-T02, D-T03).
    Do NOT merge with the existing 28-line stub. The target file is 223 lines.

    Step 3 — Update backend/tailwind.input.css: add the line
    "@import "./internal/web/ui/auth.css";" immediately after the base.css import line.
    Do not yet add imports for the component CSS files being created in Plans 02–04 — those
    are added in Plan 04.
  </action>
  <verify>
    <automated>grep -c 'color-brand-primary' backend/internal/web/ui/base.css</automated>
    Expected output: at least 3 (the token appears in multiple rules).
    Also: grep -c 'auth-provider-button' backend/internal/web/ui/auth.css — must return at least 3.
    Also: grep 'auth\.css' backend/tailwind.input.css — must match.
  </verify>
  <acceptance_criteria>
    - backend/internal/web/ui/base.css is exactly 223 lines (or close — the exact line count from go-backend)
    - backend/internal/web/ui/base.css contains "--color-brand-primary: #804eec"
    - backend/internal/web/ui/base.css contains "--color-text-primary"
    - backend/internal/web/ui/base.css contains "--shadow-surface-md"
    - backend/internal/web/ui/base.css does NOT contain "box-sizing: border-box" at the top (the stub had that; go-backend version puts it inside :root or omits it)
    - backend/internal/web/ui/auth.css exists and contains ".auth-provider-button {"
    - backend/internal/web/ui/auth.css contains ".auth-provider-separator {"
    - backend/tailwind.input.css contains "@import "./internal/web/ui/auth.css";"
    - The auth.css import appears after the base.css import in tailwind.input.css
  </acceptance_criteria>
  <done>base.css replaced with 223-line token vocabulary; auth.css created with extracted provider styles; tailwind.input.css updated with auth.css import</done>
</task>

<task type="auto" tdd="true">
  <name>Task 2: Extend variants.go with new enums and helpers.go with new helper functions</name>
  <files>
    backend/internal/web/ui/variants.go,
    backend/internal/web/ui/helpers.go,
    backend/internal/web/ui/ui_test.go
  </files>
  <read_first>
    - backend/internal/web/ui/variants.go (current file — full read to understand existing enum patterns)
    - backend/internal/web/ui/helpers.go (current file — full read to understand existing helper pattern)
    - backend/internal/web/ui/ui_test.go (current test file — understand existing test structure)
    - go-backend/internal/web/ui/variants.go (source for new enums and class functions)
    - go-backend/internal/web/ui/helpers.go (source for new helper functions)
    - 13-PATTERNS.md (Pattern for variants.go and helpers.go changes)
    - 13-RESEARCH.md (Code Examples section for exact function signatures)
  </read_first>
  <behavior>
    - Test: NormalizedButtonVariant(ButtonVariantGhost) returns ButtonVariantGhost (not ButtonVariantDefault)
    - Test: ButtonClass(ButtonVariantGhost, ButtonToneSolid, SizeMD) contains "ui-button-ghost"
    - Test: NormalizedBadgeVariant(BadgeVariantPrimary) returns BadgeVariantPrimary
    - Test: BadgeClass(BadgeVariantPrimary) == "ui-badge ui-badge-primary"
    - Test: IconButtonClass(IconButtonVariantNeutral, IconButtonToneGhost) contains "borderless-icon-button"
    - Test: IconButtonClass(IconButtonVariantNeutral, IconButtonToneSolid) contains "ui-icon-button-solid"
    - Test: SpaceXClass(SpacingStepMD) == "ui-space-x ui-space-x-md"
    - Test: SpaceYClass(SpacingStepLG) == "ui-space-y ui-space-y-lg"
  </behavior>
  <action>
    Step 1 — Write the failing tests in ui_test.go first (RED). Add test functions for:
    TestButtonVariantGhost_Normalizer, TestButtonClass_GhostVariant,
    TestBadgeVariantPrimary_Normalizer, TestBadgeClass_PrimaryVariant,
    TestIconButtonClass_GhostNeutral, TestIconButtonClass_SolidNeutral,
    TestSpaceXClass_MD, TestSpaceYClass_LG.
    Run go test ./internal/web/ui/... and confirm failures (RED).

    Step 2 — Update variants.go (GREEN):
    a. Add "ButtonVariantGhost ButtonVariant = "ghost"" to the ButtonVariant const block.
    b. Add "ButtonVariantGhost" case to NormalizedButtonVariant switch: "case ButtonVariantGhost: return variant".
    c. Add "BadgeVariantPrimary BadgeVariant = "primary"" to the BadgeVariant const block.
    d. Add "BadgeVariantPrimary" case to NormalizedBadgeVariant switch.
    e. Add IconButtonVariant type with constants: IconButtonVariantNeutral="neutral",
       IconButtonVariantWarning="warning", IconButtonVariantSuccess="success",
       IconButtonVariantDanger="danger".
    f. Add IconButtonTone type with constants: IconButtonToneSolid="solid", IconButtonToneGhost="ghost".
    g. Add SpacingStep type with constants: SpacingStepXS="xs", SpacingStepSM="sm",
       SpacingStepMD="md", SpacingStepLG="lg", SpacingStepXL="xl".
    h. Add NormalizedIconButtonVariant function (defaults to IconButtonVariantNeutral).
    i. Add NormalizedIconButtonTone function (defaults to IconButtonToneSolid).
    j. Add NormalizedSpacingStep function (defaults to SpacingStepMD).
    k. Add IconButtonClass(variant IconButtonVariant, tone IconButtonTone) string function.
       Ghost tone path: returns "borderless-icon-button ui-icon-button-ghost ui-icon-button-" + normalizedVariant.
       Solid tone path: returns "ui-icon-button ui-icon-button-solid ui-icon-button-" + normalizedVariant.
    l. Add SpaceXClass(step SpacingStep) string — returns "ui-space-x ui-space-x-" + normalizedStep.
    m. Add SpaceYClass(step SpacingStep) string — returns "ui-space-y ui-space-y-" + normalizedStep.

    Step 3 — Update helpers.go (GREEN):
    Add four new unexported helper functions (following the existing mergeAttrs pattern):
    a. buttonType(value string) string — returns "button" if value is empty, otherwise value.
    b. inputType(value string) string — returns "text" if value is empty, otherwise value.
    c. inputID(id string, name string) string — returns id if non-empty, otherwise name.
    d. textareaRows(rows int) string — returns strconv.Itoa(rows) if rows > 0, else "4".
    Add import "strconv" to helpers.go.

    Do NOT change ButtonClass() output yet — that is Plan 02's work.
    Run go test ./internal/web/ui/... and confirm all tests pass (GREEN).
  </action>
  <verify>
    <automated>cd backend && go test ./internal/web/ui/... -run "TestButtonVariantGhost|TestBadgeVariantPrimary|TestIconButtonClass|TestSpaceXClass|TestSpaceYClass" -v</automated>
  </verify>
  <acceptance_criteria>
    - All 8 new test functions pass
    - go test ./internal/web/ui/... (full suite) is green — no existing tests broken
    - variants.go contains "ButtonVariantGhost ButtonVariant = "ghost""
    - variants.go contains "BadgeVariantPrimary BadgeVariant = "primary""
    - variants.go contains type IconButtonVariant string
    - variants.go contains type SpacingStep string
    - variants.go contains func IconButtonClass
    - variants.go contains func SpaceXClass
    - helpers.go imports "strconv"
    - helpers.go contains func buttonType
    - helpers.go contains func textareaRows
    - ButtonClass() still returns the OLD compound pattern (ui-button-solid-default-md) — this is intentional; Plan 02 migrates it
  </acceptance_criteria>
  <done>variants.go extended with all new enums and class functions; helpers.go extended with component helper functions; all tests green</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| Static build → browser | CSS compiled by Tailwind CLI; no runtime input |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-13-01-01 | Information Disclosure | base.css token values | accept | Token values are design constants (brand colors, spacing). No secrets. Acceptable public visibility. |
| T-13-01-02 | Tampering | variants.go normalizer | accept | Normalizers return safe defaults for unknown inputs; no user-controlled data flows through variant enums at runtime. |
</threat_model>

<verification>
After this plan completes:
- cd backend && go test ./internal/web/ui/... — must be green
- grep -c 'color-brand-primary' backend/internal/web/ui/base.css — must return >= 3
- grep 'ButtonVariantGhost' backend/internal/web/ui/variants.go — must match
- grep 'auth-provider-button' backend/internal/web/ui/auth.css — must match
- grep 'auth\.css' backend/tailwind.input.css — must match
</verification>

<success_criteria>
1. base.css contains the full go-backend token vocabulary (223 lines, --color-brand-primary: #804eec present)
2. auth.css exists with .auth-provider-button and is imported in tailwind.input.css
3. variants.go has ButtonVariantGhost, BadgeVariantPrimary, IconButtonVariant, IconButtonTone, SpacingStep
4. variants.go has IconButtonClass(), SpaceXClass(), SpaceYClass() functions
5. helpers.go has buttonType(), inputType(), inputID(), textareaRows()
6. go test ./internal/web/ui/... is green
</success_criteria>

<output>
After completion, create `.planning/phases/13-design-system-foundation/13-01-SUMMARY.md`
</output>
