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