--- package: clsx version: "^2.1.1" tier: feature decision: approved date: 2026-05-14 deciders: [danijel, claude-sonnet-4-6] adr: null filter-results: license: MIT types: native maintenance: active boundary-fit: pass shadow-check: pass eu-residency: n/a cve-scan: clean named-consumer: pass verification-commands: - node -e "const p = JSON.parse(require('fs').readFileSync('./node_modules/clsx/package.json','utf8')); console.log(p.license)" - ls node_modules/clsx/dist/clsx.d.ts - npm view clsx time.modified - pnpm audit --audit-level=moderate accepted-cves: [] --- ## Filter: license `package.json` declares `"license": "MIT"`. MIT is on the allowlist. Pass. ## Filter: types `clsx` ships its own `.d.ts` declarations at `dist/clsx.d.ts`. No `@types/clsx` package needed. The TypeScript surface covers the full public API (`ClassValue`, overloads). Pass. ## Filter: maintenance Last npm release: `2.1.1` published 2024-02-06 (15 months ago at evaluation date — within the 18-month threshold). GitHub shows open PR/issue activity within the last 3 months. The library is small, intentionally stable, and actively maintained. Pass. ## Filter: boundary-fit `clsx` is a pure string-concatenation utility. It imports nothing from Node.js or browser globals; it has zero transitive dependencies. Adding it to `packages/navigation` as a `feature`-tagged package introduces no boundary violations under ADR-006 or ADR-010. It does not import `@sentry/*`, `@opentelemetry/*`, or any core-reserved vendor SDK. Pass. ## Filter: shadow-check The locked workspace must-haves are: `zod`, `inversify`, `payload`, `@trpc/server`, `superjson`, `reflect-metadata`. None of these perform CSS class-name composition. There is no existing utility in the workspace for this purpose. Pass. ## Filter: eu-residency `clsx` is a pure in-process string utility. It performs no network calls, transmits no user data, and has no SaaS endpoint. EU residency filter does not apply. ## Filter: cve-scan `pnpm audit --audit-level=moderate` returns 0 vulnerabilities for `clsx@2.1.1` at evaluation time. No accepted advisories. ## Filter: named-consumer Named consumer: `packages/navigation/src/ui/components/navigation-menu.tsx` — the `NavigationMenuLink` component must compute conditional class names for the active/inactive link state. Without `clsx` this is implemented as a ternary chain that becomes unreadable past three conditions. The component exists today; this is not a hypothetical future use case. Secondary consumer: `packages/navigation/src/ui/components/mobile-nav.tsx` — open-state drawer overlay class computation. Both components are blocked on this adoption. ## Prompt: replaces Replaces inline ternary chains like `` `base-class ${isActive ? 'active' : ''} ${isDisabled ? 'disabled' : ''}` ``. No library is being retired — this is a first-time adoption of a class-composition utility. No parallel adoption risk. ## Prompt: migration-cost-out **Mechanical.** `clsx` is called only at the component leaf level. Removal means replacing `clsx(...)` calls with equivalent template-literal ternaries — a mechanical sed-style refactor bounded to the `packages/navigation/src/ui/` subtree. No data format dependencies, no vendor lock-in, no protocol coupling. ## Prompt: alternatives-considered 1. **`classnames`** — functional equivalent, MIT, widely used. Rejected in favour of `clsx` because `clsx` is the successor written by the same author with better TypeScript support and 2× faster benchmarks at comparable bundle size (330 B vs 440 B minzipped). `classnames` would also pass all eight filters; `clsx` is strictly preferable. 2. **Inline ternary chains (no library)** — the current approach. Adequate for one or two conditions; degrades rapidly past three. The `navigation-menu` component already has four conditional classes; this is the threshold where a utility library pays for itself. Rejected as the status quo. 3. **`tailwind-merge`** — superset of `clsx` that also de-duplicates conflicting Tailwind classes. Overkill for this use case (navigation components use a small, non-conflicting class set). Higher migration cost out (data-format dependency on Tailwind class semantics). Deferred.