Skip to components

Interfaces,
reduced.

Useful components.
Thoughtful defaults.
Source you own.

Explore the collection

Copy command

One command. One click. Clear feedback when it works, a useful fallback when it doesn’t.

WLKR original
pnpm dlx shadcn@latest add @wlkr/copy-command
pnpm dlx shadcn@latest add @wlkr/copy-command
View source
"use client"

import * as React from "react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

const styles = `
.wlkr-copy-icon{width:16px;height:16px;overflow:visible}
.wlkr-copy-icon g{transform-origin:12px 12px;transition:opacity 180ms ease,transform 180ms ease}
.wlkr-copy-icon g[data-visible=false]{opacity:0;transform:scale(.65) rotate(-12deg)}
.wlkr-copy-icon g[data-visible=true]{opacity:1;transform:scale(1) rotate(0)}
.wlkr-copy-success{color:var(--success,#087d3d)}
.dark .wlkr-copy-success{color:var(--success,#33f282)}
.wlkr-copy-error{color:var(--destructive,#b42318)}
@media(prefers-reduced-motion:reduce){.wlkr-copy-icon g{transition:none}}
`

/** A fixed-slot copy control. Feedback is visual in the icon and announced off-screen. */
export function CopyCommand({ command, className }: { command: string; className?: string }) {
  const [state, setState] = React.useState<"idle" | "copying" | "copied" | "error">("idle")
  const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null)
  const mounted = React.useRef(true)
  const locked = React.useRef(false)
  React.useEffect(() => { mounted.current = true; return () => { mounted.current = false; if (timer.current) clearTimeout(timer.current) } }, [])
  async function copy() {
    if (locked.current) return
    locked.current = true
    if (timer.current) clearTimeout(timer.current)
    setState("copying")
    try {
      await navigator.clipboard.writeText(command)
      if (mounted.current) {
        setState("copied")
        timer.current = setTimeout(() => setState("idle"), 2200)
      }
    } catch { if (mounted.current) setState("error") }
    finally { locked.current = false }
  }
  return (
    <div className={cn("relative flex min-w-0 items-center gap-4", className)}>
      <style>{styles}</style>
      <code className="min-w-0 flex-1 overflow-x-auto py-3 text-sm whitespace-pre">{command}</code>
      <Button type="button" variant="ghost" size="icon" onClick={copy} disabled={state === "copying"} style={{ width: 44, height: 44, flexShrink: 0, padding: 0 }} aria-label={state === "copied" ? "Command copied; copy again" : state === "error" ? "Copy failed; try again" : "Copy command"}>
        <svg className="wlkr-copy-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <g data-visible={state === "idle" || state === "copying"}><rect x="8" y="8" width="12" height="12" rx="2" /><path d="M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" /></g>
          <g data-visible={state === "copied"} className="wlkr-copy-success"><path d="m5 12 4 4L19 6" /></g>
          <g data-visible={state === "error"} className="wlkr-copy-error"><path d="m6 6 12 12M18 6 6 18" /></g>
        </svg>
      </Button>
      <span role="status" className="sr-only">{state === "error" ? "Clipboard unavailable. Select and copy the command, or try again." : state === "copied" ? "Copied to clipboard." : ""}</span>
    </div>
  )
}
Registry JSON

Async action

An action that follows through. Pending, success, and a second chance when something goes wrong.

WLKR original
pnpm dlx shadcn@latest add @wlkr/async-action
View source
"use client"

import * as React from "react"
import { ArrowUpRight, Check, X } from "lucide-react"
import { Button } from "@/components/ui/button"

const styles = `
.wlkr-action{display:inline-grid!important;grid-template-areas:'state';height:44px;min-height:44px;max-width:100%;padding:0 12px!important}
.wlkr-action-state{grid-area:state;display:inline-flex;align-items:center;justify-content:flex-start;gap:10px;min-height:24px;white-space:normal;text-align:left}
.wlkr-action-state[aria-hidden=true]{visibility:hidden;pointer-events:none}
.wlkr-action-state svg{width:16px;height:16px;flex-shrink:0}
.wlkr-action-success{color:var(--success,#087d3d)}
.dark .wlkr-action-success{color:var(--success,#33f282)}
.wlkr-action-error{color:var(--destructive,#b42318)}
.wlkr-action[aria-busy=true]{opacity:1!important}
.wlkr-action-pending-label{position:relative;display:inline-block}
.wlkr-action-pending-base{opacity:.72}
.wlkr-action-pending-pixels{position:absolute;inset:0;pointer-events:none;mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Crect width='3' height='3' fill='white'/%3E%3Crect x='4' y='4' width='3' height='3' fill='white'/%3E%3C/svg%3E");mask-size:8px 8px;mask-repeat:repeat}
@keyframes wlkr-action-reconstruct{0%,100%{mask-position:0 0;opacity:.35}50%{mask-position:4px 4px;opacity:1}}
@media(prefers-reduced-motion:no-preference){.wlkr-action-pending-pixels{animation:wlkr-action-reconstruct 2800ms ease-in-out infinite}}
@media(prefers-reduced-motion:reduce){.wlkr-action-pending-base{opacity:1}.wlkr-action-pending-pixels{display:none}}

`

