oxiui_theme/
typography.rs1#[derive(Clone, Copy, Debug, PartialEq)]
5pub struct TextStyleToken {
6 pub size: f32,
8 pub line_height: f32,
10 pub letter_spacing: f32,
12 pub weight: u16,
14}
15
16impl TextStyleToken {
17 pub const fn new(size: f32, line_height: f32, letter_spacing: f32, weight: u16) -> Self {
19 Self {
20 size,
21 line_height,
22 letter_spacing,
23 weight,
24 }
25 }
26}
27
28#[derive(Clone, Copy, Debug, PartialEq)]
30pub struct TypographyScale {
31 pub display: TextStyleToken,
33 pub headline: TextStyleToken,
35 pub title: TextStyleToken,
37 pub body: TextStyleToken,
39 pub caption: TextStyleToken,
41 pub overline: TextStyleToken,
43}
44
45impl Default for TypographyScale {
46 fn default() -> Self {
48 Self {
49 display: TextStyleToken::new(32.0, 40.0, -0.5, 700),
50 headline: TextStyleToken::new(24.0, 32.0, -0.25, 600),
51 title: TextStyleToken::new(18.0, 24.0, 0.0, 600),
52 body: TextStyleToken::new(14.0, 20.0, 0.0, 400),
53 caption: TextStyleToken::new(12.0, 16.0, 0.2, 400),
54 overline: TextStyleToken::new(10.0, 14.0, 1.0, 500),
55 }
56 }
57}
58
59impl TypographyScale {
60 pub fn roles_descending(&self) -> [TextStyleToken; 6] {
62 [
63 self.display,
64 self.headline,
65 self.title,
66 self.body,
67 self.caption,
68 self.overline,
69 ]
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn sizes_are_monotonic_descending() {
79 let s = TypographyScale::default();
80 let roles = s.roles_descending();
81 for w in roles.windows(2) {
82 assert!(
83 w[0].size >= w[1].size,
84 "scale must not increase as it descends"
85 );
86 }
87 assert!(s.caption.size < s.body.size);
89 assert!(s.body.size < s.title.size);
90 assert!(s.title.size < s.headline.size);
91 assert!(s.headline.size < s.display.size);
92 }
93
94 #[test]
95 fn line_height_at_least_size() {
96 let s = TypographyScale::default();
97 for role in s.roles_descending() {
98 assert!(
99 role.line_height >= role.size,
100 "line height must cover the glyph size"
101 );
102 }
103 }
104}