Skip to content

Theming

All theming is pure CSS, driven by custom properties on :root — no JS involved.

TokenDefaultPurpose
--ghostwire-bone-coloroklch(0.92 0.005 250)Base skeleton color (light)
--ghostwire-bone-color-darkoklch(0.42 0.012 250)Base skeleton color (dark)
--ghostwire-bone-highlightoklch(0.97 0.005 250)Shimmer sheen color (light)
--ghostwire-bone-highlight-darkoklch(0.55 0.015 250)Shimmer sheen color (dark)
--ghostwire-panel-coloroklch(0 0 0 / 0.035)Panel bone color — translucent dark overlay (light)
--ghostwire-panel-color-darkoklch(1 0 0 / 0.05)Panel bone color — translucent light overlay (dark)
--ghostwire-bone-radius0.375remCorner radius on each bone
--ghostwire-bone-gap0.5remSpacing between stacked bones
--ghostwire-animationgw-shimmerAnimation name — gw-shimmer, gw-pulse, gw-wave, or none
--ghostwire-animation-speed1.4sAnimation cycle duration
--ghostwire-fade-duration150msShow/hide cross-fade duration

Automatic via prefers-color-scheme: dark — the -dark token variants apply with no configuration.

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);
}

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.

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;
}