MarsDawn

Make a theme with your agent

A reference for an AI agent that makes a MarsDawn preview theme. The agent writes one theme.json, checks and renders it with the marsdawn command-line tool, and hands you a link. You tune it by eye on the Build a theme page and submit it. The field tables below are read from the kit's own source, and a check keeps them equal to it.

Write one theme.json, run marsdawn theme validate theme.json --require-complete --json until it exits 0, render it with marsdawn theme preview in light and dark, then give the person a link. A theme is parameters only. It never contains CSS.

What a theme is

A key that is not in the tables below is an error, and so is a key written twice. The file is at most 16,384 bytes.

Check and render

Run these in the folder that holds theme.json. They need the marsdawn tool: see marsdawn for agents.

marsdawn theme validate theme.json --require-complete --json
marsdawn theme css theme.json --json
marsdawn theme preview theme.json --appearance light -o light.png
marsdawn theme preview theme.json --appearance dark -o dark.png

Start from a built-in

Copy one of the four built-in themes and change it. Each is a complete, valid file:

Example: Dawn

This is dawn.json, byte for byte. Its only style option is a thin accent rule for hr.

{
  "schemaVersion": 1,
  "id": "dawn",
  "version": "1.0.0",
  "name": { "en": "Dawn" },
  "summary": { "en": "Warm Martian sunrise" },
  "fontDesign": "sans",
  "scenarios": ["agent-review"],
  "light": {
    "background": "#FFFDFB", "surface": "#F6F0EC", "text": "#26211F", "muted": "#6F6660",
    "border": "#EADFD8", "heading": "#26211F", "accent": "#C8471B", "link": "#B03C0C", "quote": "#D97A4A",
    "syntax": { "keyword": "#B03C0C", "string": "#2F6F5E", "comment": "#746B66", "number": "#9A5B00", "function": "#5B3E8C", "type": "#1F6FB2" },
    "diagram": { "node": "#FBE9E0", "nodeBorder": "#CA6C3C", "text": "#26211F", "line": "#8A6A5C", "secondary": "#E6F1EE", "tertiary": "#FFF6F1", "note": "#FFF1D6" }
  },
  "dark": {
    "background": "#1C1A1F", "surface": "#262229", "text": "#EBE4DF", "muted": "#A39992",
    "border": "#3A343A", "heading": "#F4EEEA", "accent": "#FF8A50", "link": "#FF9E6B", "quote": "#E08A5C",
    "syntax": { "keyword": "#FF9E6B", "string": "#7FD1B9", "comment": "#938A84", "number": "#F2C572", "function": "#C7A6F5", "type": "#6CB6FF" },
    "diagram": { "node": "#3A2A26", "nodeBorder": "#E08A5C", "text": "#EBE4DF", "line": "#B8A69C", "secondary": "#23332F", "tertiary": "#2B2427", "note": "#3D3322" }
  },
  "style": {
    "hr": { "style": { "type": "line", "color": "accent", "thickness": 1 } }
  }
}

Fields

Every object below is read the same way. required means the key must be present, and a key that is not listed is an error.

Types: string, integer, number, boolean, map (language tag to text), enum (one of the listed values), role (a palette role) and object (the table it links to). A [] after a type means a list.

theme.json

FieldTypePresenceValues and notes
schemaVersionintegerrequiredAlways 1.
idstringrequired1 to 32 characters: lowercase letters and digits, in runs joined by single hyphens, such as calm-green.
versionstringrequiredMAJOR.MINOR.PATCH, such as 1.0.0.
namemaprequiredLanguage tag to text. en is required; other keys are language tags such as zh-Hant. Each text is at most 48 characters, with no links or line breaks.
summarymaprequiredThe same shape as name, at most 120 characters per text.
fontDesignenumrequiredsans, serif, rounded
scenariosenum[]requiredagent-review, technical-docs, formal-output, notes-sharing — One or two, no repeats. They set how much contrast the theme must reach.
authorobjectoptionalauthor
licensestringoptionalAn SPDX identifier, such as Apache-2.0. Submitted themes are released under Apache-2.0.
lightobjectrequiredlight, dark
darkobjectrequiredlight, dark
styleobjectoptionalstyle

author

FieldTypePresenceValues and notes
namestringrequiredAt most 64 characters, with no links.
githubstringoptionalA GitHub login. For a submission, set it to the submitter's own.

light, dark

FieldTypePresenceValues and notes
backgroundstringrequired# and six hex digits, such as #1C1C1C.
surfacestringrequired# and six hex digits, such as #1C1C1C.
textstringrequired# and six hex digits, such as #1C1C1C.
mutedstringrequired# and six hex digits, such as #1C1C1C.
borderstringrequired# and six hex digits, such as #1C1C1C.
headingstringrequired# and six hex digits, such as #1C1C1C.
accentstringrequired# and six hex digits, such as #1C1C1C.
linkstringrequired# and six hex digits, such as #1C1C1C.
quotestringrequired# and six hex digits, such as #1C1C1C.
syntaxobjectoptional (required to publish, with --require-complete)syntax
diagramobjectoptional (required to publish, with --require-complete)diagram

syntax

FieldTypePresenceValues and notes
keywordstringrequired# and six hex digits, such as #1C1C1C.
stringstringrequired# and six hex digits, such as #1C1C1C.
commentstringrequired# and six hex digits, such as #1C1C1C.
numberstringrequired# and six hex digits, such as #1C1C1C.
functionstringrequired# and six hex digits, such as #1C1C1C.
typestringrequired# and six hex digits, such as #1C1C1C.

diagram

FieldTypePresenceValues and notes
nodestringrequired# and six hex digits, such as #1C1C1C.
nodeBorderstringrequired# and six hex digits, such as #1C1C1C.
textstringrequired# and six hex digits, such as #1C1C1C.
linestringrequired# and six hex digits, such as #1C1C1C.
secondarystringrequired# and six hex digits, such as #1C1C1C.
tertiarystringrequired# and six hex digits, such as #1C1C1C.
notestringrequired# and six hex digits, such as #1C1C1C.

style

FieldTypePresenceValues and notes
bodySizenumberoptionalrange
lineHeightnumberoptionalrange
headingWeightnumberoptionalrange
radiusnumberoptionalrange
maxWidthnumberoptionalrange
h1objectoptionalstyle.h1
h2objectoptionalstyle.h2
blockquoteobjectoptionalstyle.blockquote
hrobjectoptionalstyle.hr
tableobjectoptionalstyle.table
listMarkerroleoptionala palette role
inlineCoderoleoptionala palette role — Only text or a syntax role: keyword, string, comment, number, function or type.
linkobjectoptionalstyle.link
syntaxobjectoptionalstyle.syntax

style.h1

FieldTypePresenceValues and notes
sizenumberoptionalrange
letterSpacingnumberoptionalrange
alignenumoptionalleft, center
decorationobjectoptionaltype

style.h2

FieldTypePresenceValues and notes
letterSpacingnumberoptionalrange
decorationobjectoptionaltype
italicbooleanoptionaltrue, false

style.blockquote

FieldTypePresenceValues and notes
styleobjectoptionaltype
italicbooleanoptionaltrue, false

style.hr

FieldTypePresenceValues and notes
styleobjectoptionaltype

style.table

FieldTypePresenceValues and notes
headerobjectoptionaltype
verticalRulesbooleanoptionaltrue, false
roundedbooleanoptionaltrue, false
FieldTypePresenceValues and notes
underlinebooleanoptionaltrue, false

style.syntax

FieldTypePresenceValues and notes
boldKeywordsbooleanoptionaltrue, false

Options with a type

These style options are objects with a type. Each type takes the fields listed for it and no others.

OptiontypeFieldPresenceType
style.h1.decorationrule———
style.h1.decorationnone———
style.h1.decorationshortRulecolorrequiredrole
style.h1.decorationgradientBarfromrequiredrole
style.h1.decorationgradientBartorequiredrole
style.h2.decorationrule———
style.h2.decorationnone———
style.h2.decorationdotcolorrequiredrole
style.blockquote.stylebarwidthoptionalnumber range
style.blockquote.stylepanel———
style.hr.stylelinecoloroptionalrole
style.hr.stylelinethicknessoptionalnumber range
style.hr.styleshortCenteredcolorrequiredrole
style.hr.stylegradientcolorsrequiredrole[] Two or three roles.
style.table.headersurface———
style.table.headeraccentRulecolorrequiredrole
style.table.headerfilledbackgroundrequiredrole
style.table.headerfilledtextrequiredrole
style.table.headerfilledborderoptionalrole

Palette roles

A palette role is one of these names:

background, surface, text, muted, border, heading, accent, link, quote, keyword, string, comment, number, function, type

Numbers

A number is snapped to the nearest multiple of its step, and must then lie between min and max. Otherwise the rule is number.range.

FieldMinMaxStep
style.bodySize14181
style.lineHeight1.41.90.05
style.headingWeight40090050
style.radius0161
style.maxWidth60010001
style.h1.size1.82.40.1
style.h1.letterSpacing-0.030.030.005
style.h2.letterSpacing-0.030.030.005
style.blockquote.style.width241
style.hr.style.thickness141

What else is checked

The Build a theme page opens a theme from a link, so the person can tune it by eye. The link is that page's address, then #theme=, then the file's bytes in base64url without padding. The file can be at most 16,384 bytes. This one line builds the link in zsh and in bash, on macOS and Linux:

printf 'https://marsdawn.southern-light.dev/themes/new/#theme=%s\n' "$(base64 < theme.json | tr -d '\n=' | tr '+/' '-_')"

The theme travels after a #, which a browser does not send to the site, and the site does not send it anywhere. The link does stay in your shell history and in the browser's history, and anyone you paste it to can read the theme.

Submit it

The agent prepares the file and the link. The person submits. Both routes ask for the Developer Certificate of Origin and the Apache-2.0 release, and the person makes that certification: the agent does not tick the box or sign off for them.

A submitted theme's author name, name, summary and GitHub login become public permanently, so set author.name to a name the person is happy to publish.

Published themes

To see what published themes look like, browse the theme gallery.