Files
Ready2Blob/.planning/phases/09-md3-components/09-01-PLAN.md
T
2026-04-01 09:14:15 +02:00

9.1 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
09-md3-components 01 execute 1
src/components/ui/TextFieldMD3.tsx
src/components/ui/TextFieldMD3.test.tsx
src/styles/md3-buttons.ts
src/components/ui/FieldRenderer.tsx
src/components/ui/FieldRenderer.test.tsx
true
COMP-01
COMP-02
DEBT-01
truths artifacts key_links
TextFieldMD3 renders an outlined input with a floating label that floats on focus
TextFieldMD3 floating label floats when the field has a value (not just on focus)
TextFieldMD3 preserves htmlFor/id pairing so getByLabelText queries work
MD3 button class constants exist for filled, outlined, and text variants
FieldRenderer text-branch tooltip button uses aria-label instead of sr-only span
path provides exports
src/components/ui/TextFieldMD3.tsx MD3 outlined text field with CSS floating label
TextFieldMD3
path provides min_lines
src/components/ui/TextFieldMD3.test.tsx Unit tests for floating label behavior 30
path provides exports
src/styles/md3-buttons.ts MD3 button class constants
MD3_BTN_FILLED
MD3_BTN_OUTLINED
MD3_BTN_TEXT
path provides min_lines
src/components/ui/FieldRenderer.test.tsx Tests for DEBT-01 aria-label consistency 20
from to via pattern
src/components/ui/TextFieldMD3.tsx react-hook-form registration prop spread onto input ...(registration|register)
Create MD3 UI primitives: TextFieldMD3 component with CSS-only floating label, MD3 button class constants, and fix FieldRenderer DEBT-01 aria-label inconsistency.

Purpose: Establish the reusable building blocks that Plan 03 will wire into all wizard steps. TextFieldMD3 is the most complex new component and needs its own test coverage before integration. Output: TextFieldMD3.tsx + tests, md3-buttons.ts constants, FieldRenderer aria-label fix + tests.

<execution_context> @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/09-md3-components/09-RESEARCH.md

@src/index.css @src/components/ui/FieldRenderer.tsx @src/components/ui/PasswordField.tsx

From src/components/ui/FieldRenderer.tsx:

interface FieldRendererProps {
  field: FieldDef;
  register: UseFormRegister<any>;
  error?: FieldError;
}

From src/components/ui/PasswordField.tsx:

interface PasswordFieldProps {
  id: string;
  label: string;
  error?: FieldError;
  registration: UseFormRegisterReturn;
  placeholder?: string;
  helpText?: string;
  tooltipText?: string;
}

From src/schemas/registry.ts (FieldDef shape):

interface FieldDef {
  key: string;
  label: string;
  required?: boolean;
  inputType?: 'text' | 'password' | 'select';
  options?: { value: string; label: string }[];
  placeholder?: string;
  helpText?: string;
  tooltipText?: string;
}
Task 1: Create TextFieldMD3 component with floating label + MD3 button constants src/components/ui/TextFieldMD3.tsx, src/components/ui/TextFieldMD3.test.tsx, src/styles/md3-buttons.ts - Test 1: TextFieldMD3 renders an input with the given id and a label with matching htmlFor - Test 2: screen.getByLabelText(label) finds the input (htmlFor/id pairing works) - Test 3: Input receives registration props (can be queried after typing) - Test 4: Error message renders with role="alert" when error prop is passed - Test 5: Help text renders when helpText prop is passed and no error - Test 6: Required asterisk renders when required=true - Test 7: Suffix slot renders (for PasswordField show/hide button) - Test 8: Input has placeholder=" " for CSS floating label trick Create `src/components/ui/TextFieldMD3.tsx` implementing the CSS-only floating label pattern from the research doc (Pattern 1). Key details: - Interface: `{ id, label, error?, registration, type?, helpText?, required?, suffix? }` where registration is `UseFormRegisterReturn` - Input gets `placeholder=" "` for `:placeholder-shown` CSS detection - Label is absolutely positioned, uses Tailwind `peer` utilities to float on focus and when input has value - Use `peer-[:not(:placeholder-shown)]:` arbitrary variant for has-value state (Tailwind v4 supports this) - All colors use semantic tokens: `border-outline`, `focus:border-primary`, `text-on-surface-container/60`, `text-error` - Error state: `border-error` + error message with `role="alert"` - Suffix slot: absolutely positioned right side (for password show/hide toggle) - Spread `{...registration}` onto the input element
Create `src/styles/md3-buttons.ts` exporting three constants:
- `MD3_BTN_FILLED`: `px-6 py-2.5 rounded-full bg-primary text-on-primary text-sm font-medium hover:opacity-90 active:opacity-80 transition-opacity disabled:opacity-40 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50`
- `MD3_BTN_OUTLINED`: `px-6 py-2.5 rounded-full border border-outline text-on-surface text-sm font-medium hover:bg-primary/8 active:bg-primary/12 transition-colors disabled:opacity-40 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50`
- `MD3_BTN_TEXT`: `px-4 py-2.5 rounded-full text-primary text-sm font-medium hover:bg-primary/8 active:bg-primary/12 transition-colors disabled:opacity-40 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50`

