Skip to content

Glassmorphism Generator — Kotlin source

Create frosted-glass CSS - backdrop-blur, translucent background, subtle border, and soft shadow - with a copy-ready snippet and live preview.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// =============================================================================
//  Glassmorphism.kt — CosmoDev polyglot showcase port of the
//  `glassmorphism-generator` tool
//  -----------------------------------------------------------------------------
//  Language : Kotlin (1.9, standard library only)
//  Source:   ported from src/lib/glassmorphism.ts (the canonical, live
//             TypeScript lib); mirrors
//             src/tool-sources/glassmorphism-generator/{python.py,rust.rs}
//  License  : display source — part of CosmoDev's polyglot tool pages
//             (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
//             Python ports and the other language ports.
//  -----------------------------------------------------------------------------
//  Builds a frosted-glass CSS block from blur / opacity / tint / radius /
//  borderOpacity (+ optional saturation). Hex tints (#rgb / #rrggbb) are
//  validated by hand (no regex — stdlib only); opacity is clamped to [0,1];
//  the alpha channel is rounded with floor(x*100 + 0.5)/100 — JS Math.round
//  (half-up) semantics — and numbers render like JS template literals:
//  Double.toString is already the shortest round-tripping decimal, but it
//  keeps a trailing ".0" on whole values, so those go through toLong()
//  instead. The output therefore matches the live TypeScript lib
//  byte-for-byte.
// =============================================================================

package com.cosmolabs.cosmodev

import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.min

/** Configuration for one glassmorphism declaration block. */
data class GlassConfig(
    val blur: Double = 0.0,
    val opacity: Double = 0.0,
    val tint: String = "#ffffff",
    val radius: Double = 0.0,
    val borderOpacity: Double = 0.0,
    val saturation: Double? = null,  // null => 180.0 (lib default)
)

/** The rendered glassmorphism block plus its full CSS text. */
data class GlassCss(
    val background: String,
    val backdropFilter: String,
    val webkitBackdropFilter: String,
    val border: String,
    val borderRadius: String,
    val boxShadow: String,
    val fullCss: String,
)

/** Pure glassmorphism CSS builder — deterministic, no DOM. */
object Glassmorphism {
    /** True when the character is an ASCII hex digit (0-9, a-f, A-F). */
    private fun isHexDigit(c: Char): Boolean =
        c in '0'..'9' || c in 'a'..'f' || c in 'A'..'F'

    /** True when every character is an ASCII hex digit. */
    private fun isHexRun(s: String): Boolean =
        s.isNotEmpty() && s.all(::isHexDigit)

    /** Parse a run of hex digits into its value (base 16). */
    private fun parseHex(s: String): Int = s.toInt(16)

    /**
     * Render a number like a JS template literal (10.0 -> "10", 0.3 -> "0.3").
     * Double.toString is the shortest round-tripping decimal but keeps ".0"
     * on whole values, so those render through toLong() instead (safe within
     * ±9.2e18 — far beyond any blur/radius/saturation value).
     */
    private fun fmt(x: Double): String =
        if (x == floor(x) && !x.isInfinite() && abs(x) < 9.2e18) {
            x.toLong().toString()
        } else {
            x.toString()
        }

    /**
     * Parse a #rgb or #rrggbb hex color into (r, g, b); null if invalid.
     * A 3-char hex expands each digit (e.g. "abc" -> "aabbcc"), like CSS.
     * Exactly one leading '#' is stripped, matching the lib's /^#/ replace.
     */
    fun hexToRgb(hexColor: String?): Triple<Int, Int, Int>? {
        var h = hexColor?.trim() ?: ""
        if (h.startsWith("#")) h = h.substring(1)

        if (h.length == 3 && isHexRun(h)) {
            // "a" -> 0xaa — each digit doubles into a pair, like CSS.
            fun expand(c: Char): Int {
                val d = c.digitToInt(16)
                return d * 16 + d
            }
            return Triple(expand(h[0]), expand(h[1]), expand(h[2]))
        }
        if (h.length == 6 && isHexRun(h)) {
            return Triple(parseHex(h.substring(0, 2)),
                          parseHex(h.substring(2, 4)),
                          parseHex(h.substring(4, 6)))
        }
        return null
    }

    /** Clamp to [0,1], round with JS Math.round (half-up), and render. */
    private fun alpha(a: Double): String {
        val c = min(1.0, max(0.0, a))
        return fmt(floor(c * 100.0 + 0.5) / 100.0)
    }

    /** Format rgba(), clamping + 2-decimal-rounding the alpha. */
    fun rgba(r: Int, g: Int, b: Int, a: Double): String =
        "rgba($r, $g, $b, ${alpha(a)})"

    /** Build the full glassmorphism CSS for a config. */
    fun buildGlassmorphism(cfg: GlassConfig): GlassCss {
        val blur = max(0.0, cfg.blur)
        val opacity = min(1.0, max(0.0, cfg.opacity))
        val radius = max(0.0, cfg.radius)
        val borderOpacity = min(1.0, max(0.0, cfg.borderOpacity))
        val saturation = cfg.saturation ?: 180.0
        val (r, g, b) = hexToRgb(cfg.tint) ?: Triple(255, 255, 255)

        val background = rgba(r, g, b, opacity)
        val backdropFilter = "blur(${fmt(blur)}px) saturate(${fmt(saturation)}%)"
        val border = "1px solid ${rgba(255, 255, 255, borderOpacity)}"
        val borderRadius = "${fmt(radius)}px"
        val boxShadow = "0 8px 32px 0 rgba(31, 38, 135, 0.37)"

        val fullCss = listOf(
            "background: $background;",
            "-webkit-backdrop-filter: $backdropFilter;",
            "backdrop-filter: $backdropFilter;",
            "border-radius: $borderRadius;",
            "border: $border;",
            "box-shadow: $boxShadow;",
        ).joinToString("\n")

        // Both prefixed and unprefixed backdrop-filter carry the same value.
        return GlassCss(background, backdropFilter, backdropFilter, border,
                        borderRadius, boxShadow, fullCss)
    }
}

fun main() {
    val css = Glassmorphism.buildGlassmorphism(
        GlassConfig(blur = 10.0, opacity = 0.3, tint = "#ffffff",
                    radius = 12.0, borderOpacity = 0.2))
    println(css.fullCss)
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →