css library
skui

CSS component library built around depth bevels, gradients, shadows, noise texture. everything is driven by CSS custom properties, so swapping themes is just changing one <link>

<link rel="stylesheet" href="https://skui.pages.dev/skui.css" >

colors

#colors

use variables everywhere, not hardcoded hex values. all themes define the same names, so components work across themes without changes

backgrounds
void
deep
base
raised
float
hover
primary
secondary
danger
success
warn
css variables
/* backgrounds: darkest to brightest */
--bg-void    /* deepest layer, page bg */
--bg-deep    /* sunken wells, code blocks */
--bg-base    /* main content canvas */
--bg-raised  /* elevated panels */
--bg-float   /* floating elements, popovers */
--bg-hover   /* hover state fills */

/* accents */
--blue-t / --blue-b / --blue-lit / --blue-dim
--secondary-t / --secondary-b / --secondary-lit / --secondary-glow

/* semantic */
--danger-t / --danger-b
--success-t / --success-b
--warn-t    / --warn-b
surface recipe: three layers together
raised surface gradient + bevel + border
sunken surface --shadow-inset
css
/* raised */
background: linear-gradient(180deg, var(--surf-t) 0%, var(--surf-b) 100%);
border: 1px solid var(--border-lo);
box-shadow: var(--bevel-hi), var(--bevel-lo), var(--shadow-md);

/* sunken */
background: var(--bg-deep);
border: 1px solid var(--border-lo);
box-shadow: var(--shadow-inset);

typography

#typography

plain HTML elements get styles without any classes. headings, paragraphs, links, code, kbd, blockquote, hr, etc

Display H1

Section H2

Subsection H3

Card title H4

Label H5
Micro label H6

this is regular body text. you can put links in here, bold stuff, italics, inline code snippets, keyboard shortcuts like ⌘K, and highlighted text. all of it just works on plain HTML elements

blockquote looks like this, good for quotes or text that needs to stand out
box-shadow:
  inset 0 1px 0 rgba(255,255,255,0.08),
  inset 0 -1px 0 rgba(0,0,0,0.35),
  0 3px 10px rgba(0,0,0,0.5);
html
<h1>Heading 1</h1>
<p>Body with <a>links</a>, <strong>bold</strong>, <em>italic</em>,
   <code>code</code>, <kbd>Ctrl+K</kbd>, <mark>highlight</mark>.</p>
<blockquote>Quote</blockquote>
<pre><code>code block</code></pre>

panels

#panels

basic container with the raised surface treatment: gradient, bevel lines, border, shadow. use it to group related content

default panel

default panel: raised gradient with bevel lines on the edges

panel--raised

more lift: use this for dropdowns, popovers, floating things

panel--inset: pushed inward: good for code output or read-only data

with header & footer

panel content area

html
<div class="panel">content</div>
<div class="panel panel--raised">content</div>
<div class="panel panel--inset">content</div>

<!-- with header and footer -->
<div class="panel">
  <div class="panel__header">Title</div>
  content
  <div class="panel__footer">
    <button class="btn btn--ghost">Cancel</button>
    <button class="btn btn--primary">Save</button>
  </div>
</div>

buttons

#buttons

all buttons share the base btn class. combine with a variant and optionally a size modifier

variants
html
<button class="btn btn--primary">Primary</button>
<button class="btn btn--secondary">Secondary</button>
<button class="btn btn--ghost">Ghost</button>
<button class="btn btn--danger">Danger</button>
<button class="btn btn--success">Success</button>
<button class="btn btn--primary" disabled>Disabled</button>
sizes & shapes
html
<button class="btn btn--primary btn--sm">Small</button>
<button class="btn btn--primary btn--lg">Large</button>
<button class="btn btn--secondary btn--pill">Pill</button>
<button class="btn btn--ghost btn--icon">...svg...</button>

form elements

#forms

inputs use --shadow-inset to look recessed. labels, hints, and error messages are separate elements. wrap them in .form-group to stack them

text inputs
shown publicly on your profile
enter a valid email address
html
<div class="form-group">
  <label class="form-label form-label--required">Name</label>
  <input class="input" type="text" placeholder="...">
  <span class="form-hint">hint text</span>
