khora_core/ui/theme.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Backend-agnostic UI theme — palette + sizing + type tokens.
16//!
17//! All slots are *semantic*: `primary`, `accent_a`, `success`, etc. The theme
18//! contains no brand-specific naming — apps configure the concrete values.
19//! Color slots are `[r, g, b, a]` **sRGB** components in `0.0..=1.0` (see the
20//! note on the color pipeline in [`crate::ui::brand`]); the concrete UI
21//! backend feeds them to its native format.
22//!
23//! The same struct is used by both the editor (`khora-editor`) and the hub
24//! (`hub`). Both install the one canonical [`crate::ui::brand::khora_dark`]
25//! producer, so there is a single source of truth and the two apps cannot
26//! drift. [`Default`] stays a neutral fallback for anything that wants a theme
27//! without pulling in the brand.
28
29/// Color palette and sizing tokens for any Khora UI surface.
30#[derive(Debug, Clone)]
31pub struct UiTheme {
32 // ── Surfaces ─────────────────────────────────────
33 /// Outermost background (empty regions, behind everything).
34 pub background: [f32; 4],
35 /// Default panel surface.
36 pub surface: [f32; 4],
37 /// Slightly elevated surface (e.g. tab bar, card body).
38 pub surface_elevated: [f32; 4],
39 /// Interactive surface (buttons, combo boxes).
40 pub surface_interactive: [f32; 4],
41 /// Hovered or active surface highlight.
42 pub surface_active: [f32; 4],
43
44 // ── Lines / borders ──────────────────────────────
45 /// Subtle separator (between items inside a panel).
46 pub separator: [f32; 4],
47 /// Default panel border.
48 pub border: [f32; 4],
49 /// Stronger border (used for emphasis around modals, popups).
50 pub border_strong: [f32; 4],
51
52 // ── Text ─────────────────────────────────────────
53 /// Primary text color.
54 pub text: [f32; 4],
55 /// Secondary text (labels, sub-titles).
56 pub text_dim: [f32; 4],
57 /// Tertiary text (hints, captions).
58 pub text_muted: [f32; 4],
59 /// Disabled text.
60 pub text_disabled: [f32; 4],
61 /// Dark "inverse" ink for text sitting on a filled brand (`primary`)
62 /// surface — e.g. the label of a solid silver primary button.
63 pub text_inverse: [f32; 4],
64
65 // ── Brand / accents ──────────────────────────────
66 /// Primary brand color (selection rings, focus highlights).
67 pub primary: [f32; 4],
68 /// Dimmed primary (backgrounds of selected rows, etc.).
69 pub primary_dim: [f32; 4],
70 /// Accent A — secondary brand color.
71 pub accent_a: [f32; 4],
72 /// Accent B — tertiary brand color.
73 pub accent_b: [f32; 4],
74 /// Accent C — quaternary brand color (warnings of-emphasis, special states).
75 pub accent_c: [f32; 4],
76
77 // ── Status colors ────────────────────────────────
78 /// Success indicator (green-family).
79 pub success: [f32; 4],
80 /// Warning indicator (amber-family).
81 pub warning: [f32; 4],
82 /// Error indicator (red-family).
83 pub error: [f32; 4],
84
85 // ── 3D axes ──────────────────────────────────────
86 /// Color for the X axis (typically red-orange).
87 pub axis_x: [f32; 4],
88 /// Color for the Y axis (typically green).
89 pub axis_y: [f32; 4],
90 /// Color for the Z axis (typically blue).
91 pub axis_z: [f32; 4],
92
93 // ── Sizing tokens ────────────────────────────────
94 /// Small corner radius (chips, badges).
95 pub radius_sm: f32,
96 /// Medium corner radius (default for buttons / inputs).
97 pub radius_md: f32,
98 /// Large corner radius (panels, cards).
99 pub radius_lg: f32,
100 /// Extra-large corner radius (modal dialogs, palettes).
101 pub radius_xl: f32,
102
103 // ── Type sizes ───────────────────────────────────
104 /// Caption / metadata font size in logical points.
105 pub font_size_caption: f32,
106 /// Body / default font size.
107 pub font_size_body: f32,
108 /// Section title font size.
109 pub font_size_title: f32,
110 /// Display heading font size.
111 pub font_size_display: f32,
112
113 // ── Spacing ──────────────────────────────────────
114 /// Default vertical padding for a row of content.
115 pub pad_row: f32,
116 /// Default inner padding for a card.
117 pub pad_card: f32,
118}
119
120/// Default theme: a neutral dark palette suitable for any app.
121///
122/// Apps that want a branded look (e.g. the Khora "Deep Navy / Silver" palette)
123/// build their own [`UiTheme`] and pass it where needed.
124impl Default for UiTheme {
125 fn default() -> Self {
126 Self {
127 // Surfaces
128 background: [0.039, 0.039, 0.055, 1.0],
129 surface: [0.067, 0.071, 0.090, 1.0],
130 surface_elevated: [0.094, 0.102, 0.129, 1.0],
131 surface_interactive: [0.133, 0.145, 0.188, 1.0],
132 surface_active: [0.164, 0.177, 0.220, 1.0],
133
134 // Lines
135 separator: [0.149, 0.165, 0.220, 1.0],
136 border: [0.180, 0.196, 0.255, 1.0],
137 border_strong: [0.260, 0.280, 0.345, 1.0],
138
139 // Text
140 text: [0.886, 0.910, 0.941, 1.0],
141 text_dim: [0.612, 0.647, 0.706, 1.0],
142 text_muted: [0.420, 0.455, 0.518, 1.0],
143 text_disabled: [0.300, 0.325, 0.380, 1.0],
144 text_inverse: [0.055, 0.063, 0.086, 1.0],
145
146 // Brand / accents
147 primary: [0.227, 0.529, 0.941, 1.0],
148 primary_dim: [0.098, 0.255, 0.510, 1.0],
149 accent_a: [0.486, 0.361, 0.871, 1.0],
150 accent_b: [0.392, 0.741, 0.918, 1.0],
151 accent_c: [0.953, 0.788, 0.353, 1.0],
152
153 // Status
154 success: [0.227, 0.722, 0.478, 1.0],
155 warning: [0.941, 0.627, 0.227, 1.0],
156 error: [0.941, 0.353, 0.227, 1.0],
157
158 // Axes
159 axis_x: [0.890, 0.310, 0.250, 1.0],
160 axis_y: [0.450, 0.820, 0.380, 1.0],
161 axis_z: [0.310, 0.560, 0.890, 1.0],
162
163 // Radii
164 radius_sm: 4.0,
165 radius_md: 6.0,
166 radius_lg: 10.0,
167 radius_xl: 14.0,
168
169 // Type
170 font_size_caption: 10.5,
171 font_size_body: 12.0,
172 font_size_title: 14.0,
173 font_size_display: 18.0,
174
175 // Spacing
176 pad_row: 8.0,
177 pad_card: 14.0,
178 }
179 }
180}