Skip to content

Guide Theming

MeowLynxSea edited this page Jan 26, 2026 · 9 revisions

Theming

Yororen UI ships with built-in light/dark palettes, but you can also provide your own.

How theming works

  • Yororen UI stores the active palette in a gpui::Global called GlobalTheme.
  • Components access it via ActiveTheme (cx.theme()).

Default behavior

If you install the default global theme:

use gpui::App;
use yororen_ui::theme::GlobalTheme;

fn init_theme(cx: &mut App) {
    cx.set_global(GlobalTheme::new(cx.window_appearance()));
}

Yororen UI will choose Theme::default_light() or Theme::default_dark() based on the current WindowAppearance.

Custom palettes

Use ThemeSet + GlobalTheme::new_with_themes.

  • Light theme is required.
  • Dark theme is optional.
  • If you do not provide a dark theme, Yororen UI will always use the light theme (even when the system/window appearance is dark).
use gpui::App;
use yororen_ui::theme::{GlobalTheme, Theme, ThemeSet};

fn init_theme(cx: &mut App) {
    let light = Theme::default_light();

    // Optional:
    let dark = Theme::default_dark();

    cx.set_global(GlobalTheme::new_with_themes(
        cx.window_appearance(),
        ThemeSet::new(light).dark(dark),
    ));
}

Light-only (force light mode)

use gpui::App;
use yororen_ui::theme::{GlobalTheme, Theme, ThemeSet};

fn init_theme(cx: &mut App) {
    let light = Theme::default_light();

    // No `.dark(...)`.
    cx.set_global(GlobalTheme::new_with_themes(
        cx.window_appearance(),
        ThemeSet::new(light),
    ));
}

Theme contract (required colors)

Yororen UI's Theme is not just a "primary color". It's a full color contract used across components.

When you provide a custom theme, you must provide a complete Theme value for the light palette (and optionally another complete Theme for the dark palette).

Theme

  • surface

    • canvas: app/window background
    • base: default surface background (cards/inputs)
    • raised: elevated surface (popovers/title areas)
    • sunken: recessed surface (tracks, wells)
    • hover: hover surface fill
  • content

    • primary: primary text
    • secondary: secondary text
    • tertiary: tertiary/hint text (placeholders)
    • disabled: disabled text
    • on_primary: text on top of action.primary.bg
    • on_status: text on top of status.*.bg
  • border

    • default: default border
    • muted: muted border
    • focus: focus ring / focused border
    • divider: divider line
  • action

    • neutral, primary, danger (each is an ActionVariant)
  • status

    • success, warning, error, info (each is a StatusVariant)
  • shadow

    • elevation_1
    • elevation_2

ActionVariant

Each action variant must provide:

  • bg
  • hover_bg
  • active_bg
  • fg
  • disabled_bg
  • disabled_fg

StatusVariant

Each status variant must provide:

  • bg
  • fg

Accessibility and consistency tips

  • Ensure adequate contrast for at least these pairs:
    • surface.base vs content.primary
    • action.neutral.bg vs action.neutral.fg
    • action.primary.bg vs action.primary.fg
    • status.*.bg vs status.*.fg
  • Keep content.on_primary / content.on_status as "text on top of a solid color" values.

Clone this wiki locally