Skip to content

Commit 793e6ca

Browse files
committed
Refine Notes Web design system
1 parent 9a9274a commit 793e6ca

10 files changed

Lines changed: 620 additions & 6 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ bin/
22
.DS_Store
33
*.log
44
/tmp/
5+
/docs/plans

.impeccable/design.json

Lines changed: 296 additions & 0 deletions
Large diffs are not rendered by default.

DESIGN.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
---
2+
name: Notes Web
3+
description: A calm, keyboard-first web workbench for reading, retrieving, and maintaining a Markdown vault.
4+
colors:
5+
parchment: "#f7f5ef"
6+
desk-paper: "#fffdfa"
7+
fresh-ink: "#27231d"
8+
faded-marginalia: "#786f63"
9+
pressed-fold: "#e2dccc"
10+
violet-thread: "#6b5cff"
11+
deep-violet-thread: "#3d32c2"
12+
washed-violet: "#efecff"
13+
workbench-ash-inline: "#eee9dc"
14+
workbench-ash-block: "#f1eee6"
15+
red-wax: "#b54747"
16+
amber-note: "#b76e00"
17+
moss-mark: "#2b8a3e"
18+
night-vault: "#11131a"
19+
night-paper: "#181b24"
20+
night-ink: "#ebe7df"
21+
sepia-parchment: "#f4ecd8"
22+
sepia-paper: "#fff8e8"
23+
sepia-ink: "#33291c"
24+
typography:
25+
display:
26+
fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
27+
fontSize: "40px"
28+
fontWeight: 800
29+
lineHeight: 1.2
30+
letterSpacing: "-0.04em"
31+
headline:
32+
fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
33+
fontSize: "22px"
34+
fontWeight: 800
35+
lineHeight: 1.2
36+
letterSpacing: "-0.02em"
37+
title:
38+
fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
39+
fontSize: "18px"
40+
fontWeight: 800
41+
lineHeight: 1.3
42+
body:
43+
fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
44+
fontSize: "17px"
45+
fontWeight: 400
46+
lineHeight: 1.74
47+
label:
48+
fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
49+
fontSize: "12px"
50+
fontWeight: 800
51+
lineHeight: 1.2
52+
letterSpacing: "0.08em"
53+
code:
54+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace"
55+
fontSize: "0.95em"
56+
fontWeight: 400
57+
lineHeight: 1.35
58+
rounded:
59+
sm: "8px"
60+
md: "12px"
61+
lg: "18px"
62+
xl: "22px"
63+
modal: "24px"
64+
pill: "999px"
65+
spacing:
66+
space-1: "4px"
67+
space-2: "8px"
68+
space-3: "12px"
69+
space-4: "16px"
70+
space-5: "24px"
71+
space-6: "32px"
72+
sidebar: "22px"
73+
main-x: "56px"
74+
main-y: "40px"
75+
components:
76+
button-default:
77+
backgroundColor: "{colors.desk-paper}"
78+
textColor: "{colors.fresh-ink}"
79+
rounded: "{rounded.md}"
80+
padding: "8px 12px"
81+
button-primary:
82+
backgroundColor: "{colors.violet-thread}"
83+
textColor: "{colors.desk-paper}"
84+
rounded: "{rounded.md}"
85+
padding: "8px 12px"
86+
chip:
87+
backgroundColor: "{colors.washed-violet}"
88+
textColor: "{colors.deep-violet-thread}"
89+
rounded: "{rounded.pill}"
90+
padding: "4px 10px"
91+
card:
92+
backgroundColor: "{colors.desk-paper}"
93+
textColor: "{colors.fresh-ink}"
94+
rounded: "{rounded.lg}"
95+
padding: "20px"
96+
search-input:
97+
backgroundColor: "{colors.desk-paper}"
98+
textColor: "{colors.fresh-ink}"
99+
rounded: "10px"
100+
padding: "10px 12px"
101+
palette-panel:
102+
backgroundColor: "{colors.desk-paper}"
103+
textColor: "{colors.fresh-ink}"
104+
rounded: "{rounded.xl}"
105+
padding: "0"
106+
---
107+
108+
# Design System: Notes Web
109+
110+
## 1. Overview
111+
112+
**Creative North Star: "The Operative Notebook"**
113+
114+
Notes Web should feel like an operative notebook for a private vault: calm enough for long reading, precise enough for maintenance, and fast enough that retrieval feels almost physical. The interface is not neutral. It is personal-first, open source, keyboard-native, and intentionally opinionated.
115+
116+
The system uses warm paper tones, fresh ink contrast, and a single violet thread for active states, commands, focus, and selected navigation. Density is allowed because the product is a real tool, but every dense surface must remain quiet, legible, and purposeful.
117+
118+
The visual system rejects cold documentation wikis, heavy app chrome, generic SaaS dashboards, Obsidian mimicry, and bland neutrality. It should never look like a landing page trapped inside a knowledge tool.
119+
120+
**Key Characteristics:**
121+
- Warm, paper-led product UI with strong reading comfort.
122+
- Restrained accent use: violet marks action, focus, selection, and command flow.
123+
- Tonal layering before shadow.
124+
- Keyboard-first affordances with visible focus and predictable states.
125+
- Personal craft made reusable through explicit tokens and rules.
126+
127+
## 2. Colors
128+
129+
The palette is a restrained paper-and-ink system with one violet accent that acts as a thread through navigation, commands, chips, and active states.
130+
131+
### Primary
132+
- **Violet Thread** (`--accent`): The only primary accent. Use it for primary actions, active navigation, selected filters, command affordances, and focus emphasis.
133+
- **Deep Violet Thread** (`--accent-ink`): Text and link accent on light surfaces. Use when the accent needs readable ink rather than filled color.
134+
- **Washed Violet** (`--soft`): The quiet selected and hover field. Use for active tree branches, command palette rows, tags, and calm emphasis.
135+
136+
### Secondary
137+
- **Red Wax** (`--danger`): Errors, overdue tasks, unresolved links, and destructive states. It should feel decisive, not theatrical.
138+
- **Amber Note** (`--warning`): Warnings and due-soon attention. Use sparingly so it reads as signal, not decoration.
139+
- **Moss Mark** (`--success`): Completion and resolved states. It should feel durable and quiet.
140+
141+
### Neutral
142+
- **Parchment** (`--bg`): The ambient page background. It gives the app warmth without making the vault feel antique.
143+
- **Desk Paper** (`--surface`): The main content and panel surface. It should stay close to the background so layers feel calm.
144+
- **Fresh Ink** (`--ink`): Main text. It is warm black, never pure black.
145+
- **Faded Marginalia** (`--muted`): Secondary metadata, paths, labels, timestamps, and helper text.
146+
- **Pressed Fold** (`--line`): Borders and dividers. It should separate without cutting the page into boxes.
147+
- **Workbench Ash** (`--code`, `--pre`): Inline and block code backgrounds. Technical content should feel matte and integrated.
148+
149+
### Named Rules
150+
151+
**The Violet Thread Rule.** Violet is for orientation and action only: current location, command flow, focus, selected filters, and primary actions. If violet appears as ornament, remove it.
152+
153+
**The Warm Neutral Rule.** Use tinted neutrals for every background and text layer. Pure black and pure white are forbidden in new UI work.
154+
155+
**The Semantic Restraint Rule.** Red Wax, Amber Note, and Moss Mark are state colors, not category colors. Do not decorate dashboards with them.
156+
157+
## 3. Typography
158+
159+
**Display Font:** System sans (`ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif`)
160+
**Body Font:** System sans (`ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif`)
161+
**Label/Mono Font:** System mono (`ui-monospace, SFMono-Regular, Menlo, monospace`)
162+
163+
**Character:** Native, fast, and unpretentious. Typography should feel like a tool that belongs on the user's system, with enough weight contrast to guide scanning and enough line-height to support long reading.
164+
165+
### Hierarchy
166+
- **Display** (800, 40px, 1.2, -0.04em): Page titles, note titles, folder titles, and task dashboard heroes.
167+
- **Headline** (800, 22px, 1.2, -0.02em): Brand mark and major section anchors.
168+
- **Title** (800, 18px, 1.3): Search results, card headings, note cards, and dashboard modules.
169+
- **Body** (400, 17px, 1.74): Note reading content. Keep prose readable and avoid squeezing long paragraphs into narrow widget columns.
170+
- **Label** (800, 12px, 0.08em): Eyebrows, toolbar labels, calendar weekdays, compact section labels, and structured metadata.
171+
- **Code** (400, 0.95em, 1.35): Inline code, fenced blocks, task IDs, and copyable technical fragments.
172+
173+
### Named Rules
174+
175+
**The Native Tool Rule.** Use the system sans for product UI. Do not introduce display fonts for labels, buttons, tables, task rows, or navigation.
176+
177+
**The Reading Breath Rule.** Note content earns extra line-height. Dense product modules can compress, but prose should stay comfortable.
178+
179+
## 4. Elevation
180+
181+
Depth comes from flat tonal layering: background, surface, borders, and raised overlays. Shadows are rare, short, and matte. They belong to temporary layers above the document: command palette, dialogs, menus, popovers, and hover feedback on actionable controls.
182+
183+
The existing system includes modal and card shadows, but the design direction is still flat by default. Blur is functional only. It may isolate a command mode, never create decorative glass.
184+
185+
### Shadow Vocabulary
186+
- **Surface Hairline** (`0 1px 0 rgba(39, 35, 29, 0.08)`): A nearly flat separation for buttons and quiet cards.
187+
- **Soft Card** (`0 2px 10px rgba(39, 35, 29, 0.08)`): Low elevation for cards and task groups.
188+
- **Command Lift** (`0 24px 80px rgba(17, 19, 26, 0.34)`): Temporary elevation for command palette and settings dialogs.
189+
190+
### Named Rules
191+
192+
**The Flat Until Invoked Rule.** Resting surfaces are flat. Shadows appear when an element is actionable, hovered, opened, or temporarily above the vault.
193+
194+
**The No Glass Rule.** Backdrop blur may support a modal mode, but glassmorphism is forbidden as a decorative default.
195+
196+
## 5. Components
197+
198+
Every component should behave like a calm tool: obvious at keyboard speed, useful in one glance, simple in shape, and just distinctive enough to belong to Notes Web.
199+
200+
### Buttons
201+
- **Shape:** Gently rounded rectangle (12px radius).
202+
- **Default:** Desk Paper background, Pressed Fold border, Fresh Ink text, compact padding (8px 12px).
203+
- **Primary:** Violet Thread fill with Desk Paper text. Reserve for the main action in a local context.
204+
- **Ghost:** Transparent background with the same shape and focus vocabulary as default buttons.
205+
- **Hover / Focus:** Move by transform only, never layout. Focus must be visible with a violet ring or equivalent high-contrast treatment.
206+
207+
### Chips
208+
- **Style:** Washed Violet background, Deep Violet Thread text, pill radius, compact padding (4px 10px).
209+
- **State:** Selected chips may use Violet Thread fill. Unselected chips should stay quiet.
210+
- **Use:** Tags, filters, task metadata, folder sort controls, and compact status indicators.
211+
212+
### Cards / Containers
213+
- **Corner Style:** Soft but not bubbly (18px radius).
214+
- **Background:** Desk Paper for normal surfaces, never raw white in new work.
215+
- **Shadow Strategy:** Flat by default, Soft Card only when a module needs separation from the paper.
216+
- **Border:** Pressed Fold hairline. Avoid stacking nested cards.
217+
- **Internal Padding:** 16px to 20px for cards, 14px for compact note and result rows, 18px for dense task sections.
218+
219+
### Inputs / Fields
220+
- **Style:** Desk Paper background, Pressed Fold border, 10px to 12px radius, system sans text.
221+
- **Focus:** Violet Thread border with a soft focus ring. Do not rely on color alone if the field has validation state.
222+
- **Search:** Search fields can grow larger (18px to 20px) because retrieval is a primary task.
223+
224+
### Navigation
225+
- **Sidebar:** Sticky desktop rail with warm paper surface, Pressed Fold divider, compact tree spacing, and a calm active branch using Washed Violet.
226+
- **Tree:** Folders and notes should scan quickly. Active branch treatment uses background plus weight, not color alone.
227+
- **Mobile:** Sidebar becomes a drawer overlay. Keep the same hierarchy and focus order.
228+
- **Command Palette:** Treat as the fastest navigation surface. The panel is raised, centered, large enough for typing, and selected rows use Washed Violet.
229+
230+
### Task Rows
231+
- **Style:** Dense grid with checkbox, priority badge, title, metadata, and menu access.
232+
- **State:** Completed tasks reduce opacity and strike the title. Priority badges may use semantic fills, but only when the priority is meaningful.
233+
- **Interaction:** Row menus stay quiet until hover or focus. Keyboard access must match mouse affordances.
234+
235+
### Code Blocks
236+
- **Style:** Workbench Ash backgrounds with matte contrast, 12px radius, and a copy button in the top right.
237+
- **Inline Code:** Small Workbench Ash chip with 5px radius.
238+
- **Use:** Code should feel integrated with prose, not like a separate developer console.
239+
240+
### Callouts
241+
- **Current State:** Legacy callouts use a colored side stripe.
242+
- **Future Direction:** New callouts should use tinted panels, full borders, icons, or title weight instead of side-stripe accents.
243+
- **Reason:** Side stripes create a generic documentation look and conflict with the calmer paper system.
244+
245+
## 6. Do's and Don'ts
246+
247+
### Do:
248+
- **Do** keep Notes Web product-first: retrieval, reading, tasks, and diagnostics beat decorative composition.
249+
- **Do** use Violet Thread only for orientation, action, focus, and selected states.
250+
- **Do** preserve Calm Density: information-rich modules are allowed when spacing, hierarchy, and labels make them scannable.
251+
- **Do** design keyboard-first states for buttons, tree items, command palette rows, task menus, and filters.
252+
- **Do** make diagnostics feel like maintenance, not alarm: broken links and orphans should be clear without turning the page into an error wall.
253+
- **Do** keep personal-first choices explicit so open source users can adapt them without erasing the point of view.
254+
255+
### Don't:
256+
- **Don't** make Notes Web look like a cold documentation wiki that makes personal notes feel impersonal.
257+
- **Don't** add heavy app chrome, decorative motion, unnecessary blur, or framework-like visual bulk.
258+
- **Don't** use generic SaaS dashboard patterns: repeated metric cards, hero-stat layouts, gradient accents, or ornamental panels.
259+
- **Don't** copy Obsidian's desktop app. Serve the web reading and retrieval context instead.
260+
- **Don't** neutralize the product until it loses its personal-first point of view.
261+
- **Don't** add colored side-stripe borders to new callouts, cards, list items, or alerts. Use full borders, tinted panels, icons, or title treatment.
262+
- **Don't** use gradient text, glassmorphism as decoration, pure black, or pure white in new UI work.