</div>

<!-- error state -->
<input class="input input--error">
<span class="form-error">message</span>

<textarea class="input" rows="4"></textarea>

<select class="select">
  <option>Option A</option>
</select>
checkboxes
html
<label class="checkbox-wrap">
  <input type="checkbox" checked>
  <span class="checkbox-box"></span>
  <span>Label text</span>
</label>
radio buttons
html
<label class="radio-wrap">
  <input type="radio" name="group" checked>
  <span class="radio-dot"></span>
  <span>Option A</span>
</label>
toggles & range
html
<label class="toggle-wrap">
  <input type="checkbox" checked>
  <div class="toggle-track"><div class="toggle-thumb"></div></div>
  <span>Label</span>
</label>

<input class="range" type="range" min="0" max="100" value="70">

badges & tags

#badges

nadges are small colored labels, usually for status or counts. tags are the lower-contrast version, good for categorization and filters

badges
Stable Pro Live Breaking Beta Archived
html
<span class="badge badge--primary">Stable</span>
<span class="badge badge--secondary">Pro</span>
<span class="badge badge--success">Live</span>
<span class="badge badge--danger">Breaking</span>
<span class="badge badge--warn">Beta</span>
<span class="badge badge--muted">Archived</span>
tags
default feature enhancement critical merged
html
<span class="tag">default</span>
<span class="tag tag--blue">feature</span>
<span class="tag tag--secondary">enhancement</span>
<span class="tag tag--red">critical</span>
<span class="tag tag--green">merged</span>

alerts

#alerts

full-width banners for feedback. each variant maps to colored role

Information: changes take effect on the next request
Success: changes have been saved
Warning: 84% of your hourly quota has been used
Error: something happened
html
<div class="alert alert--info">...</div>
<div class="alert alert--success">...</div>
<div class="alert alert--warn">...</div>
<div class="alert alert--danger">...</div>

tabs

#tabs

uses Alpine.js x-data for active state. the active tab receives tab--active, no other JS required

Project is healthy. Latest release reduced bundle size by 18%.
Past 30 days: 142,800 API calls, avg response 48ms.
Configure rate limits, webhooks, and access tokens.
html (requires alpine.js)
<div x-data="{ tab: 'a' }">
  <div class="tabs">
    <button class="tab"
      :class="tab === 'a' && 'tab--active'"
      @click="tab='a'">Tab A</button>
  </div>
  <div x-show="tab === 'a'">Content A</div>
</div>

list items

#list

used inside sidebars and navigation panels. list-section provides a muted group label, list-item--active highlights the selected row

Library
All Tracks
Albums
Artists
Playlists
Late Night Study
Morning Run
html
<div class="list-section">Group Label</div>
<div class="list-item list-item--active">Active item</div>
<div class="list-item">Normal item</div>

progress

#progress

inset track with a filled bar. set the bar width inline as a percentage, alternate fill colors: progress__fill--gold and progress__fill--success

html
<div class="progress">
  <div class="progress__fill" style="width: 72%"></div>
</div>

<!-- color variants -->
progress__fill--secondary
progress__fill--success

avatars

#avatars

circular (or square) initials/image containers. wrap with avatar-wrap to attach a status dot

AK
RH
MV
SL
html
<!-- sizes: avatar--sm  avatar  avatar--lg  avatar--xl -->
<!-- shapes: default (circle), avatar--square -->
<!-- status: --online  --idle  --dnd  --offline -->

<div class="avatar-wrap">
  <div class="avatar" style="background:...">AB</div>
  <span class="avatar-status avatar-status--online"></span>
</div>

tooltip

#tooltip

p tooltip, no js. the tooltip appears above the trigger element on hover

hover me stable
html
<span data-tooltip="Tooltip content">Hover me</span>

<button class="btn" data-tooltip="Save changes (⌘S)">save</button>

table

#table

wrap <table> in .table-wrap for horizontal scroll and consistent border/shadow. standard thead/tbody structure is all that's needed

