streamtoolset
DocsChecking session…

Themes

Build a theme for the toolset's widgets and submit it to the catalog. A theme is field overrides plus optional scoped CSS for one widget type — no JavaScript, no commerce, no AI-generated submissions. Hand-reviewed by the deutschmark team.

What a theme is

A theme customizes the look of a single widget type. It is two things: a set of field overrides (the same settings you see on a tool's page — colors, fonts, toggles) and an optional block of scoped CSS for finer control. Applying a theme merges those field values over your chosen browser source's current config; nothing else about the widget changes.

Themes cannot ship JavaScript. They are data and CSS only. That is the whole security model — a reviewer reads the JSON and the CSS, and there is no code to audit. A theme can never read your account, make network requests, or change a widget's behavior.

Themes are not Accents. Accents are the supporter dashboard skins (Silver, Gold, Nyan Cat) on the support page — internal, donor-only, and not community-submittable. Themes are open to anyone, free to every user, and apply to your widgets, not the dashboard.

The WidgetTheme format

A theme is a JSON file plus an optional CSS file in the same directory. The JSON validates against the widget-theme.schema.json schema shipped in the SDK.

{
  "schemaVersion": 1,
  "kind": "widget-theme",
  "widgetType": "chat-box",
  "name": "Monochrome Minimal",
  "author": "your-github-handle",
  "description": "High-contrast minimal chat, no chips.",
  "previewImage": "preview.png",
  "fields": {
    "nameColor": "#ffffff",
    "messageColor": "#c7ccd6",
    "bgColor": "transparent",
    "chipOpacity": 0,
    "showBadges": false,
    "fontFamily": "Space Grotesk"
  },
  "overridesCSS": "overrides.css"
}
  • widgetType — the widget the theme targets. Must be a known type (chat-box, death-counter, player, and others as they expose field schemas).
  • fields — a partial config. Every key must be one the widget actually exposes; unknown keys are rejected at review. Values outside a field's allowed range are coerced to the widget's default when applied, so stay inside the documented ranges.
  • overridesCSS — optional. A relative path to a CSS file in the theme directory. No @import, no url(https://…), no <script>, no external assets — everything bundles in the directory.
  • previewImage — a preview.png (max 512 KB) that honestly shows the theme applied.

Field overrides vs CSS overrides

Reach for field overrides first. They are the supported, stable surface — the same keys the widget's settings panel reads, so they survive widget updates. Use CSS only for what fields can't express (spacing, custom borders, subtle motion). CSS is scoped to the widget's root at apply time, so your selectors target inside that root.

The CSS selector contract

Field overrides are the primary, stable surface and you should reach for them first. When you do need CSS, each widget exposes a small set of stable class names on the elements you can target. Your overrides.css is scoped to the widget's root at apply time, so you write plain selectors against those classes. For the chat box, the documented hooks include:

  • .chat-line — one message row; .chat-line--stacked when name and message stack.
  • .chat-meta — the name + badges cluster.
  • .chat-message — the message body text.
  • .chat-name-chip and .chat-badges-chip — the chip backings behind the name and badges.

The full, authoritative selector list per widget lives in the SDK's docs/css-selectors.md. It is the contract: if a class is documented there, themes may rely on it; anything else is internal markup and may change without notice. Class names not on the list are off-limits — target only the documented hooks so your theme survives widget updates.

Build one locally

  1. Clone the themes repo: thedeutschmark/toolset-themes. The SDK — schema, examples, and contribution guide — lives there.
  2. Copy examples/my-first-theme/ to submissions/<your-handle>/<theme-slug>/ and edit theme.json.
  3. Validate against schema/widget-theme.schema.json and check your fields against the widget's documented keys.
  4. Add an honest preview.png and, if you wrote any, overrides.css.

Submit it

Submission is a GitHub pull request — there is no in-app submission form and no API. That friction is deliberate: it keeps the catalog small and the quality bar high.

  1. Fork thedeutschmark/toolset-themes and add your theme directory under submissions/.
  2. Open a PR titled like [theme] Monochrome Minimal — chat-box. Complete the PR checklist (it includes the no-AI attestation).
  3. Automated checks run first — schema validation, CSS safety, asset limits. Then a human reviews it.
  4. Accepted themes appear in the in-app Themes catalog, free to every user.

The rules

No AI-generated submissions.
AI-generated visuals, CSS, or copy are rejected without appeal. The PR checklist asks you to attest you wrote the theme yourself; the human review applies judgment. This is the brand, not a chokepoint.
No commerce.
There are no paid themes, tiers, or buy buttons. Once a theme is in the catalog it is free to every toolset user. What you do with your work off-platform is your business.
No JavaScript.
Themes are field values and CSS. No .js files, no <script> tags, no inline handlers. Submissions that include code are rejected automatically.
Hand-curated, no SLA.
Every accepted theme was read and accepted by a named team member. Review happens on no fixed schedule — we read every PR, but we don't commit to a turnaround time.

Reference

The format types and apply utilities live in the @deutschmark/theme-sdk package. The public toolset-themes repo carries the schema, examples, and the full contribution guide.