--- name: App shell and styling type: architecture-map-page slice: 01-frontend last_verified: 2026/05/09 --- # App shell and styling > **Where you are:** [Architecture map](../README.md) → [Frontend](README.md) → App shell and styling **Plain English summary.** Where the visual chrome and CSS plumbing live. `app.html` is the SvelteKit document. `app.css` carries the Tailwind v4 import, the `@theme` token block, the brand `@font-face` declarations, the sensory zones, and the accessibility CSS variables. There is no separate `tailwind.config.{js,ts}`; Tailwind v4 is configured entirely in `app.css`. Fonts ship from `src/fonts/` (bundled by Vite) and `static/fonts/` (served as-is). ## At a glance - **Path:** `src/app.html`, `src/app.css`, `src/app.d.ts`, `src/fonts/`, `src/assets/`, `static/`. - **LOC:** 13 (`app.html`) + 583 (`app.css`) + 8 (`app.d.ts`). - **Frameworks:** Tailwind v4 via `@tailwindcss/vite`. No PostCSS config. No standalone Tailwind config file. - **Adapter:** `@sveltejs/adapter-static` with `index.html` fallback (SPA, `svelte.config.js`). ## What's in here ### `src/app.html` (13 LOC) Standard SvelteKit document. Sets `lang="en"`, charset, viewport, favicon, title (`Lumotia`), and applies `data-sveltekit-preload-data="hover"` on ``. The body content is wrapped in `
` so the SvelteKit-injected children render inline. ### `src/app.d.ts` (8 LOC) Ambient declaration extending `Window` with `__TAURI_INTERNALS__` and `isTauri` so `utils/runtime.ts` typechecks. ### `src/app.css` (583 LOC) Layered: 1. `@import "tailwindcss"` (Tailwind v4 entry). 2. `@font-face` for Lexend, Atkinson Hyperlegible Next, OpenDyslexic, JetBrains Mono, Instrument Serif Italic. URLs use root-absolute paths (`/fonts/...`) served from `static/fonts/` by Vite. 3. `@theme` token block. Sets the design tokens that Tailwind v4 picks up as utility classes: - Colour tokens: `--color-bg`, `--color-bg-raised`, `--color-text`, `--color-text-secondary`, `--color-text-tertiary`, `--color-accent`, `--color-warning`, `--color-success`, `--color-border`, `--color-border-subtle`, `--color-hover`, `--color-nav-active`, plus a sensory-zone palette family. - Typography tokens: `--font-display`, `--font-body`, `--font-mono`, plus `--font-size-*` and `--line-height-*`. - Motion tokens: `--duration-ui`, `--duration-fast`, `--duration-slow`. Easing tokens for `cubic-bezier` curves. - Shadow tokens: `--shadow-accent-md`, `--shadow-accent-glow`, etc. 4. Sensory-zone overrides keyed on `[data-zone="..."]` on ``. Switches token values to dim, focus, etc. 5. Theme overrides keyed on `[data-theme="dark"]` and `[data-theme="light"]`. The `preferences` store writes both `data-theme` and `data-zone`. 6. Accessibility variables on ``: `--font-family-body`, `--font-size-body`, `--letter-spacing-body`, `--line-height-body`. Set by the `preferences` store via `applyToDOM()`. 7. Base styles: `body` background, font, body font-family bound to the variable, scrollbar styling, focus ring. 8. Utility classes for grain texture (`.grain` uses the noise asset under `assets/grain.svg`), CRT-style transcript surface, etc. 9. Animations: `@keyframes fade-in`, `@keyframes pulse`, etc. Reduced motion guard at the bottom (`@media (prefers-reduced-motion: reduce)`). ### `src/fonts/` (bundled woff2) - `atkinson-hyperlegible-next.woff2` - `instrument-serif-italic.woff2` - `jetbrains-mono.woff2` - `lexend-variable.woff2` - `opendyslexic.woff2` Bundled by Vite (referenced from `app.css` via `/fonts/...` paths that Vite resolves). The same files live in `static/fonts/` for the static-served path. Confirm whether both paths are required; if Vite handles font copying, `static/fonts/` may be redundant. ### `src/assets/` - `grain.svg`. Noise texture used by the `.grain` utility class. - `waveform-mark.svg`. Brand glyph. - `wordmark.svg`. Brand wordmark. ### `static/` Served as-is from the webview root. - `favicon.png`. Site favicon. - `fonts/`. Same five woff2 files as `src/fonts/`. Likely the source of `app.css /fonts/...` URLs. - `pcm-processor.js`. AudioWorklet processor (slice 02 owns the integration). 32-line file that converts microphone input to int16 PCM frames and posts them up. - `textures/grain.png`. PNG version of the grain texture. - `svelte.svg`, `tauri.svg`, `vite.svg`. SvelteKit defaults; technically unused. Candidate for removal. ## How preferences map to the DOM | Preference | DOM target | |---|---| | `theme` ("light" / "dark" / "system") | `data-theme` on ``. `system` resolves to OS preference at apply time. | | `zone` ("default" / ...) | `data-zone` on ``. | | `accessibility.fontFamily` ("lexend" / "atkinson" / "opendyslexic") | `--font-family-body` CSS variable on ``. | | `accessibility.fontSize` | `--font-size-body` (pixels). | | `accessibility.letterSpacing` | `--letter-spacing-body` (em). | | `accessibility.lineHeight` | `--line-height-body` (unitless). | | `accessibility.bionicReading` | ``. The `bionicReading` action reads this. | | `accessibility.reduceMotion` | ``. Pairs with the `prefers-reduced-motion` media query. | Plus the legacy `settings.fontSize` writes `--font-size-transcript` on `` directly (`+layout.svelte:204`). That is separate from `accessibility.fontSize`. ## Tailwind v4 setup - Installed via `@tailwindcss/vite` in `vite.config.js:3`. - Entry: `src/app.css` line 1, `@import "tailwindcss"`. - Tokens declared inline via `@theme` blocks in `app.css`. - No `tailwind.config.{js,ts}` exists. Do not look for one. - Class scanning: Tailwind v4 scans the source tree by default. Custom paths can be configured with `@source` directives if needed. ## Watch outs - **Two font sources.** `src/fonts/` (Vite bundled) and `static/fonts/` (raw served). Confirm whether both are needed; redundancy bloats the bundle. - **Mirror file `src/design-system/colors_and_type.css`** must be updated whenever `app.css` `@theme` changes. There is no automated check. - **`prefers-reduced-motion` honoured by CSS** but JS animations (e.g. `CompletionSparkline`'s stagger) are guarded in component code, not via the media query alone. - **Tailwind v4 `@theme` is class-scanning sensitive.** If you put utility class strings inside conditional template literals that Tailwind cannot see at scan time, they will not be generated. - **Removing default SvelteKit assets** (`static/svelte.svg`, `vite.svg`, `tauri.svg`) requires confirming nothing references them in `app.html` or `README.md`. - **`pcm-processor.js`** is a static asset because AudioWorklet processors must be served from a same-origin URL. Bundling it through Vite would break the worklet registration. Leave it in `static/`. ## See also - [Design system](design-system.md). The reference mirror of `app.css` tokens. - [Stores](stores.md). The `preferences` store that writes the DOM. - [Components](components.md). Where the utility classes are consumed.