# ALTARA Community Themes — APIs 1 and 2

Community themes are CSS packages that change the app's appearance and layout. They are a local layer over the existing Appearance settings, not a replacement for your account, messages or built-in palette editor.

Themes are authored with code in an external editor. The app imports validated files and lists local themes for activation; discovery and publication belong to the website. The free publication/catalogue implementation is prepared but requires the theme migration and `theme-api` deployment before it works on hosted accounts. Local work and fixture tests do not prove a public rollout.

## Try a theme

Download a `.altara-theme.json` file from the website's **Themes** catalogue. Open **Settings → Appearance → Community themes → Import theme**, or copy the file into the account's theme folder shown in Appearance. Files are validated before use. Importing does not activate the package; choose **Activate** on its card. Existing API 1 and API 2 files remain supported.

On desktop, the folder is inside ALTARA's user data directory at `themes/<account UUID>`. Its actual path is shown in the app; it differs by operating system and account. Only theme JSON files are read. The web app uses a browser-local library and does not claim access to this desktop folder. Themes and selection remain separate from messages and widget records.

Website previews use fictional data in an isolated frame. They do not display your conversations or modify ALTARA. Native-layout packages use the actual Home presenter with fictional data; other packages preview a sample chat. The sample is not an exact copy of every server, bot, call and settings surface.

Use **Deactivate** on the active theme's card in Appearance to restore the base appearance. There is no floating deactivation button. **Ctrl+Alt+Shift+T** remains an independent keyboard escape, including when a theme has hidden the main layout. If storage is unavailable, recovery still removes styling for that session; the previous saved selection may return after restart. **Explore themes** beside **Import theme** opens the website catalogue; it does not open a catalogue inside the app.

Themes and the selected install are stored separately for each signed-in account on this device. Changing accounts removes the current styling and closes editors. Returning to the account restores its saved selection. Imports do not select an account or overwrite another install.

## Package format

Download the generic [programming starter](../theme/community-theme-starter.altara-theme.json), also available at `/theme-assets/theme/community-theme-starter.altara-theme.json` on the website. Edit the JSON's `css` string directly, or keep CSS in a separate source file and build a portable package with the local tool below. The starter leaves `layout` unset and preserves the native Home and personal widget organization.

Use a UTF-8 JSON file, preferably named `my-theme.altara-theme.json`:

```json
{
  "format": "altara-theme",
  "apiVersion": 1,
  "name": "Soft panels",
  "author": "Your name",
  "version": "1.0.0",
  "description": "Rounded panels and a softer accent.",
  "css": ":root { --my-accent: #a9ccff; } .panel { border-radius: 20px; } .app { --altara-accent: var(--my-accent); }"
}
```

- `name`: required, up to 60 characters.
- `author`: optional, up to 60 characters. This is a display label, not verified publisher identity.
- `description`: optional, up to 240 characters.
- `version`: required, `x.y.z`.
- `css`: required, up to 96 KiB in UTF-8.
- API 1 JSON: up to 128 KiB. API 2 JSON: up to 1.5 MiB. The library holds up to 20 installs and 4 MiB of serialized data.

Unknown identity, permission and account fields do not become install authority. ALTARA generates a fresh local install ID. Invalid imports leave the existing library and active selection unchanged.

## Write CSS

Use normal CSS declarations, variables, CSS Grid/Flexbox, gradients, borders, shadows, filters and `backdrop-filter`. `@media` and `@supports` allow responsive layouts and fallbacks. The generic starter demonstrates actual native surface selectors without depending on ALTARA Glass.

| Surface | Initial selectors |
| --- | --- |
| Main layout | `.app`, `.panel.left`, `.panel.mid`, `.panel.right` |
| Server rail and DMs | `#leftGroupsRail`, `.leftDockDms`, `#dmList` |
| Human and bot conversations | `#dmMain`, `#botDmMain` |
| Conversation header and history | `.dmHeader`, `.dmMainBody`, `.dmThread`, `.dmMessages` |
| Message composer | `.dmComposer`, `#dmInputWrap`, `.dmInputWrap` |
| Side panels | `#dmProfilePanel`, `#serverMembersPanel` |
| Settings | `#profileOverlay.settingsOverlay .settingsCard`, `.settingsSidebar`, `.settingsMain`, `.settingsMainBody` |

These selectors refer to the current app surfaces. Test against the app version you support. Avoid relying on private data attributes, exact child counts or unrelated implementation details.

`:root` and a standalone `body` rule may define custom properties only. ALTARA maps them to the authenticated theme scope. Other rules are scoped below that body and exclude the theme manager and recovery controls. The compiler gives theme declarations priority over the existing palette rules without changing the underlying saved palette.

