docs(03): fix UI-SPEC checker blocks — copywriting labels and typography weight

- Replace generic "Save"/"Cancel" with specific labels: "Save changes",
  "Discard changes", "Keep tablo" throughout spec, component inventory,
  interaction contracts, and HTMX reference table
- Remove 500 (medium) weight; form labels now use font-semibold (600),
  consistent with Button convention — 2 declared weights total

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arthur Belleville 2026-05-14 23:42:53 +02:00
parent c779d2aee1
commit 65cdfbc00f
No known key found for this signature in database

View file

@ -74,10 +74,10 @@ All sizes sourced from existing codebase patterns in `templates/` and `ui/`.
| Display | 28px | 600 (semibold) | 1.2 (tight) | `text-[28px] font-semibold leading-tight` | Dashboard page heading ("Your Tablos") | | Display | 28px | 600 (semibold) | 1.2 (tight) | `text-[28px] font-semibold leading-tight` | Dashboard page heading ("Your Tablos") |
| Heading | 20px | 600 (semibold) | 1.3 (snug) | `text-xl font-semibold leading-snug` | Tablo card title, detail page section headings | | Heading | 20px | 600 (semibold) | 1.3 (snug) | `text-xl font-semibold leading-snug` | Tablo card title, detail page section headings |
| Body | 16px | 400 (regular) | 1.5 | `text-base text-slate-600` | Tablo descriptions, paragraph content | | Body | 16px | 400 (regular) | 1.5 | `text-base text-slate-600` | Tablo descriptions, paragraph content |
| Label | 14px | 500 (medium) | 1.4 | `text-sm font-medium text-slate-700` | Form labels, metadata (created date, owner email) | | Label | 14px | 600 (semibold) | 1.4 | `text-sm font-semibold text-slate-700` | Form labels, metadata (created date, owner email) |
| Small/muted | 14px | 400 (regular) | 1.4 | `text-sm text-slate-500` | "Signed in as" subtext, timestamps | | Small/muted | 14px | 400 (regular) | 1.4 | `text-sm text-slate-500` | "Signed in as" subtext, timestamps |
Two declared weights: 400 (regular) and 600 (semibold). The 500 (medium) weight is used only for form labels — this follows the existing auth form pattern (`text-sm font-medium text-slate-700`). 2 declared weights: 400 (regular) and 600 (semibold). Form labels use font-semibold (600), consistent with the Button component convention already established in Phase 2.
--- ---
@ -91,7 +91,7 @@ Two declared weights: 400 (regular) and 600 (semibold). The 500 (medium) weight
| Destructive | #b91c1c | bg-red-700 / text-red-700 | Delete confirmation button, field error text, general error banner | | Destructive | #b91c1c | bg-red-700 / text-red-700 | Delete confirmation button, field error text, general error banner |
Accent (#2563eb / blue-600) reserved for: Accent (#2563eb / blue-600) reserved for:
1. Primary action button — "New tablo" (create) and "Save" (edit submit) 1. Primary action button — "New tablo" (create) and "Save changes" (edit submit)
2. Focus ring on all interactive elements (`outline: 2px solid #2563eb`) 2. Focus ring on all interactive elements (`outline: 2px solid #2563eb`)
3. Nothing else — links, secondary actions, and navigation use slate tones 3. Nothing else — links, secondary actions, and navigation use slate tones
@ -110,7 +110,7 @@ Tablo color field rendering: render as a 10px × 10px filled dot (CSS circle usi
| Component | Import | Usage in Phase 3 | | Component | Import | Usage in Phase 3 |
|-----------|--------|-----------------| |-----------|--------|-----------------|
| `ui.Card` | `backend/internal/web/ui` | Tablo list card, create form container | | `ui.Card` | `backend/internal/web/ui` | Tablo list card, create form container |
| `ui.Button` (solid/default/md) | `backend/internal/web/ui` | "New tablo" CTA, "Save" on edit, "Create tablo" submit | | `ui.Button` (solid/default/md) | `backend/internal/web/ui` | "New tablo" CTA, "Save changes" on edit, "Create tablo" submit |
| `ui.Badge` | `backend/internal/web/ui` | Optional: status labels if needed | | `ui.Badge` | `backend/internal/web/ui` | Optional: status labels if needed |
| `ui.CSRFField` | `backend/internal/web/ui` | All forms (create, edit, delete) — AUTH-06 | | `ui.CSRFField` | `backend/internal/web/ui` | All forms (create, edit, delete) — AUTH-06 |
| `templates.FieldError` | `backend/templates` | Inline field validation on create/edit forms | | `templates.FieldError` | `backend/templates` | Inline field validation on create/edit forms |
@ -131,9 +131,9 @@ Tablo color field rendering: render as a 10px × 10px filled dot (CSS circle usi
| `templates.TabloDescDisplay` | `backend/templates/tablos.templ` | Display fragment for description | | `templates.TabloDescDisplay` | `backend/templates/tablos.templ` | Display fragment for description |
| `templates.TabloDescEditFragment` | `backend/templates/tablos.templ` | Edit textarea fragment for description | | `templates.TabloDescEditFragment` | `backend/templates/tablos.templ` | Edit textarea fragment for description |
| `templates.TabloDeleteButtonFragment` | `backend/templates/tablos.templ` | Delete button (swaps to confirmation row) | | `templates.TabloDeleteButtonFragment` | `backend/templates/tablos.templ` | Delete button (swaps to confirmation row) |
| `templates.TabloDeleteConfirmFragment` | `backend/templates/tablos.templ` | "Delete tablo? Yes / Cancel" confirmation row | | `templates.TabloDeleteConfirmFragment` | `backend/templates/tablos.templ` | "Delete tablo? Yes / Keep tablo" confirmation row |
| `ui.ButtonDanger` (soft/danger/md CSS) | `backend/internal/web/ui/button.css` | Delete-confirm "Yes, delete" button (red variant) | | `ui.ButtonDanger` (soft/danger/md CSS) | `backend/internal/web/ui/button.css` | Delete-confirm "Yes, delete" button (red variant) |
| `ui.ButtonNeutral` (soft/neutral/md CSS) | `backend/internal/web/ui/button.css` | "Cancel" button (slate/ghost variant) | | `ui.ButtonNeutral` (soft/neutral/md CSS) | `backend/internal/web/ui/button.css` | "Discard changes" / "Keep tablo" button (slate/ghost variant) |
--- ---
@ -172,14 +172,14 @@ Tablo color field rendering: render as a 10px × 10px filled dot (CSS circle usi
- Save: `hx-post="/tablos/{id}"` (with `_method=PATCH` hidden input for semantic clarity, handled by chi) targeting the edit fragment container, `hx-swap="outerHTML"`. - Save: `hx-post="/tablos/{id}"` (with `_method=PATCH` hidden input for semantic clarity, handled by chi) targeting the edit fragment container, `hx-swap="outerHTML"`.
- On success: server returns the updated display fragment. - On success: server returns the updated display fragment.
- On validation error: server returns the edit fragment with a field error. - On validation error: server returns the edit fragment with a field error.
- Cancel: A "Cancel" button fires `hx-get="/tablos/{id}/show-title"` (or `/show-desc`) to restore the original display fragment without modifying the DB. - Cancel: A "Discard changes" button fires `hx-get="/tablos/{id}/show-title"` (or `/show-desc`) to restore the original display fragment without modifying the DB.
- Non-HTMX fallback: `POST /tablos/{id}` with `_method=PATCH``303 /tablos/{id}` redirect. - Non-HTMX fallback: `POST /tablos/{id}` with `_method=PATCH``303 /tablos/{id}` redirect.
### 5. Delete Tablo — Inline Confirmation ### 5. Delete Tablo — Inline Confirmation
- Context: delete button visible on both the dashboard card and the detail page. - Context: delete button visible on both the dashboard card and the detail page.
- Trigger: Delete button fires `hx-get="/tablos/{id}/delete-confirm"` targeting the button's container (`hx-target="closest .tablo-delete-zone"`) with `hx-swap="outerHTML"`. - Trigger: Delete button fires `hx-get="/tablos/{id}/delete-confirm"` targeting the button's container (`hx-target="closest .tablo-delete-zone"`) with `hx-swap="outerHTML"`.
- Confirmation row: renders "Delete tablo? [Yes, delete] [Cancel]" inline. - Confirmation row: renders "Delete tablo? [Yes, delete] [Keep tablo]" inline.
- Confirm: `hx-delete="/tablos/{id}"` (or `hx-post` with `_method=DELETE`) with `hx-target="closest .ui-card"` and `hx-swap="outerHTML"` (removes the card from the DOM on success). - Confirm: `hx-delete="/tablos/{id}"` (or `hx-post` with `_method=DELETE`) with `hx-target="closest .ui-card"` and `hx-swap="outerHTML"` (removes the card from the DOM on success).
- Cancel: fires `hx-get="/tablos/{id}/delete-cancel"` to restore the original delete button fragment. - Cancel: fires `hx-get="/tablos/{id}/delete-cancel"` to restore the original delete button fragment.
- On success (dashboard): card removed from `#tablos-list`. If list is now empty, server should return the empty state fragment via `HX-Retarget: #tablos-list` + `HX-Reswap: innerHTML`. - On success (dashboard): card removed from `#tablos-list`. If list is now empty, server should return the empty state fragment via `HX-Retarget: #tablos-list` + `HX-Reswap: innerHTML`.
@ -197,13 +197,13 @@ Tablo color field rendering: render as a 10px × 10px filled dot (CSS circle usi
| Primary CTA (create) | "New tablo" | | Primary CTA (create) | "New tablo" |
| Create form heading | "Create a tablo" | | Create form heading | "Create a tablo" |
| Create submit button | "Create tablo" | | Create submit button | "Create tablo" |
| Edit save button | "Save" | | Edit save button | "Save changes" |
| Edit cancel button | "Cancel" | | Edit cancel button | "Discard changes" |
| Delete button (card/detail) | "Delete" | | Delete button (card/detail) | "Delete" |
| Delete confirm heading | "Delete tablo?" | | Delete confirm heading | "Delete tablo?" |
| Delete confirm body | "This cannot be undone." | | Delete confirm body | "This cannot be undone." |
| Delete confirm action | "Yes, delete" | | Delete confirm action | "Yes, delete" |
| Delete cancel action | "Cancel" | | Delete cancel action | "Keep tablo" |
| Empty state heading | "No tablos yet" | | Empty state heading | "No tablos yet" |
| Empty state body | "Create your first tablo to get started." | | Empty state body | "Create your first tablo to get started." |
| Empty state CTA | "New tablo" (same blue button) | | Empty state CTA | "New tablo" (same blue button) |
@ -225,7 +225,7 @@ Input: mt-1 block w-full rounded border border-slate-300 px-3 py-2 text-sm
placeholder-slate-400 focus:border-slate-500 focus:outline-none placeholder-slate-400 focus:border-slate-500 focus:outline-none
Textarea: mt-1 block w-full rounded border border-slate-300 px-3 py-2 text-sm Textarea: mt-1 block w-full rounded border border-slate-300 px-3 py-2 text-sm
placeholder-slate-400 focus:border-slate-500 focus:outline-none resize-y placeholder-slate-400 focus:border-slate-500 focus:outline-none resize-y
Label: block text-sm font-medium text-slate-700 Label: block text-sm font-semibold text-slate-700
``` ```
Field error: `<p class="mt-1 text-sm text-red-700">` (existing `templates.FieldError`). Field error: `<p class="mt-1 text-sm text-red-700">` (existing `templates.FieldError`).
@ -250,7 +250,7 @@ Phase 3 needs two new CSS rules in `backend/internal/web/ui/button.css`:
.ui-button-solid-danger-md:hover { background-color: #991b1b; } .ui-button-solid-danger-md:hover { background-color: #991b1b; }
``` ```
**Neutral soft md** (cancel, secondary actions): **Neutral soft md** (discard changes, keep tablo, secondary actions):
``` ```
.ui-button-soft-neutral-md { .ui-button-soft-neutral-md {
background-color: #f1f5f9; /* slate-100 */ background-color: #f1f5f9; /* slate-100 */
@ -288,10 +288,10 @@ Established patterns to reuse exactly (do not invent new patterns):
| Submit create (error) | hx-post="/tablos" | #create-form-slot | innerHTML | | Submit create (error) | hx-post="/tablos" | #create-form-slot | innerHTML |
| Show edit title | hx-get="/tablos/{id}/edit-title" | `.tablo-title-zone` | outerHTML | | Show edit title | hx-get="/tablos/{id}/edit-title" | `.tablo-title-zone` | outerHTML |
| Save edit title | hx-post="/tablos/{id}" | `.tablo-title-zone` | outerHTML | | Save edit title | hx-post="/tablos/{id}" | `.tablo-title-zone` | outerHTML |
| Cancel edit title | hx-get="/tablos/{id}/show-title" | `.tablo-title-zone` | outerHTML | | Discard edit title | hx-get="/tablos/{id}/show-title" | `.tablo-title-zone` | outerHTML |
| Show delete confirm | hx-get="/tablos/{id}/delete-confirm" | `.tablo-delete-zone` | outerHTML | | Show delete confirm | hx-get="/tablos/{id}/delete-confirm" | `.tablo-delete-zone` | outerHTML |
| Confirm delete | hx-post="/tablos/{id}/delete" | `.ui-card` (closest) | outerHTML | | Confirm delete | hx-post="/tablos/{id}/delete" | `.ui-card` (closest) | outerHTML |
| Cancel delete confirm | hx-get="/tablos/{id}/delete-cancel" | `.tablo-delete-zone` | outerHTML | | Keep tablo (cancel delete) | hx-get="/tablos/{id}/delete-cancel" | `.tablo-delete-zone` | outerHTML |
All state-changing requests must include the CSRF token. The `@ui.CSRFField(csrfToken)` helper must be in every `<form>` element. All state-changing requests must include the CSRF token. The `@ui.CSRFField(csrfToken)` helper must be in every `<form>` element.
@ -304,7 +304,7 @@ HTMX loading indicator: `class="htmx-indicator"` with `opacity-0` default, shown
Inherited from `base.css` `:focus-visible` rule. Phase 3 additions: Inherited from `base.css` `:focus-visible` rule. Phase 3 additions:
- All form `<input>` and `<textarea>` elements must have an associated `<label for="...">`. - All form `<input>` and `<textarea>` elements must have an associated `<label for="...">`.
- Delete confirmation buttons use `aria-label="Confirm delete tablo"` and `aria-label="Cancel delete"` to distinguish from other Cancel buttons on the page. - Delete confirmation buttons use `aria-label="Confirm delete tablo"` and `aria-label="Keep tablo"` to distinguish from other secondary action buttons on the page.
- Empty state CTA button has `aria-label="Create your first tablo"`. - Empty state CTA button has `aria-label="Create your first tablo"`.
- `<main>` landmark present in `Layout` — no additional landmark required in Phase 3. - `<main>` landmark present in `Layout` — no additional landmark required in Phase 3.