CustomizeThemes

Themes

A theme is data that sets how a page looks: nine colors, a background, font roles, shape and section layout. No CSS, no code.

The same theme JSON is rendered by the Taste app and by taste.dev, so both always match. Themes carry no code and no CSS, which is what makes it safe for anyone to publish one: every value is a hex color, a number in a fixed range, or one of a closed set of words.

The schema is theme-1.json (Schemas). The default theme is terminal.

Pick a theme

List the built-insbash
curl https://api.taste.dev/themes \
  -H "Authorization: Bearer $TOKEN"
Pick onebash
curl -X PUT https://api.taste.dev/profile/theme \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"theme_id": "studio"}'

GET /themes returns {"themes": [<theme>, …]} in picker order: terminal, studio, pearl. PUT /profile/theme returns 200 {"theme_id": "studio"}. An id that isn't a built-in returns 400 {"error": "invalid"}.

Your page carries the full resolved theme in GET /public/u/{username}, so a renderer never needs a second request.

Fields

Every field is required except description, background.angle and background.image. Objects allow no extra keys.

schema1required

The theme schema version. Only 1 exists today.

idstringrequired

1–40 lowercase letters, digits and inner hyphens: ^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$.

namestringrequired

Display name, 1–40 characters.

authorstringrequired

The author's Taste username (taste for built-ins), 1–30 of a-z 0-9 . _ -. Pages by other authors show "Theme by @author".

descriptionstring

Up to 140 characters.

modeenumrequired

light or dark. The status bar, browser chrome and system controls follow it.

colorsobjectrequired

Nine flat #RRGGBB colors. See Colors.

backgroundobjectrequired

The page background. See Background and effects.

effectsobjectrequired

scanlines, grain and glow. See Background and effects.

typeobjectrequired

Font roles and heading style. See Type.

shapeobjectrequired

Card radius, avatar shape, border width. See Shape.

layoutobjectrequired

Section order and each section's variant. See Layout.

Colors

KeyUsed for
backgroundThe page
background2The second page tone (bottom of the page, alternating rows)
surfaceCards, link buttons, chips
lineHairlines and card borders
textBody text
mutedSecondary text
accentButtons, selected states, links. Always flat: selected and pressed states never flip to a gradient
onAccentText on the accent
highlightThe @handle and section headings

