-
Notifications
You must be signed in to change notification settings - Fork 10
Skeleton [Draft]
- Skeleton specification (DRAFT)
Team Name: Design and Web Development
Developer name: Radoslav Karaivanov
Designer name:
| Version | Author | Date | Description |
|---|---|---|---|
| 1 | Radoslav Karaivanov | 2026-04-24 | Initial draft |
The igc-skeleton component overlays animated placeholder shapes on top of projected content while
it is in a loading state, then smoothly reveals the content once loading is complete. It uses
absolute-positioned shapes measured from the bounding boxes of visible leaf elements inside the
default slot, so no changes to the slotted content's markup are required. Placeholder shapes and
the reveal transition are driven entirely by CSS custom properties and animations.
The igc-skeleton must:
- be WAI-ARIA compliant.
- not alter the DOM structure or styles of slotted content.
- overlay accurately measured placeholder shapes on top of every visible leaf element when loading.
- clear all placeholder shapes when loading ends.
- animate placeholder shapes with a configurable animation style.
- smoothly animate slotted content into view after leaving the loading state.
- re-measure shapes when slotted content changes dynamically.
- re-measure shapes when the host element is resized.
- expose styling hooks for the overlay layer, placeholder shapes, and content reveal transition through CSS custom properties.
As an end-user I expect to be able to:
- see placeholder shapes that match the layout of the page content while data is loading.
- see the real content smoothly appear once loading has completed.
As a developer I expect to be able to:
- wrap any content in
igc-skeletonand toggle theloadingattribute to show or hide the skeleton overlay without modifying slotted content. - choose between multiple built-in animation styles (
pulse,breathe,shimmer,wave,glow) to match the visual language of the application. - customize the overlay and shape colors through CSS custom properties.
- rely on automatic re-measurement when slotted content or the component size changes.
[Design Handoff]
Developers set the loading attribute (or property) to true to put the component into its
loading state. The component then:
- Hides the slotted content (opacity 0, pointer-events none).
- Renders a semi-transparent overlay across the entire component.
- Measures every visible leaf element inside the default slot and renders an absolutely positioned placeholder shape for each one, using the same position, dimensions, and border-radius.
Setting loading back to false removes all placeholder shapes and overlay, and triggers a short
CSS reveal animation on the content wrapper via the :state(revealing) custom state. The reveal
runs for 600 ms and is implemented with ElementInternals.states so it does not pollute the
element's attribute list.
The animation attribute selects the CSS animation applied to each placeholder shape. For the
wave animation, shapes are staggered by 100 ms per index using the --_wave-delay CSS custom
property.
Shapes are re-measured automatically whenever:
- Slotted content changes (via a
MutationObserveronchildListandsubtree). - The host element is resized (via a
ResizeObserver).
The checkVisibility({ checkOpacity: false }) guard is applied during measurement so that
display: none and visibility: hidden leaf nodes are excluded. Elements that are opacity-0 are
intentionally included, as they may be part of a transition.
None applicable.
None applicable. The component is non-interactive and non-focusable by default.
| Property | Attribute | Reflect | Type | Default | Description |
|---|---|---|---|---|---|
loading |
loading |
Yes | boolean |
false |
Puts the component into a loading state, rendering animated placeholder shapes over the projected content. |
animation |
animation |
No | 'pulse' | 'breathe' | 'shimmer' | 'wave' | 'glow' |
'breathe' |
The animation style applied to each placeholder shape while in the loading state. |
None applicable.
None applicable.
| Slot | Description |
|---|---|
| (default) | The content to measure and overlay with skeleton placeholder shapes. |
| Part | Description |
|---|---|
content |
The wrapper element around the slotted content. Hidden while loading; animated in on reveal. |
overlay |
The semi-transparent layer rendered over the entire component during loading. |
shape |
An individual placeholder shape rendered over a visible leaf element. Also receives the animation part name (e.g. shape breathe). |
| Variable | Description |
|---|---|
--ig-skeleton-overlay-color |
Background color of the overlay layer. |
--ig-skeleton-shape-color |
Background color of the placeholder shapes. |
--border-radius |
Border radius applied to the overlay and shapes when the measured element has no explicit border-radius. Falls back to a radius derived from --ig-radius-factor. |
| State | Description |
|---|---|
:state(revealing) |
Applied to the host for 600 ms immediately after loading transitions from true to false. Used to trigger the content reveal animation. |
- Verify that the component renders and is accessible in the idle state (shadow DOM and light DOM a11y audits).
- Verify that the component renders and is accessible in the loading state (shadow DOM and light DOM a11y audits).
- Verify that
loadingdefaults tofalseandanimationdefaults to'breathe'. - Verify that the
loadingattribute is reflected to the DOM when set totrue. - Verify that no placeholder shapes are rendered when
loadingisfalse. - Verify that placeholder shapes are rendered when
loadingistrue. - Verify that all placeholder shapes are removed when
loadingreturns tofalse. - Verify that each shape carries the correct animation part name for every
animationvalue. - Verify that
--_wave-delayis set per-shape index for thewaveanimation and absent for all other animations. - Verify that the
:state(revealing)custom state is applied immediately whenloadingtransitions fromtruetofalse. - Verify that the
:state(revealing)custom state is removed after 600 ms. - Verify that
:state(revealing)is not applied whenloadingwas nevertrue. - Verify that a pending reveal timeout is cancelled when
loadingis set back totrue. - Verify that disconnecting the element clears the reveal timeout without throwing.
- Verify that
display: noneandvisibility: hiddenleaf nodes are excluded from measurement. - Verify that the correct number of leaf shapes is measured for a known content structure.
While loading, aria-busy="true" is set on the host element via ElementInternals to signal to
assistive technologies that the region is updating. The overlay and individual shapes carry
aria-hidden="true" so they are invisible to screen readers.
No other intrinsic WAI-ARIA roles or properties are applied; it is the developer's responsibility to add any additional labels or live-region attributes required by the specific application context.
- ElementInternals.states (CSS Custom State Set)
- Element.checkVisibility()
- ResizeObserver
- MutationObserver
The component should work in a Right-To-Left context without additional setup or configuration.
Shape positions are derived from getBoundingClientRect() relative to the host, so RTL layout is
handled automatically by the browser.
- Only visible leaf elements (elements with no child elements that pass
checkVisibility()) receive placeholder shapes. Container or wrapper elements are intentionally skipped. - Shapes are measured against the component's own bounding rect. Any content that is positioned
outside the host's bounds (e.g. via
overflow: visibleorposition: fixedchildren) will produce shapes that fall outside the overlay area. - Shadow DOM roots of nested custom elements are not traversed; only light-DOM leaf nodes are measured.
- The component does not support skeleton overlays for content rendered inside
<input>,<textarea>, or other replaced/form elements, as their layout is opaque to thegetBoundingClientRect-based measurement approach.