ElementorexCms.CSS (ElementorexCms v0.1.0)

Copy Markdown View Source

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.

Summary

Functions

Whether value is a color the styles accept.

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

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

Properties a style map may set.

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

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 box-shadow presets: %{name => css}.

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.

Functions

color?(value)

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

Whether value is a color the styles accept.

declarations(styles)

@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?(value)

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

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

properties()

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

Properties a style map may set.

sanitize_inline(style)

@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(css, selector)

@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()

@spec shadows() :: %{required(String.t()) => String.t()}

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

value(property, value)

@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