Project Status Language Updated Actions
RD
renderer
Live C++ 2 hours ago
UI
ui
Beta JS Yesterday
CR
core
Archived Go 3 months ago
html
<div class="table-wrap">
  <table>
    <thead><tr>
      <th>Name</th>
      <th>Status</th>
    </tr></thead>
    <tbody><tr>
      <td>renderer</td>
      <td><span class="badge badge--success">Live</span></td>
    </tr></tbody>
  </table>
</div>

toast / snackbar

#toast

temporary notifications that render inside .toast-region (fixed, bottom-right), left-border color maps to notification type

deployment started
project is building…
saved
changes pushed to main
rate limit
429: too many requests
build failed
error in routes/api.py:42
html
<div class="toast-region">
  <div class="toast toast--success">
    <div class="toast__body">
      <div class="toast__title">Saved</div>
      Changes pushed to main.
    </div>
  </div>
</div>

<!-- variants: toast--info  toast--success  toast--warn  toast--danger -->

message

#message

chat message row with avatar, author, timestamp, and body. .message--mention highlights the row. .mention-pill for inline @mentions

MK
miuku today at 14:32
just pushed the new ghost system, dials are looking great in the viewer
AV
avsitter today at 14:35
@miuku can you check the seat offset on the new pose — it's drifting about 0.3m on non-mesh chairs
MK
miuku today at 14:38
reproduced. looks like it's the attachment point offset not being applied. fix incoming llSetPos
html
<div class="message">
  <div class="avatar avatar--md">MK</div>
  <div class="message__content">
    <div class="message__author">
      username
      <span class="message__time">today at 14:32</span>
    </div>
    <div class="message__text">
      hello <span class="mention-pill">@user</span>
    </div>
  </div>
</div>

<!-- highlight row: message--mention -->
<!-- author colors: message__author--blue  message__author--secondary -->

divider

#divider

labeled separator with double hairline bevel. text is optional — omit it for a plain rule

today
unread messages
pinned
html
<div class="divider">today</div>

<!-- plain rule: leave text content empty -->
<div class="divider"></div>

accordion

#accordion

collapsible sections with a beveled trigger button and smooth max-height transition. toggle .accordion__item--open to expand

it's a design style where UI elements look like physical objects. gradients, shadows, bevels, and textures that make things look like they have actual depth
change the <link> href to point at a different theme file, each theme just overrides the same CSS variables at :root.
just use the same CSS variables (--surf-t, --bevel-hi, --shadow-md, etc) and your stuff will pick up whatever theme is active
html
<div class="accordion">
  <div class="accordion__item accordion__item--open">
    <button class="accordion__trigger">
      Title <span class="accordion__chevron"></span>
    </button>
    <div class="accordion__body">
      <div class="accordion__content">Content</div>
    </div>
  </div>
</div>

<!-- toggle .accordion__item--open to expand/collapse -->

drawer

#drawer

slide-in panel from the right (or left with .drawer--left). pair with .drawer-overlay for the backdrop. toggle --open modifiers to show

html
<div class="drawer-overlay drawer-overlay--open" onclick="closeDrawer()"></div>
<div class="drawer drawer--open">
  <div class="drawer__header">
    <span class="drawer__title">Settings</span>
    <button class="btn btn--ghost btn--icon btn--sm"></button>
  </div>
  <div class="drawer__body">content</div>
</div>

<!-- left variant: add .drawer--left -->

popover

#popover

like a tooltip but for more content: a title, text, links. use it for inline help or previews, add .popover--visible to show it, .popover--right to anchor it to the right instead

renderer
Last deployed 3 minutes ago by @dev.
Status: ● live
html
<div class="popover-anchor">
  <button>trigger</button>
  <div class="popover popover--visible">
    <div class="popover__title">Title</div>
    Popover body text.
  </div>
</div>

<!-- right variant: .popover--right -->

number stepper

#stepper

+/− buttons on either side of a number input. good for quantity fields or config values where a spinner feels too plain

html
<div class="stepper">
  <button class="stepper__btn"></button>
  <input class="stepper__input" type="number" value="4">
  <button class="stepper__btn">+</button>
</div>

slider

#slider

range input with custom thumb: blue-steel gradient, bevel highlight, glow shadow. track is an inset well

html
<input class="slider" type="range" min="0" max="100" value="72">