/** One promise at a time. All labels share a reserved slot, so feedback never moves the page. */
export function AsyncAction({ action, children, pendingLabel = "Saving…", successLabel = "Saved", errorLabel = "Could not save. Try again.", failureLabel = "Try again", disabled = false }: {
  action: () => Promise<unknown>
  children: React.ReactNode
  pendingLabel?: string
  successLabel?: string
  errorLabel?: string
  failureLabel?: string
  disabled?: boolean
}) {
  const [state, setState] = React.useState<"idle" | "pending" | "success" | "error">("idle")
  const locked = React.useRef(false)
  const mounted = React.useRef(true)
  React.useEffect(() => { mounted.current = true; return () => { mounted.current = false } }, [])
  async function run() {
    if (locked.current) return
    locked.current = true
    setState("pending")
    try { await action(); if (mounted.current) setState("success") }
    catch { if (mounted.current) setState("error") }
    finally { locked.current = false }
  }
  return (
    <span className="relative inline-flex max-w-full align-middle">
      <style>{styles}</style>
      <Button className="wlkr-action" type="button" variant="ghost" onClick={run} disabled={disabled || state === "pending"} aria-busy={state === "pending"}>
        <span className="wlkr-action-state" aria-hidden={state !== "idle"}><ArrowUpRight aria-hidden="true" />{children}</span>
        <span className="wlkr-action-state" aria-hidden={state !== "pending"}><ArrowUpRight aria-hidden="true" style={{visibility:"hidden"}} /><span className="wlkr-action-pending-label"><span className="wlkr-action-pending-base">{pendingLabel}</span><span className="wlkr-action-pending-pixels" aria-hidden="true">{pendingLabel}</span></span></span>
        <span className="wlkr-action-state" aria-hidden={state !== "success"}><Check className="wlkr-action-success" aria-hidden="true" />{successLabel}</span>
        <span className="wlkr-action-state" aria-hidden={state !== "error"}><X className="wlkr-action-error" aria-hidden="true" />{failureLabel}</span>
      </Button>
      <span role="status" className="sr-only">{state === "error" ? errorLabel : state === "success" ? successLabel : state === "pending" ? pendingLabel : ""}</span>
    </span>
  )
}
Registry JSON

Filter list

Find the thing you came for. A small, accessible search for lists that don’t need a whole search engine.

WLKR original

3 results

  • Brand Studio

    The source of truth for a considered identity.

  • WeatherGoblin

    A little character in your daily forecast.

  • PatriBible

    Scripture, thoughtfully within reach.

pnpm dlx shadcn@latest add @wlkr/filter-list
View source
"use client"

import * as React from "react"
import { Search, X } from "lucide-react"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"

export type FilterListItem = { id: string; label: string; description?: string; keywords?: string[] }

/** A small searchable list. Matching ignores case and accents, with a live result count. */
export function FilterList({ items, label = "Search items", placeholder = "Find something…" }: { items: FilterListItem[]; label?: string; placeholder?: string }) {
  const [query, setQuery] = React.useState("")
  const id = React.useId()
  const input = React.useRef<HTMLInputElement>(null)
  const normalize = (value: string) => value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLocaleLowerCase()
  const words = normalize(query).trim().split(/\s+/).filter(Boolean)
  const results = items.filter(item => words.every(word => normalize([item.label, item.description, ...(item.keywords ?? [])].join(" ")).includes(word)))
  return (
    <div className="flex w-full flex-col gap-5">
      <style>{`
.wlkr-filter-search{position:relative}
.wlkr-filter-search:focus-within>svg{color:var(--foreground,currentColor)}
.wlkr-filter-search:focus-within::after{content:"";position:absolute;left:0;bottom:1px;width:16px;height:2px;background:var(--foreground,currentColor)}
.wlkr-filter-input[data-slot=input]:focus-visible{outline:none;box-shadow:none;border-color:transparent}
.wlkr-filter-input[data-slot=input]{border:0;box-shadow:none;background:none;border-radius:0}
.wlkr-filter-input{caret-color:var(--foreground,currentColor)}
@supports(caret-shape:block){.wlkr-filter-input{caret-shape:block}}
      `}</style>
      <div className="wlkr-filter-search flex items-center gap-3">
        <Search className="size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
        <label htmlFor={id} className="sr-only">{label}</label>
        <Input className="wlkr-filter-input" ref={input} id={id} type="search" placeholder={placeholder} value={query} onChange={event => setQuery(event.target.value)} aria-describedby={`${id}-count`} />
        {query && <Button type="button" variant="ghost" size="icon" aria-label="Clear search" onClick={() => { setQuery(""); input.current?.focus() }}><X /></Button>}
      </div>
      <p id={`${id}-count`} role="status" className="text-xs text-muted-foreground">{results.length} {results.length === 1 ? "result" : "results"}</p>
      <ul className="flex flex-col gap-5">
        {results.map(item => <li key={item.id}><p className="text-sm font-medium">{item.label}</p>{item.description && <p className="mt-1 text-sm text-muted-foreground">{item.description}</p>}</li>)}
      </ul>
      {results.length === 0 && <p className="text-sm text-muted-foreground">No matches. Try another search or clear the filter.</p>}
    </div>
  )
}
Registry JSON

