bread-theme: differentiate liquid-motion/glass-workbench launcher geometry per approved demos

The two built-in launcher themes previously shared the same row radius,
padding, icon treatment, and selection fill (only mode/width/top/radius/
icon_px were themed), so breadbox's overlay rendered as one launcher
recoloured rather than two different instruments.

Adds row_radius, row_inset, row_padding_v/h, icon_radius, search_font_size,
search_padding_v/h, and selection_alpha to [launcher], all consumed by
breadbox's build_css in the paired breadbox commit. Sets liquid-motion to
the soft/roomy demo numbers (row radius 12, 28px icons, 22% selection alpha,
Recent/Apps section headers) and glass-workbench to the dense/technical demo
numbers (row radius 6, 22px icons, 28% selection alpha, flat list).

Also wires up three previously-declared-but-unconsumed tokens
(font_family, font_fallback, font_size_base) for breadbox's launcher panel
specifically — scoped consumption, not the full ecosystem-wide font system
replacement (breadbar/stylesheet() are unaffected). Updated the stale
doc comments in types.rs/mod.rs and both theme.toml files to match, and
updated the two existing launcher-geometry tests plus added a glass-workbench
equivalent.

Defaults for all new keys reproduce breadbox's pre-redesign hardcoded CSS
values, so themes that don't set them (spotlight's capsule doesn't read any
of this) are unaffected.
This commit is contained in:
Breadway 2026-08-26 09:26:39 +08:00
parent 0ec20714c5
commit 3a22b62694
5 changed files with 202 additions and 76 deletions

View file

@ -11,18 +11,21 @@
# — it is new, so demo fidelity is the only source of truth. See Phase 5's # — it is new, so demo fidelity is the only source of truth. See Phase 5's
# task notes for the two exceptions: font (IBM Plex Sans is not installed on # task notes for the two exceptions: font (IBM Plex Sans is not installed on
# the dev machine, so this renders in fallback until the user installs # the dev machine, so this renders in fallback until the user installs
# ttf-ibm-plex) and colour (the demo's #7a9a88 sage is a *flat* accent, so it # ttf-ibm-plex — this now matters for real: breadbox's launcher panel
# maps to the palette's `green` token for both accent_from and accent_to — # consumes font_family/font_fallback below, see [tokens]'s own note) and
# never a literal hex value, or pywal theming breaks). # colour (the demo's #7a9a88 sage is a *flat* accent, so it maps to the
# palette's `green` token for both accent_from and accent_to — never a
# literal hex value, or pywal theming breaks).
name = "Glass Workbench" name = "Glass Workbench"
id = "glass-workbench" id = "glass-workbench"
[tokens] [tokens]
# Declared-but-not-yet-consumed: see liquid-motion/theme.toml's identical # font_family/font_fallback/font_size_base are consumed by breadbox's
# note next to these three keys — neither breadbar nor breadbox reads them; # launcher panel only (breadbox::main::build_css's entry.search/row rules —
# the font that actually renders comes from bread_theme::stylesheet()'s own # see Tokens::font_family's doc comment for the exact scope); breadbar's
# hardcoded constant instead. # bar/chip/clock text still comes from bread_theme::stylesheet()'s own
# hardcoded FONT_FAMILY constant, unaffected by this key.
font_family = "IBM Plex Sans" font_family = "IBM Plex Sans"
font_fallback = "Inter, Noto Sans, sans-serif" font_fallback = "Inter, Noto Sans, sans-serif"
font_size_base = 13 font_size_base = 13
@ -91,21 +94,34 @@ format = "%H:%M"
show_date = true show_date = true
[launcher] [launcher]
# Glass Workbench — dense and technical, the deliberate opposite of Liquid
# Motion's soft/roomy numbers: a tight panel radius, snug row insets/padding,
# small 22px icons, a flat (no section headers) list, and a denser accent
# selection fill. Spec: bos-ui-demos/proposed/glass-workbench.html's `.bx`
# block. breadbox reads every key below now; see main.rs's build_css.
mode = "overlay" mode = "overlay"
width = 560 width = 560
top = "64px" top = "64px"
radius = 12 radius = 10
icon_px = 32 icon_px = 22
row_anim = "stagger" row_anim = "stagger"
rule = "hairline" rule = "hairline"
footer = "count_results" footer = "count_results"
# Declared-but-not-consumed, exactly like liquid-motion's own launcher block:
# breadbox's Phase 4b-i wiring only reads mode/width/top/radius/icon_px.
# row_anim/rule/footer are this theme's demo-derived aspirations breadbox
# does not implement yet; sections/modes describe today's actual (trivial,
# apps-only) behaviour but are equally unread by any code path.
sections = false sections = false
modes = ["apps"] modes = ["apps"]
# `.bx .r { border-radius: 6px; margin: 0 6px; padding: 8px 10px }`
row_radius = 6
row_inset = 6
row_padding_v = 8
row_padding_h = 10
# `.bx .ico { border-radius: 5px }` (paired with icon_px = 22 above)
icon_radius = 5
# `.bx .q { padding: 12px 15px; font-size: 14px }`
search_font_size = 14
search_padding_v = 12
search_padding_h = 15
# `.bx .r.sel { background: rgba(accent, .28) }`
selection_alpha = 0.28
# Same four satellite namespaces as liquid-motion, keyed identically so the # Same four satellite namespaces as liquid-motion, keyed identically so the
# two manifests validate against the same [compositor.*] keyspace. Offsets # two manifests validate against the same [compositor.*] keyspace. Offsets

