Ayu Islands — a theme plugin with accent propagation engine, glow rendering, and in-IDE font management

Hi everyone,

I’ve been building Ayu Islands — a theme plugin for IntelliJ-based IDEs that started as a color scheme port of Ayu and grew into something more like an IDE customization engine. Currently at v2.5.3, built with Kotlin, ~250 commits over the past two months.

I wanted to share some of the more interesting implementation details and get feedback from people who know the platform well.

Accent color propagation

The core idea: a single accent color that flows through the entire UI — not just editor colors, but scrollbars, bracket matches, inlay hints, progress bars, and even third-party plugins (CodeGlance Pro, Indent Rainbow).

On top of that, users can pin accent colors to specific projects or programming languages. The plugin reads the project’s file breakdown via FileTypeManager and language statistics to detect the dominant language and apply the matching pin. Priority chain: project override → language override → global accent.

This required patching UIManager defaults, rewriting color scheme attributes on the fly, and notifying third-party plugins about accent changes through the message bus.

What’s next: chrome tinting

I’m currently working on accent-aware chrome tinting — similar to what Peacock does in VS Code, but integrated into the accent propagation pipeline. The idea: tint the StatusBar, Main Toolbar, Tool Window stripes, NavBar, and panel borders with the active accent color so that each project window has a visually distinct chrome. Combined with per-project overrides, your work project gets lavender chrome, your side project gets gold, etc.

The blending is done in HSB space — the accent’s hue replaces the stock surface hue while preserving most of the original luminance hierarchy, controlled by an intensity slider (0–50%). Stock colors are cached before tinting (ChromeBaseColors) to prevent saturation compounding on repeated applies. There’s also a cross-platform probe (ChromeDecorationsProbe) that detects whether the IDE is using native OS title bars or JBR custom decorations — Main Toolbar tinting is disabled when the plugin can’t reach that surface, same limitation Peacock hits on macOS.

This is on a feature branch and not shipped yet — would be curious to hear whether this is something people actually want in a JetBrains theme, since I’ve already gotten some feedback on that field.

Glow engine

Neon glow borders on Islands UI panels — rendered as a layered JPanel overlay via GlowGlassPane attached to tool window components. Three styles (Soft, Sharp Neon, Gradient) and three animation modes (Pulse, Breathe, Reactive) driven by GlowAnimator with javax.swing.Timer.

The tricky part was getting the overlay to track tool window geometry correctly — ComponentHierarchyUtils walks the Swing tree to find the right parent containers and EditorTabGeometry calculates exact border positions including tab strip offsets. Overlay repaints are throttled to avoid flicker during resize.

The glow color syncs with the active accent, including project overrides.

Color scheme depth

The color scheme defines 31 Kotlin-specific and 45 Java-specific tokens beyond platform defaults. Things like KOTLIN_MUTABLE_VARIABLE (underline effect), KOTLIN_SMART_CAST_VALUE / KOTLIN_SMART_CAST_RECEIVER (background tint), KOTLIN_NAMED_ARGUMENT (distinct foreground), and separate styling for labels, built-in annotations, and backing fields.

35+ language sections total, including Rust, Python, Go, PHP, Ruby, Terraform/HCL, and others — all hand-tuned rather than falling through to DEFAULT_* attributes.

In-IDE font management

Four curated coding font presets (Victor Mono, Maple Mono, Monaspace Neon, Monaspace Xenon) that download and install via FontInstaller. The installer extracts font files from GitHub release archives, registers them through java.awt.GraphicsEnvironment.registerFont(), and persists them to a known path. A consent dialog shows the exact install location before writing anything to disk. Delete unregisters and removes the files, reverting to JetBrains Mono.

Live preview renders a Kotlin snippet in FontPreviewComponent with the selected font/size/weight before applying.

Workspace controls

Auto-fit width for Project View, Commit panel, and Git panel using ToolWindowEx.stretchWidth() with configurable min/max bounds. A ComponentAdapter on the parent container triggers resize calculations. The Git panel internal splitter ratio (branches tree vs. file changes) is controlled via Splitter.setProportion().

