css_ast/traits/declaration_metadata.rs
1use bitmask_enum::bitmask;
2
3use crate::{CssAtomSet, UnitlessZeroResolves};
4
5/// The CSS specification/module that a property belongs to.
6#[bitmask(u128)]
7#[bitmask_config(vec_debug)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum PropertyGroup {
10 Align,
11 AnchorPosition,
12 Animations,
13 Backgrounds,
14 Borders,
15 Box,
16 Break,
17 Cascade,
18 Color,
19 ColorAdjust,
20 ColorHdr,
21 Compositing,
22 Conditional,
23 Contain,
24 Content,
25 CounterStyle,
26 Display,
27 Exclusions,
28 FillStroke,
29 FilterEffects,
30 Flexbox,
31 Fonts,
32 Forms,
33 Gaps,
34 Gcpm,
35 Grid,
36 Images,
37 Inline,
38 LineGrid,
39 LinkParams,
40 Lists,
41 Logical,
42 Masking,
43 Motion,
44 Multicol,
45 Nav,
46 Overflow,
47 Overscroll,
48 Page,
49 PageFloats,
50 PointerAnimations,
51 Position,
52 Regions,
53 Rhythm,
54 RoundDisplay,
55 Ruby,
56 ScrollAnchoring,
57 ScrollAnimations,
58 ScrollSnap,
59 Scrollbars,
60 Shaders,
61 Shapes,
62 SizeAdjust,
63 Sizing,
64 Speech,
65 Tables,
66 Text,
67 TextDecor,
68 Transforms,
69 Transitions,
70 Ui,
71 Values,
72 Variables,
73 ViewTransitions,
74 Viewport,
75 WillChange,
76 WritingModes,
77}
78
79pub enum Inherits {
80 False,
81 True,
82 Unknown,
83}
84
85impl Inherits {
86 pub fn to_bool(self, unknown: bool) -> bool {
87 match self {
88 Self::False => false,
89 Self::True => true,
90 Self::Unknown => unknown,
91 }
92 }
93}
94
95pub enum Percentages {
96 /// This style value has no way of expressing values as a percentage.
97 None,
98 /// Any percentage expressed in this value pertains to the size of the containing block.
99 ContainingBlock,
100 /// Any percentage expressed in this value pertains to the size of the border box.
101 BorderBox,
102 /// Any percentage expressed in this value is a syntax affordance; a Number token would be the equivalent value.
103 Number,
104 /// Relative to the 1em Font-Size
105 FontSize,
106 /// Relative to the Font-Size of the parent element
107 ParentFontSize,
108 /// Relative to the scroll container's scrollport
109 Scrollport,
110 /// Relative to the content area dimension
111 ContentArea,
112 /// Relative to the border-edge side length
113 BorderEdge,
114 /// Relative to the background positioning area
115 BackgroundPositioningArea,
116 /// Relative to the reference box size
117 ReferenceBox,
118 /// Relative to the element's own dimensions
119 SelfSize,
120 /// Relative to the line box
121 LineBox,
122 /// Relative to the flex container
123 FlexContainer,
124 /// Relative to the border image area
125 BorderImageArea,
126 /// Map to a normalized range (e.g., `[0,1]`)
127 NormalizedRange,
128 /// Unknown or complex percentage resolution
129 Unknown,
130}
131
132/// The type of element or container this style value applies to.
133#[bitmask(u16)]
134#[bitmask_config(vec_debug)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub enum AppliesTo {
137 /// Any element which is `display: block` or equivalent.
138 Block,
139 /// Any element which is `display: grid` or equivalent.
140 Grid,
141 /// Any element which is `display: flex` or equivalent.
142 Flex,
143 /// Any inline-level box.
144 Inline,
145 /// Any floated element.
146 Float,
147 /// Any Ruby container
148 Ruby,
149 /// Any absolutely positioned element.
150 AbsPos,
151 /// Any text node.
152 Text,
153 /// Any Pseudo Elements
154 PseudoElements,
155 /// Any Element
156 Elements,
157 /// What this applies to still needs to be established.
158 Unknown,
159}
160
161pub enum AnimationType {
162 /// This property is not animatable.
163 None,
164 /// This property animates between discrete values.
165 Discrete,
166 /// Animates by interpolating computed values
167 ByComputedValue,
168 /// Each item in a list animates independently
169 RepeatableList,
170 /// Animates as a transform list
171 TransformList,
172 /// Animates as a shadow list
173 ShadowList,
174 /// Animates as a length value
175 Length,
176 /// Animates as a number value
177 Number,
178 /// Unknown or complex animation behavior
179 Unknown,
180}
181
182/// How the computed value is calculated from the specified value
183pub enum ComputedValueType {
184 /// The computed value is the same as the specified value
185 AsSpecified,
186 /// Computed to an absolute length
187 AbsoluteLength,
188 /// Computed to an absolute length or percentage
189 AbsoluteLengthOrPercentage,
190 /// Computed to an absolute length or 'none'
191 AbsoluteLengthOrNone,
192 /// A specified keyword plus an absolute length
193 SpecifiedKeywordPlusAbsoluteLength,
194 /// Two absolute lengths (e.g., for background-position)
195 TwoAbsoluteLengths,
196 /// A list of absolute lengths
197 ListOfAbsoluteLengths,
198 /// Computed as specified, but with relative lengths converted to absolute
199 SpecifiedWithAbsoluteLengths,
200 /// Computed as specified, but with relative URLs converted to absolute
201 SpecifiedWithAbsoluteUrls,
202 /// Special computation rules - see spec
203 SeeIndividualProperties,
204 /// Computed value calculation is complex or spec-specific
205 Complex,
206 /// Not yet categorized
207 Unknown,
208}
209
210/// Which side(s) of the box a property applies to.
211/// This is a bitmask so properties can apply to multiple sides.
212#[bitmask(u8)]
213#[bitmask_config(vec_debug)]
214#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
215pub enum BoxSide {
216 /// Applies to the physical top side
217 Top = 0b00000001,
218 /// Applies to the physical bottom side
219 Bottom = 0b00000010,
220 /// Applies to the physical left side
221 Left = 0b00000100,
222 /// Applies to the physical right side
223 Right = 0b00001000,
224 /// Applies to the logical block-start side
225 BlockStart = 0b00010000,
226 /// Applies to the logical block-end side
227 BlockEnd = 0b00100000,
228 /// Applies to the logical inline-start side
229 InlineStart = 0b01000000,
230 /// Applies to the logical inline-end side
231 InlineEnd = 0b10000000,
232}
233
234impl BoxSide {
235 #[inline]
236 pub fn num_sides(&self, logical: bool) -> u32 {
237 if logical { (self.bits() & 0b11110000).count_ones() } else { (self.bits() & 0b00001111).count_ones() }
238 }
239}
240
241/// Which portion(s) of the box model a property affects.
242/// This is a bitmask so properties can affect multiple portions.
243#[bitmask(u8)]
244#[bitmask_config(vec_debug)]
245#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
246pub enum BoxPortion {
247 /// Affects the content size (width/height)
248 Size,
249 /// Affects the margin area
250 Margin,
251 /// Affects the padding area
252 Padding,
253 /// Affects the border area
254 Border,
255 /// Affects the position/placement of the box
256 Position,
257}
258
259pub trait DeclarationMetadata: Sized {
260 /// Returns the initial value of this property, as a string
261 fn initial() -> &'static str;
262
263 /// Determines if this style value inherits from parent rules
264 fn inherits() -> Inherits {
265 // Most properties do not inherit, so this is a sensible default
266 Inherits::False
267 }
268
269 /// Determines what types of frames this rule applies to
270 fn applies_to() -> AppliesTo {
271 AppliesTo::none()
272 }
273
274 /// Determines how this style value resolves percentages, if they are allowed as values
275 fn percentages() -> Percentages {
276 Percentages::None
277 }
278
279 /// Returns how this style value animates
280 fn animation_type() -> AnimationType {
281 // Most properties do not animate, so this is a sensible default
282 AnimationType::None
283 }
284
285 /// Determines if this style value is a "shorthand" value, meaning it is comprised of other "longhand" style values.
286 fn is_shorthand() -> bool {
287 false
288 }
289
290 /// Determines if this style value is a "longhand" value, meaning a "shorthand" style value exists that could also
291 /// express this.
292 fn is_longhand() -> bool {
293 Self::shorthand_group() == CssAtomSet::_None
294 }
295
296 /// Returns all transitive longhands for a shorthand property.
297 /// For nested shorthands (e.g., `border-width`), this recursively expands to include
298 /// all nested longhands (e.g., `border-top-width`, `border-left-width`, etc.).
299 fn longhands() -> Option<&'static [CssAtomSet]> {
300 None
301 }
302
303 /// Returns the declaration ID of the shorthand that this property is part of.
304 /// If this is not a longhand then it will be `CssAtomSet::_None`.
305 fn shorthand_group() -> CssAtomSet {
306 CssAtomSet::_None
307 }
308
309 /// Returns which CSS specification(s) this property belongs to.
310 /// This allows tracking which CSS modules are used in a stylesheet.
311 fn property_group() -> PropertyGroup {
312 PropertyGroup::none()
313 }
314
315 /// Returns how the computed value is calculated from the specified value.
316 fn computed_value_type() -> ComputedValueType {
317 ComputedValueType::Unknown
318 }
319
320 /// Returns the canonical order for serialization (e.g., "per grammar", "unique").
321 /// Returns None if not specified or not applicable.
322 fn canonical_order() -> Option<&'static str> {
323 None
324 }
325
326 /// Returns the logical property group this property belongs to (e.g., "Margin", "Border").
327 /// This groups related logical/physical properties together.
328 /// Returns None if this is not part of a logical property group.
329 fn logical_property_group() -> Option<CssAtomSet> {
330 None
331 }
332
333 /// Returns which side(s) of the box this property applies to.
334 /// For example, `margin-top` returns BoxSide::Top, while `margin` returns all sides.
335 /// Returns BoxSide::none() if the property doesn't apply to a specific side.
336 fn box_side() -> BoxSide {
337 BoxSide::none()
338 }
339
340 /// Returns which portion(s) of the box model this property affects.
341 /// For example, `margin-top` returns BoxPortion::Margin, `border-width` returns BoxPortion::Border.
342 /// Returns BoxPortion::none() if the property doesn't affect the box model.
343 fn box_portion() -> BoxPortion {
344 BoxPortion::none()
345 }
346
347 /// Returns how unitless zero resolves for this property.
348 ///
349 /// For properties that accept both `<number>` and `<length>`, unitless zero
350 /// may resolve to a number rather than a length. This affects whether the
351 /// minifier can safely reduce `0px` to `0`.
352 ///
353 /// Examples where unitless zero resolves to Number (NOT safe to reduce):
354 /// - `line-height: 0` means 0x font-size multiplier
355 /// - `tab-size: 0` means 0 tab characters
356 /// - `border-image-outset: 0` means 0x border-width
357 fn unitless_zero_resolves() -> UnitlessZeroResolves {
358 // Default: most properties accept unitless zero as length
359 UnitlessZeroResolves::Length
360 }
361}
362
363#[cfg(test)]
364mod test {
365 use crate::*;
366
367 #[test]
368 fn test_box_side_count() {
369 assert_eq!(BoxSide::Top.num_sides(false), 1);
370 assert_eq!((BoxSide::Top | BoxSide::Right).num_sides(false), 2);
371 assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom).num_sides(false), 3);
372 assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(false), 4);
373 assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(true), 0);
374
375 assert_eq!(BoxSide::all_bits().num_sides(false), 4);
376 assert_eq!(BoxSide::all_bits().num_sides(true), 4);
377
378 assert_eq!(BoxSide::BlockStart.num_sides(true), 1);
379 assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd).num_sides(true), 2);
380 assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart).num_sides(true), 3);
381 assert_eq!(
382 (BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(true),
383 4
384 );
385 assert_eq!(
386 (BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(false),
387 0
388 );
389 }
390}