Customize

Graffiti

github

Elements

Single-purpose UI elements.

These are common, non-complex UI elements that just need a class or two. Forms, cards, buttons, and more.

Buttons

Button styling for `<button>` elements and `.button` class for links.

When to use: Buttons and links styled as actions.

Classes: .button, .primary, .success, .warning, .error, .ghost, .minimal, .dark, .light, .contrast, .mini

Reference Notes
Surface Variants

.dark, .light, and .contrast keep a fixed surface regardless of theme. Use them on buttons that need to sit on a particular surface (e.g., a .dark hero) and stay legible.

Reset Utility

.reset is a general-purpose utility (not a button variant) for stripping native chrome from any element. It removes background, border, radius, shadow, and padding while inheriting typography and color — useful when you want a <button> to behave like a bare interactive surface.

Styling Details
  • All buttons have consistent padding and border-radius
  • Hover, focus, and active states included
  • Disabled state reduces opacity and prevents interaction

Chips

Interactive pill-shaped elements for filters, categories, and selections.

When to use: Selectable pills for filters and segmented choices.

Classes: .chip, .selected, .mini

Reference Notes

Use for multi-select interfaces, tag filters, or skill selectors.

With Icons

Icons are automatically sized to 1em.

Styling Details
  • Pill shape with border-radius: var(--br-xxl)
  • Border: var(--border-1)
  • Selected state: primary color background
  • Hover/focus/active states included
  • Fluid typography at --fl: -1 (smaller text)
Chips vs Tags
  • Chips are interactive (clickable, selectable)
  • Tags are for display (category labels, metadata)

Tags

Subtle category labels with customizable colors.

When to use: Status or category labels.

Classes: .tag, .success, .warning, .error, .info, .muted

Reference Notes

Use for metadata, categories, or status indicators.

Custom Colors

For custom categories, use --tag-color as a fallback when semantic variants are not a fit:

Available colors: --red, --orange, --yellow, --green, --teal, --blue, --indigo, --purple, --pink, --gray, --slate

With Icons

Icons are automatically sized to 1em.

Styling Details
  • Soft tinted background derived from --tag-color via OKLCH lightness/chroma adjustments (theme-aware in light and dark)
  • Text color auto-adjusts for light/dark themes
  • Subtle border derived from --tag-color
  • Fluid typography at --fl: -1 (smaller text)
  • Pill shape with border-radius: var(--br-xxl)
  • Interactive tags (<a>/<button>) lift 1px on hover (translate: 0 -1px)
Tags vs Chips
  • Tags are for display (category labels, metadata)
  • Chips are interactive (clickable, selectable)

Avatar

Circular avatar for user images or initials.

When to use: User photos or initials with size variants.

Classes: .avatar, .xs, .s, .l, .xl, .bordered

Reference Notes
Styling Details
  • Circular shape (border-radius: 50%)
  • Images use object-fit: cover
  • Initials have subtle background (--fg-1)
  • Text scales with avatar size

Boxes

Container styles for cards and content blocks.

When to use: Container surface styles and quick panel variants.

Classes: .box, .glow, .semi-gloss, .ghost, .invisible

Reference Notes
Styling Details
  • .box - Tint background, padding, border-radius, border
  • .box.glow - Applies var(--box) — a soft outer drop shadow plus two inset highlights for a subtle dimensional sheen
  • .box.semi-gloss - Gradient background, premium feel
  • .box.ghost - Transparent, border outline only
  • .box.invisible - No visual styling, just structure

Callouts

Informational boxes for tips, warnings, errors, and success messages.

When to use: Inline informational or status callout blocks.

Classes: .callout, .warning, .error, .success, .ghost, .fill, .callout.stack

Reference Notes
With Icon

The first direct <svg> child is pulled into the gutter and colored with --callout-accent:

Multiple Elements

Add .stack directly to the callout to align children to the start and stack them with consistent spacing:

A nested <div class="stack"> works too if you need finer control over which children stack.

CSS Variables
  • --callout-tint - Background color (applies to .fill only)
  • --callout-accent - Icon color

Card

A padded surface for grouped content. Reach for header, footer, or media only when you need a divided bar or edge-to-edge media.

When to use: Grouped content, linked previews, and pricing tiles.

Classes: .card, .card.linked, .card.featured

Reference Notes
Basic Card

The card itself is the padded surface — drop content directly inside. No wrapper, no body class.

Children stack vertically with a small built-in gap. Override it inline with --gap when you want more breathing room:

Add a direct <header> or <footer> only when you want a divided bar with a separator. They bleed edge-to-edge through the card's padding automatically.

With Media

Direct <img>, <picture>, or <figure> children bleed edge-to-edge too. At the top or bottom of the card they extend through that side's padding.

