Skip to main content

fret_ui_kit/style/
tokens.rs

1use fret_core::{Color, Px};
2
3fn alpha_mul(mut c: Color, mul: f32) -> Color {
4    c.a = (c.a * mul).clamp(0.0, 1.0);
5    c
6}
7
8use super::ThemeTokenRead;
9
10/// Tailwind-like spacing scale for component libraries.
11///
12/// This is intentionally small and opinionated. It is used by component-level style refinements
13/// and can be overridden via theme extension metrics (ADR 0050).
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Space {
16    N0,
17    N0p5,
18    N1,
19    N1p5,
20    N2,
21    N2p5,
22    N3,
23    N3p5,
24    N4,
25    N5,
26    N6,
27    N8,
28    N10,
29    N11,
30    N12,
31}
32
33impl Space {
34    pub fn token_key(self) -> &'static str {
35        match self {
36            Self::N0 => "component.space.0",
37            Self::N0p5 => "component.space.0p5",
38            Self::N1 => "component.space.1",
39            Self::N1p5 => "component.space.1p5",
40            Self::N2 => "component.space.2",
41            Self::N2p5 => "component.space.2p5",
42            Self::N3 => "component.space.3",
43            Self::N3p5 => "component.space.3p5",
44            Self::N4 => "component.space.4",
45            Self::N5 => "component.space.5",
46            Self::N6 => "component.space.6",
47            Self::N8 => "component.space.8",
48            Self::N10 => "component.space.10",
49            Self::N11 => "component.space.11",
50            Self::N12 => "component.space.12",
51        }
52    }
53
54    pub(super) fn fallback_metric(self) -> MetricFallback {
55        match self {
56            Self::N0 => MetricFallback::Px(Px(0.0)),
57            Self::N0p5 => MetricFallback::ThemePaddingSmMulDiv { mul: 1, div: 4 },
58            Self::N1 => MetricFallback::ThemePaddingSmMulDiv { mul: 1, div: 2 },
59            Self::N1p5 => MetricFallback::ThemePaddingSmMulDiv { mul: 3, div: 4 },
60            Self::N2 => MetricFallback::ThemePaddingSm,
61            // This is intentionally tied to the baseline `metric.padding.md` token to avoid value
62            // duplication drift when themes omit `component.space.*` (ADR 0032 / ADR 0050).
63            Self::N2p5 => MetricFallback::ThemePaddingMd,
64            Self::N3 => MetricFallback::ThemePaddingSmMulDiv { mul: 3, div: 2 },
65            Self::N3p5 => MetricFallback::ThemePaddingSmMulDiv { mul: 7, div: 4 },
66            Self::N4 => MetricFallback::ThemePaddingSmMulDiv { mul: 2, div: 1 },
67            Self::N5 => MetricFallback::ThemePaddingSmMulDiv { mul: 5, div: 2 },
68            Self::N6 => MetricFallback::ThemePaddingSmMulDiv { mul: 3, div: 1 },
69            Self::N8 => MetricFallback::ThemePaddingSmMulDiv { mul: 4, div: 1 },
70            Self::N10 => MetricFallback::ThemePaddingSmMulDiv { mul: 5, div: 1 },
71            Self::N11 => MetricFallback::ThemePaddingSmMulDiv { mul: 11, div: 2 },
72            Self::N12 => MetricFallback::ThemePaddingSmMulDiv { mul: 6, div: 1 },
73        }
74    }
75}
76
77/// Tailwind-like border radius presets for component libraries.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum Radius {
80    Sm,
81    Md,
82    Lg,
83    Full,
84}
85
86#[derive(Debug, Clone)]
87pub enum MetricFallback {
88    Px(Px),
89    ThemeRadiusSm,
90    ThemeRadiusMd,
91    ThemeRadiusLg,
92    ThemePaddingSm,
93    ThemePaddingMd,
94    ThemePaddingSmMulDiv { mul: u32, div: u32 },
95}
96
97impl MetricFallback {
98    pub(super) fn resolve<T: ThemeTokenRead + ?Sized>(&self, theme: &T) -> Px {
99        match *self {
100            Self::Px(px) => px,
101            Self::ThemeRadiusSm => theme.metric_token("metric.radius.sm"),
102            Self::ThemeRadiusMd => theme.metric_token("metric.radius.md"),
103            Self::ThemeRadiusLg => theme.metric_token("metric.radius.lg"),
104            Self::ThemePaddingSm => theme.metric_token("metric.padding.sm"),
105            Self::ThemePaddingMd => theme.metric_token("metric.padding.md"),
106            Self::ThemePaddingSmMulDiv { mul, div } => {
107                if div == 0 {
108                    return Px(0.0);
109                }
110                let base = theme.metric_token("metric.padding.sm");
111                Px(base.0 * (mul as f32) / (div as f32))
112            }
113        }
114    }
115}
116
117#[derive(Debug, Clone)]
118pub enum ColorFallback {
119    Color(Color),
120    ThemeSurfaceBackground,
121    ThemePanelBackground,
122    ThemePanelBorder,
123    ThemeTextPrimary,
124    ThemeTextMuted,
125    ThemeTextDisabled,
126    ThemeAccent,
127    ThemeHoverBackground,
128    ThemeSelectionBackground,
129    ThemeFocusRing,
130    ThemeTokenAlphaMul { key: &'static str, mul: f32 },
131}
132
133impl ColorFallback {
134    pub(super) fn resolve<T: ThemeTokenRead + ?Sized>(&self, theme: &T) -> Color {
135        match *self {
136            Self::Color(c) => c,
137            Self::ThemeSurfaceBackground => theme.color_token("background"),
138            Self::ThemePanelBackground => theme.color_token("card"),
139            Self::ThemePanelBorder => theme.color_token("border"),
140            Self::ThemeTextPrimary => theme.color_token("foreground"),
141            Self::ThemeTextMuted => theme.color_token("muted-foreground"),
142            Self::ThemeTextDisabled => alpha_mul(theme.color_token("muted-foreground"), 0.5),
143            Self::ThemeAccent => theme.color_token("primary"),
144            Self::ThemeHoverBackground => theme.color_token("accent"),
145            Self::ThemeSelectionBackground => theme.color_token("selection.background"),
146            Self::ThemeFocusRing => theme.color_token("ring"),
147            Self::ThemeTokenAlphaMul { key, mul } => alpha_mul(theme.color_token(key), mul),
148        }
149    }
150}