What is phantom-ui?
maingetBoundingClientRect() to automatically generate shimmer placeholders at the exact same positions as your leaf elements.repository·main·Indexed 20 days ago
https://github.com/aejkatappaja/phantom-uiA 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.
getBoundingClientRect() to automatically generate shimmer placeholders at the exact same positions as your leaf elements.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:
loading state is removed, the shimmer disappears and the real content is revealed.aria-busy, and placeholder content is marked as inert to keep it out of the tab order and accessibility tree during loading.The component follows these steps to create the skeleton effect:
color: transparent. Media elements are hidden, and CSS mask-image icons are detected and hidden. Container backgrounds and borders remain visible.getBoundingClientRect() and getComputedStyle() (to capture border radius). Table cells receive special handling for text width.ResizeObserver, MutationObserver, and media load listener ensure the skeleton re-measures automatically when the layout changes (window resize, content injection, or images loading).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.The engine includes specialized logic for common icon rendering patterns:
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> elements, the engine captures the outer bounding box of the SVG rather than individual paths or internal shapes.::before or ::after pseudo-elements that use CSS masks.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.
--phantom-content-opacity, default 0.5) and becomes non-clickable (pointer-events: none) to prevent users from interacting with stale data.aria-busy to announce the update.shimmer animation. If pulse or breathe is selected, the light is held as a steady veil instead of a sweep.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>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>The following items are not covered by the stability guarantee and may change in minor or patch releases without a major version bump:
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.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.inert is applied via JavaScript, during Server-Side Rendering (SSR) before hydration, only the visual hiding (transparency/opacity) applies.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.When the loading state is active, phantom-ui executes a multi-step process to create a skeleton screen that matches your content's layout:
color: transparent, opacity: 0, and pointer-events: none. This preserves the exact layout while preventing user interaction.img, svg, video, canvas, iframe, input, textarea, button, hr, or any element with no child elements (only text nodes).div, section, ul) are traversed but not captured as blocks themselves.getBoundingClientRect() and its borderRadius is retrieved via getComputedStyle(). Special handling is provided for table cells (td, th) to measure text width accurately.div per measured element, positioned at the exact {x, y, width, height} of the leaf.ResizeObserver, MutationObserver, and media load listeners to re-trigger measurements if the layout changes or images/videos finish loading.loading is set to false, the overlay is destroyed and the invisible styles are removed to reveal the content.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.jsonBecause 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.