← MADLAB / library

MADLAB / TUTORIAL

Build Shape Grid from zero.

A complete, step-by-step breakdown of Shape Grid: from the static contract to repeated shapes positioned on a responsive grid, responsive behavior, accessibility, and production tuning.

LEVEL / INTERMEDIATETIME / 2–3 HOURSSTACK / REACT + TYPESCRIPT + CSS

00 / Before you code

The mental model.

Shape Grid is a small system: stable structure, explicit state, and one visual rule.

The interaction should earn its cost. If repeated shapes positioned on a responsive grid is removed, the interface should remain understandable.

01 / Build sequence

From blank file to interaction.

01

Define the component contract.

Write down the smallest public API for Shape Grid. Keep visual decisions in props so the repeated shapes positioned on a responsive grid can be reused without rewriting the component.

tsx
// Local source: src/ts-tailwind/Backgrounds/ShapeGrid/ShapeGrid.tsx
// Start with the smallest visible version of ShapeGrid.

type ShapeGridProps = {
  className?: string;
  color?: string;
};

export function Example({ className, color = "#ff2a2a" }: ShapeGridProps) {
  return (
    <div className={className} style={{ color }}>
      {/* Add the Shape Grid behavior here. */}
    </div>
  );
}
02

Build the quiet static state first.

Render the readable fallback before adding motion. The component should still communicate its purpose when JavaScript is delayed or motion is reduced.

03

Normalize the input and measurements.

Clamp numbers, handle an empty value, and measure the real container instead of assuming the viewport. This removes most edge-case bugs before the animation starts.

04

Separate structure from motion.

Keep markup, state, and animation calculations in separate layers. For Shape Grid, the visual structure should remain stable while repeated shapes positioned on a responsive grid changes over time.

05

Implement the one useful interaction.

Add the core rule only: repeated shapes positioned on a responsive grid. Use one source of truth for the active value and keep pointer, scroll, or timer listeners passive where possible.

06

Use one animation loop with a clear exit.

Start requestAnimationFrame only when a value changes. Keep the frame id in a ref, interpolate toward the target, and stop when the difference is below a small threshold.

07

Make the layout responsive.

Test narrow mobile widths, wide desktop containers, and text wrapping. Prefer CSS dimensions and ResizeObserver over hard-coded pixels tied to one screenshot.

08

Add reduced-motion and interaction fallbacks.

Respect prefers-reduced-motion and keep a non-motion state. Keyboard focus, readable labels, and a useful static result matter more than a decorative effect.

09

Integrate it into a real section.

Place Shape Grid behind a real message, card, or control. Keep content above decorative layers, preserve the MADBAK palette, and avoid letting motion compete with the hierarchy.

10

Tune, profile, and ship the smallest good version.

Check the effect on a mid-range device, remove unnecessary listeners, and keep the first release focused. Once the behavior is useful, expose only the controls that future projects really need.

02 / Debug checklist

If it feels wrong.

  • The preview is blank: confirm the container has a real height and the client component mounted.
  • It jumps on resize: keep one measured source of truth and cancel stale animation frames.
  • It feels heavy: reduce work per frame, remove duplicate listeners, and stop the loop when idle.
  • It is inaccessible: keep a readable static state, visible focus, and a reduced-motion path.

MADLAB / source

Ready to adapt it?

Open local source ↗