This component no longer exists. It was in ns-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-07 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Checkbox Tally Notch
ns-ui/checkbox-tally-notchRemovedComponent
Checkbox group rendered as a carpenter's tally: each check carves a notch stroke into its row and adds a stroke to an aggregate tally cluster in the header, where every fifth stroke slashes diagonally across its group of four. Strokes draw on with a dashoffset sweep and retract on uncheck.
Live preview
live · rendered by the registry
Rendered by ns-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://design.helpmarq.com/r/checkbox-tally-notch.jsonSource
1"use client";23import { useMemo, useRef, useState } from "react";45// Checkbox group rendered as a carpenter's tally: each check carves a notch6// stroke into the row and adds a stroke to an aggregate tally cluster, where7// every fifth stroke slashes diagonally across its group of four. Strokes8// draw on with a dashoffset sweep and retract on uncheck; per-stroke jitter9// is seeded by index so the carving looks hand-cut but renders identically10// every mount. Pure DOM+SVG, tokens only, reduced-motion snaps instantly.1112export interface TallyNotchItem {13 id: string;14 label: string;15 hint?: string;16}1718export interface TallyNotchProps {19 items: TallyNotchItem[];20 defaultChecked?: string[];21 onChange?: (checkedIds: string[]) => void;22 /** heading shown above the group; also the group's accessible name */23 label?: string;24 className?: string;25}2627// deterministic per-index jitter — mulberry32, so screenshots are stable28function jitter(i: number) {29 let t = (i + 1) * 0x6d2b79f5;30 t = Math.imul(t ^ (t >>> 15), t | 1);31 t ^= t + Math.imul(t ^ (t >>> 7), t | 61);32 const r = ((t ^ (t >>> 14)) >>> 0) / 4294967296;33 return r * 2 - 1; // -1..134}3536// delay folded into the shorthand itself, not a separate transitionDelay37// longhand — mixing transition (shorthand) and transitionDelay (longhand)38// for the same property gives React two owners for one CSS value and it39// warns (and can genuinely race) on rerender.40const STROKE_STYLE = (drawn: boolean, reduced: boolean, delayMs = 0) =>41 ({42 strokeDasharray: 1,43 strokeDashoffset: drawn ? 0 : 1,44 transition: reduced45 ? "none"46 : `stroke-dashoffset 260ms ${drawn ? "cubic-bezier(0.22, 1, 0.36, 1)" : "cubic-bezier(0.55, 0, 0.55, 0.2)"} ${delayMs}ms`,47 }) as const;4849function TallyCluster({50 count,51 max,52 reduced,53}: {54 count: number;55 max: number;56 reduced: boolean;57}) {58 const groups = Math.ceil(max / 5);59 const GW = 30; // group width60 const H = 26;61 const paths = useMemo(() => {62 const out: { d: string; idx: number }[] = [];63 for (let i = 0; i < max; i++) {64 const g = Math.floor(i / 5);65 const k = i % 5;66 const gx = g * (GW + 10);67 if (k < 4) {68 // vertical stroke with seeded lean and length variance69 const x = gx + 4 + k * 6 + jitter(i) * 1.2;70 const lean = jitter(i * 7 + 3) * 1.8;71 const top = 3 + jitter(i * 13 + 5) * 1.4;72 out.push({ d: `M ${x + lean} ${top} L ${x - lean} ${H - 3}`, idx: i });73 } else {74// … truncated
