Themes Tutorial
Rete supports custom themes defined as JSON files. Themes provide UI colors and message colors for both Light and Dark variants. Users can import themes from Preferences or by placing files in the Themes folder.
Changelog
2025-12-16
- Script editor syntax highlighting theme configuration
- Added
scriptEditorHighlightrTheme: String (optional; Highlightr theme name for script editor syntax highlighting. Default: “github” for light mode, “github-dark” for dark mode) - Themes can now specify which Highlightr theme to use for TCL script syntax highlighting in the script editor
- Each palette (light/dark) can specify its own Highlightr theme independently
- This allows themes to match the code editor appearance to the overall theme aesthetic
- Added
2025-12-15
- New blocked user badge
- Added
blockedBadge: String (optional; SF Symbol name for blocked user badge. Default: “hand.raised.fill”) - Added
blockedBadgeColor: HexColor (optional; color for blocked badge icon. Default: system red) - When a user’s hostmask is silenced/blocked, this badge is shown next to the nick in the nicklist and in the user info tooltip header.
- Added
- Font configuration support
- Added optional
fontsfield toThemePalettefor configuring message fonts - Supports custom font names, font designs (default, monospaced, serif, rounded), and font sizes (small, body, large)
- Font configuration applies to all message content throughout the app (MessageRow, TopicBar, raw buffer, notices)
- If not specified, the app uses the default system body font
- This allows themes to specify monospace fonts for IRC script alignment compatibility
- Added optional
2025-12-01
- New optional UI field
- Added
expandedGroupRowBackground: HexColor (optional; background color for expanded grouped message rows when uncollapsed. Default: accent color with ~8% opacity #0A84FF14) - This is optional - existing themes will use the default if not specified.
- Added
- New bot status badge
- Added
botBadge: String (optional; SF Symbol name for bot user badge. Default: “cpu”) - Added
botBadgeColor: HexColor (optional; color for bot badge icon. Default: system secondary label color) - When
person.isBotis true, this badge is shown next to the nick in the nicklist and in the user info tooltip header.
- Added
2025-11-27
- New message badge symbols
- Added optional badge symbol fields to UI:
joinBadge,partBadge,quitBadge,actionBadge,noticeBadge,infoBadge,topicBadge - These allow themes to customize the SF Symbol icons used for message type badges in the sender column
- All fields are optional - existing themes will use standard defaults if not specified
- Badge symbols can be customized independently for light and dark variants
- Added optional badge symbol fields to UI:
- New metadata field
- Added
description: String (optional; short human-readable description of the theme, used in UI and documentation). - This field is optional - existing themes without a description continue to work without changes.
- Added
2025-11-26
- Message color system restructured
- BREAKING: Changed message color resolution order. Previously,
tagshad priority overcategories. Nowcategoriesare checked first, then only contextualtags. - Action required: If your theme uses tag colors for message types (
join,part,quit,topic,error,notice,reply,action,ctcp), move them tocategoriesinstead.tagsshould only be used for contextual metadata (highlight,bold,server,channel,dm,raw,in,out,script).
- BREAKING: Changed message color resolution order. Previously,
- Categories expanded
- Added new category types that were previously tags:
nick,notice,mode,kick,action,ctcp - Action required: If your theme defined colors for these as
tags, move them to thecategoriesobject using the same keys.
- Added new category types that were previously tags:
- Tags redefined
- Removed message-type
tagsfrom valid tag list:join,part,quit,topic,error,notice,reply,action,ctcp(these are nowcategories) - Added new formatting
tags:highlight,bold - Added new technical metadata
tag:script tagsare now only for contextual/metadata information, not message types- Action required: Remove any
tagdefinitions forjoin,part,quit,topic,error,notice,reply,action, orctcpfrom yourtagsobject and move them tocategoriesinstead.
- Removed message-type
- New optional Theme fields
- Added
operatorBadge: String (optional; SF Symbol name for IRC operator badge. Default:"shield.checkered") - Added
awayBadge: String (optional; SF Symbol name for away user badge. Default:"moon.fill") - Added
secureBadge: String (optional; SF Symbol name for secure connection badge. Default:"lock.fill") - These are optional - existing themes will continue to work and use defaults.
- Added
- New optional UI field
- Added
highlightRowBackground: HexColor (optional; background color for highlighted message rows. Default: red with ~15% opacity#FF000026) - This is optional - existing themes will use the default if not specified.
- Added
Where to put theme files
- macOS (user): ~/Library/Application Support/Rete/Themes
- The app creates this directory on first import or load attempt.
- You can also use Preferences → Appearance → “Import theme…” to copy a .json file into this directory.
Notes:
- The directory name is hardcoded to “Rete/Themes” under Application Support and does not depend on the bundle identifier.
- Files must have the .json extension.
JSON schema (overview)
Top-level object (Theme):
name:String(unique theme name; used in the picker)description:String(optional; short human-readable description of the theme)author:String(optional)version:String(optional)preferDark:Bool(optional; hint for default variant choice)operatorBadge:String(optional; SF Symbol name for IRC operator badge. Default:"shield.checkered")awayBadge:String(optional; SF Symbol name for away user badge. Default:"moon.fill")secureBadge:String(optional; SF Symbol name for secure connection badge. Default:"lock.fill")botBadge:String(optional; SF Symbol name for bot user badge. Default:"cpu")blockedBadge:String(optional; SF Symbol name for blocked user badge. Default:"hand.raised.fill")light:ThemePalettedark:ThemePalette
ThemePalette:
ui:UImessageColors:MessageColorsfonts:Fonts(optional)
Fonts (optional):
messageFontName:String(optional; custom font name, e.g.,"SF Mono","Menlo","Courier New")messageFontDesign:String(optional; one of:"default","monospaced","serif","rounded")messageFontSize:String(optional; one of:"small","body","large")
UI (all colors are HexColor objects):
backgroundforegroundaccentdividertopicBarBackgroundtopicBarForegroundbadgeBackgroundbadgeForegroundsidebarSelectionBackgroundsidebarSelectionForegroundhighlightRowBackground(optional): Background color for highlighted message rows. Default: red with ~15% opacity (#FF000026)expandedGroupRowBackground(optional): Background color for expanded grouped message rows (uncollapsed message groups). Default: accent color with ~8% opacity (#0A84FF14)operatorBadgeColor(optional):HexColorfor operator badge icon color. Default: system secondary label color.awayBadgeColor(optional):HexColorfor away badge icon color. Default: system secondary label color.secureBadgeColor(optional):HexColorfor secure connection badge icon color. Default: system green.botBadgeColor(optional):HexColorfor bot badge icon color. Default: system secondary label color.blockedBadgeColor(optional):HexColorfor blocked badge icon color. Default: system red.joinBadge(optional):String(SF Symbol name for join message badge. Default:"arrow.right.circle.fill")partBadge(optional):String(SF Symbol name for part message badge. Default:"arrow.left.circle.fill")quitBadge(optional):String(SF Symbol name for quit message badge. Default:"arrow.left.circle.fill")actionBadge(optional):String(SF Symbol name for action (/me) message badge. Default:"star.fill")noticeBadge(optional):String(SF Symbol name for notice message badge. Default:"exclamationmark.bubble.fill")infoBadge(optional):String(SF Symbol name for info message badge. Default:"info.circle.fill")topicBadge(optional):String(SF Symbol name for topic message badge. Default:"text.quote")scriptEditorHighlightrTheme(optional):String(Highlightr theme name for script editor syntax highlighting. Default: “github” for light mode, “github-dark” for dark mode)
MessageColors:
categories:{ String: HexColor }- Keys are
IRCMessageCategory.rawValue(primary message type classification):normal,serverNotice,serverReply,info,error,join,part,quit,topic,nick,notice,mode,kick,action,ctcp
- Categories are the primary way to color messages - each message has exactly one category
- Keys are
tags:{ String: HexColor }- Keys are
IRCMessageTag.rawValue(contextual/metadata tags only):highlight,bold(formatting)server,channel,dm(context - where the message appears)raw,in,out,script(technical metadata)silence(silence/block related messages)
- Tags are only for contextual information - do NOT duplicate category values (e.g., don’t use
"join"tag if category is already"join")
- Keys are
HexColor:
- Object with a single field:
hex:"#RRGGBB"
- Important: Only 6-digit RGB is supported by the current
Color(hex:)helper (no alpha channel). If you want a “lighter” effect, choose a lighter color rather than using transparency.
Example HexColor:
{ "hex": "#0A84FF" }
Fallback behavior
If a color is missing or invalid:
- UI background falls back to system window background (macOS) / systemBackground (iOS/iPadOS).
- Foreground falls back to Color.primary.
- Accent falls back to Color.accentColor.
- Divider/topic bar/badge/sidebar selection colors fall back to reasonable defaults based on accent and system colors.
Message color resolution:
- Category color is checked first (message.category.rawValue) - this is the primary classification
- Tag colors are checked only for contextual tags (highlight, bold, server, channel, dm, raw, in, out, script) that don’t map to categories
- If neither category nor tag color is defined, the UI foreground color is used
- Final fallback is Color.primary
Important: Categories and tags no longer overlap. Categories handle message types (join, part, mode, etc.), while tags only provide contextual metadata (where the message appears, formatting, etc.). This eliminates confusion about which to use for coloring.
Appearance mode
The app supports:
- Automatic: follows system appearance
- Light: forces theme.light
- Dark: forces theme.dark
The ThemeManager resolves the active palette at runtime. The theme must define both light and dark palettes.
Example: Minimal valid theme
This is a tiny example that uses a light gray background, dark text, and a blue accent. It sets only a few message colors (others fall back to foreground).
{
"name": "Minimal Example",
"description": "A simple light theme with blue accents.",
"author": "You",
"version": "1.0",
"operatorBadge": "shield.checkered",
"awayBadge": "moon.fill",
"secureBadge": "lock.fill",
"botBadge": "cpu",
"blockedBadge": "hand.raised.fill",
"light": {
"ui": {
"background": { "hex": "#FFFFFF" },
"foreground": { "hex": "#000000" },
"accent": { "hex": "#0A84FF" },
"divider": { "hex": "#D1D1D6" },
"topicBarBackground": { "hex": "#F2F2F7" },
"topicBarForeground": { "hex": "#000000" },
"badgeBackground": { "hex": "#D6E8FF" },
"badgeForeground": { "hex": "#0A84FF" },
"sidebarSelectionBackground": { "hex": "#D6E8FF" },
"sidebarSelectionForeground": { "hex": "#0A84FF" },
"highlightRowBackground": { "hex": "#FF000026" },
"expandedGroupRowBackground": { "hex": "#0A84FF14" },
"operatorBadgeColor": { "hex": "#6B7280" },
"awayBadgeColor": { "hex": "#6B7280" },
"secureBadgeColor": { "hex": "#34C759" },
"botBadgeColor": { "hex": "#6B7280" },
"blockedBadgeColor": { "hex": "#FF3B30" },
"scriptEditorHighlightrTheme": "github",
"joinBadge": "arrow.right.circle.fill",
"partBadge": "arrow.left.circle.fill",
"quitBadge": "arrow.left.circle.fill",
"actionBadge": "star.fill",
"noticeBadge": "exclamationmark.bubble.fill",
"infoBadge": "info.circle.fill",
"topicBadge": "text.quote"
},
"messageColors": {
"categories": {
"normal": { "hex": "#000000" },
"error": { "hex": "#FF3B30" }
},
"tags": {
"notice": { "hex": "#800020" },
"ctcp": { "hex": "#FF3B30" }
}
}
},
"dark": {
"ui": {
"background": { "hex": "#000000" },
"foreground": { "hex": "#FFFFFF" },
"accent": { "hex": "#0A84FF" },
"divider": { "hex": "#3C3C43" },
"topicBarBackground": { "hex": "#1C1C1E" },
"topicBarForeground": { "hex": "#FFFFFF" },
"badgeBackground": { "hex": "#1B2A40" },
"badgeForeground": { "hex": "#0A84FF" },
"sidebarSelectionBackground": { "hex": "#1B2A40" },
"sidebarSelectionForeground": { "hex": "#0A84FF" },
"highlightRowBackground": { "hex": "#FF000026" },
"expandedGroupRowBackground": { "hex": "#0A84FF14" },
"operatorBadgeColor": { "hex": "#9CA3AF" },
"awayBadgeColor": { "hex": "#9CA3AF" },
"secureBadgeColor": { "hex": "#30D158" },
"botBadgeColor": { "hex": "#9CA3AF" },
"blockedBadgeColor": { "hex": "#FF453A" },
"scriptEditorHighlightrTheme": "github-dark",
"joinBadge": "arrow.right.circle.fill",
"partBadge": "arrow.left.circle.fill",
"quitBadge": "arrow.left.circle.fill",
"actionBadge": "star.fill",
"noticeBadge": "exclamationmark.bubble.fill",
"infoBadge": "info.circle.fill",
"topicBadge": "text.quote"
},
"messageColors": {
"categories": {
"normal": { "hex": "#FFFFFF" },
"error": { "hex": "#FF453A" }
},
"tags": {
"notice": { "hex": "#FF6B81" },
"ctcp": { "hex": "#FF453A" }
}
}
}
}
Troubleshooting
- Theme not appearing in the picker
- Ensure the file extension is .json.
- Place it in: ~/Library/Application Support/Rete/Themes
- Validate JSON syntax and schema. The app currently ignores malformed files silently, but with debug logs enabled you’ll see:
- Theme directory path
- List of candidate files
- Decoding errors per file
- Decoding error mentioning “Expected to decode Dictionary<String, Any> but found a string”
- You likely used a plain string for a color. Use an object: “background”: { “hex”: “#RRGGBB” }.
- Alpha/transparency in hex
- Not supported. Use 6-digit hex. Choose lighter/darker colors to simulate translucency.
Badge Icons
Themes can customize the SF Symbol icons used for status badges in the nicklist and user info tooltips:
- operatorBadge: SF Symbol name for IRC operator badge (default: “shield.checkered”)
- awayBadge: SF Symbol name for away user badge (default: “moon.fill”)
- secureBadge: SF Symbol name for secure connection badge (default: “lock.fill”)
- botBadge: SF Symbol name for bot user badge (default: “cpu”)
- blockedBadge: SF Symbol name for blocked user badge (default: “hand.raised.fill”)
These badges appear next to nicknames in the nicklist and in the user info tooltip when hovering over a nickname.
Example badge customization:
{
"name": "Custom Badges",
"operatorBadge": "star.fill",
"awayBadge": "zzz",
"secureBadge": "lock.shield.fill",
"botBadge": "cpu",
"blockedBadge": "hand.raised.fill",
"light": { ... },
"dark": { ... }
}
Common SF Symbol alternatives:
- Operator: “star.fill”, “crown.fill”, “badge.shield.checkered”, “checkmark.seal.fill”
- Away: “moon.fill”, “moon.stars.fill”, “zzz”, “bed.double.fill”
- Secure: “lock.fill”, “lock.shield.fill”, “checkmark.shield.fill”, “lock.circle.fill”
- Blocked: “hand.raised.fill”, “hand.raised.slash.fill”, “xmark.circle.fill”, “nosign”
If not specified, the default icons are used. The badge icons apply to both light and dark variants.
Message Badge Symbols
Themes can customize the SF Symbol icons used for message type badges in the sender column. These badges appear instead of nicknames for non-PRIVMSG messages (joins, parts, quits, actions, notices, info, and topic messages).
The following optional fields can be set in the ui object of each palette (light/dark):
- joinBadge: SF Symbol name for join messages (default: “arrow.right.circle.fill”)
- partBadge: SF Symbol name for part messages (default: “arrow.left.circle.fill”)
- quitBadge: SF Symbol name for quit messages (default: “arrow.left.circle.fill”)
- actionBadge: SF Symbol name for action (/me) messages (default: “star.fill”)
- noticeBadge: SF Symbol name for notice messages (default: “exclamationmark.bubble.fill”)
- infoBadge: SF Symbol name for info messages (default: “info.circle.fill”)
- topicBadge: SF Symbol name for topic messages (default: “text.quote”)
Example message badge customization:
{
"name": "Custom Message Badges",
"light": {
"ui": {
"joinBadge": "arrow.right.circle.fill",
"partBadge": "arrow.left.circle.fill",
"quitBadge": "arrow.left.circle.fill",
"actionBadge": "star.fill",
"noticeBadge": "exclamationmark.bubble.fill",
"infoBadge": "info.circle.fill",
"topicBadge": "text.quote",
...
},
...
},
"dark": {
"ui": {
"joinBadge": "arrow.right.circle.fill",
"partBadge": "arrow.left.circle.fill",
"quitBadge": "arrow.left.circle.fill",
"actionBadge": "star.fill",
"noticeBadge": "exclamationmark.bubble.fill",
"infoBadge": "info.circle.fill",
"topicBadge": "text.quote",
...
},
...
}
}
Common SF Symbol alternatives:
- Join: “arrow.right.circle.fill”, “arrow.down.circle.fill”, “person.badge.plus”, “arrow.turn.down.right”
- Part/Quit: “arrow.left.circle.fill”, “arrow.up.circle.fill”, “arrow.turn.up.left”, “person.badge.minus”
- Action: “star.fill”, “sparkles”, “star.circle.fill”, “star.square.fill”
- Notice: “exclamationmark.bubble.fill”, “bell.fill”, “exclamationmark.triangle.fill”, “info.bubble.fill”
- Info: “info.circle.fill”, “info.square.fill”, “info”, “circle.fill”
- Topic: “text.quote”, “quote.bubble.fill”, “text.bubble.fill”, “doc.text.fill”
If not specified, the default icons are used. Badge symbols can be customized independently for light and dark variants.
Font Configuration
Themes can specify fonts for message content to customize the appearance and enable IRC script alignment compatibility. The font configuration applies consistently throughout the app to all message content (messages, topics, raw buffer, notices).
Font Structure
The optional fonts object can be added to any ThemePalette (light or dark variant):
{
"fonts": {
"messageFontName": "SF Mono",
"messageFontDesign": "monospaced",
"messageFontSize": "body"
}
}
Font Fields
- messageFontName (optional): String
- Custom font name to use (e.g., “SF Mono”, “Menlo”, “Courier New”, “Monaco”)
- Must be a font name available on the system
- If the font is not found, the system font with the specified design will be used instead
- Leave empty or omit to use system fonts only
- messageFontDesign (optional): String
- Font design variant:
"default","monospaced","serif", or"rounded" - Default:
"default" - Use
"monospaced"for IRC script alignment compatibility (fixed-width characters) - If
messageFontNameis specified and found, this design parameter is ignored
- Font design variant:
- messageFontSize (optional): String
- Font size:
"small","body", or"large" - Default:
"body" - Maps to:
small→.callout,body→.body,large→.title3
- Font size:
Font Resolution
- If
messageFontNameis specified and the font exists on the system, use that custom font - Otherwise, use system font with the specified
messageFontDesign - If no font configuration is provided, default to system body font (
.body)
Examples
Monospace font for IRC alignment:
{
"light": {
"fonts": {
"messageFontDesign": "monospaced"
},
...
},
"dark": {
"fonts": {
"messageFontDesign": "monospaced"
},
...
}
}
Custom font with specific design:
{
"light": {
"fonts": {
"messageFontName": "SF Mono",
"messageFontSize": "body"
},
...
},
"dark": {
"fonts": {
"messageFontName": "SF Mono",
"messageFontSize": "body"
},
...
}
}
Design-only (no custom font name):
{
"light": {
"fonts": {
"messageFontDesign": "monospaced",
"messageFontSize": "small"
},
...
}
}
Notes
- Font configuration is optional. Themes without
fontswill use the default system font - Font configuration can be specified independently for light and dark variants
- The font applies to all message content: MessageRow text, TopicBar topic text, raw buffer, and notices
- UI elements (timestamps, badges, buttons) continue to use their standard fonts
- For IRC script alignment, use
"monospaced"design or a custom monospace font name
Script Editor Syntax Highlighting
Themes can customize the syntax highlighting theme used in the script editor (TCL script editor). This uses the Highlightr library, which supports many popular code editor themes.
Configuration
The optional scriptEditorHighlightrTheme field can be set in the ui object of each palette (light/dark):
{
"light": {
"ui": {
"scriptEditorHighlightrTheme": "github",
...
},
...
},
"dark": {
"ui": {
"scriptEditorHighlightrTheme": "github-dark",
...
},
...
}
}
Available Highlightr Themes
Common themes available in Highlightr include:
Light themes:
github(default for light mode)xcodedefaultatom-one-lightvsmonokai-sublime(light variant)
Dark themes:
github-dark(default for dark mode)xcode-darkatom-one-darkmonokaidraculavs2015tomorrow-night
The complete list of available themes depends on the Highlightr library version. Common themes that work well with TCL syntax include:
github/github-dark- Clean, readable (default)xcode/xcode-dark- Matches Xcode’s editormonokai- Popular dark themeatom-one-dark/atom-one-light- Atom editor styledracula- Popular dark theme with vibrant colors
Examples
Using different themes for light and dark:
{
"light": {
"ui": {
"scriptEditorHighlightrTheme": "github",
...
}
},
"dark": {
"ui": {
"scriptEditorHighlightrTheme": "monokai",
...
}
}
}
Using the same theme for both:
{
"light": {
"ui": {
"scriptEditorHighlightrTheme": "xcode",
...
}
},
"dark": {
"ui": {
"scriptEditorHighlightrTheme": "xcode-dark",
...
}
}
}
Notes
- If
scriptEditorHighlightrThemeis not specified, the editor uses"github"for light mode and"github-dark"for dark mode - The theme applies only when the Highlightr package is available in the app
- Each palette (light/dark) can specify its own Highlightr theme independently
- The syntax highlighting automatically adapts to theme changes when switching between light and dark modes
Versioning
- The Theme structure supports a version string. There’s no strict validation yet, but you can use it to track your theme iterations.