CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-compose-multiplatform

Declarative framework for sharing UIs across multiple platforms with Kotlin, including Gradle plugin, component libraries, and web support

Pending
Overview
Eval results
Files

html-web.mddocs/

HTML/Web Libraries

Compose for Web provides a complete web development framework with type-safe DOM manipulation, CSS styling, SVG support, and testing utilities. It enables building modern web applications using Compose's declarative paradigm while maintaining direct access to web platform APIs.

Capabilities

DOM Elements

Type-safe HTML element creation with full attribute and event support.

/**
 * Structural HTML elements
 */
@Composable fun Div(
    attrs: AttrBuilderContext<HTMLDivElement>? = null,
    content: ContentBuilder<HTMLDivElement>? = null
)

@Composable fun Header(
    attrs: AttrBuilderContext<HTMLElement>? = null,
    content: ContentBuilder<HTMLElement>? = null
)

@Composable fun Main(
    attrs: AttrBuilderContext<HTMLElement>? = null,
    content: ContentBuilder<HTMLElement>? = null
)

@Composable fun Section(
    attrs: AttrBuilderContext<HTMLElement>? = null,
    content: ContentBuilder<HTMLElement>? = null
)

@Composable fun Article(
    attrs: AttrBuilderContext<HTMLElement>? = null,
    content: ContentBuilder<HTMLElement>? = null
)

@Composable fun Nav(
    attrs: AttrBuilderContext<HTMLElement>? = null,
    content: ContentBuilder<HTMLElement>? = null
)

@Composable fun Footer(
    attrs: AttrBuilderContext<HTMLElement>? = null,
    content: ContentBuilder<HTMLElement>? = null
)

/**
 * Text content elements
 */
@Composable fun H1(
    attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
    content: ContentBuilder<HTMLHeadingElement>? = null
)

@Composable fun H2(
    attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
    content: ContentBuilder<HTMLHeadingElement>? = null
)

@Composable fun H3(
    attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
    content: ContentBuilder<HTMLHeadingElement>? = null
)

@Composable fun P(
    attrs: AttrBuilderContext<HTMLParagraphElement>? = null,
    content: ContentBuilder<HTMLParagraphElement>? = null
)

@Composable fun Span(
    attrs: AttrBuilderContext<HTMLSpanElement>? = null,
    content: ContentBuilder<HTMLSpanElement>? = null
)

@Composable fun Text(value: String)

/**
 * Interactive elements
 */
@Composable fun Button(
    attrs: AttrBuilderContext<HTMLButtonElement>? = null,
    content: ContentBuilder<HTMLButtonElement>? = null
)

@Composable fun A(
    href: String? = null,
    attrs: AttrBuilderContext<HTMLAnchorElement>? = null,
    content: ContentBuilder<HTMLAnchorElement>? = null
)

@Composable fun Label(
    forId: String? = null,
    attrs: AttrBuilderContext<HTMLLabelElement>? = null,
    content: ContentBuilder<HTMLLabelElement>? = null
)

Usage Example:

@Composable
fun WebPage() {
    Div({ style { padding(20.px) } }) {
        Header {
            H1 { Text("My Web Application") }
            Nav {
                A(href = "/home") { Text("Home") }
                A(href = "/about") { Text("About") }
                A(href = "/contact") { Text("Contact") }
            }
        }
        
        Main {
            Section {
                H2 { Text("Welcome") }
                P { Text("This is a Compose for Web application.") }
                
                Button({
                    onClick { event ->
                        console.log("Button clicked!")
                    }
                }) {
                    Text("Click me")
                }
            }
        }
        
        Footer {
            P { Text("© 2024 My Company") }
        }
    }
}

Form Elements

Comprehensive form controls with controlled and uncontrolled input support.

/**
 * Form container
 */
@Composable fun Form(
    action: String? = null,
    attrs: AttrBuilderContext<HTMLFormElement>? = null,
    content: ContentBuilder<HTMLFormElement>? = null
)

/**
 * Generic input element
 */