file drop zone

#dropzone

dashed-border inset surface for drag-and-drop uploads, hover and active (dragging) states change the border and background

drop files here, or browse PNG, JPG, PDF, max 20 MB
html
<div class="dropzone">
  <span class="dropzone__label">drop files or <a>browse</a></span>
  <span class="dropzone__hint">PNG, JPG — max 20 MB</span>
</div>

<!-- add .dropzone--hover on hover, .dropzone--active while dragging -->

chip input

#chipinput

tag/multi-value input. chips live inside the field; pressing enter or comma adds a new one. remove with the X button

python js css
python css-library css dark-mode
html
<div class="chip-input">
  <span class="chip chip--blue">
    python
    <button class="chip__remove"></button>
  </span>
  <input class="chip-input__field" placeholder="add tag…">
</div>

<!-- chip variants: chip--blue  chip--gold -->

color swatch

#swatch

pressable color dot with a bevel highlight, .swatch--selected shows a focus indicator

small variant
html
<div class="swatch-row">
  <div class="swatch swatch--selected" style="background:#3b5f96"></div>
  <div class="swatch" style="background:#a07820"></div>
</div>

stat card

#statcard

big number + label + optional trend indicator, useful for dashboards and summary views

requests / day
2.4M
▲ 12.3%
vs last 7 days
error rate
0.07%
▼ 0.02%
p99 latency: 48ms
active users
18,342
no change
last 30 days
html
<div class="stat-card">
  <div class="stat-card__label">requests / day</div>
  <div class="stat-card__value">2.4M</div>
  <div class="stat-card__trend stat-card__trend--up">▲ 12.3%</div>
  <div class="stat-card__sublabel">vs last 7 days</div>
</div>

<!-- trend variants: stat-card__trend--up  --down  --flat -->

skeleton loader

#skeleton

shimmer placeholders for async content, size with inline width/height or use the shape variants

html
<div class="skeleton skeleton--avatar" style="width:2.5rem;height:2.5rem"></div>
<div class="skeleton skeleton--title" style="width:55%"></div>
<div class="skeleton skeleton--text" style="width:100%"></div>
<div class="skeleton skeleton--btn" style="width:90px"></div>
<div class="skeleton skeleton--card" style="width:100%"></div>

timeline

#timeline

vertical event list with connector line runs through .timeline__spine, the last item hides it automatically

deployed to production 2 min ago
v2.4.1, fixes rate-limits on /api/events
PR merged 18 min ago
feat: add stepper and accordion components
build started 20 min ago
CI triggered on push to main
Branch created 1 hr ago
feat/new-components off main
html
<div class="timeline">
  <div class="timeline__item">
    <div class="timeline__spine">
      <div class="timeline__dot"></div>
      <div class="timeline__line"></div>
    </div>
    <div class="timeline__body">
      <div class="timeline__header">
        <span class="timeline__title">Event</span>
        <span class="timeline__time">2 min ago</span>
      </div>
      <div class="timeline__text">description</div>
    </div>
  </div>
</div>

<!-- muted dot: .timeline__dot--muted -->

avatar group

#avatargroup

overlapping avatars with a border gap, the .avatar-group__overflow pill shows the excess count

MK
AV
RZ
+4
M
A
R
J
+2
html
<div class="avatar-group">
  <div class="avatar avatar--md">MK</div>
  <div class="avatar avatar--md">AV</div>
  <div class="avatar avatar--md avatar-group__overflow">+4</div>
</div>

tree view

#treeview

nested file/folder list with chevron toggles and a border-left connector

toggle .tree-node--open to expand

src
misc
module2.py
module.py
main.py
config.yml
html
<div class="tree">
  <div class="tree-node tree-node--open">
    <div class="tree-node__row">
      <span class="tree-node__chevron"></span>
      src/
    </div>
    <div class="tree-node__children">
      <div class="tree-node tree-node--leaf">
        <div class="tree-node__row">
          <span class="tree-node__chevron"></span>
          index.ts
        </div>
      </div>
    </div>
  </div>
</div>

<!-- toggle .tree-node--open to expand. leaf nodes: .tree-node--leaf -->

empty state

