Files
bedolaga-cabinet/src/components/primitives/Button/Button.variants.ts
c0mrade 7f68e2cd5e fix(theme): readable text on any operator palette + admin consistency pass
Контраст-аудит (WCAG-обход всех текстовых узлов, 4 палитры × юзер+админ
страницы) показал системные провалы читаемости: вторичный текст 1.5-2.5
на светлой/кастомных темах, статусные цвета 1.3-1.6, белый текст на
светлых акцентах 1.9.

Корневые фиксы:
- useThemeColors: контраст-кламп производных серых токенов — dark-400/
  champagne-600 (вторичный текст) держат >=5.0 к поверхности, dark-500/
  champagne-500 (подсказки) >=3.8; палитры с достаточным контрастом
  рендерятся байт-в-байт как раньше
- --color-on-accent/-success/-warning/-error: чёрный или белый текст
  по фактической яркости цвета оператора; text-white на accent-заливках
  заменён на text-on-accent (135 мест + variant примитива Button +
  .btn-primary)
- .light: статусные текстовые оттенки *-300/400 ремапятся на тёмные
  *-700 (жёлтая «Админка» 1.26 -> 3.8+, зелёные суммы 1.4 -> 3.4+,
  бейджи состояний)

Замер после: операторская тёмная палитра — 0 нарушений (всё >=4.5),
худшие точки остальных подняты с 1.26-1.6 до 3.2+.

Консистентность:
- заголовки админ-страниц приведены к text-xl font-bold text-dark-100
  (были 18/20/24/30px и 600/700 вперемешку)
- «Создать FAQ»: warning-500/80+белый -> warning-500+text-on-warning
- formatTraffic: единицы из i18n (убран микс «GB»/«ГБ» на одном экране)
- categoryCanvas -> categoryCANVAS в локалях (битый ключ на настройках)
- «Управление сквадами» -> «Управление локациями» (юзерский экран)
- мин-макс сумм на выборе метода: dark-600 -> dark-400
- неактивные пункты нижнего нава: dark-500 -> dark-400
2026-06-11 15:49:17 +03:00

65 lines
2.0 KiB
TypeScript

import { cva, type VariantProps } from 'class-variance-authority';
export const buttonVariants = cva(
// Base styles
[
'inline-flex items-center justify-center gap-2',
'font-medium transition-all duration-200',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-dark-950',
'disabled:pointer-events-none disabled:opacity-50',
'select-none',
],
{
variants: {
variant: {
primary: [
'bg-accent-500 text-on-accent',
'hover:bg-accent-600',
'active:bg-accent-700',
'shadow-linear-sm hover:shadow-linear',
],
secondary: [
'bg-dark-800/80 text-dark-100',
'border border-dark-700/50',
'hover:bg-dark-700/80 hover:border-dark-600/50',
'active:bg-dark-800',
],
ghost: ['text-dark-300', 'hover:text-dark-100 hover:bg-dark-800/50', 'active:bg-dark-800'],
destructive: [
'bg-error-500/10 text-error-400',
'border border-error-500/20',
'hover:bg-error-500/20 hover:border-error-500/30',
'active:bg-error-500/30',
],
outline: [
'border border-dark-700/50 text-dark-200',
'hover:bg-dark-800/50 hover:border-dark-600/50 hover:text-dark-100',
'active:bg-dark-800',
],
link: [
'text-accent-400',
'hover:text-accent-300 hover:underline',
'active:text-accent-500',
],
},
size: {
sm: 'h-8 px-3 text-sm rounded-linear',
md: 'h-10 px-4 text-sm rounded-linear',
lg: 'h-12 px-6 text-base rounded-linear-lg',
icon: 'h-10 w-10 rounded-linear',
'icon-sm': 'h-8 w-8 rounded-linear',
'icon-lg': 'h-12 w-12 rounded-linear-lg',
},
fullWidth: {
true: 'w-full',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
},
);
export type ButtonVariants = VariantProps<typeof buttonVariants>;