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