frontend-design · git:20260620.70bbd7b · 2026-06-20 · sha256 017494fa8364bf3a

frontend-design git:20260620.70bbd7bA

Immutable. This exact content is served forever at /api/v1/blob/017494fa8364bf3a.

---
name: frontend-design
description: How sparq designs its TWO distinct frontends — the explanatory marketing-docs WEBSITE (site/, Next.js static export) and the operational desktop GUI (gui/, Tauri 2 + native engine). Covers the methodology that keeps them distinct, scannable, and honest: information architecture + content-reduction (fight "too much text"), explanatory-site patterns (show-don't-tell, progressive disclosure, one killer artifact), operational-GUI patterns (workbench shell, command palette, keyboard-first), the shared visual-design system, and the a11y/perf budget. Use when restructuring site/ navigation or pages, building/extending gui/, deciding whether content belongs on the site vs the GUI vs a SKILL.md, adding a surface, cutting a dense page, or reviewing a frontend PR. Grounded in research/website-redesign.md + research/gui-design.md + the real site/src tree.
---

# Frontend design (sparq)

sparq ships **two** frontends that are deliberately **different products**, not two
skins of one app. Conflating them is the single biggest design failure to avoid — and the
one the maintainer flagged: *"the website has too much text; it is overwhelming."* The fix
is not "trim words"; it is to keep each frontend doing only its own job.

| | **Website** (`site/`) | **GUI** (`gui/`) |
|---|---|---|
| Audience | the RDF/SPARQL-**curious** dev evaluating *"is this real, should I care?"* | the dev who already decided and wants to **do work** |
| Job | persuade + route + prove with a few live demos; link out for depth | operate the engine as a real tool over persistent local data |
| Stack | Next.js `output: "export"`, React 19, Tailwind v4, radix/shadcn; GitHub Pages | Tauri 2 webview + **direct native-Rust engine link**; desktop bundle |
| Shape | a small set of **pages** you read top-to-bottom | a **workbench shell**: rails + tabs + command palette, no marketing pages |
| Content | marketing-docs: one-liner → demo → link to repo/SKILL.md/papers | operational: import → query → inspect → operate surfaces as tools |
| Depth lives | `crates/*/README.md`, `skills/*/SKILL.md`, `research/`, `/papers` | the engine itself + a Help → opens the website in the system browser |

> **Litmus test for any new frontend work:** *"Does this help someone DECIDE
> (website) or DO (GUI)?"* If a change makes the website more of a workbench, or the
> GUI more of a brochure, it is going the wrong way. The GUI must **never just wrap the
> marketing site** (today `gui/` is a `src-tauri` scaffold whose `frontendDist` points at
> `site/out` — that is a placeholder, not the design).

Design records (read these for the full spec — this skill is the reusable *method*):
`research/website-redesign.md` and `research/gui-design.md`.

---

## 1. Information architecture + content reduction (fight "too much text")

The overwhelm is structural, not verbal. Audited, the *same* ~16 surfaces appear in **up
to three places at once** (`site/src/components/layout/app-shell.tsx`): a persistent
`w-64` sidebar rendering the full 6-group/16-surface tree (`sidebar-nav.tsx`) **and** a
top-tab bar (Showcase/Benchmarks/About) **and** the landing page re-renders all surfaces as
cards. Cutting prose does not fix a tripled nav. **Cut structure first.**

Principles (apply in this order):

1. **ONE nav, not two.** A persistent full sidebar tree *and* a top-tab bar split attention
   and imply two mental models. Pick one primary structure (the website chose a slim top
   bar of **5 content destinations**); the other becomes utility-only (search / GitHub /
   theme) or is deleted.
2. **Cut surface COUNT in the nav, not just words per page.** 16 flat entries overwhelm;
   group them under a handful of capability **themes** and let the user navigate *concepts*
   then drill to the feature. Re-group ONCE at the single source (`site/src/data/surfaces.ts`
   `GROUPS`) so the nav and every landing grid update together.
3. **Progressive disclosure in three layers.** (1) **SCAN** — one sentence + one tier
   badge + one runnable thing, above the fold; (2) **UNDERSTAND** — a tight capability list
   + the demo; (3) **VERIFY/DEEP** — caveats, reproduction commands, threat-model hedging,
   links to crate/SKILL — behind a closed `<details>`. Never make a hurried reader read the
   deep layer to use the surface.
4. **Earn each section; don't repeat a template.** A rigid template that *forces*
   intro + N capability cards + runsNote + caveat on every page guarantees uniform density:
   a simple surface looks as heavy as a research one. Make every block **optional** and let
   weight follow importance.
5. **Centralize cross-cutting explanation once; reference it.** The "what runs where / how
   this runs" honesty story was told four times (hero, /about, every per-page runsNote,
   every badge tooltip). State the **tier model once**, let the badge be the per-page
   pointer, and shrink each per-page "How this runs" to one sentence + a link.
