///|
fn filter_composition(backdrop : Bool) -> String {
if backdrop {
"var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)"
} else {
"var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)"
}
}
///|
fn filter_value(
theme : Map[String, String],
key : String,
theme_namespace : String,
default_value : String?,
numeric_percent : Bool,
) -> String? {
if key == "" {
return default_value
}
match arbitrary_value(key) {
Some(value) => Some(value)
None =>
match theme_css_value(theme, "\{theme_namespace}-\{key}") {
Some(value) => Some(value)
None =>
if numeric_percent && is_spacing_number(key) {
Some("\{key}%")
} else {
None
}
}
}
}
///|
/// Hoisted to a module-level constant (built once, read-only) — see
/// `directional_spacing_entries`.
let filter_roots : Array[(String, String, String, String?, Bool)] = [
("blur", "--blur", "blur", Some("8px"), false),
("brightness", "--brightness", "brightness", None, true),
("contrast", "--contrast", "contrast", None, true),
("grayscale", "--grayscale", "grayscale", Some("100%"), true),
("hue-rotate", "--hue-rotate", "hue-rotate", None, false),
("invert", "--invert", "invert", Some("100%"), true),
("opacity", "--opacity", "opacity", None, true),
("saturate", "--saturate", "saturate", None, true),
("sepia", "--sepia", "sepia", Some("100%"), true),
]
///|
fn one_filter_utility(
theme : Map[String, String],
name : String,
backdrop : Bool,
) -> Array[Declaration]? {
// First-char / prefix fast-reject: skip the whole roots loop (and its per-entry
// string building) for candidates that cannot possibly be a filter utility.
// Safe because every `filter_roots` entry starts with one of these chars, and in
// backdrop mode the candidate is always `backdrop-`.
if backdrop {
guard name.has_prefix("backdrop-") else { return None }
} else {
guard name.length() > 0 else { return None }
let c = name[0]
guard c == 'b' ||
c == 'c' ||
c == 'g' ||
c == 'h' ||
c == 'i' ||
c == 'o' ||
c == 's' else {
return None
}
}
let prefix = if backdrop { "backdrop-" } else { "" }
for entry in filter_roots {
let (root, theme_namespace, function_name, default_value, percent) = entry
if root == "opacity" && !backdrop {
continue
}
let candidate_root = "\{prefix}\{root}"
let key = if name == candidate_root {
""
} else {
guard prefixed_value(name, candidate_root) is Some(key) else { continue }
key
}
guard filter_value(theme, key, theme_namespace, default_value, percent)
is Some(value) else {
continue
}
let property = if backdrop {
"--tw-backdrop-\{root}"
} else {
"--tw-\{root}"
}
let declarations : Array[Declaration] = [
decl(property, "\{function_name}(\{value})"),
]
if backdrop {
declarations.push(
decl("-webkit-backdrop-filter", filter_composition(true)),
)
declarations.push(decl("backdrop-filter", filter_composition(true)))
} else {
declarations.push(decl("filter", filter_composition(false)))
}
return Some(declarations)
}
None
}
///|
fn filter_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
match name {
"filter-none" => return Some([decl("filter", "none")])
"backdrop-filter-none" =>
return Some([
decl("-webkit-backdrop-filter", "none"),
decl("backdrop-filter", "none"),
])
_ => ()
}
if name.has_prefix("drop-shadow-") {
let key = name[12:].to_owned()
match
(theme.get("--color-\{key}"), theme_css_value(theme, "--color-\{key}")) {
(Some(literal), Some(value)) =>
return Some([
{
property: "--tw-drop-shadow-color",
value: "color-mix(in oklab, \{value} var(--tw-drop-shadow-alpha), transparent)",
fallback: Some(literal),
},
decl("--tw-drop-shadow", "var(--tw-drop-shadow-size)"),
])
_ => ()
}
let raw_value = match arbitrary_value(key) {
Some(value) => Some(value)
None => theme.get("--drop-shadow-\{key}")
}
match raw_value {
Some(value) => {
let colored = shadow_value_with_color(value, "--tw-drop-shadow-color")
return Some([
decl("--tw-drop-shadow-size", "drop-shadow(\{colored})"),
decl("--tw-drop-shadow", "drop-shadow(var(--drop-shadow-\{key}))"),
decl("filter", filter_composition(false)),
])
}
None => ()
}
}
match one_filter_utility(theme, name, true) {
Some(value) => Some(value)
None => one_filter_utility(theme, name, false)
}
}
///|
fn touch_snap_utility(name : String) -> Array[Declaration]? {
let touch = match name {
"touch-auto" => Some([decl("touch-action", "auto")])
"touch-none" => Some([decl("touch-action", "none")])
"touch-manipulation" => Some([decl("touch-action", "manipulation")])
"touch-pan-x" =>
Some([
decl("--tw-pan-x", "pan-x"),
decl(
"touch-action", "var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)",
),
])
"touch-pan-y" =>
Some([
decl("--tw-pan-y", "pan-y"),
decl(
"touch-action", "var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)",
),
])
"touch-pinch-zoom" =>
Some([
decl("--tw-pinch-zoom", "pinch-zoom"),
decl(
"touch-action", "var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)",
),
])
_ => None
}
match touch {
Some(value) => return Some(value)
None => ()
}
match name {
"snap-none" => Some([decl("scroll-snap-type", "none")])
"snap-x" =>
Some([decl("scroll-snap-type", "x var(--tw-scroll-snap-strictness)")])
"snap-y" =>
Some([decl("scroll-snap-type", "y var(--tw-scroll-snap-strictness)")])
"snap-both" =>
Some([decl("scroll-snap-type", "both var(--tw-scroll-snap-strictness)")])
"snap-mandatory" => Some([decl("--tw-scroll-snap-strictness", "mandatory")])
"snap-proximity" => Some([decl("--tw-scroll-snap-strictness", "proximity")])
_ => None
}
}
///|
fn transition_time_value(
theme : Map[String, String],
key : String,
theme_namespace : String,
) -> String? {
match arbitrary_value(key) {
Some(value) => Some(value)
None =>
match theme_css_value(theme, "\{theme_namespace}-\{key}") {
Some(value) => Some(value)
None =>
if is_nonnegative_integer(key) {
Some("\{key}ms")
} else {
None
}
}
}
}
///|
fn transition_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
let all_properties = "color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events"
let color_properties = "color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to"
let transition = match name {
"transition-none" => Some([decl("transition-property", "none")])
"transition-all" =>
Some([
decl("transition-property", "all"),
decl("transition-timing-function", "var(--tw-ease, ease)"),
decl("transition-duration", "var(--tw-duration, 0s)"),
])
"transition-colors" =>
Some([
decl("transition-property", color_properties),
decl("transition-timing-function", "var(--tw-ease, ease)"),
decl("transition-duration", "var(--tw-duration, 0s)"),
])
"transition" =>
Some([
decl("transition-property", all_properties),
decl("transition-timing-function", "var(--tw-ease, ease)"),
decl("transition-duration", "var(--tw-duration, 0s)"),
])
_ => None
}
match transition {
Some(value) => return Some(value)
None => ()
}
if name.has_prefix("transition-") {
let key = name[11:].to_owned()
match arbitrary_value(key) {
Some(value) =>
return Some([
decl("transition-property", value),
decl("transition-timing-function", "var(--tw-ease, ease)"),
decl("transition-duration", "var(--tw-duration, 0s)"),
])
None => ()
}
}
if name.has_prefix("duration-") {
let key = name[9:].to_owned()
return transition_time_value(theme, key, "--transition-duration").map(fn(
value,
) {
[decl("--tw-duration", value), decl("transition-duration", value)]
})
}
if name.has_prefix("delay-") {
let key = name[6:].to_owned()
return transition_time_value(theme, key, "--transition-delay").map(fn(
value,
) {
[decl("transition-delay", value)]
})
}
guard prefixed_value(name, "ease") is Some(key) else { return None }
if key == "initial" {
return Some([decl("--tw-ease", "initial")])
}
// A theme key wins over the static `linear` value.
let value = match arbitrary_value(key) {
Some(value) => Some(value)
None =>
match theme_css_value(theme, "--ease-\{key}") {
Some(value) => Some(value)
None => if key == "linear" { Some("linear") } else { None }
}
}
value.map(fn(value) {
[decl("--tw-ease", value), decl("transition-timing-function", value)]
})
}
///|
fn stateful_filter_transition_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
match filter_utility(theme, name) {
Some(value) => return Some(value)
None => ()
}
match touch_snap_utility(name) {
Some(value) => return Some(value)
None => transition_utility(theme, name)
}
}