PRODUCT.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Product
2+
3+
## Register
4+
5+
product
6+
7+
## Users
8+
9+
Notes Web is built first for Arkan's own Markdown and Obsidian-style vault workflows. The primary user is a technical, keyboard-oriented reader who wants fast access to private notes, tasks, links, tags, and vault diagnostics from a local or self-hosted web surface.
10+
11+
The project is open source, so the secondary audience is people with similar self-hosted knowledge-management needs. They should be able to understand the tool's philosophy, adapt it to their vault, and benefit from its defaults without the product becoming generic or diluted.
12+
13+
## Product Purpose
14+
15+
Notes Web provides a small, self-contained web interface for browsing, reading, searching, maintaining, and acting on a Markdown vault. It exists to make retrieval fast, reading comfortable, task review actionable, and vault maintenance visible without requiring a heavy app stack.
16+
17+
Success means the interface feels like a trusted extension of the vault: quick to open, keyboard-native, calm enough for long reading, dense enough for real work, and explicit enough that other self-hosted users can adapt its opinionated choices.
18+
19+
## Brand Personality
20+
21+
Calm, fast, opinionated, and artisanal. The product should feel like a carefully made personal tool rather than a SaaS dashboard, a cold wiki, or a framework-heavy web app.
22+
23+
The voice is direct, technical, and humble. It should explain states and actions clearly, avoid marketing language, and let the user's notes remain the center of attention.
24+
25+
## Anti-references
26+
27+
- A cold documentation wiki that makes personal notes feel impersonal.
28+
- A heavy app with framework-like latency, decorative motion, or unnecessary chrome.
29+
- A generic SaaS dashboard with repeated cards, hero metrics, gradients, and marketing polish.
30+
- An Obsidian clone that copies the desktop app instead of serving the web reading and retrieval context.
31+
- A tool that becomes so neutral that it loses the personal-first point of view that makes it useful.
32+
33+
## Design Principles
34+
35+
1. **Retrieval before decoration.** Every design choice should make notes, links, tags, search results, and tasks easier to reach.
36+
2. **Calm density.** Surfaces can be information-rich, but they should not feel noisy, anxious, or mechanically crowded.
37+
3. **Keyboard-native flow.** Navigation, search, command palette usage, and task actions should be efficient without a mouse.
38+
4. **Diagnostics as maintenance, not alarm.** Broken links, orphans, and task states should support ongoing vault care without turning the interface into an error wall.
39+
5. **Personal-first, reusable by design.** Decisions start from real personal workflows, then stay explicit and adaptable so other self-hosted users can understand, keep, replace, or extend them.
40+
41+
## Accessibility & Inclusion
42+
43+
Target WCAG AA for contrast, readable states, focus visibility, and navigable structure. The interface should be keyboard-first, with predictable focus order, reliable shortcuts, and command-palette access to core actions.
44+
45+
Interactive states must not rely on color alone. Labels, structure, focus treatment, and state text should carry meaning for users who navigate by keyboard or assistive technology.