6. **Move reference-grade content OUT of the frontend.** Reproduction commands, the full
   capability enumeration, edge-case boundaries, and threat-model nuance are **reference**:
   they belong in `crates/<x>/README.md`, `skills/<x>/SKILL.md`, and `research/` — which
   already exist. The website persuades; the docs exhaust. Link, do not inline.
7. **A search / command palette is the real answer to "N surfaces is a lot."** A fuzzy
   index lets you SHRINK visible nav without burying anything — power users jump by name in
   0 clicks. On the website this is Cmd-K; in the GUI it is the spine of the keyboard-first
   model. **It is load-bearing: ship it BEFORE deleting the sidebar**, or the collapsed
   surfaces lose their only fast path.

Content-reduction is a *commitment*, not a vibe. A redesign must name **what to cut, what to
collapse, and what to move** (see `research/website-redesign.md` §"content-reduction plan").

---

## 2. Explanatory-site patterns (the website)

The website's strongest asset is the **live REPL** (`site/src/components/repl.tsx`) — a
real in-tab SPARQL engine, no mocks. Build every page around that energy.

- **ONE killer artifact beats ten paragraphs.** A developer trusts a runnable result far
  more than prose claiming it works. Lead with the demo/result; prose is the **caption**,
  not the lede. The hero already nails this — the failure was that *inner* pages reverted to
  prose-first (e.g. a GeoSPARQL intro of ~3 dense paragraphs + ~30 inline `<code>` spans
  *before* the walkthrough). Invert it everywhere.
- **Show, don't tell — lead with a runnable/copyable example.** The first interaction is
  *doing*, not reading. Copyable code (with a copy button) is itself a navigation aid: it
  tells a dev "you belong here."
- **One-line what-is-it above the fold, then proof.** A stranger should understand
  *what this is and why care* in one sentence, immediately followed by ONE compelling proof
  (a live query, a number). Do **not** front-load a multi-sentence honesty preamble; reduce
  it to one badge + a tier-legend strip.
- **≤3 canonical next steps, always visible.** Converge on a small fixed CTA set
  (Try it / Docs / GitHub). The hero gets ~2 buttons, not 20 equally-weighted links.
- **Scannability is typographic before it's editorial.** Walls of justified prose studded
  with inline `<code>` read as noise. Capabilities → a scannable list with a **bolded lead
  term**; "what's NOT covered" → a secondary/collapsed block, not a co-equal warning card
  competing with the demo.
- **Honesty can be terse — and is MORE honest terse.** A 5-clause hedging paragraph gets
  skipped; a one-line bold flag ("Research-grade — not externally audited") + a "Read the
  full caveat" `<details>` gets *read*. Keep the honesty-tier taxonomy
  (`Tier` in `surfaces.ts`) verbatim; the badge carries the per-page truth.
- **Triage by tier for prominence.** "Live in your tab" surfaces are the proof and should
  lead; "walkthrough" surfaces are credible-but-static and sit in a secondary tier or behind
  a theme. Equal visual weight for unequal content reads as "too much."

Exemplars worth imitating (why each): **tRPC / Turso / DuckDB** — one runnable example
carries the pitch, deep API lives in a separate `/docs`. **Tailwind / Stripe docs** — a
large feature set grouped into ~8 themed, collapsible sections + a prominent Cmd-K; short
conceptual intro then collapsible deep sections. **Linear** — each capability is one bold
line + one clause + one visual, never a paragraph (a sophisticated product reads as MORE
capable when each claim is one scannable line).

**Static-export gotcha (load-bearing).** `site/` is `output: "export"` (no server), so you
**cannot issue a real 301**. Removing a route (e.g. collapsing `/surface/*`) requires
generating a **client-side redirect stub** page at the old path, or external/inbound links
and the existing `[slug]` catch-all (`dynamicParams=false`) hard-404. Always ship stubs when
you delete a route.

