feat(design): apply Monolith Editorial design system to all Fase 1 screens

- Remove all 1px border separators (sidebar border-r, page header border-b,
  section border-t, member list divide-y, invite card border). Separation is
  now achieved via background color shifts and whitespace per the "No-Line" rule.
- Fix sidebar: bg-surface-raised vs bg-background creates tonal separation.
- Correct dark background to #020617 (slate-950) per spec.
- Add surface-container-low, text-primary/secondary/muted, destructive tokens.
- Fix CSS variable color space: all tokens are RGB triplets; switch Tailwind
  config and app.css from hsl() to rgb() to prevent yellow/warm color bleed.
- Replace emoji nav icons with Lucide icons at 16px / 1.5 stroke-width.
- Apply Masthead typography (uppercase Label-MD + Title-LG) to all page headers
  and section labels across lists, tasks, notes, search, settings, manage.
- Destructive action (sign out) uses bg-destructive per spec.
- Add `just serve` command: builds and runs the prod Node server at :3000
  against the dev Docker stack for production build testing.
- Add nav_collective i18n key (en + es).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-12 22:09:25 +02:00
parent b297d253d9
commit ce1bcfb7a6
17 changed files with 240 additions and 515 deletions

526
DESIGN.md
View File

@@ -1,442 +1,96 @@
# DESIGN.md
Design reference for the Colectivo app. Stack: SvelteKit + shadcn-svelte + Tailwind CSS.
Generated mockups are in `.stitch/designs/`. Stitch project ID: `10316144241804324155`.
# Design System Specification
## 1. Overview & Creative North Star: "The Monolith Editorial"
The creative direction for this design system is **The Monolith Editorial**. We are moving away from the "busy" aesthetics of modern SaaS and toward a disciplined, high-end editorial experience. The goal is to create a sense of permanent, quiet authority. By stripping away all accent colors and relying on a monochromatic slate palette, we force the user to focus on content, structure, and intent.
The "Template" look is avoided through **Intentional Negative Space** and **Tonal Depth**. We do not use lines to separate ideas; we use the weight of the typography and the shift of the background. It is a system that feels carved rather than assembled.
---
## Visual Style
### Direction
Clean and minimal. Content is the UI — no decorative elements, no gradients, no shadows unless they carry meaning. Generous whitespace. Every element earns its place.
### Color
Monochromatic slate palette. No accent color — contrast and weight carry hierarchy.
```
--background: slate-50 / slate-950
--surface: white / slate-900
--surface-raised: slate-100 / slate-800
--border: slate-200 / slate-700
--text-primary: slate-900 / slate-50
--text-secondary: slate-500 / slate-400
--text-disabled: slate-300 / slate-600
--destructive: red-500 / red-400 ← only color in the UI
```
Interactive elements (buttons, checkboxes, active states) use `slate-900` on light and `slate-50` on dark — inverting the surface. The only true color is destructive red.
### Typography
Font: **Inter** (or `system-ui` as fallback — acceptable on iOS/macOS).
| Role | Size | Weight | Notes |
|---|---|---|---|
| Screen title | 20px | 600 | |
| Section label | 13px | 600 | Uppercase, `letter-spacing: 0.05em` |
| Body / list item | 16px | 400 | |
| Meta / secondary | 13px | 400 | `text-slate-500` |
| Badge / tag | 11px | 500 | Uppercase, `rounded-full` |
| Input | 16px | 400 | Min 16px prevents iOS zoom on focus |
Line height: 1.5 for body, 1.2 for headings. No text smaller than 11px.
### Spacing
Base unit: 4px (Tailwind default). Prefer multiples of 4. Common values:
- List item padding: `px-4 py-3` (min 48px touch target)
- Screen horizontal margin: `px-4` (mobile), `px-6` (desktop)
- Section gap: `gap-6`
- Between section label and content: `mt-2`
- Bottom nav / sticky input safe area: always account for `env(safe-area-inset-bottom)`
### Iconography
**Lucide** (ships with shadcn-svelte). Size `20px` inline, `24px` for nav and primary actions. Stroke width `1.5`. Never fill icons.
### Dark Mode
Class-based (`dark` on `<html>`). Respect `prefers-color-scheme` on first load, then persist user preference in `localStorage`. shadcn-svelte's CSS variable system handles theming — no additional setup needed per component.
## 2. Colors & Tonal Architecture
The palette is strictly monochromatic, utilizing a "Slate" foundation to provide a cooling, sophisticated atmosphere.
### Core Palette
- **Background**: `#f7f9fb` (Light) / `#020617` (Dark)
- **Primary (Action)**: `#000000` (Light) / `#ffffff` (Dark) — *Note: Primary actions invert the surface.*
- **Destructive**: `#ba1a1a` (The only color allowed to break the monochrome seal).
### The "No-Line" Rule
**Explicit Instruction:** Designers are prohibited from using 1px solid borders to section off major UI areas. To define boundaries, you must use background shifts.
- Use `surface-container-low` for secondary content areas.
- Use `surface-container-highest` for high-priority nested components.
- Sectioning is achieved via a 32px48px vertical gap or a subtle change in the background hex, never a line.
### Surface Hierarchy & Nesting
Treat the UI as stacked sheets of gallery-grade paper.
1. **Base Layer:** `surface` (The foundation).
2. **Raised Layer:** `surface-container-low` (For subtle grouping).
3. **Focus Layer:** `surface-container-lowest` (For cards and interactive modules to "pop" via tonal contrast).
---
## Component Library
**shadcn-svelte** — copy-paste components built on bits-ui + Tailwind. No runtime dependency, full ownership of component code.
### Conventions
- Copy components into `apps/web/src/lib/components/ui/` (shadcn-svelte default)
- App-specific compositions go in `apps/web/src/lib/components/` (e.g. `ItemRow.svelte`, `ListCard.svelte`)
- Never modify shadcn-svelte primitives directly — compose or wrap them
- Use shadcn-svelte's `cn()` utility for conditional classes
### Key components to install
| Component | Used for |
|---|---|
| `Button` | All actions |
| `Input` | Add item, search, forms |
| `Checkbox` | Shopping items, tasks |
| `Sheet` | Mobile bottom drawers (item edit, member invite, avatar picker) |
| `Dialog` | Confirmations (delete list, dissolve collective) |
| `Popover` | Presence avatars tooltip |
| `Badge` | Priority tags (TODAY, URGENT), status labels (DRAFT, HIGH PRIORITY) |
| `Skeleton` | Loading states |
| `Sonner` | Toast notifications (sync errors, member joined, undo delete) |
## 3. Typography: The Inter Hierarchy
We use **Inter** exclusively. To achieve an editorial feel, we rely on extreme contrast between `headline-lg` and `label-sm`.
| Level | Size | Weight | Case | Spacing |
| :--- | :--- | :--- | :--- | :--- |
| **Display-LG** | 3.5rem | 600 | Default | -0.02em |
| **Title-LG** | 20px | 600 | Default | -0.01em |
| **Header (Label-MD)** | 13px | 600 | Uppercase | +0.05em |
| **Body-LG** | 16px | 400 | Default | Normal |
| **Meta (Label-SM)** | 13px | 400 | Default | Normal |
**Editorial Note:** Always pair an Uppercase Label-MD header directly above a Title-LG to create a "Masthead" effect for sections.
---
## Layout & Navigation
### App Shell
Two-column on desktop, single-column on mobile.
```
Mobile Desktop (lg+)
┌─────────────────────┐ ┌──────────┬──────────────────────┐
│ Header │ │ │ Breadcrumb / Title │
├─────────────────────┤ │ Sidebar │──────────────────────┤
│ │ │ │ │
│ Content │ │ │ Content │
│ │ │ │ │
├─────────────────────┤ │ User │ │
│ Bottom Nav │ │ Info │ │
└─────────────────────┘ └──────────┴──────────────────────┘
```
### Sidebar (desktop)
Fixed left sidebar, `surface-raised` background (vs `surface` main area — tonal shift, no border line).
```
┌──────────────┐
│ Colectivo │ ← collective name, 14px/600
├──────────────┤
│ 🛒 Lists │ ← active item: slate-900 bg, white text
│ ✓ Tasks │ ← inactive: slate-500 text
│ 📄 Notes │
│ 🔍 Search │
│ │
│ (spacer) │
│ │
│ ┌──┐ Name │ ← user avatar + display name
│ │AG│ Role │ ⚙ ← settings gear icon
└──────────────┘
```
- Collective name at top, 14px/600
- 4 nav items: Lists, Tasks, Notes, Search
- User avatar + display name + settings gear at bottom
- Width: `w-56` (`224px`)
### Navigation (mobile)
Fixed bottom tab bar, 4 tabs. Icons only — no text labels. Active icon: `slate-900`. Inactive: `slate-400`. Glassmorphism background: `backdrop-blur-md bg-white/80`. Respects `safe-area-inset-bottom`.
| Tab | Icon | Route |
|---|---|---|
| Lists | `shopping-cart` | `/lists` |
| Tasks | `check-square` | `/tasks` |
| Notes | `file-text` | `/notes` |
| Search | `search` | `/search` |
### Page Headers
Desktop: breadcrumb-style context path above the title, in uppercase 11px/500 `text-slate-400`:
```
WORKSPACE OVERVIEW
Shared Lists
```
```
PROJECT ATLAS / Tasks
```
Mobile: plain title with optional back arrow and overflow `⋯` menu on the right.
### Responsive Breakpoints
Tailwind defaults: `sm` (640), `md` (768), `lg` (1024). Sidebar appears at `lg`. Content max-width: `max-w-2xl` centered on large screens.
## 4. Elevation & Depth: Tonal Layering
Traditional drop shadows are largely forbidden. Hierarchy must be achieved through **Tonal Layering**.
- **The Layering Principle:** Instead of a shadow, place a `surface-container-lowest` card on a `surface-container-low` background. The slight shift in slate value creates a "natural" lift.
- **Ambient Shadows:** For floating elements like Sheets or Dialogs, use a "Ghost Shadow": `0px 20px 40px rgba(15, 23, 42, 0.06)`. It should feel like a soft glow, not a dark stain.
- **Glassmorphism:** For the "Sticky Bottom Input," use a backdrop-blur of `12px` and a semi-transparent `surface` color (80% opacity). This ensures the content "flows" under the interface, maintaining a sense of depth.
---
## Screen Patterns
### Lists View (`/lists`)
Featured card for the primary/active list (full width, larger), secondary lists in a 2-column grid below.
```
WORKSPACE OVERVIEW
Shared Lists
┌─────────────────────────────────────┐
│ Home 👤👤 │ ← featured card
│ 12 items · Updated 2h ago │
│ [GROCERIES] [HOUSEHOLD] │ ← category tags, rounded-full
└─────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐
│ Office │ │ Party │
│ 5 items │ │ 26 items │
│ 🗂 │ │ HIGH PRTY │ ← badge
└──────────────┘ └──────────────┘
┌─────────────────────────────────────┐
│ + Create a new shared list... [Create] │ ← sticky bottom input
└─────────────────────────────────────┘
```
- Category tags on list cards: `rounded-full text-xs font-medium bg-slate-100 px-2 py-0.5`
- Presence avatars overlap on cards with active members
### List Detail (`/lists/[id]`)
Normal (non-session) view. Items with quantity controls.
```
ACTIVE LIST
Grocery List ⋯
┌─────────────────────────────────────┐
│ ☐ Honeycrisp Apples 6 + │
│ ☐ Organic Baby Spinach 2 + │
│ ☐ Whole milk 1L 2 + │
│ ☐ Salted Butter 1 + │
└─────────────────────────────────────┘
[MILK] [BREAD] [EGGS] [COFFEE] [APPLES] ← frequency chips
┌─────────────────────────────────────┐
│ Add item (e.g. Greek Yogurt 500g) [+] │
└─────────────────────────────────────┘
```
- Quantity controls: `` count `+` inline at the right of each row
- Frequency chips always visible above the input (not just on focus); active chip: `bg-slate-900 text-white`; inactive: `bg-slate-100 text-slate-700`
### Shopping Session Mode (`/lists/[id]/session`)
Full-screen takeover. No sidebar, no bottom nav.
```
✕ Compra semanal 👤👤
─────────────────────────────────────
TO BUY
☐ Whole milk 1L
☐ Bread
☐ Eggs 12 uds
☐ Avocados
CHECKED
☑ Butter 250g (muted, struck through)
☑ Olive Oil 1L
─────────────────────────────────────
[ Finish shopping ● ]
```
- `TO BUY` and `CHECKED` are uppercase section labels, not divider lines
- Items slide from TO BUY to CHECKED on tap — no page scroll
- Minimum tap target: `min-h-[56px]`
- Presence avatars: `size-6 rounded-full`, overlapping, top-right of header
- "Finish shopping" button: full-width, `bg-slate-900 text-white`, with a subtle pulse indicator dot (●)
### Tasks View (`/tasks`)
Tasks grouped into named sections. Filter pills at the bottom.
```
PROJECT ATLAS / Tasks 👤👤
─────────────────────────────────────
HIGH PRIORITY
☐ Finalize Q4 strategy deck TODAY
☐ Coordinate with design on monolith tokens URGENT
PRODUCT DEVELOPMENT
☐ Core API Integration
☐ Review documentation for Auth endpoints (indented sub-item)
☑ Map database schema to editorial structure (completed)
☐ User testing: Editorial workflow
BACKLOG
☐ Update dependency libraries
─────────────────────────────────────
[DAILY] [WEEKLY] [URGENT] ← filter pills
┌─────────────────────────────────────┐
│ Add task to Tasks... [Save] │
└─────────────────────────────────────┘
```
- Section names are user-defined (not system-fixed)
- Priority badges on individual tasks: `text-xs uppercase text-slate-400` (TODAY, URGENT)
- Filter pills: same style as frequency chips; filter the visible tasks
### Notes View (`/notes`)
Notes displayed as cards in a masonry-style grid, grouped by user-defined categories.
```
WORKSPACE / NOTES 🔔 ⊞ 👤
─────────────────────────────────────
CATEGORY
Meeting Notes
┌─────────────────┐ ┌─────────────────┐
│ Q4 Product │ │ Sprint Retro: │
│ Roadmap... │ │ Project Apollo │
│ Oct 24, 2023 │ │ Oct 22, 2023 │
└─────────────────┘ └─────────────────┘
CATEGORY
Project Ideas
┌─────────────────┐
│ Minimalist │
│ Portfolio... │
└─────────────────┘
─────────────────────────────────────
│ Capture a new thought... # @ [SAVE] │
```
- Cards: `bg-white rounded-md` on `surface-raised` background (tonal lift, no shadow)
- Category labels: uppercase section labels above each group
- Inline new-note input at the bottom (not a separate screen)
## 5. Components
All components follow a 4px base grid and a `rounded-md` (0.375rem) corner radius.
### Buttons (Inversion Logic)
- **Primary:** `bg-slate-900 text-slate-50` (Light Mode). This "Inversion" is the signature interactive state.
- **Destructive:** `bg-red-500 text-white`. Reserved only for irreversible actions.
- **Ghost:** No background, `text-slate-500`. On hover, shift to `surface-raised`.
### Input Fields & Sticky Input
- **The Monolith Input:** A sticky bottom container with `backdrop-blur`. No border; only a subtle `surface-raised` background.
- **Chips:** `rounded-full`, `bg-slate-200`. When active, they invert to `bg-slate-900 text-white`.
### Lists & Swipe Actions
- **Forbid Dividers:** Do not use lines between list items. Use 12px of vertical padding (`py-3`) to create separation through whitespace.
- **Swipe-to-Delete:** The "Red Zone" emerges from behind the item. As the user swipes, the background transitions into `red-500` with a white Lucide `trash-2` icon.
### Shopping Mode Components
- **Large Tap Targets:** Increase minimum hit area to 56px.
- **Presence Avatars:** 24px circles with a 2px `surface` border, overlapped horizontally to show collaboration.
---
## UX Patterns
### Optimistic Updates
All mutations update the local store immediately before the server responds. On error, revert and show a toast. Never block the UI waiting for a network response.
### Offline Indicator
A slim bar at the top of the content area (not the header) when offline:
```
No connection · Saving locally
```
`bg-slate-800 text-slate-100`, 36px tall. Mounts/unmounts immediately — no animation.
### Frequency Chips (Add Item Input)
Sticky input area at the bottom of list and task screens. Frequency chips are **always visible** above the input, not conditional on focus.
```
[MILK] [BREAD ●] [EGGS] [COFFEE] [APPLES] ← scrollable row
┌─────────────────────────────────────────────────┐
│ Add item (e.g. Greek Yogurt 500g) [+] │
└─────────────────────────────────────────────────┘
```
- Chips: `rounded-full text-xs font-medium px-3 py-1 bg-slate-100` (inactive) / `bg-slate-900 text-white` (active/selected)
- Tapping a chip fills the input; user can still edit before confirming
- While typing, chips switch to prefix-filtered suggestions (max 10, same pill style)
- Confirm with `+` button or `Enter`
### Swipe to Delete
On list items and tasks. Swipe left reveals a red destructive zone:
```
┌────────────────────────────────────────┐
│ ☐ Whole milk 1L ░░░░░░ │ ← item sliding left
│ [🗑] │ ← red-500 zone, trash icon only
└────────────────────────────────────────┘
```
Full swipe: immediate deletion + Sonner toast "Deleted · Undo" (4 seconds). Partial swipe: snaps back. No confirmation dialog for single items.
### Drag & Drop Reordering
Handle (`grip-vertical` Lucide icon) on the left — always visible on desktop, revealed on long-press on mobile. Use `svelte-dnd-action`. During drag: lifted item has `shadow-md bg-white`, others compress to show insertion point.
### Confirmations
**Destructive, hard-to-reverse actions** (delete list, remove member, dissolve collective): `Dialog` with explicit description text. No shortcut.
**Soft deletions** (single item, single task): swipe + Sonner undo. No dialog.
### Avatar Component
Three mutually exclusive states, all `rounded-full`.
| State | Appearance | Size tokens |
|---|---|---|
| `initials` | Initials on `slate-700` bg, white 600-weight text | `size-6` presence, `size-8` lists, `size-16` settings |
| `emoji` | Emoji centered on `slate-700` bg | same |
| `upload` | `<img object-cover>`, fallback to initials on error | same |
Initials rule: first letter of first + last word in `display_name` ("Ana García" → "AG", "Borja" → "B").
### User Settings Screen (`/settings`)
In the desktop sidebar shell. Content area:
```
← Settings
┌──────┐
│ AG │ ← size-16 avatar
└──────┘
Change avatar
DISPLAY NAME
┌────────────────────────────────┐
│ Ana García │ ← auto-save, 500ms debounce
└────────────────────────────────┘
Changes saved automatically ← slate-400 hint
LANGUAGE
[ English ] [ Español ] ← pill segmented control
English selected: slate-900/white. Instant effect, no reload.
ACCOUNT ← background shift (no line)
Email & password → ← external link to Keycloak
Managed in Keycloak ← slate-400
DANGER ZONE ← section label in red-500
[ Delete account ] ← ghost red button
```
**Avatar sheet**`Sheet` (bottom on mobile, dialog on desktop):
- Segmented tabs: Initials | Emoji | Photo
- Emoji grid: 8 columns, ~40 emoji (🐸 🐼 🦊 🐨 🦁 🐯 🐻 🦋 🌵 🌻 🍀 🌈 ⭐ 🔥 💎 🎯 🎸 🎹 🎨 📚 ✈️ 🏔️ 🌊 🎭 🍕 🍜 🍣 ☕ 🍎 🥑 🍓 🎂 🏠 🚲 🛹 ⚽ 🏄 🧘 🎮 🤿)
- Photo upload: file picker → 1:1 crop in-browser → preview → confirm. Max 2MB. JPG/PNG/WebP.
### Empty States
Text-only, no illustrations. `text-slate-500`, centered in content area.
```
No lists yet.
Tap + to create one.
```
Two lines max.
### Loading States
`Skeleton` components matching the shape of the content (list rows, card grids). Spinner only for full-screen blocking operations (login, initial data fetch).
## 6. Do's and Don'ts
### Do
- **Do** use uppercase labels with letter-spacing for all category headers.
- **Do** use `slate-500` for meta-text to create a visual "recession" of less important data.
- **Do** maximize the use of the `2xl` max-width container on desktop to ensure the "Editorial" line lengths remain readable.
- **Do** use Lucide icons at 1.5 stroke width to match the weight of Inter's medium-weight characters.
### Don't
- **Don't** use a 1px border to separate the sidebar from the main content. Use a background color shift.
- **Don't** use any colors outside of the Slate and Red-500 palette. No "Success Green" or "Warning Orange." Use icons and text to convey state.
- **Don't** use standard "drop shadows" on cards. If it doesn't pop via tonal shift, the layout needs more whitespace, not more shadow.
- **Don't** use "Slate-900" for body text. Use "Slate-900" only for titles; use "Slate-800" or "Slate-700" for long-form reading to reduce eye strain.
---
## Stitch Mockup Inventory
Screens in `.stitch/designs/` — project `10316144241804324155`:
| File | Screen | Status |
|---|---|---|
| `01-lists-view.png` | Lists View | ✅ |
| `02-shopping-session.png` | Shopping Session | ✅ |
| `03-notes-view.png` | Notes View | ✅ |
| `04-tasks-view.png` | Tasks View | ✅ |
| `05-list-detail.png` | List Detail with frequency chips | ✅ |
| — | User Settings | ⏳ pending generation |
| — | Collective Management | ⏳ pending generation |
## 7. Layout Philosophy
- **Desktop (lg+):** The left sidebar is a fixed Monolith. It stays `surface-raised` while the main content area remains `surface`.
- **Mobile:** The bottom tab bar utilizes Glassmorphism. Navigation icons should be `slate-400`, switching to `slate-900` when active. No text labels on mobile tabs; the icons must be clear enough to stand alone.