# 033 — Downloadable and re-uploadable custom themes ## Summary Let a user export the active theme's `--c-*` variable values as a file, edit it, and load it back in as a custom theme — reusing the CSS import/link machinery `Settings` already has (`CssEntry`, `import_css`/`link_css` in `src/settings.rs`) rather than inventing a parallel storage mechanism. ## Scope - **Download**: given a theme name (one of the built-ins from [032](032-greyscale-theme-rework.md), or a previously-uploaded custom one), produce a small CSS (or JSON) file containing just its `--c-*` variable values, served for download from the webapp's theme picker. - **Upload**: accept such a file back, validate it defines the expected set of `--c-*` variables, and register it as a selectable custom theme — persisted via the existing `import_css`-style flow (a new CSS entry scoped under a `html.` selector, or a dedicated theme entry if that shape doesn't fit `CssEntry` cleanly — decide during implementation which reuses more of the existing settings model). - **Linked** custom themes: since `CssEntry::Linked` already models "reference a URL, don't own the content," a custom theme can also be registered by URL instead of upload, consistent with how linked CSS works today. - Theme picker UI: list built-in themes alongside any uploaded/linked custom ones, each with a download action. ## Acceptance criteria - Downloading a theme produces a file that, re-uploaded unmodified, recreates the same theme. - Editing a downloaded theme's variable values and re-uploading it registers a distinct selectable theme without touching the built-ins. - A linked (URL) custom theme behaves like linked CSS today: not deletable via the "remove" action that applies to uploaded ones, per `CssError::NotImported`'s existing semantics. ## Depends on - [032 — Greyscale theme rework](032-greyscale-theme-rework.md) - [008 — Settings model](008-settings-model.md) (`CssEntry` import/link flow)