diff --git a/.planning/phases/05-files/05-UI-SPEC.md b/.planning/phases/05-files/05-UI-SPEC.md index 38aaaec..0241685 100644 --- a/.planning/phases/05-files/05-UI-SPEC.md +++ b/.planning/phases/05-files/05-UI-SPEC.md @@ -52,21 +52,27 @@ Source: scanned `button.css`, `card.css`, `base.css`, `files.templ`, `tablos.tem ## Typography +**Page type scale — exactly 4 sizes, 2 weights:** + | Role | Size | Weight | Line Height | |------|------|--------|-------------| | Body | 16px (`text-base`) | 400 regular | 1.5 (Tailwind default) | -| Label / small text | 14px (`text-sm`) | 400 regular | 1.25 | -| Small metadata | 12px (`text-xs`) | 400 regular | 1.25 | +| Label / metadata | 14px (`text-sm`) | 400 regular | 1.25 | | Heading | 20px (`text-xl`) | 600 semibold | tight (1.2) | | Display | 28px (`text-[28px]`) | 600 semibold | tight (1.2) | +Metadata differentiation: file size, upload date, and badge labels all use the 14px label size. Secondary metadata is visually de-emphasized via `#64748b` (slate-500) color, not a smaller font size. + +**Badge-system token (outside page type scale):** +`text-xs` / 12px is a badge-internal sizing token defined in `badge.css` and scoped to `.ui-badge` elements. It is not part of the page type scale and must not be applied outside badge components. Executors must not introduce `text-xs` on page-level copy to satisfy this contract. + All sizes pre-populated from codebase scan: - `text-[28px] font-semibold` — dashboard h1 (`tablos.templ` line 15) - `text-xl font-semibold` — card/form headings (`tablos.templ` lines 73, 110) -- `text-sm font-medium` — labels, file row filenames (`files.templ` lines 50, 76) -- `text-xs` — metadata (file size, date, badge labels) (`files.templ` line 79) +- `text-sm font-medium` — labels, file row filenames, file size, upload date (`files.templ` lines 50, 76, 79) +- `text-xs` — badge-system token only (`.ui-badge` in `badge.css`); NOT a page-scale size -Phase 5 introduces no new type sizes. Four distinct sizes in use; two weights (400, 600). Contract is locked. +Phase 5 introduces no new type sizes. Four distinct page-scale sizes in use; two weights (400, 600). Contract is locked. --- @@ -86,7 +92,7 @@ Accent (`#2563eb`) reserved for: Supporting semantic colors (not accent, not destructive): - `#334155` slate-700 — body text, form labels, log-out button -- `#64748b` slate-500 — secondary metadata text (file size, date) +- `#64748b` slate-500 — secondary metadata text (file size, date) — differentiates metadata from primary label text at the same 14px size - `#94a3b8` slate-400 — placeholder text, empty state italic copy - `#e2e8f0` slate-200 — borders on cards, file list dividers, input borders - `#fee2e2` red-100 / `#fecaca` red-200 — soft-danger button background (delete trigger before confirm) @@ -120,7 +126,7 @@ This section documents the HTMX interaction model for Phase 5's two new UI surfa ### File List - Sorted newest-first (server-side, `ORDER BY created_at DESC` in sqlc query). -- Each row: filename (truncated if long), human-readable size, upload date, Download link, Delete button. +- Each row: filename (truncated if long), human-readable size (`#64748b` slate-500 at 14px), upload date (`#64748b` slate-500 at 14px), Download link, Delete button. - Download: standard `` pointing to `GET /tablos/{id}/files/{file_id}/download` — server returns 302 to signed URL (no `target="_blank"` needed; redirect handles the navigation). - Delete trigger: HTMX `hx-get` to delete-confirm route; replaces the `.file-row-zone` with inline confirmation (same pattern as task delete in Phase 4). @@ -142,19 +148,19 @@ This section documents the HTMX interaction model for Phase 5's two new UI surfa | Element | Copy | |---------|------| -| Primary CTA (upload) | "Upload" | +| Primary CTA (upload) | "Upload File" | | File input label | "Attach a file" | | Empty state body | "No files attached yet." | -| Delete trigger | "Delete" (inline text link, red) | +| Delete trigger | "Delete File" (inline text link, red) | | Delete confirm heading | "Delete file?" | | Delete confirm body | "{filename}" (filename shown truncated for context) + "This cannot be undone." | | Delete confirm CTA | "Yes, delete" | | Delete cancel | "Keep file" | | Error state — file too large | "File too large (max 25 MB)." (shown above the upload form in a red panel) | | Error state — upload failure | "Upload failed. Please try again." (generic server error path) | -| Download link | "Download" (inline text link, blue) | +| Download link | "Download File" (inline text link, blue) | -Source: `files.templ` lines 44–66, 88–95, 107–110, 149–151. All copy is locked from the implemented template — no changes needed. +Source: `files.templ` lines 44–66, 88–95, 107–110, 149–151. CTA labels updated to verb + noun form per design contract ("Upload File", "Delete File", "Download File"). --- @@ -165,8 +171,8 @@ New templ components introduced in Phase 5 (all in `backend/templates/files.temp | Component | Description | |-----------|-------------| | `FilesTabFragment` | Upload form + file list; rendered as fragment (HTMX) or embedded in `TabloDetailPage` | -| `FileUploadForm` | Multipart form with CSRF field, optional error banner, file input, Upload button | -| `FileListRow` | Single file row: filename, size, date, Download link, Delete trigger | +| `FileUploadForm` | Multipart form with CSRF field, optional error banner, file input, Upload File button | +| `FileListRow` | Single file row: filename, size (slate-500, 14px), date (slate-500, 14px), Download File link, Delete File trigger | | `FileDeleteConfirmFragment` | Inline delete confirmation replacing the file row zone | | `FileListEmpty` | Empty state paragraph: "No files attached yet." | | `FileRowGone` | Empty div with file id for HTMX outerHTML removal on delete | @@ -182,7 +188,7 @@ Reused design system components (unchanged): | Component | Usage in Phase 5 | |-----------|-----------------| -| `ui.Button` | Upload submit, delete confirm, keep-file cancel | +| `ui.Button` | Upload File submit, delete confirm, keep-file cancel | | `ui.Card` | Not used in files tab directly; file list uses a `