Bit Set Circuit

A self-contained <svg> that draws a combinational boolean circuit. Pass an array of inputs (each a 0 or 1) and a list of ops, where every op is one of AND, OR, XOR, NOT with operand indices into the merged [...inputs, ...opResults] stream. The component evaluates the circuit bottom-up, lays out one switch column on the left, one gate per op in the middle, and a single output port on the right.

Wires carrying a 1 light up in the track accent; wires carrying a 0 sit at the muted foreground tone. When onInputToggle is provided, each switch becomes a 44px hit target wired to Space / Enter; otherwise the picture renders read-only.

Boolean circuit with inputs x0=1, x1=0, x2=1; gates AND(0,1) → NOT 2 → XOR(3,4); output 0.1x00x11x2ANDNOTXOR0outBoolean circuit with inputs x0=1, x1=0, x2=1; gates AND(0,1) → NOT 2 → XOR(3,4); output 0.
Customize
Layout
44
88
Behaviour

Installation

npx shadcn@latest add https://craftbits.dev/r/bit-set-circuit.json

Usage

import { BitSetCircuit } from "@craft-bits/core";
 
<BitSetCircuit
  inputs={[1, 0, 1]}
  ops={[
    { kind: "AND", a: 0, b: 1 },
    { kind: "NOT", a: 2 },
    { kind: "XOR", a: 3, b: 4 },
  ]}
/>;

Interactive — parent owns the input bits, child fires onInputToggle on every flip:

const [inputs, setInputs] = useState([1, 0, 1]);
 
<BitSetCircuit
  inputs={inputs}
  ops={ops}
  onInputToggle={(i, next) => {
    const copy = inputs.slice();
    copy[i] = next;
    setInputs(copy);
  }}
/>;

Custom labels and a hex accent (enables alpha-suffix tinting on the gate fills):

<BitSetCircuit
  inputs={[1, 1, 0]}
  ops={[{ kind: "OR", a: 0, b: 1 }]}
  inputLabels={["lo", "mid", "hi"]}
  accentHex="#4A7BF7"
/>

Understanding the component

  1. Stream-indexed operands. Each op's a and b index into the concatenated [...inputs, ...opResultsSoFar]. So a = 0 references the first input; a = inputs.length references the first op's output. This makes gate cascades trivial — op i + 1 can read op i by index.
  2. NOT is unary. When kind is NOT, only a is read. b is ignored if supplied, so callers can keep a uniform op-builder.
  3. Hot wires light up. Every wire carrying a 1 is painted in the track accent and rendered slightly thicker; wires carrying a 0 sit at a muted foreground tone. The output port mirrors the colour of the final gate.
  4. Out-of-range indices are safe. If an op references an operand index beyond the current stream, the operand resolves to 0 and the gate still renders — useful while a circuit is being authored.
  5. Read-only vs. interactive. Omit onInputToggle to render a static picture; supply it to enable per-switch click-and-keyboard activation with a 44px hit target on each input.
  6. Reduced motion. usePrefersReducedMotion() collapses every wire-colour spring to instant. Values still update; only the motion drops.

Props

PropTypeDefaultDescription
inputsreadonly number[]requiredInput bits (0/1). Non-zero values are coerced to 1.
opsreadonly BitSetCircuitOp[]requiredOrdered gate list — { kind, a, b? } per gate.
inputLabelsreadonly string[]x0, x1, …Per-input display labels.
onInputToggle(index: number, next: number) => voidFires when a switch is clicked or activated. Omit for read-only.
accentHexstringvar(--cb-accent)Track accent. Use a #hex to enable alpha-tinted gate fills.
rowHeightnumber44Row spacing in px between switches.
columnWidthnumber88Column spacing in px between input column, gate columns, and the output port.
transitionTransitionSPRINGS.smoothOverride the wire / gate colour transition.
classNamestringMerged onto the outer <svg> via cn().

Accessibility

  • The outer <svg> is role="img" with a <title> summarising every input value, every gate, and the final output bit so screen readers can read the circuit state without parsing geometry.
  • Every interactive switch is role="switch" with aria-checked, an explicit aria-label, tabIndex={0}, and Space / Enter activation that mirrors the click handler.
  • Each switch's hit target is at least 44 by 44 px (WCAG 2.5.8 AAA), even though the visible disc is smaller.
  • Gates expose data-kind (AND / OR / XOR / NOT) and data-state (on / off) on their group element so consumer apps can hook custom styles or assistive tooling.
  • Motion respects prefers-reduced-motion: wire-colour, gate fill, and switch scale transitions all snap to instant.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/construction/BitSetCircuit.tsx). The original was a two-phase wire-then-operate lesson scoped to set-membership encoding — it took an elements: string[] payload, drove a workbench reducer + dissectable contract, played sound effects on every toggle, and gated phase one behind a drag-to-wire interaction. The library extract keeps only the visual primitive — input bits, gate list, hot-wire rendering — and lets the caller compose any reducer-driven scoring or wiring puzzle on top.