i99dash docs
Themes

ThemeManifest

The theme.json schema — identity, catalog metadata, the compatibility gate, and the inline ThemeSpec the car paints.

The full schema for a theme's theme.json — the durable identity + catalog metadata, plus the inline spec the car paints. It mirrors MiniAppManifest field-for-field where the concept overlaps, with two deliberate differences:

  • No url — a theme is not a WebView. Instead an inline spec, so a catalog tile can render a palette preview without downloading the bundle.
  • No permissions / privileged / safeWhileDriving — a theme only paints; it has no host capability or driving surface.

Minimal valid manifest

{
  "id": "midnight-neon",
  "name": { "en": "Midnight Neon", "ar": "نيون منتصف الليل" },
  "icon": "./icon.png",
  "version": "1.0.0",
  "category": "neon",
  "spec": {
    "schema": 1,
    "brightness": "dark",
    "colors": {
      "background": "#07070D",
      "surfaceLow": "#0F1018",
      "surfaceContainer": "#13141C",
      "surfaceHigh": "#1A1C26",
      "outline": "#4B5064",
      "outlineVariant": "#24262F",
      "onSurface": "#F3F4F8",
      "onSurfaceVariant": "#8A90A4",
      "accent": "#22D3A8",
      "secondary": "#5B8CFF",
      "error": "#E76F51"
    }
  }
}

That is the floor: identity, one icon, a category, and a spec whose colors carries all 8 surfaces plus accent/secondary/error.

Field-by-field

Identity

FieldRequiredNotes
idyesURL-safe, 2–64 chars, lowercase alphanumeric / - / _, must not start with a separator (^[a-z0-9][a-z0-9_-]{1,63}$). Globally unique and immutable — the car persists it as the active-theme selection. Never rotate post-publish; bump version instead.
versionyesOpaque, semver-shaped by convention. Bump per release to bust the bundle/CDN cache.
minHostVersionnoCars below this version hide the theme. Omit for "any".

Display

FieldRequiredNotes
nameyesLocale map — at least one entry. Catalog renders the tile title from it. Fallback: requested locale → en → first entry.
descriptionnoSame locale-map shape as name. UI degrades gracefully when absent.
iconyesBundle-relative path (./...), PNG or SVG, 256×256, ≤ 100 KB. Publish rewrites to a CDN URL.
coverImageno16:9 cover for the detail surface. PNG / JPEG / WebP / SVG, 1280×720, ≤ 500 KB.
screenshotsnoUp to 8 images. PNG / JPEG / WebP / SVG, ≤ 1920×1080, ≤ 800 KB each. Order is publisher-chosen.

Catalog placement

FieldRequiredNotes
categoryyesClosed enum of 11 slugs — see Categories. Drives the catalog grouping.
tagsnoFree-form, lowercase alphanumeric + hyphens (^[a-z0-9-]+$), ≤ 24 chars each, ≤ 8 tags. Search/filter only.

Compatibility + spec

FieldRequiredNotes
requiresnoHard car/host compatibility requirements. Reuses the mini-app requires block verbatim, so evaluateCompatibility() applies the same gate — the catalog hides, and the car refuses to apply, an incompatible theme. Omit for "applies to any car".
specyesThe inline ThemeSpec design-token document — the payload the car actually paints. Embedded in the catalog row so a tile previews the palette without the bundle.

Catalog wire shape

When the backend serves a theme it adds a distribution envelope to each manifest (asset paths already rewritten to CDN URLs), mirroring the mini-app catalog entry. The catalog body is raw (not wrapped in a { success, data } envelope):

// GET /api/v1/themes        (public)
// GET /api/v1/themes/me     (authenticated; includes the user's beta themes)
{
  "themes": [
    {
      /* ...all ThemeManifest fields, asset paths rewritten to CDN URLs... */
      "bundleUrl": "https://cdn.i99dash.app/themes/midnight-neon/1.0.0/bundle.tar.gz",
      "bundleSha256": "<64 hex>",
      "track": "production",
      "releaseNotes": null
    }
  ]
}

Query params mirror mini-apps: limit (1–200, default 50), offset (≥ 0), category (optional slug). The endpoint supports If-None-Match

  • ETag + 304.

On this page