#emptystate

placeholder for when there's nothing to show yet: icon box, title, description, and an optional action button

no projects yet
you don't have any projects yet, create one to get started.
html
<div class="empty-state">
  <div class="empty-state__icon">
    <!-- svg icon -->
  </div>
  <div class="empty-state__title">No items yet</div>
  <div class="empty-state__text">Descriptive helper copy.</div>
  <button class="btn btn--primary btn--sm">create</button>
</div>

spoiler

#spoiler

blur-hidden inline text, revealed on click. common in chat UIs for sensitive content or spoilers

radagon is marika

your API key: xK9mP2nQr8vTsWz

click the blurred text to reveal

html
The answer is
<span class="spoiler"
      onclick="this.classList.toggle('spoiler--revealed')">
  hidden text here
</span>

segmented control

#seg

pill-style toggle group. active segment gets a raised bevel appearance. .seg--blue for blue-steel active state

html
<div class="seg seg--blue">
  <button class="seg__btn">grid</button>
  <button class="seg__btn seg__btn--active">list</button>
  <button class="seg__btn">table</button>
</div>

<!-- variants: seg--blue  sizes: seg--sm  seg--lg -->

spinner

#spinner

circular indeterminate loader. glow color matches the active accent via variant modifier

html
<div class="spinner"></div>

<!-- sizes:    spinner--sm  spinner--lg  spinner--xl -->
<!-- variants: spinner--gold  --success  --danger  --muted -->

pagination

#pagination

page navigation with beveled buttons. active page gets blue-steel inset treatment

html
<div class="pagination">
  <button class="page-btn page-btn--disabled"></button>
  <button class="page-btn page-btn--active">1</button>
  <button class="page-btn">2</button>
  <span class="page-ellipsis">···</span>
  <button class="page-btn"></button>
</div>

steps / wizard

#steps

multi-step progress indicator. connector line transitions from muted to green as steps complete

account
profile
3
billing
4
confirm
clone
build
3
test
4
deploy
html
<div class="steps">
  <div class="step step--done">
    <div class="step__dot"></div>
    <div class="step__label">account</div>
  </div>
  <div class="step step--active">
    <div class="step__dot">2</div>
    <div class="step__label">billing</div>
  </div>
  <div class="step">
    <div class="step__dot">3</div>
    <div class="step__label">confirm</div>
  </div>
</div>

<!-- states: step--done  step--active  step--error -->

split button

#splitbtn

primary action + dropdown arrow as a single compound control. the divider between the two halves is a pressed inset border

html
<div class="split-btn split-btn--primary">
  <button class="split-btn__main">deploy</button>
  <button class="split-btn__arrow"></button>
</div>

<!-- variants: split-btn--primary  --secondary  --ghost  --danger -->

command palette

#cmdpal

cmd-K style spotlight overlay. blurred backdrop, beveled panel, keyboard shortcut hints in footer

html
<div class="cmdpal-overlay" id="cmdpal">
  <div class="cmdpal">
    <div class="cmdpal__input-wrap">
      <span class="cmdpal__icon"></span>
      <input class="cmdpal__input" placeholder="search commands…">
      <kbd class="cmdpal__kbd">esc</kbd>
    </div>
    <div class="cmdpal__results">
      <div class="cmdpal__group-label">actions</div>
      <div class="cmdpal__item cmdpal__item--active">
        <div class="cmdpal__item-icon"></div>
        <span class="cmdpal__item-label">deploy to production</span>
      </div>
    </div>
  </div>
</div>

rating

#rating

star rating with gold glow on fill, hover previews the selection before clicking

readonly
html
<div class="rating" data-value="3">
  <span class="rating__star"></span>
  <!-- × 5 -->
</div>

<!-- sizes: rating--sm  rating--lg  rating--xl -->
<!-- no interaction: rating--readonly -->

dial / knob

#dial

drag up/down to rotate. deep inset recess + inner hub with bevel. SVG arc tracks value

