boneyard

Use boneyard-js to add, configure, debug, or rebuild skeleton screens. Triggers when working with Skeleton components, bones JSON, the boneyard CLI, fixtures, leafTags, snapshotConfig, skeleton loading states, or boneyard.config.json.

install
source · Clone the upstream repo
git clone https://github.com/0xGF/boneyard
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/0xGF/boneyard "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/boneyard" ~/.claude/skills/0xgf-boneyard-boneyard && rm -rf "$T"
manifest: .claude/skills/boneyard/SKILL.md
source content

Boneyard Skeleton Skill

You are an expert on

boneyard-js
, a skeleton screen generator that snapshots real UI into positioned rectangle "bones". Use this knowledge to help with any boneyard-related task.

Architecture

Core files (in
packages/boneyard/
)

  • src/react.tsx
    <Skeleton>
    React component (also exports
    configureBoneyard
    ,
    registerBones
    )
  • src/preact.tsx
    — Native Preact integration (no compat needed)
  • src/Skeleton.svelte
    — Svelte 5 component
  • src/Skeleton.vue
    — Vue component
  • src/angular.ts
    — Angular component
  • src/native.tsx
    /
    src/react-native.tsx
    — React Native
  • src/extract.ts
    snapshotBones()
    DOM walker,
    fromElement()
    descriptor extractor
  • src/shared.ts
    — bone registry, animation constants (
    SHIMMER
    ,
    PULSE
    ,
    DEFAULTS
    ),
    resolveResponsive
  • src/types.ts
    SnapshotConfig
    ,
    Bone
    ,
    CompactBone
    ,
    ResponsiveBones
  • src/runtime.ts
    — vanilla
    renderBones()
    for non-React usage
  • bin/cli.js
    — CLI entry (
    boneyard-js build
    )

Package exports

  • boneyard-js
    — snapshotBones, renderBones, fromElement
  • boneyard-js/react
    — Skeleton, registerBones, configureBoneyard
  • boneyard-js/preact
    — Skeleton, registerBones, configureBoneyard
  • boneyard-js/native
    — Skeleton, registerBones, configureBoneyard (React Native)
  • boneyard-js/svelte
    — Skeleton component, registerBones
  • boneyard-js/vue
    — Skeleton component, registerBones, configureBoneyard
  • boneyard-js/angular
    — SkeletonComponent, registerBones, configureBoneyard
  • boneyard-js/vite
    — boneyardPlugin() Vite plugin

Bones format

Compact array:

[x%, y_px, w%, h_px, borderRadius, isContainer?]

  • x
    and
    w
    are percentages of container width
  • y
    and
    h
    are pixels
  • borderRadius
    is number (px) or string ("50%")
  • isContainer
    (optional 6th element, truthy) — container bones are skipped during rendering. They represent parent backgrounds and would cause opacity overlap if rendered alongside child bones.

Bone resolution priority

  1. Explicit
    initialBones
    prop (highest priority)
  2. Registry lookup by
    name
    (from
    registry.js
    )
  3. Fixture fallback (only during CLI build mode when
    window.__BONEYARD_BUILD === true
    )

Animation constants (in
shared.ts
)

All frameworks import from

shared.ts
— single source of truth:

SHIMMER = { angle: 110, start: 30, end: 70, speed: '2s', lightHighlight: '#f7f7f7', darkHighlight: '#2c2c2c' }
PULSE = { speed: '1.8s', lightAdjust: 0.3, darkAdjust: 0.02 }
DEFAULTS = { web: { light: '#f0f0f0', dark: '#222222' }, native: { light: '#f0f0f0', dark: '#222222' } }

Dark mode

Detected via

.dark
class on
<html>
or any parent element (standard Tailwind convention). Does NOT use
prefers-color-scheme
— gives the app developer explicit control. When
.dark
is present,
darkColor
and
darkShimmerColor
are used.

SnapshotConfig options

{
  leafTags?: string[]              // Tags treated as atomic bones (merged with defaults: p,h1-h6,li,td,th)
  captureRoundedBorders?: boolean  // Capture bordered+rounded elements even without bg (default: true)
  excludeTags?: string[]           // Skip these tags entirely
  excludeSelectors?: string[]      // Skip elements matching CSS selectors
}

Config file (
boneyard.config.json
)

The primary customization point. Controls both CLI build and runtime defaults. Runtime options are baked into the generated

registry.js
via
configureBoneyard()
.

