<p>The good news is that themes in Elyra aren't compiled assets or buried somewhere in the source. They're just JSON files. One per theme. Drop yours in the right folder and it shows up immediately in the theme selector — no rebuild, no restart, no ceremony.</p><p>Here's how it all fits together.</p><h2>The basics</h2><p>A theme file goes in <code>~/.elyra/themes/</code>. Name it whatever you want. The filename, minus the <code>.json</code>, becomes the theme name.</p><pre><code class="language-text">~/.elyra/themes/mytheme.json
</code></pre><p>Once saved, it appears in <code>/theme</code> straight away. The selector previews themes as you arrow through the list, so you can iterate by saving the file, alt-tabbing back to Elyra, and reopening the selector to see what changed.</p><h2>Starting from an existing theme</h2><p>You <em>could</em> write a theme from a blank file, with the schema in one tab and a color picker in another. You probably shouldn't. The easier path is to grab a built-in theme that's already close to what you want and edit from there.</p><p>The built-in theme files live at <code>packages/coding-agent/src/modes/interactive/theme/</code> in the Elyra source. Copy whichever one you like into <code>~/.elyra/themes/</code> under a new name, and you've got a starting point that already has every required key filled in with sensible values.</p><p>If you'd rather start from scratch, here's a minimal theme that gives you a feel for the shape:</p><pre><code class="language-json">{
    "$schema": "https://raw.githubusercontent.com/kwhorne/elyra/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
    "name": "mytheme",
    "vars": {
        "blue": "#61afef",
        "green": "#98c379",
        "red": "#e06c75",
        "yellow": "#e5c07b",
        "purple": "#c678dd",
        "cyan": "#56b6c2",
        "gray": "#7f848e",
        "dimGray": "#5c6370",
        "bg": "#282c34"
    },
    "colors": {
        "accent": "blue",
        "border": "blue",
        "borderAccent": "cyan",
        "borderMuted": "#3e4452",
        "success": "green",
        "error": "red",
        "warning": "yellow",
        "muted": "gray",
        "dim": "dimGray",
        "text": "",
        "thinkingText": "gray",

        "selectedBg": "#2c313a",
        "userMessageBg": "bg",
        "userMessageText": "",
        "customMessageBg": "#28252e",
        "customMessageText": "",
        "customMessageLabel": "purple",
        "toolPendingBg": "#252930",
        "toolSuccessBg": "#252e28",
        "toolErrorBg": "#2e2528",
        "toolTitle": "",
        "toolOutput": "gray",

        "mdHeading": "yellow",
        "mdLink": "blue",
        "mdLinkUrl": "dimGray",
        "mdCode": "green",
        "mdCodeBlock": "green",
        "mdCodeBlockBorder": "gray",
        "mdQuote": "gray",
        "mdQuoteBorder": "gray",
        "mdHr": "gray",
        "mdListBullet": "yellow",

        "toolDiffAdded": "green",
        "toolDiffRemoved": "red",
        "toolDiffContext": "gray",

        "syntaxComment": "#7f848e",
        "syntaxKeyword": "#c678dd",
        "syntaxFunction": "#61afef",
        "syntaxVariable": "#e06c75",
        "syntaxString": "#98c379",
        "syntaxNumber": "#d19a66",
        "syntaxType": "#e5c07b",
        "syntaxOperator": "#56b6c2",
        "syntaxPunctuation": "#abb2bf",

        "thinkingOff": "#3e4452",
        "thinkingMinimal": "#5c6370",
        "thinkingLow": "#7f848e",
        "thinkingMedium": "blue",
        "thinkingHigh": "purple",
        "thinkingXhigh": "#de73ff",

        "bashMode": "green"
    },
    "export": {
        "pageBg": "#181a1f",
        "cardBg": "#21252b",
        "infoBg": "#2c313c"
    }
}
</code></pre><p>That's the whole shape. Two main sections, an optional third, and a <code>$schema</code> line that pulls its weight in any editor that knows what to do with it.</p><h2>How the two sections work</h2><h3><code>vars</code></h3><p>The <code>vars</code> section is your palette. It's where you give your colors human-readable names so you don't have to keep typing <code>#61afef</code> everywhere.</p><pre><code class="language-json">"vars": {
    "blue": "#61afef",
    "green": "#98c379",
    "bg": "#282c34"
}
</code></pre><p>You can put hex codes here (<code>#RRGGBB</code>) or, for terminals stuck in the 256-color era, palette indices from 0 to 255. You can define as many variables as you want, with whatever names make sense to you. Some people name them by hue (<code>blue</code>, <code>green</code>), some by role (<code>primaryBg</code>, <code>accentWarm</code>). Both work fine — <code>vars</code> is just sugar.</p><h3><code>colors</code></h3><p>The <code>colors</code> section is where the actual work happens. Every key here maps a <em>semantic role</em> — "this is the accent color," "this is what errors look like" — to either a variable name from <code>vars</code> or a hex value used directly.</p><pre><code class="language-json">"colors": {
    "accent": "blue",
    "error": "#ff0000",
    "text": ""
}
</code></pre><p>Three formats are valid:</p><ul><li><p><strong>Variable reference</strong>: <code>"blue"</code> looks up <code>blue</code> in <code>vars</code>.</p></li><li><p><strong>Direct hex</strong>: <code>"#ff0000"</code> uses the color literally.</p></li><li><p><strong>Empty string</strong>: <code>""</code> means <em>use the terminal's default color</em>.</p></li></ul><p>That last one is more useful than it looks. Setting <code>text</code> or <code>userMessageText</code> to <code>""</code> lets the terminal's own foreground color come through, which means your theme stops fighting the user's terminal background. The same theme reads cleanly whether their terminal is pure black, dark gray, or the slightly-off-cream that some people swear by.</p><p>Every key in <code>colors</code> is required. The schema enforces this — leave one out and validation will tell you exactly which one.</p><h2>What each color controls</h2><p>There are 51 required colors in total. That sounds like a lot until you realize they're really six small groups doing six different jobs.</p><h3>Core UI (11 colors)</h3><p>The chrome of the application. Accents, borders, status colors, the basic text-and-emphasis vocabulary.</p><p>Color What it affects <code>accent</code> Logo, cursor, selected items, list bullets, active elements <code>border</code> Normal borders around tool output, code blocks <code>borderAccent</code> Highlighted borders (active input, focused elements) <code>borderMuted</code> Subtle borders (separators, inactive elements) <code>success</code> Success states, green indicators <code>error</code> Error messages, failed tool calls <code>warning</code> Warnings, caution indicators <code>muted</code> Secondary text, labels, descriptions <code>dim</code> Very faded text (timestamps, meta info) <code>text</code> Default text color (usually empty for terminal default) <code>thinkingText</code> Thinking block text</p><h3>Backgrounds (8 colors)</h3><p>The bigger color areas — message backgrounds, tool execution boxes, selection states.</p><p>Color What it affects <code>selectedBg</code> Selected item in lists and menus <code>userMessageBg</code> Your messages in the chat <code>userMessageText</code> Your message text color <code>customMessageBg</code> Extension-injected messages <code>customMessageText</code> Extension message text <code>customMessageLabel</code> Extension message type label <code>toolPendingBg</code> Tool execution box while running <code>toolSuccessBg</code> Tool execution box on success <code>toolErrorBg</code> Tool execution box on error</p><p>A small piece of advice that took me too long to figure out: for backgrounds, <em>subtle</em> almost always beats <em>expressive</em>. Take your base background color and shift it 10–20 points in one RGB channel toward green for success, red for error, blue or gray for pending. That's usually enough. Anything stronger and the chat starts to feel like a traffic light.</p><h3>Markdown (10 colors)</h3><p>How rendered markdown — headings, code, links, quotes — picks up its colors.</p><p>Color What it affects <code>mdHeading</code> <code># Heading</code> text <code>mdLink</code> <code>[link text](url)</code> <code>mdLinkUrl</code> The URL part of links <code>mdCode</code> <code>inline code</code> <code>mdCodeBlock</code> Fenced code block content <code>mdCodeBlockBorder</code> The <code>```</code> fence lines <code>mdQuote</code> <code>&gt; blockquote</code> text <code>mdQuoteBorder</code> Blockquote border <code>mdHr</code> <code>---</code> horizontal rules <code>mdListBullet</code> <code>-</code> and <code>1.</code> list markers</p><h3>Diffs (3 colors)</h3><p>The unsung heroes. You'll see a lot of these.</p><p>Color What it affects <code>toolDiffAdded</code> <code>+</code> lines in diffs <code>toolDiffRemoved</code> <code>-</code> lines in diffs <code>toolDiffContext</code> Unchanged context lines</p><h3>Syntax highlighting (9 colors)</h3><p>The actual code colors inside code blocks.</p><p>Color What it affects <code>syntaxComment</code> <code>// comments</code> <code>syntaxKeyword</code> <code>if</code>, <code>return</code>, <code>const</code>, <code>class</code> <code>syntaxFunction</code> Function names <code>syntaxVariable</code> Variable names <code>syntaxString</code> <code>"string literals"</code> <code>syntaxNumber</code> <code>42</code>, <code>3.14</code> <code>syntaxType</code> Type names, class names <code>syntaxOperator</code> <code>+</code>, <code>=</code>, <code>=&gt;</code>, <code>&amp;&amp;</code> <code>syntaxPunctuation</code> <code>{</code>, <code>}</code>, <code>;</code>, <code>,</code></p><p>If you've ever picked colors for a VS Code theme, these will feel familiar. Most of the work of porting an existing theme happens right here.</p><h3>Thinking levels (6 colors)</h3><p>These color the editor's border based on how much thinking is currently enabled. The point is at-a-glance feedback: a quick downward glance should tell you whether you're in <code>low</code> or <code>xhigh</code> without reading anything.</p><p>Color Thinking level <code>thinkingOff</code> Off (darkest) <code>thinkingMinimal</code> Minimal <code>thinkingLow</code> Low <code>thinkingMedium</code> Medium <code>thinkingHigh</code> High <code>thinkingXhigh</code> Xhigh (brightest)</p><p>The trick here is to make them a gradient — dark and subtle at the bottom, bright and saturated at the top. If <code>low</code> and <code>high</code> look basically the same, the indicator stops indicating.</p><h3>One more thing</h3><p>Color What it affects <code>bashMode</code> Editor border color in bash mode (<code>!</code> prefix)</p><p>That's the lot. 51 colors, six small groups.</p><h2>The <code>export</code> section (optional)</h2><p>If you ever run <code>/export</code> to dump a session as HTML, the output uses its own little palette for the page, the cards, and the info blocks. The <code>export</code> section is where you tune those.</p><pre><code class="language-json">"export": {
    "pageBg": "#181a1f",
    "cardBg": "#21252b",
    "infoBg": "#2c313c"
}
</code></pre><p>Leave it out and Elyra derives reasonable defaults from <code>userMessageBg</code>. So unless you're picky about your exports, you can skip this section entirely.</p><h2>Porting a VS Code theme</h2><p>Honestly, this is how most Elyra themes come into the world. Palenight, Catppuccin, OneDark, Norwegian Midnight — all of them started as VS Code themes that got translated into Elyra's format. The mapping is mostly mechanical:</p><p>Elyra VS Code equivalent <code>accent</code> <code>activityBarBadge.background</code> or cursor color <code>bg</code> / <code>userMessageBg</code> <code>editor.background</code> <code>border</code> <code>focusBorder</code> or <code>panel.border</code> <code>muted</code> <code>editorLineNumber.foreground</code> or comment color <code>syntaxKeyword</code> Token color for <code>keyword</code> scope <code>syntaxFunction</code> Token color for <code>entity.name.function</code> scope <code>syntaxString</code> Token color for <code>string</code> scope <code>syntaxComment</code> Token color for <code>comment</code> scope <code>syntaxNumber</code> Token color for <code>constant.numeric</code> scope <code>syntaxType</code> Token color for <code>entity.name.type.class</code> scope <code>syntaxOperator</code> Token color for <code>keyword.operator</code> scope <code>toolSuccessBg</code> <code>editor.background</code> shifted slightly green <code>toolErrorBg</code> <code>editor.background</code> shifted slightly red</p><p>Pull the VS Code theme's JSON, find the relevant tokens, paste them into the right Elyra fields. The <code>tokenColors</code> array in VS Code themes can be a bit fiddly — token rules sometimes target multiple scopes, sometimes apply only under specific parents — but for the basics above, the first match is almost always the right one.</p><h2>A few things I wish someone had told me</h2><p><strong>Test in the terminal you actually live in.</strong> Colors render differently across terminals. iTerm2, Kitty, Alacritty, Ghostty, and the default macOS Terminal all have subtly different ideas about what <code>#61afef</code> should look like. A theme that's gorgeous in Kitty can look washed out in macOS Terminal, and vice versa. Don't tune your theme in one and use it in another.</p><p><strong>Use </strong><code>/theme</code><strong> for live preview.</strong> Save the file, open the selector, arrow through. The selector previews each theme as you highlight it, so you can compare your work against the built-ins side by side without committing to anything. Cancel out and nothing changes.</p><p><strong>Start with backgrounds.</strong> Get <code>userMessageBg</code>, <code>toolPendingBg</code>, <code>toolSuccessBg</code>, and <code>toolErrorBg</code> right before you touch anything else. These are the biggest patches of color on screen, and they decide the whole mood of the theme. Everything after that is accent work.</p><p><strong>Keep contrast accessible.</strong> Your <code>text</code> (or terminal default) needs to be readable on <code>userMessageBg</code>. Your <code>error</code> needs to be readable on <code>toolErrorBg</code>. If your background is dark, your text needs to be light. Obvious, right? It's still the single most common mistake in custom themes. Run your candidate colors through a contrast checker if you're not sure.</p><p><strong>The </strong><code>$schema</code><strong> line earns its keep.</strong> In any editor that understands JSON Schema — and that's most of them now, including VS Code — that one line gives you autocomplete on every color name and a red squiggle the moment you forget one. Leave it in.</p><p>That's really all there is to it. A JSON file, two sections, 51 colors, and a folder that watches itself. The hardest part isn't the schema — it's deciding what you actually want your terminal to feel like at 11pm on a Tuesday. Once you know that, the rest is just typing.</p><p>Have fun. ☕</p>