htmlang
playground
-- A comprehensive tour of htmlang: layout, style, components, and control flow. @page htmlang Demo @lang en @meta description A one-page tour of htmlang. @og title htmlang @og type website -- Design tokens become both $vars and CSS custom properties @theme primary #3b82f6 ink #0f172a muted #64748b subtle #f8fafc -- Computed @let with arithmetic @let base 8 @let gap = $base * 2 @let gutter = $gap + $base -- Reusable attribute bundle @let card [ padding $gap, background white, rounded $base, border 1 #e5e7eb, shadow 0 1px 3px rgba(0,0,0,0.05) ] -- Composable style group (used with spread) @let interactive [ cursor pointer, hover:shadow 0 4px 12px rgba(0,0,0,0.08), transition all 0.2s ease ] -- Keyframes using attribute syntax @keyframes fade-up from [opacity 0, transform translateY(8px)] to [opacity 1, transform translateY(0)] -- Raw HTML escape hatch @raw """
""" -- Component with default parameter + @children slot @let card $title $icon=◆ @article [$card, ...$interactive, animate fade-up 0.3s ease] @row [spacing 10, align-items center] @text [size 20, color $primary] $icon @text [bold, size 16, color $ink] $title @children -- Component that wraps children in a titled section (function + slot) @let titled $title @section [spacing 12, padding-y $base] @text [bold, size 22, color $ink] $title @children -- Top-level layout @column [ max-width 960, center-x, padding 32, spacing $gutter, background white, dark:background #0b1220, dark:color #e2e8f0 ] -- Header: nav + hero @header [spacing $base] @nav [spacing 6, color $muted, size 13] @row [spacing 6, align-items center] @link [color $muted] / Home @text / @link [color $muted] /demos Demos @text / @text [color $ink] htmlang @row [spacing $gap, align-items center] @column [width fill, spacing 6] @text [bold, size 40, color $ink, dark:color white] Welcome to htmlang @paragraph [line-height 1.6, color $muted] A minimalist layout language that compiles to {@text [bold, color $primary] static HTML} — no JS, no build step. Visit the {@link [color $primary, underline] https://github.com repo} for more. @image [width 72, height 72, rounded 36, alt "Avatar"] https://picsum.photos/72 @hr [border-top 1 #e5e7eb] -- Feature grid using @card + @titled components @titled [title Features] @row [wrap, gap $base] @card [title Simple, icon ✦] Write layouts without CSS classes. @card [title Fast, icon ⚡] Compiles to a single self-contained HTML file. @card [title Minimal, icon ○] Zero runtime JavaScript by default. @card [title Typed] LSP with diagnostics and go-to-definition. -- Typography showcase: inline elements, blockquote, code @titled [title Typography] @paragraph [line-height 1.7] A quick brown fox jumps over {@text [bold] the lazy} {@text [italic] dog}. Press {@kbd [ padding 2 6, background #f1f5f9, rounded 4, font monospace, size 13 ] Ctrl+K} to search. {@mark [background #fef08a] Highlighted} uses mark. {@abbr [title HyperText Markup Language + language] htmlang} is an abbreviation. @blockquote [ padding-inline-start $gap, border-left 3 $primary, color $muted, italic ] Layout is code. Style is code. Everything should compose. @cite — Anonymous @pre [ background #0f172a, color #e2e8f0, padding 16, rounded 8, font monospace, size 13, overflow-x auto ] @raw """ @let button $label @el [padding 12, background blue] @text $label """ -- Control flow: @each, @else, @repeat, @for, @match, if() @titled [title Control flow] @column [spacing 6] @each $name, $role in Alice Engineer, Bob Designer, Carol PM @row [spacing 10, align-items baseline] @text [bold, min-width 80] $name @text [color $muted] $role -- @each ... @else fallback for empty list @let nothing "" @each $x in $nothing @text $x @else @text [color $muted, italic, data-state $nothing] (Nothing — @each ... @else fallback) -- @repeat @row [spacing 6] @repeat 6 @el [width 18, height 18, rounded 9, background $primary, opacity 0.4] -- @for range @row [spacing 4] @for $i in 1..6 @el [ width 24, height 24, rounded 4, background $primary, color white, text-align center, size 12, bold, line-height 24 ] $i -- @if / @else if / @else (with @assert to verify at compile time) @let count 3 @assert $count == 3 @row [spacing 6, align-items center] @text [bold, min-width 64] Count: @if $count == 0 @text [color $muted] Empty @else if $count == 1 @text [color $primary] Exactly one @else @text [color $primary, bold] $count items -- @match @let status ready @match $status @case pending @text [color #f59e0b] Pending… @case ready @text [color #10b981, bold] ✓ Ready @case error @text [color #ef4444] Error @default @text Unknown -- Conditional attribute value + before: pseudo-element @let emphasized true @el [ padding 12, rounded 6, background if($emphasized, $primary, #e5e7eb), color if($emphasized, white, $ink), before:content "→ ", before:bold ] Conditional styling via {@code if(cond, a, b)}. -- Forms + pseudo-states @titled [title Forms] @form [spacing 10, max-width 420] /subscribe @label [bold, size 13, for email] Email @input [ id email, type email, name email, placeholder you@example.com, required, padding 10, border 1 #e5e7eb, rounded 6, focus:border 1 $primary, valid:border 1 #10b981, invalid:border 1 #ef4444, transition border 0.15s ease ] @label [bold, size 13, for role] Role @select [id role, name role, padding 10, border 1 #e5e7eb, rounded 6] @option [value dev] Developer @option [value designer] Designer @option [value other] Other @label [bold, size 13, for msg] Message @textarea [ id msg, name msg, rows 3, padding 10, border 1 #e5e7eb, rounded 6 ] @row [spacing 8] @button [ type submit, padding 10 16, background $primary, color white, bold, rounded 6, border 0, hover:background $primary|darken:10, active:background $primary|darken:20, transition background 0.15s ease, cursor pointer ] Subscribe @button [ type reset, padding 10 16, background white, border 1 #e5e7eb, rounded 6, cursor pointer ] Reset @progress [value 60, max 100] -- Lists and tables @titled [title Data] @row [gap $gap, wrap] @column [width fill, spacing $base, min-width 240] @text [bold] Unordered @list [spacing 4, list-style disc, padding-inline-start 20] @item [display list-item] Composable with @each @item [display list-item] Indent to nest children @item [display list-item] Bold list items work too @text [bold] Ordered @list [ordered, list-style decimal, padding-inline-start 20] @item [display list-item] First @item [display list-item] Second @item [display list-item] Third @column [width fill, spacing $base, min-width 240] @text [bold] Table @table @thead @tr @th [text-align left, padding 8] Item @th [text-align right, padding 8] Qty @tbody @tr @td [padding 8, border-top 1 #e5e7eb] Widgets @td [padding 8, border-top 1 #e5e7eb, text-align right] 12 @tr @td [padding 8, border-top 1 #e5e7eb] Gadgets @td [padding 8, border-top 1 #e5e7eb, text-align right] 7 @figure [spacing 6] @image [alt "A random placeholder", rounded 8] https://picsum.photos/640/200 @figcaption [color $muted, size 13, text-align center] A placeholder image. -- Disclosure + named grid areas @titled [title Disclosure + grid areas] @details [padding 12, border 1 #e5e7eb, rounded 6, background $subtle] @summary [bold, cursor pointer] What is htmlang? @paragraph [color $muted, line-height 1.6, padding-block-start 6] htmlang is a layout-first language that compiles to a single, self-contained HTML file with embedded CSS. -- Implicit @el via bare [attrs] line [ grid, grid-template-columns 1fr 2fr, grid-template-areas "h h" "s m" "f f", gap $base, padding $base, background $subtle, rounded 8 ] @el [grid-area h, padding 10, background $primary, color white, rounded 6, text-align center] Header @el [grid-area s, padding 10, background white, rounded 6, border 1 #e5e7eb] Sidebar @el [grid-area m, padding 10, background white, rounded 6, border 1 #e5e7eb] Main @el [grid-area f, padding 10, background $ink, color white, rounded 6, text-align center] Footer -- Misc: address, time, meter @titled [title Misc] @address [spacing 4, font-style normal] @text [bold] htmlang HQ @text 123 Flexbox Avenue @link [color $primary] mailto:hi@htmlang.dev hi@htmlang.dev @row [spacing 10, align-items center] @text [size 13, color $muted] Published @time [datetime 2026-04-16, size 13] April 16 2026 @row [spacing 10, align-items center] @text [size 13, color $muted] Confidence @meter [value 8, min 0, max 10, low 3, high 7] 8/10 -- Footer: hover/active button, visited link, print-hidden @footer [ padding-block-start $gutter, margin-block-start $gutter, border-top 1 #e5e7eb, spacing $base, print:display none ] @row [spacing $base, align-items center] @el [ padding 12 20, background $primary, color white, bold, rounded 8, hover:background $primary|darken:10, active:background $primary|darken:20, transition all 0.15s ease, cursor pointer ] > @link https://github.com Get Started @link [ color $primary, visited:color #8b5cf6, hover:underline ] https://github.com Browse source @el [width fill] @text [color $muted, size 13] © 2026 htmlang
▶ Run (Ctrl+Enter)
Copy HTML
Share
Ready
preview