<!-- Generated by community/.ai/render-guides.mjs; edit community/.agents/skills/jewel-ui/SKILL.md -->
Jewel UI
Implement UI with Jewel by first selecting the runtime context, then applying the correct theme wrapper, then using
Jewel components and icon APIs.
Quick Snippets
Standalone:
IntUiTheme(isDark = false) {
App()
}
IntelliJ plugin:
SwingBridgeTheme {
App()
}
Icon loading:
object MyIcons {
val Settings = PathIconKey("icons/settings.svg", MyIcons::class.java)
}
Icon(key = MyIcons.Settings, contentDescription = "Settings")
Decorated window (standalone, custom title bar):
IntUiTheme(
theme = themeDefinition,
styling = ComponentStyling.default().decoratedWindow(
titleBarStyle = TitleBarStyle.light(),
),
) {
DecoratedWindow(onCloseRequest = ::exitApplication) {
TitleBar { /* title bar content */ }
App()
}
}
Customize the title bar (colors, metrics, and fullscreen-control handling) by passing TitleBarStyle to
ComponentStyling.default().decoratedWindow(...), not through IntUiTheme(isDark = ...) alone.
Scope Boundary
This skill covers Jewel's theme wrappers, components, layout, icons, and typography — the Compose side of the UI. It
does not cover Compose-in-Swing embedding mechanics. If the user's question is primarily about:
- Registering
ToolWindowFactory / ToolWindow content
- Instantiating
ComposePanel or wiring setContent
plugin.xml tool window extensions
enableNewSwingCompositing and related compositing/AWT flags
LocalComponent and Swing ↔ Compose bidirectional integration
…the correct response is to name jewel-swing-interop explicitly as the skill for that work and stop. Do not walk
through ToolWindowFactory / ComposePanel / plugin.xml setup here, even if you know how. You can still state that
content inside the ComposePanel must use SwingBridgeTheme (a theming concern, in scope), and defer the rest to
jewel-swing-interop.
Classify Runtime Context
Decide runtime before writing code:
- IntelliJ Platform plugin: use the Swing bridge (
SwingBridgeTheme) and IntelliJ bundled modules.
- Standalone Compose Desktop app: use
IntUiTheme and standalone Jewel dependencies.
For agent-generated desktop UI prototypes, prefer a Jewel/desktop preview or sandbox when one is available. Choose
Material-style mobile previews only deliberately for mobile/Android-targeting prototypes or when the user explicitly
wants a mobile device-frame preview; Material styling is not the general desktop default.
If context is unclear, inspect build files and imports first:
org.jetbrains.jewel.bridge.theme.SwingBridgeTheme implies plugin context.
org.jetbrains.jewel.intui.standalone.theme.IntUiTheme implies standalone context.
Use STANDALONE-VS-BRIDGE.md for dependency and wrapper snippets.
Keep IntelliJ Platform APIs Out Of Composition
Do not leak or access IntelliJ Platform APIs (IJPL) directly or indirectly inside Jewel/Compose composition.
Composables must render explicit state and emit events; IntelliJ Platform interaction belongs at the presenter,
controller, service, or action boundary.
Avoid calling or indirectly reaching from composition into platform services, application state, registry, action
system, project model, PSI, VFS, message bus, disposables, or other platform-owned mutable/thread-bound APIs. This
protects recomposition stability, preview/test isolation, and compatibility across IntelliJ Platform updates.
Preferred pattern:
- Platform boundary reads/writes IntelliJ state and owns threading/lifecycle concerns.
- The boundary maps that platform state into stable UI state for the composable.
- Composable renders the state and invokes callbacks.
- Boundary handles callbacks and performs platform work outside composition.
If direct or indirect platform access from composition is unavoidable, pass an injected collaborator as a parameter or
composition-local dependency that tests can replace with a fake. Keep the collaborator surface minimal and exclude
platform objects from stable UI state. Manage threading, read/write actions, disposables, and Compose stability
explicitly.
Apply Theming Correctly
Use the simplest valid theme API first:
- Standalone quick start:
IntUiTheme(isDark = ...).
- For advanced standalone configuration, call
IntUiTheme(theme = ..., styling = ..., swingCompatMode = ...).
- IntelliJ plugin:
SwingBridgeTheme { ... }.
When implementing custom standalone themes:
- Build
ThemeDefinition via JewelTheme.lightThemeDefinition(...) or JewelTheme.darkThemeDefinition(...).
- Override text styles using
JewelTheme.createDefaultTextStyle() and JewelTheme.createEditorTextStyle().
- Pass composed styling through
ComponentStyling.default().with(...) (or specialized style builders).
- Prefer public API packages; avoid internal/experimental APIs unless explicitly required.
Use THEMING.md for concrete patterns.
Use THEMING-COLORS.md for color-palette and semantic-color guidance.
Use TYPOGRAPHY.md for text-style guidance and when-to-use rules.
Key Theme-Derived Caches By Theme State
Do not key remember only by JewelTheme.name, a LaF name, or another human-readable label when the cached value is
derived from theme data. Colours, metrics, typography, icon data, and Swing defaults can change while the visible theme
name stays the same.
Prefer the narrowest correct key:
- If the calculation is cheap, skip
remember.
- If the cached value depends on concrete values, key by those concrete values, such as colours, text styles, metrics,
shortcut text, or strings.
- If the cached value depends on broad Jewel theme or LaF environment state that is not practical to enumerate, key by
JewelTheme.instanceUuid.
Use JewelTheme.instanceUuid as an invalidation token for theme-instance changes, not as a blanket replacement for
precise keys. Avoid JewelTheme.name as a proxy for theme state.
Pick the Right Component
Before writing component code, check COMPONENT-SELECTION.md for the when-to-use
rule. Jewel implements the JetBrains IntelliJ Platform UI Guidelines — those guidelines constrain which control fits a
given interaction, not just which APIs exist.
Most common decisions:
- Mutually exclusive pick from 2–4 options →
RadioButtonRow under a GroupHeader (label ends with :). Do not
use three DefaultButtons.
- For multi-select of independent boolean options, use a group of
CheckboxRow controls, and use
ThreeStateCheckbox for a "select all" parent.
- Pick from 5+ options, long labels, or less-frequent setting →
ListComboBox / ComboBox.
- Use
DefaultButton for the primary action in a form or dialog and OutlinedButton for secondary or cancel actions.
Never place two DefaultButton controls side by side.
- Icon-only buttons → wrap in a
Tooltip carrying the action name + keyboard shortcut. Every icon-only control needs
one.
- Text input →
TextField for one-line; TextArea for multi-line / newline-valid content (commit messages,
descriptions, code).
Use COMPONENT-SELECTION.md for the full decision tables and
LABEL-RULES.md for label-writing conventions.
Build UI With Jewel Components
Use Jewel components from org.jetbrains.jewel.ui.component for IntelliJ-styled UI.
- Use Compose layout containers (
Row, Column, Box) for composition.
- Use Jewel controls (
DefaultButton, OutlinedButton, TextField, Checkbox, Tabs, etc.) for interactive
primitives.
- Access colors and metrics through Jewel theme locals and component styling APIs instead of hardcoded values.
Use local samples as source-of-truth examples:
Use LAYOUT-PATTERNS.md for composition archetypes extracted from those sample apps.
Use COMPONENTS-CATALOG.md for component-by-component catalog guidance.
Keep Composables Small And Navigable
Treat composables like any other production code: keep them clean, clear, and as small as practical. Avoid huge
composables with deep nesting and mixed responsibilities. Extract sub-composables when a branch, section, row, toolbar,
footer, popup, or repeated layout block has a distinct purpose.
Organize files so readers can follow the UI top-down: put public entry-point composables near the top, then the private
sub-composables they call in usage order. Keep helper functions, constants, and state-holder glue close to the code that
uses them unless project conventions say otherwise.
Reusable composables are APIs. Give public or cross-file reusable composables KDoc that states their purpose, key
parameters, slot expectations, and any non-obvious behavior. For high-level API design, use the AndroidX Compose
guidance as a reference, adapted for Desktop/Jewel rather than Android-only APIs:
Do not copy those guidelines wholesale into this skill; link to the source so the guidance stays current.
Load Icons The Jewel Way
Use IconKey-based loading for portability across standalone and bridge:
- Use
PathIconKey(path, iconClass) when icon path is the same across old/new UI.
- Use
IntelliJIconKey(oldUiPath, newUiPath, iconClass) when paths differ.
- Use
Icon(key = ..., contentDescription = ...) or Image(iconKey = ...) instead of deprecated raw
painterResource.
- Use
AllIconsKeys for IntelliJ platform icons.
When using AllIconsKeys in standalone apps, make sure IntelliJ icons are on the classpath (recommended:
com.jetbrains.intellij.platform:icons).
Use PainterHint only when you need stateful or dynamic paths or runtime icon patching behavior.
When debugging icon-not-found errors, confirm runtime context (standalone vs plugin) before recommending a fix —
AllIconsKeys availability and classpath layout differ between the two. Ask the user if context is not clear from the
question.
Use ICONS.md for icon patterns and pitfalls.
Source Permalinks
When citing source in responses, prefer master links for always-latest behavior:
Version Discipline
Treat this skill as Jewel-version scoped.
- When version matters, check build files (
libs.versions.toml, build.gradle.kts) first; ask only if ambiguous.
- Validate compatibility against
Jewel release notes.
- Prefer APIs available in the target version; avoid suggesting newer APIs without stating the minimum version.
- If publishing a version-pinned variant of this skill, replace
master links with release-tag links.
Implementation Checklist
Before finishing:
- Confirm context-specific theme wrapper is correct (
IntUiTheme vs SwingBridgeTheme).
- Confirm icon code uses
IconKey and classpath-friendly resource resolution.
- Confirm dependencies match context.
- Confirm no unnecessary Material dependency is introduced in standalone flows.
- Confirm code compiles with current module and imports.
- Confirm composables do not directly or indirectly access IntelliJ Platform APIs; platform interaction stays at the
presenter/controller boundary or behind a tiny injected fakeable collaborator.
- Confirm large or deeply nested composables are split into clear sub-composables, file order follows top-down usage,
and reusable composables have useful KDoc.
- Confirm theme-derived
remember values are either uncached, keyed by the concrete values they use, or keyed by
JewelTheme.instanceUuid when broad theme/LaF state is the dependency; do not key them by JewelTheme.name alone.
- For standalone apps, confirm JetBrains Runtime is used (Jewel requires JBR for full font/rendering behavior).