ferro_json_ui/render/
classes.rs1pub(crate) const FOCUS_RING: &str =
11 "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2";
12
13pub(crate) const MOTION_FAST: &str = "transition-colors duration-fast ease-base";
15
16pub(crate) const MOTION_BASE: &str = "transition-opacity duration-base ease-base";
18
19pub(crate) const DISABLED_BASE: &str = "disabled:opacity-50 disabled:pointer-events-none";
21
22pub(crate) const TOAST_TONE_NEUTRAL: &str = "bg-primary/70 text-primary-foreground";
27pub(crate) const TOAST_TONE_SUCCESS: &str = "bg-success/70 text-primary-foreground";
29pub(crate) const TOAST_TONE_WARNING: &str = "bg-warning/70 text-primary-foreground";
31pub(crate) const TOAST_TONE_DESTRUCTIVE: &str = "bg-destructive/70 text-primary-foreground";
33
34pub(crate) const INTERACTIVE_BASE: &str = concat!(
36 "transition-colors duration-fast ease-base ",
37 "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
38);
39
40pub const TOUCH_ACTION: &str = "touch-manipulation";
42
43pub const HIT_TARGET_MIN: &str = "min-h-[44px] min-w-[44px]";
45
46pub const HIT_TARGET_NUMPAD: &str = "min-h-[56px] min-w-[56px]";
48
49pub const PRESS_ACTIVE: &str = "active:scale-95 active:bg-border";
52
53pub const OVERSCROLL_CONTAIN: &str = "overscroll-contain";
55
56pub const TAP_HIGHLIGHT: &str = "pos-tap-highlight";
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
67 fn interactive_base_is_motion_fast_plus_focus_ring() {
68 assert_eq!(INTERACTIVE_BASE, format!("{MOTION_FAST} {FOCUS_RING}"));
69 }
70
71 #[test]
73 fn fragments_use_token_utilities() {
74 assert!(FOCUS_RING.contains("focus-visible:ring-ring"));
75 assert!(MOTION_FAST.contains("duration-fast") && MOTION_FAST.contains("ease-base"));
76 assert!(MOTION_BASE.contains("duration-base") && MOTION_BASE.contains("ease-base"));
77 assert!(DISABLED_BASE.contains("disabled:pointer-events-none"));
78 }
79
80 #[test]
83 fn render_functions_use_constants_not_literals() {
84 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR");
85 let render_dir = std::path::Path::new(&manifest_dir).join("src/render");
86 let guarded = [
87 "touch-manipulation",
88 "min-h-[44px] min-w-[44px]",
89 "min-h-[56px] min-w-[56px]",
90 ];
91 for entry in std::fs::read_dir(&render_dir).expect("src/render readable") {
92 let path = entry.unwrap().path();
93 if path.extension().and_then(|e| e.to_str()) != Some("rs") {
94 continue;
95 }
96 let filename = path.file_name().unwrap().to_str().unwrap().to_string();
97 if filename == "classes.rs" {
98 continue;
99 }
100 let source = std::fs::read_to_string(&path).unwrap();
101 for lit in &guarded {
102 assert!(
103 !source.contains(lit),
104 "{filename}: raw POS literal {lit:?} — import from render::classes instead"
105 );
106 }
107 }
108 }
109
110 #[test]
111 fn touch_constants_are_full_literals_and_token_compliant() {
112 assert_eq!(TOUCH_ACTION, "touch-manipulation");
113 assert_eq!(HIT_TARGET_MIN, "min-h-[44px] min-w-[44px]");
114 assert_eq!(HIT_TARGET_NUMPAD, "min-h-[56px] min-w-[56px]");
115 assert_eq!(OVERSCROLL_CONTAIN, "overscroll-contain");
116 assert_eq!(TAP_HIGHLIGHT, "pos-tap-highlight");
117 assert!(PRESS_ACTIVE.contains("active:scale-95"));
118 assert!(PRESS_ACTIVE.contains("active:bg-border"));
119 for raw in ["red-", "blue-", "orange-", "zinc-", "gray-", "slate-"] {
120 assert!(
121 !PRESS_ACTIVE.contains(raw),
122 "raw palette class in PRESS_ACTIVE"
123 );
124 }
125 }
126}