volume
0
gain
0
freq hz
0
html
<div class="dial-wrap">
  <div class="dial">
    <svg class="dial__svg" viewBox="0 0 110 110">
      <circle class="dial__track-bg" cx="55" cy="55" r="48"
        stroke-dasharray="226.2 75.4" transform="rotate(135 55 55)"/>
      <circle class="dial__track-fill" cx="55" cy="55" r="48"
        stroke-dasharray="0 301.6" transform="rotate(135 55 55)"/>
    </svg>
    <div class="dial__body">
      <div class="dial__inner">
        <div class="dial__indicator"></div>
      </div>
    </div>
  </div>
  <div class="dial__label">volume</div>
  <div class="dial__value">0</div>
</div>

<!-- sizes: dial-wrap--sm  dial-wrap--lg -->

inline edit

#inlineedit

click any text to edit in place. confirm with ✓ or Enter, cancel with ✕ or Esc

untitled project
description: click to add a description…
html
<span class="inline-edit" onclick="startInlineEdit(this)">
  <span class="inline-edit__text">edit me</span>
  <span class="inline-edit__edit-icon"></span>
  <input class="inline-edit__input" type="text">
  <span class="inline-edit__actions">
    <button class="inline-edit__confirm"></button>
    <button class="inline-edit__cancel"></button>
  </span>
</span>

meter / gauge

#meter

radial arc gauge using SVG stroke-dasharray. value + unit centered inside. use stroke-dasharray to set fill pct: arc_length = 226.2, full_circ = 301.6

80
%
cpu
60
%
memory
20
%
disk
95
%
bandwidth
html
<!-- arc math: r=46, full=2πr≈289, track arc=75% of circle=216.8, remainder=71.2 -->
<!-- fill at X%: stroke-dasharray="(X/100*216.8) (289-(X/100*216.8))" -->

<div class="meter-wrap">
  <div class="meter" style="width:120px;height:120px">
    <svg width="120" height="120" viewBox="0 0 110 110">
      <circle class="meter__track" cx="55" cy="55" r="46" stroke-width="8"
        stroke-dasharray="216.8 71.2" transform="rotate(135 55 55)"/>
      <circle class="meter__fill meter__fill--blue" cx="55" cy="55" r="46" stroke-width="8"
        stroke-dasharray="173.4 128.2" transform="rotate(135 55 55)"/>
    </svg>
    <div class="meter__center">
      <div class="meter__value">80</div>
      <div class="meter__unit">%</div>
    </div>
  </div>
  <div class="meter__label">cpu</div>
</div>

<!-- fill variants: meter__fill--blue  --gold  --success  --danger  --warn -->

color picker

HSL sliders with live preview, hex readout, and preset swatches.

hue
saturation
lightness
html
<div class="color-picker" id="cp1">
  <div class="cp-preview" id="cp1-preview"></div>
  <div class="cp-row">
    <span class="cp-slider-label">hue</span>
    <input class="cp-hue" type="range" min="0" max="360" value="210">
  </div>
  <!-- cp-sat, cp-lit rows... -->
  <div class="cp-hex-row">
    <span class="cp-hex-dot"></span>
    <input class="cp-hex" type="text" maxlength="7">
  </div>
  <div class="cp-swatches">
    <div class="cp-swatch" style="background:#6ca3e0" data-hex="#6ca3e0"></div>
    <!-- more swatches... -->
  </div>
</div>

resize handle

Drag handle for resizable panel layouts. Horizontal and vertical orientations.

horizontal split

panel A

drag the handle to resize

panel B

vertical split

panel top

panel bottom

html
<div class="resize-panel">
  <div class="panel" style="flex:0 0 160px">panel A</div>
  <div class="resize-handle resize-handle--h" onmousedown="initResize(event,...)">
    <div class="resize-grip">
      <div class="resize-grip__dot"></div>
      <!-- x3 dots -->
    </div>
  </div>
  <div class="panel" style="flex:1">panel B</div>
</div>
<!-- vertical: add resize-panel--v + resize-handle--v -->

sparkline

Inline SVG trend lines, area fills, and bar charts in four accent colors.

