1mod pseudo;
2pub mod registry;
3pub mod utilities;
4
5use std::fmt;
6use std::hash::Hasher;
7
8pub use cabin::html::elements::common::Class;
9use cabin::View;
10pub use cabin_macros::tw;
11pub use utilities as css;
12
13pub mod prelude {
14 pub use crate::{tw, utilities as tw, Responsive, Utility};
15}
16
17pub trait Utility {
18 fn declarations(&self, f: &mut dyn fmt::Write) -> fmt::Result;
19
20 fn selector_prefix(&self, _f: &mut dyn fmt::Write) -> fmt::Result {
21 Ok(())
22 }
23
24 fn selector_suffix(&self, _f: &mut dyn fmt::Write) -> fmt::Result {
25 Ok(())
26 }
27
28 fn suffix(&self, _f: &mut dyn fmt::Write) -> fmt::Result {
29 Ok(())
30 }
31
32 fn write_animate_from(&self, _f: &mut dyn fmt::Write) -> fmt::Result {
33 Ok(())
34 }
35
36 fn write_animate_to(&self, _f: &mut dyn fmt::Write) -> fmt::Result {
37 Ok(())
38 }
39
40 fn hash_modifier(&self, _hasher: &mut dyn Hasher) {}
41
42 fn override_class_name(&self) -> Option<&str> {
43 None
44 }
45
46 fn order(&self) -> usize {
49 0
50 }
51
52 fn active(self) -> pseudo::active::Active<Self>
54 where
55 Self: Sized,
56 {
57 pseudo::active::Active(self)
58 }
59
60 fn after(self) -> pseudo::after::After<Self>
62 where
63 Self: Sized,
64 {
65 pseudo::after::After(self)
66 }
67
68 fn animate_from(self) -> pseudo::animate_from::AnimateFrom<Self>
69 where
70 Self: Sized,
71 {
72 pseudo::animate_from::AnimateFrom(self)
73 }
74
75 fn animate_to(self) -> pseudo::animate_to::AnimateTo<Self>
76 where
77 Self: Sized,
78 {
79 pseudo::animate_to::AnimateTo(self)
80 }
81
82 fn apply_to_children(self) -> pseudo::apply_to_children::ApplyToChildren<Self>
84 where
85 Self: Sized,
86 {
87 pseudo::apply_to_children::ApplyToChildren(self)
88 }
89
90 fn before(self) -> pseudo::before::Before<Self>
92 where
93 Self: Sized,
94 {
95 pseudo::before::Before(self)
96 }
97
98 fn disabled(self) -> pseudo::disabled::Disabled<Self>
100 where
101 Self: Sized,
102 {
103 pseudo::disabled::Disabled(self)
104 }
105
106 fn enabled(self) -> pseudo::enabled::Enabled<Self>
108 where
109 Self: Sized,
110 {
111 pseudo::enabled::Enabled(self)
112 }
113
114 fn focus(self) -> pseudo::focus::Focus<Self>
116 where
117 Self: Sized,
118 {
119 pseudo::focus::Focus(self)
120 }
121
122 fn focus_visible(self) -> pseudo::focus_visible::FocusVisible<Self>
124 where
125 Self: Sized,
126 {
127 pseudo::focus_visible::FocusVisible(self)
128 }
129
130 fn focus_within(self) -> pseudo::focus_within::FocusWithin<Self>
132 where
133 Self: Sized,
134 {
135 pseudo::focus_within::FocusWithin(self)
136 }
137
138 fn group_hover(self) -> pseudo::group_hover::GroupHover<Self>
139 where
140 Self: Sized,
141 {
142 pseudo::group_hover::GroupHover(self)
143 }
144
145 fn hover(self) -> pseudo::hover::Hover<Self>
147 where
148 Self: Sized,
149 {
150 pseudo::hover::Hover(self)
151 }
152
153 fn visited(self) -> pseudo::visited::Visited<Self>
155 where
156 Self: Sized,
157 {
158 pseudo::visited::Visited(self)
159 }
160
161 fn min_width_px(self, min_width_px: u32) -> pseudo::min_width::MinWidth<Self>
164 where
165 Self: Sized,
166 {
167 pseudo::min_width::MinWidth::new(min_width_px, self)
168 }
169
170 fn max_width_px(self, max_width_px: u32) -> pseudo::max_width::MaxWidth<Self>
173 where
174 Self: Sized,
175 {
176 pseudo::max_width::MaxWidth::new(max_width_px, self)
177 }
178}
179
180include!(concat!(env!("OUT_DIR"), "/responsive.rs"));
181
182pub struct Property<V = &'static str>(pub(crate) &'static str, pub(crate) V);
183pub struct PropertyTwice<V = &'static str>(
184 pub(crate) &'static str,
185 pub(crate) &'static str,
186 pub(crate) V,
187);
188
189pub struct StaticClass(pub(crate) &'static str);
190
191#[derive(Debug, Clone, Copy, PartialEq)]
192pub enum Length {
193 Auto,
194 MinContent,
195 MaxContent,
196 FitContent,
197 Vw(u16),
198 Vh(u16),
199 Px(f32),
200 Em(f32),
201 Rem(f32),
202 Percent(f32),
203}
204
205impl Length {
206 fn is_zero(&self) -> bool {
207 match self {
208 Length::Auto | Length::MinContent | Length::MaxContent | Length::FitContent => false,
209 Length::Vw(v) | Length::Vh(v) => *v == 0,
210 Length::Px(v) | Length::Em(v) | Length::Rem(v) | Length::Percent(v) => {
211 v.abs() < f32::EPSILON
212 }
213 }
214 }
215}
216
217impl fmt::Display for Length {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 if self.is_zero() {
220 return f.write_str("0");
221 }
222
223 match self {
224 Length::Auto => f.write_str("auto"),
225 Length::MinContent => f.write_str("min-content"),
226 Length::MaxContent => f.write_str("max-content"),
227 Length::FitContent => f.write_str("fit-content"),
228 Length::Vw(v) => write!(f, "{v}vw"),
229 Length::Vh(v) => write!(f, "{v}vh"),
230 Length::Px(v) => write!(f, "{v}px"),
231 Length::Em(v) => write!(f, "{v}em"),
232 Length::Rem(v) => write!(f, "{v}rem"),
233 Length::Percent(v) => write!(f, "{v}%"),
234 }
235 }
236}
237
238impl<V: fmt::Display> Utility for Property<V> {
239 fn declarations(&self, f: &mut dyn fmt::Write) -> fmt::Result {
240 writeln!(f, "{}: {};", self.0, self.1)
241 }
242}
243
244impl<V: fmt::Display> Utility for PropertyTwice<V> {
245 fn declarations(&self, f: &mut dyn fmt::Write) -> fmt::Result {
246 writeln!(f, "{}: {};", self.0, self.2)?;
247 writeln!(f, "{}: {};", self.1, self.2)?;
248 Ok(())
249 }
250}
251
252impl Utility for StaticClass {
253 fn declarations(&self, _: &mut dyn fmt::Write) -> fmt::Result {
254 Ok(())
255 }
256
257 fn hash_modifier(&self, hasher: &mut dyn Hasher) {
258 hasher.write(self.0.as_bytes());
259 }
260
261 fn override_class_name(&self) -> Option<&str> {
262 Some("group")
263 }
264}
265
266pub fn cabin_stylesheets() -> impl View {
267 use std::sync::OnceLock;
268
269 use cabin::html::Common;
270 use cabin::{content_hash, html};
271 use html::elements::link::Link;
272
273 static HREF: OnceLock<String> = OnceLock::new();
274 let href = HREF.get_or_init(|| {
275 let hash = content_hash(registry::StyleRegistry::global().style_sheet().as_bytes());
276 format!("/styles.css?{hash}")
277 });
278
279 html::link()
280 .id("cabin-styles")
281 .rel(html::elements::link::Rel::StyleSheet)
282 .href(href)
283}