Write tests FIRST (RED), then implement (GREEN).
npx vitest run src/components/ui/TextFieldMD3.test.tsx --reporter=dot TextFieldMD3 component renders floating label with proper htmlFor/id pairing, getByLabelText works, error/helpText/suffix/required all render correctly. MD3 button constants exported. All tests pass. Task 2: Fix DEBT-01 aria-label inconsistency in FieldRenderer + add tests src/components/ui/FieldRenderer.tsx, src/components/ui/FieldRenderer.test.tsx - Test 1: Text-branch tooltip button has aria-label="More info about {field.label}" (not sr-only span) - Test 2: Select-branch tooltip button has aria-label="More info about {field.label}" - Test 3: Both branches' tooltip buttons are findable via getByRole('button', { name: /more info about/i }) Create `src/components/ui/FieldRenderer.test.tsx` with tests for DEBT-01 aria-label consistency. Tests render FieldRenderer with a mock field that has tooltipText, and verify the tooltip button uses `aria-label` attribute (not sr-only span) for both text-branch and select-branch.
Then fix FieldRenderer.tsx text-branch (lines 91-99): Replace the `<span className="sr-only">...</span><span aria-hidden="true">...</span>` pattern with a direct `aria-label={...}` on the button and plain `i` icon content, matching the select-branch pattern (lines 49-54).

The fix is exactly:
```tsx
// Before (text branch, ~line 91):
<button type="button" onClick={...} className="...">
  <span className="sr-only">More info about {field.label}</span>
  <span aria-hidden="true">ⓘ</span>
</button>

// After:
<button
  type="button"
  onClick={...}
  aria-label={`More info about ${field.label}`}
  className="..."
>
  ⓘ
</button>
```

Also apply the same fix to PasswordField.tsx (lines 27-31) which has the identical sr-only pattern.

Run full test suite after to confirm no regressions.
npx vitest run src/components/ui/FieldRenderer.test.tsx --reporter=dot && npx vitest run --reporter=dot Both text-branch and select-branch tooltip buttons use aria-label attribute consistently. PasswordField tooltip also fixed. All 166+ existing tests still pass. DEBT-01 resolved. - `npx vitest run --reporter=dot` — all existing tests pass (no regressions) - `npx vitest run src/components/ui/TextFieldMD3.test.tsx` — new component tests pass - `npx vitest run src/components/ui/FieldRenderer.test.tsx` — DEBT-01 tests pass - `src/styles/md3-buttons.ts` exports MD3_BTN_FILLED, MD3_BTN_OUTLINED, MD3_BTN_TEXT

<success_criteria>

  • TextFieldMD3 component exists with floating label, proper htmlFor/id, and all tests passing
  • MD3 button constants exported from src/styles/md3-buttons.ts
  • FieldRenderer and PasswordField tooltip buttons use consistent aria-label (DEBT-01 complete)
  • Zero test regressions across the full suite </success_criteria>
After completion, create `.planning/phases/09-md3-components/09-01-SUMMARY.md`