api requests
1.24M
+18% this week
html
<div class="sparkline sparkline--blue">
  <svg viewBox="0 0 120 40" preserveAspectRatio="none" style="width:120px;height:40px">
    <polyline class="sparkline__area" points="0,32 30,20 60,26 90,8 120,14" fill="currentColor" stroke="none"/>
    <polyline class="sparkline__line" points="0,32 30,20 60,26 90,8 120,14" fill="none"/>
    <circle class="sparkline__dot" cx="120" cy="14" r="3"/>
  </svg>
</div>
<!-- bar: add sparkline--bar modifier -->
<!-- colors: sparkline--blue  --gold  --success  --danger -->

heat map

Contribution-style grid with 5 intensity levels. JS-generated or hand-coded cells.

html
<div class="heatmap">
  <div class="heatmap__top">
    <span class="heatmap__month-label">Jan</span>
    <!-- more month labels... -->
  </div>
  <div class="heatmap__main">
    <div class="heatmap__day-labels">
      <span class="heatmap__day-label">Mon</span>
      <!-- Wed, Fri -->
    </div>
    <div class="heatmap__grid">
      <div class="heatmap__col">
        <div class="heatmap__cell" data-level="3" title="12 commits"></div>
        <!-- 7 cells per col -->
      </div>
      <!-- 52 cols -->
    </div>
  </div>
  <div class="heatmap__legend">
    <span class="heatmap__legend-label">less</span>
    <div class="heatmap__legend-cells">
      <div class="heatmap__cell" data-level="0"></div>
      <!-- levels 1-4 -->
    </div>
    <span class="heatmap__legend-label">more</span>
  </div>
</div>
<!-- gold variant: heatmap--gold -->

code block

Syntax-highlighted code display with macOS chrome, gutter line numbers, and copy button.

server.ts typescript
12345678
import express from 'express'
import { createRoute } from './router'

const app = express()
app.use('/api', createRoute())

const PORT = process.env.PORT ?? 3000
app.listen(PORT, () => console.log(`listening on :${PORT}`))
query.sql sql
SELECT
  u.id, u.email,
  COUNT(o.id) AS order_count,
  SUM(o.total) AS revenue
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.created_at > NOW() - INTERVAL '30 days'
GROUP BY u.id
ORDER BY revenue DESC
LIMIT 20
html
<div class="code-block code-block--lined">
  <div class="code-block__header">
    <div class="code-block__dots">
      <span class="code-block__dot code-block__dot--close"></span>
      <span class="code-block__dot code-block__dot--min"></span>
      <span class="code-block__dot code-block__dot--max"></span>
    </div>
    <span class="code-block__filename">app.ts</span>
    <span class="code-block__lang">typescript</span>
    <button class="code-block__copy">copy</button>
  </div>
  <div class="code-block__body">
    <div class="code-block__lines">
      <div class="code-block__gutter"><span>1</span><!-- ... --></div>
      <pre class="code-block__code"><code>
        <!-- syntax tokens: tok-kw tok-str tok-num tok-fn tok-cmt -->
        <!-- tok-type tok-op tok-var tok-attr tok-tag tok-val     -->
      </code></pre>
    </div>
  </div>
</div>

terminal

Scanline-overlaid terminal emulator surface with prompt, output variants, and blinking cursor.

bash — ~/projects/renderer
$ ~/projects/renderer git status
On branch main
Changes not staged for commit:
  modified:   src/renderer.ts
  modified:   src/pipeline.ts
 
$ ~/projects/renderer npm run build
> renderer@2.4.1 build
> tsc && rollup -c
✓ compiled 24 modules in 1.8s
✓ bundle written to dist/
 
$ ~/projects/renderer npm run deploy
✗ deploy failed: no auth token found
run: skui auth login
$ ~/projects/renderer 
html
<div class="terminal">
  <div class="terminal__header">
    <!-- reuse code-block__dots for macOS chrome -->
    <span class="terminal__title">bash — ~/project</span>
  </div>
  <div class="terminal__body">
    <div class="terminal__line">
      <span class="terminal__prompt">$</span>
      <span class="terminal__cwd">~/project</span>
      <span class="terminal__cmd">git status</span>
    </div>
    <!-- output: terminal__out  --ok  --err  --warn  --dim  --accent -->
    <!-- cursor: <span class="terminal__cursor"></span> -->
  </div>
</div>

image compare

Drag the handle to reveal before/after. Works with any background or content.