View file

@ -1,28 +1,31 @@
# The compiled-in builtin theme (bread-theme/src/shell/builtin.rs). Describes # The compiled-in builtin theme (bread-theme/src/shell/builtin.rs). The bar
# breadbar/breadbox AS THEY EXIST TODAY, not the 01-liquid-motion.html demo — # side (workspaces/clock/chips/satellites) describes breadbar AS IT EXISTS
# where the two disagree (bar side margin, launcher width/top, the two easing # TODAY, not the 01-liquid-motion.html demo — where the two disagree (bar
# curves), this file follows the current Rust source, since Phase 2's # side margin, the two easing curves), this file follows the current Rust
# acceptance test is pixel-identical rendering against today's bar. # source, since Phase 2's acceptance test is pixel-identical rendering
# against today's bar. The `[launcher]` table is the one exception: it now
# follows bos-ui-demos/proposed/liquid-motion.html's `.bx` block (the
# launcher-core redesign's spec) rather than breadbox's old, pre-redesign
# hardcoded CSS — see that section's own note.
# #
# Sources: breadbar/src/main.rs:16-22 (BAR_* / CHIP_HEIGHT / ICON_PX consts), # Sources: breadbar/src/main.rs:16-22 (BAR_* / CHIP_HEIGHT / ICON_PX consts),
# breadbar/src/theme.rs (load_css's radius/pad/spring locals and the actual # breadbar/src/theme.rs (load_css's radius/pad/spring locals and the actual
# CSS selectors), breadbar/src/{panel,osd}.rs and # CSS selectors), breadbar/src/{panel,osd}.rs and
# breadbar/src/notifications/{popup,history}.rs (satellite window anchors, # breadbar/src/notifications/{popup,history}.rs (satellite window anchors,
# margins, namespaces), breadbox/breadbox/src/main.rs:341-346 (launcher # margins, namespaces), bos-ui-demos/proposed/liquid-motion.html (launcher),
# margin/size), :151 (build_css's .launcher-bg radius), :174-193 (make_icon's # ~/.config/hypr/scripts/ui/rules.lua (compositor rules).
# set_pixel_size calls), ~/.config/hypr/scripts/ui/rules.lua (compositor
# rules).
name = "Liquid Motion" name = "Liquid Motion"
id = "liquid-motion" id = "liquid-motion"
[tokens] [tokens]
# Declared-but-not-yet-consumed: neither breadbar nor breadbox reads # font_family/font_fallback are now consumed, but only by breadbox's launcher
# font_family/font_fallback/font_size_base. Both get the font that actually # panel (breadbox::main::build_css's entry.search/row font-family) — see
# renders from bread_theme::stylesheet()'s own hardcoded FONT_FAMILY # Tokens::font_family's doc comment for the exact scope. breadbar's bar/chip/
# constant — a separate, ecosystem-wide system shared by every bread GUI, # clock text still comes from bread_theme::stylesheet()'s own hardcoded
# not this per-theme manifest. See Tokens::font_family's doc comment. # FONT_FAMILY constant, unaffected by this key. font_size_base is likewise
font_family = "Varela Round, sans-serif" # now consumed for the launcher's row font-size only.
font_family = "Outfit, sans-serif"
font_fallback = "sans-serif" font_fallback = "sans-serif"
font_size_base = 14 font_size_base = 14
radius_bar = 16 radius_bar = 16
@ -100,35 +103,34 @@ format = "%H:%M"
show_date = false show_date = false
[launcher] [launcher]
# breadbox/breadbox/src/main.rs:341-344 sets vbox.set_margin_top(120) and # Liquid Motion / Glass Workbench redesign (bos-ui-demos/proposed/
# vbox.set_size_request(600, -1) — the demo's "540px overlay, 16% top" is a # liquid-motion.html's `.bx` block is the spec): soft and roomy — a big
# design-doc approximation, not what the code does today. # panel radius, generous row insets/padding, 28px icons, section headers,
# and a translucent (not solid) accent selection fill. breadbox reads every
# key below now; see main.rs's build_css for the consuming CSS.
mode = "overlay" mode = "overlay"
width = 600 width = 600
top = "120px" top = "120px"
# .launcher-bg { border-radius: 8px } (build_css, main.rs:151) — corrected radius = 20
# from an earlier demo-derived 20px; breadbox does not implement the demo's icon_px = 28
# 20px rounding.
radius = 8
# make_icon() (main.rs:174-193) calls img.set_pixel_size(32) on both the
# cached-texture (gio::File/gdk::Texture) path and the GTK icon-theme
# fallback path — corrected from a demo-derived 36px.
icon_px = 32
# Declared-but-not-yet-consumed: breadbox's Phase 4b-i wiring reads only
# mode/width/top/radius/icon_px above; it does not read any of the five keys
# below. row_anim/rule/footer are demo-derived aspirations breadbox
# implements not at all today (no row entrance/exit animation, no
# rule/divider element between the search entry and the list, no footer
# widget summarizing app count). sections/modes happen to already describe
# today's actual (trivial) behaviour — one flat unsectioned list, plain app
# search with no calc/command/url modes — but are likewise unread by any
# code path; a real sections/modes implementation is Phase 5/6, same as the
# other three.
row_anim = "flip" row_anim = "flip"
rule = "gradient" rule = "gradient"
footer = "count_apps" footer = "count_apps"
sections = false sections = true
modes = ["apps"] modes = ["apps"]
# `.bx .r { border-radius: 12px; margin: 0 8px; padding: 10px 12px }`
row_radius = 12
row_inset = 8
row_padding_v = 10
row_padding_h = 12
# `.bx .ico { border-radius: 9px }` (paired with icon_px = 28 above)
icon_radius = 9
# `.bx .q { padding: 16px 18px; font-size: 16px }`
search_font_size = 16
search_padding_v = 16
search_padding_h = 18
# `.bx .r.sel { background: rgba(accent, .22) }`
selection_alpha = 0.22
# Keyed by layer-shell namespace, matching [compositor.*] below, so the two # Keyed by layer-shell namespace, matching [compositor.*] below, so the two
# tables share one keyspace and can be validated against each other. # tables share one keyspace and can be validated against each other.

