# Ghostly — Complete Documentation > Zero-config skeleton loaders for React. CSS-first engine that transforms your existing components into skeleton loading states. No CLI, no build step, no code changes. ~3KB gzipped. --- ## Installation Install both packages: ```bash npm install @ghostly-ui/core @ghostly-ui/react ``` ### Step 1: Import the CSS ```css /* globals.css */ @import '@ghostly-ui/core/css'; ``` ### Step 2: Wrap your components ```tsx import { Ghostly } from '@ghostly-ui/react' function UserPage() { const { data, isLoading } = useFetch('/api/user') return ( ) } ``` Requirements: React >= 18, any bundler with CSS import support. ### Optional: Global configuration ```tsx import { GhostlyProvider } from '@ghostly-ui/react' import '@ghostly-ui/core/css' export default function RootLayout({ children }) { return ( {children} ) } ``` --- ## API Reference ### `` Component Wraps any content to show skeleton loaders while loading. Supports ref forwarding. ```tsx import { Ghostly } from '@ghostly-ui/react' ``` Props: - `loading` (boolean, required): When true, shows skeleton effect - `animation` ('shimmer' | 'pulse' | 'wave' | 'none', default: 'shimmer'): Animation style - `radius` ('none' | 'xs' | 'sm' | 'md' | 'lg' | 'full', default: 'sm'): Border radius - `speed` ('slow' | 'normal' | 'fast', default: 'normal'): Animation speed - `as` ('div' | 'section' | 'article' | 'main' | 'aside' | 'span', default: 'div'): Wrapper tag - `className` (string): CSS classes for wrapper - `style` (CSSProperties): Inline styles, merged with CSS variables - `children` (ReactNode, required): Content to display or skeletonize - `ref` (Ref): Forwarded to wrapper element Behavior when `loading={true}`: - Sets `data-ghostly="{animation}"` on wrapper - Sets `aria-busy="true"` for accessibility - Sets CSS variables `--ghostly-radius` and `--ghostly-speed` - All descendant text/media elements become skeleton blocks via CSS Example: ```tsx ``` ### `` Component Skeleton loader for lists and grids. Clones a template element N times during loading. Supports ref forwarding. ```tsx import { GhostlyList } from '@ghostly-ui/react' ``` Props: - `loading` (boolean, required): When true, shows skeleton items - `count` (number, required): Number of skeleton items to render - `item` (ReactElement, default: first child): Template element to clone - `animation`, `radius`, `speed`: Same as Ghostly - `className` (string): CSS classes for list container - `children` (ReactNode, required): Real content when not loading Example: ```tsx } className="grid grid-cols-3 gap-4" > {products?.map(p => )} ``` ### `` Component Sets default config for all Ghostly descendants. Priority: Instance props > Provider > Defaults. Props: - `animation` (GhostlyAnimation, default: 'shimmer') - `radius` (GhostlyRadius, default: 'sm') - `speed` (GhostlySpeed, default: 'normal') - `children` (ReactNode, required) ### `useGhostly()` Hook Returns the current Ghostly context from the nearest ancestor. ```tsx const { loading, animation, radius, speed } = useGhostly() ``` Returns: `{ loading: boolean, animation: GhostlyAnimation, radius: GhostlyRadius, speed: GhostlySpeed }` --- ## CSS Reference ### Custom Properties Override these globally to customize: ```css :root { --ghostly-color: hsl(220 13% 87%); /* Skeleton block color */ --ghostly-shine: hsl(220 13% 94%); /* Shimmer highlight */ --ghostly-radius: 4px; /* Border radius */ --ghostly-speed: 1.5s; /* Animation duration */ } .dark { --ghostly-color: hsl(220 13% 18%); --ghostly-shine: hsl(220 13% 25%); } ``` ### Animations Shimmer: gradient sweep left-to-right (default, most professional) Pulse: opacity fades 100% → 40% → 100% Wave: cascading opacity with staggered delays per child (80ms intervals, up to 12 children) None: static blocks, no animation ### Dark Mode Detection Automatic via three methods (priority order): 1. `.dark` CSS class (Tailwind convention) 2. `data-theme="dark"` attribute (shadcn/ui convention) 3. `prefers-color-scheme: dark` media query (system preference) ### Reduced Motion All animations disabled when `prefers-reduced-motion: reduce` is active. Skeletons render as static blocks. ### HTML Attributes `data-ghostly="shimmer|pulse|wave"`: Set by Ghostly component. Activates skeleton CSS on all descendants. `data-ghostly-ignore`: Add manually to exclude an element and its children from skeleton effect. `aria-busy="true"`: Set automatically when loading. `aria-live="polite"`: Set automatically for screen reader announcements. ### Element Coverage Text elements covered: h1-h6, p, span, a, li, td, th, dt, dd, label, legend, figcaption, caption, summary, blockquote, cite, q, em, strong, small, mark, code, pre, button, input, textarea, select, option, fieldset, dialog, details, address. Media elements covered: img, svg, video, canvas, picture, iframe. ### Minimum Dimensions (prevents empty element collapse) h1: 1.75em height, 40% width h2: 1.5em height, 50% width h3: 1.3em height, 55% width p: 3em height, 80% width pre: 5em height, 100% width input/textarea: 2.5rem height, 6rem width button: 2.25rem height, 5rem width img (no size): 12rem height, 100% width svg: 1.5rem height/width ### Specificity Ghostly uses `:where()` selectors (zero specificity). Override with a single class selector: ```css [data-ghostly] p { min-height: 2em; } ``` --- ## TypeScript Types ```tsx type GhostlyAnimation = 'shimmer' | 'pulse' | 'wave' | 'none' type GhostlyRadius = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'full' type GhostlySpeed = 'slow' | 'normal' | 'fast' interface GhostlyConfig { animation?: GhostlyAnimation; radius?: GhostlyRadius; speed?: GhostlySpeed } ``` Radius values: none=0px, xs=2px, sm=4px, md=8px, lg=12px, full=9999px Speed values: slow=2s, normal=1.5s, fast=0.8s --- ## Patterns ### Suspense fallback ```tsx }> ``` ### Next.js loading.tsx ```tsx export default function Loading() { return ( } className="grid grid-cols-3 gap-4"> <> ) } ``` ### TanStack Query ```tsx const { data, isLoading } = useQuery({ queryKey: ['user'], queryFn: fetchUser }) ``` ### SWR ```tsx const { data, isLoading } = useSWR('/api/posts', fetcher) }>{data?.map(...)} ``` ### Exclude elements ```tsx