**Lazy-mount heavy demos (load-bearing).** A consolidated gallery that mounts many
interactive components (ZK prover via `bb.js`/`noir_js` ~MB, MPC sim, geo/vector
walkthroughs) on one route MUST `next/dynamic`/`React.lazy` each demo so its chunk loads
**only on expand** — preserving the route-scoping `sidebar-nav.tsx` already enforces for the
ZK prover. Otherwise the "lighter" gallery is heavier than the pages it replaced.

---

## 3. Operational-GUI patterns (the desktop app)

The GUI is a **workbench shell**, not a route tree. Its model is an IDE, not a brochure.

- **Persistent shell, per-workspace tabs.** Three fixed regions — a stateful **left rail**
  (workspace switcher · datasets tree of the live store · TOOLS list as *verbs you open*),
  a thin **top bar** (connection target LOCAL⇄ENDPOINT · store size · status LED · theme), a
  **work area** = an IDE-style **tab strip** of open tools (default = Query editor), and a
  **bottom status bar** (measured last-run latency · row count · target · persistence
  backend). No Showcase/Benchmarks/About tabs.
- **The 16 marketing surfaces become TOOLS, not pages.** Query · Graph view · SHACL ·
  Inference · Full-text · Vector · GeoSPARQL · Federation · ZK · MPC · Server — each is a
  verb the user *opens as a tab against the current store*, carrying a small honesty-tier
  dot — **not** a page that *describes* the feature.
- **Command palette (Cmd-K) is the spine.** Fuzzy-index every tool, named graph, recent
  query, import/connect/export action, "run as EXPLAIN", "switch workspace". This *replaces*
  the website's sidebar-as-discovery and is what makes "many surfaces" navigable.
- **Keyboard-first, offline-first.** Run = Cmd-Enter; works with no network against a
  persistent local native store. The native engine link (no wasm ceiling: threads, mmap,
  persistence, native-only formats like HDT) is the GUI's reason to exist over the website.
- **Operate over a LIVE persistent store, not a fixture.** Where the site's playgrounds run
  against a sample graph, every GUI tool runs against the active workspace's real
  imported-from-disk/URL store, persisted across sessions (the `@sparq/client`
  `createWorkspaceStore` → Tauri fs backend model).
- **Reuse component LOGIC, rebuild the HOST.** The GUI should import the editor/results/
  validation *logic* from `site/` (`sparql-editor.tsx`, `@sparq/client` `results.ts`,
  `shacl-playground.tsx`, …) but mount it in operational hosts (full-height panes, multiple
  co-resident result views, a drawer-based importer) — **not** the site's hero+prose wrappers.
- **Reference/marketing content does NOT ship in the app.** No `/about`, `/papers`,
  `/benchmarks`, `/showcase` routes. A single **Help** item opens the website / GitHub in the
  system browser. *The app is the tool; the website is the explainer.*