View file

@ -194,6 +194,15 @@ pub(super) struct RawLauncher {
pub(super) modes: Option<Vec<String>>, pub(super) modes: Option<Vec<String>>,
pub(super) search_width: Option<i64>, pub(super) search_width: Option<i64>,
pub(super) search_radius: Option<i64>, pub(super) search_radius: Option<i64>,
pub(super) row_radius: Option<i64>,
pub(super) row_inset: Option<i64>,
pub(super) row_padding_v: Option<i64>,
pub(super) row_padding_h: Option<i64>,
pub(super) icon_radius: Option<i64>,
pub(super) search_font_size: Option<i64>,
pub(super) search_padding_v: Option<i64>,
pub(super) search_padding_h: Option<i64>,
pub(super) selection_alpha: Option<f64>,
} }
#[derive(Debug, serde::Deserialize)] #[derive(Debug, serde::Deserialize)]
@ -411,6 +420,22 @@ fn resolve_launcher(theme_id: &str, l: Option<&RawLauncher>) -> anyhow::Result<L
// never asked for (see the field docs on `Launcher`). // never asked for (see the field docs on `Launcher`).
search_width: l.and_then(|l| l.search_width).map(|v| v as i32).unwrap_or(width), search_width: l.and_then(|l| l.search_width).map(|v| v as i32).unwrap_or(width),
search_radius: l.and_then(|l| l.search_radius).map(|v| v as i32).unwrap_or(radius), search_radius: l.and_then(|l| l.search_radius).map(|v| v as i32).unwrap_or(radius),
// Defaults below reproduce breadbox's pre-redesign hardcoded CSS
// literals (`row { padding: 8px 12px; border-radius: 6px; }`,
// `listbox { padding: 4px; }`, no icon swatch, a solid — not
// alpha-blended — selection fill), so a theme that omits this whole
// block of new keys (spotlight does; its capsule doesn't read them
// at all) renders the same as before this change, not a jump to
// either built-in's specific numbers.
row_radius: l.and_then(|l| l.row_radius).unwrap_or(6) as i32,
row_inset: l.and_then(|l| l.row_inset).unwrap_or(4) as i32,
row_padding_v: l.and_then(|l| l.row_padding_v).unwrap_or(8) as i32,
row_padding_h: l.and_then(|l| l.row_padding_h).unwrap_or(12) as i32,
icon_radius: l.and_then(|l| l.icon_radius).unwrap_or(0) as i32,
search_font_size: l.and_then(|l| l.search_font_size).unwrap_or(16) as i32,
search_padding_v: l.and_then(|l| l.search_padding_v).unwrap_or(12) as i32,
search_padding_h: l.and_then(|l| l.search_padding_h).unwrap_or(16) as i32,
selection_alpha: l.and_then(|l| l.selection_alpha).unwrap_or(0.24),
}) })
} }