{data?.title}

``` ### Custom colors per section ```tsx ``` ### Multiple independent sections ```tsx } animation="wave"> {orders.data?.map(...)} ``` --- ## How It Works Your component IS the skeleton. Ghostly adds `data-ghostly` attribute to a wrapper. CSS rules then: 1. Make text transparent + show background color (skeleton block) 2. Hide images via object-position offset + show background color 3. Apply animation (shimmer gradient, pulse opacity, wave stagger) 4. Block pointer events and user selection 5. Set min-height to prevent empty element collapse When loading ends, the attribute is removed. No layout shift because the skeleton uses the exact same layout. Zero runtime JavaScript for rendering. CSS animations are GPU-accelerated and off the main thread. ## Bundle Size @ghostly-ui/core (CSS): ~2KB gzipped @ghostly-ui/react: ~1KB gzipped Total: ~3KB gzipped ## Browser Support Chrome 88+, Firefox 78+, Safari 14+, Edge 88+, iOS Safari 14+. Limiting factor: `:where()` selector. ## Component Requirements Components should handle undefined data with optional chaining (`data?.title`). This is standard React practice. --- ## Links - GitHub: https://github.com/AdanSerrano/ghostly - npm @ghostly-ui/core: https://www.npmjs.com/package/@ghostly-ui/core - npm @ghostly-ui/react: https://www.npmjs.com/package/@ghostly-ui/react