# `Premailex.CSSParser`
[🔗](https://github.com/danschultzer/premailex/blob/v1.0.1/lib/premailex/css_parser.ex#L1)

CSS parser.

## Parser limitations

  * At-rules (`@media`, `@font-face`, `@import`, etc.) and comments are
    stripped.

  * Specificity for `:not(...)`, `:is(...)`, and `:where(...)` is approximated
    as a single pseudo-class. In Selectors Level 4, specificity is derived from
    the argument (or is `0` for `:where`).

# `declaration`

```elixir
@type declaration() :: %{
  property: String.t(),
  value: String.t(),
  important?: boolean()
}
```

A CSS rule declaration.

# `pseudo`

```elixir
@type pseudo() :: %{
  :name =&gt; String.t(),
  :expression =&gt; String.t() | nil,
  :kind =&gt; :pseudo_class | :pseudo_element,
  optional(:nth) =&gt; {integer(), integer()} | :invalid
}
```

A CSS pseudo-class or pseudo-element.

Pseudo-classes in the `an+b` family (`:nth-child`, `:nth-of-type`,
`:nth-last-child`, `:nth-last-of-type`) include an additional `:nth` field
holding the parsed `{a, b}` coefficients, or `:invalid` if the expression is
missing or cannot be parsed.

# `rule`

```elixir
@type rule() :: %{
  declarations: [declaration()],
  selector: String.t(),
  specificity: specificity()
}
```

A CSS rule with computed specificity.

# `selector_group`

```elixir
@type selector_group() :: [selector_group_step()]
```

A list of selector steps ordered from right to left.

# `selector_group_step`

```elixir
@type selector_group_step() :: %{
  tag: String.t() | nil,
  id: String.t() | nil,
  classes: [String.t()],
  attrs: [String.t() | {String.t(), String.t()}],
  pseudos: [pseudo()],
  combinator: :descendant | :child | :adjacent | :sibling | :column | nil
}
```

A single element step in a selector.

# `specificity`

```elixir
@type specificity() :: {0..1, non_neg_integer(), non_neg_integer(), non_neg_integer()}
```

CSS specificity, matching the Selectors Level 4 four-tuple model.

Structured as `{inline?, ids, classes, elements}`:

  * `inline?` - `1` if the style is inline.
  * `ids` - number of ID selectors.
  * `classes` - number of class, attribute, and pseudo-class selectors.
  * `elements` - number of tag and pseudo-element selectors.

# `cascade`

```elixir
@spec cascade([rule()]) :: [declaration()]
```

Combines CSS rules into a final list of declarations using the CSS Cascade
Level 4 algorithm.

Conflicts between declarations for the same property are resolved by
`!important` and then by specificity.

See https://www.w3.org/TR/css-cascade-4/#cascading for details.

## Examples

    iex> rules = Premailex.CSSParser.parse("p {background-color: #fff !important; color: #000;} p {background-color: #000;}")
    iex> Premailex.CSSParser.cascade(rules)
    [
      %{property: "background-color", value: "#fff !important", important?: true},
      %{property: "color", value: "#000", important?: false}
    ]

# `parse`

```elixir
@spec parse(String.t()) :: [rule()]
```

Parses a CSS string into a list of CSS rules.

Ignores at-rules (e.g. `@media`, `@font-face`, etc.) and comments, as they
are not relevant for inlining CSS.

## Examples

    iex> Premailex.CSSParser.parse("body { background-color: #fff !important; color: red; }")
    [
      %{
        declarations: [
          %{property: "background-color", value: "#fff !important", important?: true},
          %{property: "color", value: "red", important?: false}
        ],
        selector: "body",
        specificity: {0, 0, 0, 1}
      }
    ]

# `parse_declaration_block`

```elixir
@spec parse_declaration_block(String.t()) :: [declaration()]
```

Parses a CSS declaration block string into a list of declarations.

Each declaration is a map containing the property, value, and a `!important`
flag.

## Examples

    iex> Premailex.CSSParser.parse_declaration_block("background-color: #fff; color: red;")
    [
      %{property: "background-color", value: "#fff", important?: false},
      %{property: "color", value: "red", important?: false}
    ]

# `parse_selector_groups`

```elixir
@spec parse_selector_groups(String.t()) :: [selector_group()]
```

Parses a selector string into a list of selector groups.

Each selector group is a list of steps, where each step is a map containing
the tag, id, classes, attributes, pseudo-classes/elements, and combinator.
Steps are ordered right to left.

Selectors that fail to parse are dropped and a debug log is emitted.

## Examples

    iex> Premailex.CSSParser.parse_selector_groups("div.foo")
    [[%{tag: "div", id: nil, classes: ["foo"], attrs: [], pseudos: [], combinator: nil}]]

    iex> Premailex.CSSParser.parse_selector_groups("body > p")
    [
      [
        %{tag: "p", id: nil, classes: [], attrs: [], pseudos: [], combinator: nil},
        %{tag: "body", id: nil, classes: [], attrs: [], pseudos: [], combinator: :child}
      ]
    ]

# `split_selector_groups`

```elixir
@spec split_selector_groups(String.t()) :: [String.t()]
```

Splits a comma-separated selector string into individual selectors.

## Examples

    iex> Premailex.CSSParser.split_selector_groups("div, .foo")
    ["div", ".foo"]

    iex> Premailex.CSSParser.split_selector_groups("div > p, a[href]")
    ["div > p", "a[href]"]

# `to_string`

```elixir
@spec to_string([declaration()] | declaration()) :: String.t()
```

Converts a rule declaration or list of declarations into a string.

## Examples

    iex> declarations = Premailex.CSSParser.parse_declaration_block("background-color: #fff !important; color: red;")
    iex> Premailex.CSSParser.to_string(declarations)
    "background-color: #fff !important; color: red;"

    iex> declarations = Premailex.CSSParser.parse_declaration_block("background-color: #fff")
    iex> Premailex.CSSParser.to_string(declarations)
    "background-color: #fff;"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