Pixel object

Give a shape a little life. Project a point cloud or a simple mask into a quiet field of pixels.

WLKR original
Object is still.
pnpm dlx shadcn@latest add @wlkr/pixel-object
View source
"use client"

import * as React from "react"
import { Pause, Play } from "lucide-react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

export type PixelPoint = { x: number; y: number; z?: number }

/** A sphere point cloud; pass your own points or a #/space mask to display an object. */
export function spherePoints(count = 420): PixelPoint[] {
  const total = Math.max(12, Math.min(2000, Math.round(count)))
  return Array.from({ length: total }, (_, index) => {
    const y = 1 - (index / (total - 1)) * 2
    const radius = Math.sqrt(1 - y * y)
    const angle = index * Math.PI * (3 - Math.sqrt(5))
    return { x: Math.cos(angle) * radius, y, z: Math.sin(angle) * radius }
  })
}
const sphere = spherePoints()
const styles = `
.wlkr-pixel-object{display:flex;flex-direction:column;align-items:center;gap:12px;max-width:100%}
.wlkr-pixel-object svg{display:block;width:100%;height:auto;max-width:320px;overflow:visible}
.wlkr-pixel-toggle{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-width:44px;min-height:44px;padding:0 12px;cursor:pointer}
.wlkr-pixel-toggle svg{width:14px;height:14px}
.wlkr-pixel-toggle:focus-visible{outline:2px solid var(--ring,currentColor);outline-offset:4px}
.wlkr-pixel-note{font-size:12px;color:var(--muted-foreground,#696969)}
`

/**
 * A projected pixel object, not a video or canvas. Coordinates are normalized to [-1, 1].
 * A mask uses # for filled pixels and spaces/dots for empty pixels; rows need not be equal.
 * Motion pauses off-screen, in hidden tabs, on user pause, and for reduced motion.
 */
