Shape Explorer
A calculator-style primitive for tensor shapes. Each dimension is an editable number input; the header reads back the shape tuple, the total element count, and the memory size at the chosen precision. The user types or arrows up/down to change a dim and the readouts update on every keystroke.
Shape (32, 128, 768). 3,145,728 elements. 6.00 MiB at 2 bytes per element.
Shape · (32, 128, 768)rank 3
- elements
- 3,145,728
- memory (2 B/elem)
- 6.00 MiB
Customize
Shape
[32, 128, 768]
Precision
2 (fp16)
Limits
6
Installation
npx shadcn@latest add https://craftbits.dev/r/shape-explorer.jsonUsage
import { ShapeExplorer } from "@craft-bits/core";
<ShapeExplorer defaultShape={[32, 128, 768]} />;Wire to outside state — useful when a parent lesson needs to react to the shape:
const [shape, setShape] = useState<readonly number[]>([2, 3, 4]);
<ShapeExplorer shape={shape} onShapeChange={setShape} />;Switch precision to fp32 / int8:
// fp32: 4 bytes per element
<ShapeExplorer defaultShape={[32, 128, 768]} bytesPerElement={4} />
// int8: 1 byte per element
<ShapeExplorer defaultShape={[32, 128, 768]} bytesPerElement={1} />Cap the rank to a smaller value:
<ShapeExplorer defaultShape={[8, 16]} maxDims={3} />Understanding the component
- One editable input per dimension.
shapeis a flatnumber[]; the component renders an input box per entry, labelleddim 0,dim 1, …. Each edit is parsed, clamped to[minDim, maxDim], and committed viaonShapeChange(or internal state in uncontrolled mode). - The readouts are derived.
elements = product(shape);memory = elements * bytesPerElement. Both update on every keystroke; the memory readout uses binary prefixes (KiB,MiB,GiB) because tensor allocations align to pages and most ML docs quote binary. - Rank is editable too. The
+/−buttons append / pop a trailing dimension. New dims seed to1so the product (and therefore the memory) doesn't jump when you add a slot. The+button disables atmaxDims, the−button disables at rank 1. - Controlled or uncontrolled. Pass
shape+onShapeChangeto lift state, or leave them undefined and the component manages its own value (seeded fromdefaultShape). - Clamping is invisible. Every dim is forced into
[minDim, maxDim]on commit. Type0,-3, or99999and the input snaps to the bound — element-count / memory readouts never display nonsense. - Reduced-motion fallback. With
prefers-reduced-motion: reduce, the readout fade transitions collapse to instant.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
shape | readonly number[] | — | Controlled shape. Pair with onShapeChange. |
defaultShape | readonly number[] | [2, 3, 4] | Uncontrolled initial shape. |
onShapeChange | (shape: readonly number[]) => void | — | Fires on every dim edit or add / remove. |
bytesPerElement | number | 2 | Bytes per tensor element. 2 ≈ fp16/bf16; 4 ≈ fp32; 1 ≈ int8. |
maxDims | number | 6 | Hard cap on rank. The + button disables when reached. |
minDim | number | 1 | Lower clamp applied to each dim on edit. |
maxDim | number | 8192 | Upper clamp applied to each dim on edit. |
transition | Transition | SPRINGS.smooth | Spring used for the readout fade. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The root is
role="figure"witharia-labelledbypointing at the shape heading (Shape · (32, 128, 768)) andaria-describedbyat a visually-hiddenaria-live="polite"summary. - The summary announces the shape, element count, memory size, and bytes-per-element whenever a dim is edited — screen-reader users hear the recalculated total without watching the readout.
- Every dimension input has an
aria-label(Dimension 0 of 3) and a visible monospace label.inputMode="numeric"surfaces the digit keypad on touch devices. - Native
min/max/stepattributes mean arrow keys step by 1 and out-of-range values snap to the bounds on commit. +/−buttons carryaria-labels and visibly disable when the rank cap is reached; the disabled state is also conveyed by reduced opacity and acursor-not-allowedcursor — colour is never the only signal.- All focusable controls render a
focus-visible:ring-2 ring-cb-accentoutline against the--cb-bgpage background. prefers-reduced-motion: reducecollapses the readout fade to instant.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/ShapeExplorer.tsx). The source was a bundled lesson widget — a four-shape preset selector, a four-op selector (+,@,mean(0),mean(1)), broadcast / matmul / reduce shape-arithmetic compute, animated cell grids for tensors A / B / Result, a "Shape mismatch" inline banner, and bespoke colour vars (--color-accent-500,--color-warn-400,--color-success-500). The library extract is the form half of that widget — the editable dimension row plus element-count + memory readout — generalised from the source's fixed-rank-2/3 presets to an arbitrarynumber[]shape withadd/removecontrols bounded bymaxDims. Op selection, paired-tensor broadcast / matmul rendering, and the result grid stay with the source lesson (see alsoShapeTracerVizfor the multi-step op pipeline andReshapeExplorerfor the cell-morph animation).