No.206 · Decisions· New
Magnet Board
Explore things that differ in several ways at once. Each enamel magnet stands for one quality; put it on the board and every item drifts toward it in proportion to how much of that quality it has, so clusters show the trade-offs. Flip a magnet to pull the lowest values instead, or switch to the list for the same model as a table.
The prompt
Written for the variant you picked above: Light
Build an exploration board for comparing items on several qualities at once (24 laptops: price, battery, weight, rating), based on the dust-and-magnet technique: qualities are magnets, items are dust. Panel: white with a 14px radius and a hairline border. A header bar holds '24 laptops', a row of pill chips (one per quality: an active chip has a coloured dot, the name, a 'High'/'Low' toggle and a ×; an inactive one is a dashed '+ Price'), and a Board/List segmented switch on the right. Board: 340–500px tall, drawn as graph paper (a faint blue 24px grid with a stronger 120px grid on warm off-white). Items are 14px ink dots with a 2px paper-coloured ring; each magnet's strongest item keeps a small label beside their dot, and hovering any dot enlarges it, shows its label and opens a white card with every value. Magnets are 56px enamel pucks in red, blue, ochre and green with a darker 4px rim, a short code in white heavy type ('Bt', 'Kg', '$', '★'), a drop shadow and an uppercase name tag under them. A flipped magnet turns white with coloured text and its tag reads 'Weight · low'. Layout rule (deterministic, no physics loop): each item's resting point is the weighted mean of the magnet positions, each weighted by the square of the item's normalised value for that quality (or 1 minus it when flipped), plus a weak pull to the centre; 40 relaxation passes then push overlapping dots apart and keep them inside the board. Moving, adding, flipping or removing a magnet recomputes it, and the dots drift to their new places. List view: a table with one column per magnet (a small pull bar beside each value) and the remaining qualities muted, rows sorted by total pull, header sticky. Selected variant — Light (light): Palette for this theme (Light): --mgbd-bg #f6f3ea; --mgbd-dot #1d2633; --mgbd-focus #2b5fa8; --mgbd-grid rgb(52 84 130 / 0.1); --mgbd-grid-major rgb(52 84 130 / 0.18); --mgbd-ink #1d2633; --mgbd-line rgb(29 38 51 / 0.14); --mgbd-m1 #c2412d; --mgbd-m2 #2b5fa8; --mgbd-m3 #b8860b; --mgbd-m4 #3f7d4e; --mgbd-muted #5d6573; --mgbd-panel #fffdf8. Treat these as the root colour tokens, one value per role, and name them to suit your code. Select it with the theme option set to "light". Keep every dimension, spacing value, state and motion from the brief unchanged, and keep text at 4.5:1 contrast or better on these surfaces. This selection takes precedence over references to other themes or options in the general brief. Implement this selected appearance; do not add a variant picker or alternative appearances unless requested. Implementation requirements: Use React and TypeScript. Keep the component self-contained, with uniquely scoped CSS and no animation libraries. Include required imports, styles and font setup in the result. Interaction: Drag pucks with the pointer; dots follow as you drag. Each puck is a button: arrow keys move it 16px (Shift 64px), I flips it, Delete removes it. Chips add a magnet at the next free spot, flip it or remove it. Hover or tap a dot for its values; onSelect(item) fires on click. Motion: Dots drift with transform over 640ms cubic-bezier(.2,.8,.2,1), shortened to 320ms while a magnet is being dragged so they keep up. Pucks lift slightly while held. Reduced motion or motion={false} moves dots straight to their places. Accessibility: The plotted dots are decorative; the List view is the accessible twin of the board. Pucks are labelled buttons that describe their quality, direction and keys. After each move or flip a polite live region names the three items that magnet now pulls hardest. Chips and view buttons expose aria-pressed. Responsive behavior: The board's height scales with the viewport between 340px and 500px and its layout follows its measured size; the header wraps chips onto a second line on narrow screens. Touch: Drag pucks with a finger (the board disables scrolling under it); tap a dot to see its card; the List view works without dragging at all. Preserve keyboard access, visible focus and reduced-motion behavior. Keep fictional demo data and simulated service calls in a separate usage example; expose callbacks for real actions and recover from rejected requests. Keep usage-example links inside the demo: use local buttons or prevent navigation. Demonstration links must not navigate to Vitrine routes or open other pages; preserve real URL support in the reusable component itself. Verify the result at narrow and wide viewport sizes, with keyboard input and reduced motion enabled.
Notes
- Interaction
- Drag pucks with the pointer; dots follow as you drag. Each puck is a button: arrow keys move it 16px (Shift 64px), I flips it, Delete removes it. Chips add a magnet at the next free spot, flip it or remove it. Hover or tap a dot for its values; onSelect(item) fires on click.
- Motion
- Dots drift with transform over 640ms cubic-bezier(.2,.8,.2,1), shortened to 320ms while a magnet is being dragged so they keep up. Pucks lift slightly while held. Reduced motion or motion={false} moves dots straight to their places.
- Accessibility
- The plotted dots are decorative; the List view is the accessible twin of the board. Pucks are labelled buttons that describe their quality, direction and keys. After each move or flip a polite live region names the three items that magnet now pulls hardest. Chips and view buttons expose aria-pressed.
- Responsive
- The board's height scales with the viewport between 340px and 500px and its layout follows its measured size; the header wraps chips onto a second line on narrow screens.
- On touch
- Drag pucks with a finger (the board disables scrolling under it); tap a dot to see its card; the List view works without dragging at all.