Editor scrollbar visibility is toggled through EditorSettings on all open editors plus a FileEditorManagerListener for new ones.

Some numbers

  • 5,000+ downloads, 6 theme variants (Mirage/Dark/Light × Classic/Islands UI)

  • BSL-1.1 licensed (converts to Apache 2.0 in 2030)

  • Freemium: complete theme is free, customization engine is premium with a 30-day trial

  • The original Ayu author (dempfi) endorsed the project

Would love feedback — especially on the accent propagation approach (is there a better way than patching UIManager defaults at runtime?) and the glow overlay strategy. Also curious if anyone has run into similar challenges with tool window geometry tracking.

Marketplace | GitHub

2 Likes

For folks already on 2.5.x, here’s what landed in 2.6.0 — focused on per-window visual differentiation and a 2026.1 New UI compatibility fix that took longer than I’d like to admit.

Per-project / per-language chrome tinting

The workflow VS Code’s Peacock established — pin a color to a workspace, every chrome surface tints together — now lives in Ayu Islands. Built on JBUI.CurrentTheme primitives, not ported from VSCode. Pin to project or to language; status bar, nav bar (2026.1 Compact Navigation included), tool-window stripes, and panel borders all tint together. Intensity slider in Settings → Ayu Islands → Theme Synchronization, 0–50% range tuned for daily readability.

Foreground contrast is sampled per surface against the tinted band rather than the unblended base, so the WCAG pick stays in the light family across the full slider range.

Editor color scheme auto-bind

Free, default ON, toggled in Settings → Theme Synchronization. Switch Ayu Mirage / Dark / Light and the editor scheme follows. Never overwrites a custom user scheme — only re-binds when the active scheme is already one of ours.

2026.1 Compact Navigation path-widget fix

The 2026.1 New UI relocated the path breadcrumb into the status-bar tree. Pre-fix, chrome tinting wrote to the legacy breadcrumb keys and the new widget didn’t follow. Symptoms users hit:

  • Breadcrumb text turning BLACK at intensity ≥ 20% (WCAG was correctly picking BLACK against a mid-luminance “tinted band” surface — caller-side perception bug, not a contrast-algorithm bug)
  • Slider needing two clicks to land

Two layered root causes:

  1. Compact Navigation reads state-dependent JBUI.CurrentTheme.StatusBar.Breadcrumbs.* keys (hoverBackground, selectionBackground, selectionInactiveBackground, floatingBackground) — translucent overlay keys with different roles than the opaque fill keys (StatusBar.background) we were writing. Tinting an overlay key with opaque RGB stacks paint over the parent and produces mid-luminance bands that confuse the WCAG pick.
  2. The apply pipeline mutated state.chromeTintIntensity mid-flight, racing a LafManagerListener cascade that invalidated ChromeBaseColors and made blend reads observe tinted-as-base values — a fixed-point anomaly that surfaced as “the slider needs two clicks.”

Fix: introduced a ChromeTintContext ThreadLocal scoped override so applyForFocusedProject reads the pending intensity without mutating state until the apply succeeds; nulled the translucent overlay keys (so the LAF re-resolves stock translucency over our already-tinted parent); walked the live component tree to the NewNavBarPanel peer for explicit repaint; switched to a WCAG light-family palette that excludes BLACK.

Lesson tucked away for the next platform debug: stop running runtime experiments after the third failed cycle and decompile the relevant platform class instead.

Plugin ecosystem fixes

CodeGlance Pro and Indent Rainbow integrations now revert in lockstep with the Settings sync toggle — used to leak the accent until restart. Sticky-line panel transparency fixed by adding the missing STICKY_LINES_* color keys across all three editor schemes.

What’s next

Closing the remaining Peacock-parity items in the next few releases — shared accent committed to .idea/ for team workflows, Remote Environment awareness (Gateway / WSL / DevContainers / Code With Me), darken/lighten keyboard shortcuts, named favorites management, and Command Menu integration for every action. No specific dates.

Links

Feedback very welcome — JetBrains Platform questions especially. If you’ve hit similar patterns with translucent UIDefaults keys vs opaque overlays, or have cleaner ways to handle the LafManagerListener cascade, I’d love to hear it.