SemanticUI DSL

A Kotlin DSL for the FomanticUI CSS framework. Write ui.blue.button instead of class="ui blue button".

We chose FomanticUI because it looks decent out of the box and doesn't require a build pipeline for its CSS. Could we support Tailwind or other frameworks? Sure. We just haven't needed to yet.

How it works

The DSL starts with ui (or noui for non-UI classes) and chains modifiers using Kotlin properties:

// This:
ui.inverted.blue.segment {
    ui.header H2 { +"Welcome" }
    ui.basic.button { +"Click me" }
}

// Generates this HTML:
// <div class="ui inverted blue segment">
//   <h2 class="ui header">Welcome</h2>
//   <button class="ui basic button">Click me</button>
// </div>

Every modifier is a Kotlin property that adds a CSS class. The final call (segment {}, button {}, header H2 {}) creates the HTML element and opens a block for its children.

Core elements

Buttons

ui.button { +"Default" }
ui.blue.button { +"Blue" }
ui.basic.green.button { +"Outlined green" }
ui.icon.button { icon.heart() }
ui.labeled.icon.button { icon.download(); +"Download" }

// Button group
ui.buttons {
    ui.button { +"One" }
    ui.button { +"Two" }
    ui.button { +"Three" }
}

Segments

ui.segment { +"Basic segment" }
ui.raised.segment { +"Raised" }
ui.padded.segment { +"Extra padding" }
ui.inverted.segment { +"Dark background" }
ui.placeholder.segment { +"Empty state placeholder" }

// Stacked segments
ui.segments {
    ui.segment { +"First" }
    ui.segment { +"Second" }
}

Headers

ui.header H1 { +"Page title" }
ui.header H2 { +"Section header" }
ui.sub.header { +"Sub-header" }
ui.icon.header {
    icon.settings()
    noui.content { +"Settings" }
}

Grid system

ui.two.column.grid {
    noui.column {
        +"Left column"
    }
    noui.column {
        +"Right column"
    }
}

// Responsive
ui.four.column.doubling.stackable.grid {
    noui.column { +"A" }
    noui.column { +"B" }
    noui.column { +"C" }
    noui.column { +"D" }
}

Cards

ui.three.cards {
    noui.card {
        noui.content {
            ui.header { +"Card title" }
            noui.description { +"Card content" }
        }
        noui.extra.content {
            icon.user()
            +"4 members"
        }
    }
}

Labels

ui.label { +"Default" }
ui.red.label { +"Error" }
ui.green.label { +"Success" }
ui.circular.blue.label { +"42" }

Menus

ui.vertical.inverted.menu {
    noui.item A {
        onClick { evt -> router.navToUri(evt, Nav.home()) }
        +"Home"
    }
    noui.item A {
        onClick { evt -> router.navToUri(evt, Nav.about()) }
        +"About"
    }
}

Tables

ui.celled.table Table {
    thead {
        tr {
            th { +"Name" }
            th { +"Status" }
        }
    }
    tbody {
        users.forEach { user ->
            tr {
                td { +user.name }
                td { ui.green.label { +"Active" } }
            }
        }
    }
}

Conditional styling

The given() modifier applies styles based on a condition:

// Disable button when form is invalid
ui.blue.button.given(!isValid) { disabled }.then {
    onClick { submit() }
    +"Submit"
}

// Highlight active menu item
ui.item.given(isActive) { active }.then A {
    +"Dashboard"
}

// Add loading state
ui.button.given(isLoading) { loading }.then {
    +"Save"
}

How it reads: "A blue button, given it's not valid, make it disabled, then render it."

Colors

16 named colors plus brand colors:

ui.red.label { +"Red" }
ui.orange.label { +"Orange" }
ui.yellow.label { +"Yellow" }
ui.olive.label { +"Olive" }
ui.green.label { +"Green" }
ui.teal.label { +"Teal" }
ui.blue.label { +"Blue" }
ui.violet.label { +"Violet" }
ui.purple.label { +"Purple" }
ui.pink.label { +"Pink" }
ui.brown.label { +"Brown" }
ui.grey.label { +"Grey" }
ui.black.label { +"Black" }
ui.white.label { +"White" }

Sizes

ui.mini.button { +"Mini" }
ui.tiny.button { +"Tiny" }
ui.small.button { +"Small" }
ui.medium.button { +"Medium" }
ui.large.button { +"Large" }
ui.big.button { +"Big" }
ui.huge.button { +"Huge" }
ui.massive.button { +"Massive" }

Icons

FomanticUI includes Font Awesome icons:

icon.home()
icon.user()
icon.search()
icon.trash()
icon.check_circle()
icon.exclamation_circle()
icon.sign_out_alternate()
icon.grip_vertical()
icon.eraser()
icon.dollar_sign()
icon.heart()
icon.settings()
icon.download()

Modals

ui.button {
    onClick {
        modals.show { handle ->
            OkCancelModal {
                mini(
                    handle = handle,
                    header = { ui.header { +"Confirm" } },
                    content = {
                        ui.content {
                            +"Are you sure you want to delete this?"
                        }
                    },
                ) { result ->
                    when (result) {
                        OkCancelModal.Result.Ok -> deleteItem()
                        OkCancelModal.Result.Cancel -> { /* do nothing */ }
                    }
                }
            }
        }
    }
    +"Delete"
}

Modal sizes: mini(), tiny(), small(), large(), and full-size.

Toast notifications

// Info toast
toasts.info("Settings saved successfully")

// Warning
toasts.warning("Your session expires in 5 minutes")

// Error
toasts.error("Failed to save changes")

// Random type
toasts.append(Message.Type.entries.random(), "Some message")

Context menus and popups

// Context menu on right-click
ui.segment {
    onContextMenuStoppingEvent { evt ->
        popups.showContextMenu(evt, PopupsManager.Positioning.BottomLeft) {
            ui.vertical.menu {
                noui.item { +"Copy" }
                noui.item { +"Paste" }
                noui.item { +"Delete" }
            }
        }
    }
    +"Right-click me"
}

// Hover popup
ui.button {
    popups.showHoverPopup.topLeft(tag = this) {
        +"This is a tooltip"
    }
    +"Hover me"
}

Custom CSS

Combine the DSL with inline CSS when needed:

div {
    css {
        marginTop = 16.px
        marginLeft = 250.px
        width = 100.pct
        height = 50.vh
    }
    +"Styled content"
}

See it in action

The full SemanticUI component showcase — buttons, cards, menus, tables, grids, and more: