You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

4.9 KiB

Style DSL in Compose Web

The API is experimental, and breaking changes can be expected

Introduction

In this tutorial we have a look at how to style the components using the Style DSL. It’s a typesafe DSL for style sheets, which you can use to express CSS rules in your Kotlin code, and even modify styles based on the state of your Compose application.

Inline Style

You can declare inline styles via the style block of a component

Div({
    style {
        display(DisplayStyle.Flex)
        padding(20.px)
        
        // custom property (or not supported out of a box)
        property("font-family", value("Arial, Helvetica, sans-serif"))
    }
}) { /* content goes here */ }

In HTML, it will look like this:

<div style="display: flex; padding: 20px; font-family: Arial, Helvetica, sans-serif;"></div>

Stylesheet

An alternative way is to define a Stylesheet that contains rules:

object AppStylesheet : StyleSheet() {
    val container by style { // container is a class
        display(DisplayStyle.Flex)
        padding(20.px)

        // custom property (or not supported out of a box)
        property("font-family", value("Arial, Helvetica, sans-serif"))
    }
}

// Stylesheet needs to be mounted:
renderComposable("root") {
    Style(AppStylesheet)
    
    Container {
        Text("Content")
    }
}

@Composable
fun Container(content: @Composable () -> Unit) {
    Div(
        attrs = { classes(AppStylesheet.container) }
    ) {
        content()
    }
}

In HTML, it will look like this:

<style></style>
<div class="AppStylesheet-container">Content</div>

Selectors examples

The Style DSL also provides a way to combine and unify selectors:

object AppStylesheet : StyleSheet() {
    
    init {
        // CSSSelector.Universal can be used instead of "*"
        "*" style { 
            fontSize(15.px)
            padding(0.px)
        }
        
        // raw selector
        "h1, h2, h3, h4, h5, h6" style {
            property("font-family", value("Arial, Helvetica, sans-serif"))
            
        }

        // combined selector
        type("A") + attr( // selects all tags <a> with href containing 'jetbrains'
            name = "href",
            value = "jetbrains",
            operator = CSSSelector.Attribute.Operator.Equals
        ) style {
            fontSize(25.px)
        }
    }
    
    // A convenient way to create a class selector
    // AppStylesheet.container can be used as a class in component attrs
    val container by style {
        color("red")
        
        // hover selector for a class
        self + hover() style { // self is a selector for `container`
            color("green")
        }
    }
}

Media query example

To specify media queries, you can use the media function, which takes the related query, and a block of styles:

object AppStylesheet : StyleSheet() {
    val container by style {
        padding(48.px)

        media(maxWidth(640.px)) {
            self style {
                padding(12.px)
            }
        }
    }
}

CSS Variables

The style DSL also provides support for CSS variables.

object MyVariables : CSSVariables {
    // declare a variable
    val contentBackgroundColor by variable<Color>() 
}

object MyStyleSheet: StyleSheet() {
    
    val container by style {
        //set variable's value for the `container` scope
        MyVariables.contentBackgroundColor(Color("blue"))
    }
    
    val content by style {
        // get the value
        backgroundColor(MyVariables.contentBackgroundColor.value())
    }

    val contentWithDefaultBgColor by style {
        // default value can be provided as well
        // default value is used when the value is not previously set
        backgroundColor(MyVariables.contentBackgroundColor.value(Color("#333")))
    }
}

Runnable example

import androidx.compose.runtime.Composable
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.*
import org.jetbrains.compose.web.renderComposable

object AppStylesheet : StyleSheet() {
    val container by style { // container is a class
        display(DisplayStyle.Flex)
        padding(20.px)

        // custom property (or not supported out of a box)
        property("font-family", value("Arial, Helvetica, sans-serif"))
    }
}

@Composable
fun Container(content: @Composable () -> Unit) {
    Div(
        attrs = { classes(AppStylesheet.container) }
    ) {
        content()
    }
}

fun main() {
    renderComposable(rootElementId = "root") {
        Div({
            style {
                display(DisplayStyle.Flex)
                padding(20.px)

                // custom property (or not supported out of a box)
                property("font-family", value("Arial, Helvetica, sans-serif"))
            }
        }) { /* content goes here */ }


        Style(AppStylesheet)

        Container {
            Text("Content")
        }
    }
}