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