# `ElementorexCms.CSS`
[🔗](https://github.com/rocket4ce/elementorex_cms/blob/v0.1.0/lib/elementorex_cms/css.ex#L1)

Validation of the CSS that documents carry.

Styles are data: a map of property → value per device. Before a declaration is
written to a stylesheet its property must be in an allow-list and its value
must match the grammar of that property (lengths, colors, keywords...), so a
stored document can never break out of its rule (`}`), load resources
(`@import`) or run script (`expression()`).

Custom CSS written by hand (the *Custom CSS* field of an element or of the
document) is a different case: it is free text, so `scope/2` neutralizes the
dangerous parts and nests it inside the element's selector, which keeps its
rules from leaking to the rest of the page.

# `color?`

```elixir
@spec color?(term()) :: boolean()
```

Whether `value` is a color the styles accept.

# `declarations`

```elixir
@spec declarations(map()) :: String.t()
```

Writes a declaration block from a map of property → value, dropping invalid
declarations. Keys are written in a stable order.

    iex> ElementorexCms.CSS.declarations(%{"color" => "#fff", "x" => "1", "padding-top" => "8px"})
    "color:#fff;padding-top:8px"

# `length?`

```elixir
@spec length?(term()) :: boolean()
```

Whether `value` is a CSS length (`12px`, `1.5rem`, `50%`, `0`...).

# `properties`

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

Properties a style map may set.

# `sanitize_inline`

```elixir
@spec sanitize_inline(String.t()) :: String.t()
```

Parses an inline `style` attribute (`"color: red; padding: 4px"`) keeping only
valid declarations. Used by the HTML sanitizer.

# `scope`

```elixir
@spec scope(String.t() | nil, String.t()) :: String.t()
```

Prepares hand-written CSS and nests it inside `selector` using CSS nesting, so
every rule applies only inside the element (or document) it belongs to.

The keyword `selector` refers to the element itself, like in Elementor:

    selector { box-shadow: none }
    selector .btn:hover { transform: translateY(-2px) }

Declarations written outside any rule apply to the element as well. Before
nesting, the text loses `<` (so it can never close the `<style>` element),
`@import`/`@charset`/`@namespace` rules, `expression()`, `behavior`,
`-moz-binding`, `javascript:` and every `url()` that is not a safe http(s) or
relative URL. Returns `""` for empty input.

# `shadows`

```elixir
@spec shadows() :: %{required(String.t()) =&gt; String.t()}
```

The box-shadow presets: `%{name => css}`.

# `value`

```elixir
@spec value(String.t(), term()) :: String.t() | nil
```

Validates one declaration. Returns the value to write (sometimes normalized,
like a shadow preset expanded to its CSS) or `nil` when it must be dropped.

    iex> ElementorexCms.CSS.value("padding-top", "24px")
    "24px"
    iex> ElementorexCms.CSS.value("color", "var(--color-primary)")
    "var(--color-primary)"
    iex> ElementorexCms.CSS.value("color", "red;}body{display:none")
    nil
    iex> ElementorexCms.CSS.value("behavior", "url(x.htc)")
    nil

---

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