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	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}