lightningcss/properties/
mod.rs

1//! CSS property values.
2//!
3//! Each property provides parsing and serialization support using the [Parse](super::traits::Parse)
4//! and [ToCss](super::traits::ToCss) traits. Properties are fully parsed as defined by the CSS spec,
5//! and printed in their canonical form. For example, most CSS properties are case-insensitive, and
6//! may be written in various orders, but when printed they are lower cased as appropriate and in a
7//! standard order.
8//!
9//! CSS properties often also contain many implicit values that are automatically filled in during
10//! parsing when omitted. These are also omitted when possible during serialization. Many properties
11//! also implement the [Default](std::default::Default) trait, which returns the initial value for the property.
12//!
13//! Shorthand properties are represented as structs containing fields for each of the sub-properties.
14//! If some of the sub-properties are not specified in the shorthand, their default values are filled in.
15//!
16//! The [Property](Property) enum contains the values of all properties, and can be used to parse property values by name.
17//! The [PropertyId](PropertyId) enum represents only property names, and not values and is used to refer to known properties.
18//!
19//! # Example
20//!
21//! This example shows how the `background` shorthand property is parsed and serialized. The `parse_string`
22//! function parses the background into a structure with all missing fields filled in with their default values.
23//! When printed using the `to_css_string` function, the components are in their canonical order, and default
24//! values are removed.
25//!
26//! ```
27//! use smallvec::smallvec;
28//! use lightningcss::{
29//!   properties::{Property, PropertyId, background::*},
30//!   values::{url::Url, image::Image, color::{CssColor, RGBA}, position::*, length::*},
31//!   stylesheet::{ParserOptions, PrinterOptions},
32//!   dependencies::Location,
33//! };
34//!
35//! let background = Property::parse_string(
36//!   PropertyId::from("background"),
37//!   "url('img.png') repeat fixed 20px 10px / 50px 100px",
38//!   ParserOptions::default()
39//! ).unwrap();
40//!
41//! assert_eq!(
42//!   background,
43//!   Property::Background(smallvec![Background {
44//!     image: Image::Url(Url {
45//!       url: "img.png".into(),
46//!       loc: Location { line: 1, column: 1 }
47//!     }),
48//!     color: CssColor::RGBA(RGBA {
49//!       red: 0,
50//!       green: 0,
51//!       blue: 0,
52//!       alpha: 0
53//!     }),
54//!     position: BackgroundPosition {
55//!       x: HorizontalPosition::Length(LengthPercentage::px(20.0)),
56//!       y: VerticalPosition::Length(LengthPercentage::px(10.0)),
57//!     },
58//!     repeat: BackgroundRepeat {
59//!       x: BackgroundRepeatKeyword::Repeat,
60//!       y: BackgroundRepeatKeyword::Repeat,
61//!     },
62//!     size: BackgroundSize::Explicit {
63//!       width: LengthPercentageOrAuto::LengthPercentage(LengthPercentage::px(50.0)),
64//!       height: LengthPercentageOrAuto::LengthPercentage(LengthPercentage::px(100.0)),
65//!     },
66//!     attachment: BackgroundAttachment::Fixed,
67//!     origin: BackgroundOrigin::PaddingBox,
68//!     clip: BackgroundClip::BorderBox,
69//!   }])
70//! );
71//!
72//! assert_eq!(
73//!   background.to_css_string(false, PrinterOptions::default()).unwrap(),
74//!   r#"background: url("img.png") 20px 10px / 50px 100px fixed"#
75//! );
76//! ```
77//!
78//! If you have a [cssparser::Parser](cssparser::Parser) already, you can also use the `parse` and `to_css`
79//! methods instead, rather than parsing from a string.
80//!
81//! # Unparsed and custom properties
82//!
83//! Custom and unknown properties are represented by the [CustomProperty](custom::CustomProperty) struct, and the
84//! `Property::Custom` variant. The value of these properties is not parsed, and is stored as a raw
85//! [TokenList](custom::TokenList), with the name as a string.
86//!
87//! If a known property is unable to be parsed, e.g. it contains `var()` references, then it is represented by the
88//! [UnparsedProperty](custom::UnparsedProperty) struct, and the `Property::Unparsed` variant. The value is stored
89//! as a raw [TokenList](custom::TokenList), with a [PropertyId](PropertyId) as the name.
90
91#![deny(missing_docs)]
92
93pub mod align;
94pub mod animation;
95pub mod background;
96pub mod border;
97pub mod border_image;
98pub mod border_radius;
99pub mod box_shadow;
100pub mod contain;
101pub mod css_modules;
102pub mod custom;
103pub mod display;
104pub mod effects;
105pub mod flex;
106pub mod font;
107pub mod grid;
108pub mod list;
109pub(crate) mod margin_padding;
110pub mod masking;
111pub mod outline;
112pub mod overflow;
113pub mod position;
114pub(crate) mod prefix_handler;
115pub mod size;
116pub mod svg;
117pub mod text;
118pub mod transform;
119pub mod transition;
120pub mod ui;
121
122use crate::declaration::DeclarationBlock;
123use crate::error::{ParserError, PrinterError};
124use crate::logical::{LogicalGroup, PropertyCategory};
125use crate::macros::enum_property;
126use crate::parser::starts_with_ignore_ascii_case;
127use crate::parser::ParserOptions;
128use crate::prefixes::Feature;
129use crate::printer::{Printer, PrinterOptions};
130use crate::targets::Targets;
131use crate::traits::{Parse, ParseWithOptions, Shorthand, ToCss};
132use crate::values::number::{CSSInteger, CSSNumber};
133use crate::values::string::CowArcStr;
134use crate::values::{
135  alpha::*, color::*, easing::EasingFunction, ident::DashedIdentReference, ident::NoneOrCustomIdentList, image::*,
136  length::*, position::*, rect::*, shape::FillRule, size::Size2D, time::Time,
137};
138use crate::vendor_prefix::VendorPrefix;
139#[cfg(feature = "visitor")]
140use crate::visitor::Visit;
141use align::*;
142use animation::*;
143use background::*;
144use border::*;
145use border_image::*;
146use border_radius::*;
147use box_shadow::*;
148use contain::*;
149use css_modules::*;
150use cssparser::*;
151use custom::*;
152use display::*;
153use effects::*;
154use flex::*;
155use font::*;
156use grid::*;
157use list::*;
158use margin_padding::*;
159use masking::*;
160use outline::*;
161use overflow::*;
162use size::*;
163use smallvec::{smallvec, SmallVec};
164#[cfg(feature = "into_owned")]
165use static_self::IntoOwned;
166use svg::*;
167use text::*;
168use transform::*;
169use transition::*;
170use ui::*;
171
172macro_rules! define_properties {
173  (
174    $(
175      $(#[$meta: meta])*
176      $name: literal: $property: ident($type: ty $(, $vp: ty)?) $( / $prefix: ident )* $( unprefixed: $unprefixed: literal )? $( options: $options: literal )? $( shorthand: $shorthand: literal )? $( [ logical_group: $logical_group: ident, category: $logical_category: ident ] )? $( if $condition: ident )?,
177    )+
178  ) => {
179    /// A CSS property id.
180    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
181    #[cfg_attr(feature = "visitor", derive(Visit))]
182    #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
183    pub enum PropertyId<'i> {
184      $(
185        #[doc=concat!("The `", $name, "` property.")]
186        $(#[$meta])*
187        $property$(($vp))?,
188      )+
189      /// The `all` property.
190      All,
191      /// An unknown or custom property name.
192      Custom(CustomPropertyName<'i>)
193    }
194
195    macro_rules! vp_name {
196      ($x: ty, $n: ident) => {
197        $n
198      };
199      ($x: ty, $n: expr) => {
200        $n
201      };
202    }
203
204    macro_rules! get_allowed_prefixes {
205      ($v: literal) => {
206        VendorPrefix::empty()
207      };
208      () => {
209        VendorPrefix::None
210      };
211    }
212
213    impl<'i> From<CowArcStr<'i>> for PropertyId<'i> {
214      fn from(name: CowArcStr<'i>) -> PropertyId<'i> {
215        let name_ref = name.as_ref();
216        let (prefix, name_ref) = if starts_with_ignore_ascii_case(name_ref, "-webkit-") {
217          (VendorPrefix::WebKit, &name_ref[8..])
218        } else if starts_with_ignore_ascii_case(name_ref, "-moz-") {
219          (VendorPrefix::Moz, &name_ref[5..])
220        } else if starts_with_ignore_ascii_case(name_ref, "-o-") {
221          (VendorPrefix::O, &name_ref[3..])
222        } else if starts_with_ignore_ascii_case(name_ref, "-ms-") {
223          (VendorPrefix::Ms, &name_ref[4..])
224        } else {
225          (VendorPrefix::None, name_ref)
226        };
227
228        Self::from_name_and_prefix(name_ref, prefix)
229          .unwrap_or_else(|_| PropertyId::Custom(name.into()))
230      }
231    }
232
233    impl<'i> From<&'i str> for PropertyId<'i> {
234      #[inline]
235      fn from(name: &'i str) -> PropertyId<'i> {
236        PropertyId::from(CowArcStr::from(name))
237      }
238    }
239
240    impl<'i> Parse<'i> for PropertyId<'i> {
241      fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
242        let name = input.expect_ident()?;
243        Ok(CowArcStr::from(name).into())
244      }
245    }
246
247    impl<'i> ToCss for PropertyId<'i> {
248      fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
249        let mut first = true;
250        macro_rules! delim {
251          () => {
252            #[allow(unused_assignments)]
253            if first {
254              first = false;
255            } else {
256              dest.delim(',', false)?;
257            }
258          };
259        }
260
261        let name = self.name();
262        for p in self.prefix().or_none() {
263          delim!();
264          p.to_css(dest)?;
265          dest.write_str(name)?;
266        }
267
268        Ok(())
269      }
270    }
271
272    impl<'i> PropertyId<'i> {
273      fn from_name_and_prefix(name: &str, prefix: VendorPrefix) -> Result<Self, ()> {
274        match_ignore_ascii_case! { name.as_ref(),
275          $(
276            $(#[$meta])*
277            $name => {
278              macro_rules! get_propertyid {
279                ($v: ty) => {
280                  PropertyId::$property(prefix)
281                };
282                () => {
283                  PropertyId::$property
284                };
285              }
286
287              let allowed_prefixes = get_allowed_prefixes!($($unprefixed)?) $(| VendorPrefix::$prefix)*;
288              if allowed_prefixes.contains(prefix) {
289                return Ok(get_propertyid!($($vp)?))
290              }
291            },
292          )+
293          "all" => return Ok(PropertyId::All),
294          _ => {}
295        }
296
297        Err(())
298      }
299
300      /// Returns the vendor prefix for this property id.
301      pub fn prefix(&self) -> VendorPrefix {
302        use PropertyId::*;
303        match self {
304          $(
305            $(#[$meta])*
306            $property$((vp_name!($vp, prefix)))? => {
307              $(
308                macro_rules! return_prefix {
309                  ($v: ty) => {
310                    return *prefix;
311                  };
312                }
313
314                return_prefix!($vp);
315              )?
316              #[allow(unreachable_code)]
317              VendorPrefix::empty()
318            },
319          )+
320          _ => VendorPrefix::empty()
321        }
322      }
323
324      pub(crate) fn with_prefix(&self, prefix: VendorPrefix) -> PropertyId<'i> {
325        use PropertyId::*;
326        match self {
327          $(
328            $(#[$meta])*
329            $property$((vp_name!($vp, _p)))? => {
330              macro_rules! get_prefixed {
331                ($v: ty) => {
332                  PropertyId::$property(prefix)
333                };
334                () => {
335                  PropertyId::$property
336                }
337              }
338
339              get_prefixed!($($vp)?)
340            },
341          )+
342          _ => self.clone()
343        }
344      }
345
346      pub(crate) fn add_prefix(&mut self, prefix: VendorPrefix) {
347        use PropertyId::*;
348        match self {
349          $(
350            $(#[$meta])*
351            $property$((vp_name!($vp, p)))? => {
352              macro_rules! get_prefixed {
353                ($v: ty) => {{
354                  *p |= prefix;
355                }};
356                () => {{}};
357              }
358
359              get_prefixed!($($vp)?)
360            },
361          )+
362          _ => {}
363        }
364      }
365
366      pub(crate) fn set_prefixes_for_targets(&mut self, targets: Targets) {
367        match self {
368          $(
369            $(#[$meta])*
370            #[allow(unused_variables)]
371            PropertyId::$property$((vp_name!($vp, prefix)))? => {
372              macro_rules! get_prefixed {
373                ($v: ty, $u: literal) => {};
374                ($v: ty) => {{
375                  *prefix = targets.prefixes(*prefix, Feature::$property);
376                }};
377                () => {};
378              }
379
380              get_prefixed!($($vp)? $(, $unprefixed)?);
381            },
382          )+
383          _ => {}
384        }
385      }
386
387      /// Returns the property name, without any vendor prefixes.
388      pub fn name(&self) -> &str {
389        use PropertyId::*;
390
391        match self {
392          $(
393            $(#[$meta])*
394            $property$((vp_name!($vp, _p)))? => $name,
395          )+
396          All => "all",
397          Custom(name) => name.as_ref()
398        }
399      }
400
401      /// Returns whether a property is a shorthand.
402      pub fn is_shorthand(&self) -> bool {
403        $(
404          macro_rules! shorthand {
405            ($s: literal) => {
406              if let PropertyId::$property$((vp_name!($vp, _prefix)))? = self {
407                return true
408              }
409            };
410            () => {}
411          }
412
413          shorthand!($($shorthand)?);
414        )+
415
416        false
417      }
418
419      /// Returns a shorthand value for this property id from the given declaration block.
420      pub(crate) fn shorthand_value<'a>(&self, decls: &DeclarationBlock<'a>) -> Option<(Property<'a>, bool)> {
421        // Inline function to remap lifetime names.
422        #[inline]
423        fn shorthand_value<'a, 'i>(property_id: &PropertyId<'a>, decls: &DeclarationBlock<'i>) -> Option<(Property<'i>, bool)> {
424          $(
425            #[allow(unused_macros)]
426            macro_rules! prefix {
427              ($v: ty, $p: ident) => {
428                *$p
429              };
430              ($p: ident) => {
431                VendorPrefix::None
432              };
433            }
434
435            macro_rules! shorthand {
436              ($s: literal) => {
437                if let PropertyId::$property$((vp_name!($vp, prefix)))? = &property_id {
438                  if let Some((val, important)) = <$type>::from_longhands(decls, prefix!($($vp,)? prefix)) {
439                    return Some((Property::$property(val $(, *vp_name!($vp, prefix))?), important))
440                  }
441                }
442              };
443              () => {}
444            }
445
446            shorthand!($($shorthand)?);
447          )+
448
449          None
450        }
451
452        shorthand_value(self, decls)
453      }
454
455      /// Returns a list of longhand property ids for a shorthand.
456      pub fn longhands(&self) -> Option<Vec<PropertyId<'static>>> {
457        macro_rules! prefix_default {
458          ($x: ty, $p: ident) => {
459            *$p
460          };
461          () => {
462            VendorPrefix::None
463          };
464        }
465
466        $(
467          macro_rules! shorthand {
468            ($s: literal) => {
469              if let PropertyId::$property$((vp_name!($vp, prefix)))? = self {
470                return Some(<$type>::longhands(prefix_default!($($vp, prefix)?)));
471              }
472            };
473            () => {}
474          }
475
476          shorthand!($($shorthand)?);
477        )+
478
479        None
480      }
481
482      /// Returns the logical property group for this property.
483      pub(crate) fn logical_group(&self) -> Option<LogicalGroup> {
484        $(
485          macro_rules! group {
486            ($g: ident) => {
487              if let PropertyId::$property$((vp_name!($vp, _prefix)))? = self {
488                return Some(LogicalGroup::$g)
489              }
490            };
491            () => {}
492          }
493
494          group!($($logical_group)?);
495        )+
496
497        None
498      }
499
500      /// Returns whether the property is logical or physical.
501      pub(crate) fn category(&self) -> Option<PropertyCategory> {
502        $(
503          macro_rules! category {
504            ($c: ident) => {
505              if let PropertyId::$property$((vp_name!($vp, _prefix)))? = self {
506                return Some(PropertyCategory::$c)
507              }
508            };
509            () => {}
510          }
511
512          category!($($logical_category)?);
513        )+
514
515        None
516      }
517    }
518
519    #[cfg(feature = "serde")]
520    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
521    impl<'i> serde::Serialize for PropertyId<'i> {
522      fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
523      where
524        S: serde::Serializer,
525      {
526        use serde::ser::SerializeStruct;
527
528        let name = self.name();
529        let prefix = self.prefix();
530
531        if prefix.is_empty() {
532          let mut s = serializer.serialize_struct("PropertyId", 1)?;
533          s.serialize_field("property", name)?;
534          s.end()
535        } else {
536          let mut s = serializer.serialize_struct("PropertyId", 2)?;
537          s.serialize_field("property", name)?;
538          s.serialize_field("vendor_prefix", &prefix)?;
539          s.end()
540        }
541      }
542    }
543
544    #[cfg(feature = "serde")]
545    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
546    impl<'i, 'de: 'i> serde::Deserialize<'de> for PropertyId<'i> {
547      fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
548      where
549        D: serde::Deserializer<'de>,
550      {
551        #[derive(serde::Deserialize)]
552        #[serde(field_identifier, rename_all = "snake_case")]
553        enum Field {
554          Property,
555          VendorPrefix
556        }
557
558        struct PropertyIdVisitor;
559        impl<'de> serde::de::Visitor<'de> for PropertyIdVisitor {
560          type Value = PropertyId<'de>;
561
562          fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
563            formatter.write_str("a PropertyId")
564          }
565
566          fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
567          where
568            A: serde::de::MapAccess<'de>,
569          {
570            let mut property: Option<CowArcStr> = None;
571            let mut vendor_prefix = None;
572            while let Some(key) = map.next_key()? {
573              match key {
574                Field::Property => {
575                  property = Some(map.next_value()?);
576                }
577                Field::VendorPrefix => {
578                  vendor_prefix = Some(map.next_value()?);
579                }
580              }
581            }
582
583            let property = property.ok_or_else(|| serde::de::Error::missing_field("property"))?;
584            let vendor_prefix = vendor_prefix.unwrap_or(VendorPrefix::None);
585            let property_id = PropertyId::from_name_and_prefix(property.as_ref(), vendor_prefix)
586              .unwrap_or_else(|_| PropertyId::Custom(property.into()));
587            Ok(property_id)
588          }
589        }
590
591        deserializer.deserialize_any(PropertyIdVisitor)
592      }
593    }
594
595    #[cfg(feature = "jsonschema")]
596    #[cfg_attr(docsrs, doc(cfg(feature = "jsonschema")))]
597    impl<'i> schemars::JsonSchema for PropertyId<'i> {
598      fn is_referenceable() -> bool {
599        true
600      }
601
602      fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
603        macro_rules! property {
604          ($n: literal) => {
605            fn property(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
606              schemars::schema::Schema::Object(schemars::schema::SchemaObject {
607                instance_type: Some(schemars::schema::InstanceType::String.into()),
608                enum_values: Some(vec![$n.into()]),
609                ..Default::default()
610              })
611            }
612          }
613        }
614
615        schemars::schema::Schema::Object(schemars::schema::SchemaObject {
616          subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
617            one_of: Some(vec![
618              $(
619                {
620                  property!($name);
621
622                  macro_rules! with_prefix {
623                    ($v: ty) => {{
624                      #[derive(schemars::JsonSchema)]
625                      struct T<'i> {
626                        #[schemars(rename = "property", schema_with = "property")]
627                        _property: &'i u8,
628                        #[schemars(rename = "vendorPrefix")]
629                        _vendor_prefix: VendorPrefix,
630                      }
631
632                      T::json_schema(gen)
633                    }};
634                    () => {{
635                      #[derive(schemars::JsonSchema)]
636                      struct T<'i> {
637                        #[schemars(rename = "property", schema_with = "property")]
638                        _property: &'i u8,
639                      }
640
641                      T::json_schema(gen)
642                    }};
643                  }
644
645                  with_prefix!($($vp)?)
646                },
647              )+
648              {
649                property!("all");
650
651                #[derive(schemars::JsonSchema)]
652                struct T<'i> {
653                  #[schemars(rename = "property", schema_with = "property")]
654                  _property: &'i u8,
655                }
656
657                T::json_schema(gen)
658              },
659              {
660                #[derive(schemars::JsonSchema)]
661                struct T {
662                  #[schemars(rename = "property")]
663                  _property: String,
664                }
665
666                T::json_schema(gen)
667              }
668            ]),
669            ..Default::default()
670          })),
671          ..Default::default()
672        })
673      }
674
675      fn schema_name() -> String {
676        "PropertyId".into()
677      }
678    }
679
680    /// A CSS property.
681    #[derive(Debug, Clone, PartialEq)]
682    #[cfg_attr(feature = "visitor", derive(Visit), visit(visit_property, PROPERTIES))]
683    #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
684    pub enum Property<'i> {
685      $(
686        #[doc=concat!("The `", $name, "` property.")]
687        $(#[$meta])*
688        $property($type, $($vp)?),
689      )+
690      /// The [all](https://drafts.csswg.org/css-cascade-5/#all-shorthand) shorthand property.
691      All(CSSWideKeyword),
692      /// An unparsed property.
693      Unparsed(UnparsedProperty<'i>),
694      /// A custom or unknown property.
695      Custom(CustomProperty<'i>),
696    }
697
698    impl<'i> Property<'i> {
699      /// Parses a CSS property by name.
700      pub fn parse<'t>(property_id: PropertyId<'i>, input: &mut Parser<'i, 't>, options: &ParserOptions<'_, 'i>) -> Result<Property<'i>, ParseError<'i, ParserError<'i>>> {
701        let state = input.state();
702
703        match property_id {
704          $(
705            $(#[$meta])*
706            PropertyId::$property$((vp_name!($vp, prefix)))? $(if options.$condition.is_some())? => {
707              if let Ok(c) = <$type>::parse_with_options(input, options) {
708                if input.expect_exhausted().is_ok() {
709                  return Ok(Property::$property(c $(, vp_name!($vp, prefix))?))
710                }
711              }
712            },
713          )+
714          PropertyId::All => return Ok(Property::All(CSSWideKeyword::parse(input)?)),
715          PropertyId::Custom(name) => return Ok(Property::Custom(CustomProperty::parse(name, input, options)?)),
716          _ => {}
717        };
718
719        // If a value was unable to be parsed, treat as an unparsed property.
720        // This is different from a custom property, handled below, in that the property name is known
721        // and stored as an enum rather than a string. This lets property handlers more easily deal with it.
722        // Ideally we'd only do this if var() or env() references were seen, but err on the safe side for now.
723        input.reset(&state);
724        return Ok(Property::Unparsed(UnparsedProperty::parse(property_id, input, options)?))
725      }
726
727      /// Returns the property id for this property.
728      pub fn property_id(&self) -> PropertyId<'i> {
729        use Property::*;
730
731        match self {
732          $(
733            $(#[$meta])*
734            $property(_, $(vp_name!($vp, p))?) => PropertyId::$property$((*vp_name!($vp, p)))?,
735          )+
736          All(_) => PropertyId::All,
737          Unparsed(unparsed) => unparsed.property_id.clone(),
738          Custom(custom) => PropertyId::Custom(custom.name.clone())
739        }
740      }
741
742      /// Parses a CSS property from a string.
743      pub fn parse_string(property_id: PropertyId<'i>, input: &'i str, options: ParserOptions<'_, 'i>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
744        let mut input = ParserInput::new(input);
745        let mut parser = Parser::new(&mut input);
746        Self::parse(property_id, &mut parser, &options)
747      }
748
749      /// Sets the vendor prefixes for this property.
750      ///
751      /// If the property doesn't support vendor prefixes, this function does nothing.
752      /// If vendor prefixes are set which do not exist for the property, they are ignored
753      /// and only the valid prefixes are set.
754      pub fn set_prefix(&mut self, prefix: VendorPrefix) {
755        use Property::*;
756        match self {
757          $(
758            $(#[$meta])*
759            $property(_, $(vp_name!($vp, p))?) => {
760              macro_rules! set {
761                ($v: ty) => {
762                  *p = (prefix & (get_allowed_prefixes!($($unprefixed)?) $(| VendorPrefix::$prefix)*)).or(*p);
763                };
764                () => {};
765              }
766
767              set!($($vp)?);
768            },
769          )+
770          _ => {}
771        }
772      }
773
774      /// Serializes the value of a CSS property without its name or `!important` flag.
775      pub fn value_to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
776        use Property::*;
777
778        match self {
779          $(
780            $(#[$meta])*
781            $property(val, $(vp_name!($vp, _p))?) => {
782              val.to_css(dest)
783            }
784          )+
785          All(keyword) => keyword.to_css(dest),
786          Unparsed(unparsed) => {
787            unparsed.value.to_css(dest, false)
788          }
789          Custom(custom) => {
790            custom.value.to_css(dest, matches!(custom.name, CustomPropertyName::Custom(..)))
791          }
792        }
793      }
794
795      /// Serializes the value of a CSS property as a string.
796      pub fn value_to_css_string(&self, options: PrinterOptions) -> Result<String, PrinterError> {
797        let mut s = String::new();
798        let mut printer = Printer::new(&mut s, options);
799        self.value_to_css(&mut printer)?;
800        Ok(s)
801      }
802
803      /// Serializes the CSS property, with an optional `!important` flag.
804      pub fn to_css<W>(&self, dest: &mut Printer<W>, important: bool) -> Result<(), PrinterError> where W: std::fmt::Write {
805        use Property::*;
806
807        let mut first = true;
808        macro_rules! start {
809          () => {
810            #[allow(unused_assignments)]
811            if first {
812              first = false;
813            } else {
814              dest.write_char(';')?;
815              dest.newline()?;
816            }
817          };
818        }
819
820        macro_rules! write_important {
821          () => {
822            if important {
823              dest.whitespace()?;
824              dest.write_str("!important")?;
825            }
826          }
827        }
828
829        let (name, prefix) = match self {
830          $(
831            $(#[$meta])*
832            $property(_, $(vp_name!($vp, prefix))?) => {
833              macro_rules! get_prefix {
834                ($v: ty) => {
835                  *prefix
836                };
837                () => {
838                  VendorPrefix::None
839                };
840              }
841
842              ($name, get_prefix!($($vp)?))
843            },
844          )+
845          All(_) => ("all", VendorPrefix::None),
846          Unparsed(unparsed) => {
847            let mut prefix = unparsed.property_id.prefix();
848            if prefix.is_empty() {
849              prefix = VendorPrefix::None;
850            }
851            (unparsed.property_id.name(), prefix)
852          },
853          Custom(custom) => {
854            custom.name.to_css(dest)?;
855            dest.delim(':', false)?;
856            self.value_to_css(dest)?;
857            write_important!();
858            return Ok(())
859          }
860        };
861        for p in prefix {
862          start!();
863          p.to_css(dest)?;
864          dest.write_str(name)?;
865          dest.delim(':', false)?;
866          self.value_to_css(dest)?;
867          write_important!();
868        }
869        Ok(())
870      }
871
872      /// Serializes the CSS property to a string, with an optional `!important` flag.
873      pub fn to_css_string(&self, important: bool, options: PrinterOptions) -> Result<String, PrinterError> {
874        let mut s = String::new();
875        let mut printer = Printer::new(&mut s, options);
876        self.to_css(&mut printer, important)?;
877        Ok(s)
878      }
879
880      /// Returns the given longhand property for a shorthand.
881      pub fn longhand(&self, property_id: &PropertyId) -> Option<Property<'i>> {
882        $(
883          macro_rules! shorthand {
884            ($s: literal) => {
885              if let Property::$property(val $(, vp_name!($vp, prefix))?) = self {
886                $(
887                  if *vp_name!($vp, prefix) != property_id.prefix() {
888                    return None
889                  }
890                )?
891                return val.longhand(property_id)
892              }
893            };
894            () => {}
895          }
896
897          shorthand!($($shorthand)?);
898        )+
899
900        None
901      }
902
903      /// Updates this shorthand from a longhand property.
904      pub fn set_longhand(&mut self, property: &Property<'i>) -> Result<(), ()> {
905        $(
906          macro_rules! shorthand {
907            ($s: literal) => {
908              if let Property::$property(val $(, vp_name!($vp, prefix))?) = self {
909                $(
910                  if *vp_name!($vp, prefix) != property.property_id().prefix() {
911                    return Err(())
912                  }
913                )?
914                return val.set_longhand(property)
915              }
916            };
917            () => {}
918          }
919
920          shorthand!($($shorthand)?);
921        )+
922        Err(())
923      }
924    }
925
926    #[cfg(feature = "serde")]
927    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
928    impl<'i> serde::Serialize for Property<'i> {
929      fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
930      where
931        S: serde::Serializer,
932      {
933        use serde::ser::SerializeStruct;
934        use Property::*;
935
936        match self {
937          Unparsed(unparsed) => {
938            let mut s = serializer.serialize_struct("Property", 2)?;
939            s.serialize_field("property", "unparsed")?;
940            s.serialize_field("value", unparsed)?;
941            return s.end()
942          }
943          Custom(unparsed) => {
944            let mut s = serializer.serialize_struct("Property", 2)?;
945            s.serialize_field("property", "custom")?;
946            s.serialize_field("value", unparsed)?;
947            return s.end()
948          }
949          _ => {}
950        }
951
952        let id = self.property_id();
953        let name = id.name();
954        let prefix = id.prefix();
955
956        let mut s = if prefix.is_empty() {
957          let mut s = serializer.serialize_struct("Property", 2)?;
958          s.serialize_field("property", name)?;
959          s
960        } else {
961          let mut s = serializer.serialize_struct("Property", 3)?;
962          s.serialize_field("property", name)?;
963          s.serialize_field("vendorPrefix", &prefix)?;
964          s
965        };
966
967        match self {
968          $(
969            $(#[$meta])*
970            $property(value, $(vp_name!($vp, _p))?) => {
971              s.serialize_field("value", value)?;
972            }
973          )+
974          All(value) => {
975            s.serialize_field("value", value)?;
976          }
977          Unparsed(_) | Custom(_) => unreachable!()
978        }
979
980        s.end()
981      }
982    }
983
984    #[cfg(feature = "serde")]
985    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
986    impl<'i, 'de: 'i> serde::Deserialize<'de> for Property<'i> {
987      fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
988      where
989        D: serde::Deserializer<'de>,
990      {
991        enum ContentOrRaw<'de> {
992          Content(serde_content::Value<'de>),
993          Raw(CowArcStr<'de>)
994        }
995
996        struct PartialProperty<'de> {
997          property_id: PropertyId<'de>,
998          value: ContentOrRaw<'de>,
999        }
1000
1001        #[derive(serde::Deserialize)]
1002        #[serde(field_identifier, rename_all = "camelCase")]
1003        enum Field {
1004          Property,
1005          VendorPrefix,
1006          Value,
1007          Raw
1008        }
1009
1010        struct PropertyIdVisitor;
1011        impl<'de> serde::de::Visitor<'de> for PropertyIdVisitor {
1012          type Value = PartialProperty<'de>;
1013
1014          fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1015            formatter.write_str("a Property")
1016          }
1017
1018          fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1019          where
1020            A: serde::de::MapAccess<'de>,
1021          {
1022            let mut property: Option<CowArcStr> = None;
1023            let mut vendor_prefix = None;
1024            let mut value: Option<ContentOrRaw<'de>> = None;
1025            while let Some(key) = map.next_key()? {
1026              match key {
1027                Field::Property => {
1028                  property = Some(map.next_value()?);
1029                }
1030                Field::VendorPrefix => {
1031                  vendor_prefix = Some(map.next_value()?);
1032                }
1033                Field::Value => {
1034                  value = Some(ContentOrRaw::Content(map.next_value()?));
1035                }
1036                Field::Raw => {
1037                  value = Some(ContentOrRaw::Raw(map.next_value()?));
1038                }
1039              }
1040            }
1041
1042            let property = property.ok_or_else(|| serde::de::Error::missing_field("property"))?;
1043            let vendor_prefix = vendor_prefix.unwrap_or(VendorPrefix::None);
1044            let value = value.ok_or_else(|| serde::de::Error::missing_field("value"))?;
1045            let property_id = PropertyId::from_name_and_prefix(property.as_ref(), vendor_prefix)
1046              .unwrap_or_else(|_| PropertyId::from(property));
1047            Ok(PartialProperty {
1048              property_id,
1049              value,
1050            })
1051          }
1052        }
1053
1054        let partial = deserializer.deserialize_any(PropertyIdVisitor)?;
1055
1056        let content = match partial.value {
1057          ContentOrRaw::Raw(raw) => {
1058            let res = Property::parse_string(partial.property_id, raw.as_ref(), ParserOptions::default())
1059              .map_err(|_| serde::de::Error::custom("Could not parse value"))?;
1060            return Ok(res.into_owned())
1061          }
1062          ContentOrRaw::Content(content) => content
1063        };
1064
1065        let deserializer = serde_content::Deserializer::new(content).coerce_numbers();
1066        match partial.property_id {
1067          $(
1068            $(#[$meta])*
1069            PropertyId::$property$((vp_name!($vp, prefix)))? => {
1070              let value = <$type>::deserialize(deserializer).map_err(|e| serde::de::Error::custom(e.to_string()))?;
1071              Ok(Property::$property(value $(, vp_name!($vp, prefix))?))
1072            },
1073          )+
1074          PropertyId::Custom(name) => {
1075            if name.as_ref() == "unparsed" {
1076              let value = UnparsedProperty::deserialize(deserializer).map_err(|e| serde::de::Error::custom(e.to_string()))?;
1077              Ok(Property::Unparsed(value))
1078            } else {
1079              let value = CustomProperty::deserialize(deserializer).map_err(|e| serde::de::Error::custom(e.to_string()))?;
1080              Ok(Property::Custom(value))
1081            }
1082          }
1083          PropertyId::All => {
1084            let value = CSSWideKeyword::deserialize(deserializer).map_err(|e| serde::de::Error::custom(e.to_string()))?;
1085            Ok(Property::All(value))
1086          }
1087        }
1088      }
1089    }
1090
1091    #[cfg(feature = "jsonschema")]
1092    #[cfg_attr(docsrs, doc(cfg(feature = "jsonschema")))]
1093    impl<'i> schemars::JsonSchema for Property<'i> {
1094      fn is_referenceable() -> bool {
1095        true
1096      }
1097
1098      fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1099        macro_rules! property {
1100          ($n: literal) => {
1101            fn property(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
1102              schemars::schema::Schema::Object(schemars::schema::SchemaObject {
1103                instance_type: Some(schemars::schema::InstanceType::String.into()),
1104                enum_values: Some(vec![$n.into()]),
1105                ..Default::default()
1106              })
1107            }
1108          }
1109        }
1110
1111        schemars::schema::Schema::Object(schemars::schema::SchemaObject {
1112          subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
1113            one_of: Some(vec![
1114              $(
1115                {
1116                  property!($name);
1117
1118                  macro_rules! with_prefix {
1119                    ($v: ty) => {{
1120                      #[derive(schemars::JsonSchema)]
1121                      struct T<'i> {
1122                        #[schemars(rename = "property", schema_with = "property")]
1123                        _property: &'i u8,
1124                        #[schemars(rename = "vendorPrefix")]
1125                        _vendor_prefix: VendorPrefix,
1126                        #[schemars(rename = "value")]
1127                        _value: $type,
1128                      }
1129
1130                      T::json_schema(gen)
1131                    }};
1132                    () => {{
1133                      #[derive(schemars::JsonSchema)]
1134                      struct T<'i> {
1135                        #[schemars(rename = "property", schema_with = "property")]
1136                        _property: &'i u8,
1137                        #[schemars(rename = "value")]
1138                        _value: $type,
1139                      }
1140
1141                      T::json_schema(gen)
1142                    }};
1143                  }
1144
1145                  with_prefix!($($vp)?)
1146                },
1147              )+
1148              {
1149                property!("all");
1150                #[derive(schemars::JsonSchema)]
1151                struct T {
1152                  #[schemars(rename = "property", schema_with = "property")]
1153                  _property: u8,
1154                  #[schemars(rename = "value")]
1155                  _value: CSSWideKeyword
1156                }
1157                T::json_schema(gen)
1158              },
1159              {
1160                property!("unparsed");
1161
1162                #[derive(schemars::JsonSchema)]
1163                struct T<'i> {
1164                  #[schemars(rename = "property", schema_with = "property")]
1165                  _property: &'i u8,
1166                  #[schemars(rename = "value")]
1167                  _value: UnparsedProperty<'i>,
1168                }
1169
1170                T::json_schema(gen)
1171              },
1172              {
1173                property!("custom");
1174
1175                #[derive(schemars::JsonSchema)]
1176                struct T<'i> {
1177                  #[schemars(rename = "property", schema_with = "property")]
1178                  _property: &'i u8,
1179                  #[schemars(rename = "value")]
1180                  _value: CustomProperty<'i>,
1181                }
1182
1183                T::json_schema(gen)
1184              }
1185            ]),
1186            ..Default::default()
1187          })),
1188          ..Default::default()
1189        })
1190      }
1191
1192      fn schema_name() -> String {
1193        "Declaration".into()
1194      }
1195    }
1196  };
1197}
1198
1199define_properties! {
1200  "background-color": BackgroundColor(CssColor),
1201  "background-image": BackgroundImage(SmallVec<[Image<'i>; 1]>),
1202  "background-position-x": BackgroundPositionX(SmallVec<[HorizontalPosition; 1]>),
1203  "background-position-y": BackgroundPositionY(SmallVec<[VerticalPosition; 1]>),
1204  "background-position": BackgroundPosition(SmallVec<[BackgroundPosition; 1]>) shorthand: true,
1205  "background-size": BackgroundSize(SmallVec<[BackgroundSize; 1]>),
1206  "background-repeat": BackgroundRepeat(SmallVec<[BackgroundRepeat; 1]>),
1207  "background-attachment": BackgroundAttachment(SmallVec<[BackgroundAttachment; 1]>),
1208  "background-clip": BackgroundClip(SmallVec<[BackgroundClip; 1]>, VendorPrefix) / WebKit / Moz,
1209  "background-origin": BackgroundOrigin(SmallVec<[BackgroundOrigin; 1]>),
1210  "background": Background(SmallVec<[Background<'i>; 1]>) shorthand: true,
1211
1212  "box-shadow": BoxShadow(SmallVec<[BoxShadow; 1]>, VendorPrefix) / WebKit / Moz,
1213  "opacity": Opacity(AlphaValue),
1214  "color": Color(CssColor),
1215  "display": Display(Display),
1216  "visibility": Visibility(Visibility),
1217
1218  "width": Width(Size) [logical_group: Size, category: Physical],
1219  "height": Height(Size) [logical_group: Size, category: Physical],
1220  "min-width": MinWidth(Size) [logical_group: MinSize, category: Physical],
1221  "min-height": MinHeight(Size) [logical_group: MinSize, category: Physical],
1222  "max-width": MaxWidth(MaxSize) [logical_group: MaxSize, category: Physical],
1223  "max-height": MaxHeight(MaxSize) [logical_group: MaxSize, category: Physical],
1224  "block-size": BlockSize(Size) [logical_group: Size, category: Logical],
1225  "inline-size": InlineSize(Size) [logical_group: Size, category: Logical],
1226  "min-block-size": MinBlockSize(Size) [logical_group: MinSize, category: Logical],
1227  "min-inline-size": MinInlineSize(Size) [logical_group: MinSize, category: Logical],
1228  "max-block-size": MaxBlockSize(MaxSize) [logical_group: MaxSize, category: Logical],
1229  "max-inline-size": MaxInlineSize(MaxSize) [logical_group: MaxSize, category: Logical],
1230  "box-sizing": BoxSizing(BoxSizing, VendorPrefix) / WebKit / Moz,
1231  "aspect-ratio": AspectRatio(AspectRatio),
1232
1233  "overflow": Overflow(Overflow) shorthand: true,
1234  "overflow-x": OverflowX(OverflowKeyword),
1235  "overflow-y": OverflowY(OverflowKeyword),
1236  "text-overflow": TextOverflow(TextOverflow, VendorPrefix) / O,
1237
1238  // https://www.w3.org/TR/2020/WD-css-position-3-20200519
1239  "position": Position(position::Position),
1240  "top": Top(LengthPercentageOrAuto) [logical_group: Inset, category: Physical],
1241  "bottom": Bottom(LengthPercentageOrAuto) [logical_group: Inset, category: Physical],
1242  "left": Left(LengthPercentageOrAuto) [logical_group: Inset, category: Physical],
1243  "right": Right(LengthPercentageOrAuto) [logical_group: Inset, category: Physical],
1244  "inset-block-start": InsetBlockStart(LengthPercentageOrAuto) [logical_group: Inset, category: Logical],
1245  "inset-block-end": InsetBlockEnd(LengthPercentageOrAuto) [logical_group: Inset, category: Logical],
1246  "inset-inline-start": InsetInlineStart(LengthPercentageOrAuto) [logical_group: Inset, category: Logical],
1247  "inset-inline-end": InsetInlineEnd(LengthPercentageOrAuto) [logical_group: Inset, category: Logical],
1248  "inset-block": InsetBlock(InsetBlock) shorthand: true,
1249  "inset-inline": InsetInline(InsetInline) shorthand: true,
1250  "inset": Inset(Inset) shorthand: true,
1251
1252  "border-spacing": BorderSpacing(Size2D<Length>),
1253
1254  "border-top-color": BorderTopColor(CssColor) [logical_group: BorderColor, category: Physical],
1255  "border-bottom-color": BorderBottomColor(CssColor) [logical_group: BorderColor, category: Physical],
1256  "border-left-color": BorderLeftColor(CssColor) [logical_group: BorderColor, category: Physical],
1257  "border-right-color": BorderRightColor(CssColor) [logical_group: BorderColor, category: Physical],
1258  "border-block-start-color": BorderBlockStartColor(CssColor) [logical_group: BorderColor, category: Logical],
1259  "border-block-end-color": BorderBlockEndColor(CssColor) [logical_group: BorderColor, category: Logical],
1260  "border-inline-start-color": BorderInlineStartColor(CssColor) [logical_group: BorderColor, category: Logical],
1261  "border-inline-end-color": BorderInlineEndColor(CssColor) [logical_group: BorderColor, category: Logical],
1262
1263  "border-top-style": BorderTopStyle(LineStyle) [logical_group: BorderStyle, category: Physical],
1264  "border-bottom-style": BorderBottomStyle(LineStyle) [logical_group: BorderStyle, category: Physical],
1265  "border-left-style": BorderLeftStyle(LineStyle) [logical_group: BorderStyle, category: Physical],
1266  "border-right-style": BorderRightStyle(LineStyle) [logical_group: BorderStyle, category: Physical],
1267  "border-block-start-style": BorderBlockStartStyle(LineStyle) [logical_group: BorderStyle, category: Logical],
1268  "border-block-end-style": BorderBlockEndStyle(LineStyle) [logical_group: BorderStyle, category: Logical],
1269  "border-inline-start-style": BorderInlineStartStyle(LineStyle) [logical_group: BorderStyle, category: Logical],
1270  "border-inline-end-style": BorderInlineEndStyle(LineStyle) [logical_group: BorderStyle, category: Logical],
1271
1272  "border-top-width": BorderTopWidth(BorderSideWidth) [logical_group: BorderWidth, category: Physical],
1273  "border-bottom-width": BorderBottomWidth(BorderSideWidth) [logical_group: BorderWidth, category: Physical],
1274  "border-left-width": BorderLeftWidth(BorderSideWidth) [logical_group: BorderWidth, category: Physical],
1275  "border-right-width": BorderRightWidth(BorderSideWidth) [logical_group: BorderWidth, category: Physical],
1276  "border-block-start-width": BorderBlockStartWidth(BorderSideWidth) [logical_group: BorderWidth, category: Logical],
1277  "border-block-end-width": BorderBlockEndWidth(BorderSideWidth) [logical_group: BorderWidth, category: Logical],
1278  "border-inline-start-width": BorderInlineStartWidth(BorderSideWidth) [logical_group: BorderWidth, category: Logical],
1279  "border-inline-end-width": BorderInlineEndWidth(BorderSideWidth) [logical_group: BorderWidth, category: Logical],
1280
1281  "border-top-left-radius": BorderTopLeftRadius(Size2D<LengthPercentage>, VendorPrefix) / WebKit / Moz [logical_group: BorderRadius, category: Physical],
1282  "border-top-right-radius": BorderTopRightRadius(Size2D<LengthPercentage>, VendorPrefix) / WebKit / Moz [logical_group: BorderRadius, category: Physical],
1283  "border-bottom-left-radius": BorderBottomLeftRadius(Size2D<LengthPercentage>, VendorPrefix) / WebKit / Moz [logical_group: BorderRadius, category: Physical],
1284  "border-bottom-right-radius": BorderBottomRightRadius(Size2D<LengthPercentage>, VendorPrefix) / WebKit / Moz [logical_group: BorderRadius, category: Physical],
1285  "border-start-start-radius": BorderStartStartRadius(Size2D<LengthPercentage>) [logical_group: BorderRadius, category: Logical],
1286  "border-start-end-radius": BorderStartEndRadius(Size2D<LengthPercentage>) [logical_group: BorderRadius, category: Logical],
1287  "border-end-start-radius": BorderEndStartRadius(Size2D<LengthPercentage>) [logical_group: BorderRadius, category: Logical],
1288  "border-end-end-radius": BorderEndEndRadius(Size2D<LengthPercentage>) [logical_group: BorderRadius, category: Logical],
1289  "border-radius": BorderRadius(BorderRadius, VendorPrefix) / WebKit / Moz shorthand: true,
1290
1291  "border-image-source": BorderImageSource(Image<'i>),
1292  "border-image-outset": BorderImageOutset(Rect<LengthOrNumber>),
1293  "border-image-repeat": BorderImageRepeat(BorderImageRepeat),
1294  "border-image-width": BorderImageWidth(Rect<BorderImageSideWidth>),
1295  "border-image-slice": BorderImageSlice(BorderImageSlice),
1296  "border-image": BorderImage(BorderImage<'i>, VendorPrefix) / WebKit / Moz / O shorthand: true,
1297
1298  "border-color": BorderColor(BorderColor) shorthand: true,
1299  "border-style": BorderStyle(BorderStyle) shorthand: true,
1300  "border-width": BorderWidth(BorderWidth) shorthand: true,
1301
1302  "border-block-color": BorderBlockColor(BorderBlockColor) shorthand: true,
1303  "border-block-style": BorderBlockStyle(BorderBlockStyle) shorthand: true,
1304  "border-block-width": BorderBlockWidth(BorderBlockWidth) shorthand: true,
1305
1306  "border-inline-color": BorderInlineColor(BorderInlineColor) shorthand: true,
1307  "border-inline-style": BorderInlineStyle(BorderInlineStyle) shorthand: true,
1308  "border-inline-width": BorderInlineWidth(BorderInlineWidth) shorthand: true,
1309
1310  "border": Border(Border) shorthand: true,
1311  "border-top": BorderTop(BorderTop) shorthand: true,
1312  "border-bottom": BorderBottom(BorderBottom) shorthand: true,
1313  "border-left": BorderLeft(BorderLeft) shorthand: true,
1314  "border-right": BorderRight(BorderRight) shorthand: true,
1315  "border-block": BorderBlock(BorderBlock) shorthand: true,
1316  "border-block-start": BorderBlockStart(BorderBlockStart) shorthand: true,
1317  "border-block-end": BorderBlockEnd(BorderBlockEnd) shorthand: true,
1318  "border-inline": BorderInline(BorderInline) shorthand: true,
1319  "border-inline-start": BorderInlineStart(BorderInlineStart) shorthand: true,
1320  "border-inline-end": BorderInlineEnd(BorderInlineEnd) shorthand: true,
1321
1322  "outline": Outline(Outline) shorthand: true,
1323  "outline-color": OutlineColor(CssColor),
1324  "outline-style": OutlineStyle(OutlineStyle),
1325  "outline-width": OutlineWidth(BorderSideWidth),
1326
1327  // Flex properties: https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119
1328  "flex-direction": FlexDirection(FlexDirection, VendorPrefix) / WebKit / Ms,
1329  "flex-wrap": FlexWrap(FlexWrap, VendorPrefix) / WebKit / Ms,
1330  "flex-flow": FlexFlow(FlexFlow, VendorPrefix) / WebKit / Ms shorthand: true,
1331  "flex-grow": FlexGrow(CSSNumber, VendorPrefix) / WebKit,
1332  "flex-shrink": FlexShrink(CSSNumber, VendorPrefix) / WebKit,
1333  "flex-basis": FlexBasis(LengthPercentageOrAuto, VendorPrefix) / WebKit,
1334  "flex": Flex(Flex, VendorPrefix) / WebKit / Ms shorthand: true,
1335  "order": Order(CSSInteger, VendorPrefix) / WebKit,
1336
1337  // Align properties: https://www.w3.org/TR/2020/WD-css-align-3-20200421
1338  "align-content": AlignContent(AlignContent, VendorPrefix) / WebKit,
1339  "justify-content": JustifyContent(JustifyContent, VendorPrefix) / WebKit,
1340  "place-content": PlaceContent(PlaceContent) shorthand: true,
1341  "align-self": AlignSelf(AlignSelf, VendorPrefix) / WebKit,
1342  "justify-self": JustifySelf(JustifySelf),
1343  "place-self": PlaceSelf(PlaceSelf) shorthand: true,
1344  "align-items": AlignItems(AlignItems, VendorPrefix) / WebKit,
1345  "justify-items": JustifyItems(JustifyItems),
1346  "place-items": PlaceItems(PlaceItems) shorthand: true,
1347  "row-gap": RowGap(GapValue),
1348  "column-gap": ColumnGap(GapValue),
1349  "gap": Gap(Gap) shorthand: true,
1350
1351  // Old flex (2009): https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/
1352  "box-orient": BoxOrient(BoxOrient, VendorPrefix) / WebKit / Moz unprefixed: false,
1353  "box-direction": BoxDirection(BoxDirection, VendorPrefix) / WebKit / Moz unprefixed: false,
1354  "box-ordinal-group": BoxOrdinalGroup(CSSInteger, VendorPrefix) / WebKit / Moz unprefixed: false,
1355  "box-align": BoxAlign(BoxAlign, VendorPrefix) / WebKit / Moz unprefixed: false,
1356  "box-flex": BoxFlex(CSSNumber, VendorPrefix) / WebKit / Moz unprefixed: false,
1357  "box-flex-group": BoxFlexGroup(CSSInteger, VendorPrefix) / WebKit unprefixed: false,
1358  "box-pack": BoxPack(BoxPack, VendorPrefix) / WebKit / Moz unprefixed: false,
1359  "box-lines": BoxLines(BoxLines, VendorPrefix) / WebKit / Moz unprefixed: false,
1360
1361  // Old flex (2012): https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/
1362  "flex-pack": FlexPack(FlexPack, VendorPrefix) / Ms unprefixed: false,
1363  "flex-order": FlexOrder(CSSInteger, VendorPrefix) / Ms unprefixed: false,
1364  "flex-align": FlexAlign(BoxAlign, VendorPrefix) / Ms unprefixed: false,
1365  "flex-item-align": FlexItemAlign(FlexItemAlign, VendorPrefix) / Ms unprefixed: false,
1366  "flex-line-pack": FlexLinePack(FlexLinePack, VendorPrefix) / Ms unprefixed: false,
1367
1368  // Microsoft extensions
1369  "flex-positive": FlexPositive(CSSNumber, VendorPrefix) / Ms unprefixed: false,
1370  "flex-negative": FlexNegative(CSSNumber, VendorPrefix) / Ms unprefixed: false,
1371  "flex-preferred-size": FlexPreferredSize(LengthPercentageOrAuto, VendorPrefix) / Ms unprefixed: false,
1372
1373  "grid-template-columns": GridTemplateColumns(TrackSizing<'i>),
1374  "grid-template-rows": GridTemplateRows(TrackSizing<'i>),
1375  "grid-auto-columns": GridAutoColumns(TrackSizeList),
1376  "grid-auto-rows": GridAutoRows(TrackSizeList),
1377  "grid-auto-flow": GridAutoFlow(GridAutoFlow),
1378  "grid-template-areas": GridTemplateAreas(GridTemplateAreas),
1379  "grid-template": GridTemplate(GridTemplate<'i>) shorthand: true,
1380  "grid": Grid(Grid<'i>) shorthand: true,
1381  "grid-row-start": GridRowStart(GridLine<'i>),
1382  "grid-row-end": GridRowEnd(GridLine<'i>),
1383  "grid-column-start": GridColumnStart(GridLine<'i>),
1384  "grid-column-end": GridColumnEnd(GridLine<'i>),
1385  "grid-row": GridRow(GridRow<'i>) shorthand: true,
1386  "grid-column": GridColumn(GridColumn<'i>) shorthand: true,
1387  "grid-area": GridArea(GridArea<'i>) shorthand: true,
1388
1389  "margin-top": MarginTop(LengthPercentageOrAuto) [logical_group: Margin, category: Physical],
1390  "margin-bottom": MarginBottom(LengthPercentageOrAuto) [logical_group: Margin, category: Physical],
1391  "margin-left": MarginLeft(LengthPercentageOrAuto) [logical_group: Margin, category: Physical],
1392  "margin-right": MarginRight(LengthPercentageOrAuto) [logical_group: Margin, category: Physical],
1393  "margin-block-start": MarginBlockStart(LengthPercentageOrAuto) [logical_group: Margin, category: Logical],
1394  "margin-block-end": MarginBlockEnd(LengthPercentageOrAuto) [logical_group: Margin, category: Logical],
1395  "margin-inline-start": MarginInlineStart(LengthPercentageOrAuto) [logical_group: Margin, category: Logical],
1396  "margin-inline-end": MarginInlineEnd(LengthPercentageOrAuto) [logical_group: Margin, category: Logical],
1397  "margin-block": MarginBlock(MarginBlock) shorthand: true,
1398  "margin-inline": MarginInline(MarginInline) shorthand: true,
1399  "margin": Margin(Margin) shorthand: true,
1400
1401  "padding-top": PaddingTop(LengthPercentageOrAuto) [logical_group: Padding, category: Physical],
1402  "padding-bottom": PaddingBottom(LengthPercentageOrAuto) [logical_group: Padding, category: Physical],
1403  "padding-left": PaddingLeft(LengthPercentageOrAuto) [logical_group: Padding, category: Physical],
1404  "padding-right": PaddingRight(LengthPercentageOrAuto) [logical_group: Padding, category: Physical],
1405  "padding-block-start": PaddingBlockStart(LengthPercentageOrAuto) [logical_group: Padding, category: Logical],
1406  "padding-block-end": PaddingBlockEnd(LengthPercentageOrAuto) [logical_group: Padding, category: Logical],
1407  "padding-inline-start": PaddingInlineStart(LengthPercentageOrAuto) [logical_group: Padding, category: Logical],
1408  "padding-inline-end": PaddingInlineEnd(LengthPercentageOrAuto) [logical_group: Padding, category: Logical],
1409  "padding-block": PaddingBlock(PaddingBlock) shorthand: true,
1410  "padding-inline": PaddingInline(PaddingInline) shorthand: true,
1411  "padding": Padding(Padding) shorthand: true,
1412
1413  "scroll-margin-top": ScrollMarginTop(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Physical],
1414  "scroll-margin-bottom": ScrollMarginBottom(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Physical],
1415  "scroll-margin-left": ScrollMarginLeft(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Physical],
1416  "scroll-margin-right": ScrollMarginRight(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Physical],
1417  "scroll-margin-block-start": ScrollMarginBlockStart(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Logical],
1418  "scroll-margin-block-end": ScrollMarginBlockEnd(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Logical],
1419  "scroll-margin-inline-start": ScrollMarginInlineStart(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Logical],
1420  "scroll-margin-inline-end": ScrollMarginInlineEnd(LengthPercentageOrAuto) [logical_group: ScrollMargin, category: Logical],
1421  "scroll-margin-block": ScrollMarginBlock(ScrollMarginBlock) shorthand: true,
1422  "scroll-margin-inline": ScrollMarginInline(ScrollMarginInline) shorthand: true,
1423  "scroll-margin": ScrollMargin(ScrollMargin) shorthand: true,
1424
1425  "scroll-padding-top": ScrollPaddingTop(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Physical],
1426  "scroll-padding-bottom": ScrollPaddingBottom(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Physical],
1427  "scroll-padding-left": ScrollPaddingLeft(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Physical],
1428  "scroll-padding-right": ScrollPaddingRight(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Physical],
1429  "scroll-padding-block-start": ScrollPaddingBlockStart(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Logical],
1430  "scroll-padding-block-end": ScrollPaddingBlockEnd(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Logical],
1431  "scroll-padding-inline-start": ScrollPaddingInlineStart(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Logical],
1432  "scroll-padding-inline-end": ScrollPaddingInlineEnd(LengthPercentageOrAuto) [logical_group: ScrollPadding, category: Logical],
1433  "scroll-padding-block": ScrollPaddingBlock(ScrollPaddingBlock) shorthand: true,
1434  "scroll-padding-inline": ScrollPaddingInline(ScrollPaddingInline) shorthand: true,
1435  "scroll-padding": ScrollPadding(ScrollPadding) shorthand: true,
1436
1437  "font-weight": FontWeight(FontWeight),
1438  "font-size": FontSize(FontSize),
1439  "font-stretch": FontStretch(FontStretch),
1440  "font-family": FontFamily(Vec<FontFamily<'i>>),
1441  "font-style": FontStyle(FontStyle),
1442  "font-variant-caps": FontVariantCaps(FontVariantCaps),
1443  "line-height": LineHeight(LineHeight),
1444  "font": Font(Font<'i>) shorthand: true,
1445  "vertical-align": VerticalAlign(VerticalAlign),
1446  "font-palette": FontPalette(DashedIdentReference<'i>),
1447
1448  "transition-property": TransitionProperty(SmallVec<[PropertyId<'i>; 1]>, VendorPrefix) / WebKit / Moz / Ms,
1449  "transition-duration": TransitionDuration(SmallVec<[Time; 1]>, VendorPrefix) / WebKit / Moz / Ms,
1450  "transition-delay": TransitionDelay(SmallVec<[Time; 1]>, VendorPrefix) / WebKit / Moz / Ms,
1451  "transition-timing-function": TransitionTimingFunction(SmallVec<[EasingFunction; 1]>, VendorPrefix) / WebKit / Moz / Ms,
1452  "transition": Transition(SmallVec<[Transition<'i>; 1]>, VendorPrefix) / WebKit / Moz / Ms shorthand: true,
1453
1454  "animation-name": AnimationName(AnimationNameList<'i>, VendorPrefix) / WebKit / Moz / O,
1455  "animation-duration": AnimationDuration(SmallVec<[Time; 1]>, VendorPrefix) / WebKit / Moz / O,
1456  "animation-timing-function": AnimationTimingFunction(SmallVec<[EasingFunction; 1]>, VendorPrefix) / WebKit / Moz / O,
1457  "animation-iteration-count": AnimationIterationCount(SmallVec<[AnimationIterationCount; 1]>, VendorPrefix) / WebKit / Moz / O,
1458  "animation-direction": AnimationDirection(SmallVec<[AnimationDirection; 1]>, VendorPrefix) / WebKit / Moz / O,
1459  "animation-play-state": AnimationPlayState(SmallVec<[AnimationPlayState; 1]>, VendorPrefix) / WebKit / Moz / O,
1460  "animation-delay": AnimationDelay(SmallVec<[Time; 1]>, VendorPrefix) / WebKit / Moz / O,
1461  "animation-fill-mode": AnimationFillMode(SmallVec<[AnimationFillMode; 1]>, VendorPrefix) / WebKit / Moz / O,
1462  "animation-composition": AnimationComposition(SmallVec<[AnimationComposition; 1]>),
1463  "animation-timeline": AnimationTimeline(SmallVec<[AnimationTimeline<'i>; 1]>),
1464  "animation-range-start": AnimationRangeStart(SmallVec<[AnimationRangeStart; 1]>),
1465  "animation-range-end": AnimationRangeEnd(SmallVec<[AnimationRangeEnd; 1]>),
1466  "animation-range": AnimationRange(SmallVec<[AnimationRange; 1]>),
1467  "animation": Animation(AnimationList<'i>, VendorPrefix) / WebKit / Moz / O shorthand: true,
1468
1469  // https://drafts.csswg.org/css-transforms-2/
1470  "transform": Transform(TransformList, VendorPrefix) / WebKit / Moz / Ms / O,
1471  "transform-origin": TransformOrigin(Position, VendorPrefix) / WebKit / Moz / Ms / O, // TODO: handle z offset syntax
1472  "transform-style": TransformStyle(TransformStyle, VendorPrefix) / WebKit / Moz,
1473  "transform-box": TransformBox(TransformBox),
1474  "backface-visibility": BackfaceVisibility(BackfaceVisibility, VendorPrefix) / WebKit / Moz,
1475  "perspective": Perspective(Perspective, VendorPrefix) / WebKit / Moz,
1476  "perspective-origin": PerspectiveOrigin(Position, VendorPrefix) / WebKit / Moz,
1477  "translate": Translate(Translate),
1478  "rotate": Rotate(Rotate),
1479  "scale": Scale(Scale),
1480
1481  // https://www.w3.org/TR/2021/CRD-css-text-3-20210422
1482  "text-transform": TextTransform(TextTransform),
1483  "white-space": WhiteSpace(WhiteSpace),
1484  "tab-size": TabSize(LengthOrNumber, VendorPrefix) / Moz / O,
1485  "word-break": WordBreak(WordBreak),
1486  "line-break": LineBreak(LineBreak),
1487  "hyphens": Hyphens(Hyphens, VendorPrefix) / WebKit / Moz / Ms,
1488  "overflow-wrap": OverflowWrap(OverflowWrap),
1489  "word-wrap": WordWrap(OverflowWrap),
1490  "text-align": TextAlign(TextAlign),
1491  "text-align-last": TextAlignLast(TextAlignLast, VendorPrefix) / Moz,
1492  "text-justify": TextJustify(TextJustify),
1493  "word-spacing": WordSpacing(Spacing),
1494  "letter-spacing": LetterSpacing(Spacing),
1495  "text-indent": TextIndent(TextIndent),
1496
1497  // https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506
1498  "text-decoration-line": TextDecorationLine(TextDecorationLine, VendorPrefix) / WebKit / Moz,
1499  "text-decoration-style": TextDecorationStyle(TextDecorationStyle, VendorPrefix) / WebKit / Moz,
1500  "text-decoration-color": TextDecorationColor(CssColor, VendorPrefix) / WebKit / Moz,
1501  "text-decoration-thickness": TextDecorationThickness(TextDecorationThickness),
1502  "text-decoration": TextDecoration(TextDecoration, VendorPrefix) / WebKit / Moz shorthand: true,
1503  "text-decoration-skip-ink": TextDecorationSkipInk(TextDecorationSkipInk, VendorPrefix) / WebKit,
1504  "text-emphasis-style": TextEmphasisStyle(TextEmphasisStyle<'i>, VendorPrefix) / WebKit,
1505  "text-emphasis-color": TextEmphasisColor(CssColor, VendorPrefix) / WebKit,
1506  "text-emphasis": TextEmphasis(TextEmphasis<'i>, VendorPrefix) / WebKit shorthand: true,
1507  "text-emphasis-position": TextEmphasisPosition(TextEmphasisPosition, VendorPrefix) / WebKit,
1508  "text-shadow": TextShadow(SmallVec<[TextShadow; 1]>),
1509
1510  // https://w3c.github.io/csswg-drafts/css-size-adjust/
1511  "text-size-adjust": TextSizeAdjust(TextSizeAdjust, VendorPrefix) / WebKit / Moz / Ms,
1512
1513  // https://drafts.csswg.org/css-writing-modes-3/
1514  "direction": Direction(Direction),
1515  "unicode-bidi": UnicodeBidi(UnicodeBidi),
1516
1517  // https://www.w3.org/TR/css-break-3/
1518  "box-decoration-break": BoxDecorationBreak(BoxDecorationBreak, VendorPrefix) / WebKit,
1519
1520  // https://www.w3.org/TR/2021/WD-css-ui-4-20210316
1521  "resize": Resize(Resize),
1522  "cursor": Cursor(Cursor<'i>),
1523  "caret-color": CaretColor(ColorOrAuto),
1524  "caret-shape": CaretShape(CaretShape),
1525  "caret": Caret(Caret) shorthand: true,
1526  "user-select": UserSelect(UserSelect, VendorPrefix) / WebKit / Moz / Ms,
1527  "accent-color": AccentColor(ColorOrAuto),
1528  "appearance": Appearance(Appearance<'i>, VendorPrefix) / WebKit / Moz / Ms,
1529
1530  // https://www.w3.org/TR/2020/WD-css-lists-3-20201117
1531  "list-style-type": ListStyleType(ListStyleType<'i>),
1532  "list-style-image": ListStyleImage(Image<'i>),
1533  "list-style-position": ListStylePosition(ListStylePosition),
1534  "list-style": ListStyle(ListStyle<'i>) shorthand: true,
1535  "marker-side": MarkerSide(MarkerSide),
1536
1537  // CSS modules
1538  "composes": Composes(Composes<'i>) if css_modules,
1539
1540  // https://www.w3.org/TR/SVG2/painting.html
1541  "fill": Fill(SVGPaint<'i>),
1542  "fill-rule": FillRule(FillRule),
1543  "fill-opacity": FillOpacity(AlphaValue),
1544  "stroke": Stroke(SVGPaint<'i>),
1545  "stroke-opacity": StrokeOpacity(AlphaValue),
1546  "stroke-width": StrokeWidth(LengthPercentage),
1547  "stroke-linecap": StrokeLinecap(StrokeLinecap),
1548  "stroke-linejoin": StrokeLinejoin(StrokeLinejoin),
1549  "stroke-miterlimit": StrokeMiterlimit(CSSNumber),
1550  "stroke-dasharray": StrokeDasharray(StrokeDasharray),
1551  "stroke-dashoffset": StrokeDashoffset(LengthPercentage),
1552  "marker-start": MarkerStart(Marker<'i>),
1553  "marker-mid": MarkerMid(Marker<'i>),
1554  "marker-end": MarkerEnd(Marker<'i>),
1555  "marker": Marker(Marker<'i>),
1556  "color-interpolation": ColorInterpolation(ColorInterpolation),
1557  "color-interpolation-filters": ColorInterpolationFilters(ColorInterpolation),
1558  "color-rendering": ColorRendering(ColorRendering),
1559  "shape-rendering": ShapeRendering(ShapeRendering),
1560  "text-rendering": TextRendering(TextRendering),
1561  "image-rendering": ImageRendering(ImageRendering),
1562
1563  // https://www.w3.org/TR/css-masking-1/
1564  "clip-path": ClipPath(ClipPath<'i>, VendorPrefix) / WebKit,
1565  "clip-rule": ClipRule(FillRule),
1566  "mask-image": MaskImage(SmallVec<[Image<'i>; 1]>, VendorPrefix) / WebKit,
1567  "mask-mode": MaskMode(SmallVec<[MaskMode; 1]>),
1568  "mask-repeat": MaskRepeat(SmallVec<[BackgroundRepeat; 1]>, VendorPrefix) / WebKit,
1569  "mask-position-x": MaskPositionX(SmallVec<[HorizontalPosition; 1]>),
1570  "mask-position-y": MaskPositionY(SmallVec<[VerticalPosition; 1]>),
1571  "mask-position": MaskPosition(SmallVec<[Position; 1]>, VendorPrefix) / WebKit,
1572  "mask-clip": MaskClip(SmallVec<[MaskClip; 1]>, VendorPrefix) / WebKit,
1573  "mask-origin": MaskOrigin(SmallVec<[GeometryBox; 1]>, VendorPrefix) / WebKit,
1574  "mask-size": MaskSize(SmallVec<[BackgroundSize; 1]>, VendorPrefix) / WebKit,
1575  "mask-composite": MaskComposite(SmallVec<[MaskComposite; 1]>),
1576  "mask-type": MaskType(MaskType),
1577  "mask": Mask(SmallVec<[Mask<'i>; 1]>, VendorPrefix) / WebKit shorthand: true,
1578  "mask-border-source": MaskBorderSource(Image<'i>),
1579  "mask-border-mode": MaskBorderMode(MaskBorderMode),
1580  "mask-border-slice": MaskBorderSlice(BorderImageSlice),
1581  "mask-border-width": MaskBorderWidth(Rect<BorderImageSideWidth>),
1582  "mask-border-outset": MaskBorderOutset(Rect<LengthOrNumber>),
1583  "mask-border-repeat": MaskBorderRepeat(BorderImageRepeat),
1584  "mask-border": MaskBorder(MaskBorder<'i>) shorthand: true,
1585
1586  // WebKit additions
1587  "-webkit-mask-composite": WebKitMaskComposite(SmallVec<[WebKitMaskComposite; 1]>),
1588  "mask-source-type": WebKitMaskSourceType(SmallVec<[WebKitMaskSourceType; 1]>, VendorPrefix) / WebKit unprefixed: false,
1589  "mask-box-image": WebKitMaskBoxImage(BorderImage<'i>, VendorPrefix) / WebKit unprefixed: false,
1590  "mask-box-image-source": WebKitMaskBoxImageSource(Image<'i>, VendorPrefix) / WebKit unprefixed: false,
1591  "mask-box-image-slice": WebKitMaskBoxImageSlice(BorderImageSlice, VendorPrefix) / WebKit unprefixed: false,
1592  "mask-box-image-width": WebKitMaskBoxImageWidth(Rect<BorderImageSideWidth>, VendorPrefix) / WebKit unprefixed: false,
1593  "mask-box-image-outset": WebKitMaskBoxImageOutset(Rect<LengthOrNumber>, VendorPrefix) / WebKit unprefixed: false,
1594  "mask-box-image-repeat": WebKitMaskBoxImageRepeat(BorderImageRepeat, VendorPrefix) / WebKit unprefixed: false,
1595
1596  // https://drafts.fxtf.org/filter-effects-1/
1597  "filter": Filter(FilterList<'i>, VendorPrefix) / WebKit,
1598  "backdrop-filter": BackdropFilter(FilterList<'i>, VendorPrefix) / WebKit,
1599
1600  // https://drafts.csswg.org/css2/
1601  "z-index": ZIndex(position::ZIndex),
1602
1603  // https://drafts.csswg.org/css-contain-3/
1604  "container-type": ContainerType(ContainerType),
1605  "container-name": ContainerName(ContainerNameList<'i>),
1606  "container": Container(Container<'i>) shorthand: true,
1607
1608  // https://w3c.github.io/csswg-drafts/css-view-transitions-1/
1609  "view-transition-name": ViewTransitionName(ViewTransitionName<'i>),
1610  // https://drafts.csswg.org/css-view-transitions-2/
1611  "view-transition-class": ViewTransitionClass(NoneOrCustomIdentList<'i>),
1612  "view-transition-group": ViewTransitionGroup(ViewTransitionGroup<'i>),
1613
1614  // https://drafts.csswg.org/css-color-adjust/
1615  "color-scheme": ColorScheme(ColorScheme),
1616}
1617
1618impl<'i, T: smallvec::Array<Item = V>, V: Parse<'i>> Parse<'i> for SmallVec<T> {
1619  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1620    // Copied from cssparser `parse_comma_separated` but using SmallVec instead of Vec.
1621    let mut values = smallvec![];
1622    loop {
1623      input.skip_whitespace(); // Unnecessary for correctness, but may help try() in parse_one rewind less.
1624      match input.parse_until_before(Delimiter::Comma, &mut V::parse) {
1625        Ok(v) => values.push(v),
1626        Err(err) => return Err(err),
1627      }
1628      match input.next() {
1629        Err(_) => return Ok(values),
1630        Ok(&cssparser::Token::Comma) => continue,
1631        Ok(_) => unreachable!(),
1632      }
1633    }
1634  }
1635}
1636
1637impl<T: smallvec::Array<Item = V>, V: ToCss> ToCss for SmallVec<T> {
1638  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1639  where
1640    W: std::fmt::Write,
1641  {
1642    let len = self.len();
1643    for (idx, val) in self.iter().enumerate() {
1644      val.to_css(dest)?;
1645      if idx < len - 1 {
1646        dest.delim(',', false)?;
1647      }
1648    }
1649    Ok(())
1650  }
1651}
1652
1653impl<'i, T: Parse<'i>> Parse<'i> for Vec<T> {
1654  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1655    input.parse_comma_separated(|input| T::parse(input))
1656  }
1657}
1658
1659impl<T: ToCss> ToCss for Vec<T> {
1660  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1661  where
1662    W: std::fmt::Write,
1663  {
1664    let len = self.len();
1665    for (idx, val) in self.iter().enumerate() {
1666      val.to_css(dest)?;
1667      if idx < len - 1 {
1668        dest.delim(',', false)?;
1669      }
1670    }
1671    Ok(())
1672  }
1673}
1674
1675enum_property! {
1676  /// A [CSS-wide keyword](https://drafts.csswg.org/css-cascade-5/#defaulting-keywords).
1677  pub enum CSSWideKeyword {
1678    /// The property's initial value.
1679    "initial": Initial,
1680    /// The property's computed value on the parent element.
1681    "inherit": Inherit,
1682    /// Either inherit or initial depending on whether the property is inherited.
1683    "unset": Unset,
1684    /// Rolls back the cascade to the cascaded value of the earlier origin.
1685    "revert": Revert,
1686    /// Rolls back the cascade to the value of the previous cascade layer.
1687    "revert-layer": RevertLayer,
1688  }
1689}