Docs


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

2025-12-15

2025-12-01

2025-11-27

2025-11-26

Where to put theme files

Notes:

JSON schema (overview)

Top-level object (Theme):

ThemePalette:

Fonts (optional):

UI (all colors are HexColor objects):

MessageColors:

HexColor:

Example HexColor: { "hex": "#0A84FF" }

Fallback behavior

If a color is missing or invalid:

Message color resolution:

  1. Category color is checked first (message.category.rawValue) - this is the primary classification
  2. Tag colors are checked only for contextual tags (highlight, bold, server, channel, dm, raw, in, out, script) that don’t map to categories
  3. If neither category nor tag color is defined, the UI foreground color is used
  4. 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:

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

Badge Icons

Themes can customize the SF Symbol icons used for status badges in the nicklist and user info tooltips:

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:

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):

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:

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

Font Resolution

  1. If messageFontName is specified and the font exists on the system, use that custom font
  2. Otherwise, use system font with the specified messageFontDesign
  3. 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

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:

Dark themes:

The complete list of available themes depends on the Highlightr library version. Common themes that work well with TCL syntax include:

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

Versioning