Graffiti

github

UI Blocks

Composed multi-element patterns.

Larger composed patterns built from Graffiti primitives. Navigation, dialogs, forms, and app shells.

Accordion

Native HTML disclosure element with smooth animations using `<details>` and `<summary>`.

When to use: Expandable sections for FAQ and settings content.

Classes: details, .bordered, .right, .minimal

Reference Notes
Styling Details
  • Smooth height animation using @starting-style and allow-discrete
  • Custom arrow indicator that rotates on open
  • Proper focus-visible states
  • No JavaScript required
Exclusive Accordion (One Open at a Time)

Use the name attribute to group accordions:

Only one in the group can be open at a time.

Simple breadcrumb navigation with customizable separators.

When to use: Hierarchy path navigation inside app and docs views.

Classes: .breadcrumbs

Reference Notes
Custom Separator

Default separator is /. Change with --separator:

Other separator ideas: , , », |, ·

Current Page

Mark the current page with aria-current="page" on the <li>:

This removes the link styling and shows it as plain text.

Styling Details
  • Flexbox layout with wrapping
  • Separators via ::before pseudo-elements
  • Links are muted (--fg-5) and brighten on hover (--fg-7)
  • Proper focus-visible states
Accessibility
  • Use aria-label="Breadcrumb" on the <nav>
  • Use aria-current="page" on the current page <li>
  • Use semantic <nav>, <ul>, <li> structure

Pagination

Card footer style pagination with previous/next controls and current page state.

When to use: Paginated result navigation with previous and next controls.

Classes: .pagination

Reference Notes
Styling Details
  • .pagination handles layout only (flex, gap, border-top)
  • Buttons use existing .button.ghost styles
  • Page number buttons in ul get fixed 2rem square sizing
  • Current page uses primary border and stronger text
  • Border-top and padding give card footer feel
Accessibility
  • Use aria-label="Pagination" on the <nav>
  • Use aria-current="page" on the active page link
  • Use aria-disabled="true" for disabled controls

Native dropdown menu using HTML popover API and CSS anchor positioning.

When to use: Action menus using popover and anchor positioning.

Classes: .dropdown, .dropdown-menu, .dropdown-header, .end

Reference Notes

No JavaScript required for open/close.

How It Works
  • popovertarget on the button points to the menu's id
  • popover attribute enables native popover behavior
  • .dropdown-menu provides styling and positioning
  • Clicking outside automatically closes the menu

Vertical navigation for app sidebars with collapsible sections.

When to use: Sectioned app navigation with collapsible groups.

Classes: .sidebar-nav, .sidebar-nav.compact, .sub

Reference Notes

Uses native <details>/<summary> for expand/collapse.

With Icons

Icons are automatically sized to 20px (customizable via --sidebar-nav-icon-size).

CSS Variables
  • --sidebar-nav-icon-size - Icon size (default: 20px)
  • --sidebar-nav-indent - Indentation for nested items (default: 1.5rem)
Compact Variant

Use .compact when a sidebar needs denser rows:

Compact mode keeps focus and hover behavior, while reducing row padding and icon size.

App Shell Pattern (Dashboard / Settings)

Combine with .layout-sidebar.fill for the canonical shell:

Notes:

  • .layout-sidebar.fill is app-shell-oriented by default (--layout-gap: 0, height: 100dvh)
  • Fill children auto-scroll when they are not .app-shell
  • If a fill child is .app-shell, overflow is not forced there, avoiding double-scroll
Fixed Sidebar Pattern

Use this when navigation should remain visible while main content scrolls.

Mobile Patterns

iOS and Android-friendly CSS patterns for PWAs and native-like web apps.

When to use: App shell, bottom nav, bottom sheet, safe areas.

Classes: .app-shell, .bottom-nav, .bottom-sheet, .safe-top, .safe-bottom, .safe-x, .hide-scrollbar, .momentum-scroll

Reference Notes
Safe Area Variables

Graffiti provides CSS variables for iOS safe areas (notch, home indicator, status bar):

These use env() which returns the actual safe area on iOS/Android, or 0px on desktop.

App Shell

Grid-based container that avoids the iOS URL bar 100vh bug:

Features:

  • Uses 100dvh (dynamic viewport height) to avoid iOS URL bar issues
  • Sticky header with blur backdrop
  • Main content scrolls independently
  • Respects safe areas automatically
  • Better nested scroll resilience (min-block-size: 0 on shell and direct regions)

Behavior:

  • .layout-sidebar.fill now acts as an app-shell frame by default (--layout-gap: 0, height: 100dvh)
  • Non-.app-shell first/second children auto-scroll on larger layouts
  • If a fill child is .app-shell, overflow is not forced on that child; its main handles scrolling
Bottom Navigation

Fixed tab bar for mobile apps:

Features:

  • Fixed at bottom of viewport
  • Respects --safe-bottom for home indicator
  • Active state via aria-current="page" or .active class
  • Icon + label pattern
  • Automatic light/dark theming
Bottom Sheet

Drawer that slides up from bottom:

Features:

  • Rounded top corners
  • Automatic drag handle visual at top
  • Respects --safe-bottom
  • Max height 80dvh to allow dismissal

For interactive open/close, wrap in <dialog> or use with popover API.

Important: Viewport Meta Tag

For safe areas to work, include viewport-fit=cover:

Without this, safe area insets may not be reported correctly on iOS.

Full-width site header with navigation.

When to use: Page-level site/app top navigation bar.

Classes: .header, .border, .sticky

Reference Notes
Styling Details
  • Full viewport width
  • Flexbox with space-between
  • Nav <ul> styled as horizontal flex list
  • All direct children have margin reset

