diff --git a/docs/ui/UI_AUDIT.md b/docs/ui/UI_AUDIT.md
index d1b407a..5c73f26 100644
--- a/docs/ui/UI_AUDIT.md
+++ b/docs/ui/UI_AUDIT.md
@@ -76,7 +76,7 @@ So a Button intended as a "card-style action" (e.g. a title above a prompt) will
grep -rEn ']*>.*\n[^<]*<' --include='*.tsx' web/src
```
-**Recommendation for design system:** add a documented `` primitive that drops `whitespace-nowrap` and removes fixed height, OR add an `as="card"` variant to ``. This is upstream work in `learning_ai_common_plat`.
+**Recommendation for design system:** ✅ shipped in `@bytelyst/ui@0.1.6` as ``. Drops `whitespace-nowrap`, removes fixed height, preserves the focus-visible ring. Use it whenever a `` would otherwise wrap a `` or stacked `
`s. The 5 Pattern A sites in this repo were converted in commit `c10de34`+ (StrategyWizard ×2, SimpleView ×2, MyStrategiesTab ×1).
### Pattern B — Inline styles instead of classes
diff --git a/web/src/components/StrategyWizard.tsx b/web/src/components/StrategyWizard.tsx
index c8e1e4b..8015b5a 100644
--- a/web/src/components/StrategyWizard.tsx
+++ b/web/src/components/StrategyWizard.tsx
@@ -24,6 +24,7 @@ import { createTradeProfile, updateTradeProfile } from '../lib/profileApi';
import { Button } from './ui/button';
import { Card } from './ui/card';
import { Input } from './ui/input';
+import { CardButton } from '@bytelyst/ui';
interface WizardState {
step: number;
@@ -185,11 +186,10 @@ export const StrategyWizard: React.FC<{
const isLocked = !isFeatureAllowed(tier, 'risk_style', style.id);
return (
-
setState({ ...state, riskStyle: style })}
- className={`card-button ${optionBaseClass} ${isLocked ? 'cursor-not-allowed opacity-40 grayscale' : 'hover:scale-[1.01] active:scale-[0.99]'
+ className={`${optionBaseClass} ${isLocked ? 'cursor-not-allowed opacity-40 grayscale' : 'hover:scale-[1.01] active:scale-[0.99]'
} ${state.riskStyle?.id === style.id
? optionSelectedClass
: optionIdleClass
@@ -212,7 +212,7 @@ export const StrategyWizard: React.FC<{
{style.description}
-
+
{isLocked && (
Pro/Elite Only
@@ -338,11 +338,10 @@ export const StrategyWizard: React.FC<{
{(['24/7', 'London + New York', 'Asia only'] as const).map(option => (
- setState({ ...state, hours: option })}
- className={`card-button rounded-2xl border-2 p-6 text-left transition-all ${state.hours === option
+ className={`rounded-2xl border-2 p-6 text-left transition-all ${state.hours === option
? optionSelectedClass
: optionIdleClass
}`}
@@ -360,7 +359,7 @@ export const StrategyWizard: React.FC<{
-
+
))}
diff --git a/web/src/layout-fixes.css b/web/src/layout-fixes.css
index 1ba4360..afe83b6 100644
--- a/web/src/layout-fixes.css
+++ b/web/src/layout-fixes.css
@@ -494,35 +494,3 @@
overflow-wrap: anywhere;
}
-/* ---------------------------------------------------------------------------
- Section 25 — card-button utility (UI audit Pattern A)
- Use on a native when you need a button-shaped CARD with stacked
- block content. The @bytelyst/ui Button primitive has whitespace-nowrap +
- fixed h-{size} that collapses multi-line content. This class restores the
- focus ring + reset behavior without those constraints.
---------------------------------------------------------------------------- */
-.card-button {
- appearance: none;
- -webkit-appearance: none;
- background: transparent;
- border: 0;
- font: inherit;
- color: inherit;
- cursor: pointer;
- display: block;
- width: 100%;
- text-align: left;
- white-space: normal;
- height: auto;
- transition: background-color 150ms ease, border-color 150ms ease, box-shadow 150ms ease;
-}
-.card-button:disabled {
- cursor: not-allowed;
- opacity: 0.4;
-}
-.card-button:focus-visible {
- outline: none;
- box-shadow: 0 0 0 2px var(--bl-bg-canvas, #0b0f17),
- 0 0 0 4px var(--bl-focus-ring, var(--bl-accent, #5A8CFF));
- border-radius: inherit;
-}
diff --git a/web/src/tabs/MyStrategiesTab.tsx b/web/src/tabs/MyStrategiesTab.tsx
index 5b97450..f604126 100644
--- a/web/src/tabs/MyStrategiesTab.tsx
+++ b/web/src/tabs/MyStrategiesTab.tsx
@@ -27,6 +27,7 @@ import { BacktestRunnerPanel } from '../backtest/components/BacktestRunnerPanel'
import { useBacktestFeatureGate } from '../backtest/useBacktestFeatureGate';
import { deleteTradeProfile, fetchTradeProfiles, setTradeProfileActive } from '../lib/profileApi';
import { Button, IconButton } from '../components/ui/Primitives';
+import { CardButton } from '@bytelyst/ui';
function getStrategyKindLabel(config: any) {
if (config?.type === 'visual') return 'Visual Builder';
@@ -150,10 +151,8 @@ const ActiveStrategyCard: React.FC<{
{/* 5. Health Diagnostic (Education Layer) */}
-
onToggleExpand(profile.id)}
- className="card-button"
style={{
width: '100%',
padding: '16px',
@@ -190,7 +189,7 @@ const ActiveStrategyCard: React.FC<{
{explanation.recommendation}
)}
-
+
{/* 6. Action */}
diff --git a/web/src/views/SimpleView.tsx b/web/src/views/SimpleView.tsx
index 5525f42..831dba4 100644
--- a/web/src/views/SimpleView.tsx
+++ b/web/src/views/SimpleView.tsx
@@ -24,6 +24,7 @@ import {
type TriggerMode,
} from './tradePlansState';
import { useTradePlansNavigationState } from './useTradePlansNavigationState';
+import { CardButton } from '@bytelyst/ui';
type SimpleHolding = {
symbol: string;
@@ -1006,14 +1007,13 @@ export function SimpleView() {
-
{
dispatch({ type: 'clear-feedback' });
dispatch({ type: 'set-selected-holding-trade-id', value: null });
updateDraft('side', 'buy');
}}
- className={`card-button h-auto justify-start rounded-[1.25rem] border px-5 py-5 text-left transition ${
+ className={`h-auto justify-start rounded-[1.25rem] border px-5 py-5 text-left transition ${
draft.side === 'buy'
? 'border-[var(--primary)] bg-[var(--accent-soft)]'
: 'border-[var(--border)] bg-[var(--card-elevated)]'
@@ -1023,9 +1023,8 @@ export function SimpleView() {
New short-term buy plan
Arm a dip-buy trigger and let the app manage the profit exit after fill.
-
-
+ {
dispatch({ type: 'clear-feedback' });
if (availableSellHoldings.length > 0) {
@@ -1034,7 +1033,7 @@ export function SimpleView() {
updateDraft('side', 'sell');
}
}}
- className={`card-button h-auto justify-start rounded-[1.25rem] border px-5 py-5 text-left transition ${
+ className={`h-auto justify-start rounded-[1.25rem] border px-5 py-5 text-left transition ${
draft.side === 'sell'
? 'border-[var(--primary)] bg-[var(--accent-soft)]'
: 'border-[var(--border)] bg-[var(--card-elevated)]'
@@ -1044,7 +1043,7 @@ export function SimpleView() {
Manage an existing holding
Choose a filled holding and place it back under managed profit-taking.
-
+