This specification is the shared contract for the ReTheme desktop runtime, community review, and AI-assisted theme creation. Use the runnable theme-example/package, the complete slot catalog, the Banner and image-generation guide, the AI workflow, and the machine-readable JSON Schema together.
A ReTheme theme is a declarative package containing JSON, CSS, and local static images. The engine detects the active ChatGPT version, maps native nodes to stable slots, mounts controlled images, mirrors appearance and locale state, and restores the original interface when the theme stops.
A theme may change colors, borders, radii, shadows, opacity, backgrounds, icon colors, text hierarchy, and non-interactive decoration. It may provide a home Hero, compact conversation Banner, controlled assets, and localized copy.
A theme must not:
- Execute JavaScript or alter events, ARIA semantics, business state, or native DOM order.
- Depend on minified ChatGPT class names, text content, DOM depth, or version-specific attributes.
- Override the font family. Always inherit ChatGPT's native font.
- reposition structural areas with fixed/absolute positioning or layout offsets.
- hide essential buttons, fields, messages, focus indicators, or accessible labels.
The root receives stable state attributes:
:root[data-ct-theme="studio.example.my-theme"]
:root[data-ct-color-scheme="light"]
:root[data-ct-color-scheme="dark"]
:root[data-ct-view="home"]
:root[data-ct-view="home-compact"]
:root[data-ct-view="conversation"]
:root[data-ct-view="other"]| View | Meaning | Required checks |
|---|---|---|
home |
Empty home screen | Full Hero, prompt, suggestion cards, composer |
home-compact |
Draft text or an open workspace panel | Engine hides full Hero/cards and may show the compact Banner |
conversation |
Started or historical conversation | Compact Banner sits above the message viewport; native composer position remains intact |
other |
Settings and other pages | Page, settings, and menu slots appear only when their DOM exists |
The engine owns these attributes. Theme CSS must never set or simulate them.
my-theme/
├── manifest.json
├── styles/
│ ├── tokens.css
│ └── overrides.css
└── assets/
├── hero.webp
├── hero-foreground.webp
└── conversation.webp
- Local development selects the directory containing
manifest.json. - A community source ZIP must place
manifest.jsondirectly at its root. - CSS belongs under
styles/; images belong underassets/. - Paths use UTF-8,
/, and canonical relative components. Absolute paths,.., backslashes, drive letters, and symlinks are rejected. - A development directory is not a
.ctheme. Only the platform creates signed.cthemereleases.
- Copy
theme-example/packageand readmanifest.annotated.jsonc. - Replace the ID, name, version, author, copy, and preview colors.
- Define light and dark semantic tokens before writing slot overrides.
- Load the directory through “Load local theme”.
- Test English/Chinese, light/dark, home/compact/conversation/settings/menu, macOS/Windows, and narrow/medium/wide/maximized windows.
- Remove structural CSS, overflow, flicker, poor contrast, and interactive overlays.
- Bump SemVer, ZIP the directory contents, and submit the source ZIP.
Validate a directory or ZIP without cloning ReTheme or installing Rust:
pnpm dlx @duxweb/retheme-theme-skill validate /absolute/path/to/my-theme
pnpm dlx @duxweb/retheme-theme-skill validate /absolute/path/to/my-theme.zipThe npm package invokes the compiled Rust validator for the current platform and writes one JSON report to stdout. Exit code 0 means valid, 1 means a protocol failure, and 2 means invalid CLI usage. Desktop, server, and authoring tools share the same protocol implementation; it is not WASM and is not duplicated in JavaScript or PHP.
Unknown fields, misspelled fields, duplicate styles, and duplicate slots fail validation.
| Field | Required | Contract |
|---|---|---|
schemaVersion |
yes | Exactly 1 |
id |
yes | Global lowercase reverse-domain ID; max 128; immutable after publication |
name |
yes | 1–120 characters in the base language |
description |
yes | 1–240 characters in the base language |
version |
yes | Strict SemVer |
author |
yes | { id, name }; name is 1–100 characters |
testedCodexVersions |
no | Versions actually tested; never a runtime lock |
styles |
yes | At least one unique package-relative CSS path |
slots |
yes | Unique entries from the 164-slot allowlist |
permissions |
yes | Exactly [] in v1 |
preview |
yes | background, surface, and accent as #RRGGBB |
experience |
yes | Engine-created copy and controlled assets |
locales |
no | Locale tag to localized copy |
access |
platform | Omit from source ZIPs |
integrity |
platform | Omit from source ZIPs |
signature |
platform | Omit from source ZIPs |
supportedCodexVersions is a legacy read alias. Do not use it in new themes. The target is called ChatGPT; the v1 field name remains testedCodexVersions for protocol stability.
slots declares the capabilities used by the theme. Controlled experience.assets entries must also be declared in slots. Current compatibility permits ordinary CSS to hit an undeclared runtime slot, but new themes must list every slot they use so review and migration remain deterministic.
experience.homeHero is required:
| Property | Contract |
|---|---|
eyebrow |
1–80 characters |
title |
1–120 characters |
description |
1–240 characters |
asset |
Package image path |
fit |
cover or contain |
position |
center, top, bottom, left, or right |
foreground |
Optional transparent foreground image |
divider |
Optional { label, asset? }; label is 1–80 characters |
The Hero is the first flow item in home.stage. A theme may define visual height or minimum height, but never detach it from document flow or move cards into it.
homePrompt.title optionally replaces the native home prompt and restores it when the theme stops.
conversationBanner is optional and uses the Hero image/layout fields except divider. The engine creates a stable conversation header above the message viewport and aligns it to the conversation content column. The theme owns height, colors, border, copy, and foreground visuals only.
| Manifest property | Runtime slot | Parent |
|---|---|---|
composerSubmit |
composer.submit.decoration |
Submit button |
composerDecoration |
composer.decoration |
Composer surface |
conversationSummaryDecoration |
conversation.summary.decoration |
Summary/task card |
sidebarSectionDecoration |
sidebar.section.decoration |
Project section |
Each value is { "asset": "assets/file.svg" }. Mounts are non-interactive and must not obscure controls.
Each experience.assets entry uses { "slot", "asset" }. Optional lightAsset and darkAsset paths provide appearance-specific images; the engine switches them in place and falls back to asset when a matching variant is absent.
experience.assets accepts only:
app.background, main.background, main.overlay, main.frame, sidebar.brand.icon, sidebar.brand.badge, sidebar.header.background, sidebar.header.decoration, sidebar.frame, home.card.background, home.card.arrow.asset.
Declare each slot once in both experience.assets and top-level slots. Every declared path must reference an image inside the package; prefer compressed WebP for complex backgrounds.
experience.decorations may be omitted and defaults to []. It accepts decoration.top-right and decoration.bottom-right only.
| Limit | Value |
|---|---|
| Formats | SVG, PNG, JPEG, WebP |
| One image | 8 MiB |
| Other file | 1 MiB |
| Source ZIP | 30 MiB protocol maximum; the community endpoint may impose a lower upload limit |
| Extracted total | 60 MiB |
| File count | 256 |
ReTheme serves assets through a temporary loopback-only 127.0.0.1 service with a session token. Base64 is unnecessary. CSS url() is rejected; every image must enter through a controlled Manifest field.
SVG validation rejects scripts, foreignObject, event attributes, external URLs, and JavaScript URLs. Use WebP for complex illustrations, transparent WebP/PNG for foregrounds, and SVG for simple static vectors.
Read theme-banner-assets.md for exact home, compact Banner, and transparent foreground dimensions, safe areas, export checks, and Chinese/English generation prompts.
Every top-level selector begins with the exact theme root:
:root[data-ct-theme="studio.example.my-theme"] [data-ct-slot="sidebar"] {
background: var(--rt-sidebar-background) !important;
}Every entry in a selector list repeats the root. :is() and :where() may contain commas; the validator parses CSS with an AST.
Allowed selector attributes are data-ct-theme, data-ct-color-scheme, data-ct-view, data-ct-slot, data-ct-mount, role="button", and role="switch". Platform classes are limited to the documented ct-home-hero__* classes and ct-decoration. IDs, ChatGPT internal classes, data-app-*, and arbitrary attributes are rejected.
Allowed rule types are ordinary style rules, @media, and @keyframes. Animation names start with ct-. CSS nesting, @import, external URLs, every url(), expression(), javascript:, and </style are rejected.
Do not use font, font-family, font custom properties, or --ct-font*. Local font-size, font-weight, line-height, and letter-spacing remain available when readability requires them.
The engine owns structural display, positioning, scrolling, and responsive alignment for home, conversation, composer, page, and settings containers. If a design requires structural changes, request a stable engine slot instead of adding a selector workaround.
Keep theme-owned semantic variables under a unique prefix such as --rt-*:
:root[data-ct-theme="studio.example.my-theme"] {
--rt-app-background: #eef4ff;
--rt-surface: rgba(255, 255, 255, 0.84);
--rt-text: #18213d;
--rt-text-muted: #5e6885;
--rt-accent: #526ee8;
--rt-border: rgba(82, 110, 232, 0.22);
--rt-radius-card: 14px;
}
:root[data-ct-theme="studio.example.my-theme"][data-ct-color-scheme="dark"] {
--rt-app-background: #101521;
--rt-surface: rgba(28, 36, 55, 0.86);
--rt-text: #edf2ff;
}Do not redefine engine --ct-* variables except the documented Hero/foreground visual variables. Dynamic content width, titlebar safe area, summary width, and card count variables are read-only.
Both light and dark palettes are mandatory. Use data-ct-color-scheme, never prefers-color-scheme, to follow ChatGPT. Maintain WCAG contrast, visible focus and selection states, and readable translucent surfaces in both modes.
Provide one complete base language and override the other language in locales. ReTheme currently normalizes its interface locale to zh-CN or en.
Localizable values include theme name/description; Hero eyebrow/title/description/divider label; home prompt title; and conversation Banner eyebrow/title/description. Assets and layout are not duplicated by locale. Test overflow and wrapping in both languages at narrow widths.
Prefer transform and opacity; avoid layout-changing animation and continuous large-area filters. Decoration layers use pointer-events: none. Every animated theme provides reduced motion:
@media (prefers-reduced-motion: reduce) {
:root[data-ct-theme="studio.example.my-theme"] *,
:root[data-ct-theme="studio.example.my-theme"] *::before,
:root[data-ct-theme="studio.example.my-theme"] *::after {
animation: none !important;
transition-duration: 0.01ms !important;
}
}Test all combinations of Chinese/English and light/dark. On macOS and Windows, test narrow, medium, wide, and maximized windows, plus an open workspace panel.
Cover empty home, drafted home, expanded suggestion cards, new and historical conversations, long-message scrolling, summary/task cards, empty/single-line/multiline/attachment composers, sidebar states, every settings navigation section, controls and switches, menus and separators, code, inline code, diff, terminal, reduced motion, theme switching, timeout, and restoration.
Publication blockers include horizontal scroll, a Banner covering messages, composer relocation, unclickable controls, poor contrast, persistent settings flash, missing light/dark or localization coverage, hover flicker, scroll rebound, and leftover mounts after restoration.
Upload a source ZIP, not .ctheme. Remove unused files, verify that the ZIP root contains manifest.json, increment SemVer, and omit access, integrity, and signature.
The platform revalidates the same protocol, runs business/content review, renders a cover, normalizes the Manifest, creates integrity.json, signs it with Ed25519, and generates the downloadable .ctheme. Platform private keys, remote adaptation selectors, and compatibility records never belong in theme source.
Themes depend only on stable slots. ReTheme selects signed compatibility data by ChatGPT and engine version, so older and newer ChatGPT releases can coexist. testedCodexVersions records regression coverage and never locks compatibility.
When a slot disappears after a ChatGPT update, reproduce with another known-good theme. If multiple themes lose the same slot, update compatibility data or the engine. Never copy the new internal ChatGPT selector into a theme.
Install the complete Skill instead of asking an AI to reconstruct rules from this document:
pnpm dlx @duxweb/retheme-theme-skill installRestart Codex, then ask it to use retheme-theme-development to create, improve, or review a theme. The Skill loads its bundled protocol, slot, QA, Banner, and template references as needed and does not require a ReTheme repository checkout.