Theme system
The Qomicex Theme system (theme system)uses a three-tier primitive → semantic → component semantic token structure, carried by.qtheme theme packages, so the launcher and plugin UI reskin instantly and completely, with no plugin-ui dist rebuild.
The three-tier semantic model
- v1 scope:
.qthemeexpresses the semantic/component tiers directly (the flat--*variables); primitive derivation and thetheme.mjscomputation tier are deferred to v2 - plugin-ui components consume only
var(--*), with zero inline color values → switching themes reskins every component instantly, eliminating the stale “change a component, rebuild dist” problem
Semantic token naming
Dot-separated naming, where. normalizes to - (background.emphasis → --background-emphasis).
Color tokens (the standard component-level set = everything plugin-ui consumes)
Extended semantics (optional in v1, emitted as
--foreground-accent and so on, for forward compatibility): foreground.accent, foreground.muted, foreground.destructive, background.elevated, background.emphasis, background.sunken, border.strong, border.accent, accent.hover, accent.active, status.success, status.warning, status.error.
Non-color tokens
space.*, font.family/size/weight, motion.*, and shadow.* belong to the v2 non-color extensions.
.qtheme package format
theme.json schema v1
An invalid theme.json is rejected by
src/theme/schema.ts with a friendly error.
Theme manager
The theme manager (src/theme/manager.ts)provides:
applyTheme(json): validate, then inject CSS variables into:root→ every component reskins instantly; persists the theme id tolocalStorageclearTheme(): remove the custom theme and fall back to the settings layer light/dark/presetrestoreSavedTheme(): restore the registered theme at startupuseTheme(): React Hook that subscribes to change events and drives re-renderingregisterTheme(): register the bundled built-in.qthemepackages (including the four Catppuccin presets)
Theme contributions (contributes)
Plugins and theme packages can declare three kinds of contributions through manifests; v1 supports color themes only:Icon theme (icon-theme.json)
The Icon theme (icon theme)is a new file in the.qtheme package, alongside theme.json, that provides override mappings for icons such as plugin sidebar entries and menu items.
Format
Fields
Icon definition object
Render priority
ThePluginIcon component renders in this order:
- Look up the
srckey in the current theme’sicon-theme.json→ if it matches, render SVG/char/URL - No match and
srcis a URL →<img> - No match and
srcis a FontAwesome class name →<FontAwesomeIcon> - No match at all →
null
Registration
Icon themes are registered through the theme manager and bound to color themes:registerTheme(colorJson, iconJson?)— register a color and icon theme togetherregisterIconTheme(themeId, iconJson)— bind icons separately to an already registered color theme
getActiveIconTheme() or useIconTheme() (a React Hook). The mappings update automatically when the theme switches.
Font and ligature contribution
manifest declaration
Plugin manifests declare font resources through thecontributes field:
fontLinks(string[]): list of font CSS/CDN URLs. When the plugin activates, the launcher automatically injects<link rel="stylesheet">into<head>; it removes them when the plugin deactivates.- You can use the
font-feature-settingsscoping convention to enable ligatures in specific areas (see below).
Ligature scoping convention
The launcher predefines the.font-ligatures class (the plugin-internal equivalent is .p-font-ligatures); apply it to areas that need font ligatures (such as console logs or version diffs):
.p-font-ligatures for the same effect.
Relationship with plugin-ui
Hard rule: plugin-ui components may consume tokens only throughvar(--*); inline color values are forbidden. An audit confirms packages/plugin-ui/src/ has zero inline color values in total → switching themes reskins instantly with no dist rebuild.
Preset theme
The launcher bundles four Catppuccin Preset theme (preset theme)packages (latte / frappe / macchiato / mocha), already migrated to the new token naming. When you switch presets or apply a custom.qtheme, every UI component that consumes var(--*) responds immediately.
Plugin-driven Theme override
Plugins can dynamically override the launcher UI’s CSS variables through__PLUGIN_API__, reskinning at runtime without shipping a .qtheme package.
Priority levels
- The
<style>injected byapplyThemeOverridehas higher specificity than.qthemeand@layer base - If the user has set a theme color (the inline style from
themeColor.ts), inline styling takes over--primary/--ring/--primary-foreground, so overrides of those three do not take effect
API
Usage example
Security limits
- Token whitelist: only 30 predefined semantic tokens are allowed (
background,foreground,primary,accent,border,status-*, and so on) - HSL format validation: values must strictly match
H S% L%(such as142 71% 48%), rejecting arbitrary CSS injection - Tokens outside the whitelist and invalid formats are silently ignored
Lifecycle
applyThemeOverridecan be called repeatedly; the latest call overrides earlier ones- Overrides ARE cleared automatically when the plugin is disabled: the disable path unconditionally calls
clearThemeOverride, so the theme falls back to the active.qthemeor the built-in light/dark theme. You do not need to clean up in a disable hook; callclearThemeOverrideexplicitly only if you want to drop the override while the plugin stays enabled clearThemeOverrideremoves only the override styles; it does not affect.qthemeor the built-in light/dark themes