Skip to content

Glassmorphism Generator — Ruby 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 Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.

# =============================================================================
#  glassmorphism_generator.rb — CosmoDev polyglot showcase port of the
#  `glassmorphism-generator` tool
#  -----------------------------------------------------------------------------
#  Language : Ruby (3.2, 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 /
#  border_opacity (+ optional saturation). Hex tints (#rgb / #rrggbb) are
#  matched with anchored stdlib regexes; opacity is clamped to [0,1]; the
#  alpha channel is rounded with floor(x*100 + 0.5)/100 — JS Math.round
#  (half-up) semantics, NOT Float#round's banker's behavior on ties — so the
#  output matches the live TypeScript lib byte-for-byte. Numbers render like
#  JS template literals (10.0 -> "10", 0.3 -> "0.3"): Float#to_s is the
#  shortest round-tripping decimal, but it keeps ".0" on whole values, so
#  those render as Integers instead.
# =============================================================================

module GlassmorphismGenerator
  # Configuration for one glassmorphism declaration block.
  Config = Struct.new(
    :blur, :opacity, :tint, :radius, :border_opacity, :saturation,
    keyword_init: true
  ) do
    def initialize(blur: 0.0, opacity: 0.0, tint: '#ffffff', radius: 0.0,
                   border_opacity: 0.0, saturation: nil)
      super
    end
  end

  # The rendered glassmorphism block plus its full CSS text.
  Css = Struct.new(
    :background, :backdrop_filter, :webkit_backdrop_filter,
    :border, :border_radius, :box_shadow, :full_css,
    keyword_init: true
  )

  HEX3 = /\A[0-9a-fA-F]{3}\z/
  HEX6 = /\A[0-9a-fA-F]{6}\z/

  class << self
    # Parse a #rgb or #rrggbb hex color into [r, g, b]; nil 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.
    def hex_to_rgb(hex_color)
      h = hex_color.to_s.strip
      h = h[1..] if h.start_with?('#')

      if HEX3.match?(h)
        [(h[0] * 2).to_i(16), (h[1] * 2).to_i(16), (h[2] * 2).to_i(16)]
      elsif HEX6.match?(h)
        [h[0, 2].to_i(16), h[2, 2].to_i(16), h[4, 2].to_i(16)]
      end
    end

    # Format an rgba() string, clamping + 2-decimal-rounding the alpha.
    def rgba(r, g, b, a)
      format('rgba(%<r>d, %<g>d, %<b>d, %<alpha>s)',
             r: r, g: g, b: b, alpha: alpha(a))
    end

    # Build the full glassmorphism CSS for a Config.
    def build_glassmorphism(cfg)
      blur = [cfg.blur.to_f, 0.0].max
      opacity = cfg.opacity.to_f.clamp(0.0, 1.0)
      radius = [cfg.radius.to_f, 0.0].max
      border_opacity = cfg.border_opacity.to_f.clamp(0.0, 1.0)
      saturation = cfg.saturation.nil? ? 180.0 : cfg.saturation.to_f
      r, g, b = hex_to_rgb(cfg.tint) || [255, 255, 255]

      background = rgba(r, g, b, opacity)
      backdrop_filter = format('blur(%<b>spx) saturate(%<s>s%%)',
                               b: fmt_num(blur), s: fmt_num(saturation))
      border = "1px solid #{rgba(255, 255, 255, border_opacity)}"
      border_radius = "#{fmt_num(radius)}px"
      box_shadow = '0 8px 32px 0 rgba(31, 38, 135, 0.37)'

      full_css = [
        "background: #{background};",
        "-webkit-backdrop-filter: #{backdrop_filter};",
        "backdrop-filter: #{backdrop_filter};",
        "border-radius: #{border_radius};",
        "border: #{border};",
        "box-shadow: #{box_shadow};"
      ].join("\n")

      # Both prefixed and unprefixed backdrop-filter carry the same value.
      Css.new(background: background, backdrop_filter: backdrop_filter,
              webkit_backdrop_filter: backdrop_filter, border: border,
              border_radius: border_radius, box_shadow: box_shadow,
              full_css: full_css)
    end

    private

    # Render a number like a JS template literal (10.0 -> '10', 0.3 -> '0.3').
    def fmt_num(x)
      x = Float(x)
      return x.to_i.to_s if x == x.truncate && x.finite?
      x.to_s
    end

    # Clamp to [0,1], round with JS Math.round (half-up), and render.
    def alpha(a)
      c = a.to_f.clamp(0.0, 1.0)
      fmt_num((c * 100.0 + 0.5).floor / 100.0)
    end
  end
end

if $PROGRAM_NAME == __FILE__
  cfg = GlassmorphismGenerator::Config.new(
    blur: 10, opacity: 0.3, tint: '#ffffff', radius: 12, border_opacity: 0.2
  )
  puts GlassmorphismGenerator.build_glassmorphism(cfg).full_css
end

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 →