🎨 Theme authoring guide
Learn the ForcedSkin theme JSON schema and palette field rules—design palettes for any site and submit them for the community.
Quick start
Choose a mode
Decide whether the theme runs in light (light palettes) or dark (dark palettes).
Fill in colours
Follow the field specification below — at minimum all required keys must be present in your JSON.
Submit for review
On the Themes page click “Submit Theme”. Admins approve before it appears for everyone.
Colour fields
All palette entries must be hex (#RRGGBB) strings or CSS rgba(...) values.
backgroundRequiredPrimary page background—light themes lean near white, dark themes near deep neutrals. CSS var: --color-background
Example: #F8FFF8 / #101410
foregroundRequiredPrimary text—readably contrasted against background. CSS var: --color-foreground
Example: #2C3E2C / #E0E0E0
surfaceRequiredCard/panel/container fill—slightly lifted from background. CSS var: --color-surface
Example: #F0FFF0 / #1E221E
surfaceMutedOptionalMuted hovers/sub-containers stepped from surface. CSS var: --color-surface-muted
Example: #F5FDF5 / #161816
borderRequiredHairlines & dividers—typically low saturation greys/semi-transparent tones. CSS var: --color-border
Example: #D8E8D8 / #333633
mutedRequiredDe-emphasised copy & placeholders. CSS var: --color-muted
Example: #6C7E6C / #A0A0A0
primaryRequiredPrimary brand ramp for buttons/links/focus—can be single colour or ramp (50–950). Requires at least shades 500/600/700. Maps to CSS variables --color-primary- plus shade tokens (50–950)
Example: { "500": "#4CAF50", "600": "#43A047", "700": "#388E3C" }
secondaryOptionalSecondary accent ramps for chips/ghost buttons—structure matches primary—maps to --color-secondary- plus shade tokens
Example: { "500": "#AAA", ... }
accentOptionalHighlights, badges, spotlight colours—structure matches primary—maps to --color-accent- plus shade tokens
Example: { "500": "#FFEB3B", ... }
varsOptionalArbitrary CSS custom properties—keys omit “--”; values applied verbatim on :root
Example: { "custom-radius": "8px" }
Colour ramps
The JSON keys primary, secondary, and accent each accept either authoring style below:
Compact (three core shades)
{
"primary": {
"500": "#4CAF50",
"600": "#43A047",
"700": "#388E3C"
}
}The engine promotes shade 500 as the default hue; 600/700 cover hover/active affordances.
Full ramp (50 → 950, eleven stops)
{
"primary": {
"50": "#E8F5E9",
"100": "#C8E6C9",
...
"500": "#4CAF50",
...
"950": "#0F3D12"
}
}Publishing the complete ramp yields smoother tonal transitions everywhere.
Full samples
🌿 Light sample (light-mint)
{
"background": "#F8FFF8",
"foreground": "#2C3E2C",
"surface": "#F0FFF0",
"surfaceMuted": "#F5FDF5",
"border": "#D8E8D8",
"muted": "#6C7E6C",
"primary": {
"50": "#E8F5E9",
"100": "#C8E6C9",
"200": "#A5D6A7",
"300": "#81C784",
"400": "#66BB6A",
"500": "#4CAF50",
"600": "#43A047",
"700": "#388E3C",
"800": "#2E7D32",
"900": "#1B5E20",
"950": "#0F3D12"
},
"secondary": {
"500": "#AAAAAA",
"600": "#999999",
"700": "#777777"
},
"accent": {
"500": "#FFEB3B",
"600": "#FDD835",
"700": "#FBC02D"
}
}🌑 Dark sample (dark-forest)
{
"background": "#101410",
"foreground": "#E0E0E0",
"surface": "#1E221E",
"surfaceMuted": "#161816",
"border": "#333633",
"muted": "#A0A0A0",
"primary": {
"500": "#4A9B6B",
"600": "#3F855C",
"700": "#346F4D"
}
}Custom variables (vars)
If default tokens fall short, add extra CSS vars under a vars node—omit the -- prefix in each key.
{
"background": "#101010",
"foreground": "#E0E0E0",
...
"vars": {
"radius-card": "16px",
"shadow-card": "0 2px 12px rgba(0,0,0,0.4)"
}
}Slug rules (name)
- Only lowercase ASCII letters, digits, and hyphen (
a-z 0-9 -). - Prefer concise “mode-style” pairs such as “dark-ocean” or “light-sakura”.
- Slugs must be globally unique—they cannot change after publishing, so double-check beforehand.
- Maximum length 50 characters.
Submission & review
🔐 Sign in before submitting.
⏳ Submissions queue for review—staff typically respond within 1–3 working days.
✅ Approved themes appear in the marketplace for everyone to favourite.
❌ Invalid colours, conflicting names, or policy breaches will be rejected—delete and submit a corrected version.
🚫 No malicious code, hate content, or obviously infringing palettes.
Ready? Head to the theme marketplace and publish your palette.
🎨 Submit a theme