@Composable fun <K : Any> Input(
    type: InputType<K>,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

/**
 * Controlled input elements
 */
@Composable fun TextInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun NumberInput(
    value: Number? = null,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun EmailInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun PasswordInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun CheckboxInput(
    checked: Boolean,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun RadioInput(
    checked: Boolean,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun DateInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun DateTimeLocalInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun TimeInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun FileInput(
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun RangeInput(
    value: Number,
    min: Number? = null,
    max: Number? = null,
    step: Number? = null,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun SearchInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun TelInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

@Composable fun UrlInput(
    value: String,
    attrs: AttrBuilderContext<HTMLInputElement>? = null
)

/**
 * Text area for multi-line input
 */
@Composable fun TextArea(
    value: String,
    attrs: AttrBuilderContext<HTMLTextAreaElement>? = null
)

/**
 * Select dropdown
 */
@Composable fun Select(
    attrs: AttrBuilderContext<HTMLSelectElement>? = null,
    multiple: Boolean = false,
    content: ContentBuilder<HTMLSelectElement>? = null
)

@Composable fun Option(
    value: String,
    attrs: AttrBuilderContext<HTMLOptionElement>? = null,
    content: ContentBuilder<HTMLOptionElement>? = null
)

@Composable fun OptGroup(
    label: String,
    attrs: AttrBuilderContext<HTMLOptGroupElement>? = null,
    content: ContentBuilder<HTMLOptGroupElement>? = null
)

Usage Example:

@Composable
fun ContactForm() {
    var name by remember { mutableStateOf("") }
    var email by remember { mutableStateOf("") }
    var message by remember { mutableStateOf("") }
    var newsletter by remember { mutableStateOf(false) }
    
    Form {
        Div({ style { marginBottom(16.px) } }) {
            Label(forId = "name") { Text("Name:") }
            TextInput(
                value = name,
                attrs = {
                    id("name")
                    onInput { event ->
                        name = event.value
                    }
                }
            )
        }
        
        Div({ style { marginBottom(16.px) } }) {
            Label(forId = "email") { Text("Email:") }
            EmailInput(
                value = email,
                attrs = {
                    id("email")
                    onInput { event ->
                        email = event.value
                    }
                }
            )
        }
        
        Div({ style { marginBottom(16.px) } }) {
            Label(forId = "message") { Text("Message:") }
            TextArea(
                value = message,
                attrs = {
                    id("message")
                    rows(5)
                    onInput { event ->
                        message = event.value
                    }
                }
            )
        }
        
        Div({ style { marginBottom(16.px) } }) {
            CheckboxInput(
                checked = newsletter,
                attrs = {
                    id("newsletter")
                    onInput { event ->
                        newsletter = event.value
                    }
                }
            )
            Label(forId = "newsletter") { Text("Subscribe to newsletter") }
        }
        
        Button({
            type("submit")
            onClick { event ->
                event.preventDefault()
                submitForm(name, email, message, newsletter)
            }
        }) {
            Text("Send Message")
        }
    }
}

CSS Styling

Type-safe CSS styling with units, properties, and responsive design support.

/**
 * Style scope for CSS properties
 */
interface StyleScope {
    /** Add arbitrary CSS property */
    fun property(propertyName: String, value: StylePropertyValue)
    
    /** Set CSS custom property (variable) */
    fun variable(variableName: String, value: StylePropertyValue)
}

/**
 * Style scope builder implementation
 */
class StyleScopeBuilder : StyleScope, StyleHolder

/**
 * Mount style tag with CSS rules
 */
@Composable fun Style(cssRules: CSSRuleDeclarationList)

/**
 * Build and mount styles
 */
@Composable inline fun Style(rulesBuild: StyleSheetBuilder.() -> Unit)

/**
 * CSS unit system
 */
interface CSSUnit
interface CSSUnitLength : CSSUnit
interface CSSUnitPercentage : CSSUnit
interface CSSUnitAngle : CSSUnit
interface CSSUnitTime : CSSUnit

/** Length units */
val Number.px: CSSLengthValue
val Number.em: CSSLengthValue  
val Number.cssRem: CSSLengthValue
val Number.vw: CSSLengthValue
val Number.vh: CSSLengthValue
val Number.vmin: CSSLengthValue
val Number.vmax: CSSLengthValue

/** Percentage units */
val Number.percent: CSSPercentageValue

/** Angle units */
val Number.deg: CSSAngleValue
val Number.rad: CSSAngleValue
val Number.turn: CSSAngleValue

/** Time units */  
val Number.s: CSSTimeValue
val Number.ms: CSSTimeValue

/**
 * CSS variables
 */
class CSSStyleVariable<TValue : StylePropertyValue>
fun <TValue : StylePropertyValue> variable(): CSSStyleVariable<TValue>

Usage Example:

@Composable
fun StyledComponents() {
    Style {
        // Global styles
        ".card" style {
            backgroundColor(Color.white)
            borderRadius(8.px)
            boxShadow(0.px, 2.px, 4.px, rgba(0, 0, 0, 0.1))
            padding(16.px)
            margin(8.px)
        }
        
        ".button-primary" style {
            backgroundColor(Color.blue)
            color(Color.white)
            border(0.px)
            borderRadius(4.px)
            padding(12.px, 24.px)
            cursor("pointer")
            
            hover style {
                backgroundColor(Color.darkBlue)
            }
        }
    }
    
    Div({ classes("card") }) {
        H3({ style { marginTop(0.px) } }) {
            Text("Styled Card")
        }
        
        P { Text("This card has custom styling applied.") }
        
        Button({ classes("button-primary") }) {
            Text("Primary Action")
        }
    }
}

Attributes API

Comprehensive attribute and event handling system.

/**
 * Attributes scope for element configuration
 */
interface AttrsScope<out TElement : Element> : EventsListenerScope {
    /** Set arbitrary HTML attribute */
    fun attr(attr: String, value: String)
    
    /** Add inline styles */
    fun style(builder: StyleScope.() -> Unit)
    
    /** Add CSS classes */
    fun classes(classes: Collection<String>)
    fun classes(vararg classes: String)
    
    /** Set DOM properties directly */
    fun <E : HTMLElement, V> prop(update: (E, V) -> Unit, value: V)
    
    /** Get element reference for imperative operations */
    fun ref(effect: DisposableEffectScope.(TElement) -> DisposableEffectResult)
}

/**
 * Common HTML attributes
 */
fun AttrsScope<*>.id(value: String)
fun AttrsScope<*>.title(value: String)
fun AttrsScope<*>.hidden()
fun AttrsScope<*>.tabIndex(value: Int)
fun AttrsScope<*>.contentEditable(value: Boolean)
fun AttrsScope<*>.draggable(value: Draggable)

/**
 * Type aliases for attribute and content builders
 */
typealias AttrBuilderContext<T> = AttrsScope<T>.() -> Unit
typealias ContentBuilder<T> = @Composable ElementScope<T>.() -> Unit

Event Handling

Type-safe event handling with synthetic event wrappers.

/**
 * Base synthetic event wrapper
 */
open class SyntheticEvent<Element : EventTarget>

/**
 * Specific event types
 */
class SyntheticMouseEvent<Element : EventTarget> : SyntheticEvent<Element>
class SyntheticKeyboardEvent<Element : EventTarget> : SyntheticEvent<Element>
class SyntheticFocusEvent<Element : EventTarget> : SyntheticEvent<Element>
class SyntheticInputEvent<Element : EventTarget> : SyntheticEvent<Element>
class SyntheticChangeEvent<Element : EventTarget> : SyntheticEvent<Element>
class SyntheticSubmitEvent<Element : EventTarget> : SyntheticEvent<Element>
class SyntheticClipboardEvent<Element : EventTarget> : SyntheticEvent<Element>
class SyntheticTouchEvent<Element : EventTarget> : SyntheticEvent<Element>

/**
 * Event listener scope
 */
interface EventsListenerScope

SVG Support

Complete SVG creation and manipulation API for vector graphics.

/**
 * SVG container element
 */
@ExperimentalComposeWebSvgApi
@Composable fun Svg(
    viewBox: String? = null,
    attrs: AttrBuilderContext<SVGSVGElement>? = null,
    content: ContentBuilder<SVGSVGElement>? = null
)

/**
 * SVG shape elements
 */
@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Circle(
    cx: CSSLengthOrPercentageValue? = null,
    cy: CSSLengthOrPercentageValue? = null,
    r: CSSLengthOrPercentageValue? = null,
    attrs: AttrBuilderContext<SVGCircleElement>? = null,
    content: ContentBuilder<SVGCircleElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Rect(
    x: CSSLengthOrPercentageValue? = null,
    y: CSSLengthOrPercentageValue? = null,
    width: CSSLengthOrPercentageValue? = null,
    height: CSSLengthOrPercentageValue? = null,
    attrs: AttrBuilderContext<SVGRectElement>? = null,
    content: ContentBuilder<SVGRectElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Path(
    d: String,
    attrs: AttrBuilderContext<SVGPathElement>? = null,
    content: ContentBuilder<SVGPathElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Line(
    x1: CSSLengthOrPercentageValue? = null,
    y1: CSSLengthOrPercentageValue? = null,
    x2: CSSLengthOrPercentageValue? = null,
    y2: CSSLengthOrPercentageValue? = null,
    attrs: AttrBuilderContext<SVGLineElement>? = null,
    content: ContentBuilder<SVGLineElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Ellipse(
    cx: CSSLengthOrPercentageValue? = null,
    cy: CSSLengthOrPercentageValue? = null,
    rx: CSSLengthOrPercentageValue? = null,
    ry: CSSLengthOrPercentageValue? = null,
    attrs: AttrBuilderContext<SVGEllipseElement>? = null,
    content: ContentBuilder<SVGEllipseElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Polygon(
    points: String,
    attrs: AttrBuilderContext<SVGPolygonElement>? = null,
    content: ContentBuilder<SVGPolygonElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Polyline(
    points: String,
    attrs: AttrBuilderContext<SVGPolylineElement>? = null,
    content: ContentBuilder<SVGPolylineElement>? = null
)

/**
 * SVG text elements
 */
@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.SvgText(
    text: String,
    x: CSSLengthOrPercentageValue? = null,
    y: CSSLengthOrPercentageValue? = null,
    attrs: AttrBuilderContext<SVGTextElement>? = null
)

/**
 * SVG gradient elements
 */
@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.LinearGradient(
    id: String,
    attrs: AttrBuilderContext<SVGLinearGradientElement>? = null,
    content: ContentBuilder<SVGLinearGradientElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.RadialGradient(
    id: String,
    attrs: AttrBuilderContext<SVGRadialGradientElement>? = null,
    content: ContentBuilder<SVGRadialGradientElement>? = null
)

@ExperimentalComposeWebSvgApi
@Composable fun ElementScope<SVGElement>.Stop(
    attrs: AttrBuilderContext<SVGStopElement>? = null,
    content: ContentBuilder<SVGStopElement>? = null
)

Usage Example:

@OptIn(ExperimentalComposeWebSvgApi::class)
@Composable
fun SvgExample() {
    Svg(
        viewBox = "0 0 200 200",
        attrs = {
            style {
                width(200.px)
                height(200.px)
                border(1.px, LineStyle.Solid, Color.black)
            }
        }
    ) {
        // Define gradient
        LinearGradient(id = "myGradient") {
            Stop {
                attr("offset", "0%")
                attr("stop-color", "red")
            }
            Stop {
                attr("offset", "100%") 
                attr("stop-color", "blue")
            }
        }
        
        // Draw shapes
        Circle(
            cx = 100.px,
            cy = 100.px,
            r = 50.px,
            attrs = {
                fill("url(#myGradient)")
                attr("stroke", "black")
                attr("stroke-width", "2")
            }
        )
        
        Path(
            d = "M 50 150 Q 100 100 150 150",
            attrs = {
                fill("none")
                attr("stroke", "green")
                attr("stroke-width", "3")
            }
        )
        
        SvgText(
            text = "SVG Text",
            x = 100.px,
            y = 180.px,
            attrs = {
                attr("text-anchor", "middle")
                attr("font-family", "Arial")
                attr("font-size", "16")
            }
        )
    }
}

Testing Utilities

Testing framework for web Compose applications with composition and DOM testing support.

/**
 * Main test scope with coroutine support
 */
@ComposeWebExperimentalTestsApi
class TestScope : CoroutineScope

/**
 * Execute test block with test scope
 */
@ComposeWebExperimentalTestsApi
fun runTest(block: suspend TestScope.() -> Unit)

/**
 * Test scope API
 */
interface TestScope {
    /** Root test element */
    val root: HTMLElement
    
    /** Create test composition */
    fun composition(content: @Composable () -> Unit)
    
    /** Get next child element */
    fun nextChild(): HTMLElement
    fun <T> nextChild(): T
    
    /** Wait for recomposition to complete */
    suspend fun waitForRecompositionComplete()
    
    /** Wait for DOM changes */
    suspend fun waitForChanges(elementId: String)
    suspend fun waitForChanges(element: HTMLElement)
}

/**
 * Test utilities
 */
val HTMLElement.computedStyle: CSSStyleDeclaration

Usage Example:

@OptIn(ComposeWebExperimentalTestsApi::class)
fun testMyComponent() = runTest {
    var clicked by mutableStateOf(false)
    
    composition {
        Button({
            id("test-button")
            onClick { clicked = true }
        }) {
            Text(if (clicked) "Clicked!" else "Click me")
        }
    }
    
    val button = nextChild<HTMLButtonElement>()
    assertEquals("Click me", button.textContent)
    
    button.click()
    waitForRecompositionComplete()
    
    assertEquals("Clicked!", button.textContent)
    assertTrue(clicked)
}

Experimental APIs

/**
 * Marks experimental web APIs
 */
@RequiresOptIn
annotation class ExperimentalComposeWebApi

/**
 * Marks experimental style APIs
 */
@RequiresOptIn
annotation class ExperimentalComposeWebStyleApi

/**
 * Marks experimental SVG APIs
 */
@RequiresOptIn
annotation class ExperimentalComposeWebSvgApi

/**
 * Marks experimental test APIs
 */
@RequiresOptIn
annotation class ComposeWebExperimentalTestsApi

Install with Tessl CLI

npx tessl i tessl/maven-compose-multiplatform

docs

component-libraries.md

gradle-plugin.md

html-web.md

index.md

tile.json