Summary
This pattern was discovered during the Phase 2 verification of the Laws of UX motion ontology. Three concepts — goal-gradient-effect, zeigarnik-effect, and postels-law — on the lawsofux.com production page resolve to the same underlying scaffold: a vertical stack of horizontal bands with stepped per-band opacity. One generator covers three distinct UX heuristics across three different color domains. That is the case for promotion.
Three roles, one scaffold
Generator parameters
The primitive accepts five core parameters and one optional one. Stagger is fixed at row-by-row top-to-bottom, ~120ms per row, cubic ease-out, with a 2.5s hold before loop.
| parameter | type | role |
|---|---|---|
| row_count | int (4–9) | number of horizontal bands stacked vertically |
| opacity_start | float (0–1) | fill-opacity of the top band |
| opacity_end | float (0–1) | fill-opacity of the bottom band; intermediate rows interpolate |
| cream_row | int | null | row index that gets full-opacity cream override (null = no override) |
| truncation_row | int | null | row index whose width is truncated; null = full width |
| truncation_pct | float (0–1) | fraction of canvas width the truncated row occupies |
Cavalry pseudo-code
function buildOpacityBands(api, prefix, params) {
var step = params.stripeH + params.gutter;
var topY = ((params.rowCount - 1) * step) / 2;
var bg = api.primitive("rectangle", prefix + "bg");
api.set(bg, { "generator.dimensions": [params.canvasW, params.canvasH] });
api.setFill(bg, true);
api.set(bg, { "material.materialColor": params.bg });
for (var r = 1; r <= params.rowCount; r++) {
var t = (r - 1) / (params.rowCount - 1);
var opacity = params.opacityStart + (params.opacityEnd - params.opacityStart) * t;
var fill = params.mutedColor || params.cream;
if (params.creamRow === r) { opacity = 1.0; fill = params.cream; }
var width = params.canvasW * 0.85;
if (params.truncationRow === r && params.truncationPct) {
width = params.canvasW * params.truncationPct;
}
var shape = api.primitive("rectangle", prefix + "row_" + r);
api.set(shape, {
"generator.dimensions": [width, params.stripeH],
"position.y": topY - (r - 1) * step,
"opacity": 0
});
api.setFill(shape, true);
api.set(shape, { "material.materialColor": fill });
var t0 = (r - 1) * 4, t1 = t0 + 8;
api.keyframe(shape, t0, { "opacity": 0 });
api.keyframe(shape, t1, { "opacity": Math.round(opacity * 100) });
api.magicEasing(shape, "opacity", t1, "EaseOut", "");
}
}
Rationale for promotion
Three independently-designed Laws of UX concepts — encoded by an external designer years before ShurIQ existed — converge on the same underlying scaffold. That convergence is rare and meaningful. When one motion family carries three distinct semantic loads (momentum, cascade, incompleteness) without any visual recoloring of the bands themselves — only opacity steps and one truncation — it stops being a coincidence and starts being a grammar element worth naming.
For shuriq-motion specifically, this primitive is a natural sibling to value-flow (which moves left-to-right) and rubric-axis (which charts a single dimension). opacity-stepped-bands charts a vertical decay across N items — the missing axis: temporal/causal layering rather than spatial flow. Promoting it into the visual grammar gives ShurIQ a reusable component for partial recall, multi-source ingestion with canonical selection, open-loop gap cards, and proximity-to-goal indicators in regulatory-grade brand intelligence reports — patterns we already need but currently render ad hoc.
opacity-stepped-bands is the proposed handle) and merge it into shuriq-motion/references/visual-grammar.md. Until then, this page is the spec a future implementer can reference without re-discovering the pattern.