From Component Extractor
Make a faithful replica of a component whose look is an EMERGENT COMPOSITE of CSS layers — stacked gradients, mix-blend-mode, masks, opacity, pseudo-element overlays — possibly sitting over a <canvas> or <svg>, by extracting the COMPUTED runtime styles and the full layer/stacking/blend graph, not the authored stylesheet. Use when the look comes from several coupled CSS layers blending together (the appearance is more than any single layer), when blend modes / pseudo-element overlays / masks are involved, or when a canvas/SVG looks wrong even though its own source matches. Do NOT use for a single declarative element with no blending (just copy its computed style) — this skill is for the coupling.
How this skill is triggered — by the user, by Claude, or both
Slash command
/component-extractor:css-compositeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Status: battle-tested on the Vercel triangle hero** (`../vercel-crack/`) — a conic
Status: battle-tested on the Vercel triangle hero (
../vercel-crack/) — a conic rainbowhard-light-blended over a grayscale WebGL shader, masked by a radial vignette pseudo-element, with a wireframe SVG on top. Both defects that made it "look off" lived here, in the CSS coupling — not in the (verbatim-correct) shader.
Make a faithful replica of a component whose look is an emergent composite of CSS layers by extracting the computed runtime state and the layer graph (stacking order, blend modes, isolation boundaries, masks, pseudo-elements) — not by reading the authored stylesheet and not by eyeballing the blended result.
One of a family of per-rendering extraction skills. This one owns the compositional
coupling layer: the CSS that sits over and combines other layers. When the pixels of one
layer come from a <canvas> or <svg>, extract that layer with the webgl /
svg-js-generator sibling, and use this skill for how the layers stack and blend.
Driver: browser-harness (heredoc form, on $PATH). Verify with the verify skill.
Extract the COMPUTED runtime state and the LAYER GRAPH — never the authored rule. Two traps, both proven on Vercel:
Authored ≠ computed. Vercel's shipped stylesheet rule said white bg / pastel /
yellow-first; the CSS vars were overridden at runtime to black bg / saturated /
blue-first. Reading the source .css lied. Always reconcile against getComputedStyle
on the live node — the same "capture runtime, not source" law as live GL uniforms.
The look is in the coupling, not the layers. The hard part isn't any single gradient —
it's how they blend. A mix-blend-mode layer's appearance depends entirely on what it
blends against, which is decided by the stacking / isolation graph. Miss the graph
and every individual layer can be byte-correct while the composite is 2.3× off.
Corollary: difficulty is compositional, not modal. Classify a component by how coupled its layers are, not by render type. A lone declarative element is trivial (copy computed style); a blend-coupled stack over an animated canvas is the frontier.
mix-blend-mode blends an element with its backdrop — the accumulated paint behind it,
within the nearest stacking context that isolates. So three things are load-bearing and
must be extracted together, as a unit:
hard-light, screen, …),isolation:isolate, or a z-index + positioning, or opacity<1, etc.), andVercel proof. Identical shader + identical conic-rainbow markup. Isolating at the outer
.hero(opaque black bg) madehard-lightblend against black → composite came out lum 54, sat 0.61 (dark, oversaturated). Isolating one level down at the.gradientwrapper (transparent bg) made it blend against shader-over-transparent → lum 73, sat 0.19, matching live exactly. Same inputs, 2.3× brightness swing, purely from where the stacking context lives. Copyingmix-blend-modewithout copying the isolation boundary is a silent, large error.
browser-harness <<'PY'
import json
r = js(r"""(() => {
const el = document.querySelector('YOUR TARGET SELECTOR');
const all = [el, ...el.querySelectorAll('*')];
const blends = all.filter(e=>getComputedStyle(e).mixBlendMode!=='normal').length;
const masks = all.filter(e=>getComputedStyle(e).maskImage!=='none' || getComputedStyle(e).webkitMaskImage!=='none').length;
const grads = all.filter(e=>/gradient/.test(getComputedStyle(e).backgroundImage)).length;
const iso = all.filter(e=>getComputedStyle(e).isolation==='isolate').length;
return {layers:all.length, blends, masks, grads, isolations:iso,
hasCanvas:!!el.querySelector('canvas'), hasSvg:!!el.querySelector('svg')};
})()""")
print(r)
PY
blends/masks/grads > 0, or multiple stacked positioned layers → this skill (the
coupling is real).hasCanvas/hasSvg → extract that layer with the webgl / svg-js-generator
sibling; use this skill for the stack around it.Don't just dump the target's style. Walk every descendant and its ::before/::after,
recording the properties that decide compositing. The defect is usually a layer you didn't
know existed.
browser-harness <<'PY'
import json
out = js(r"""(() => {
const root = document.querySelector('YOUR TARGET SELECTOR');
const dump = (el,pseudo) => { if(!el) return null; const s=getComputedStyle(el,pseudo||null);
return {sel:(el.className||el.tagName)+(pseudo||''),
z:s.zIndex, pos:s.position, opacity:s.opacity, isolation:s.isolation,
blend:s.mixBlendMode, bg:s.backgroundColor,
bgImage:s.backgroundImage.slice(0,140),
mask:(s.maskImage||s.webkitMaskImage), filter:s.filter, transform:s.transform,
content: pseudo? s.content : undefined}; };
const rows=[]; const walk=el=>{ rows.push(dump(el));
for(const p of ['::before','::after']){ const ps=getComputedStyle(el,p);
if(ps.content && ps.content!=='none' && ps.content!=='normal') rows.push(dump(el,p)); }
for(const c of el.children) walk(c); };
walk(root); return rows;
})()""")
print(json.dumps(out, indent=1))
PY
The pseudo-element sweep is not optional. Vercel's whole structural defect was a
radial-gradientvignette on.gradient::before(z-index:100) that crushed the upper/side field to black. The prior attempt had checked only the root's::after, called it "no vignette," and missed it. Enumerate::beforeand::afterfor every element in the subtree, with z-index — overlays hide on child wrappers' pseudo-elements.
For each layer, trust getComputedStyle on the live node, not the .css source. Watch for
runtime-overridden CSS variables (theme toggles, container queries, JS that rewrites
--vars). If a transform/scale only applies at some @container width, capture it at the
actual rendered width (Vercel's authored scale(1.7) was none at desktop).
For each mix-blend-mode layer, determine which ancestor isolates it and that ancestor's
background. Capture isolation, z-index, position, opacity for the chain. Note gradient
geometry precisely — conic-gradient(from <angle> at <x> <y>, …) orientation and
radial-gradient(<size> at <x> <y>, …) center decide where color vs transparency lands (and
therefore which parts the blend lights up vs crushes).
Reproduce the same isolation boundary on the same element with the same background, the same z-order, the same pseudo-element overlays. A blend layer placed in a different stacking context is the #1 way a faithful-looking rebuild comes out wrong.
Static composite → ../verify/scripts/diff_oracle.py. Animated layer underneath (shader/keyframes) →
../verify/scripts/temporal_oracle.py (flicker field). When brightness/color is "a little off" with correct
hue, suspect the blend backdrop / isolation boundary and use isolate-and-measure (toggle
one stacking property per capture, measure the region mean) — don't derive blend math by hand.
::after. The missing layer was
.gradient::before. Sweep the whole subtree's pseudo-elements with their z-index.mix-blend-mode isolation boundary is load-bearing — extract it as a unit. Which element
isolates + that element's bg swung brightness 2.3× and flipped saturation 0.61↔0.19 with
identical layer markup. Don't copy the blend mode without the stacking context it resolves
against.--var overrides. getComputedStyle, always.background-color on a blend layer is part of the blend. Vercel's rainbow had
background-color:#000 under the conic; the opaque-black backing means transparent-gradient
regions hard-light as black, not as "show through." Capture the layer's bg, not just its image.conic-gradient(from 180deg at 50% 70%) puts the
transparent seam pointing down from a low center; radial-gradient(150% 150% at 50% 150%)
centers the bloom below the bottom edge. A few degrees / percent off relocates the whole
bright region. Copy the numbers verbatim from computed style.getComputedStyle reports the requested value, not the rendered one — fonts especially.
fontFamily:"SuisseIntl" stays true when the glyphs silently fell back, or when a weight
resolves to the wrong file (a -Regular @font-face mapped to weight 300, not 400 — a
naming trap that inks ~9% heavier yet looks fine to the eye). Confirm text with a render
oracle: measureText advance widths + a glyph ink-column profile vs the live raster (a real
fallback shows ~16–130px width gaps; the right file matches to sub-px). Same law as runtime
--var overrides — trust the rendered value, not the requested one.::before/::after.
getComputedStyle(el).backgroundColor on the element reads transparent while the pill, the
0 0 0 1px ring, and the backdrop-filter sit on its pseudo-element. Query the
pseudo-elements' background/box-shadow/backdrop-filter, not just the element's — and when
a source class says …Typing…/…Cursor…, there's animated structure (typed text, blinking
caret) the static computed style won't show.@property-animated gradients ship as JS — read the worklet
source (closer to the svg-js-generator playbook) rather than treating it as declarative CSS.examples/vercel-triangle.md — conic rainbow hard-light over a grayscale WebGL shader
.gradient::before radial vignette + wireframe SVG. Both fixes (missing pseudo-element
vignette; isolation boundary on .gradient not .hero) and the live-computed value table.
Replica + full crack write-up: ../vercel-crack/ (HANDOFF.md).When you finish a composite extraction, add examples/<site>-<component>.md here instead of
threading specifics into this file.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub novarii/component-extractor --plugin component-extractor