before
after
◄►
before after
html
<div class="img-compare" id="ic1">
  <div class="img-compare__before" style="background-image:url(before.jpg)"></div>
  <div class="img-compare__after"  style="background-image:url(after.jpg)"></div>
  <div class="img-compare__divider">
    <div class="img-compare__knob">&#9668;&#9658;</div>
  </div>
  <span class="img-compare__label img-compare__label--before">before</span>
  <span class="img-compare__label img-compare__label--after">after</span>
</div>

sortable list

Drag items by the grip handle to reorder. Drop indicator shows insertion point.

renderer.ts 4.2 kb
pipeline.ts 8.7 kb
config.json 1.1 kb
shader.glsl 2.8 kb
types.d.ts 640 b
html
<div class="sortable" id="sortable1">
  <div class="sortable__item" draggable="true">
    <div class="sortable__grip">
      <!-- 6x sortable__grip-dot -->
    </div>
    <span class="sortable__label">item label</span>
    <span class="sortable__meta">meta</span>
  </div>
</div>
<!-- call initSortable('sortable1') -->

rich text

Contenteditable editor with formatting toolbar. Uses document.execCommand for inline formatting.

project notes

The renderer pipeline has been refactored to support multi-pass compositing. Performance improved by ~38% on complex scenes.

the old approach was single-pass with no depth sorting — this caused z-fighting on transparent surfaces

Next steps:

  • integrate shader.glsl depth pre-pass
  • benchmark on mobile GPUs
  • update API docs for new pipeline flags
html
<div class="richtext">
  <div class="richtext__toolbar">
    <button class="richtext__btn" onmousedown="event.preventDefault();document.execCommand('bold')"><b>B</b></button>
    <!-- italic, underline, lists... -->
    <div class="richtext__sep"></div>
  </div>
  <div class="richtext__body" contenteditable="true">
    <!-- editable content here -->
  </div>
  <div class="richtext__footer">word count</div>
</div>

xy pad

2D drag surface that emits normalized x/y coordinates. Crosshairs track the point.

warm bright cool dark
x0.50
y0.50
L R ↓ freq ↑ freq
pan+0.20
hz8.4k
html
<div class="xy-pad" id="xy1">
  <div class="xy-pad__axis-line xy-pad__axis-line--h"></div>
  <div class="xy-pad__axis-line xy-pad__axis-line--v"></div>
  <div class="xy-pad__crosshair-h" id="xy1-ch"></div>
  <div class="xy-pad__crosshair-v" id="xy1-cv"></div>
  <div class="xy-pad__point"     id="xy1-pt"></div>
</div>
<div class="xy-pad__readout">
  <div class="xy-pad__axis">
    <span class="xy-pad__axis-key">x</span>
    <span class="xy-pad__axis-val" id="xy1-xv">0.50</span>
  </div>
</div>
<!-- call initXyPad('xy1','xy1-xv','xy1-yv') -->

node editor

Wired node graph for shader, audio, or data pipeline editors. Drag nodes to reposition.

texture input
rgba
uv
color grade
color
exposure +0.4
contrast 1.2
out
uv distort
uv
strength 0.08
uv
composite
color
uv
blend multiply
output
output
final
▶ preview
html
<div class="node-editor" id="ne1">
  <svg class="node-editor__wires"></svg>
  <div class="node" style="left:24px;top:60px">
    <div class="node__header node__header--blue">
      <span class="node__title">node name</span>
    </div>
    <div class="node__body">
      <div class="node__port-row node__port-row--in">
        <div class="node__port node__port--in node__port--blue"></div>
        <span class="node__port-label">input</span>
      </div>
      <div class="node__port-row node__port-row--out">
        <span class="node__port-label">out</span>
        <div class="node__port node__port--out node__port--blue"></div>
      </div>
    </div>
  </div>
</div>
<!-- header variants: node__header--blue --gold --success --danger --neutral -->
<!-- port colors:    node__port--blue   --gold --success --danger           -->
esc
actions
deploy to production D
new project N
open settings ,
navigation
go to dashboard
view deployment logs
environment variables
Settings
Navigation
dashboard
projects
deployments
settings