export function PixelObject({ points, mask, resolution = 36, size = 260, label = "Rotating pixel sphere", rotating = true, className }: {
  points?: PixelPoint[]
  mask?: string[]
  resolution?: number
  size?: number
  label?: string
  rotating?: boolean
  className?: string
}) {
  const root = React.useRef<HTMLDivElement>(null)
  const angle = React.useRef(0)
  const [rotation, setRotation] = React.useState(0)
  const [paused, setPaused] = React.useState(false)
  const [motion, setMotion] = React.useState(false)
  const grid = Math.max(8, Math.min(80, Math.round(Number.isFinite(resolution) ? resolution : 36)))
  const dimension = Math.max(64, Math.min(1000, Number.isFinite(size) ? size : 260))
  const cloud = React.useMemo(() => {
    if (mask) {
      const rows = mask.slice(0, 80)
      const width = Math.max(1, ...rows.map(row => row.slice(0, 80).length))
      return rows.flatMap((row, y) => [...row.slice(0, 80)].flatMap((pixel, x) => pixel === "#" ? [{ x: width === 1 ? 0 : (x / (width - 1)) * 2 - 1, y: rows.length === 1 ? 0 : 1 - (y / (rows.length - 1)) * 2, z: 0 }] : []))
    }
    return (points ?? sphere).filter(point => Number.isFinite(point.x) && Number.isFinite(point.y) && Number.isFinite(point.z ?? 0)).slice(0, 2000)
  }, [points, mask])

  React.useEffect(() => {
    const preference = window.matchMedia("(prefers-reduced-motion: reduce)")
    let intersecting = false
    let frame = 0
    let last = 0
    let drawn = 0
    function stop() { if (frame) cancelAnimationFrame(frame); frame = 0; last = 0 }
    function tick(time: number) {
      const elapsed = last ? Math.min(time - last, 80) : 0
      last = time
      angle.current += elapsed * .00015
      // Slow rotation sampled at 12 fps keeps the discrete pixels deliberate.
      if (time - drawn >= 80) { setRotation(angle.current); drawn = time }
      frame = requestAnimationFrame(tick)
    }
    function sync() {
      const active = rotating && !paused && !preference.matches && intersecting && !document.hidden
      setMotion(active)
      if (active && !frame) frame = requestAnimationFrame(tick)
      if (!active) stop()
    }
    const observer = new IntersectionObserver(entries => { intersecting = entries[0]?.isIntersecting ?? false; sync() }, { threshold: .05 })
    if (root.current) observer.observe(root.current)
    preference.addEventListener("change", sync)
    document.addEventListener("visibilitychange", sync)
    sync()
    return () => { stop(); observer.disconnect(); preference.removeEventListener("change", sync); document.removeEventListener("visibilitychange", sync) }
  }, [rotating, paused])

  const pixels = new Map<string, { x: number; y: number; opacity: number }>()
  const cosine = Math.cos(rotation)
  const sine = Math.sin(rotation)
  for (const point of cloud) {
    const z = point.z ?? 0
    const x = point.x * cosine + z * sine
    const depth = -point.x * sine + z * cosine
    const column = Math.round((x * .43 + .5) * (grid - 1))
    const row = Math.round((-point.y * .43 + .5) * (grid - 1))
    if (column < 0 || column >= grid || row < 0 || row >= grid) continue
    const opacity = .22 + Math.max(0, Math.min(1, (depth + 1) / 2)) * .73
    const key = `${column},${row}`
    if (!pixels.has(key) || pixels.get(key)!.opacity < opacity) pixels.set(key, {x:column,y:row,opacity})
  }
  return <div ref={root} className={cn("wlkr-pixel-object", className)} style={{width:dimension}} data-motion={motion ? "running" : "paused"}>
    <style>{styles}</style>
    <svg viewBox={`0 0 ${grid} ${grid}`} role="img" aria-label={label}>
      {[...pixels.entries()].map(([key, pixel]) => <rect key={key} x={pixel.x} y={pixel.y} width=".72" height=".72" rx=".06" fill="currentColor" opacity={pixel.opacity} />)}
    </svg>
    {rotating && <Button type="button" className="wlkr-pixel-toggle" variant="ghost" aria-label={paused ? "Resume object rotation" : "Pause object rotation"} aria-pressed={paused} onClick={() => setPaused(value => !value)}>{paused ? <Play aria-hidden="true" /> : <Pause aria-hidden="true" />}<span>{paused ? "Resume" : "Pause"}</span></Button>}
    <span role="status" className="sr-only">{motion ? "Object rotation active." : "Object is still."}</span>
  </div>
}
Registry JSON

Primitives

Good foundations, straight from shadcn/ui. We use the originals, with WLKR’s shared design tokens.

shadcn/ui
pnpm dlx shadcn@latest add button input switch

Installed directly from shadcn/ui. Upstream source is unchanged.

Foundations

A common language for what comes next. Semantic color, considered type, and space to think.

WLKR system
Canvas
Ink
Quiet
WLKR green

Meaning before appearance.

Canvas, foreground, muted, and accent describe a purpose. Their values change with the light. Green marks a useful moment; it never has to carry the message alone.

One typeface. Room to breathe.

Geist. A compact scale. Tight display typography and comfortable reading lengths. Focus stays visible. Motion follows interaction and respects reduced-motion preferences.

pnpm dlx shadcn@latest add @wlkr/theme
Token reference
--background     Canvas
--foreground     Primary text
--muted-foreground  Supporting text
--primary        Primary action
--success        Successful completion
--destructive    Failure feedback
--ring           Keyboard focus
--brand          #33F282
--brand-foreground #010101

Apply .dark to the document root for dark mode.
The theme leaves your app’s typography unchanged.

Make it yours.

Copy the source into your project with the shadcn CLI. Keep what you need. Change what you like. No component runtime to subscribe to.

1. Start with shadcn

pnpm dlx shadcn@latest init

Use an existing React, TypeScript, and Tailwind project. Already configured? Continue below.

2. Add the namespace

Merge this entry into the registries field of your project’s components.json.

"registries": {
  "@wlkr": "https://ui.wlkrlabs.com/r/{name}.json"
}

3. Bring a component home

pnpm dlx shadcn@latest add @wlkr/copy-command

Originals install to components/wlkr. Their stock primitives come directly from shadcn. The optional theme adds light and dark color tokens.

Using it at wlkrlabs.com

The current website uses Astro. Future adoption first needs the official @astrojs/react integration and compatible Tailwind/shadcn setup. Import these React components into an Astro page with a client:load directive for interactive islands. Review generated source and token conflicts in a separate website change; the existing site stays unchanged here.

Built with shadcn/ui, Radix, Lucide, and Geist. Based on the official registry starter. WLKR originals are MIT licensed.