Existing visual variables such as `--altara-accent`, `--altara-text`, `--altara-text-muted` and `--ui-primary-bg` can be overridden on `.app` or a specific surface. Add your own named variables for reusable colors and spacing. Some native components set their own variables, so a root variable alone may not change every descendant.

## One appearance for all widgets

Define the following variables once on `.app`. ALTARA resolves and validates them, then shares only this presentation record with each isolated widget. Native widgets, v1 worker widgets and installed HTML snapshots use the same contract. Adding another supported widget requires no new theme selectors. Existing theme APIs 1 and 2 remain compatible; missing variables use the app palette and bounded defaults.

```css
.app {
  --altara-widget-surface: rgba(24, 33, 44, .72);
  --altara-widget-surface-raised: #25334b;
  --altara-widget-input: #141d29;
  --altara-widget-text: #edf4ff;
  --altara-widget-muted: #afbfce;
  --altara-widget-accent: #a9ccff;
  --altara-widget-accent-text: #101318;
  --altara-widget-border: rgba(210, 230, 255, .16);
  --altara-widget-radius: 18px;
  --altara-widget-spacing: 16px;
  --altara-widget-blur: 12px;
}
```

| Shared token suffix | Meaning / accepted range |
| --- | --- |
| `background`, `surface`, `surface-raised`, `input` | CSS color; surfaces may be translucent |
| `text`, `muted`, `accent`, `accent-text`, `border` | CSS color; text is never made translucent as a group |
| `success`, `warning`, `danger` | Separate semantic colors; keep states distinguishable |
| `font-family` | Exactly one local stack below; no remote fonts |
| `font-size`, `line-height` | 10–24 px; unitless 1–2 |
| `radius`, `spacing`, `control-height` | 0–32 px; 4–32 px; 24–64 px |
| `border-width`, `blur`, `shadow` | 0–3 px; 0–24 px; 0–48 px shadow blur |
| `color-scheme` | `dark` or `light` |

Font stacks: `"Segoe UI", system-ui, sans-serif`; `Arial, Helvetica, sans-serif`; `Georgia, "Times New Roman", serif`; `Consolas, "SFMono-Regular", monospace`. Every token starts with `--altara-widget-`. Invalid values fall back safely; no author CSS, URLs, account IDs or messages cross the iframe boundary. Blur belongs to the native card, avoiding nested filters inside each widget.

Standard HTML controls, text, common surfaces, links and progress indicators adapt automatically. Widget creators can mark an unusual component once with `data-altara-widget-role="surface|muted|accent|success|warning|danger"`; all themes then understand it. For canvas charts or closed Shadow DOM, creators integrate `altara.appearance.get()` / `subscribe()` from the hosted SDK once and redraw with the supplied visual values. An arbitrary image, canvas or custom component cannot be recolored safely by a parent stylesheet. Existing live external development pages need the updated SDK; installed fixed HTML releases receive the trusted appearance bridge even when their original SDK predates themes.

Switching the theme changes the style layer in place, without restarting the worker, replacing widget content or writing widget data. Disabling/recovering the theme removes the layer and restores creator styling. Account changes dispose mounts under the existing ownership rules. Appearance pauses alongside theme CSS on sensitive native dialogs. The system's reduced-transparency and reduced-motion settings are propagated; an explicit per-theme Glass/solid choice is respected. No animations or polling loop are added.

The original installed HTML bytes and release hash remain unchanged. ALTARA verifies them before adding its own trusted bootstrap with a separate CSP script hash. Sandbox, network permissions and the dedicated storage port remain unchanged. A theme's appearance record cannot grant behavior or permissions.

## Boundaries and compatibility

The package is parsed using pinned **CSS-tree 3.2.1**, then validated and scoped from its syntax tree. It is not loaded as arbitrary HTML or JavaScript. The MIT license and upstream notice are in `lib/vendor/css-tree`.

The v1 policy rejects resource addresses, `url()`, `@import`, `@font-face`, unsupported at-rules/functions, CSS-generated text, executable properties and malformed syntax. A package cannot declare new external image/font requests or run script. Native app assets and values already defined by the app remain under the app's normal behavior; this policy does not disable the app's network traffic.

Animations, nested style rules and custom keyframes are not supported in v1. Transitions must have explicit durations no longer than two seconds. Rule, selector, declaration and nesting limits bound the work accepted by the compiler. Use reduced-motion rules and avoid large blur surfaces when a simpler effect is sufficient.

Themes change presentation, not DOM structure or product behavior. They cannot add buttons with new actions, receive messages through an SDK or grant access to an API. Widgets keep their existing isolated runtime. Theme packages and CSS are not written to `profiles.theme_settings`. Public releases are uploaded only through an explicit creator publication flow, independently of messages and widget data.

## API 2: portable images and native Home layout

