diff --git a/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt b/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt
index 2fb1ee162..108467f6d 100644
--- a/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt
+++ b/frontend/compose-html-ext/src/jsMain/kotlin/com/varabyte/kobweb/compose/css/Background.kt
@@ -207,10 +207,79 @@ fun StyleScope.backgroundSize(backgroundSize: BackgroundSize) {
backgroundSize(backgroundSize.toString())
}
+// See: https://developer.mozilla.org/en-US/docs/Web/CSS/background
+sealed class Background private constructor(private val value: String) : StylePropertyValue {
+ override fun toString(): String = value
+
+ private class Keyword(value: String) : Background(value)
+
+ class Repeatable internal constructor(
+ val image: BackgroundImage?,
+ val repeat: BackgroundRepeat?,
+ val size: BackgroundSize?,
+ val position: BackgroundPosition?,
+ val blend: BackgroundBlendMode?, // See StyleScope.background for where this is used
+ val origin: BackgroundOrigin?,
+ val clip: BackgroundClip?,
+ val attachment: BackgroundAttachment?,
+ ) : Background(
+ buildList {
+ image?.let { add(it.toString()) }
+ repeat?.let { add(it) }
+ position?.let { add(it.toString()) }
+ size?.let {
+ // Size must ALWAYS follow position with a slash
+ // See: https://developer.mozilla.org/en-US/docs/Web/CSS/background#syntax
+ if (position == null) add(BackgroundPosition.of(CSSPosition.TopLeft))
+ add("/")
+ add(it.toString())
+ }
+ origin?.let {
+ add(it)
+ // See: https://developer.mozilla.org/en-US/docs/Web/CSS/background#values
+ if (clip == null) add(BackgroundClip.BorderBox.toString())
+ }
+ clip?.let {
+ // See: https://developer.mozilla.org/en-US/docs/Web/CSS/background#values
+ if (origin == null) add(BackgroundOrigin.PaddingBox.toString())
+ add(it)
+ }
+ attachment?.let { add(it) }
+ }.joinToString(" ")
+ )
+
+ companion object {
+ // Keyword
+ val None: Background = Keyword("none")
+
+ // Global Keywords
+ val Inherit: Background = Keyword("inherit")
+ val Initial: Background = Keyword("initial")
+ val Revert: Background = Keyword("revert")
+ val Unset: Background = Keyword("unset")
+
+ fun of(
+ image: BackgroundImage? = null,
+ repeat: BackgroundRepeat? = null,
+ size: BackgroundSize? = null,
+ position: BackgroundPosition? = null,
+ blend: BackgroundBlendMode? = null,
+ origin: BackgroundOrigin? = null,
+ clip: BackgroundClip? = null,
+ attachment: BackgroundAttachment? = null,
+ ): Repeatable = Repeatable(image, repeat, size, position, blend, origin, clip, attachment)
+ }
+}
+
+
// See: https://developer.mozilla.org/en-US/docs/Web/CSS/background
// Note: Color is actually a separate property and intentionally not included here.
// Note: blend mode *is* specified here but needs to be handled externally, since
// (probably for legacy reasons?) the `background` property does not accept it.
+@Deprecated(
+ "Please use `Background.of` instead.",
+ ReplaceWith("Background.of(image, repeat, size, position, blend, origin, clip, attachment)")
+)
data class CSSBackground(
val image: BackgroundImage? = null,
val repeat: BackgroundRepeat? = null,
@@ -246,10 +315,40 @@ data class CSSBackground(
}.joinToString(" ")
}
+fun StyleScope.background(background: Background) {
+ property("background", background)
+}
+
+fun StyleScope.background(vararg backgrounds: Background.Repeatable) {
+ background(null, *backgrounds)
+}
+
fun StyleScope.background(vararg backgrounds: CSSBackground) {
background(null, *backgrounds)
}
+fun StyleScope.background(color: CSSColorValue?, vararg backgrounds: CSSBackground) {
+ // CSS order is backwards (IMO). We attempt to fix that in Kobweb.
+ @Suppress("NAME_SHADOWING") val backgrounds = backgrounds.reversed()
+ property("background", buildString {
+ append(backgrounds.joinToString(", "))
+ // backgrounds only allow you to specify a single color. If provided, it must be included with
+ // the final layer.
+ if (color != null) {
+ if (this.isNotEmpty()) append(' ')
+ append(color)
+ }
+ })
+ val defaultBlendMode = BackgroundBlendMode.Normal
+ val blendModes = backgrounds
+ .map { it.blend ?: defaultBlendMode }
+ // Use toString comparison because otherwise equality checks are against instance
+ .takeIf { blendModes -> blendModes.any { it.toString() != defaultBlendMode.toString() } }
+ if (blendModes != null) {
+ property("background-blend-mode", blendModes.joinToString())
+ }
+}
+
/**
* A Kotlin-idiomatic API to configure the `background` CSS property.
*
@@ -263,7 +362,9 @@ fun StyleScope.background(vararg backgrounds: CSSBackground) {
*
* See also: https://developer.mozilla.org/en-US/docs/Web/CSS/background
*/
-fun StyleScope.background(color: CSSColorValue?, vararg backgrounds: CSSBackground) {
+fun StyleScope.background(color: CSSColorValue?, vararg backgrounds: Background.Repeatable) {
+ if (backgrounds.isEmpty()) return
+
// CSS order is backwards (IMO). We attempt to fix that in Kobweb.
@Suppress("NAME_SHADOWING") val backgrounds = backgrounds.reversed()
property("background", buildString {
diff --git a/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt b/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt
index 51697a034..a7776f42d 100644
--- a/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt
+++ b/frontend/kobweb-compose/src/jsMain/kotlin/com/varabyte/kobweb/compose/ui/modifiers/BackgroundModifiers.kt
@@ -7,7 +7,11 @@ import com.varabyte.kobweb.compose.ui.Modifier
import com.varabyte.kobweb.compose.ui.styleModifier
import org.jetbrains.compose.web.css.*
-fun Modifier.background(vararg backgrounds: CSSBackground) = styleModifier {
+fun Modifier.background(background: Background) = styleModifier {
+ background(background)
+}
+
+fun Modifier.background(vararg backgrounds: Background.Repeatable) = styleModifier {
background(*backgrounds)
}
@@ -24,6 +28,14 @@ fun Modifier.background(vararg backgrounds: CSSBackground) = styleModifier {
*
* See also: https://developer.mozilla.org/en-US/docs/Web/CSS/background
*/
+fun Modifier.background(color: CSSColorValue?, vararg backgrounds: Background.Repeatable) = styleModifier {
+ background(color, *backgrounds)
+}
+
+fun Modifier.background(vararg backgrounds: CSSBackground) = styleModifier {
+ background(*backgrounds)
+}
+
fun Modifier.background(color: CSSColorValue?, vararg backgrounds: CSSBackground) = styleModifier {
background(color, *backgrounds)
}