Notes
  • .card is the padded surface. Don't wrap content in an extra <div>.
  • Children stack with gap: var(--gap, var(--vs-s)). Override with style="--gap: var(--vs-m);".
  • <header>, <footer>, and <img>/<picture>/<figure> direct children bleed to the card edge — use them only when you actually want that treatment.
  • Skip headers and footers for simple cards. Most cards don't need them.

Bubble

Chat-friendly message container with configurable colors, width, and spacing.

When to use: Chat message presentation and conversation snippets.

Classes: .bubble, .chat-thread, .chat-row, .chat-row.self, .chat-message, .chat-composer

Reference Notes

.bubble is a rounded chat container with configurable padding, max-width, and child flow spacing.

Chat Layout Helpers

Use these helpers to build complete chat threads:

  • .chat-thread - Vertical message stack with configurable spacing/padding
  • .chat-row - Left-aligned row
  • .chat-row.self - Right-aligned row
  • .chat-message - Width-constrained wrapper for each message
  • .chat-composer - Composer row where .input-group expands to fill space
CSS Variables
  • --bubble-bg - Bubble background color
  • --bubble-border - Bubble border color
  • --bubble-max-inline - Max bubble width
  • --bubble-pad-block - Block-axis padding
  • --bubble-pad-inline - Inline-axis padding
  • --bubble-radius - Corner radius
  • --bubble-flow-space - Spacing between child elements

Horizontal scrolling with CSS scroll-snap. No JavaScript required.

When to use: Horizontal scroll-snap for cards or media strips.

Classes: .carousel

Reference Notes
Styling Details
  • display: flex with horizontal overflow
  • scroll-snap-type: x mandatory for snapping
  • Children have scroll-snap-align: start
  • Thin scrollbar (scrollbar-width: thin)
  • Default gap of 1rem
How It Works
  • Drag, swipe, or scroll horizontally
  • Items snap to start position
  • Works with mouse, touch, trackpad, and keyboard
  • No JavaScript needed for basic functionality

Reel

Vertical scrolling with CSS scroll-snap. Like carousel but vertical.

When to use: Vertical scroll-snap list or feed.

Classes: .reel

Reference Notes
CSS Variables
  • --reel-height - Container height (default: 80vh)
  • --gap - Gap between panels (default: 1rem)
Styling Details
  • display: flex with flex-direction: column
  • scroll-snap-type: y mandatory for snapping
  • Children have scroll-snap-align: start
  • Vertical overflow with thin scrollbar
How It Works
  • Scroll vertically to navigate panels
  • Panels snap to top
  • Works with mouse wheel, touch, and keyboard

Tables

Responsive table wrapper with clean data table styling.

When to use: Responsive table wrapper and default table styling.

Classes: .table, .table.zebra

Reference Notes
Why the Wrapper?

The .table wrapper provides:

  • Horizontal scrolling on small screens
  • Border and border-radius on the container
  • Proper overflow handling
Styling Details
  • Tables are 100% width with collapsed borders
  • Headers have bottom border separator
  • Cells have consistent padding
  • Last row has no bottom border
  • Wrapper has overflow-x: auto for responsiveness
CSS Variables
  • --table-border - Custom border-radius for wrapper

Dialog

Native HTML `<dialog>` element with open/close animations.

When to use: Native modal flows and confirmations.

Classes: dialog, .close

Reference Notes

No JavaScript required when using HTML invokers.

How It Works
  • commandfor points to the dialog's id
  • command="show-modal" opens the dialog as a modal
  • command="close" closes the dialog
  • No JavaScript needed for basic open/close
Close Button

