///|
fn stateful_numeric_value(
theme : Map[String, String],
key : String,
theme_namespace : String,
unit : 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_spacing_number(key) {
if unit == "" {
Some(key)
} else {
Some("\{key}\{unit}")
}
} else {
None
}
}
}
}
///|
fn transform_origin_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
guard prefixed_value(name, "origin") is Some(key) else { return None }
let keywords : Array[(String, String)] = [
("center", "center"),
("top", "top"),
("top-right", "100% 0"),
("right", "100%"),
("bottom-right", "100% 100%"),
("bottom", "bottom"),
("bottom-left", "0 100%"),
("left", "0"),
("top-left", "0 0"),
]
let value = match arbitrary_value(key) {
Some(value) => Some(value)
None =>
match theme_css_value(theme, "--transform-origin-\{key}") {
// A theme key wins over the built-in keyword, as it does everywhere.
Some(value) => Some(value)
None => {
let mut keyword = None
for entry in keywords {
let (candidate, value) = entry
if key == candidate {
keyword = Some(value)
break
}
}
keyword
}
}
}
value.map(fn(value) { [decl("transform-origin", value)] })
}
///|
fn translate_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
if name == "translate-3d" {
return Some([
decl(
"translate", "var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z)",
),
])
}
let (root, properties) = if name.has_prefix("translate-x-") {
("translate-x", ["--tw-translate-x"])
} else if name.has_prefix("translate-y-") {
("translate-y", ["--tw-translate-y"])
} else if name.has_prefix("translate-z-") {
("translate-z", ["--tw-translate-z"])
} else if name.has_prefix("translate-") {
("translate", ["--tw-translate-x", "--tw-translate-y"])
} else {
return None
}
guard prefixed_value(name, root) is Some(key) else { return None }
// `translate-full` and its two axes are upstream statics; the z axis has none.
let value = if key == "full" && root != "translate-z" {
Some("100%")
} else {
match theme_css_value(theme, "--translate-\{key}") {
Some(value) => Some(value)
None => resolved_value(theme, key, "--translate", true)
}
}
guard value is Some(value) else { return None }
let declarations = properties.map(fn(property) { decl(property, value) })
if root == "translate-z" {
declarations.push(
decl(
"translate", "var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z)",
),
)
} else {
declarations.push(
decl("translate", "var(--tw-translate-x) var(--tw-translate-y)"),
)
}
Some(declarations)
}
///|
fn rotate_skew_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
// `skew-*` drives both axes; every other member drives one, and bare
// `rotate-*` sets the `rotate` property instead of a transform function.
let (root, functions) : (String, Array[(String, String)]) = if name.has_prefix(
"rotate-x-",
) {
("rotate-x", [("--tw-rotate-x", "rotateX")])
} else if name.has_prefix("rotate-y-") {
("rotate-y", [("--tw-rotate-y", "rotateY")])
} else if name.has_prefix("rotate-z-") {
("rotate-z", [("--tw-rotate-z", "rotateZ")])
} else if name.has_prefix("skew-x-") {
("skew-x", [("--tw-skew-x", "skewX")])
} else if name.has_prefix("skew-y-") {
("skew-y", [("--tw-skew-y", "skewY")])
} else if name.has_prefix("skew-") {
("skew", [("--tw-skew-x", "skewX"), ("--tw-skew-y", "skewY")])
} else if name.has_prefix("rotate-") {
("rotate", [])
} else {
return None
}
guard prefixed_value(name, root) is Some(key) else { return None }
let theme_namespace = if root.has_prefix("skew") {
"--skew"
} else {
"--rotate"
}
guard stateful_numeric_value(theme, key, theme_namespace, "deg")
is Some(value) else {
return None
}
if functions.is_empty() {
return Some([decl("rotate", value)])
}
let declarations = functions.map(fn(entry) {
let (property, function_name) = entry
decl(property, "\{function_name}(\{value})")
})
declarations.push(
decl(
"transform", "var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)",
),
)
Some(declarations)
}
///|
fn scale_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
let (root, properties) = if name.has_prefix("scale-x-") {
("scale-x", ["--tw-scale-x"])
} else if name.has_prefix("scale-y-") {
("scale-y", ["--tw-scale-y"])
} else if name.has_prefix("scale-z-") {
("scale-z", ["--tw-scale-z"])
} else if name.has_prefix("scale-") {
("scale", ["--tw-scale-x", "--tw-scale-y", "--tw-scale-z"])
} else {
return None
}
guard prefixed_value(name, root) is Some(key) else { return None }
// `scale-[2]` sets the property itself rather than the per-axis pair.
if root == "scale" && arbitrary_value(key) is Some(value) {
return Some([decl("scale", value)])
}
let value = match arbitrary_value(key) {
Some(value) => Some(value)
None =>
match theme_css_value(theme, "--scale-\{key}") {
Some(value) => Some(value)
// Upstream takes a positive integer here, so `scale-1.5` is no candidate.
None => if is_nonnegative_integer(key) { Some("\{key}%") } else { None }
}
}
guard value is Some(value) else { return None }
let declarations = properties.map(fn(property) { decl(property, value) })
// Touching the z axis puts it in the shorthand too.
declarations.push(
decl(
"scale",
if root == "scale-z" {
"var(--tw-scale-x) var(--tw-scale-y) var(--tw-scale-z)"
} else {
"var(--tw-scale-x) var(--tw-scale-y)"
},
),
)
Some(declarations)
}
///|
fn perspective_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
if name.has_prefix("perspective-origin-") {
let key = name[19:].to_owned()
// A theme key resolves under both `--perspective-origin-*` and, read as
// `perspective-origin-`, under `--perspective-*`, so upstream compiles
// the candidate against both roots. An arbitrary value only matches the
// `perspective-origin` root.
match arbitrary_value(key) {
Some(value) => return Some([decl("perspective-origin", value)])
None => ()
}
return theme_css_value(theme, "--perspective-origin-\{key}").map(fn(value) {
[decl("perspective-origin", value), decl("perspective", value)]
})
}
guard prefixed_value(name, "perspective") is Some(key) else { return None }
let value = match arbitrary_value(key) {
Some(value) => Some(value)
None =>
match theme_css_value(theme, "--perspective-\{key}") {
Some(value) => Some(value)
None => if key == "none" { Some("none") } else { None }
}
}
value.map(fn(value) { [decl("perspective", value)] })
}
///|
fn stateful_transform_utility(
theme : Map[String, String],
name : String,
) -> Array[Declaration]? {
match name {
"transform-flat" => return Some([decl("transform-style", "flat")])
"transform-3d" => return Some([decl("transform-style", "preserve-3d")])
"transform-none" => return Some([decl("transform", "none")])
"transform" | "transform-cpu" =>
return Some([
decl(
"transform", "var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)",
),
])
"transform-gpu" =>
return Some([
decl(
"transform", "translateZ(0) var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)",
),
])
"transform-view" => return Some([decl("transform-box", "view-box")])
_ => ()
}
// `transform-[scaleZ(2)_rotateY(45deg)]` writes the property outright.
if prefixed_value(name, "transform") is Some(key) {
match arbitrary_value(key) {
Some(value) => return Some([decl("transform", value)])
None => ()
}
}
match transform_origin_utility(theme, name) {
Some(value) => return Some(value)
None => ()
}
match translate_utility(theme, name) {
Some(value) => return Some(value)
None => ()
}
match rotate_skew_utility(theme, name) {
Some(value) => return Some(value)
None => ()
}
match scale_utility(theme, name) {
Some(value) => return Some(value)
None => perspective_utility(theme, name)
}
}