diff --git a/README.md b/README.md index b4aabe37..d0f5eafe 100644 --- a/README.md +++ b/README.md @@ -383,7 +383,6 @@ Open Notebook is MIT licensed. See the [LICENSE](LICENSE) file for details. [license-url]: https://github.com/lfnovo/open-notebook/blob/master/LICENSE.txt [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 [linkedin-url]: https://linkedin.com/in/lfnovo -[product-screenshot]: images/screenshot.png [Next.js]: https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=next.js&logoColor=white [Next-url]: https://nextjs.org/ [React]: https://img.shields.io/badge/React-61DAFB?style=for-the-badge&logo=react&logoColor=black diff --git a/docs/7-DEVELOPMENT/decisions/ADR-011-design-token-system.md b/docs/7-DEVELOPMENT/decisions/ADR-011-design-token-system.md new file mode 100644 index 00000000..458a85d2 --- /dev/null +++ b/docs/7-DEVELOPMENT/decisions/ADR-011-design-token-system.md @@ -0,0 +1,33 @@ +# ADR-011: Visual identity is a token contract in globals.css, reviewed through /dev/design + +- **Status**: Accepted +- **Date**: 2026-07 +- **Related**: Discussion #1202 (community co-design), #1218 (foundation), #1220 (screen reskin), [frontend rules](../../7-DEVELOPMENT/frontend.md) + +## Context + +The 2026 redesign ("Quiet Green", co-designed with the community in Discussion #1202) replaced the stock shadcn theme with a product-specific visual identity. Before it, colors were scattered across components as raw Tailwind palette classes (`text-red-600`, `bg-amber-50`), each with hand-written `dark:` twins — visually inconsistent, semantically meaningless, and expensive to change. The redesign also pre-designed Stage-2 UX (citations, per-source context states, content-type identity) that needs colors with *meaning*, not decoration. + +## Decision + +**All visual identity lives as a layered custom-property token system in `frontend/src/app/globals.css`. Components consume semantic tokens only — raw palette classes are banned. `/dev/design` is the living reference.** + +- **Layers**: raw palette (fern, sage, gold, teal, plum, mauve, slate, violet, clay) → semantic slots (surfaces, ink ramp, hairlines, action, danger, warn) → product vocabularies: content-type hues (`--type-*`), evidence/citation classes (`--cite-*`), context states (`--ctx-*`). The product vocabularies are canonized now even where nothing consumes them yet, so Stage-2 features and community PRs share one contract. +- **The laws** (enforced in review): fern acts · teal speaks (the AI/system voice) · red destroys, and only destroys · warn is clay, never an action hue · color never washes a reading surface · hairlines separate, popovers own the only real shadow · geometry is squared (4–6px) · mono is for data, not prose. +- **Dark mode** overrides only the raw layers on `.dark`; every alias re-resolves via `var()`. This works **only** because the `dark` class sits on the document root (`theme-store` behavior). CSS custom properties resolve where they are *declared*, so a nested `.dark` wrapper inherits already-resolved light values — never theme a subtree. +- **Values are hex**, kept 1:1 with the validated design spec (a pixel-diff re-application test against the design mockups reached 0.000% visible difference). Converting to oklch would reintroduce drift for zero benefit. +- **`/dev/design`** (dev-only route, 404 in production) renders every token and primitive in both themes. It is the acceptance reference for visual PRs and the regression detector: screen-level PRs must leave it byte-identical. + +## Alternatives considered + +- **Keep per-component palette classes** — rejected: the status quo that produced the inconsistency; every rebrand becomes a 38-file sweep (we did exactly one and never want another). +- **A theming library / CSS-in-JS tokens** — rejected: Tailwind v4 `@theme` already bridges custom properties to utilities; adding a runtime dependency contradicts the lean posture. +- **Full design-system tooling (Storybook, Figma library, published npm package)** — rejected as over-engineering for a single-app project; `/dev/design` gives the same review value at near-zero maintenance cost. +- **oklch values (shadcn convention)** — rejected: fidelity to the pixel-diff-validated spec wins over convention; the file documents this so nobody "fixes" it later. + +## Consequences + +- Contributors must use token utilities (`text-destructive`, `bg-warn-tint`, `text-type-video`…) — see the styling rule in [frontend/AGENTS.md](../../../frontend/AGENTS.md). A PR reintroducing `text-red-600` is a review reject. +- `slate` and `violet` are exposed to Tailwind as `slate-hue`/`violet-hue` to avoid colliding with Tailwind's built-in palettes. +- Rebrands and theme tweaks are token edits, verified in `/dev/design`, instead of app-wide sweeps. +- Stage-2 features consume `--cite-*`/`--ctx-*` without new color decisions; the vocabulary already exists. diff --git a/docs/7-DEVELOPMENT/decisions/README.md b/docs/7-DEVELOPMENT/decisions/README.md index a8b42628..8c10134c 100644 --- a/docs/7-DEVELOPMENT/decisions/README.md +++ b/docs/7-DEVELOPMENT/decisions/README.md @@ -50,5 +50,7 @@ What this makes easier, what it makes harder, what to watch. (bullets) | [ADR-006](ADR-006-migration-granularity.md) | Migration granularity follows merge granularity, not release granularity | Accepted | | [ADR-007](ADR-007-optin-runtimes.md) | Heavy extraction runtimes (Docling, Crawl4AI local) are opt-in, installed at startup | Accepted | | [ADR-008](ADR-008-notebook-scoped-search.md) | Notebook scope is an optional filter on the existing search functions | Accepted | +| [ADR-009](ADR-009-pbkdf2-credential-encryption.md) | PBKDF2 credential key derivation with versioned ciphertext | Accepted | +| [ADR-011](ADR-011-design-token-system.md) | Visual identity is a token contract in globals.css, reviewed through /dev/design | Accepted | | [PDR-001](PDR-001-single-user-first.md) | Single-user first; don't preclude multi-user | Accepted | | [PDR-002](PDR-002-provider-agnostic-core.md) | Provider-agnostic core by default | Accepted | diff --git a/docs/assets/asset_list.png b/docs/assets/asset_list.png index 87327a80..f9d14228 100644 Binary files a/docs/assets/asset_list.png and b/docs/assets/asset_list.png differ diff --git a/docs/assets/hero.svg b/docs/assets/hero.svg index 87013478..e804f283 100644 --- a/docs/assets/hero.svg +++ b/docs/assets/hero.svg @@ -1,60 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index 483af19d..dd6e5a11 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -12,6 +12,7 @@ Normative rules for working on the Next.js frontend. Architecture and flow walkt ## Hard rules - **i18n is mandatory**: every UI string goes through `t('section.key')` and the key must exist in **all locales** under `src/lib/locales/` (currently 14; en-US is the reference). Missing keys fall back to en-US silently — keep locales in sync. Each non-en-US locale ends with `satisfies TranslationShape` (type derived from en-US), so a missing/extra key fails `tsc`; the parity test (`src/lib/locales/index.test.ts`) checks the same at runtime. +- **Styling goes through design tokens** ([ADR-011](../docs/7-DEVELOPMENT/decisions/ADR-011-design-token-system.md)): use semantic utilities (`text-destructive`, `bg-warn-tint`, `text-teal`, `text-type-video`, …) defined in `src/app/globals.css` — never raw Tailwind palette classes (`text-red-600`, `bg-amber-50`) and never hand-written `dark:` color twins (tokens re-resolve per theme). The laws: fern acts, teal is the AI voice, red only destroys, warn is clay, color never washes a reading surface. `/dev/design` (dev-only route) renders every token and primitive in both themes — it's the visual reference, and screen-level PRs must not change it. - All requests go through `apiClient` (`src/lib/api/client.ts`); never create a second axios instance. Auth token is auto-added from localStorage key `auth-storage`. - Data fetching uses TanStack Query hooks in `src/lib/hooks/` with `QUERY_KEYS`; mutations invalidate caches and show toasts (sonner). Follow the existing hook shape. - FormData requests: nested objects/arrays must be `JSON.stringify`-ed before appending; the interceptor strips Content-Type so the browser sets the multipart boundary — don't re-add it.