API 2 retains the fields above and optionally adds:

```json
{
  "apiVersion": 2,
  "assets": { "room": { "mime": "image/webp", "base64": "CANONICAL_BASE64_BYTES" } },
  "layout": "dashboard"
}
```

This snippet describes the extra fields, not a complete installable package. Asset keys are lowercase identifiers of up to 32 characters. Static PNG, JPEG and WebP are accepted only after canonical base64, matching file signatures, dimensions and animation checks. Each image is limited to 512 KiB; all images together to 1 MiB; dimensions to 4096 pixels per side and 12 megapixels. SVG, GIF, APNG and animated WebP are rejected. Authors embed assets in the package; validated assets survive preview and download.

Use `theme-asset(room)` in a declaration to refer to an embedded image. Define a reusable variable once, for example `:root { --room: theme-asset(room); }`, then use `var(--room)` in backgrounds. ALTARA generates the internal data URL after validating the package. Raw author URLs remain forbidden. Expanded CSS is capped at 2 MiB before generation to bound repeated image expansion.

`backdrop-filter: theme-filter(liquid-glass) blur(16px) saturate(1.2)` uses ALTARA's fixed, native SVG displacement filter followed by ordinary CSS blur. It bends the background at a rounded rim, without capturing messages or running an animation loop. Packages cannot provide SVG, shader programs or arbitrary filter URLs. Browsers that reject SVG backdrop syntax, or documents unable to create its Canvas map, retain the ordinary blur/saturation path. Visual behavior is verified in Chrome; other engines require their own visual checks.

`layout: "dashboard"` requests a native Home presentation: greeting, status actions, known servers and current public activity. The account and navigation remain under ALTARA's normal permissions. No author HTML or executable theme code is accepted. The native presenter and the existing widget grid join one visual composition through CSS Grid. Native widget nodes, records, custom widget iframes and message DOM keep their existing ownership. The optional Glass widget disposition is a separate, explicit, reversible choice; activating a package does not apply it. Removing or disabling the theme clears that section. The native Home presenter supplies `data-theme-slot` values such as `home-hero`, `home-servers`, `home-activity` and `home-status-actions` for styling.

The original API 1 behavior and limits are retained. Files with assets or a native layout must declare API 2. Supported layouts are `dashboard` (the original integrated widget view) and `glass-home` (the native modular Home). Activation never overwrites saved widget organization; Glass Home exposes the unchanged grid through My widgets.

## Programming tokens and stable slots

The app is a file importer and theme selector, not a theme builder. Write ordinary CSS in your own editor. The `--ct-*` names below are conventions used by the starter: your CSS must actually consume each variable. Defining a token alone does not automatically restyle unrelated native components. Existing files containing an `ALTARA visual tokens` block still work; it is ordinary CSS and is not removed when a package is imported.

| Public token | Value |
| --- | --- |
| `--ct-background`, `--ct-surface` | background/surface colors |
| `--ct-accent`, `--ct-text`, `--ct-muted` | accent and readable text colors |
| `--ct-surface-alpha` | recommended .30–1; apply to backgrounds, not text opacity |
| `--ct-blur` | recommended 0–24px; avoid nested full-window blur |
| `--ct-radius` | recommended 0–28px |
| `--ct-spacing` | recommended 8–24px |
| `--ct-border`, `--ct-shadow` | recommended 0–2px border, 0–30px shadow size |
| `--ct-font` | supported local/native font stack |

Preferred stable selectors include `[data-theme-slot="widget-grid"]`, `widget-card`, `widget-title`, `home-presentation`, `home-navigation`, `home-hero`, `home-servers`, `home-server-card`, `home-activity` and `home-status-actions`. The ALTARA Glass example uses the same API2 layout, token fallbacks and fixed native material available to other creators. Author styling is paused completely, including inherited variables and ancestor styles, while native confirmation, settings or dialog decisions are open. Keyboard recovery remains independent of the theme's layout and cascade. Recording/microphone-sharing indicators are also sensitive: the base appearance is shown for the duration of the indicator so author overlays cannot hide ongoing consent. This deliberate safety tradeoff may visibly pause a theme during capture.

For `glass-home`, additional slots are `home-board`, `home-brand-mark`, `home-search`, `home-native-actions`, `home-server-directory`, `home-recent-activity`, `home-today`, `home-messages`, `home-active-now`, `home-direct-messages`, `home-quick-notes`, `home-focus` and `home-footer`. Select them with the full attribute selector, for example `[data-theme-slot="home-focus"]`. A slot is present only when the corresponding native presentation/component is active. The layout names select host-owned presenters; they do not load author templates. CSS may rearrange these native slots responsively, but cannot generate new actions, data bindings, widgets or HTML. Preserve native buttons, focus and readable states.

