Rectangle Trick

A self-contained <svg> histogram with a candidate-rectangle overlay. Pass heights as a numeric array (one bar per entry) and a rectangle describing the currently-evaluated span — { left, right, height } — and the component draws every bar baseline-aligned plus a dashed translucent rectangle covering bars left..right at height. The classic teaching tool for the largest-rectangle-in-histogram problem: every monotonic-stack pop produces one of these candidate rectangles, and the answer is the maximum-area one.

Purely visual — no clicks, no internal state. Drive it from a step controller or any reducer that produces a rolling best-rectangle guess.

Histogram with heights 2, 1, 5, 6, 2, 3. Current rectangle spans bars 0-0 at height 2.215623area = 2
Customize
Layout
36
140
Display
1

Installation

npx shadcn@latest add https://craftbits.dev/r/rectangle-trick.json

Usage

import { RectangleTrick } from "@craft-bits/core";
 
<RectangleTrick
  heights={[2, 1, 5, 6, 2, 3]}
  rectangle={{ left: 2, right: 3, height: 5 }}
  rectangleLabel="area = 10"
/>

Bare histogram (no overlay):

<RectangleTrick heights={[2, 1, 5, 6, 2, 3]} />

Drive the overlay from a monotonic-stack stepper — when a bar at index r pops the entry at index p, the resulting candidate spans (stackTop + 1)..(r - 1) at heights[p]:

<RectangleTrick
  heights={heights}
  rectangle={{ left: candidateLeft, right: candidateRight, height: candidateHeight }}
  rectangleLabel={`area = ${candidateArea}`}
  tone={candidateArea === best ? "success" : "accent"}
/>

Tall thin bars, success tone for the running best:

<RectangleTrick
  heights={heights}
  rectangle={best}
  rectangleLabel={`best area = ${bestArea}`}
  barWidth={20}
  plotHeight={200}
  tone="success"
/>

Understanding the component

  1. Self-contained SVG. One <svg> sized to fit every bar plus the value / rectangle labels. The caller positions it; no top / left / transform: translate is set internally.
  2. Inclusive span semantics. rectangle is { left, right, height } with right inclusive — the rectangle covers right - left + 1 bars. Out-of-range indices are clamped silently so the overlay never escapes the chart bounds.
  3. Auto-scaled y-axis. maxHeight defaults to the tallest bar in heights, so the overlay always fills the available vertical space. Pass maxHeight explicitly when you want stable scaling across step-to-step renders.
  4. Five tones. default reads as "neutral candidate", accent as "currently evaluating", success as "this is the best so far", warning as "watch this rectangle", error as "rejected". Bars inside the overlay pick up the tone; bars outside stay neutral.
  5. Bar values. Each bar carries its integer height above the baseline by default. Pass showValues={false} for a cleaner static chart.
  6. Spring transitions. Bar heights, rectangle position, and rectangle dimensions all animate with SPRINGS.smooth. The label slides under the overlay rather than blinking, so scrub controls feel continuous.
  7. Reduced motion. Bar enter animation, value-change transition, and overlay morph all collapse to instant under prefers-reduced-motion: reduce.

Props

PropTypeDefaultDescription
heightsnumber[]requiredBar heights, left to right. Clamped to [0, maxHeight] at render.
rectangle{ left, right, height }Currently-evaluated rectangle overlay. Omit for the bare histogram.
rectangleLabelstringOptional label rendered under the overlay (e.g. "area = 10").
maxHeightnumbermax(heights)Maximum bar value. Falls back to the tallest height.
tone"default" | "accent" | "success" | "warning" | "error""accent"Overlay palette.
barWidthnumber36Bar width in pixels.
barGapnumber6Gap between bars in pixels.
plotHeightnumber140Plot area height in pixels.
showValuesbooleantrueRender the integer height above each bar.
transitionTransitionSPRINGS.smoothOverride bar / overlay transitions. Reduced-motion users snap regardless.
classNamestringMerged onto the <svg> root via cn().

Accessibility

  • The outer <svg> is role="img" with a <title> summarising the bar heights and the current rectangle span / height. Screen readers hear both the chart and the candidate without parsing SVG geometry.
  • The root exposes data-has-rectangle (true / false) and data-tone so consumer apps can hook custom styles or assistive tooling.
  • Every bar <g> exposes data-index and data-state (in-rectangle / rest) so step controllers can target specific bars via the DOM.
  • Tone is never the only signal — the overlay carries a dashed stroke and an optional area label, so colourblind users see the rectangle even when the highlight colour is hard to discriminate.
  • Motion respects prefers-reduced-motion: reduce — the enter animation and the candidate-morph spring collapse to instant.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/observation/RectangleTrick.tsx). The library extract reframes the primitive around the classic largest-rectangle-in-histogram visual — a heights[] row with one candidate overlay — and lets the caller compose any reducer-driven scoring on top.