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