internal/app/static/app.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,21 @@ function renderPalette(query) {
8080
if (!results) return;
8181
if (!paletteItems.length) {
8282
paletteMatches = [];
83-
results.innerHTML = paletteLoadError ? '<p class="palette-empty empty-state">Unable to load search results.</p>' : '<p class="palette-empty empty-state">Loading…</p>';
83+
results.setAttribute('aria-busy', String(!paletteLoadError));
84+
results.innerHTML = paletteLoadError ? renderPaletteState('error', 'Unable to load search results.', 'Check the server, then reopen search.') : renderPaletteState('loading', 'Loading…', 'Preparing notes, tags, and favorites.');
8485
return;
8586
}
87+
results.setAttribute('aria-busy', 'false');
8688
const q = (query || '').trim().toLowerCase();
8789
paletteMatches = paletteItems.filter((item) => {
8890
const haystack = [item.title, item.path, item.kind].filter(Boolean).join(' ').toLowerCase();
8991
return !q || haystack.includes(q);
9092
}).slice(0, 30);
9193
if (paletteSelectedIndex >= paletteMatches.length) paletteSelectedIndex = 0;
92-
results.innerHTML = paletteMatches.map((item, index) => '<button type="button" role="option" aria-selected="' + (index === paletteSelectedIndex) + '" class="palette-item' + (index === paletteSelectedIndex ? ' is-selected' : '') + '" data-palette-index="' + index + '"><strong>' + escapeHTML(item.title || item.path || item.url || 'Untitled') + '</strong><small>' + escapeHTML(item.path || item.url || '') + '</small><span class="chip palette-kind">' + escapeHTML(item.kind || 'item') + '</span></button>').join('') || '<p class="palette-empty empty-state">No results.</p>';
94+
results.innerHTML = paletteMatches.map((item, index) => '<button type="button" role="option" aria-selected="' + (index === paletteSelectedIndex) + '" class="palette-item' + (index === paletteSelectedIndex ? ' is-selected' : '') + '" data-palette-index="' + index + '"><strong>' + escapeHTML(item.title || item.path || item.url || 'Untitled') + '</strong><small>' + escapeHTML(item.path || item.url || '') + '</small><span class="chip palette-kind">' + escapeHTML(item.kind || 'item') + '</span></button>').join('') || renderPaletteState('empty', 'No results.', 'Try a note title, tag, or favorite.');
95+
}
96+
function renderPaletteState(kind, title, detail) {
97+
return '<div class="palette-empty empty-state palette-state palette-state-' + kind + '" role="status"><strong>' + escapeHTML(title) + '</strong><small>' + escapeHTML(detail) + '</small><span class="palette-state-rule" aria-hidden="true"></span></div>';
9398
}
9499
function openPaletteMatch(index) {
95100
const item = paletteMatches[index];

0 commit comments

Comments
 (0)