Skip to main content

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, Default, Eq, PartialEq)]
265pub enum ShorthandReset {
266	/// Reset coverage has not been audited.
267	#[default]
268	Unknown,
269	/// Properties reset in addition to the expressible longhands.
270	Properties(&'static [CssAtomSet]),
271	/// All properties are reset except the exclusions defined for `all`.
272	All,
273}
274
275pub trait DeclarationMetadata: Sized {
276	/// Returns the initial value of this property, as a string
277	fn initial() -> &'static str;
278
279	/// Determines if this style value inherits from parent rules
280	fn inherits() -> Inherits {
281		// Most properties do not inherit, so this is a sensible default
282		Inherits::False
283	}
284
285	/// Determines what types of frames this rule applies to
286	fn applies_to() -> AppliesTo {
287		AppliesTo::none()
288	}
289
290	/// Determines how this style value resolves percentages, if they are allowed as values
291	fn percentages() -> Percentages {
292		Percentages::None
293	}
294
295	/// Returns how this style value animates
296	fn animation_type() -> AnimationType {
297		// Most properties do not animate, so this is a sensible default
298		AnimationType::None
299	}
300
301	/// Determines if this style value is a "shorthand" value, meaning it is comprised of other "longhand" style values.
302	fn is_shorthand() -> bool {
303		false
304	}
305
306	/// Determines if this style value is a "longhand" value, meaning a "shorthand" style value exists that could also
307	/// express this.
308	fn is_longhand() -> bool {
309		Self::shorthand_group() == CssAtomSet::_None
310	}
311
312	/// Returns all transitive longhands for a shorthand property.
313	/// For nested shorthands (e.g., `border-width`), this recursively expands to include
314	/// all nested longhands (e.g., `border-top-width`, `border-left-width`, etc.).
315	fn longhands() -> Option<&'static [CssAtomSet]> {
316		None
317	}
318
319	/// Returns the declaration ID of the shorthand that this property is part of.
320	/// If this is not a longhand then it will be `CssAtomSet::_None`.
321	fn shorthand_group() -> CssAtomSet {
322		CssAtomSet::_None
323	}
324
325	/// Returns how this property may reset others, if this is a shorthand property.
326	fn shorthand_reset() -> ShorthandReset {
327		ShorthandReset::Unknown
328	}
329
330	/// Returns shorthands that will reset this property.
331	fn reset_by_shorthands() -> &'static [CssAtomSet] {
332		&[]
333	}
334
335	/// Returns which CSS specification(s) this property belongs to.
336	/// This allows tracking which CSS modules are used in a stylesheet.
337	fn property_group() -> PropertyGroup {
338		PropertyGroup::none()
339	}
340
341	/// Returns how the computed value is calculated from the specified value.
342	fn computed_value_type() -> ComputedValueType {
343		ComputedValueType::Unknown
344	}
345
346	/// Returns the canonical order for serialization (e.g., "per grammar", "unique").
347	/// Returns None if not specified or not applicable.
348	fn canonical_order() -> Option<&'static str> {
349		None
350	}
351
352	/// Returns the logical property group this property belongs to (e.g., "Margin", "Border").
353	/// This groups related logical/physical properties together.
354	/// Returns None if this is not part of a logical property group.
355	fn logical_property_group() -> Option<CssAtomSet> {
356		None
357	}
358
359	/// Returns which side(s) of the box this property applies to.
360	/// For example, `margin-top` returns BoxSide::Top, while `margin` returns all sides.
361	/// Returns BoxSide::none() if the property doesn't apply to a specific side.
362	fn box_side() -> BoxSide {
363		BoxSide::none()
364	}
365
366	/// Returns which portion(s) of the box model this property affects.
367	/// For example, `margin-top` returns BoxPortion::Margin, `border-width` returns BoxPortion::Border.
368	/// Returns BoxPortion::none() if the property doesn't affect the box model.
369	fn box_portion() -> BoxPortion {
370		BoxPortion::none()
371	}
372
373	/// Returns how unitless zero resolves for this property.
374	///
375	/// For properties that accept both `<number>` and `<length>`, unitless zero
376	/// may resolve to a number rather than a length. This affects whether the
377	/// minifier can safely reduce `0px` to `0`.
378	///
379	/// Examples where unitless zero resolves to Number (NOT safe to reduce):
380	/// - `line-height: 0` means 0x font-size multiplier
381	/// - `tab-size: 0` means 0 tab characters
382	/// - `border-image-outset: 0` means 0x border-width
383	fn unitless_zero_resolves() -> UnitlessZeroResolves {
384		// Default: most properties accept unitless zero as length
385		UnitlessZeroResolves::Length
386	}
387}
388
389#[cfg(test)]
390mod test {
391	use crate::*;
392
393	#[test]
394	fn test_box_side_count() {
395		assert_eq!(BoxSide::Top.num_sides(false), 1);
396		assert_eq!((BoxSide::Top | BoxSide::Right).num_sides(false), 2);
397		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom).num_sides(false), 3);
398		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(false), 4);
399		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(true), 0);
400
401		assert_eq!(BoxSide::all_bits().num_sides(false), 4);
402		assert_eq!(BoxSide::all_bits().num_sides(true), 4);
403
404		assert_eq!(BoxSide::BlockStart.num_sides(true), 1);
405		assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd).num_sides(true), 2);
406		assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart).num_sides(true), 3);
407		assert_eq!(
408			(BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(true),
409			4
410		);
411		assert_eq!(
412			(BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(false),
413			0
414		);
415	}
416}