Ruleset Skins
A skin restyles Realm VTT for the campaigns that use your ruleset: windows and their headers, chat cards, the chat input, the drawer and hotbar, scene tools, character sheets, tag colors, chat macro and sheet buttons, the loading and pause screens, and the logo shown when no scene is open. A skin can bring its own colors, tiling textures (paper, leather, stone), fonts and images.
Skins belong to a ruleset. Once the ruleset is published, anyone can use its skins in any campaign. A ruleset can ship several skins and pick one as the default players see.
A skin is data only. Realm checks every value in it and builds the styles itself, so a skin can't run scripts or load files from anywhere but its own folder.
How players choose a skin
Each player picks a skin per ruleset. In Settings, under Appearance, Skin for all (ruleset name) campaigns applies to every campaign they play on that ruleset:
- Ruleset's default uses the skin the ruleset author picked.
- None (use Theme) keeps their regular Realm theme.
- Or pick any other skin they have access to.
Only that player sees their choice. While a skin is on, it replaces their Theme. When a GM creates a campaign on a ruleset with a default skin, the welcome window also asks whether to use it or keep their Realm theme; that answer is the same per-ruleset setting.
Making a skin
A skin is a folder with a skin.json manifest and the images and fonts it uses:
my-skin/
skin.json
frames/paper.png
frames/leather.png
fonts/MyFont-Regular.woff2
fonts/MyFont-Bold.woff2File paths in skin.json are relative to this folder. Images can be png, jpg, webp or avif; fonts can be woff2, woff, ttf or otf. Other files in the folder, such as a font license, are skipped when you upload. Only use art and fonts you have the rights to share: a published skin is public.
A minimal skin:
{
"name": "Parchment",
"colorScheme": "dark",
"fonts": [{ "family": "MyFont", "src": "fonts/MyFont-Regular.woff2" }],
"fontRoles": { "heading": "MyFont", "windowTitle": "MyFont" },
"frames": {
"chatCard": {
"texture": { "src": "frames/paper.png", "scale": 2 },
"textColor": "#191813",
"scheme": "light",
"border": "#6c6964",
"radius": 5
},
"sheetCorner": { "hidden": true }
}
}Uploading a skin
From the ruleset editor
Open your ruleset in the Ruleset Editor and choose the Skins tab:
- Upload skin folder reads a folder with
skin.json, checks it, and uploads it. Use a new id to add a skin, or an existing id to replace that skin. - Preview applies a skin while you stay in the editor.
- Make default sets the skin players see unless they pick another one.
- Delete removes a skin. Campaigns that used it go back to their normal look.
Only the ruleset's owner can upload, replace or delete its skins.
With the Ruleset Compiler
If you keep your ruleset in a folder and push it with the Ruleset Compiler, put each skin in a skins/ folder next to ruleset.config.json. The folder name is the skin's id (lowercase letters, digits and dashes):
my-ruleset/
ruleset.config.json
skins/
parchment/
skin.json
frames/paper.pngEvery push uploads the skins after the ruleset, with your login or with a deploy token from CI. The build fails if a skin.json is missing, is not valid JSON, or points at a file that isn't in its folder. Pass --skip-skins to push only the ruleset, and --dry-run to see which skins would upload.
Setting the default skin
Set the default in ruleset.config.json with the skin's id:
{ "settings": { "skin": "parchment" } }or use Make default in the editor's Skins tab.
skin.json reference
Colors can be hex (#5e0000), rgb(), rgba() or transparent. Sizes are in CSS pixels.
Top-level fields
Field | What it does |
|---|---|
| Display name (required) |
|
|
| Short description |
|
|
| Sheet colors: |
| Up to 12 fonts: |
| Which font each role uses: |
| Default corner radius for inputs and buttons |
| How each part of the screen is drawn (below) |
| Frames for one record type's windows (below) |
| Tag colors by tag text (below) |
| Chat macro button looks (below) |
| Looks for sheet buttons the ruleset tags with |
|
|
Frames
frames maps a part of the screen (a slot) to how it is drawn. Leave a slot out to keep the normal look.
Area | Slots |
|---|---|
Windows |
|
Chat |
|
Drawer and app bar |
|
Scene |
|
Hotbar |
|
Combat tracker |
|
Sheets |
|
Loading and pause |
|
Buttons |
|
A frame can set:
Field | What it does |
|---|---|
| Solid color |
|
|
|
|
| Border color, and width (up to 8) |
| Corner radius (up to 32) |
| Drop shadow color |
| Square size of a control or art box (12 to 320), and the icon size inside it |
| Space between children (up to 32), and the inset as |
| Text color (it also recolors the Realm logo in |
| Hide the element, such as the corner marks on sheets: |
| A frame layered on while the pointer is over the element |
| Recolor everything inside the frame. A light parchment area in a dark skin sets |
Record types
recordTypes styles one record type's windows, using the ruleset's record type names. For example, crimson headers for characters and NPCs but not items:
"recordTypes": {
"characters": { "frames": { "windowHeader": { "background": "#5e0000" } } },
"npcs": { "frames": { "windowHeader": { "background": "#5e0000" } } }
}The slots you can use here are window, windowHeader, tabList, tab, tabActive and recordBody.
Tags
Tags in chat and on sheets (traits, ranges, rarity) can be colored by their text, so your ruleset doesn't have to send any colors. Every tag gets base plus the default variant, and match picks a variant by the tag's text, in lowercase. A * at the start or end matches any text there. A variant the ruleset sends on the tag wins over matching, and a color the ruleset sets on the tag wins over everything.
"tags": {
"base": { "radius": 2, "textColor": "#ffffff", "fontWeight": 600, "uppercase": true },
"variants": {
"trait": { "background": "#786452" },
"action": { "background": "#5e0000" },
"uncommon": { "background": "#98513d" },
"rare": { "background": "#002664" }
},
"default": "trait",
"match": {
"action": ["attack", "* save", "concentrate"],
"uncommon": ["uncommon"],
"rare": ["rare"]
}
}Macro buttons
These style the macro buttons in chat messages (the buttons a ruleset adds with a macro fence, such as Roll Damage or Apply Half Damage).
Field | What it does |
|---|---|
| The base frame for every macro button |
|
|
|
|
| The look for macros that apply an effect or condition, detected from the macro's code |
| Named looks, matched by macro name: lowercase, spaces as underscores, |
| One macro's icon and a shorter label, keyed by macro name |
Icons are Realm icon names: Tabler icons (IconHeartBroken), game-icons (GiShield), dice (d20), or damage types (fire, cold, acid). A variant's icon applies to matched buttons that don't have their own.
"macroButtons": {
"style": { "background": "#d8d5ca", "border": "#7a7971", "textColor": "#191813" },
"layout": "stacked",
"row": true,
"kinds": { "effect": { "background": "#5e0000", "textColor": "#ffffff", "icon": "IconSparkles" } },
"variants": {
"attack": { "background": "#3a2e26", "textColor": "#f4ecd8", "icon": "d20" },
"damage": { "background": "#f1e6cf", "textColor": "#5e0000", "icon": "IconDice5" }
},
"match": { "damage": ["*_damage"], "attack": ["roll_attack*"] },
"buttons": {
"Roll_Fire_Damage": { "icon": "fire" },
"Apply_Half_Damage": { "icon": "IconHeartMinus", "label": "Half" }
}
}Sheet buttons
These style buttons on your ruleset's sheets, such as Damage, Cast or Use. Tag a <button> or <iconbutton> in your sheet HTML with a skin attribute naming a look (lowercase letters, digits and dashes):
<button label="Damage" skin="damage" onclick="rollDamage(false)"></button>
<button label="CAST" skin="use" onclick="onCastSpell()"></button>
<iconbutton skin="use" options='[{"label": "Send to Chat", "icon": "IconSend", "value": "send"}]'></iconbutton>In skin.json, sheetButtons.base applies to every tagged button and sheetButtons.variants holds one frame per name. Without a skin, or with a skin that doesn't name that variant, the button keeps its normal look.
"sheetButtons": {
"base": { "textColor": "#ffffff", "border": "rgba(0, 0, 0, 0.5)", "radius": 3, "fontWeight": 500 },
"variants": {
"attack": { "background": "#171f69", "hover": { "background": "#242f8f" } },
"use": { "background": "#171f69", "hover": { "background": "#242f8f" } },
"damage": { "background": "#5e0000", "hover": { "background": "#7a1010" } }
}
}The icon and label follow the button's textColor.
Limits
- Up to 48 files per skin, 4 MB per file and 16 MB in total;
skin.jsonup to 200 KB. - Up to 12 fonts, 32 record types, 24 tag variants, 400 tag patterns, 64 macro buttons and 24 sheet button variants. Macro labels up to 24 characters.
- Each upload creates a new version with fresh file URLs, so players never see a mix of old and new art.
Tips
- Start from the default skin of a ruleset you like, and change one area at a time.
- Use the editor's Preview to check a skin before you make it the default.
- Textures should tile seamlessly. Export them at twice the size you want them drawn and keep
scaleat 2. - If a skin doesn't apply, the browser console shows what Realm rejected in its
skin.json.