**Ground-truth caveat (do not repeat the false premise).** `gui/` today is a `src-tauri`
scaffold with **no React frontend** and zero imports of `site/` components ("a scaffold, not
a shipped app"). Building the GUI frontend that imports site components is **net-new work**,
gated on its CI lane (`.github/workflows/gui.yml`) being green — it is *not* a move within an
existing reuse path.

---

## 4. Visual design system (shared between both)

Both frontends share ONE design language (so the project reads as one product) while the
*shells* differ.

- **Tokens, not hex.** Tailwind v4 + the shadcn/radix token set already in `site/src` —
  `bg-background`, `text-foreground`, `bg-sidebar`, `border`, `muted`, semantic variants.
  Never hardcode colours; theme (light/dark via `theme-provider.tsx`) must keep working.
- **Honesty-tier badge is a first-class component.** `TIER_LABEL` / `TIER_VARIANT`
  (`surfaces.ts`) map each tier to a colour + label; `success`=live, `warning`=hosted,
  `muted`=walkthrough, `default`=sim. Reuse it on the website (per-surface badge) and in the
  GUI (per-tool dot). A `walkthrough` must never be dressed up as `live`.
- **Density follows the product.** Website = roomy, marketing density (`max-w-6xl`,
  generous padding). GUI = dense, IDE chrome (denser rail `w-56`, thin `h-10` top bar,
  `h-6` status bar, **no `max-w` cap** — full-bleed work area).
- **Iconography:** `lucide-react`, one icon per surface, declared once in `surfaces.ts`.
- **Code rendering:** the SPARQL/Turtle/JSON-LD highlighters (`sparql-editor.tsx`,
  `rdf-highlight.tsx`, `pretty-turtle`) are shared assets — reuse, don't re-implement.
- **Logo / brand:** `site/src/components/logo.tsx`; for new branding/SVG work use the
  vendored `logo-designer` skill. The wordmark + tier-badge language are the brand.

---

## 5. Accessibility + performance budget (non-negotiable)

- **A11y:** semantic landmarks (`<nav aria-label>`, `<main>`), `aria-current` on the active
  nav item, focus-visible rings, ESC-closes-dialog, a command palette reachable by keyboard,
  `sr-only` titles on icon-only controls and drawers (the existing shell already does several
  of these — keep them). Colour is never the *only* signal a tier carries — pair it with the
  text label.
- **Perf (website):** it is a static bundle on Pages — keep the **main bundle wasm-free**
  (load the ~2.5 MB engine wasm *async after* page load; lazy-load every heavy demo chunk
  on-expand, never on route entry). Image `unoptimized` + a hardcoded `basePath:"/sparq"` are
  load-bearing for Pages/Tauri — don't break them. Track regressions; do not bundle the wasm
  into the initial payload.
- **Perf (GUI):** stream large SELECTs (`streamQueryRows`) so big results don't blow memory;
  show **measured** `performance.now()` latency per run (labelled), never a baked benchmark
  number.
- **Honesty gates apply to all frontend copy.** `scripts/check-privacy-claims.sh` (ZK/MPC
  must stay qualified — research-grade, not externally audited; the MPC site surface is a
  faithful JS *simulation*), the terminology gate, and the no-hard-coded-perf-numbers rule
  gate user-facing copy. Write copy that passes them by construction.

---

## 6. Where content belongs (the routing rule)

When you have a piece of content, route it by **intent**, not by convenience:

| Content | Home |
|---|---|
| "What is sparq, why care" (one line) + one live proof | Website home + `/try` |
| A curated runnable demo ("show me it working") | Website `/examples` + `/showcase/*` detail |
| Compact per-surface blurb + tier + expand-in-place demo | Website `/capabilities` (one gallery) |
| The full capability matrix, every flag, edge cases, repro commands | `crates/<x>/README.md` + `skills/<x>/SKILL.md` |
| Threat-model / soundness nuance, prior art, the design itself | `research/*.md` |
| Benchmark provenance | Website `/benchmarks` (honest, per-commit, same-box framing) |
| Papers | Website `/papers` |
| **Doing** RDF/SPARQL work over real data | the **GUI**, never a website page |

If you are about to inline reference material on a marketing page, stop — link to the asset
that already holds it. If you are about to add a marketing explainer to the GUI, stop — link
to the website. Keeping content in its right home is *how* the website stops being
overwhelming and *how* the GUI stays a tool.

---

## Key file citations (ground truth)

- Design records: `research/website-redesign.md`, `research/gui-design.md`,
  `research/feature-showcase-site-design.md`.
- Single IA source: `site/src/data/surfaces.ts` (`Tier`, `GROUPS`, `FLAGSHIPS`,
  `TIER_LABEL`, `TIER_VARIANT`).
- Website shell + nav (the audited overwhelm): `site/src/components/layout/app-shell.tsx`,
  `site/src/components/layout/sidebar-nav.tsx`.
- The rigid template to make optional: `site/src/components/surface-content.tsx`.
- The killer artifact: `site/src/components/repl.tsx`; reusable logic:
  `site/src/components/sparql-editor.tsx`, `site/src/components/rdf-highlight.tsx`,
  the per-surface playgrounds/walkthroughs, `@sparq/client` (`packages/sparq-client`).
- GUI panels to migrate (net-new host): `site/src/components/connect-panel.tsx`,
  `server-health-panel.tsx`, `subscriptions-view.tsx`.
- Static-export config (don't break): `site/next.config.ts`.
- Honesty gates: `scripts/check-privacy-claims.sh`, the terminology + no-perf gates in
  `.github/workflows/docs-quality.yml`.
- Epics: website redesign epic + GUI epic `sq-ixc3` (`.beads/issues.jsonl`).