Theming
All theming is pure CSS, driven by custom properties on :root — no JS involved.
| Token | Default | Purpose |
|---|---|---|
--ghostwire-bone-color | oklch(0.92 0.005 250) | Base skeleton color (light) |
--ghostwire-bone-color-dark | oklch(0.42 0.012 250) | Base skeleton color (dark) |
--ghostwire-bone-highlight | oklch(0.97 0.005 250) | Shimmer sheen color (light) |
--ghostwire-bone-highlight-dark | oklch(0.55 0.015 250) | Shimmer sheen color (dark) |
--ghostwire-panel-color | oklch(0 0 0 / 0.035) | Panel bone color — translucent dark overlay (light) |
--ghostwire-panel-color-dark | oklch(1 0 0 / 0.05) | Panel bone color — translucent light overlay (dark) |
--ghostwire-bone-radius | 0.375rem | Corner radius on each bone |
--ghostwire-bone-gap | 0.5rem | Spacing between stacked bones |
--ghostwire-animation | gw-shimmer | Animation name — gw-shimmer, gw-pulse, gw-wave, or none |
--ghostwire-animation-speed | 1.4s | Animation cycle duration |
--ghostwire-fade-duration | 150ms | Show/hide cross-fade duration |
Dark mode
Section titled “Dark mode”Automatic via prefers-color-scheme: dark — the -dark token variants apply with no configuration.
Class-based dark mode (e.g. Tailwind)
Section titled “Class-based dark mode (e.g. Tailwind)”This is the common case for Tailwind apps, not an edge case: Tailwind’s
@custom-variant dark (&:where(.dark, .dark *)) drives dark mode off a
.dark class the app toggles, not the OS/browser prefers-color-scheme
media query. Ghostwire’s own -dark tokens only react to the media query,
so whenever the OS preference and the app’s active theme diverge (e.g. OS
light + app toggled to .dark, or the reverse), Ghostwire’s bones desync
from the rest of the page. Override the base tokens — not the -dark ones
— inside your own .dark selector:
.dark { --ghostwire-bone-color: oklch(0.42 0.012 250); --ghostwire-bone-highlight: oklch(0.55 0.015 250);}Reduced motion
Section titled “Reduced motion”prefers-reduced-motion: reduce disables the gw-shimmer/gw-pulse/gw-wave animation automatically, falling back to the sheen’s static (invisible) state — same as setting --ghostwire-animation: none by hand.
Overriding
Section titled “Overriding”Set any token on :root (or scoped to a container) in your own CSS, after Ghostwire’s stylesheet loads. Override both the light and dark pair together — overriding only the light value leaves dark mode on Ghostwire’s own default, which may no longer match your palette:
:root { --ghostwire-bone-color: #e2e8f0; --ghostwire-animation: gw-pulse;}
@media (prefers-color-scheme: dark) { :root { --ghostwire-bone-color: #1e293b; }}Scoped to one part of your app instead of the whole page:
.dashboard-widgets { --ghostwire-bone-radius: 9999px; --ghostwire-animation: gw-wave;}