{
  "breakpoints": [375, 768, 1280],
  "out": "./src/bones",
  "wait": 800,
  "color": "#e5e5e5",
  "darkColor": "#2a2a2a",
  "animate": "shimmer",
  "shimmerColor": "#ebebeb",
  "darkShimmerColor": "#333333",
  "speed": "2s",
  "shimmerAngle": 110,
  "stagger": false,
  "transition": false,
  "boneClass": "",
  "resolveEnvVars": true,
  "auth": {
    "cookies": [{ "name": "session", "value": "env[SESSION_TOKEN]", "domain": "localhost" }],
    "headers": { "Authorization": "Bearer env[API_TOKEN]" }
  }
}

Build-time options

KeyDefaultDescription
breakpoints[375, 768, 1280]Viewport widths captured by CLI (auto-detects Tailwind)
out./src/bonesOutput directory
wait800ms to wait after page load before capturing

Runtime options (baked into registry.js)

KeyDefaultDescription
color#f0f0f0Bone fill color (light mode)
darkColor#222222Bone fill color (dark mode,
.dark
class)
animate"pulse"Animation: "pulse", "shimmer", or "solid"
shimmerColor#f7f7f7Shimmer highlight color (light mode)
darkShimmerColor#2c2c2cShimmer highlight color (dark mode)
speed"2s" (shimmer) / "1.8s" (pulse)Animation duration
shimmerAngle110Shimmer gradient angle in degrees
staggerfalseDelay between bones in ms (true = 80ms)
transitionfalseFade transition when loading ends in ms (true = 300ms)
boneClassCSS class applied to each bone element

Precedence

Per-component props > config file (via

configureBoneyard()
) > package defaults in
shared.ts
. CLI flags override config file for build options.

Common tasks

Adding a skeleton to a component

import { Skeleton } from 'boneyard-js/react'

<Skeleton name="my-component" loading={isLoading}>
  <MyComponent data={data} />
</Skeleton>

Using a fixture (when real data isn't available at build time)

<Skeleton
  name="my-component"
  loading={isLoading}
  fixture={<MyFixture />}
  snapshotConfig={{ leafTags: ["section"] }}
>
  <MyComponent data={data} />
</Skeleton>

Key pattern: use

<section>
(or any custom tag) as leaf elements in the fixture, then add that tag to
leafTags
so the extractor treats each as a single flat bone without recursing into children.

Excluding elements from capture

<nav data-no-skeleton>
  {/* No bone will be generated */}
</nav>

Or via

snapshotConfig
:

<Skeleton snapshotConfig={{ excludeSelectors: ['.icon', 'svg'], excludeTags: ['nav'] }}>

Running the CLI

# Auto-detect dev server
npx boneyard-js build

# Explicit URL + output
npx boneyard-js build http://localhost:PORT --out src/bones

# Force rebuild all (skip hash check)
npx boneyard-js build --force

# Watch mode (re-capture on HMR)
npx boneyard-js build --watch

# Custom breakpoints
npx boneyard-js build --breakpoints 375,640,768,1024,1280,1536

# React Native mode
npx boneyard-js build --native --out ./bones

Skeleton props reference

PropTypeDefaultDescription
loading
boolean
requiredShow skeleton vs children
children
ReactNode
requiredReal content
name
string
requiredRegistry key + CLI identifier
initialBones
ResponsiveBones
Pre-generated bones (overrides registry)
color
string
#f0f0f0Bone fill color (light mode) — any CSS color (hex, rgba, hsl, etc.)
darkColor
string
#222222Dark mode bone fill color (
.dark
class) — any CSS color
animate
AnimationStyle
'pulse'
"pulse", "shimmer", "solid" (also accepts boolean)
stagger
number | boolean
false
Stagger delay (true=80ms)
transition
number | boolean
false
Fade-out duration (true=300ms)
boneClass
string
CSS class per bone
className
string
Container class
fallback
ReactNode
Shown when loading + no bones
fixture
ReactNode
Mock content for CLI capture
snapshotConfig
SnapshotConfig
Controls bone extraction

Debugging checklist

  1. Skeleton shows nothing: Check that
    registry.js
    is imported in app entry AND bones JSON exists for that name
  2. Too many bones / internal shapes: Add
    leafTags
    to
    snapshotConfig
    and rebuild
  3. Bones don't match layout: Rebuild with
    --force
    to regenerate from current DOM
  4. Wrong breakpoint: Check container width — bones resolve using nearest
    <=
    breakpoint
  5. Dark mode not detected: Skeleton uses
    .dark
    class on
    <html>
    or ancestor. Does NOT use
    prefers-color-scheme
  6. CLI finds no skeletons: Components need
    loading={false}
    (or a fixture) so the real UI renders for capture
  7. Opacity overlap / double-layering: Container bones (
    c: true
    ) should be skipped in rendering — if they're not, the filter is missing
  8. Shimmer not visible: Check that
    shimmerColor
    has enough contrast with
    color
    — defaults are
    #f7f7f7
    on
    #f0f0f0