All nine must be 6-digit hex (#39FF6A). No alpha, no names, no gradients.

Background and effects

FieldValues
background.typesolid, gradient or image
background.colors1–4 hex colors. solid uses the first; gradient blends all of them (use 2–4)
background.angleGradient angle in degrees, 0–360. Renderers use 180 (top to bottom) when it's missing
background.imageA URI. Only Taste-hosted uploads are rendered; anything else falls back to background.colors[0]
effects.scanlinestrue draws faint CRT scanlines
effects.grainFilm grain opacity, 0–0.15
effects.glowtrue adds a soft glow in the highlight color

On taste.dev an image background never sits directly under text: the page content moves onto a surface card.

Type

Three font roles, each picked from a short list:

FieldValuesSets
type.displaysans, serif, mono, pixelThe name
type.bodysans, serif, monoBody text
type.labelsans, mono, pixelThe @handle, section headings and small labels
type.headingStyleprompt, label, noneHow section headings read

Each role maps to a font on each platform:

RoleApp (iOS)Web
sansSF Pro (system)Inter
serifNew York (system serif)Instrument Serif
monoSF Mono / MenloJetBrains Mono
pixelVT323 (bundled)VT323

Heading styles:

headingStyleA section heading reads
prompt> building
labelBUILDING, in the highlight color
noneNo headings (sections keep their accessible names)

Shape

FieldValues
shape.radiusCard corner radius in px, 0–32
shape.avatarcircle, rounded or square
shape.borderCard border width in px, 0–2

Layout

FieldValues
layout.orderThe sections, in order, no repeats, at least one: building, tools, links, projects, pay_it_forward, topics, here_for, badges
layout.linksbuttons (full-width rows), grid (two-up tiles) or chips
layout.projectscards (screenshot on top) or list (thumbnail rows)
layout.toolstiles (logo tiles) or chips (logo + name)

A theme decides how a page looks, never what it hides. Sections left out of order still render, after the listed ones. A builder hides sections in their profile settings, not through a theme.

Contrast rules

A theme is checked for contrast (WCAG ratio) when it's accepted:

  • text and muted each reach 4.5:1 against both surface and background.
  • onAccent reaches 4.5:1 against accent.

highlight, line and background2 aren't checked, so don't use highlight for body text. The built-ins all pass. The check lives in the API's theme validator, which will gate community themes (Contributing themes).

Built-in themes

Terminal

Green phosphor on a dark tube. The default.

terminal.jsonjson
{
  "schema": 1,
  "id": "terminal",
  "name": "Terminal",
  "author": "taste",
  "description": "Green phosphor on a dark tube. The original.",
  "mode": "dark",
  "colors": {
    "background": "#020803",
    "background2": "#06120B",
    "surface": "#0B1510",
    "line": "#1D2E24",
    "text": "#EAF3EE",
    "muted": "#8AA597",
    "accent": "#AEF8D0",
    "onAccent": "#04170E",
    "highlight": "#39FF6A"
  },
  "background": { "type": "gradient", "colors": ["#06120B", "#020803"], "angle": 180 },
  "effects": { "scanlines": true, "grain": 0, "glow": true },
  "type": { "display": "sans", "body": "sans", "label": "pixel", "headingStyle": "prompt" },
  "shape": { "radius": 18, "avatar": "circle", "border": 1 },
  "layout": {
    "order": ["building", "tools", "links", "projects", "pay_it_forward", "topics", "here_for", "badges"],
    "links": "buttons",
    "projects": "cards",
    "tools": "tiles"
  }
}
Studio

Clean and light, with a studio-green accent.

studio.jsonjson
{
  "schema": 1,
  "id": "studio",
  "name": "Studio",
  "author": "taste",
  "description": "Clean and light, with a studio-green accent.",
  "mode": "light",
  "colors": {
    "background": "#F5F7F4",
    "background2": "#EEF2EE",
    "surface": "#FFFFFF",
    "line": "#DCE6DF",
    "text": "#0E1A14",
    "muted": "#54665C",
    "accent": "#14634B",
    "onAccent": "#FFFFFF",
    "highlight": "#14634B"
  },
  "background": { "type": "solid", "colors": ["#F5F7F4"] },
  "effects": { "scanlines": false, "grain": 0, "glow": false },
  "type": { "display": "sans", "body": "sans", "label": "sans", "headingStyle": "label" },
  "shape": { "radius": 20, "avatar": "circle", "border": 1 },
  "layout": {
    "order": ["building", "tools", "links", "projects", "pay_it_forward", "topics", "here_for", "badges"],
    "links": "buttons",
    "projects": "cards",
    "tools": "tiles"
  }
}
Pearl

Soft pearl light, a little anime. Serif name, rounded avatar, tools as chips.

pearl.jsonjson
{
  "schema": 1,
  "id": "pearl",
  "name": "Pearl",
  "author": "taste",
  "description": "Soft pearl light, a little anime.",
  "mode": "light",
  "colors": {
    "background": "#FBF8FF",
    "background2": "#F1F6FF",
    "surface": "#FFFFFF",
    "line": "#E6E0F5",
    "text": "#1B1730",
    "muted": "#6B6485",
    "accent": "#6D5BD0",
    "onAccent": "#FFFFFF",
    "highlight": "#6D5BD0"
  },
  "background": { "type": "gradient", "colors": ["#FFF1F8", "#F1F6FF", "#EFFBF6"], "angle": 160 },
  "effects": { "scanlines": false, "grain": 0.04, "glow": false },
  "type": { "display": "serif", "body": "sans", "label": "sans", "headingStyle": "label" },
  "shape": { "radius": 26, "avatar": "rounded", "border": 1 },
  "layout": {
    "order": ["building", "links", "projects", "tools", "pay_it_forward", "topics", "here_for", "badges"],
    "links": "buttons",
    "projects": "cards",
    "tools": "chips"
  }
}

Preview any of them on the sample page: taste.dev/@demo?theme=studio.

Fallback

A broken theme must never break a page.

  • Unknown theme id. If a page's stored theme no longer exists, the API sends terminal. theme in a public page is always a complete theme.
  • Unreadable theme. A renderer that can't read a theme (wrong schema, a bad color, a missing field) renders terminal. taste.dev does exactly this.
  • Unknown enum value. A value newer than your renderer (say a future type.display font) should render as that field's default: the value terminal uses. See Versioning.