Ghostly
Guides

Customization

Colors, animations, dark mode, transitions, and theming.

Colors

Via CSS custom properties (global)

Override CSS custom properties to match your design system:

globals.css
:root {
  --ghostly-color: hsl(210 40% 88%);
  --ghostly-shine: hsl(210 40% 94%);
}

.dark {
  --ghostly-color: hsl(210 15% 15%);
  --ghostly-shine: hsl(210 15% 22%);
}

Via props (per-instance)

Use the color and shine props directly — no CSS needed:

<Ghostly
  loading={true}
  color="hsl(260 60% 90%)"
  shine="hsl(260 60% 95%)"
>
  <HeroContent data={data} />
</Ghostly>

This also works on GhostlyList and GhostlySuspense:

<GhostlyList
  loading={isLoading}
  count={4}
  item={<Card />}
  color="hsl(340 60% 90%)"
  shine="hsl(340 60% 95%)"
>
  {items?.map(i => <Card key={i.id} item={i} />)}
</GhostlyList>

Via Tailwind plugin (arbitrary values)

<div class="ghostly-color-[hsl(210_40%_88%)] ghostly-shine-[hsl(210_40%_94%)]">
  ...
</div>

Animations

AnimationBest forDescription
shimmerGeneral contentGradient sweep left to right
pulseMinimal UIsOpacity fade in/out
waveLists, timelinesStaggered pulse across children
nonePrint, reduced motionStatic blocks

Speed

<Ghostly loading={true} speed="fast">    {/* 0.8s */}
<Ghostly loading={true} speed="normal">  {/* 1.5s */}
<Ghostly loading={true} speed="slow">    {/* 2.0s */}

Border radius

<Ghostly loading={true} radius="none">   {/* 0px */}
<Ghostly loading={true} radius="xs">     {/* 2px */}
<Ghostly loading={true} radius="sm">     {/* 4px (default) */}
<Ghostly loading={true} radius="md">     {/* 8px */}
<Ghostly loading={true} radius="lg">     {/* 12px */}
<Ghostly loading={true} radius="full">   {/* 9999px — pill */}

Smooth transitions

Enable a fade-out effect when loading ends with the smooth prop:

<Ghostly loading={isLoading} smooth>
  <UserCard user={data} />
</Ghostly>

Customize the transition speed globally:

globals.css
:root {
  --ghostly-transition: 0.5s; /* default: 0.3s */
}

Skeleton line count

Control how many lines a text element simulates:

<p data-ghostly-lines="1">Short label</p>
<p data-ghostly-lines="5">Long description paragraph</p>

Image aspect ratio

Images without explicit dimensions use 16/9 by default. Override per-element:

<img data-ghostly-ratio="1/1" src="avatar.jpg" alt="" />
<img data-ghostly-ratio="4/3" src="photo.jpg" alt="" />

Overriding element styles

Ghostly uses :where() (zero specificity). Override with one class:

[data-ghostly] p { min-height: 2em; min-width: 50%; }
[data-ghostly] img { aspect-ratio: 4 / 3; }

Global defaults

app/layout.tsx
<GhostlyProvider animation="shimmer" radius="md" speed="normal">
  <App />
</GhostlyProvider>

Individual instances override the provider. Nested providers inherit unspecified values from their parent.