From 953730ff511b5930fbf4b2a1779637adb9ab47a4 Mon Sep 17 00:00:00 2001 From: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 09:31:29 +0000 Subject: [PATCH] feat(ui): add primitive (UI audit Pattern A fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Button primitive applies `whitespace-nowrap` + a fixed `h-{size}` because it's tuned for single-line CTAs. Consumers using Button as a card-shaped picker — title above description, icon next to multi-line label — hit collapsed/clipped content because of those constraints. CardButton is the right primitive for that use case: - block layout, full-width, left-aligned by default - whitespace: normal so multi-line content wraps - height: auto so any number of stacked rows works - focus-visible ring tied to --bl-focus-ring/--bl-accent (matches Button) - disabled opacity + pointer-events - selected? prop with subtle inset accent ring (override-able) - asChild support via Radix Slot for / handoff Bumps @bytelyst/ui to 0.1.6 and re-exports CardButton + CardButtonProps from the package entry point. Initial consumers: 5 sites in learning_ai_invt_trdg (StrategyWizard risk + hours pickers, SimpleView buy + sell plan cards, MyStrategiesTab diagnostic toggle). See docs/ui/UI_AUDIT.md Pattern A in that repo. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- packages/ui/package.json | 2 +- packages/ui/src/components/CardButton.tsx | 71 +++++++++++++++++++++++ packages/ui/src/index.ts | 1 + 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/components/CardButton.tsx diff --git a/packages/ui/package.json b/packages/ui/package.json index 3013b93c..c95dac7f 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@bytelyst/ui", - "version": "0.1.5", + "version": "0.1.6", "type": "module", "scripts": { "storybook": "storybook dev -p 6006", diff --git a/packages/ui/src/components/CardButton.tsx b/packages/ui/src/components/CardButton.tsx new file mode 100644 index 00000000..6d462a4d --- /dev/null +++ b/packages/ui/src/components/CardButton.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { clsx } from 'clsx'; + +/** + * CardButton — a button-shaped CARD that allows stacked, multi-line content. + * + * The {@link Button} primitive applies `whitespace-nowrap` and a fixed + * `h-{size}` because it's tuned for single-line CTAs (Save, Cancel, Submit). + * When you put two stacked spans inside it (e.g. a title + a description), + * the second collapses or overflows the fixed height. This is by design — + * Button is the wrong primitive for picker/option/action *cards*. + * + * CardButton drops those constraints and gives you: + * - `display: block` (or flex via `as-child`) + * - `white-space: normal` so text wraps + * - `height: auto` so any number of stacked rows work + * - the same focus-visible ring as Button (keyboard a11y preserved) + * - `disabled` opacity behavior + * + * Use it whenever a `