phantom-ui

repository·main·Indexed 20 days ago

https://github.com/aejkatappaja/phantom-ui

A structure-aware shimmer skeleton loader built as a universal Web Component with Lit. It automatically generates shimmer placeholders by measuring the actual DOM at runtime, eliminating the need for separate skeleton components. Compatible with React, Vue, Svelte, Angular, Solid, Qwik, or vanilla JS. Version 1.6.1.

Tokens
29.2K
Snippets
108
Records
139
Agent score
69%

What's inside phantom-ui

  1. What is phantom-ui?

    main
    phantom-ui is a structure-aware skeleton loader delivered as a single Web Component. Instead of manually building separate skeleton components that must be kept in sync with your real UI, you wrap your existing UI components with phantom-ui. It uses runtime DOM measurement via getBoundingClientRect() to automatically generate shimmer placeholders at the exact same positions as your leaf elements.
  2. What is phantom-ui and how does it work?

    main

    phantom-ui is a framework-agnostic library for creating skeleton loaders without maintaining separate skeleton components. Instead of building a second layout, phantom-ui renders your actual component with invisible text and uses getBoundingClientRect() to measure the position and size of every leaf element. It then overlays animated shimmer blocks at those exact coordinates.

    Key behaviors:

    • Layout Sync: Because it measures the real DOM, the skeleton is always in sync with the actual component layout.
    • Visual Mapping: Text blocks become horizontal bars, avatars become circular blocks, and buttons/inputs become rectangles. Border radii are preserved.
    • Automatic Reveal: When the loading state is removed, the shimmer disappears and the real content is revealed.
    • Accessibility: The host element receives aria-busy, and placeholder content is marked as inert to keep it out of the tab order and accessibility tree during loading.
  3. How phantom-ui works internally

    main

    The component follows these steps to create the skeleton effect:

    1. Content Hiding: Real content is rendered with color: transparent. Media elements are hidden, and CSS mask-image icons are detected and hidden. Container backgrounds and borders remain visible.
    2. Leaf Identification: The component walks the DOM to find "leaf" elements (text nodes, images, buttons, inputs, or elements without children).
    3. Measurement: Each leaf is measured using getBoundingClientRect() and getComputedStyle() (to capture border radius). Table cells receive special handling for text width.
    4. Overlay Rendering: An absolutely-positioned overlay renders one shimmer block per measured element using a CSS gradient animation.
    5. Auto-Resizing: A ResizeObserver, MutationObserver, and media load listener ensure the skeleton re-measures automatically when the layout changes (window resize, content injection, or images loading).
    6. Reveal: When loading is set to false, the overlay is destroyed, aria-busy is removed, and the real content is revealed. While loading, non-ignored content is made inert to stay out of the accessibility tree and tab order.
  4. How phantom-ui handles icons and SVGs

    main

    The engine includes specialized logic for common icon rendering patterns:

    • Mask-image Icons: Many design systems use CSS mask-image with a background-color to render icons. phantom-ui detects these at runtime via getComputedStyle and hides them along with other media during the loading state.
    • SVG Bounding Boxes: For <svg> elements, the engine captures the outer bounding box of the SVG rather than individual paths or internal shapes.
    • Pseudo-elements: The engine can detect and hide icons rendered via ::before or ::after pseudo-elements that use CSS masks.
  5. Use Refresh mode with `mode="overlay"`

    main

    Use mode="overlay" when you are in a stale-while-revalidate state (refetching data you already have). Unlike the default skeleton mode which hides content, overlay keeps the existing content visible but dimmed while a light glint sweeps over the elements.

    • Behavior: Content is dimmed (controlled by --phantom-content-opacity, default 0.5) and becomes non-clickable (pointer-events: none) to prevent users from interacting with stale data.
    • Accessibility: The host element automatically sets aria-busy to announce the update.
    • Animations: The sweep uses the shimmer animation. If pulse or breathe is selected, the light is held as a steady veil instead of a sweep.
    • Note: count and count-gap attributes do not apply in overlay mode.
    <phantom-ui loading mode="overlay">
      <div class="grid"><!-- the previous result --></div>
    </phantom-ui>
  6. Use live theming with var() in attributes

    main

    To achieve instant theme updates without component re-renders, pass a var() reference directly into the component's HTML attributes. This keeps the value unresolved until the browser paints, allowing the component to re-resolve the color live whenever the referenced CSS variable changes (e.g., when toggling a .dark class on an ancestor).

    <phantom-ui shimmer-color="var(--brand-shimmer)" background-color="var(--brand-bg)">
    
    <style>
    :root {
      --brand-shimmer: rgba(0, 0, 0, 0.08);
      --brand-bg: rgba(0, 0, 0, 0.06);
    }
    
    .dark {
      --brand-shimmer: rgba(255, 255, 255, 0.3);
      --brand-bg: rgba(255, 255, 255, 0.08);
    }
    </style>
  7. Understand phantom-ui accessibility and inert behavior

    main

    During the loading state, phantom-ui ensures the application remains accessible and follows best practices for screen readers and keyboard navigation:

    • aria-busy: The host component is marked with aria-busy while loading.
    • inert attribute: Slotted content is made inert to remove it from the tab order and the accessibility tree. This prevents users from interacting with invisible placeholder elements.
    • Smart inert application: To maintain performance and allow for specific interactive areas, phantom-ui only applies inert to the largest subtrees that do not contain a data-shimmer-ignore element. This allows elements marked with data-shimmer-ignore to remain interactive.
    • SSR Compatibility: Because inert is applied via JavaScript, during Server-Side Rendering (SSR) before hydration, only the visual hiding (transparency/opacity) applies.
  8. Understand the Qwik City project structure

    main

    This project uses Qwik with QwikCity for directory-based routing and layouts. The core directory structure is:

    • src/routes: Contains the directory-based routing. Pages are defined by index.tsx files, while layout.tsx files provide hierarchical layouts. index.ts files are used to define endpoints.
    • src/components: The recommended location for reusable components.
    • public: Stores static assets like images.
  9. How phantom-ui's shimmer algorithm works

    main

    When the loading state is active, phantom-ui executes a multi-step process to create a skeleton screen that matches your content's layout:

    1. Invisible Rendering: The real content is rendered but hidden using color: transparent, opacity: 0, and pointer-events: none. This preserves the exact layout while preventing user interaction.
    2. DOM Walking: The engine recursively walks slotted children to identify "leaf" elements (terminal content nodes) that should be replaced by shimmer blocks.
      • Leaf elements include: img, svg, video, canvas, iframe, input, textarea, button, hr, or any element with no child elements (only text nodes).
      • Container elements (like div, section, ul) are traversed but not captured as blocks themselves.
    3. Measurement: Each leaf element is measured using getBoundingClientRect() and its borderRadius is retrieved via getComputedStyle(). Special handling is provided for table cells (td, th) to measure text width accurately.
    4. Overlay Rendering: An absolute-positioned overlay is rendered with one div per measured element, positioned at the exact {x, y, width, height} of the leaf.
    5. Reactive Re-measurement: The engine uses ResizeObserver, MutationObserver, and media load listeners to re-trigger measurements if the layout changes or images/videos finish loading.
    6. Reveal: Once loading is set to false, the overlay is destroyed and the invisible styles are removed to reveal the content.
  10. Understand the Starlight project structure

    main

    Starlight projects follow a specific directory structure for content and assets:

    • src/content/docs/: The primary location for documentation. Starlight automatically exposes .md or .mdx files in this directory as routes based on their filenames.
    • src/assets/: Place images here to embed them in Markdown using relative links.
    • public/: Use this directory for static assets that do not need processing, such as favicons.
    • astro.config.mjs: The configuration file for the Astro project.
    • src/content.config.ts: Configuration for content collections.
    .
    ├── public/
    ├── src/
    │   ├── assets/
    │   ├── content/
    │   │   └── docs/
    │   └── content.config.ts
    ├── astro.config.mjs
    ├── package.json
    └── tsconfig.json
  11. How phantom-ui works with SSR Frameworks

    main

    Because phantom-ui relies on browser-specific APIs (getBoundingClientRect, ResizeObserver, and customElements) to perform DOM measurements, the package must be imported on the client-side only.

    The <phantom-ui> HTML tag is safe to include in server-rendered markup. The browser treats it as an unknown element until hydration occurs, at which point the Web Component activates. This allows content to render normally on the server, which is beneficial for SEO.