The .close class creates a circular red button. When it is a direct child of a <dialog>, an extra rule pins it to the top-right corner (slightly overlapping the dialog's top edge). Used outside a <dialog>, it's just the circular button — position it yourself.

Styling Details
  • max-width: 40ch - Character-based width for good proportions
  • Dark backdrop overlay
  • Smooth scale/opacity animation on open/close
  • Works in light and dark themes automatically

Tabs

Pure CSS tabs using `<details>` and `<summary>` with CSS Grid and Subgrid.

When to use: CSS-only tabbed content using details/summary.

Classes: .tabs, .tabs.boxed, .tabs.pill

Reference Notes

No JavaScript required.

How It Works
  • The name attribute on <details> ensures only one tab can be open at a time (native HTML behavior)
  • --n CSS variable positions each tab's summary in the correct grid column
  • --tab-count on the container sets the number of columns (default: 3)
Important Notes
  1. Unique names: Each tab group needs a unique name attribute
  2. Open one by default: Add open to the tab that should be visible initially
  3. Sequential --n values: Must match the visual order (1, 2, 3, etc.)
  4. Match --tab-count: If you have more than 3 tabs, set --tab-count to match
Styling Details
  • Uses CSS Grid with Subgrid for alignment
  • Smooth opacity transitions with @starting-style
  • Works in light and dark modes
  • Keyboard accessible (native details/summary behavior)
  • Active tab has underline indicator (default), card connection (boxed), or sliding thumb segment (pill)

Tooltip

Hover tooltips using CSS anchor positioning. No JavaScript required.

When to use: Popover-based tooltips with CSS anchor positioning.

Classes: .tooltip, .tooltip-content, .bottom, .left, .right

Reference Notes
Styling Details
  • Uses CSS anchor positioning (anchor-scope, position-anchor, position-area)
  • Background: var(--bg)
  • Border: var(--border-1)
  • Shadow: var(--shadow-3)
  • Max width: 30ch (wraps longer text)
  • Shows on :hover and :focus-within
  • Smooth opacity transition
Accessibility
  • Triggers work with focus (:focus-within) for keyboard users
  • Use buttons or links as triggers when possible
  • Tooltip content is always in DOM (just hidden via opacity)

List Navigation

Navigation list with clickable rows for settings pages, menus, and navigation indexes.

When to use: Grouped list-style navigation rows.

Classes: .list-nav

Reference Notes

Each item is a pill-shaped card with subtle shadow.

Use Cases
  • Settings pages
  • Mobile app menus
  • Feature indexes
  • Dashboard navigation
  • Account/profile menus
Item Structure

Items are direct <a> or <button> children of .list-nav. No additional classes needed.

Each item can contain:

  1. <svg> - Icon on the left (sized to 1.25em)
  2. Text - Title text
  3. <small> - Optional description (muted, smaller text)
Styling

Each item automatically gets:

  • Pill radius - var(--br-xxl) rounded corners
  • Subtle shadow - var(--shadow-2) for elevated card appearance
  • Background - Uses var(--bg) for proper theming
  • Gap - var(--pad-m) spacing between items
  • Dark mode - Items pick up a var(--border-1) border for definition
States
  • Default: Pill-shaped card with subtle shadow
  • Hover: Background highlight, icon brightens
  • Focus: Focus ring with inset offset
  • Active: Slightly darker background
  • Disabled: 65% opacity, no pointer events
Differences from Sidebar Nav
FeatureList NavSidebar Nav
Item styleIndividual cards with shadowFlat items in a list
DescriptionSupported via <small>Not supported
Use caseStandalone navigation rowsSticky sidebar menus
NestingNot supportedSupports details/summary
Visual weightHigher (cards with shadow)Lower (compact)
Accessibility
  • Use semantic <nav> container
  • Use <a> for navigation links, <button> for actions
  • Disabled buttons use disabled attribute
  • Links can use aria-disabled="true" when needed
  • Focus states are clearly visible

Toggle Switch

Accessible toggle/switch input using native checkbox.

When to use: Boolean settings with checkbox semantics.

Classes: .toggle, .compact

Reference Notes

<span id="forms">

CSS Variables
  • --toggle-color - Color when checked (default: var(--primary))
  • --toggle-width - Track width
  • --toggle-height - Track height
Accessibility

The toggle uses a native checkbox, so it:

  • Works with keyboard (Space to toggle)
  • Announces state to screen readers
  • Respects prefers-reduced-motion

Input Group

Input field with connected button.

When to use: Input plus attached action button patterns.

Classes: .input-group, .input-group.stack-mobile

Reference Notes

Use for copy-to-clipboard, search with button, URL sharing, or any input that needs an action.

Stack on Mobile

Add .stack-mobile to break the group into a vertical stack below 640px. Each child reclaims a full border-radius so the input and button read as separate controls.

Styling Details
  • Input stretches to fill available space
  • Button stays sized to content
  • Connected with no gap, shared border-radius
  • Works with all button variants (.primary, .ghost, etc.)

Search input with icon positioned inside.

When to use: Search field with icon and compact action behavior.

Classes: .search

Reference Notes
Icon Source

Get icons from Phosphor Icons. The magnifying glass icon shown above is the "MagnifyingGlass" icon.

Styling Details
  • Icon is absolutely positioned, vertically centered
  • Input has left padding to accommodate the icon
  • Icon is muted (var(--fg-3)) so the user's typed text reads as the primary content

File Dropzone

Drag-and-drop file upload zone with click-to-upload fallback.

When to use: Drag-and-drop upload zones with native fallback.

Classes: .dropzone, .dragover

Reference Notes
Icon Source

Get icons from Phosphor Icons. The upload icon shown above is the "UploadSimple" icon.

Styling Details
  • Padding: var(--pad-xxxl), radius: var(--br-l) (hardcoded; theme by editing the source or wrapping)
  • Dashed border (2px dashed var(--fg-2))
  • Centered flex layout for icon and text
  • File input is hidden but covers entire area
  • Click anywhere to trigger file picker
  • .dragover state swaps border and icon colour to var(--accent)