Site footer with navigation columns, copyright, and legal links.

When to use: Site footer with grouped navigation and legal links.

Classes: .footer

Reference Notes

Uses container queries for responsive behavior.

Required Classes
  • .footer - Container with container-type: inline-size, removes link underlines (shows on hover)
  • .grid.auto - Responsive auto-fit grid for nav columns
Key Features
  1. Container queries - .footer sets container-type: inline-size so child layouts respond to footer width, not viewport
  2. No underlines - Links have no text-decoration by default, underline appears on hover
  3. Responsive grid - .grid.auto uses auto-fit with --grid-min variable for responsive columns
  4. Layout stacking - .layout-sidebar and .split automatically stack in narrow containers
CSS Variables
  • --grid-min - Minimum column width for .grid.auto (default: 150px, recommended: 120px for footer)
Responsive Behavior

The footer uses container queries, not media queries:

  • When footer container is narrow, .layout-sidebar stacks vertically
  • .grid.auto columns wrap based on --grid-min
  • .split stacks when container is < 500px
Utility Classes Used
  • .box - Adds padding and border
  • .stack - Vertical spacing between children
  • .cluster - Horizontal wrapping layout
  • .split - Space-between horizontal layout
  • .layout-sidebar - Two-column layout (stacks in narrow containers)
  • .grid.auto - Auto-fit responsive grid

Swipe

Horizontal swipe-to-reveal component using CSS scroll-snap.

When to use: Swipe-to-reveal row actions for touch interactions.

Classes: .swipe, .stop

Reference Notes

Reveal action buttons by swiping left or right.

Structure
  • First child = left action
  • Second child = main visible content
  • Third child = right action
Styling Details
  • CSS scroll-snap for smooth snapping
  • Three-column layout
  • Hidden scrollbar
  • Container query for center content width

User Menu

User account dropdown combining avatar trigger with dropdown menu.

When to use: Avatar trigger plus account actions dropdown.

Classes: .avatar, .dropdown

Reference Notes

Works with both image and initials avatars.

Key Classes
  • .dropdown.end - Aligns menu to right edge of avatar
  • .avatar - Circular avatar styling on the button
  • .dropdown-menu - Menu styling
  • .dropdown-header - User name display in menu

Confirm Dialog

A confirmation dialog pattern using native `<dialog>` with title, message, and action buttons.

When to use: Destructive or important confirmations using native dialog.

Classes: dialog, .close

Reference Notes
Key Classes Used
  • .close - Circular close button (positioned top-right automatically)
  • .stack - Vertical layout for dialog content
  • .cluster - Horizontal layout for action buttons
  • .h4 - Heading style without using an actual heading element
  • .primary - Primary action button
How It Works
  • Uses HTML invokers (commandfor/command) - no JavaScript for open/close
  • .stack provides consistent vertical spacing between title, message, and buttons
  • .cluster with justify-content: flex-end aligns buttons to the right
  • Both Cancel and Confirm close the dialog; add your own JS for the confirm action

Timeline

Activity feeds, step indicators, and progress tracking with status variants and glow effects.

When to use: Progress steps and chronological activity flows.

Classes: .timeline, .horizontal, .active, .completed, .success, .warning, .error, .info

Reference Notes
State Classes
ClassUseVisual
.activeCurrent step in progressBold filled marker with ring
.completedFinished stepGreen filled marker with glow
.successSuccess statusGreen-tinted background with glow
.warningWarning statusYellow-tinted background with glow
.errorError statusRed-tinted background with glow
.infoInfo statusBlue-tinted background with glow
Marker Content

The .marker element can contain:

  • Text/numbers: <span class="marker">1</span>
  • SVG icons: <span class="marker"><svg>...</svg></span>
  • Emoji: <span class="marker">✓</span>

SVG icons are automatically sized to 1.125rem (18px).

Visual Features
  • Shadows: Multi-layered box-shadow for depth
  • Inner highlight: Subtle top highlight for 3D effect
  • Borders: Colored borders matching status variants
  • Glow rings: 0 0 0 3px spread shadow for colored halo effect
  • Gradients: Completed markers have gradient fill (lighter top, darker bottom)
Use Cases
  • Activity feeds: PR activity, commit history, user actions
  • Changelogs: Version history with status indicators
  • Steppers: Multi-step forms, checkout flow, onboarding
  • Build logs: CI/CD pipeline status
  • Notifications: Action history with status

Login Form

A complete login form using Graffiti's base form styles.

When to use: Email/password auth form with native control styling.

Classes: form, .stack

Reference Notes

No custom CSS needed.

Key Classes Used
  • .stack - Vertical layout with consistent spacing
  • .split - Flexbox with space-between (checkbox left, link right)
  • .primary - Primary action button style
Notes
  • Form inputs are styled automatically (no classes needed on inputs)
  • Labels automatically get proper spacing
  • Use .error, .success, .warning classes on inputs for validation states
  • Add <small class="error">Message</small> after inputs for validation messages

Forms

All form inputs are styled automatically. No classes needed on inputs.

When to use: Field rows and form actions.

Classes: .row, .form-option-row, .form-actions

Reference Notes
Option Row Labels

Use .form-option-row on checkbox/radio labels to align controls and text with consistent spacing.

Field Rows

Use .row as a field wrapper to group label + control + help text.

Notes:

  • Outside forms, .row keeps its spacing utility behavior (margin-block).
  • Inside forms and fieldsets, .row becomes a compact field-group wrapper.
Form Actions

Use .form-actions for submit/cancel rows.

Behavior:

  • End-aligned action row on larger containers
  • Wrap-friendly spacing for longer labels
  • Stacks action controls full-width in narrow containers