Wayle Button Styling System

Complete architecture map for theme/style system in wayle-settings

1. Crate Location

Path: /home/stubbe/git/private/wayle/crates/wayle-settings

Button rendering uses the wayle-widgets component library and wayle-styling for CSS/themes.

2. UI Framework

Framework: Relm4 (Elm-inspired reactive UI library)

Rendering: GTK4

Styling: CSS (via GTK's CSS provider system)

crates/wayle-settings/Cargo.toml
gtk4.workspace = true relm4.workspace = true wayle-widgets = { path = "../wayle-widgets" } wayle-styling = { path = "../wayle-styling" }

3. Theme System Architecture

Dynamic Theme Generation (Runtime CSS Variables)

At startup, Rust generates CSS custom properties (variables) based on config values. These are injected as a :root { ... } block.

crates/wayle-styling/src/lib.rs:54–151
pub fn theme_css(palette, general, bar, styling) → String { // Generates :root { // --palette-bg, --palette-surface, etc. // --bar-btn-icon-size, --bar-btn-icon-padding, etc. // --cfg-rounding-element, --cfg-bar-button-rounding-element // } }

Static SCSS Compiled at Build Time

Base CSS is compiled from SCSS source once, embedded in binary, enhanced by runtime theme variables.

crates/wayle-styling/src/lib.rs:39
pub const STATIC_CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/style.css"));

4. Button Colors & Theme Tokens

Palette Colors (Runtime CSS Variables)

Generated dynamically from active palette (Wayle, Matugen, Wallust, or Pywal):

crates/wayle-styling/src/lib.rs:99–150
:root { --palette-bg: #1a1a2e; --palette-surface: #16213e; --palette-elevated: #0f3460; --palette-fg: #eaeaea; --palette-fg-muted: #a8a8a8; --palette-primary: #e94560; --palette-red: #e74c3c; --palette-yellow: #f39c12; --palette-green: #27ae60; --palette-blue: #3498db; --dropdown-surface: rgba(10, 20, 34, 0.95); }

Button-Specific Colors (Per-Button CSS Variables)

Set inline by the BarButton component for each button instance:

crates/wayle-widgets/src/components/bar_buttons/styling.rs:84–95
format!( "* {{ \ --bar-btn-icon-color: {}; \ --bar-btn-label-color: {}; \ --bar-btn-icon-bg: {}; \ --bar-btn-bg: {}; \ --bar-btn-border-color: {}; \ --bar-btn-border-width: {}px; \ }}", icon_color, label_color, icon_bg, button_bg, border_color, border_width )

5. Border Radius & Rounding Configuration

Configurable Rounding Levels

User selects global rounding preference; translates to semantic tokens:

Rounding Level Element Value Container Value
none --radius-none (0px) --radius-none (0px)
sm --radius-sm (0.375rem ≈ 6px) --radius-md (0.625rem ≈ 10px)
md (default) --radius-md (0.625rem ≈ 10px) --radius-lg (0.875rem ≈ 14px)
lg --radius-lg (0.875rem ≈ 14px) --radius-xl (1.125rem ≈ 18px)
full --radius-full (9999px, pill shape) --radius-xl (1.125rem ≈ 18px)

Bar Button Rounding (Independent Override)

crates/wayle-config/src/schemas/bar/mod.rs:170–233
pub button_rounding: ConfigProperty<RoundingLevel>, pub button_group_rounding: ConfigProperty<RoundingLevel>,

Maps to CSS variables at runtime:

--cfg-bar-button-rounding-element: var(--bar-button-radius-md); --cfg-bar-group-rounding-element: var(--radius-full);

Applied in SCSS:

crates/wayle-styling/scss/components/bar_button/_base.scss:17
border-radius: var(--bar-button-rounding-element);

6. Button Sizing Configuration

Base Pixel Values (SCSS Constants)

Dimension Value In Pixels (Global Scale × 1.0)
Icon Size $base-btn-icon-size: 1.6rem ≈ 25.6px
Label Size $base-btn-label-size: 1.04rem ≈ 16.6px
Button Padding $base-btn-padding: 0.5rem ≈ 8px
Icon-Label Gap $base-btn-gap: 0.5rem ≈ 8px

Runtime CSS Variables (with Bar Scale)

crates/wayle-styling/scss/tokens/_index.scss:185–207
--bar-btn-icon-size: var( --bar-btn-icon-size-override, calc(1.6rem * var(--bar-scale) * var(--bar-btn-icon-scale)) ); --bar-btn-icon-padding: var( --bar-btn-icon-padding-override, calc(0.5rem * var(--bar-scale) * var(--bar-btn-icon-padding-scale)) ); --bar-btn-label-size: var( --bar-btn-label-size-override, calc(1.04rem * var(--bar-scale) * var(--bar-btn-label-size-scale)) ); --bar-btn-label-padding: var( --bar-btn-label-padding-override, calc(0.5rem * var(--bar-scale) * var(--bar-btn-label-padding-scale)) ); --bar-btn-gap: var( --bar-btn-gap-override, calc(0.5rem * var(--bar-scale) * var(--bar-btn-gap-scale)) );

Config Properties (Settable by User)

crates/wayle-config/src/schemas/bar/mod.rs:147–178
pub button_icon_size: ConfigProperty<Size>, // Scale or px pub button_icon_padding: ConfigProperty<Size>, // Scale or px pub button_label_size: ConfigProperty<Size>, // Scale or px pub button_label_padding: ConfigProperty<Size>, // Scale or px pub button_gap: ConfigProperty<Size>, // Scale or px

7. Button Variants (Visual Styles)

Three mutually exclusive rendering modes, each with distinct styling:

Basic Variant

Structure: Padding applied to content box; gap as margin on label

crates/wayle-styling/scss/components/bar_button/_base.scss:45–86
&.basic .bar-button-content { margin: var(--bar-btn-label-padding); } &.basic .bar-button-label { margin-left: var(--bar-btn-gap); }

Colors applied: Icon color, label color, button background

IconSquare Variant

Structure: Icon in colored square inside button padding

crates/wayle-styling/scss/components/bar_button/_icon_square.scss:4–59
.icon-container { background-color: var(--bar-btn-icon-bg); border-radius: var(--bar-button-rounding-element); transition: background-color var(--duration-fast); margin: calc(var(--bar-btn-icon-padding) * 0.3); } .icon-container image { color: var(--bar-btn-icon-color); } &:hover .icon-container { filter: brightness(1.2); }

Hover state: brightness(1.2) on icon container

BlockPrefix Variant

Structure: Icon in colored container bleeding to button edge

crates/wayle-styling/scss/components/bar_button/_block_prefix.scss:5–55
.icon-container { background-color: var(--bar-btn-icon-bg); border-top-left-radius: var(--bar-button-rounding-element); border-bottom-left-radius: var(--bar-button-rounding-element); } .icon-container image { color: var(--bar-btn-icon-color); margin: var(--bar-btn-icon-padding); } &:hover .icon-container { filter: brightness(1.1); }

Hover state: brightness(1.1) on icon container

Note: Radius adapts to orientation (vertical) and icon position (end)

8. Hover & Active States

Defined via CSS filters and brightness modulation:

Variant Hover Filter Active (:checked) Filter
Basic brightness(1.15) brightness(0.95)
IconSquare brightness(1.2) on icon-container —
BlockPrefix brightness(1.1) on icon-container —

Focus Ring

crates/wayle-styling/scss/components/bar_button/_base.scss:28–31
&:focus-visible > button.toggle { outline: var(--focus-ring-width) solid var(--fg-muted); outline-offset: calc(0.125rem * var(--bar-scale)); }

9. Button Component (Rust Implementation)

Component Constructor

crates/wayle-widgets/src/components/bar_buttons/component.rs:25–40
#[derive(Debug, Clone)] pub struct BarButtonInit { pub icon: String, pub label: String, pub tooltip: Option<String>, pub colors: BarButtonColors, pub behavior: BarButtonBehavior, pub settings: BarSettings, }

View Template (Relm4)

crates/wayle-widgets/src/components/bar_buttons/component.rs:93–176
view! { #[root] gtk::MenuButton { set_css_classes: &model.css_classes(), #[wrap(Some)] set_child = >k::Box { add_css_class: "bar-button-content", set_orientation: model.orientation(), gtk::Box { add_css_class: "icon-container", ... }, gtk::Box { add_css_class: "label-container", ... }, }, } }

CSS Classes Applied Dynamically

crates/wayle-widgets/src/components/bar_buttons/component.rs:281–308
fn css_classes(&self) -> Vec<&'static str> { let mut classes = vec!["bar-button"]; classes.push(match self.variant { BarButtonVariant::Basic => "basic", BarButtonVariant::BlockPrefix => "block-prefix", BarButtonVariant::IconSquare => "icon-square", }); if !self.behavior.show_label.get() { classes.push("icon-only"); } if !self.behavior.show_icon.get() { classes.push("label-only"); } if self.settings.is_vertical.get() { classes.push("vertical"); } if self.behavior.show_border.get() { classes.push(border_class); } if let Some(pos_class) = self.settings.icon_position.get().css_class() { classes.push(pos_class); } classes }

10. Shared Theme Token Modules

Design Tokens (Global Palette & Semantic Tokens)

crates/wayle-styling/scss/tokens/_index.scss
// Compiled at build time; enhanced at runtime by Rust $base-space-xs: 0.25rem; $base-space-sm: 0.5rem; $base-space-md: 1rem; :root { // Color tokens --palette-bg, --palette-surface, --palette-fg, --palette-primary // Spacing (scaled by global-scale) --space-xs: calc(0.25rem * var(--global-scale)); --space-md: calc(1rem * var(--global-scale)); // Rounding --radius-sm: calc(0.375rem * var(--global-scale)); --radius-md: calc(0.625rem * var(--global-scale)); // Button-specific --bar-btn-icon-color, --bar-btn-label-color, --bar-btn-bg }

Palette Definition

crates/wayle-styling/scss/_palette.scss
// Maps palette colors via CSS custom properties // Values come from Rust at :root level

Theme Generation (Rust)

crates/wayle-styling/src/lib.rs:54–151 (theme_css function)
Generates :root block with: - Palette colors (--palette-*) - Rounding tokens (--cfg-rounding-element, --cfg-bar-button-rounding-element) - Size scale multipliers (--bar-scale, --bar-btn-icon-scale) - Font family tokens (--cfg-font-sans, --cfg-font-mono)

11. CSS Loading & Priority

crates/wayle-settings/src/app/css.rs:10–46
pub fn load_css(config_service: &ConfigService) -> CssProvider { let provider = CssProvider::new(); let css = build_css(config_service); // Combines STATIC_CSS + theme_css + user_css provider.load_from_string(&css); style_context_add_provider_for_display( &display, &provider, STYLE_PROVIDER_PRIORITY_USER + 100 // User+100 priority ); provider } pub fn build_css(config_service: &ConfigService) -> String { let palette = config.styling.palette(); let theme = theme_css(&palette, &config.general, &config.bar, &config.styling); let user = user_css(config_dir); format!("{STATIC_CSS}\n{theme}\n{user}") }

12. Key File References

Purpose File Path
Button component (Relm4) crates/wayle-widgets/src/components/bar_buttons/component.rs
Button styling (CSS generation) crates/wayle-widgets/src/components/bar_buttons/styling.rs
Button SCSS (all variants) crates/wayle-styling/scss/components/bar_button/ (4 files)
Design tokens (SCSS) crates/wayle-styling/scss/tokens/_index.scss
Theme generation (Rust) crates/wayle-styling/src/lib.rs:54–151
CSS loading (app startup) crates/wayle-settings/src/app/css.rs
Rounding config types crates/wayle-config/src/schemas/styling/types/rounding.rs
Bar config schema crates/wayle-config/src/schemas/bar/mod.rs