Schematic — section hatch
Variant: Section A–A
The cutting plane is declared in one beat — a plane is an instruction, not geometry, so it does not get a draw-on — and then the drawing answers it: sixteen hatch strokes applied lower-left to upper-right, one pen pass per 111ms beat, all clipped to the material and none to the bore. Shading is applied, never faded in, because a section face is a claim about where the metal is. The whole application runs off a single registered progress number that every stroke reads its own start time out of, so the cascade is arithmetic rather than sixteen tuned keyframe blocks. As the shading completes, the plane and its flags withdraw: once the face is hatched the view has become the section, and a drawing cannot carry the plane and its own answer at once. Only then does SECTION A–A land — the title of a finished drawing, not a promise about one in progress. Reach for it where a cutaway or an under-the-surface reveal needs to feel drafted rather than rendered.
The source
A complete HTML document. Paste it into an empty .html file and it plays — no stylesheet, script, font, or image to fetch.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Schematic — section hatch</title>
<style>
/* One registered number drives all sixteen hatch strokes: each stroke
reads its own start out of the shared progress, so the application
order is arithmetic, not sixteen hand-tuned keyframe blocks. */
@property --_p {
syntax: '<number>';
initial-value: 0;
inherits: true;
}
:root {
/* Neutrals — fixed, never remixed. */
--ivory: #faf9f5;
--paper: #ffffff;
--oat: #e3dacc;
--slate: #141413;
/* Knobs — the remix API. A knob exists iff declared here and used below. */
--hue: 38.8;
--lift: 0;
--chroma: 1;
--speed: 1;
/* Accents — derived from the knobs with the family's offsets baked in,
so one hue roll moves them together. Defaults reproduce the original hex. */
--clay: oklch(calc(0.6724 + var(--lift)) calc(0.1308 * var(--chroma)) var(--hue));
--clay-d: oklch(calc(0.5797 + var(--lift)) calc(0.127 * var(--chroma)) var(--hue));
--olive: oklch(
calc(0.6118 + var(--lift)) calc(0.0713 * var(--chroma)) calc(var(--hue) + 88.3)
);
--rust: oklch(
calc(0.5408 + var(--lift)) calc(0.1357 * var(--chroma)) calc(var(--hue) - 10.3)
);
--_muted: color-mix(in srgb, var(--slate) 62%, transparent);
/* Clock — 7s: the cut is declared in the first tenth, the shading is
applied over the next third at a steady 111ms a stroke, and the
finished section holds for over two seconds before clearing. */
--cycle: 7s;
--t: calc(var(--cycle) / var(--speed));
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
background: var(--ivory);
font-family:
system-ui,
-apple-system,
'Segoe UI',
Roboto,
sans-serif;
color: var(--slate);
}
.sheet {
width: min(320px, 90vw);
height: auto;
display: block;
}
.part {
fill: var(--paper);
stroke: var(--slate);
stroke-width: 2;
}
.bore {
fill: var(--ivory);
stroke: var(--slate);
stroke-width: 2;
}
.anno {
animation: anno-clear var(--t) var(--ease-out) infinite;
}
/* The plane arrives in one beat rather than drawing on — a cutting plane
is an instruction, not geometry — and it withdraws as the shading
completes: once the face is hatched, the view has become the section,
and a drawing cannot carry the plane and its own answer at once. */
.plane {
stroke: var(--slate);
stroke-width: 1.25;
stroke-dasharray: 9 3 2 3;
opacity: 0;
animation: plane-in var(--t) var(--ease-out) infinite;
}
.flag {
opacity: 0;
animation: flag-top var(--t) var(--ease-out) infinite;
}
.flag.south {
animation-name: flag-bottom;
}
.flag line {
stroke: var(--slate);
stroke-width: 1.5;
}
.flag path {
fill: var(--slate);
}
.flag text {
fill: var(--slate);
font-family: inherit;
font-size: 13px;
font-weight: 600;
}
.hatchset {
animation: apply var(--t) linear infinite;
}
/* Every stroke is ruled lower-left to upper-right, one pen pass per
111ms beat — shading is applied, never faded in, because a section
face is a claim about material. Each segment is pre-trimmed to the
face's bounding box so ink lands on the first frame of its draw; the
corner rounds and the bore are left to the clip. Start times are
--_i * 5.55 in progress units; the *6 gain is one stroke's draw span
(16.7 units) normalised to a 0-100 dash. */
.h {
stroke: var(--clay);
stroke-width: 1.2;
opacity: 0.6;
fill: none;
stroke-dasharray: 100 200;
stroke-dashoffset: calc((100 - clamp(0, (var(--_p) - var(--_i) * 5.55) * 6, 100)) * 1px);
}
.caption {
fill: var(--_muted);
font-family: inherit;
font-size: 12.5px;
font-weight: 600;
letter-spacing: 0.12em;
opacity: 0;
animation: caption-in var(--t) var(--ease-out) infinite;
}
@keyframes apply {
0%,
16% {
--_p: 0;
}
44.5%,
100% {
--_p: 100;
}
}
@keyframes plane-in {
0%,
5% {
opacity: 0;
}
9%,
38% {
opacity: 0.55;
}
45%,
100% {
opacity: 0;
}
}
/* The flags land top then bottom, the order you read a cutting plane,
and leave with it. */
@keyframes flag-top {
0%,
9% {
opacity: 0;
}
11.5%,
38% {
opacity: 1;
}
45%,
100% {
opacity: 0;
}
}
@keyframes flag-bottom {
0%,
10.5% {
opacity: 0;
}
13%,
38% {
opacity: 1;
}
45%,
100% {
opacity: 0;
}
}
/* The name arrives only once the plane has cleared and the shading is
done: SECTION A-A is the title of a finished drawing, not a promise
about one in progress. */
@keyframes caption-in {
0%,
46% {
opacity: 0;
transform: translateY(2px);
}
50.5%,
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes anno-clear {
0%,
85% {
opacity: 1;
}
92%,
100% {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
/* The application is the content; it runs at half rate, which slows
each pen stroke to about 660ms — an uncover rather than a flick. */
.anno,
.plane,
.flag,
.hatchset,
.caption {
animation-duration: calc(var(--t) * 2);
}
}
</style>
</head>
<body>
<svg
class="sheet"
viewBox="0 0 300 170"
role="img"
aria-label="A section drawing: a dash-dot cutting plane is declared through a plate, diagonal hatch strokes shade the cut material one by one while skipping the bore, the plane withdraws, and the caption SECTION A–A lands once the shading is complete"
>
<defs>
<clipPath id="face" clip-rule="evenodd">
<path
clip-rule="evenodd"
d="M78 46 H222 A8 8 0 0 1 230 54 V102 A8 8 0 0 1 222 110 H78 A8 8 0 0 1 70 102 V54 A8 8 0 0 1 78 46 Z
M166 78 A16 16 0 1 1 134 78 A16 16 0 1 1 166 78 Z"
/>
</clipPath>
</defs>
<rect class="part" x="70" y="46" width="160" height="64" rx="8" />
<circle class="bore" cx="150" cy="78" r="16" />
<g class="anno">
<line class="plane" x1="150" y1="26" x2="150" y2="130" />
<g class="flag">
<line x1="150" y1="26" x2="143" y2="26" />
<path d="M134 26 L143 24.5 V27.5 Z" />
<text x="127" y="31" text-anchor="end">A</text>
</g>
<g class="flag south">
<line x1="150" y1="130" x2="143" y2="130" />
<path d="M134 130 L143 128.5 V131.5 Z" />
<text x="127" y="135" text-anchor="end">A</text>
</g>
<g class="hatchset" clip-path="url(#face)">
<path class="h" style="--_i: 0" pathLength="100" d="M70 59 L83 46" />
<path class="h" style="--_i: 1" pathLength="100" d="M70 72 L96 46" />
<path class="h" style="--_i: 2" pathLength="100" d="M70 85 L109 46" />
<path class="h" style="--_i: 3" pathLength="100" d="M70 98 L122 46" />
<path class="h" style="--_i: 4" pathLength="100" d="M71 110 L135 46" />
<path class="h" style="--_i: 5" pathLength="100" d="M84 110 L148 46" />
<path class="h" style="--_i: 6" pathLength="100" d="M97 110 L161 46" />
<path class="h" style="--_i: 7" pathLength="100" d="M110 110 L174 46" />
<path class="h" style="--_i: 8" pathLength="100" d="M123 110 L187 46" />
<path class="h" style="--_i: 9" pathLength="100" d="M136 110 L200 46" />
<path class="h" style="--_i: 10" pathLength="100" d="M149 110 L213 46" />
<path class="h" style="--_i: 11" pathLength="100" d="M162 110 L226 46" />
<path class="h" style="--_i: 12" pathLength="100" d="M175 110 L230 55" />
<path class="h" style="--_i: 13" pathLength="100" d="M188 110 L230 64" />
<path class="h" style="--_i: 14" pathLength="100" d="M201 110 L230 73" />
<path class="h" style="--_i: 15" pathLength="100" d="M214 110 L230 84" />
</g>
<text class="caption" x="150" y="158" text-anchor="middle">SECTION A–A</text>
</g>
</svg>
</body>
</html>
The variant family
Same idea, one axis moved. Compare them side by side before you commit.