mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
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
This commit is contained in:
@@ -114,7 +114,7 @@ export function AnalyticsTab() {
|
||||
<button
|
||||
onClick={handleSaveYandex}
|
||||
disabled={updateMutation.isPending}
|
||||
className="rounded-xl bg-accent-500 px-4 py-2.5 text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="rounded-xl bg-accent-500 px-4 py-2.5 text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
<CheckIcon />
|
||||
</button>
|
||||
@@ -253,7 +253,7 @@ export function AnalyticsTab() {
|
||||
<button
|
||||
onClick={handleSaveGoogleId}
|
||||
disabled={updateMutation.isPending}
|
||||
className="rounded-xl bg-accent-500 px-4 py-2.5 text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="rounded-xl bg-accent-500 px-4 py-2.5 text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
<CheckIcon />
|
||||
</button>
|
||||
@@ -307,7 +307,7 @@ export function AnalyticsTab() {
|
||||
<button
|
||||
onClick={handleSaveGoogleLabel}
|
||||
disabled={updateMutation.isPending}
|
||||
className="rounded-xl bg-accent-500 px-4 py-2.5 text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="rounded-xl bg-accent-500 px-4 py-2.5 text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
<CheckIcon />
|
||||
</button>
|
||||
|
||||
@@ -68,7 +68,7 @@ export function BackgroundEditor() {
|
||||
'w-full rounded-xl py-3 text-sm font-medium transition-colors',
|
||||
saveStatus === 'saved'
|
||||
? 'bg-success-500/20 text-success-400'
|
||||
: 'bg-accent-500 text-white hover:bg-accent-600 disabled:opacity-50',
|
||||
: 'bg-accent-500 text-on-accent hover:bg-accent-600 disabled:opacity-50',
|
||||
)}
|
||||
>
|
||||
{saveStatus === 'saving'
|
||||
|
||||
@@ -168,7 +168,7 @@ export function BrandingTab({ accentColor = '#3b82f6' }: BrandingTabProps) {
|
||||
<button
|
||||
onClick={() => updateBrandingMutation.mutate(newName)}
|
||||
disabled={updateBrandingMutation.isPending}
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
<CheckIcon />
|
||||
</button>
|
||||
|
||||
@@ -228,7 +228,7 @@ export function ButtonsTab() {
|
||||
? 'bg-success-500 text-white'
|
||||
: cfg.style === 'danger'
|
||||
? 'bg-error-500 text-white'
|
||||
: 'bg-accent-500 text-white'
|
||||
: 'bg-accent-500 text-on-accent'
|
||||
}`}
|
||||
>
|
||||
{t(`admin.buttons.styles.${cfg.style}`)}
|
||||
@@ -321,7 +321,7 @@ export function ButtonsTab() {
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={updateMutation.isPending}
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-sm font-medium text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
{updateMutation.isPending ? t('common.saving') : t('common.save')}
|
||||
</button>
|
||||
|
||||
@@ -346,7 +346,7 @@ export function ColoredItemCombobox({
|
||||
type="button"
|
||||
onClick={handleCreate}
|
||||
disabled={isCreating}
|
||||
className="flex min-h-[44px] w-full items-center justify-center gap-2 rounded-lg bg-accent-500 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-accent-600 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="flex min-h-[44px] w-full items-center justify-center gap-2 rounded-lg bg-accent-500 px-4 py-2.5 text-sm font-medium text-on-accent transition-colors hover:bg-accent-600 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{isCreating ? (
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
|
||||
|
||||
@@ -72,7 +72,7 @@ function MaxPerRowSelector({ value, onChange }: MaxPerRowSelectorProps) {
|
||||
onClick={() => onChange(n)}
|
||||
className={`flex h-7 w-7 items-center justify-center rounded-lg text-xs font-semibold transition-all ${
|
||||
value === n
|
||||
? 'bg-accent-500 text-white'
|
||||
? 'bg-accent-500 text-on-accent'
|
||||
: 'bg-dark-700/50 text-dark-400 hover:bg-dark-600 hover:text-dark-300'
|
||||
}`}
|
||||
>
|
||||
@@ -788,7 +788,7 @@ export function MenuEditorTab() {
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={updateMutation.isPending}
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-sm font-medium text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
{updateMutation.isPending ? t('common.saving') : t('common.save')}
|
||||
</button>
|
||||
|
||||
@@ -122,7 +122,7 @@ export function SettingInput({ setting, onUpdate, disabled }: SettingInputProps)
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="flex items-center gap-1.5 rounded-lg bg-accent-500 px-3 py-1.5 text-sm text-white transition-colors hover:bg-accent-600"
|
||||
className="flex items-center gap-1.5 rounded-lg bg-accent-500 px-3 py-1.5 text-sm text-on-accent transition-colors hover:bg-accent-600"
|
||||
>
|
||||
<CheckIcon />
|
||||
{t('admin.settings.saveButton')}
|
||||
@@ -159,7 +159,7 @@ export function SettingInput({ setting, onUpdate, disabled }: SettingInputProps)
|
||||
/>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="rounded-lg bg-accent-500 p-2 text-white transition-colors hover:bg-accent-600"
|
||||
className="rounded-lg bg-accent-500 p-2 text-on-accent transition-colors hover:bg-accent-600"
|
||||
title={t('admin.settings.saveHint')}
|
||||
>
|
||||
<CheckIcon />
|
||||
|
||||
@@ -215,7 +215,7 @@ export function SortableSelectedMethodCard({
|
||||
className={cn(
|
||||
'flex h-3.5 w-3.5 items-center justify-center rounded',
|
||||
enabled
|
||||
? 'bg-accent-500 text-white'
|
||||
? 'bg-accent-500 text-on-accent'
|
||||
: 'border border-dark-600 bg-dark-700',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -428,7 +428,7 @@ export function ThemeTab() {
|
||||
<button
|
||||
onClick={() => updateColorsMutation.mutate(draftColors)}
|
||||
disabled={updateColorsMutation.isPending}
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="rounded-xl bg-accent-500 px-4 py-2 text-sm font-medium text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
{updateColorsMutation.isPending
|
||||
? t('common.saving', t('common.save'))
|
||||
|
||||
@@ -621,7 +621,7 @@ export function ActionModal({
|
||||
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-full rounded-xl bg-accent-500 px-4 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-accent-600"
|
||||
className="w-full rounded-xl bg-accent-500 px-4 py-2.5 text-sm font-semibold text-on-accent transition-colors hover:bg-accent-600"
|
||||
>
|
||||
{t('common.close')}
|
||||
</button>
|
||||
@@ -647,7 +647,7 @@ export function ActionModal({
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={isConfirmDisabled}
|
||||
className="flex min-h-[44px] flex-1 items-center justify-center gap-2 rounded-xl bg-accent-500 px-4 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="flex min-h-[44px] flex-1 items-center justify-center gap-2 rounded-xl bg-accent-500 px-4 py-2.5 text-sm font-semibold text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
{t('admin.bulkActions.confirm')}
|
||||
</button>
|
||||
|
||||
@@ -208,7 +208,7 @@ export function FloatingActionBar({
|
||||
<div className="relative ml-auto">
|
||||
<button
|
||||
onClick={() => setOpen(!open)}
|
||||
className="flex items-center gap-2 rounded-xl bg-accent-500 px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-accent-600"
|
||||
className="flex items-center gap-2 rounded-xl bg-accent-500 px-4 py-2 text-sm font-semibold text-on-accent transition-colors hover:bg-accent-600"
|
||||
>
|
||||
{t('common.actions')}
|
||||
<ChevronDownIcon />
|
||||
|
||||
@@ -52,7 +52,7 @@ export function CountryFilter({
|
||||
>
|
||||
<GlobeIcon className="h-4 w-4" />
|
||||
{activeCount > 0 && (
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-white">
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-on-accent">
|
||||
{activeCount}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -56,7 +56,7 @@ export function NodeFilter({
|
||||
<ServerIcon className="h-4 w-4" />
|
||||
{t('admin.trafficUsage.nodes')}
|
||||
{activeCount > 0 && (
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-white">
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-on-accent">
|
||||
{activeCount}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -79,7 +79,7 @@ export function PeriodSelector({
|
||||
onClick={() => onChange(p)}
|
||||
className={`rounded-lg px-2.5 py-1 text-xs font-medium transition-colors ${
|
||||
value === p
|
||||
? 'bg-accent-500 text-white'
|
||||
? 'bg-accent-500 text-on-accent'
|
||||
: 'bg-dark-800 text-dark-400 hover:bg-dark-700 hover:text-dark-200'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -67,7 +67,7 @@ export function StatusFilter({
|
||||
<StatusIcon className="h-4 w-4" />
|
||||
{t('admin.trafficUsage.status')}
|
||||
{activeCount > 0 && (
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-white">
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-on-accent">
|
||||
{activeCount}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function TariffFilter({
|
||||
<FilterIcon className="h-4 w-4" />
|
||||
{t('admin.trafficUsage.tariff')}
|
||||
{activeCount > 0 && (
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-white">
|
||||
<span className="rounded-full bg-accent-500 px-1.5 text-[10px] text-on-accent">
|
||||
{activeCount}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -378,7 +378,7 @@ export function InfoTab(props: InfoTabProps) {
|
||||
<button
|
||||
onClick={onUpdateReferralCommission}
|
||||
disabled={actionLoading}
|
||||
className="w-full rounded-lg bg-accent-500 px-2 py-1 text-xs text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="w-full rounded-lg bg-accent-500 px-2 py-1 text-xs text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
{actionLoading ? t('common.loading') : t('common.save')}
|
||||
</button>
|
||||
@@ -475,7 +475,7 @@ export function InfoTab(props: InfoTabProps) {
|
||||
disabled={actionLoading}
|
||||
className={`rounded-lg px-3 py-2 text-sm font-medium transition-all disabled:opacity-50 ${
|
||||
confirmingAction === 'resetTrial'
|
||||
? 'bg-accent-500 text-white'
|
||||
? 'bg-accent-500 text-on-accent'
|
||||
: 'bg-accent-500/15 text-accent-400 hover:bg-accent-500/25'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -464,7 +464,7 @@ export function SubscriptionTab(props: SubscriptionTabProps) {
|
||||
<button
|
||||
onClick={() => selectedTrafficGb && onAddTraffic(Number(selectedTrafficGb))}
|
||||
disabled={actionLoading || !selectedTrafficGb}
|
||||
className="shrink-0 rounded-lg bg-accent-500 px-4 py-2 text-sm text-white transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
className="shrink-0 rounded-lg bg-accent-500 px-4 py-2 text-sm text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50"
|
||||
>
|
||||
{t('admin.users.detail.subscription.addButton')}
|
||||
</button>
|
||||
|
||||
@@ -348,7 +348,7 @@ function ChatView({
|
||||
onClick={onReply}
|
||||
disabled={!replyText.trim() || replySending}
|
||||
aria-label={t('admin.tickets.sendReply', 'Send reply')}
|
||||
className="min-h-[44px] min-w-[44px] shrink-0 self-end rounded-lg bg-accent-500 px-4 py-2 text-sm text-white transition-colors hover:bg-accent-600 disabled:opacity-50 sm:min-h-0 sm:min-w-0"
|
||||
className="min-h-[44px] min-w-[44px] shrink-0 self-end rounded-lg bg-accent-500 px-4 py-2 text-sm text-on-accent transition-colors hover:bg-accent-600 disabled:opacity-50 sm:min-h-0 sm:min-w-0"
|
||||
>
|
||||
{replySending ? (
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
||||
|
||||
Reference in New Issue
Block a user