View file

@ -119,8 +119,14 @@ impl ShellTheme {
/// neither breadbar nor breadbox calls this method. Both hand-roll their /// neither breadbar nor breadbox calls this method. Both hand-roll their
/// own CSS instead (`breadbar::theme::load_css` reads individual /// own CSS instead (`breadbar::theme::load_css` reads individual
/// `Tokens::xxx()` accessors and `format!`s a literal stylesheet; /// `Tokens::xxx()` accessors and `format!`s a literal stylesheet;
/// `breadbox::main::build_css` reads only `launcher.radius`). That means /// `breadbox::main::build_css` reads a growing set of individual
/// the compiled-in `assets/shell/*/*.css` templates this method /// `Launcher`/`Tokens` accessors — `radius`, `width`, `icon_px`,
/// `row_radius`, `row_inset`, `row_padding_v/h`, `icon_radius`,
/// `search_font_size`, `search_padding_v/h`, `selection_alpha`,
/// `sections`, `footer`, `font_family`, `font_fallback`,
/// `font_size_base` as of the liquid-motion/glass-workbench redesign —
/// and still never calls this method or its template substitution).
/// That means the compiled-in `assets/shell/*/*.css` templates this method
/// substitutes into render nothing a user ever sees, and a theme's /// substitutes into render nothing a user ever sees, and a theme's
/// `css = "extra.css"` overlay (parsed, path-resolved, read from disk, /// `css = "extra.css"` overlay (parsed, path-resolved, read from disk,
/// token-substituted into [`Self::extra_css`] below) is fully /// token-substituted into [`Self::extra_css`] below) is fully
@ -606,18 +612,48 @@ mod tests {
} }
#[test] #[test]
fn builtin_launcher_matches_current_breadbox_geometry() { fn builtin_launcher_matches_liquid_motion_demo_geometry() {
// breadbox/breadbox/src/main.rs:341-346 (margin/size), :151 // bos-ui-demos/proposed/liquid-motion.html's `.bx` block is the spec
// (build_css's .launcher-bg radius), :174-193 (make_icon's // as of the launcher-core redesign — radius=20/icon_px=28, not the
// set_pixel_size calls). radius=8 and icon_px=32 are current CODE // old pre-redesign hardcoded CSS's 8/32 (see git history for that
// values, not the demo's 20/36 — see Phase 4b-i's manifest audit. // era's `builtin_launcher_matches_current_breadbox_geometry`).
let theme = resolve_builtin(); let theme = resolve_builtin();
let l = theme.launcher(); let l = theme.launcher();
assert!(matches!(l.mode, LauncherMode::Overlay)); assert!(matches!(l.mode, LauncherMode::Overlay));
assert_eq!(l.width, 600); assert_eq!(l.width, 600);
assert_eq!(l.top, "120px"); assert_eq!(l.top, "120px");
assert_eq!(l.radius, 8); assert_eq!(l.radius, 20);
assert_eq!(l.icon_px, 32); assert_eq!(l.icon_px, 28);
assert!(l.sections, "liquid-motion shows Recent/Apps section headers");
assert_eq!(l.row_radius, 12);
assert_eq!(l.row_inset, 8);
assert_eq!((l.row_padding_v, l.row_padding_h), (10, 12));
assert_eq!(l.icon_radius, 9);
assert_eq!(l.search_font_size, 16);
assert_eq!((l.search_padding_v, l.search_padding_h), (16, 18));
assert!((l.selection_alpha - 0.22).abs() < f64::EPSILON);
}
#[test]
fn glass_workbench_launcher_matches_demo_geometry_and_differs_from_liquid_motion() {
let theme = load_named(builtin::GLASS_WORKBENCH_ID)
.expect("glass-workbench should resolve");
let l = theme.launcher();
assert_eq!(l.width, 560);
assert_eq!(l.top, "64px");
assert_eq!(l.radius, 10);
assert_eq!(l.icon_px, 22);
assert!(
!l.sections,
"glass-workbench is a flat list — no Recent/Apps headers"
);
assert_eq!(l.row_radius, 6);
assert_eq!(l.row_inset, 6);
assert_eq!((l.row_padding_v, l.row_padding_h), (8, 10));
assert_eq!(l.icon_radius, 5);
assert_eq!(l.search_font_size, 14);
assert_eq!((l.search_padding_v, l.search_padding_h), (12, 15));
assert!((l.selection_alpha - 0.28).abs() < f64::EPSILON);
} }
#[test] #[test]

