## What this does Consolidates the frontend's colour/theme system into a small, well-defined token layer and reworks the theme picker. The goal was a minimal, scalable set of semantic tokens that the editor **and** the Processor/portal (and Storybook) all share, plus a theme model that's easy to reason about. ## Token architecture (`core/theme/`) A four-file layer, imported once via `index.css`: | File | Role | |---|---| | `primitives.css` | The raw palette — the **only** place literal colours live (neutral ramps `--p-gray-*`/`--p-zinc-*` + status hues). | | `colors.css` | ~21 semantic `--c-*` tokens (surfaces, text, borders, primary, status) mapped from primitives per theme. **Reference these.** | | `compat.css` | Legacy names (`--bg-*`, `--text-*`, `--color-*`) aliased onto `--c-*` via `:root:root` so ~200 existing files keep working. | | `dimensions.css` | All non-colour tokens (spacing, radius, z-index, type, motion) — single source, resolving prior collisions. | A blocking linter (`scripts/lint/theme-lint.mjs`, run in `frontend:lint`) enforces "literals only in `primitives.css`" within `core/theme/`, and has a non-blocking WCAG contrast report. See `core/theme/README.md`. ## Theme model - **Mode** (`light` / `dark` / `system`) and **accent** are independent. Each mode has its own accent (`lightPrimary` / `darkPrimary`). - The editor is always `data-app-theme="custom"`; `ThemeProvider` injects the accent as `--user-primary` and sets `data-accent`. - **Two accent states:** - A **colour** (preset or custom hex) → tints every surface that hue (whole-app theming). - The **`default`** sentinel → neutral surfaces (white/grey light, zinc black/grey dark) with blue buttons, no tint. (`data-accent="default"` opts surfaces out of the tint.) - Accent contrast guardrails (`utils/customPrimary.ts`): lightness clamps so an accent can't collapse into the base, a contrast-picked on-primary foreground, and an accent-as-foreground variant so accent text is never dark-on-dark. ## Theme picker (Settings → General) - 3×5 grid: a distinct **Default** icon chip (not a colour) + 14 curated accents, in a dropdown per mode. - **Custom** colour via the shared `ColorInput`, with a live gamut clamp (`clampValue`) that refuses white/grey/black — the picker handle sticks at the boundary and preserves the working hue at achromatic extremes. - "Restore theme to default" resets both modes. ## Other - Dark mode is a true neutral zinc (no navy "midnight" tint); the Mantine dark ramp and Tailwind dark channels were neutralised to match. - Pre-paint inline script in `index.html` applies theme + accent before first paint (no FOUC); portal and editor now share the same `preferences.theme` source of truth. - High-visibility surfaces migrated to tokens (FAB, landing upload buttons, portal hero banners); scattered per-component colour swaps were intentionally **left for a follow-up** to keep this PR focused. ## Testing - `task frontend:check:all` (typecheck all variants + eslint + prettier + colour-lint) green. - Verified light/dark, default vs tinted accents, and the custom clamp via computed styles in the dev preview. > Note: the `prerender-og` build step failing in the e2e/deploy jobs is unrelated to this diff — it's in `vite.config.ts` (untouched here) and builds cleanly locally.
Stirling PDF - The Open-Source PDF Platform
Stirling PDF is a powerful, open-source PDF editing platform. Run it as a personal desktop app, in the browser, or deploy it on your own servers with a private API. Edit, sign, redact, convert, and automate PDFs without sending documents to external services.
Key Capabilities
- Everywhere you work - Desktop client, browser UI, and self-hosted server with a private API.
- 50+ PDF tools - Edit, merge, split, sign, redact, convert, OCR, compress, and more.
- Automation & workflows - No-code pipelines direct in UI with APIs to process millions of PDFs.
- Enterprise‑grade - SSO, auditing, and flexible on‑prem deployments.
- Developer platform - REST APIs available for nearly all tools to integrate into your existing systems.
- Global UI - Interface available in 40+ languages.
For a full feature list, see the docs: https://docs.stirlingpdf.com
Quick Start
docker run -p 8080:8080 docker.stirlingpdf.com/stirlingtools/stirling-pdf
Then open: http://localhost:8080
For full installation options (including desktop and Kubernetes), see our Documentation Guide.
Resources
Support
- Community: Discord
- Bug Reports: GitHub Issues
Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
This project uses Task as a unified command runner for all build, dev, and test commands. Run task dev to get started running the editor, run task to see the most common commands, or see the Developer Guide for full details.
For adding translations, see the Translation Guide.
License
Stirling PDF is open-core. See LICENSE for details.

