From surface
The advanced visual layer for a frontend build — where icons, imagery, Canvas, WebGL, GPU, and generative technique live, and where "random icon packs", "hero image that tanks LCP", and "three.js because it looks cool" are decided. Use after the core visual language is set (color, type, form), when establishing or auditing the graphics and generative layer, or when a UI uses icons that don't match, images that shift layout, or animated surfaces that drain the budget. The one shift: the DOM is the default and the budget — you drop to Canvas / SVG / WebGL / GPU only where the DOM genuinely cannot go, ALWAYS with a fallback and a perf budget, and icons come from ONE coherent set (matched stroke, grid, optical size). The agent reaches for canvas/three.js as a reflex, mixes three icon families, lazy-loads the LCP hero, and feels no wrongness — so the escape-to-GPU and the icon/imagery systems must be decided and gated. Triggers on "icon / icon set / icon pack / SVG icon / icon font", "image / hero / LCP / CLS / layout shift / srcset / lazy load / AVIF / WebP / blurhash / placeholder", "illustration / spot illustration / empty state / art direction", "canvas / WebGL / WebGPU / three.js / PixiJS / shader / GLSL / WGSL / SDF / raymarching", "GPU / generative / procedural / particle / data visualization / Deck.gl", "retina / devicePixelRatio / HiDPI / blurry canvas", "icon accessibility / aria-hidden / icon-only button", "duotone / photo treatment".
How this skill is triggered — by the user, by Claude, or both
Slash command
/surface:graphics [the UI / component / page where icons, imagery, or canvas/GPU rendering lives][the UI / component / page where icons, imagery, or canvas/GPU rendering lives]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!--
!checklist init ${CLAUDE_SKILL_DIR} --force
A graphics studio keeps every rendered mark — the icon drawn to a keyline, the hero photograph boxed to its ratio, the procedural surface compiled to the GPU — under a discipline that makes the whole surface read as one hand at work. graphics is the advanced visual lens of the atelier suite: where the other skills decide what color the surface wears and how the type scales, graphics decides what renders when the document model can't, and makes that too a system, not a pile of ad-hoc choices. Its product is a written graphics contract: one icon system (one family, one stroke, one optical-size rule), one imagery system (allowed ratios, CLS-proof pipeline, loading strategy, alt discipline), and a disciplined Canvas / WebGL / GPU layer with a DOM fallback and a frame budget — each committed before any component consumes them. It runs across gated stages and will not advance past a GATE until the checklist tool clears it.
The governing fact: the DOM is the default and the budget — escape to Canvas/WebGL/GPU only where the DOM genuinely can't go, always with a fallback, always within a frame budget, and always with one coherent icon set and an aspect-ratio-boxed imagery system. Mixed icon packs (three families, different strokes, mismatched corners), a hero <img> with loading="lazy" (the single most common way to wreck LCP), canvas blurry on retina because no one applied the devicePixelRatio fix, or three.js imported for a UI that a <div> and a CSS transition could have handled — each is a default move, and their sum is the technical and visual noise the eye and the profiler both read as wrong without being able to name it.
This is where the agent era bites:
loading="lazy" is a sensible default for most images; applied to the above-the-fold hero it cracks the LCP score in half. The LCP image gets loading="eager" + fetchpriority="high" + a <link rel="preload"> in the head. This is the rule the agent consistently breaks.<canvas> on a 2× display is blurry unless the backing pixel buffer is scaled by devicePixelRatio and CSS holds the logical size. The agent sets up the canvas loop, ships the blur, and moves on.<canvas> fallback content, a parallel accessible DOM, a data table) is non-negotiable; without it the feature is invisible to a screen reader. And an icon-only button without an aria-label is equally invisible.Read references/dom-vs-canvas-vs-webgl-and-fallback.md first — the decision tree that earns each escape from the DOM, the fallback contract, and the frame budget. Load at the start, re-check at every gate.
Speak the user's language. The decisions are the user's — which icon family, which allowed aspect ratios, which generative technique fits the surface. Read their fluency and gloss terms on first use (keyline / optical sizing, immediate mode vs retained scene graph, HiDPI / devicePixelRatio, CLS / aspect-ratio, LCP / fetchpriority, SDF / signed distance field, GLSL / WGSL, fallback path, frame budget / 16.7ms). A rendering pipeline the user can't trace is imposed infrastructure, not shared craft.
The depth lives in references/. Open each when a stage sends you there — not all upfront.
The arc is one graphics contract. Four stages — fallback contract · icon system · imagery system · Canvas/WebGL/GPU — turn a surface with ad-hoc visuals into one governed by a single rendering discipline: the DOM is the default; icons read as one hand drew them; images never shift layout or break LCP; generative/GPU work earns its place with a fallback and a budget.
graphicsruns after the core visual language is set (color, type, form) and its contract is what the surface's visual integrity depends on at the advanced layer.
Greenfield or retrofit? The entry differs, the gates do not. Starting clean: walk STAGE 0→3 in order. Auditing an existing surface: inventory first — every icon family in use, every
<img>missingaspect-ratioor mis-usingloading="lazy", every canvas call missing the dpr fix — then bring that inventory to STAGE 0 and run the gates unchanged. The inventory tells you what exists; the gates tell you what's right.
Open references/dom-vs-canvas-vs-webgl-and-fallback.md. Decide the rendering boundary before any canvas or WebGL code is written.
<canvas> fallback content (a static image, a description), a parallel accessible DOM mirror, or a data table/text alternative — one of these is non-negotiable. Canvas and WebGL are accessibility black holes; AT sees only what is in the DOM alongside them.opacity run on the compositor thread without touching layout or paint; that handles the majority of animation use-cases. Canvas or WebGL animation is for work CSS genuinely cannot do.checklist check fallback dom-first-and-fallback-contractchecklist verify fallbackOpen references/icon-system.md. The icon system is decided and written down before any icon is placed in a component.
stroke-linecap / stroke-linejoin must match the family), and style vocabulary (which style is the default; which is the active/selected state). Without this spec, every new icon is a hand-pick.vector-effect: non-scaling-stroke.fill/stroke: currentColor makes the icon inherit its text color and respond to every state (hover/active/disabled) for free. Remove fixed width/height, keep the viewBox, let CSS control size. SVG sprite (<use href="#icon">) for high-repeat cases. Icon fonts: avoid in new systems (legacy, a11y-broken, single-color; variable icon fonts are the modern exception).aria-hidden="true" + focusable="false". Icon-only button (no visible label): aria-label or visually-hidden text — an icon-only button with no accessible name is invisible to a screen reader. Hit target ≥44px (padding brings a 24px icon up). Contrast ≥3:1 (WCAG 1.4.11). Icon-only buttons carry a tooltip that is keyboard- and screen-reader-reachable. Don't lean on obscure metaphors alone; pair with a text label when universality is in doubt.checklist check icons icon-system-one-family-written-specchecklist verify iconsOpen references/imagery-and-illustration.md. The imagery pipeline is decided before any <img> is placed in production code.
width+height or an aspect-ratio declaration so the browser reserves space before the image loads — this is the highest-leverage non-default for preventing CLS. A layout-shifting hero image is almost always a missing aspect-ratio. Use object-fit: cover + object-position for focal-point cropping across ratios.loading="eager" + fetchpriority="high" + a <link rel="preload"> in the <head>. Everything below the fold gets loading="lazy" + decoding="async". Lazy-loading the LCP image is the single most common way to crater the LCP metric.<picture>/<source>. Vectors, icons, logos: SVG. Use an image CDN (Cloudinary / imgix / Next Image) to transcode and resize on demand rather than pre-baking every variant. srcset + sizes for responsive resolution; <picture> + <source media="..."> for art direction (a different crop or image per viewport, not just a smaller file).aspect-ratio. Fill it: LQIP/blur-up (a tiny blurred base64 placeholder or a BlurHash/ThumbHash token, cross-faded to the full image on load) or a dominant-color block. This turns a blank-then-pop into a perceived-fast blur-in.feColorMatrix or mix-blend-mode + gradient) pulls any photo into the brand — premium, non-default. Scrim (gradient overlay) for text-over-image legibility.currentColor/CSS-variable theming) — light-background art on a dark page gets white fringes. Transparent-PNG white anti-aliased edges show on dark → use SVG or correct alpha. Logos ship light and dark versions. Photos can take a different treatment or a slight filter: brightness(.85) to cut glare.alt. Decorative → alt="" (empty, so SR skips it — a missing alt makes SR read the filename). Functional image (is a link/button) → alt describes the action. Complex image (chart) → short alt + adjacent long description. Never start with "image of." Text baked into an image is inaccessible; if unavoidable, the alt must contain that text.<Image> wrapper component (Next/Image et al.) that bakes in format selection, srcset, loading strategy, aspect-ratio, placeholder, and mandatory alt — so every image is handled correctly by construction.checklist check imagery imagery-and-illustration-systemchecklist verify imageryOpen references/canvas-webgl-gpu.md. Every canvas or WebGL surface has a fallback path, a frame budget, and a named reason for leaving the DOM.
devicePixelRatio, then ctx.scale(dpr, dpr), CSS size kept at logical values — without this, every canvas is blurry on 2× displays, always. Render loop: requestAnimationFrame (syncs to display, auto-pauses on hidden tabs); use delta time for frame-rate-independent motion. State: save()/restore() around transforms; don't leak. Performance: OffscreenCanvas + Web Worker (render off the main thread); layering (static + dynamic on separate canvases); dirty rectangles (redraw only changed regions); batch by style (fillStyle switches are costly); Path2D to cache paths; avoid getImageData on the hot path (forces sync flush); avoid sub-pixel positioning (blurry). Hit detection: none — implement it (isPointInPath or an offscreen hit-canvas with a unique color per object). Text: measureText exists; line-breaking is your own.(uv) → color running once per pixel in massive parallel (SIMT) — no shared state. Branches are expensive (divergence: both paths execute, one is discarded); write branchless: step / mix / smoothstep / clamp instead of if. SDF (signed distance fields): encode shapes as "distance from this point to the shape boundary" — resolution-independent forms, crisp text, rich effects (Inigo Quilez route). Raymarching: render 3D by marching through SDFs inside a single fragment shader. Procedural noise: Perlin / Simplex / Worley. GLSL toolbox: mix / step / smoothstep / clamp / dot / cross / length / normalize / fract / mod.colorSpace can be set to display-p3 for wide-gamut surfaces.nearest for pixel art, linear otherwise. Dithering/blue noise against banding (connects to gradients in form).readPixels / getImageData / gl.finish force synchronization — keep them off the hot path. Explicitly release GPU resources (textures, buffers) or leak GPU memory. Pause on hidden tabs (Page Visibility API).webglcontextlost) — you must listen and restore. Coordinate mapping: pointer/touch → canvas coords via getBoundingClientRect + dpr. Resize: ResizeObserver to reset canvas size + dpr. Tainted canvas: a cross-origin image without CORS blocks getImageData/toDataURL — textures need crossOrigin.checklist check canvas canvas-webgl-gpu-with-fallback-and-budgetchecklist verify canvaschecklist show — confirm all four stages passed.checklist done — clear this run's state.graphics is the suite's advanced rendering conscience — the place where the DOM's ceiling is honestly named and the work that earns the right to cross it is gated. It runs after the core visual language (color, type, form) is set, and its contract — one icon family, aspect-ratio-boxed imagery, LCP-safe loading, and a canvas/WebGL layer with a DOM fallback — is what the surface's visual integrity depends on at the advanced layer. The through-line is the suite's own — push correctness into structure — applied to rendering: an icon drawn to a written spec can't drift the way a hand-picked icon can, an image with a mandated aspect-ratio can't shift layout, a canvas loop with a written frame budget can't silently bloat. The line that keeps graphics honest: the references hold the encodable technique (which GPU execution model, how a retina fix works, how a BlurHash cross-fades); the taste decisions — which icon family, which allowed aspect ratios, which generative technique fits the surface's purpose — stay gates the user clears. Blur that line and the skill becomes a rendering tutorial; hold it and it stays a craft lens.
aria-label or visually-hidden text is non-negotiable.aria-hidden — the screen reader reads it anyway; add aria-hidden="true" + focusable="false".vector-effect: non-scaling-stroke or per-size art.aspect-ratio or width/height — CLS waiting to happen; the browser can't reserve space; always set one.loading="eager" + fetchpriority="high" + <link rel="preload">.<picture> + AVIF → WebP → JPEG.<picture>.devicePixelRatio and scale the context.step / mix / smoothstep.readPixels / gl.finish on the render loop — CPU-GPU sync stall; keep them off the hot path.webglcontextlost handler — the GPU will eventually reclaim the context; handle and restore.npx claudepluginhub iamk77/skill --plugin atelierProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.