--- name: "PrivaTools — Air and Play" description: "Two distinct consumer experiences sharing one functional application." updated: "2026-10-05" experiences: air: light: "Morning Mist" dark: "Graphite" background-light: "#edf3f9" background-dark: "#151619" action-light: "#285cc7" action-dark: "#91b5ff" font: "Manrope" play: light: "Blush" dark: "Charcoal" background-light: "#fae7eb" background-dark: "#181818" action-light: "#a32749" action-dark: "#ff9677" body-font: "Outfit" heading-font: "Bricolage Grotesque" --- # PrivaTools design system The user selected Air and Play and their four appearance palettes on 13 September 2026. This document describes the shipped implementation, live since v2.2.0; previous visual directions are superseded. ## Shared behavior The same stable application owns routing, processing, files, account state and the local password vault. `ExperienceShell` changes navigation presentation without changing the mounted workspace ancestry. Never key the application or processor by experience or appearance. Processor keys use the tool slug only. A tool page shows the tool first and its guide (`ToolGuide`) directly below: numbered "How to use" steps, every FAQ answer written out rather than folded away, and links to the guides that mention the tool. It is the same text the server renders for crawlers, loaded per tool. One tool-page structure, the same in Air and Play (decided 2026-09-28, built 2026-10-02): - **Header.** One row of wayfinding and page utilities (All tools, the category, "Free to use. No account needed." on wide screens, pin, How it works, and Find another tool with a search icon), then the H1: the tool's name and its one-line promise, the registry `description`, on two lines ("Compress PDF" / "Reduce the file size of your PDF"). The H1's text is "Name: promise"; the colon is visually hidden. The search title (`seoTitle`) is the tab and meta title only, never the heading and never a kicker: no eyebrow sits above the H1. The server's crawler HTML renders the same H1 (`_tool_page_body` in `backend/app/seo_meta.py`; both read `toolSeo` / the manifest). Registry copy is not edited for the layout. - **Task order.** The intake (and what was chosen), then the options, then the action bar, then the result, which replaces them. The DOM and the focus order follow it. Play keeps its horizontal work shelf, now below the intake rather than above it. - **Where the file goes** is readable text in the intake (`ToolWhere`, 13px, an icon per kind), in the words and the classification the header chip used to carry (`toolLocation`: the registry's `byok`, then `clientOnly`, else temporary server processing). It must stay exactly true for the tool, so a tool its flags cannot describe has its own words in `OWN_LOCATION`, taken from its engine cards, notes or guide: Background Remover, OCR PDF and Image OCR (a choice of engines), every tool that can use the visitor's AI key (what reaches the provider, what reaches PrivaTools, what stays on the device), the tools that upload a file as soon as it is chosen (`UPLOADS_WHEN_CHOSEN`: Organize Pages and Remove Watermark), Fill Form and Form Creator (their "Detect form fields" and "Detect fields" steps upload the PDF before the fill or the form is created; `upload-on-choice.test.tsx` also presses each step and holds it to one upload to its route), and the server tools whose input is not a file (URL to PDF, HTML to PDF, Generate Barcode, QR Code), whose sentence names what is sent. `src/test/upload-on-choice.test.tsx` chooses a file on every tool page and fails on one that sends anything without being listed and worded so. Tool UIs that do not say it themselves get the same line just above them. Once a tool UI has said it (a shared kit or its own line), the page line stays out for the rest of the page view (`data-own-where` on the workspace), so it never jumps in above an editor that replaced its intake. Two intakes side by side (Compare PDF, Add Attachment, and `PairedIntake`'s Alternate & Mix and Overlay) say it once, under the pair. A tool that takes no file says it in its own intake card (`StudioSource`: URL to PDF, HTML to PDF), and its sentence also names what the server fetches with what was sent: URL to PDF renders the page with the files it loads (stylesheets, fonts, images, attachments); HTML to PDF fetches an address's HTML alone, or the files pasted HTML loads. Name them as an open list ("such as its stylesheets, fonts and images"): WeasyPrint fetches whatever the HTML loads. Merge keeps its own, more specific disclosure. - **One action bar** (`StudioActionBar`): the run action with the file count and the location label ("1 PDF · Temporary server processing"), nothing else; secondary actions such as "Reset to defaults" or "Clear selection" live with the options or the files. Beside the canvas it sits at the foot of the options column and sticks to the bottom of the screen within that column. In one column (Play at every size, Air below 900px, the media studio below 760px and in Play from 1001px) it sticks to the bottom of the screen while the tool is in view, once there is something to run: a strip along the bottom edge in Air, a floating dock 12px above it in Play (both clear of a bottom safe area). It docks only on screens taller than 420px: at 300% zoom and more, or on a phone held sideways, the header and a docked bar would leave the tool no room and could hide what has focus, so there it stays at the end of the tool. `useActionBarClearance` gives the root's bottom scroll padding and the toasts the room from its top edge to the bottom of the screen only while it sits low on the screen, docked or resting just above where toasts land: nothing while it sits mid-screen at the foot of the media studio's short settings column, or once the visitor has scrolled on to the guide. It arrives with a short ease-out rise (none under reduced motion), and toasts rise above it. The count names what the tool takes (`fileNoun`): "1 PDF" on a PDF-only tool, "2 images", otherwise "files". - **After a choice** the intake shrinks to a quiet dashed row with an "Add files" text button (a plus, not a filled button), so the run action is the obvious next step. File-picker buttons use a folder icon and Find another tool a search icon: ↗ no longer marks a file picker or an in-page dialog. - **Headings.** H1, then h2 for each part of the tool (the intake, the options, a result, the guide); file names in lists are labels, not headings, and a run's progress label is a status line. The media studio's stage heading appears only to say how a run ended. - **The shared kit (step 2b, 2026-10-02).** A tool that makes files from files is built from `skins/experience/ToolStudio.tsx` and, on a `useMultiFileProcessor` queue (or a queue of the same shape), `ProcessorStudio.tsx`: `FileIntake` (with the location line), `ProcessorFiles` for the chosen files, the options in `StudioLayout`'s `options` (an h2 per group; fields in `.ts-setting` with a real `