54639d466a
The SvelteKit app under /frontend has fully superseded the legacy
vanilla-JS/CSS frontend in /static, which was only ever served by a
debug `cargo run` / `PROFILE=dev` and never shipped to production.
Remove it together with the whole subsystem that existed only to
support it (~54k lines).
Frontend & assets:
- Delete /static (js/, css/, *.html, sw.js, basemaps/, locales symlink).
- Relocate the brand/PWA assets (logo/, favicon.ico, manifest.webmanifest)
to frontend/static/ so they ship with the SPA. This also fixes the
favicon, which app.html referenced but was missing from the prod bundle.
- Migrate the Nextcloud login-flow redirects from /nextcloud-error.html
to the SvelteKit /nextcloud/error route.
Web layer:
- Simplify resolve_static_path: drop the PROFILE=dev branch; always prefer
the Vite static-dist/ build, fall back to the configured path.
- Resolve i18n locales from the served SPA dir with a frontend/static
fallback so `just dev` works without a prior build.
Build:
- Prune build.rs from 1262 to ~70 lines (git metadata only); the Rust asset
pipeline and the OXICLOUD_RUST_ASSETS rollback flag are gone.
- Drop the now-unused build-dependencies (oxc_*, lightningcss).
- Remove the COPY static lines from the Dockerfile (cacher + builder).
Tooling & docs:
- Delete biome.json, jsconfig.json, tools/check-*.py, identifier.sh.
- Remove the legacy front-* justfile recipes; repoint the design-system
scripts (locales, dead-tokens, brand-drift, token-docs) at the frontend,
and drop check-contrast/check-headings (coupled to the old token
taxonomy / multi-page HTML).
- Repoint docs/DESIGN-SYSTEM.md links; remove 5 superseded docs/plan/*.
Backend dead code:
- Remove the dead `folder_repo` field from FileBlobWriteRepository.
- Remove the deprecated GET /api/folders/{id}/listing endpoint
(superseded by /resources).
Verified: cargo clippy (all-features/all-targets) clean, cargo test
--workspace 448 passed, cargo fmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
106 lines
5.6 KiB
Markdown
106 lines
5.6 KiB
Markdown
# OxiCloud Design System
|
||
|
||
The single source of truth for how the frontend looks, behaves, and stays
|
||
accessible. Pairs with [TOKENS.md](TOKENS.md) (auto-generated token reference)
|
||
and [UIUX-ROADMAP.md](UIUX-ROADMAP.md) (the work plan).
|
||
|
||
---
|
||
|
||
## 1. Design tokens
|
||
|
||
Everything visual routes through a token in
|
||
[`static/css/base/variables.css`](../frontend/src/lib/styles/base/variables.css). Six scales,
|
||
plus a curated color system. **Never hand-write a raw px/hex for these** — consume
|
||
a token so the next change happens in one place.
|
||
|
||
| Axis | Tokens | Notes |
|
||
| --- | --- | --- |
|
||
| Spacing | `--space-0 … --space-24` | 4px grid (+ half-steps 2/6/10/14). |
|
||
| Radius | `--radius-xs … --radius-4xl`, `--radius-full` | `--radius` aliases `--radius-2xl`. |
|
||
| Type | `--text-2xs … --text-6xl`, `--leading-*`, `--weight-*`, `--tracking-*` | rem-based (honors zoom). `.heading-page/-section/-card` utilities decouple role from element. |
|
||
| Z-index | `--z-base … --z-max` | Semantic layers with gaps; no magic numbers. |
|
||
| Motion | `--motion-fast/base/moderate/slow`, `--ease-standard/-emphasized/...` | `--ease-standard` is the decelerate default for entrances. |
|
||
| Elevation | `--shadow-xs … --shadow-2xl` | Composed recipes layered on the `--color-shadow-*` alphas. |
|
||
|
||
**Color.** One brand accent (orange `#ff5e3a`), one neutral slate ramp, and five
|
||
semantics (success/warning/danger/info + accent), each unified to a single hue.
|
||
Text tiers collapse to a handful that all clear **WCAG AA (4.5:1)** — verified by
|
||
[`scripts/check-contrast.mjs`](../scripts/check-contrast.mjs). Use
|
||
`--color-accent-text` (not `--color-accent`) for accent *text/links*.
|
||
|
||
Governance: **one brand accent, one neutral ramp, five semantics — no new hero
|
||
hues.** Run `node scripts/gen-token-docs.mjs` after editing tokens.
|
||
|
||
---
|
||
|
||
## 2. Accessibility rules (non-negotiable)
|
||
|
||
These are enforced/aided by CI scripts and must hold for every new surface.
|
||
|
||
- **Keyboard.** Everything interactive is reachable and operable by keyboard.
|
||
Use `<button>`/`<a>` (not clickable `<div>`s). Nav exposes `aria-current="page"`.
|
||
- **Focus.** A global `:focus-visible` ring lives in
|
||
[`base/a11y.css`](../frontend/src/lib/styles/base/a11y.css). Never `outline: none` without a
|
||
paired `:focus-visible` style. Mouse focus stays ring-free; keyboard focus never.
|
||
- **Contrast.** Every text/background pair clears 4.5:1 in **light AND dark** —
|
||
`node scripts/check-contrast.mjs` fails the build otherwise.
|
||
- **Landmarks + skip link.** `<nav aria-label>`, `role="main"`/`<main id="main">`,
|
||
a skip link as the first focusable element, and `lang` on `<html>`.
|
||
- **Headings.** Exactly one `h1` per page, no skipped levels —
|
||
`node scripts/check-headings.mjs`.
|
||
- **Motion.** `@media (prefers-reduced-motion: reduce)` neutralizes animation
|
||
globally. Also honor `prefers-contrast` and `forced-colors`.
|
||
- **Dialogs.** `role="dialog"` + `aria-modal` + `aria-labelledby`, focus trapped
|
||
while open, focus restored to the trigger on close (see
|
||
[`modal.js`](../frontend/src/lib/components/Modal.svelte)).
|
||
- **Icon-only buttons.** Always an `aria-label`; decorative icons get
|
||
`aria-hidden="true"`.
|
||
- **Touch targets.** ≥44×44px on phones.
|
||
|
||
---
|
||
|
||
## 3. Brand
|
||
|
||
- **Mark.** The cloud glyph ([`logo-plain.svg`](../frontend/static/logo/logo-plain.svg)).
|
||
Always rendered from the real SVG (never a stock `fa-cloud`). On surfaces it
|
||
sits in an accent-gradient tile via the `.brand-mark` component.
|
||
- **Wordmark.** "OxiCloud", weight 700, tight tracking. The accent-coloured
|
||
"Oxi" lockup is the ownable treatment.
|
||
- **Accent.** Orange→coral `#ff5e3a → #ff2d55` — warm and distinctive in a
|
||
blue-dominated cloud-storage market; nods to Rust oxidation. It is the *only*
|
||
brand/primary hue (blue/indigo/purple were demoted to the single info ramp).
|
||
- **Logo gradient.** One token (`--color-logo-gradient`) everywhere.
|
||
- **Clear-space / min-size.** Keep ≥ the tile's corner-radius of padding around
|
||
the mark; don't render the wordmark below ~16px.
|
||
- **Don't:** recolor the mark, stretch it, place it on a low-contrast background,
|
||
or introduce a second "primary" hue.
|
||
- **Maskable / OG.** [`logo-maskable.svg`](../frontend/static/logo/logo-maskable.svg) (PWA,
|
||
safe-zone) and [`og-image.svg`](../frontend/static/logo/og-image.svg) (social). Export
|
||
both to PNG for full platform support (see roadmap).
|
||
|
||
---
|
||
|
||
## 4. CI guardrails (what keeps the score from regressing)
|
||
|
||
Run all the dependency-free checks with **`just frontend-check`**:
|
||
|
||
| Script | Enforces |
|
||
| --- | --- |
|
||
| `check-contrast.mjs` | WCAG AA on every text/bg + semantic token pair (light + dark). |
|
||
| `check-headings.mjs` | One `h1`, no skipped levels, per HTML page. |
|
||
| `check-locales.mjs` | Locale completeness + `{placeholder}` integrity across all 16 locales. |
|
||
| `check-dead-tokens.mjs` | Reports tokens defined but never referenced (cleanup aid). |
|
||
| `check-brand-drift.mjs` | Locks the logo SVG hash + `--color-logo-gradient` against silent change. |
|
||
| `gen-token-docs.mjs` | Regenerates [TOKENS.md](TOKENS.md). |
|
||
|
||
**Build pipeline.** `build.rs` (release) bundles + content-hashes + minifies all
|
||
CSS/JS into `static-dist/` using the Rust crates **lightningcss** and **oxc** — no
|
||
npm. It also injects early `<link rel="preload">` / `<link rel="modulepreload">`
|
||
hints for the hashed bundles. Debug builds (`cargo run`) serve raw `static/`
|
||
unprocessed, so any source CSS must be valid as-authored (e.g. `@custom-media`
|
||
is off the table — it has no native browser support).
|
||
|
||
Still to wire (genuinely need npm devDependencies / browsers): stylelint rules
|
||
banning raw px/hex outside `:root`, axe-core + Playwright (keyboard,
|
||
visual-regression, dark-parity, cross-browser), and Lighthouse-CI perf budgets.
|