View file

@ -189,7 +189,8 @@ pub struct Modules {
pub clock: ClockModule, pub clock: ClockModule,
} }
#[derive(Debug, Clone, PartialEq, Eq)] // No `Eq`: `selection_alpha` is an `f64`, which doesn't implement it.
#[derive(Debug, Clone, PartialEq)]
pub struct Launcher { pub struct Launcher {
pub mode: LauncherMode, pub mode: LauncherMode,
pub width: i32, pub width: i32,
@ -198,7 +199,20 @@ pub struct Launcher {
pub icon_px: i32, pub icon_px: i32,
pub row_anim: String, pub row_anim: String,
pub rule: String, pub rule: String,
/// Selects the footer label's noun — `"count_apps"` renders
/// `"{n} applications"`, anything else (`"count_results"` included)
/// renders `"{n} results"`. Consumed by `breadbox::main`'s footer label
/// (appended below `ResultsList::scroller`, updated on every
/// `set_query` call alongside the visible-row count).
pub footer: String, pub footer: String,
/// Consumed by breadbar's embedded capsule (`breadbar::main::run_ui`,
/// passed straight to `ResultsList::new`) for every theme, and by
/// breadbox's own overlay (`breadbox::main::run_ui`) as of the
/// liquid-motion/glass-workbench redesign — `true` groups the idle
/// (empty-query) view into "Recent"/"Apps" headers via
/// `bread_launcher::gtk::split_sections`, `false` reproduces the flat
/// list. breadbox no longer hardcodes `false` at its `ResultsList::new`
/// call site; it reads this field.
pub sections: bool, pub sections: bool,
pub modes: Vec<String>, pub modes: Vec<String>,
/// `LauncherMode::Embedded` only (theme 04/spotlight, plan §7 phase 6c): /// `LauncherMode::Embedded` only (theme 04/spotlight, plan §7 phase 6c):
@ -214,6 +228,38 @@ pub struct Launcher {
/// vs the collapsed 22px `radius`). Same default-to-`radius` fallback /// vs the collapsed 22px `radius`). Same default-to-`radius` fallback
/// reasoning as `search_width`. /// reasoning as `search_width`.
pub search_radius: i32, pub search_radius: i32,
/// Result row `border-radius` (px) — `breadbox::main::build_css`'s
/// `row { border-radius: ... }`. Liquid Motion's soft 12px vs Glass
/// Workbench's dense 6px is the clearest single signal that the two
/// themes are different instruments, not one launcher recoloured.
pub row_radius: i32,
/// Result row horizontal inset (px) from the panel's edge —
/// `row { margin: 0 {row_inset}px; }`. Mirrors the demos' `.bx .r`
/// `margin: 0 Npx` rule (liquid-motion 8px, glass-workbench 6px).
pub row_inset: i32,
/// Result row vertical padding (px) — `row { padding: {row_padding_v}px
/// {row_padding_h}px; }`'s first component.
pub row_padding_v: i32,
/// Result row horizontal padding (px) — same rule's second component.
pub row_padding_h: i32,
/// Row icon `border-radius` (px) — `breadbox::main::build_css`'s
/// `image { border-radius: ...; }`, paired with `icon_px` for the
/// swatch's size. Liquid Motion's rounder 9px vs Glass Workbench's
/// tighter 5px.
pub icon_radius: i32,
/// Search entry font-size (px) — distinct from `tokens.font_size_base`
/// (which sizes the result rows): both demos give the search field a
/// larger face than its rows (liquid-motion 16 vs its rows' 14,
/// glass-workbench 14 vs its rows' 13).
pub search_font_size: i32,
/// Search entry vertical padding (px).
pub search_padding_v: i32,
/// Search entry horizontal padding (px).
pub search_padding_h: i32,
/// Selected/hovered row background: `alpha(@accent, selection_alpha)`.
/// Liquid Motion's softer 0.22 vs Glass Workbench's denser 0.28 — see
/// each demo's `.bx .r.sel` rule.
pub selection_alpha: f64,
} }
/// A satellite surface, keyed by layer-shell namespace in `[surfaces.*]` — /// A satellite surface, keyed by layer-shell namespace in `[surfaces.*]` —
@ -350,27 +396,28 @@ impl Tokens {
} }
} }
/// Declared-but-not-yet-consumed in production: neither breadbar nor /// Consumed by `breadbox::main::build_css` for the launcher panel's
/// breadbox calls this accessor. Both get their actual rendered font /// `font-family` (the `entry.search`/`row` rule) as of the
/// from [`crate::stylesheet`]'s own hardcoded `crate::tokens::FONT_FAMILY` /// liquid-motion/glass-workbench redesign — combined with
/// constant — a completely separate, ecosystem-wide font system shared /// [`Self::font_fallback`] into a CSS font stack. Still NOT consumed by
/// by every bread GUI (not just shell-themed ones), unrelated to this /// `breadbar` (bar/chip/clock text) or by [`crate::stylesheet`]'s
/// per-theme manifest field. Setting `[tokens] font_family = "..."` in a /// ecosystem-wide base rule, which still hardcodes
/// theme.toml today has zero effect on what actually renders; every /// `crate::tokens::FONT_FAMILY` for every non-launcher widget — this is
/// built-in theme sets this key anyway (see the "not yet consumed" note /// a scoped, launcher-only wire-up, not the full "per-shell-theme font
/// next to it in each `theme.toml`), preserved as an honest declaration /// everywhere" replacement a `stylesheet()` owner would need to decide
/// of the demo's intended font pending someone deciding how (or /// on separately.
/// whether) a per-shell-theme font should override the ecosystem-wide
/// one — that's a design decision for whoever owns `stylesheet()`, not
/// a mechanical wire-up this crate can do to itself.
pub fn font_family(&self) -> String { pub fn font_family(&self) -> String {
self.str_or("font_family", crate::tokens::FONT_FAMILY) self.str_or("font_family", crate::tokens::FONT_FAMILY)
} }
/// See [`Self::font_family`] — same "declared but never read" status. /// See [`Self::font_family`] — same scoped (launcher-only) consumption,
/// appended after `font_family` in the CSS font stack breadbox builds.
pub fn font_fallback(&self) -> String { pub fn font_fallback(&self) -> String {
self.str_or("font_fallback", "sans-serif") self.str_or("font_fallback", "sans-serif")
} }
/// See [`Self::font_family`] — same "declared but never read" status. /// Consumed by `breadbox::main::build_css` for the result rows'
/// `font-size` (the search entry uses `[launcher].search_font_size`
/// instead — both demos give the search field a larger face than its
/// rows). Still not read by `breadbar`.
pub fn font_size_base(&self) -> i64 { pub fn font_size_base(&self) -> i64 {
self.int_or("font_size_base", crate::tokens::FONT_SIZE_BASE as i64) self.int_or("font_size_base", crate::tokens::FONT_SIZE_BASE as i64)
} }