```css
/* Keep layout responsive and leave the native widget records untouched. */
[data-theme-slot="widget-grid"] { gap: 16px; }
[data-theme-slot="widget-card"] {
  background: rgba(24, 33, 44, .72);
  border: 1px solid rgba(210, 230, 255, .16);
  border-radius: 18px;
  backdrop-filter: blur(12px);
}
@media (prefers-reduced-transparency: reduce) {
  [data-theme-slot="widget-card"] {
    background: #18212c;
    backdrop-filter: none;
  }
}
```

## Separate CSS source and local validation

Download the **starter kit ZIP** from `/developers/themes/guide`. It contains separate `metadata.json` and `theme.css` files, an example package and the same validator/builder used by ALTARA. Extract it, use Node.js 22 or newer and run the commands below from that folder. No npm installation or app checkout is required for the downloaded kit. The readable website guide covers each step from editing to publishing; its offline Markdown copy is included in the kit.

In the ALTARA source checkout, use the supplied Node tool. It never evaluates theme JavaScript and uses the same parser, image validation and canonical package format as the client/backend. Create `metadata.json` with the package fields above, omitting `css`; keep CSS in `theme.css`:

```sh
node scripts/theme-package.mjs --build metadata.json theme.css my-theme-1.0.0.altara-theme.json
node scripts/theme-package.mjs --check my-theme-1.0.0.altara-theme.json
```

`--build` validates before creating the output and refuses to overwrite an existing file. Choose a new output filename after edits. The result prints API compatibility, byte size and the SHA-256 of canonical bytes. Assets must be embedded in `metadata.json` using the API 2 contract above. This is optional: uploading your JSON in the website developer area also validates it and offers an isolated preview before saving a private draft. Do not upload compiled scoped CSS; write source CSS and let ALTARA scope it.

Maintainers regenerate the downloadable kit with `node scripts/build-theme-starter-kit.cjs` using JSZip from their development tools (`ALTARA_THEME_ZIP_MODULE` can select its installed module path). `--check` verifies the archive and every source fingerprint without ZIP tooling. The kit contains only an explicit file allowlist, with CSS-tree's licence and notice. Changes to its validator, CLI, starter or offline guide require regenerating the archive before distribution.

## Free publication and installation

Use `/themes` to discover/download and `/developers/themes` to publish. Write files locally → upload a package → validate/preview → save a private draft → review → publish an immutable free release. To edit a project, download its draft if necessary, edit externally and choose **Replace draft package**. The existing project owner and expected draft revision are checked by the server; the uploaded package cannot select a creator account. Public creator identity comes from the verified session/project owner, never the textual `author` field. Screenshots are decoded by the browser and resized to at most 640px/96KiB before publication, then structurally revalidated by the server. The server also checks PNG chunk CRC/header integrity; it does not run a full raster pixel decoder.

Anyone can search public themes, open details, preview and download. The release's canonical bytes, API and SHA-256 are verified before a download starts. Downloading does not create a cloud installation, activate a theme or modify the app. Import the downloaded file or copy it into the desktop account folder, then explicitly activate it. Publishing and reporting require the normal ALTARA account; ALTARA+ is not required. The portal never applies author CSS to its own authentication, publication or download controls. API 1 packages retain their existing local behavior.

Published releases are pinned by version and SHA-256 canonical bytes. A new release never rewrites an older downloaded file. Download a newer version deliberately and activate it after import; local folder packages are not automatically updated or remotely revoked. Removing a theme from the public catalogue prevents future website downloads after an authoritative refresh, but cannot erase copies already downloaded to offline devices. Local packages still pass the client validator before application. A downloaded `author` label is not verified identity; the website listing provides the server-owned creator identity. Account changes fence pending website operations.

Existing account-installation records and backend API compatibility remain intact for older clients. Those older managed installations have separate explicit-update and revocation rules; they are not presented as the new folder workflow.

The local library remains bounded to 20 entries/4MiB, and published account packages to 3MiB total. Capacity is checked before an install mutation. A rare device-write failure after a successful server save is reported explicitly: the account accepted that version, but the previous local appearance stays in place until a successful refresh. This is not a claim of a cross-device storage transaction.

Themes are free in this delivery for normal authenticated accounts. ALTARA+, payments, commissions and payouts do not control access and are outside scope. See [backend contract and rollout](COMMUNITY_THEMES_BACKEND.md) for migration, deployment, Auth and moderation prerequisites.

## Reference

The [Zen Mods documentation](https://docs.zen-browser.app/user-manual/extensions) describes CSS mods for the browser interface. ALTARA uses a separate, app-specific package and validation policy. The [CSS-tree project](https://github.com/csstree/csstree) supplies the parser used here.
