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