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/// Reset coverage recorded for a shorthand property.
264#[derive(Clone, Copy, Debug, Eq, PartialEq)]
265pub enum ShorthandReset {
266 /// Properties reset in addition to the expressible longhands.
267 Properties(&'static [CssAtomSet]),
268 /// All properties are reset except the exclusions defined for `all`.
269 All,
270}
271
272/// The part of a shorthand's value that represents the longhand property that part sets.
273///
274/// The slots of `font`, for example, report that it writes an optional style and weight, then a size, then an optional
275/// line height after a `/`, then a family.
276#[derive(Clone, Copy, Debug, Eq, PartialEq)]
277pub struct Slot {
278 pub property: CssAtomSet,
279 /// The token the grammar writes before this slot to tell it apart from the slot before it, such as the `/` of
280 /// `font`'s line height. Empty when the grammar writes nothing.
281 pub before: &'static str,
282 /// The token the grammar writes after this slot. Empty when the grammar writes nothing.
283 pub after: &'static str,
284 /// True when the grammar lets the slot be left out.
285 pub optional: bool,
286 /// True when leaving the slot out gives its property the value of the slot before it, rather than the initial value.
287 pub copies: bool,
288}
289
290/// How a shorthand's value writes the longhands it sets.
291#[derive(Clone, Copy, Debug, Eq, PartialEq)]
292pub enum Writes {
293 /// The value writes one value per slot, in this order.
294 Slots(&'static [Slot]),
295 /// The value takes one to as many values as the shorthand sets longhands, and the position of each value picks the
296 /// longhand in [Shorthand::longhands] it sets, as `margin`'s `<'margin-top'>{1,4}` does. A left-out position takes
297 /// the value of an earlier one.
298 Repeat,
299 /// The value is written once and every longhand takes it, as `marker`'s `none | <url>` does.
300 Same,
301}
302
303/// What a shorthand property does to the properties it covers.
304#[derive(Clone, Copy, Debug, Eq, PartialEq)]
305pub struct Shorthand {
306 /// Every property it sets.
307 ///
308 /// Nested shorthands are included with the properties they set, so `border` states `border-width` as well as
309 /// `border-top-width`.
310 pub longhands: &'static [CssAtomSet],
311 /// How its value writes them, when that is stated. Every longhand it sets is written, so a grammar which states only
312 /// some of them states none.
313 pub writes: Option<Writes>,
314 /// What it resets besides them. In some cases a shorthand will reset properties it cannot express itself, such as
315 /// `border` resetting `border-image`.
316 pub resets: ShorthandReset,
317}
318
319/// What the shorthands which cover a property do to it.
320///
321/// A shorthand may have one of these as well, since a shorthand can be nested inside another: `border-width` is both a
322/// shorthand of `border-top-width` and a longhand of `border`.
323#[derive(Clone, Copy, Debug, Eq, PartialEq)]
324pub struct Longhand {
325 /// Every shorthand which expresses this property, narrowest first.
326 pub shorthands: &'static [CssAtomSet],
327 /// Every shorthand which resets this property without expressing it.
328 pub reset_by: &'static [CssAtomSet],
329}
330
331pub trait DeclarationMetadata: Sized {
332 /// Returns the initial value of this property, as a string
333 fn initial() -> &'static str;
334
335 /// Determines if this style value inherits from parent rules
336 fn inherits() -> Inherits {
337 // Most properties do not inherit, so this is a sensible default
338 Inherits::False
339 }
340
341 /// Determines what types of frames this rule applies to
342 fn applies_to() -> AppliesTo {
343 AppliesTo::none()
344 }
345
346 /// Determines how this style value resolves percentages, if they are allowed as values
347 fn percentages() -> Percentages {
348 Percentages::None
349 }
350
351 /// Returns how this style value animates
352 fn animation_type() -> AnimationType {
353 // Most properties do not animate, so this is a sensible default
354 AnimationType::None
355 }
356
357 /// Returns what this property does to the properties it covers, or [None] if it is not a
358 /// shorthand.
359 fn shorthand() -> Option<&'static Shorthand> {
360 None
361 }
362
363 /// Returns what the shorthands which cover this property do to it, or [None] if no shorthand
364 /// covers it.
365 fn longhand() -> Option<&'static Longhand> {
366 None
367 }
368
369 /// Returns which CSS specification(s) this property belongs to.
370 /// This allows tracking which CSS modules are used in a stylesheet.
371 fn property_group() -> PropertyGroup {
372 PropertyGroup::none()
373 }
374
375 /// Returns how the computed value is calculated from the specified value.
376 fn computed_value_type() -> ComputedValueType {
377 ComputedValueType::Unknown
378 }
379
380 /// Returns the canonical order for serialization (e.g., "per grammar", "unique").
381 /// Returns None if not specified or not applicable.
382 fn canonical_order() -> Option<&'static str> {
383 None
384 }
385
386 /// Returns the logical property group this property belongs to (e.g., "Margin", "Border").
387 /// This groups related logical/physical properties together.
388 /// Returns None if this is not part of a logical property group.
389 fn logical_property_group() -> Option<CssAtomSet> {
390 None
391 }
392
393 /// Returns which side(s) of the box this property applies to.
394 /// For example, `margin-top` returns BoxSide::Top, while `margin` returns all sides.
395 /// Returns BoxSide::none() if the property doesn't apply to a specific side.
396 fn box_side() -> BoxSide {
397 BoxSide::none()
398 }
399
400 /// Returns which portion(s) of the box model this property affects.
401 /// For example, `margin-top` returns BoxPortion::Margin, `border-width` returns BoxPortion::Border.
402 /// Returns BoxPortion::none() if the property doesn't affect the box model.
403 fn box_portion() -> BoxPortion {
404 BoxPortion::none()
405 }
406
407 /// Returns how unitless zero resolves for this property.
408 ///
409 /// For properties that accept both `<number>` and `<length>`, unitless zero
410 /// may resolve to a number rather than a length. This affects whether the
411 /// minifier can safely reduce `0px` to `0`.
412 ///
413 /// Examples where unitless zero resolves to Number (NOT safe to reduce):
414 /// - `line-height: 0` means 0x font-size multiplier
415 /// - `tab-size: 0` means 0 tab characters
416 /// - `border-image-outset: 0` means 0x border-width
417 fn unitless_zero_resolves() -> UnitlessZeroResolves {
418 // Default: most properties accept unitless zero as length
419 UnitlessZeroResolves::Length
420 }
421}
422
423#[cfg(test)]
424mod test {
425 use crate::*;
426
427 #[test]
428 fn test_box_side_count() {
429 assert_eq!(BoxSide::Top.num_sides(false), 1);
430 assert_eq!((BoxSide::Top | BoxSide::Right).num_sides(false), 2);
431 assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom).num_sides(false), 3);
432 assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(false), 4);
433 assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(true), 0);
434
435 assert_eq!(BoxSide::all_bits().num_sides(false), 4);
436 assert_eq!(BoxSide::all_bits().num_sides(true), 4);
437
438 assert_eq!(BoxSide::BlockStart.num_sides(true), 1);
439 assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd).num_sides(true), 2);
440 assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart).num_sides(true), 3);
441 assert_eq!(
442 (BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(true),
443 4
444 );
445 assert_eq!(
446 (BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(false),
447 0
448 );
449 }
450}