docs(09): UI design contract
This commit is contained in:
parent
df741e43ee
commit
1e0821915f
1 changed files with 287 additions and 0 deletions
287
.planning/phases/09-etapes/09-UI-SPEC.md
Normal file
287
.planning/phases/09-etapes/09-UI-SPEC.md
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
---
|
||||||
|
phase: 09
|
||||||
|
slug: etapes
|
||||||
|
status: approved
|
||||||
|
shadcn_initialized: false
|
||||||
|
preset: none
|
||||||
|
created: 2026-05-15
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 09 - UI Design Contract
|
||||||
|
|
||||||
|
> Visual and interaction contract for Phase 9. This phase adds etape controls to the existing Go/HTMX Tasks tab without redesigning the kanban.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design System
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| Tool | none |
|
||||||
|
| Preset | not applicable |
|
||||||
|
| Component library | local `backend/internal/web/ui` helpers plus Tailwind utility classes |
|
||||||
|
| Icon library | none currently used in this backend UI; do not introduce an icon package for this phase |
|
||||||
|
| Font | inherited app/system sans-serif |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Product Intent
|
||||||
|
|
||||||
|
Phase 9 should feel like an extension of the current working kanban, not a new task product. The user asked for a first functional UI now and a beautiful UI later. Keep the surface quiet, compact, and explicit.
|
||||||
|
|
||||||
|
Locked decisions from `09-CONTEXT.md`:
|
||||||
|
|
||||||
|
- Etapes live in a compact top strip above the existing kanban.
|
||||||
|
- Etapes render as chips with task counts.
|
||||||
|
- Include an `Unassigned` chip.
|
||||||
|
- Selecting an etape filters the existing kanban.
|
||||||
|
- Status columns and task drag/drop behavior remain the primary task interaction.
|
||||||
|
- Task assignment happens in task create/edit forms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Layout Contract
|
||||||
|
|
||||||
|
### Tasks Tab Structure
|
||||||
|
|
||||||
|
`TasksTabFragment` must render in this order:
|
||||||
|
|
||||||
|
1. Etape strip zone.
|
||||||
|
2. Existing kanban board.
|
||||||
|
|
||||||
|
Use a whole-fragment swap for etape filtering so active chip state, chip counts, add-task forms, and kanban contents stay in sync.
|
||||||
|
|
||||||
|
Required outer ids/classes:
|
||||||
|
|
||||||
|
- `#tab-content` remains the tab switch target.
|
||||||
|
- `#tasks-tab` should wrap the full Tasks tab fragment after Phase 9.
|
||||||
|
- `#etape-strip` should wrap the chip strip and etape management controls.
|
||||||
|
- `#kanban-board` remains the kanban board id.
|
||||||
|
- Existing `.sortable-column`, `.task-card-zone`, and `.task-card` semantics must remain stable.
|
||||||
|
|
||||||
|
### Etape Strip
|
||||||
|
|
||||||
|
The etape strip sits directly above `#kanban-board`.
|
||||||
|
|
||||||
|
Visual contract:
|
||||||
|
|
||||||
|
- Horizontal flex row with wrapping: `flex flex-wrap items-center gap-2`.
|
||||||
|
- Bottom spacing before kanban: `mb-4`.
|
||||||
|
- No surrounding card, no nested card, no panel border around the whole strip.
|
||||||
|
- Chips are compact inline buttons or links, not large cards.
|
||||||
|
- Chips should not resize the kanban columns.
|
||||||
|
- Long etape titles must truncate inside the chip with `max-w-*`, `truncate`, and visible count.
|
||||||
|
|
||||||
|
Minimum chip set:
|
||||||
|
|
||||||
|
- `All` chip: shows all tasks.
|
||||||
|
- `Unassigned` chip: filters tasks with no etape.
|
||||||
|
- One chip per etape.
|
||||||
|
|
||||||
|
The `All` chip is allowed even though the user only explicitly requested `Unassigned`; it is the necessary return path from a filtered view.
|
||||||
|
|
||||||
|
### Kanban Board
|
||||||
|
|
||||||
|
Keep the existing four horizontal columns and fixed column width.
|
||||||
|
|
||||||
|
Do not:
|
||||||
|
|
||||||
|
- split columns into etape groups,
|
||||||
|
- turn etapes into lanes,
|
||||||
|
- move status labels under etapes,
|
||||||
|
- change `TaskColumns`,
|
||||||
|
- place the kanban inside a decorative card.
|
||||||
|
|
||||||
|
When a filter has no tasks in a status, keep the existing empty copy style: `No tasks yet`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interaction Contract
|
||||||
|
|
||||||
|
### Filtering
|
||||||
|
|
||||||
|
Chip behavior:
|
||||||
|
|
||||||
|
- `All` chip requests `/tablos/{id}/tasks` with no etape filter.
|
||||||
|
- `Unassigned` chip requests `/tablos/{id}/tasks?etape=unassigned`.
|
||||||
|
- Etape chips request `/tablos/{id}/tasks?etape={etape_id}`.
|
||||||
|
- HTMX target should be `#tab-content` or `#tasks-tab`, not only `#kanban-board`, because active chip state and counts must update with the board.
|
||||||
|
- `hx-push-url` should keep the current filter URL shareable and refresh-safe when practical.
|
||||||
|
|
||||||
|
Active chip state:
|
||||||
|
|
||||||
|
- Active chip uses a stronger border/text/background than inactive chips.
|
||||||
|
- Active chip must be visible without relying on color alone: use both border weight/color and text weight.
|
||||||
|
|
||||||
|
### Etape Create/Edit/Delete
|
||||||
|
|
||||||
|
Management controls should remain close to the strip.
|
||||||
|
|
||||||
|
Recommended first shape:
|
||||||
|
|
||||||
|
- A compact `New etape` button at the end of the strip.
|
||||||
|
- Inline create form swaps into a small strip-adjacent zone, not into the kanban columns.
|
||||||
|
- Edit/delete controls may be exposed from the active etape chip or an inline edit state; the plan may choose the simplest HTMX pattern that stays compact.
|
||||||
|
- Delete confirmation copy must make clear that tasks are kept and unassigned.
|
||||||
|
|
||||||
|
Etape reorder:
|
||||||
|
|
||||||
|
- Reordering can be implemented with explicit up/down controls or Sortable.js over the chip strip.
|
||||||
|
- If using Sortable.js, do not interfere with `.sortable-column` task drag/drop.
|
||||||
|
- Reorder controls must preserve chip dimensions and avoid layout shift.
|
||||||
|
|
||||||
|
### Task Assignment
|
||||||
|
|
||||||
|
Task create/edit forms must include an etape selector.
|
||||||
|
|
||||||
|
Selector contract:
|
||||||
|
|
||||||
|
- Label: `Etape`.
|
||||||
|
- Options include `No etape`.
|
||||||
|
- Existing etapes are listed by title.
|
||||||
|
- If the current board filter is a specific etape, new task forms may preselect that etape.
|
||||||
|
- If the current filter is `Unassigned`, forms should default to `No etape`.
|
||||||
|
- If the current filter is `All`, forms should default to `No etape` unless editing an assigned task.
|
||||||
|
|
||||||
|
Selector styling should match existing inputs:
|
||||||
|
|
||||||
|
- text size `text-sm`,
|
||||||
|
- border `border-slate-300`,
|
||||||
|
- rounded border,
|
||||||
|
- `px-2 py-1` inside compact task cards or `px-3 py-2` in larger forms.
|
||||||
|
|
||||||
|
### Deletion
|
||||||
|
|
||||||
|
Etape delete confirmation copy:
|
||||||
|
|
||||||
|
- Heading: `Delete etape?`
|
||||||
|
- Body: `Tasks in this etape will stay in the tablo and move to Unassigned.`
|
||||||
|
- Primary destructive action: `Delete etape`
|
||||||
|
- Secondary action: `Keep etape`
|
||||||
|
|
||||||
|
Deleting an etape must not visually remove task cards permanently from the tablo. After deletion, affected tasks should appear under `Unassigned` or `All` depending on the active filter.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spacing Scale
|
||||||
|
|
||||||
|
Declared values are multiples of 4 and align with existing Tailwind usage.
|
||||||
|
|
||||||
|
| Token | Value | Usage |
|
||||||
|
|-------|-------|-------|
|
||||||
|
| xs | 4px | Icon-free inline gaps, chip inner count spacing |
|
||||||
|
| sm | 8px | Chip gaps, compact form field gaps |
|
||||||
|
| md | 16px | Strip-to-board spacing, card padding, column gaps |
|
||||||
|
| lg | 24px | Existing tab content rhythm |
|
||||||
|
| xl | 32px | Not required for this compact feature |
|
||||||
|
| 2xl | 48px | Not required |
|
||||||
|
| 3xl | 64px | Not required |
|
||||||
|
|
||||||
|
Exceptions: none.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
Use current app typography; do not introduce viewport-scaled text.
|
||||||
|
|
||||||
|
| Role | Size | Weight | Line Height |
|
||||||
|
|------|------|--------|-------------|
|
||||||
|
| Body | 14px (`text-sm`) | 400 | default Tailwind normal |
|
||||||
|
| Label | 14px (`text-sm`) | 500 | default Tailwind normal |
|
||||||
|
| Compact meta/count | 12px (`text-xs`) | 500 | default Tailwind normal |
|
||||||
|
| Section heading | 16px (`text-base`) | 600 | default Tailwind normal |
|
||||||
|
| Page heading | unchanged existing page heading | unchanged | unchanged |
|
||||||
|
|
||||||
|
Chip titles should use `text-sm`. Counts should use `text-xs` or local badge styling.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Color
|
||||||
|
|
||||||
|
Use the existing slate-forward palette. Do not add a new brand accent for etapes.
|
||||||
|
|
||||||
|
| Role | Value | Usage |
|
||||||
|
|------|-------|-------|
|
||||||
|
| Dominant (60%) | `#ffffff` / `bg-white` | Task cards, form surfaces |
|
||||||
|
| Secondary (30%) | `#f1f5f9` / `bg-slate-100`; `#e2e8f0` / `border-slate-200` | Column headers, chip backgrounds, borders |
|
||||||
|
| Accent (10%) | `#1e293b` / `text-slate-800`, `border-slate-800` | Active chip, active tab-like state |
|
||||||
|
| Destructive | existing danger button styles | Etape delete confirmation only |
|
||||||
|
|
||||||
|
Accent reserved for:
|
||||||
|
|
||||||
|
- active etape chip,
|
||||||
|
- selected tab/filter state,
|
||||||
|
- primary create/save actions through existing `ui.Button` default styling.
|
||||||
|
|
||||||
|
Do not use gradients, decorative colored strips, or a one-off etape color palette.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Copywriting Contract
|
||||||
|
|
||||||
|
| Element | Copy |
|
||||||
|
|---------|------|
|
||||||
|
| Primary CTA | `New etape` |
|
||||||
|
| Etape title label | `Title` |
|
||||||
|
| Etape description label | `Description (optional)` |
|
||||||
|
| Empty etape strip body | `No etapes yet` |
|
||||||
|
| Empty etape strip action | `New etape` |
|
||||||
|
| Filter chip for all tasks | `All` |
|
||||||
|
| Filter chip for tasks without etape | `Unassigned` |
|
||||||
|
| Task assignment label | `Etape` |
|
||||||
|
| Task assignment empty option | `No etape` |
|
||||||
|
| Etape title validation | `Title is required` |
|
||||||
|
| Etape delete heading | `Delete etape?` |
|
||||||
|
| Etape delete body | `Tasks in this etape will stay in the tablo and move to Unassigned.` |
|
||||||
|
| Etape delete action | `Delete etape` |
|
||||||
|
| Etape delete cancel | `Keep etape` |
|
||||||
|
| Generic server error | `Something went wrong. Please try again.` |
|
||||||
|
|
||||||
|
Avoid explanatory UI text about keyboard shortcuts, implementation, or how filtering works. The chip labels and active state should carry the interaction.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Accessibility Contract
|
||||||
|
|
||||||
|
- Chips must be real links or buttons.
|
||||||
|
- Active chip must include `aria-current="true"` when rendered as a link.
|
||||||
|
- Delete/edit controls must have task- or etape-specific `aria-label` values when the visible label is short.
|
||||||
|
- Form labels must be associated with inputs via `for`/`id` or an equivalent explicit label pattern.
|
||||||
|
- Count badges must remain readable as text, not only visual badges.
|
||||||
|
- Focus styles must remain visible through existing browser/Tailwind focus treatment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HTMX Swap Contract
|
||||||
|
|
||||||
|
Preferred fragment boundaries:
|
||||||
|
|
||||||
|
- Filtering: swap `#tasks-tab` or `#tab-content`.
|
||||||
|
- Etape create/edit/delete: swap a dedicated etape strip/form zone and include out-of-band updates if counts or board contents change.
|
||||||
|
- Task create/update: preserve current `.task-card-zone` and add-task slot behavior.
|
||||||
|
- Reorder: preserve `#kanban-board` as the task reorder response target.
|
||||||
|
|
||||||
|
Any response that changes etape assignment or etape deletion should update chip counts. Whole Tasks-tab swap is acceptable for the first version.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registry Safety
|
||||||
|
|
||||||
|
| Registry | Blocks Used | Safety Gate |
|
||||||
|
|----------|-------------|-------------|
|
||||||
|
| shadcn official | none | not required |
|
||||||
|
| third-party registry | none | do not use |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checker Sign-Off
|
||||||
|
|
||||||
|
- [x] Dimension 1 Copywriting: PASS
|
||||||
|
- [x] Dimension 2 Visuals: PASS
|
||||||
|
- [x] Dimension 3 Color: PASS
|
||||||
|
- [x] Dimension 4 Typography: PASS
|
||||||
|
- [x] Dimension 5 Spacing: PASS
|
||||||
|
- [x] Dimension 6 Registry Safety: PASS
|
||||||
|
|
||||||
|
**Approval:** approved 2026-05-15
|
||||||
Loading…
Reference in a new issue