Automatic Skeletonization
Automatic Skeletonization
The heart of the module is a DOM-scanning engine that runs inside every active
<Skeletonizer>. It walks the rendered subtree (leaf-stop DFS), measures every element it will
skeletonize, classifies it, and renders the whole result as one <svg> overlay positioned above
the (hidden but still laid-out) real content — without ever moving, wrapping or removing a node.
What it recognises
The engine classifies elements into semantic bone kinds:
text · heading · image · avatar · badge · tag · button · icon · input ·
textarea · select · checkbox · radio · switch · plus generic block / container and
custom components.
Classification uses tag names, ARIA roles, computed styles and structure (e.g. a roughly-square
element with a circular radius becomes an avatar; an <img> becomes an image).
Layout fidelity — a structural replica, not an approximation
The skeleton is not a rough stand-in: it is measured to be a structurally faithful replica of the real layout, coordinate for coordinate.
Real element → Bounding box (scan) → SVG blueprint → Rendered skeleton
312×48px { x, y, w:312, <rect width="312" identical
rx 8px h:48, radius:8 } height="48" rx="8"/> 312×48 box
What is preserved exactly, with no invented values:
- Width & height — read via
getBoundingClientRect()and carried through to the SVGwidth/heightunchanged (rounded tosvgPrecisiondecimal places, so the blueprint matches the real box to within ±1px). No arbitrary widths, no percentage guesses, no random shrinkage. - Border radius — the element's own computed
border-radiusmaps straight onto the shape'srx/ry(or a<circle>when the element is square and visibly round — see Avatars below). - Text lines — a wrapped text node is measured line by line (
Range.getClientRects()), so a paragraph that wraps into 3 real lines of 320px / 287px / 164px produces exactly 3 bones of 320px / 287px / 164px. The engine never falls back to an artificial100% / 90% / 75%ladder when the real per-line width is available; only when no text-layout information exists at all (e.g. an empty leaf) does it fall back to the element's own box. - Images & avatars — an
<img>/<picture>/<video>bone keeps the element's real width, height and aspect ratio (never deformed into a square). A roughly-square element with a visible circular radius becomes anavatar: a true<circle>when it reads as fully round, or a<rect rx>matching the measured radius when it is a rounded square instead. - Flex & grid structure — because every descendant is drawn at its own real absolute position
(relative to the host), a grid never collapses into a vertical list and a flex row never
collapses into a stack: the gaps and alignment are implicit in the measured coordinates. The
scan layer additionally records each flex/grid container's
display,flex-direction/justify-content/align-items, and row/columngapfor verification and DevTools/Explain — this metadata is a record of what the renderer already reproduces geometrically, not a separate approximation.
The renderer is deterministic: the same scanned nodes always produce the same SVG markup — no random line lengths, no cosmetic jitter.
How a bone is painted
- The scan layer never mutates the real subtree — it only reads
getBoundingClientRect()andgetComputedStyle(). The real content is hidden withvisibility: hidden(its layout box stays in place, which is exactly what makes the measurement possible). - The SVG renderer turns the measured nodes into one absolutely-positioned
<svg>overlay: each bone is a<rect>(or<circle>for avatars/radios), filled by a shared, namespaced<linearGradient>that drives the shimmer sweep for every shape at once. - Disabling the skeleton removes the overlay and restores
visibility: visible— the real markup was never touched.
Re-scanning
A debounced MutationObserver watches the subtree, so content added after the first scan (lazy
lists, async components) is skeletonized automatically. You can also call refresh() manually.
Mixing with manual skeletons
Manual Skeleton* components are recognised and left untouched by the engine, so you can freely mix
automatic and hand-authored skeletons in the same tree.
Steering it
Use the directives — v-skeleton-ignore, v-skeleton-keep, v-skeleton-replace,
v-skeleton-union, v-skeleton-shimmer — to override the engine where you need precision.