Skip to content

Parse Style from (and serialize to?) CSS syntax #440

@SimonSapin

Description

@SimonSapin

This is a roadmap item (#345) that I’m interesting in working on. (Though I make no promise in terms of timeline or my future availability.) Below is an opinionated proposal of how this could be done.

User-visible changes

The taffy crate gains a new Cargo feature named css, disabled by default. Enabling it:

  • Adds a dependency from taffy to https://crates.io/crates/cssparser
  • Adds a dependency from taffy to taffy-css-derive, a new proc macro crate that lives in the same repository. Since many Rust types are involved, manually implementing everything without proc-macros would be quite repetitive and prone copy-paste errors. This crate is similar to Servo’s style_derive. It depends on syn and quote.
  • Adds these APIs to taffy::style:
    impl Style {
        pub fn parse_css(input: &str) -> (Self, Vec<CssParseError>) {..}
        pub fn serialize_css(&self) -> String {..} // maybe?
    }
    
    #[derive(Clone, Debug)]
    pub struct CssParseError {..}
    impl Display for CssParseError {..}
    impl Error for CssParseError {..}

Specifications

As an "input" of layout, taffy::style::Style is the internal representation of computed values for a given element. Style::DEFAULT is effectively initial values.

Style::parse_css returns computed values as if the input string was a list of declarations from an HTML style attribute (so with syntax like display: flex; margin: 10%) and as if there were no other applicable declarations and no parent element for inheritance. Parsing is infallible: a Style struct is always returned. Unspecified properties get their initial value. Parse errors (reported on the side) cause parts of the input to be ignored until the parser can recover. (For a declaration list, recovery is at the next semicolon.) Parse errors include invalid declaration syntax, unsupported property, invalid/unsupported value, etc.

Relative length units (font-relative like 1.5em or viewport-relative like 80vw) are not supported. (Per the above, using them cause the declaration to be ignored and to emit a parse error.)

Style::serialize_css is similar to serialize a CSS declaration block. It omits properties that have their initial values (so Style::DEFAULT.serialize_css() is the empty string). It round-trips without errors: Style::parse_css(x.serialize_css()) == (x, vec![]) for any x: Style

Testing

Use snapshot testing with https://insta.rs/ to check the derive(Debug) output for various CSS inputs

Open API and behavior questions

  • API naming. Feel free to propose any change. (from_css / to_css, parse / impl Display, …)

  • Should serialization to CSS syntax be included? It’s relatively easy to do while also doing parsing, but maybe not as useful.

  • How are absolute length units anchored? CSS has seven of them. For historical reasons they all have a fixed ratio to each other: 1in is 96px is 72pt is 6pc is 2.54cm is 25.4mm is 101.6Q. See reference pixel discussion.

    Taffy documents Dimension::Points as: “Points are abstract absolute units. Users of Taffy may define what they correspond to in their application (pixels, logical pixels, mm, etc) as they see fit.” The enum variant name suggests 100pt should parse as Dimension::Points(100_f32), but on the web the px unit is much more common and getting Dimension(75_f32) from 100px might be unexpected. I don’t really like this idea but maybe to force users to consider units, all units except pt could be artificially unsupported?

    (By the way, I didn’t find this explicitly documented but I assume Dimension::Points(100_f32) maps to 100_f32 in taffy::layout::Layout?)

    Or there could be a configurable "DPI" or "device pixel ratio". A taffy::node::Taffy method seems like the natural place for it (next to enable_rounding / disable_rounding), but would require either Taffy::parse_css() or Style::parse_css(&Taffy) instead of Style::parse_css().

Open licensing questions

cssparser is licensed under MPL-2.0. Is that OK for a dependency?

Similarly, would it be OK for taffy-css-derive to be licensed under MPL-2.0? This would allow copying some code from style_derive. Alternatively taffy-css-derive could be entirely new code, MIT-licensed like taffy, obviously at the cost of more time and effort.

Disclaimer: although it’s been years since I’ve looked at it closely, I’ve contributed to style_derive in the past.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    • Status

      Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions