Skins let you customize how CA:DE looks — changing panel graphics, colors, icons, and more. This guide walks you through creating your first skin from scratch.
A skin is a folder (or ZIP file) containing:
skin.json) that describes your skin and what it changesSkins are layered on top of the default skin. You only need to include what you want to change — everything else falls back to the default.
CA:DE automatically discovers skins placed in its data directory. Navigate to:
%AppData%\CaptureAge\
Create a skins folder there if it doesn't already exist, then create your skin folder inside it:
%AppData%\CaptureAge\skins\my-skin\
The folder name is up to you — use something short without spaces.
At minimum, a skin just needs a name. Create skin.json inside your skin folder:
{
"name": "my-skin",
"options": {},
"stylesheets": [],
"assets": {}
}
This is a valid (though visually identical to default) skin.
Restart CaptureAge. Your skin will be discovered automatically and appear in Settings Tab → Select Skin.
Alternative — load from anywhere: You can also load a skin folder or ZIP file from any location on your PC. Go to Settings Tab → Three dots button next to the Select Skin dropdown, choose Folder or ZIP file, then browse to it. This is useful when working on a skin in a different location.
A typical skin folder looks like this:
my-skin/
skin.json ← required manifest
skin.css ← compiled CSS
assets/
bar-panel.svg ← replacement SVG for the top bar panel
score-panel.svg ← replacement SVG for the scoreboard
All paths in skin.json are relative to the skin's root folder.
The manifest tells CaptureAge everything about your skin.
{
"name": "my-skin",
"options": {},
"stylesheets": [],
"assets": {}
}
{
"name": "my-skin",
"options": {
"alwaysShowUnitPanel": false,
"standardColorOverrides": []
},
"stylesheets": [
"skin.css"
],
"assets": {
"assets/ca/bar-panel.svg": "assets/bar-panel.svg",
"assets/ca/score-panel.svg": "assets/score-panel.svg"
}
}
| Field | Required | Description |
|---|---|---|
name | Yes | A display name for your skin |
options | Yes | Configuration options (can be empty {}) |
stylesheets | Yes | Array of CSS file paths to load (relative to skin root). Can be empty. |
assets | Yes | Map of asset keys to file paths (see Asset Keys Reference). Can be empty. |
Each entry maps an asset key (the identifier CaptureAge uses internally) to a path in your skin folder:
"assets": {
"assets/ca/bar-panel.svg": "assets/bar-panel.svg"
}
The key on the left is fixed (it must match exactly one of the known asset keys). The value on the right is the path to your replacement file, relative to your skin root. These don't have to be the same — you can organize your files however you like:
"assets": {
"assets/ca/bar-panel.svg": "panels/my-bar.svg"
}
Add a CSS file to your skin folder and reference it in stylesheets:
"stylesheets": ["skin.css"]
CaptureAge uses CSS Modules, but the compiled class names follow a simple, predictable pattern:
.{ComponentFilename}-{localClassName}
For example, the .teamName class inside the Scoreboard becomes .Scoreboard-teamName in the browser. This means you can target any component directly:
.Scoreboard-scoreboard .Scoreboard-teamName {
color: var(--pcPrimaryBright);
}
.TeamHeader-ageReq .TeamHeader-resReqBars {
position: absolute;
bottom: 10px;
}
.EndGame-endGameContainer {
background-color: #1a1a2e;
border: 2px solid gold;
}
The easiest way to find the class names you want to target is to use the built-in skin debugger. In the Settings Tab, check Enable HTTP Server for skin debugging, then click Open Webpage. This opens the live CaptureAge UI in your browser. Use the browser's DevTools inspector (F12) to click on any element and see its exact class names.
Files in your skin are served relative to your CSS file's location. You can use relative paths in url() directly:
/* file is at the same level as skin.css */
background-image: url(./my-background.png);
/* file is in a subdirectory */
background-image: url(./assets/my-background.png);
The file must be listed in the assets map in skin.json with a key matching the relative path from the skin root.
@font-face {
font-family: "My Font";
font-weight: 400;
src: url(./assets/fonts/MyFont-Regular.ttf) format("truetype");
}
.Scoreboard-scoreboard .Scoreboard-teamName {
font-family: "My Font", sans-serif;
}
The font file must also be listed in assets:
"assets": {
"assets/fonts/MyFont-Regular.ttf": "assets/fonts/MyFont-Regular.ttf"
}
CaptureAge scales its UI with the window size. Sizes in the compiled CSS are expressed in vw units at standard aspect ratio and with a @media screen and (min-aspect-ratio: 16 / 9) block for ultrawide displays (expressed in vh units). If you need precise sizing, you will typically need both variants.
No external CSS — you cannot @import from external URLs. Load everything through the skin's stylesheets array or inline it.
The main UI panels (bar panel, score panel, dashboard panel, etc.) are SVG files. You can replace them entirely with your own artwork.
assets in skin.json using the correct asset keyAll SVGs are automatically processed through SVGO when served. This means:
width and height attributes are removed (the SVG will fill its container)viewBox is preserved — always include a viewBox in your SVGImportant: Because IDs are prefixed, you cannot rely on bare
#my-idreferences within your SVG to survive. SVGO will prefix them to something like#score-panel_svg__my-id. The same applies to class names:class="fillLeft"becomesclass="score-panel_svg__fillLeft".
Since width and height are removed by SVGO, the SVG renders at width: 100% of its container, and its height is calculated from the viewBox aspect ratio. If the ratio is wrong, the panel will visually overflow into other UI elements or appear too small.
Always match your viewBox to the panel's actual dimensions. See Known Panel Dimensions for a reference table.
The ID prefix format is based on the SVG's filename. For example, if your SVG file is assets/ca/score-panel.svg, its IDs will be prefixed with score-panel_svg__.
So if your SVG has:
<polygon id="colorBar-left" ... />
After SVGO processing it becomes:
<polygon id="score-panel_svg__colorBar-left" ... />
Your CSS can target it like this:
#score-panel_svg__colorBar-left {
fill: var(--pcPrimary);
}
SVGO prefixes both IDs and class names the same way. Some panels in the app use CSS class selectors to target SVG elements — notably bar-panel.svg, which uses classes fillLeft and fillRight for player colors:
<!-- in bar-panel.svg — use class, not id -->
<rect class="fillLeft" ... />
<rect class="fillRight" ... />
/* in skin.css — class selector, not ID */
.bar-panel_svg__fillLeft { fill: var(--pcPrimary); }
.bar-panel_svg__fillRight { fill: var(--pcPrimaryAlt); }
Some panels are used as dynamic bar charts by the app — the components target specific element IDs inside the SVG to drive colors and bar widths. If your SVG is missing these IDs, the dynamic display will not work.
| SVG asset | Required element IDs |
|---|---|
assets/ca/economy-bar.svg | fill_left, fill_right |
assets/ca/army-population.svg | armyPopulation-left, armyPopulation-right, armyPopulation-divider, armyPopulation-shadow-center |
assets/ca/army-value.svg | armyValue-left, armyValue-right |
These IDs follow the same SVGO prefix rule, so fill_left in economy-bar.svg becomes #economy-bar_svg__fill_left in CSS.
Use any vector editor (Illustrator, Inkscape, Figma, etc.). A few tips:
viewBox attribute matching the panel's known dimensions (see below).st0, .myClass) on shapes to make CSS targeting easierUse these exact viewBox dimensions to avoid panels overflowing or appearing too small. Values come from the app's internal size constants.
| Panel | Asset Key | Recommended viewBox |
|---|---|---|
| Balance panel background | assets/ca/bar-panel.svg | 0 0 600 68 |
| Economy balance bar | assets/ca/economy-bar.svg | 0 0 600 14 |
| Army population bar | assets/ca/army-population.svg | 0 0 600 14 |
| Army value bar | assets/ca/army-value.svg | 0 0 600 12 |
| Resources + age (1v1) | assets/ca/res-age-panel-1v1.svg | 0 0 888 57 |
| Resources + age (team games) | assets/ca/res-age-panel-tg.svg | 0 0 544 56 |
| Minimap under/over | assets/ca/minimap-*-panel.svg | 0 0 464 464 |
| Score panel (1v1) | assets/ca/score-panel.svg | 0 0 1254 130 |
| Score panel (team games) | assets/ca/score-panel-tg.svg | 0 0 1254 130 |
Note: The balance bars (
economy-bar,army-population,army-value) are rendered inside the 600×68 balance panel, not as full-width top-bar elements. They are short horizontal bar charts, not background panels.
Player colors in CA:DE are dynamic — they change based on game state and color assignment. The skin system exposes them as CSS custom properties that you can use anywhere in your CSS.
These properties are set by the game on the wrapping element of each player panel:
| CSS Custom Property | Description |
|---|---|
--pcPrimary | Main player color |
--pcPrimaryBright | Brighter version of the main color |
--pcSecondary | Secondary/accent shade |
--pcDark | Darker shade |
--pcFade | Faded/transparent-ish shade |
For 1v1 panels that show both players simultaneously, a second set of properties is available for the "alt" (opposing) player:
| CSS Custom Property | Description |
|---|---|
--pcPrimaryAlt | Opposing player's main color |
--pcPrimaryBrightAlt | Opposing player's bright color |
--pcSecondaryAlt | Opposing player's secondary color |
--pcDarkAlt | Opposing player's dark shade |
--pcFadeAlt | Opposing player's fade shade |
.playerName {
color: var(--pcPrimaryBright);
border-bottom: 2px solid var(--pcDark);
}
To apply dynamic player colors to elements within your SVG panel, target their (prefixed) IDs from your skin's CSS:
/* in skin.css */
#score-panel_svg__colorBar-left {
fill: var(--pcPrimary);
}
#score-panel_svg__colorBar-right {
fill: var(--pcPrimaryAlt);
}
You can include PNG (or other image format) files in your skin. Note that if the asset key starts with assets CA:DE will try to find its own asset with that name and might throw an error.
"assets": {
"images/background.png": "images/background.png"
}
.myBackground {
background-image: url(skin://get/my-skin/images/background.png);
}
Set these in the options field of skin.json.
"alwaysShowUnitPanel": true
When true, the unit detail panel stays visible even when no unit is selected. Useful for always-on overlay layouts.
You can override the color palette used for each player color (Blue, Red, Green, etc.) via standardColorOverrides in skin.json. This changes how those colors appear throughout the UI — in text, borders, SVG fills, minimap dots, and so on.
Each override targets a specific color ID and replaces one palette key with a hex color:
"standardColorOverrides": [
{ "id": "gameBlue", "key": "primary", "color": "#3399ff" },
{ "id": "gameBlue", "key": "primaryBright", "color": "#66bbff" },
{ "id": "gameBlue", "key": "dark", "color": "#1155aa" },
{ "id": "gameRed", "key": "primary", "color": "#ff3333" }
]
Game player colors:
| ID | Default game color |
|---|---|
gameBlue | Blue player |
gameRed | Red player |
gameGreen | Green player |
gameYellow | Yellow player |
gameTeal | Teal player |
gamePink | Pink player |
gameGrey | Grey player |
gameOrange | Orange player |
gameGaia | Gaia (neutral) |
Special colors:
| ID | Usage |
|---|---|
clown | Fallback/placeholder color |
teamNeutralA | Team neutral color A |
teamNeutralB | Team neutral color B |
| Key | Used for |
|---|---|
primary | Main UI color for this player (borders, fills, --pcPrimary) |
primaryBright | Brighter accent (--pcPrimaryBright) |
secondary | Secondary shade (--pcSecondary) |
dark | Dark shade for backgrounds (--pcDark) |
fade | Faded/semi-transparent appearance (--pcFade) |
minimap | Color of player's units on the minimap |
minimapDark | Darker minimap color |
iconMultiply | Multiply blend color for player-colored icons |
These are all the assets that can be replaced in a skin. Use the key exactly as shown in your skin.json assets map.
Note: Assets marked not overridable cannot be replaced by a skin.
| Asset Key | Description |
|---|---|
assets/ca/bar-panel.svg | Top bar panel background |
assets/ca/economy-bar.svg | Economy bar panel |
assets/ca/dashboard-panel.svg | Dashboard/stats panel background |
assets/ca/dashboard-over-panel.svg | Dashboard overlay layer |
assets/ca/selection-panel.svg | Unit selection panel background |
assets/ca/selection-over-panel.svg | Unit selection overlay |
assets/ca/score-panel.svg | Score/scoreboard panel (1v1) |
assets/ca/score-panel-tg.svg | Score panel (team games) |
assets/ca/score-over-panel.svg | Score panel overlay |
assets/ca/score-no-teams-panel.svg | Score panel (no teams mode) |
assets/ca/minimap-under-panel.svg | Minimap underlay |
assets/ca/minimap-over-panel.svg | Minimap overlay |
assets/ca/minimap-over-panel-pompeii.svg | Minimap overlay (Pompeii game type) |
assets/ca/res-age-panel-1v1.svg | Resources and age panel (1v1) |
assets/ca/res-age-panel-tg.svg | Resources and age panel (team games) |
assets/ca/army-population.svg | Army / population display |
assets/ca/army-value.svg | Army value display |
assets/ca/victory-screen.svg | Victory/defeat screen |
assets/ca/table-icons-bar.svg | Stats table icon bar |
assets/ca/team-positions-1.svg | Team position display (1 player) |
assets/ca/team-positions-2.svg | Team position display (2 players) |
assets/ca/team-positions-3.svg | Team position display (3 players) |
assets/ca/team-positions-4.svg | Team position display (4 players) |
| Asset Key | Description |
|---|---|
assets/ca/pompeii/res-age-panel-1v1.svg | Resources/age panel (Pompeii 1v1) |
assets/ca/pompeii/d3-bg-left.svg | D3 background left (Pompeii) |
assets/ca/pompeii/d3-bg-right.svg | D3 background right (Pompeii) |
assets/ca/pompeii/d3-age1.svg | D3 age 1 graphic (Pompeii) |
assets/ca/pompeii/d3-age2.svg | D3 age 2 graphic (Pompeii) |
assets/ca/pompeii/d3-age3.svg | D3 age 3 graphic (Pompeii) |
assets/ca/pompeii/PompeiiCountdown-BG.svg | Pompeii countdown background |
| Asset Key | Description |
|---|---|
assets/ca/icon/question-mark.svg | Question mark icon |
assets/ca/icon/food.svg | Food resource icon |
assets/ca/icon/wood.svg | Wood resource icon |
assets/ca/icon/stone.svg | Stone resource icon |
assets/ca/icon/gold.svg | Gold resource icon |
assets/ca/icon/pop-cap.svg | Population cap icon |
assets/ca/icon/kills.svg | Kills icon |
assets/ca/icon/deaths.svg | Deaths icon |
assets/ca/icon/eco-kills.svg | Eco kills icon |
assets/ca/icon/eco-deaths.svg | Eco deaths icon |
assets/ca/icon/dead-unit.svg | Dead unit icon |
assets/ca/icon/killed-value.svg | Killed value icon |
assets/ca/icon/creation-value.svg | Creation value icon |
assets/ca/icon/military-value.svg | Military value icon |
assets/ca/icon/military-building.svg | Military building icon |
assets/ca/icon/castle.svg | Castle icon |
assets/ca/icon/relic.svg | Relic icon |
assets/ca/icon/wonder.svg | Wonder icon |
assets/ca/icon/trade-cart.svg | Trade cart icon |
assets/ca/icon/trade-workshop.svg | Trade workshop icon |
assets/ca/icon/swords.svg | Swords icon |
assets/ca/icon/town-center.svg | Town center icon |
assets/ca/icon/town-center-idle.svg | Town center idle icon |
assets/ca/icon/hammer.svg | Builder/hammer icon |
assets/ca/icon/hammer-idle.svg | Idle builder icon |
assets/ca/icon/lazy-figure.svg | Idle villager icon |
assets/ca/icon/garrison.svg | Garrison icon |
assets/ca/icon/melee-armor.svg | Melee armor icon |
assets/ca/icon/melee-damage.svg | Melee damage icon |
assets/ca/icon/pierce-armor.svg | Pierce armor icon |
assets/ca/icon/pierce-damage.svg | Pierce damage icon |
assets/ca/icon/range.svg | Range icon |
assets/ca/icon/damageDealt.svg | Damage dealt icon |
assets/ca/icon/damageTaken.svg | Damage taken icon |
assets/ca/icon/health-regen.svg | Health regen icon |
assets/ca/icon/conversions.svg | Conversions icon |
assets/ca/icon/kills-and-conversions.svg | Kills + conversions icon |
assets/ca/icon/heresy.svg | Heresy icon |
assets/ca/icon/ecoConvert.svg | Eco conversion icon |
assets/ca/icon/buy-sell.svg | Buy/sell icon |
assets/ca/icon/chest-res.svg | Chest/resources icon |
assets/ca/icon/percent-gold.svg | Percent gold icon |
assets/ca/icon/sent-received.svg | Sent/received icon |
assets/ca/icon/cross.svg | Cross/close icon |
assets/ca/icon/absolute.svg | Absolute value icon |
assets/ca/icon/percent-of-current.svg | Percent of current icon |
assets/ca/icon/percent-of-max.svg | Percent of max icon |
assets/ca/icon/eye-combined.svg | FOW combined view icon |
assets/ca/icon/eye-full-fow.svg | Full fog of war icon |
assets/ca/icon/eye-no-fow.svg | No fog of war icon |
assets/ca/icon/eye-spy-fow.svg | Spy fog of war icon |
assets/ca/icon/building-req.svg | Building requirement icon |
assets/ca/icon/castle-req.svg | Castle requirement icon |
assets/ca/icon/nolt.svg | Nolt feedback icon |
assets/ca/icon/aoe2-slash.svg | AoE2 separator icon |
assets/ca/icon/check-outline.svg | Checkmark outline icon |
assets/ca/icon/skull-outline.svg | Skull outline icon |
assets/ca/icon/times-outline.svg | Times/X outline icon |
assets/ca/icon/artifact-icon.svg | Artifact icon (Pompeii) |
assets/ca/icon/ruins-icon.svg | Ruins icon (Pompeii) |
| Asset Key | Description |
|---|---|
assets/ca/icon/mini-tc.svg | Mini town center icon |
assets/ca/icon/mini-wonder.svg | Mini wonder icon |
assets/ca/icon/mini-castle.svg | Mini castle icon |
assets/ca/icon/mini-trade-workshop.svg | Mini trade workshop icon |
assets/ca/icon/mini-king.svg | Mini king icon |
assets/ca/icon/mini-king-garrisoned.svg | Mini garrisoned king icon |
| Asset Key | Description |
|---|---|
assets/ca/icon/king-status/dead.svg | King dead |
assets/ca/icon/king-status/refuge.svg | King in refuge |
assets/ca/icon/king-status/threatened.svg | King threatened |
assets/ca/icon/king-status/transport-ship.svg | King on transport ship |
assets/ca/icon/king-status/under-attack.svg | King under attack |
| Asset Key | Description |
|---|---|
assets/ca/icon/notification/noti-ship-threatened.svg | Ship threatened |
assets/ca/icon/notification/noti-unboarded.svg | Unit unboarded |
assets/ca/icon/notification/noti-ungarrisoned.svg | Unit ungarrisoned |
assets/ca/icon/notification/noti-boarded.svg | Unit boarded |
assets/ca/icon/notification/noti-garrisoned.svg | Unit garrisoned |
assets/ca/icon/notification/noti-garrison-threatened.svg | Garrison threatened |
assets/ca/icon/notification/noti-king-dead.svg | King dead notification |
assets/ca/icon/notification/noti-king-threatened.svg | King threatened notification |
assets/ca/icon/notification/noti-king-under-attack.svg | King under attack notification |
| Asset Key | Description |
|---|---|
assets/ca/icon/diplomacy-status/mutual-ally.svg | Mutual ally |
assets/ca/icon/diplomacy-status/mutual-enemy.svg | Mutual enemy |
assets/ca/icon/diplomacy-status/mutual-neutral.svg | Mutual neutral |
assets/ca/icon/diplomacy-status/one-sided-ally.svg | One-sided ally |
assets/ca/icon/diplomacy-status/one-sided-enemy.svg | One-sided enemy |
assets/ca/icon/diplomacy-status/one-sided-neutral.svg | One-sided neutral |
| Asset Key | Description |
|---|---|
assets/ca/controls/bookmark-add.svg | Add bookmark |
assets/ca/controls/bookmark-navigate.svg | Navigate bookmarks |
assets/ca/controls/pause.svg | Pause |
assets/ca/controls/resume.svg | Resume |
assets/ca/controls/skip.svg | Skip |
assets/ca/controls/speed-default.svg | Default speed |
assets/ca/controls/speed-more.svg | Faster speed |
assets/ca/controls/speed-most.svg | Fastest speed |
assets/ca/controls/speed-slow.svg | Slow speed |
| Asset Key | Description |
|---|---|
assets/game/icon/age-1.png | Dark Age icon |
assets/game/icon/age-2.png | Feudal Age icon |
assets/game/icon/age-3.png | Castle Age icon |
assets/game/icon/age-4.png | Imperial Age icon |
assets/game/icon/age-1-pompeii.png | Age 1 (Pompeii) |
assets/game/icon/age-2-pompeii.png | Age 2 (Pompeii) |
assets/game/icon/age-3-pompeii.png | Age 3 (Pompeii) |
assets/game/icon/age-4-pompeii.png | Age 4 (Pompeii) |
assets/game/icon/techtree_dark_age.png | Dark Age techtree icon |
assets/game/icon/techtree_feudal_age.png | Feudal Age techtree icon |
assets/game/icon/techtree_castle_age.png | Castle Age techtree icon |
assets/game/icon/techtree_imperial_age.png | Imperial Age techtree icon |
assets/game/icon/techtree_stone_age.png | Stone Age techtree icon |
assets/game/icon/techtree_tool_age.png | Tool Age techtree icon |
assets/game/icon/techtree_bronze_age.png | Bronze Age techtree icon |
assets/game/icon/techtree_iron_age.png | Iron Age techtree icon |
assets/game/icon/techtree_archaic_age.png | Archaic Age techtree icon |
assets/game/icon/techtree_civic_age.png | Civic Age techtree icon |
assets/game/icon/techtree_classical_age.png | Classical Age techtree icon |
assets/game/icon/techtree_ant_imperial_age.png | Ant Imperial Age techtree icon |
| Asset Key | Description |
|---|---|
assets/ca/pop-icon-empires1.png | Population icon (Empires 1 game type) |
The following assets are protected and cannot be replaced by a skin:
assets/ca/skin-logo.svg (used for the skin logo feature itself)assets/ca/ca-logo.pngassets/ca/ca-logo.svgassets/ca/ca-logo-diamond.svg / .pngassets/ca/ca-logo-heart.svg / .png... selectorThe skin loads immediately; errors appear as notifications.
CA:DE watches your skin folder for file changes. When you save a CSS or SVG file, the skin reloads automatically — you don't need to restart.
CA:DE includes a built-in HTTP server that serves the live application UI in a regular browser tab. This is the most powerful tool available for skin development.
To enable it: Settings Tab → check Enable HTTP Server for skin debugging → click Open Webpage.
This opens the full CA:DE UI in your browser while a game or recording is running. From there you have access to the browser's complete DevTools:
.ComponentName-localClass selectors you need to target in your CSS.Tip: Keep the browser tab open alongside CA:DE. When you save your
skin.css, both the app and the browser tab will hot-reload automatically.
Resolution matters: The browser tab renders at its own viewport size, which may differ from the resolution CA:DE is running at. If you want the browser view to accurately reflect what you see in the app, make sure the browser window is sized to match your CA:DE resolution. Otherwise, the ultrawide screen media query (
min-aspect-ratio: 16/9) may activate or deactivate differently in the browser vs. the app, causing layout differences.
Validation errors (missing files, bad skin.json syntax, invalid asset keys) appear as yellow warning notifications in the app. Check these messages when something doesn't look right.
Instead of distributing a folder, you can package your skin as a .zip file. Users can load it the same way they load a folder skin.
Zip the contents of your skin folder so that skin.json is at the root of the ZIP (not inside a subfolder):
my-skin.zip
skin.json ← must be at root
skin.css
assets/
bar-panel.svg
Not like this:
my-skin.zip
my-skin/ ← wrong! nested folder
skin.json
...
skin.json must be at the rootskin.json must exist in the ZIP.. (no parent directory traversal){
"name": "my-skin",
"options": {},
"stylesheets": ["skin.css"],
"assets": {
"assets/ca/bar-panel.svg": "assets/ca/bar-panel.svg"
}
}
--pcPrimary --pcPrimaryAlt
--pcPrimaryBright --pcPrimaryBrightAlt
--pcSecondary --pcSecondaryAlt
--pcDark --pcDarkAlt
--pcFade --pcFadeAlt