feat: complete UI redesign — editorial-paper palette, Monaco theme, lucide icons, primitives
Palette overhaul: - Warm cream canvas (#faf8f4) + near-white surfaces (#fdfdfb) + deep teal accent (#0f766e) - Unified on stone-warm neutrals via @theme block in index.css Typography: - Inter Variable + JetBrains Mono Variable via fontsource (self-hosted) - Monaco editor set to JetBrains Mono for consistency Icons: - lucide-react replaces all 8 hand-rolled SVGs - Leaf brand mark beside wordmark Shared primitives (src/components/ui/): - Icon, Button (4 variants, 2 sizes), IconButton, TextInput, SectionHeader, StatusDot, Chip Custom Monaco theme (src/theme/monaco-theme.ts): - typst-leaf-light: warm near-white background, restrained rainbow-free syntax - Keywords teal, strings warm green, comments stone italic, most tokens ink Component reworks: - TopBar: Leaf icon, h-12, chip-based status, Button/IconButton primitives - Sidebar: w-72, icon+label tabs with teal bottom border - StatusBar: lucide GitBranch/ArrowUp/ArrowDown, chip-based status - FileTree: lucide Plus/FolderPlus/Pencil/Trash2, teal active state - GitPanel: lucide status icons, restored staged/unstaged bg tint - Preview: RotateCw reload icon, chip status - EmptyState: muted Leaf icon - ToastHost: StatusDot + lucide X Fixes: - GitPanel staged/unstaged/untracked rows now have distinct bg tints - Button defaults to type="button" to prevent accidental form submission - Editor error fallback from red-400 to rose-600 - DESIGN.md: removed emoji from layout skeleton, fixed Modals/Monaco typo Build: pnpm -r typecheck + pnpm build pass.
This commit is contained in:
@@ -1,171 +1,160 @@
|
||||
# DESIGN.md — typst-leaf UI Rehaul
|
||||
# DESIGN.md — typst-leaf UI
|
||||
|
||||
This document is the aesthetic reference for the next chunk in `PLAN.md`. Every UI decision in `PLAN.md` should match this guide. Direction is "document editor" — warmth, restraint, a thin layer of leaf.
|
||||
## Vision
|
||||
|
||||
**"A fresh sheet of paper on a warm desk."** Refined editorial-paper aesthetic with a deep teal accent and warm cream neutrals. The app chrome (sidebar, topbar, statusbar) is a warm cream; editor and preview surfaces are a brighter near-white — so reading and writing happen on a sheet of paper that lifts off the desk.
|
||||
|
||||
## Brand
|
||||
|
||||
- Name: **typst-leaf** — warm, organic, paper-and-ink.
|
||||
- Personality: quiet, considered, a serious editor for prose, papers, and beautiful documents.
|
||||
- Not "developer marketing." Not "dashboard SaaS." Closer to a focused notebook.
|
||||
- **Name:** typst-leaf — warm, organic, paper-and-ink.
|
||||
- **Mark:** A teal `Leaf` icon (lucide) beside the wordmark.
|
||||
- **Personality:** Quiet, considered, a serious editor for prose, papers, and beautiful documents. Like a focused notebook, not a dashboard.
|
||||
|
||||
## Aesthetic Pillars
|
||||
## Palette (Tailwind v4 `@theme`)
|
||||
|
||||
1. **Paper, not glass.**
|
||||
Surfaces are warm off-white, not slate. Borders separate panels; we don't blur or float.
|
||||
2. **Functional color, decorative restraint.**
|
||||
Greens only when communicating success/active/leaf. Use amber for warnings, slate for muted text, rose for errors. No gradients on chrome.
|
||||
3. **Typography is the interface.**
|
||||
System sans for chrome, monospace for paths/git/lsp status. We say things in words, not icons.
|
||||
4. **A whisper, not a shout.**
|
||||
Rounded corners are subtle (`rounded-md`). Focus rings are visible but quiet.
|
||||
Custom tokens in `src/index.css`:
|
||||
|
||||
## Palette (Tailwind v4 via CSS-first)
|
||||
|
||||
| Token | Use | Tailwind class |
|
||||
| Token | Value | Use |
|
||||
|---|---|---|
|
||||
| Page background | Shell chrome | `bg-stone-50` |
|
||||
| Panel surface | Cards, panels | `bg-white` |
|
||||
| Sidebar | Activity + sections | `bg-stone-50` with inner panels `bg-white` |
|
||||
| Text primary | Body | `text-zinc-800` |
|
||||
| Text muted | Meta | `text-zinc-500` |
|
||||
| Text subtle | File paths | `text-zinc-400` |
|
||||
| Divider | 1px borders | `border-zinc-200` |
|
||||
| Hover surface | Buttons/list rows | `hover:bg-stone-100` |
|
||||
| Accent (primary) | Save, commit | `bg-emerald-600 hover:bg-emerald-700 text-white` |
|
||||
| Accent (soft) | Active file, focus | `bg-emerald-50 text-emerald-800 ring-emerald-200` |
|
||||
| Warning | Behind | `text-amber-600` |
|
||||
| Error | Dirty error | `text-rose-600` |
|
||||
| Pill bg (status) | Status chips | `bg-white border-zinc-200` |
|
||||
| `canvas` | `#faf8f4` | App chrome (sidebar, topbar, statusbar, app bg) |
|
||||
| `surface` | `#fdfdfb` | Editor, preview, raised panels, inputs |
|
||||
| `surface-hover` | `#f3f1ec` | Hover backgrounds |
|
||||
| `ink` | `#1c1917` | Primary body text |
|
||||
| `ink-muted` | `#78716c` | Secondary text, meta |
|
||||
| `ink-subtle` | `#a8a29e` | Placeholders, subtle meta |
|
||||
| `line` | `#e7e5e4` | Hairline borders, dividers |
|
||||
| `line-strong` | `#d6d3d1` | Input borders |
|
||||
|
||||
We do not introduce dark classes. Tailwind v4 is in CSS-first mode (`@import "tailwindcss"`); tokens live in plain CSS, not `tailwind.config.js`.
|
||||
| Token | Value | Use |
|
||||
|---|---|---|
|
||||
| `teal-50` | `#f0f9f8` | Active row tints, soft accents |
|
||||
| `teal-100` | `#d5edea` | Focus ring, highlights |
|
||||
| `teal-500` | `#14b8a6` | Status dots, focus border |
|
||||
| `teal-600` | `#0f766e` | Primary actions, active text |
|
||||
| `teal-700` | `#115e59` | Primary hover |
|
||||
|
||||
Functional: `amber-*` (warnings, dirty, behind), `rose-*` (errors, destructive). Monaco defaults (`zinc-*` border, body text) are left as-is.
|
||||
|
||||
No dark classes. Tailwind v4 CSS-first only.
|
||||
|
||||
## Typography
|
||||
|
||||
- UI: `font-sans` (system stack already from Tailwind).
|
||||
- Monospace: `font-mono` for paths, git refs, LSP logs, status messages.
|
||||
- Toolbar/header: `text-sm font-medium tracking-tight`.
|
||||
- Meta and toasts: `text-xs text-zinc-500`.
|
||||
- File names: `text-sm font-mono`.
|
||||
- **UI:** `font-sans` → Inter Variable (via `@fontsource-variable/inter`).
|
||||
- **Mono:** `font-mono` → JetBrains Mono Variable (via `@fontsource-variable/jetbrains-mono`).
|
||||
- **Monaco editor** also uses JetBrains Mono for consistency.
|
||||
- Brand wordmark: `font-semibold tracking-tight`.
|
||||
- Section headers: `text-[11px] uppercase tracking-wider text-ink-subtle`.
|
||||
- File paths: `font-mono text-ink-muted`.
|
||||
- Meta: `text-xs text-ink-subtle`.
|
||||
|
||||
## Icons
|
||||
|
||||
lucide-react, tree-shakeable. Wrapped via `Icon` component with `size-4 stroke-[1.5] text-current`. Curated set: `Leaf`, `Plus`, `FolderPlus`, `Pencil`, `Trash2`, `ChevronDown`, `ChevronRight`, `X`, `Settings`, `RotateCw`, `GitBranch`, `ArrowUp`, `ArrowDown`, `Files`, `GitGraph`.
|
||||
|
||||
## Layout Skeleton
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ TopBar: leaf · [project ▾] · main.typ · Save · ●LSP ◌PRV │
|
||||
│ TopBar: # typst-leaf · project · main.typ Save LSP PRV @ │
|
||||
├──────────┬───────────────────────────────────────────────────────────┤
|
||||
│ Sidebar │ │
|
||||
│ Files │ │
|
||||
│ ▣ main │ Monaco Editor │
|
||||
│ ─────── │ Monaco Editor │
|
||||
│ ▣ main │ (typst-leaf-light theme) │
|
||||
│ ▣ lib/ │ │
|
||||
│ Git │ │
|
||||
│ ⚙ set │ │
|
||||
│ ├─────────────────────────────────────┬─────────────────────┤
|
||||
│ Settings├─────────────────────────────────────┬─────────────────────┤
|
||||
│ │ Editor (resizable) │ Preview │
|
||||
│ │ │ (iframe) │
|
||||
│ │ │ │
|
||||
└──────────┴─────────────────────────────────────┴─────────────────────┘
|
||||
│ StatusBar: main · clean · ready · vim:n · 1:23 │
|
||||
│ StatusBar: main · clean · LSP · Preview · vim · saving… │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Component Specs (Tailwind)
|
||||
## Component Specs
|
||||
|
||||
### TopBar
|
||||
```tsx
|
||||
<header class="flex items-center gap-3 border-b border-zinc-200 bg-white px-4 h-11">
|
||||
<span class="font-semibold tracking-tight">typst-leaf</span>
|
||||
<span class="text-zinc-300">·</span>
|
||||
<button class="text-sm hover:bg-stone-100 px-2 py-1 rounded-md">project ▾</button>
|
||||
<span class="ml-2 text-sm font-mono text-zinc-500 truncate">main.typ</span>
|
||||
<span class="ml-auto flex items-center gap-2">
|
||||
<span class="text-xs px-2 py-0.5 rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200">LSP · ready</span>
|
||||
<span class="text-xs px-2 py-0.5 rounded-full bg-stone-100 text-zinc-600 border border-zinc-200">PRV · starting</span>
|
||||
<button class="text-sm bg-emerald-600 hover:bg-emerald-700 text-white rounded-md px-3 py-1">Save</button>
|
||||
<button class="text-sm text-zinc-500 hover:bg-stone-100 px-2 py-1 rounded-md">⚙</button>
|
||||
</span>
|
||||
</header>
|
||||
```
|
||||
- `h-12`, `bg-canvas`, `border-b border-line`.
|
||||
- Left: `Leaf` icon (teal-600, size-5), "typst-leaf" (semibold, tracking-tight), middot divider, project name, `/` divider, file path (`font-mono text-ink-subtle`).
|
||||
- Right: LSP/Preview Chip (hidden when no file), `Button primary` Save, `IconButton` Settings.
|
||||
- When no project: text-centered "Select a project to begin".
|
||||
|
||||
### Sidebar (`PanelSection` + tabs)
|
||||
```tsx
|
||||
<aside class="w-64 border-r border-zinc-200 bg-stone-50 flex flex-col min-h-0">
|
||||
<div class="px-3 py-2 text-[11px] uppercase tracking-wider text-zinc-400">Files</div>
|
||||
<nav class="flex-1 overflow-auto p-1">
|
||||
<button class="w-full text-left text-sm px-2 py-1 rounded-md bg-emerald-50 text-emerald-800">main.typ</button>
|
||||
<button class="w-full text-left text-sm px-2 py-1 rounded-md hover:bg-stone-100">lib/util.typ</button>
|
||||
</nav>
|
||||
<div class="border-t border-zinc-200 p-2 text-xs text-zinc-500">12 uncommitted</div>
|
||||
</aside>
|
||||
```
|
||||
### Sidebar
|
||||
- `w-72`, `bg-canvas`, `border-r border-line`.
|
||||
- ProjectPicker at top with `border-b border-line`.
|
||||
- Three icon+label tabs (Files, Git, Settings): active = teal text + 2px teal bottom border on surface; inactive = ink-muted.
|
||||
- Section headers: `uppercase tracking-wider text-ink-subtle text-[11px]`.
|
||||
|
||||
### StatusBar
|
||||
- `h-7`, `bg-canvas`, `border-t border-line`.
|
||||
- `GitBranch` icon + branch name, clean/dirty, ahead/behind with arrows.
|
||||
- LSP/Preview Chip.
|
||||
- Vim indicator and saving indicator.
|
||||
|
||||
### ResizableSplit
|
||||
```tsx
|
||||
<div class="flex h-full min-h-0">
|
||||
<div class="min-w-[280px]" style={{ width: `${left}%` }}>/* editor */</div>
|
||||
<div
|
||||
role="separator"
|
||||
aria-orientation="vertical"
|
||||
class="w-1 bg-zinc-200 hover:bg-emerald-300 cursor-col-resize"
|
||||
onPointerDown={startDrag}
|
||||
onDoubleClick={reset}
|
||||
/>
|
||||
<div class="flex-1 min-w-[280px]">/* preview */</div>
|
||||
</div>
|
||||
```
|
||||
- `flex h-full min-h-0 w-full flex-1`.
|
||||
- Divider: `w-1 bg-line hover:bg-teal-400`, `cursor-col-resize`, `transition-colors`.
|
||||
- Panels: `min-w-[280px] overflow-hidden`.
|
||||
|
||||
### EmptyState
|
||||
```tsx
|
||||
<div class="flex-1 flex flex-col items-center justify-center text-zinc-400 gap-2">
|
||||
<div class="text-sm">Open a file from the sidebar.</div>
|
||||
<div class="text-xs text-zinc-500">Tip: ⌘P to jump, ⌘S to save.</div>
|
||||
</div>
|
||||
```
|
||||
- Centered with muted `Leaf` icon, message, and optional hint.
|
||||
- `text-ink-subtle`.
|
||||
|
||||
### Toast
|
||||
```tsx
|
||||
<div class="fixed bottom-4 right-4 z-50 flex flex-col gap-2">
|
||||
<div class="bg-white border border-zinc-200 rounded-md shadow-sm px-3 py-2 text-sm">
|
||||
<span class="text-emerald-700 mr-2">●</span>
|
||||
Committed <span class="font-mono">a1b2c3d</span>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
### ToastHost
|
||||
- Fixed bottom-right, `rounded-lg shadow-sm`.
|
||||
- `StatusDot` + message + lucide `X` close.
|
||||
- Kinds: teal (success), rose (error), neutral (info).
|
||||
|
||||
### Status chips
|
||||
```tsx
|
||||
<span class="text-[11px] px-2 py-0.5 rounded-full border">
|
||||
<span class="inline-block size-1.5 rounded-full bg-emerald-500 mr-1" />
|
||||
LSP · ready
|
||||
</span>
|
||||
```
|
||||
### Preview
|
||||
- `bg-surface`, toolbar with "Preview" label, status Chip, `RotateCw` reload button.
|
||||
- Content: full-height iframe or centered placeholder text.
|
||||
|
||||
### Editor
|
||||
- `h-full flex flex-col min-h-0`. Toolbar: mono file path, Vim toggle, vim status.
|
||||
- Monaco with custom `typst-leaf-light` theme (warm near-white background, restrained rainbow-free syntax palette).
|
||||
|
||||
### FileTree
|
||||
- Nested tree with `hover:bg-surface-hover` rows, active file `bg-teal-50 text-teal-700`.
|
||||
- Inline rename/create inputs with `border-teal-400`.
|
||||
- Hover actions with lucide icons (Plus, FolderPlus, Pencil, Trash2).
|
||||
- Dirty files marked with amber dot.
|
||||
|
||||
### GitPanel
|
||||
- Section header with branch name, ahead/behind arrows.
|
||||
- Changed files with lucide status icons (Plus/Pencil/X/HelpCircle).
|
||||
- Button primary Commit, Button secondary Push/Pull with ArrowUp/ArrowDown icons.
|
||||
- Collapsible Log with chevron.
|
||||
|
||||
### SettingsPanel
|
||||
- "Git Identity" section.
|
||||
- TextInputs for name/email, Button primary Save, Button secondary Clear.
|
||||
|
||||
## States
|
||||
|
||||
- **No project:** large empty state in main area; sidebar shows "No project yet."
|
||||
- **Connecting:** preview shows "starting…" with a quiet spinner (CSS, not library).
|
||||
- **External preview source:** toast "Source outside project — ignored."
|
||||
- **Save error:** rose-600 message in toast + small dot in TopBar file path.
|
||||
- **Git dirty:** emerald-50 row tint + small dot next to filename in tree.
|
||||
- **No project:** large empty state in main area; sidebar shows project list.
|
||||
- **Connecting:** amber pulse dot on LSP/Preview chips.
|
||||
- **Error:** rose-600 text in error banner and toasts.
|
||||
- **Save error:** rose toast + dirty marker on file name.
|
||||
- **Git dirty:** amber dot on file in tree.
|
||||
|
||||
## Motion
|
||||
|
||||
- Hover: `transition-colors duration-100`.
|
||||
- Splitter drag: no transition.
|
||||
- Toast in/out: 150ms opacity, no spring.
|
||||
- Hover/focus: `transition-colors duration-150`.
|
||||
- Button press: `active:scale-[0.98]` on primary buttons.
|
||||
- Toast: none (instant show/hide).
|
||||
- Connecting dots: `animate-pulse`.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Visible focus rings: `focus-visible:ring-2 focus-visible:ring-emerald-300 focus-visible:ring-offset-1`.
|
||||
- All interactive controls reachable by keyboard.
|
||||
- Status communicated by text + color, never color alone.
|
||||
- Visible focus rings: `focus-visible:ring-2 focus-visible:ring-teal-100 focus-visible:ring-offset-2`.
|
||||
- All interactive controls keyboard-reachable.
|
||||
- State communicated by text + color, never color alone.
|
||||
|
||||
## Don'ts
|
||||
|
||||
- No gradients on chrome.
|
||||
- No shadows on whitespace panels.
|
||||
- No animated chrome unless it's a real state change.
|
||||
- No emoji decorations in product UI (only as icons in narrow toolbars).
|
||||
|
||||
## Thumbnail: vibe check
|
||||
|
||||
> Imagine a warm, slightly off-white IDE with a single green accent for "commit ready" and a quiet three-dot status footer. It's the IDE equivalent of a blank page of cream paper, not a glass dashboard.
|
||||
- No shadows on panels (only on toasts).
|
||||
- No animated chrome unless real state change.
|
||||
- No emoji in product UI — icons only from lucide.
|
||||
- No dark mode.
|
||||
|
||||
Reference in New Issue
Block a user