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.mdsource 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/
)
packages/boneyard/
—src/react.tsx
React component (also exports<Skeleton>
,configureBoneyard
)registerBones
— Native Preact integration (no compat needed)src/preact.tsx
— Svelte 5 componentsrc/Skeleton.svelte
— Vue componentsrc/Skeleton.vue
— Angular componentsrc/angular.ts
/src/native.tsx
— React Nativesrc/react-native.tsx
—src/extract.ts
DOM walker,snapshotBones()
descriptor extractorfromElement()
— bone registry, animation constants (src/shared.ts
,SHIMMER
,PULSE
),DEFAULTSresolveResponsive
—src/types.ts
,SnapshotConfig
,Bone
,CompactBoneResponsiveBones
— vanillasrc/runtime.ts
for non-React usagerenderBones()
— CLI entry (bin/cli.js
)boneyard-js build
Package exports
— snapshotBones, renderBones, fromElementboneyard-js
— Skeleton, registerBones, configureBoneyardboneyard-js/react
— Skeleton, registerBones, configureBoneyardboneyard-js/preact
— Skeleton, registerBones, configureBoneyard (React Native)boneyard-js/native
— Skeleton component, registerBonesboneyard-js/svelte
— Skeleton component, registerBones, configureBoneyardboneyard-js/vue
— SkeletonComponent, registerBones, configureBoneyardboneyard-js/angular
— boneyardPlugin() Vite pluginboneyard-js/vite
Bones format
Compact array:
[x%, y_px, w%, h_px, borderRadius, isContainer?]
andx
are percentages of container widthw
andy
are pixelsh
is number (px) or string ("50%")borderRadius
(optional 6th element, truthy) — container bones are skipped during rendering. They represent parent backgrounds and would cause opacity overlap if rendered alongside child bones.isContainer
Bone resolution priority
- Explicit
prop (highest priority)initialBones - Registry lookup by
(fromname
)registry.js - Fixture fallback (only during CLI build mode when
)window.__BONEYARD_BUILD === true
Animation constants (in shared.ts
)
shared.tsAll 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
)
boneyard.config.jsonThe 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
| Key | Default | Description |
|---|---|---|
| breakpoints | [375, 768, 1280] | Viewport widths captured by CLI (auto-detects Tailwind) |
| out | ./src/bones | Output directory |
| wait | 800 | ms to wait after page load before capturing |
Runtime options (baked into registry.js)
| Key | Default | Description |
|---|---|---|
| color | #f0f0f0 | Bone fill color (light mode) |
| darkColor | #222222 | Bone fill color (dark mode, class) |
| animate | "pulse" | Animation: "pulse", "shimmer", or "solid" |
| shimmerColor | #f7f7f7 | Shimmer highlight color (light mode) |
| darkShimmerColor | #2c2c2c | Shimmer highlight color (dark mode) |
| speed | "2s" (shimmer) / "1.8s" (pulse) | Animation duration |
| shimmerAngle | 110 | Shimmer gradient angle in degrees |
| stagger | false | Delay between bones in ms (true = 80ms) |
| transition | false | Fade transition when loading ends in ms (true = 300ms) |
| boneClass | — | CSS 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
| Prop | Type | Default | Description |
|---|---|---|---|
| | required | Show skeleton vs children |
| | required | Real content |
| | required | Registry key + CLI identifier |
| | — | Pre-generated bones (overrides registry) |
| | #f0f0f0 | Bone fill color (light mode) — any CSS color (hex, rgba, hsl, etc.) |
| | #222222 | Dark mode bone fill color ( class) — any CSS color |
| | | "pulse", "shimmer", "solid" (also accepts boolean) |
| | | Stagger delay (true=80ms) |
| | | Fade-out duration (true=300ms) |
| | — | CSS class per bone |
| | — | Container class |
| | — | Shown when loading + no bones |
| | — | Mock content for CLI capture |
| | — | Controls bone extraction |
Debugging checklist
- Skeleton shows nothing: Check that
is imported in app entry AND bones JSON exists for that nameregistry.js - Too many bones / internal shapes: Add
toleafTags
and rebuildsnapshotConfig - Bones don't match layout: Rebuild with
to regenerate from current DOM--force - Wrong breakpoint: Check container width — bones resolve using nearest
breakpoint<= - Dark mode not detected: Skeleton uses
class on.dark
or ancestor. Does NOT use<html>prefers-color-scheme - CLI finds no skeletons: Components need
(or a fixture) so the real UI renders for captureloading={false} - Opacity overlap / double-layering: Container bones (
) should be skipped in rendering — if they're not, the filter is missingc: true - Shimmer not visible: Check that
has enough contrast withshimmerColor
— defaults arecolor
on#f7f7f7#f0f0f0