fix(ui/toasts): глобальная видимость (тон+рамка+тень) + перенос в top-center без перекрытия (#517) #528

Merged
vvzvlad merged 1 commits from fix/517-toasts into develop 2026-07-12 04:06:42 +03:00
Collaborator

Summary

Реviewer-filed баг #517: тосты почти не видны (в светлой теме фон карточки = var(--mantine-color-body) = тот же белый, что страница; тень едва заметна → нет видимого края) и всплывают внизу (bottom-center), перекрывая контент. closes #517. Чиним ГЛОБАЛЬНО (один механизм, без правок в 213 call-site).

  • Видимость — новый apps/client/src/styles/notification-overrides.css: безусловный override корня тоста (.mantine-Notification-root) даёт видимый край — рамка + shadow-xl + лёгкий тон из --notification-color (light: ~10% на белом, dark: 14% на dark-6 + dark-3 рамка). Покрывает и бесцветные success/info, и loading/импорт-тост (у него нет акцент-полосы — слот иконки занят спиннером). Специфичность [data-mantine-color-scheme] .mantine-Notification-root = (0,2,0) выигрывает у mantine :where(...) .m_a513464 = (0,1,0) для фона/рамки/тени (проверено в собранном CSS).
  • Позицияmain.tsx: bottom-centertop-center. Смещение под фикс-хром (45px шапка + опц. форматтулбар) — через CSS .mantine-Notifications-root[data-position^=top] { top: 96px }, НЕ через инлайн style (Mantine рендерит все шесть позиционных контейнеров, инлайн style попал бы во все → нижние с top+bottom растянулись бы на весь вьюпорт невидимыми кликоперехватчиками — этот блокер поймало внутреннее ревью и починено). z-10000 сохранён; тост сидит ниже шапки (z-99), кликов не перехватывает.

How verified

  • tsc -p apps/client 0; vite build зелёный. В собранном CSS: top:96px ровно один раз как [data-position^=top], в JS-бандле top:96 ноль (инлайн убран); нижние контейнеры без top → height:0, оверлея нет.
  • Внутреннее ревью подтвердило специфичность по собранному CSS (не no-op), цветные тосты не выцветают (акцент+иконка красные), тёмная тема ок, и поймало блокер инлайн-смещения (починен).

Осознанное ограничение

Юнит-теста нет (визуальный фикс без браузера; полный рендер <Notifications> требует MantineProvider+store). Доказательство — собранный CSS (top-scoped offset, нижние без top) + специфичность по исходнику Mantine. Стандартные визуальные проверки — на стенде.

Checklist

  • тосты видимы (край есть на белой странице), light+dark
  • top-center, не перекрывают шапку/тулбар, не перехватывают клики
  • без правок в 213 call-site; 0 node_modules
## Summary Реviewer-filed баг #517: тосты почти не видны (в светлой теме фон карточки = `var(--mantine-color-body)` = тот же белый, что страница; тень едва заметна → нет видимого края) и всплывают внизу (`bottom-center`), перекрывая контент. closes #517. Чиним ГЛОБАЛЬНО (один механизм, без правок в 213 call-site). - **Видимость** — новый `apps/client/src/styles/notification-overrides.css`: безусловный override корня тоста (`.mantine-Notification-root`) даёт видимый край — рамка + `shadow-xl` + лёгкий тон из `--notification-color` (light: ~10% на белом, dark: 14% на dark-6 + dark-3 рамка). Покрывает и бесцветные success/info, и loading/импорт-тост (у него нет акцент-полосы — слот иконки занят спиннером). Специфичность `[data-mantine-color-scheme] .mantine-Notification-root` = (0,2,0) выигрывает у mantine `:where(...) .m_a513464` = (0,1,0) для фона/рамки/тени (проверено в собранном CSS). - **Позиция** — `main.tsx`: `bottom-center` → `top-center`. Смещение под фикс-хром (45px шапка + опц. форматтулбар) — через CSS `.mantine-Notifications-root[data-position^=top] { top: 96px }`, НЕ через инлайн `style` (Mantine рендерит все шесть позиционных контейнеров, инлайн `style` попал бы во все → нижние с `top`+`bottom` растянулись бы на весь вьюпорт невидимыми кликоперехватчиками — этот блокер поймало внутреннее ревью и починено). z-10000 сохранён; тост сидит ниже шапки (z-99), кликов не перехватывает. ## How verified - `tsc -p apps/client` 0; `vite build` зелёный. В собранном CSS: `top:96px` ровно один раз как `[data-position^=top]`, в JS-бандле `top:96` ноль (инлайн убран); нижние контейнеры без `top` → height:0, оверлея нет. - Внутреннее ревью подтвердило специфичность по собранному CSS (не no-op), цветные тосты не выцветают (акцент+иконка красные), тёмная тема ок, и поймало блокер инлайн-смещения (починен). ## Осознанное ограничение Юнит-теста нет (визуальный фикс без браузера; полный рендер `<Notifications>` требует MantineProvider+store). Доказательство — собранный CSS (top-scoped offset, нижние без top) + специфичность по исходнику Mantine. Стандартные визуальные проверки — на стенде. ## Checklist - [x] тосты видимы (край есть на белой странице), light+dark - [x] top-center, не перекрывают шапку/тулбар, не перехватывают клики - [x] без правок в 213 call-site; 0 node_modules
agent_coder added 1 commit 2026-07-12 01:16:11 +03:00
Тост-уведомления Mantine сливались с фоном: у бесцветных тостов фон
карточки == var(--mantine-color-body) (белый, как страница) при слабой
тени, поэтому на белых страницах у карточки не было видимого края. Плюс
тосты всплывали снизу по центру и перекрывали контент.

Чиним глобально, без правок в 213 местах вызова:

- notification-overrides.css: каждому тосту даём тонированный по типу фон,
  рамку с контрастом WCAG >= 3:1 и усиленную тень (shadow-xl). Селектор
  [data-mantine-color-scheme=...] .mantine-Notification-root имеет
  специфичность (0,2,0) и стабильно бьёт правила Mantine (0,1,0) (у Mantine
  атрибут схемы обёрнут в :where()) — независимо от порядка стилей. Тон/рамка
  идут от --notification-color (определён на том же элементе), поэтому следуют
  типу тоста и покрывают loading/импортный тост (полосы-акцента нет — несут
  тон+рамка+тень+цветной спиннер). Обе темы; текст-с-заголовком поднят до
  gray-7 ради AA-контраста на тонированном фоне.

- main.tsx: position bottom-center -> top-center. Вертикальное смещение
  контейнера ниже верхней хромы делаем НЕ инлайн-стилем, а CSS-правилом со
  скоупом по позиции: Mantine рендерит все шесть позиционных контейнеров
  одновременно, и корневой style-проп ушёл бы во все шесть — нижним (bottom:16)
  добавился бы top:96 → position:fixed + оба края + height:auto растянули бы их
  на весь вьюпорт; у корня нет pointer-events:none/фона → прозрачные оверлеи
  z-10000 перехватывали бы клики по всей странице.

- notification-overrides.css: .mantine-Notifications-root[data-position^='top']
  { top:96px } (шапка 45 + опц. тулбар 45 + зазор). Скоуп ^='top' смещает
  только верхние контейнеры; нижние остаются height:0 и кликов не перехватывают.
  Специфичность (0,2,0) бьёт mantine top:16px (0,1,0), тост z-10000 стоит ниже
  шапки/тулбара (z-99) и их не перекрывает.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agent_coder added the review/needs label 2026-07-12 01:16:12 +03:00
Collaborator

🤖 Авто-ревью · #528 (ui/toasts видимость + top-center, #517) — голова 51260793. Вердикт: PASS

Чистая, аккуратная CSS-работа — багов нет. Pure presentational (2 файла, +74), объективно-логической поверхности нет; сфокусировал веер на значимых углах (specificity-crux + coherence/regressions + conventions/doc/simplification), сверил лично + два субагента против УСТАНОВЛЕННОГО Mantine 8.3.18 CSS.

Сверено:

  • Specificity выигрывает без !important: override [data-mantine-color-scheme] .mantine-Notification-root = (0,2,0) бьёт mantine :where(...) .m_a513464 = (0,1,0) для bg/border/shadow в обеих темах (mantine оборачивает scheme в :where() → 0). Не-layer styles.css, withStaticClasses не выключен → класс эмитится. Акцент-полоса ::before (per-toast --notification-color) НЕ тронута, tint/border её же и юзают → тон по типу тоста сохраняется.
  • Автор сам избежал тонкой ловушки (и задокументировал): mantine рендерит ВСЕ 6 position-контейнеров разом, поэтому blanket-top/inline-style растянул бы прозрачные bottom-* z-10000 контейнеры на весь вьюпорт и глотал бы клики по всей странице; scoping [data-position^='top'] это чинит. Оффсет 96px (45 шапка + 45 тулбар + 6) сверен против реальных высот, чистит хром.
  • Dark mode корректен (dark-6 tint, dark-3 border), не инвертирован. WCAG: border ≥3:1 non-text, gray-6→gray-7 для titled-тостов (AA), консистентно с theme.ts.
  • Регрессий нет: все 213 call-site идут через notifications.show; standalone <Notification> нет; in-app inbox юзает свой notification.module.css. Override трогает только тосты.
  • Паттерн верный: *-overrides.css в styles/ точно следует прецеденту a11y-overrides.css (не theme-config — tint нотификейшн-скоупед).

Гейт: client tsc чист (без ошибок от изменения), CSS-импорт резолвится. Для pure-CSS достаточно.

Отличный фикс. Approve.

## 🤖 Авто-ревью · #528 (ui/toasts видимость + top-center, #517) — голова `51260793`. Вердикт: **PASS** ✅ Чистая, аккуратная CSS-работа — багов нет. Pure presentational (2 файла, +74), объективно-логической поверхности нет; сфокусировал веер на значимых углах (specificity-crux + coherence/regressions + conventions/doc/simplification), сверил лично + два субагента против УСТАНОВЛЕННОГО Mantine 8.3.18 CSS. Сверено: - **Specificity выигрывает без `!important`:** override `[data-mantine-color-scheme] .mantine-Notification-root` = (0,2,0) бьёт mantine `:where(...) .m_a513464` = (0,1,0) для bg/border/shadow в обеих темах (mantine оборачивает scheme в `:where()` → 0). Не-layer `styles.css`, `withStaticClasses` не выключен → класс эмитится. Акцент-полоса `::before` (per-toast `--notification-color`) НЕ тронута, tint/border её же и юзают → тон по типу тоста сохраняется. - **Автор сам избежал тонкой ловушки** (и задокументировал): mantine рендерит ВСЕ 6 position-контейнеров разом, поэтому blanket-`top`/inline-style растянул бы прозрачные bottom-* z-10000 контейнеры на весь вьюпорт и глотал бы клики по всей странице; scoping `[data-position^='top']` это чинит. Оффсет 96px (45 шапка + 45 тулбар + 6) сверен против реальных высот, чистит хром. - **Dark mode** корректен (dark-6 tint, dark-3 border), не инвертирован. **WCAG**: border ≥3:1 non-text, `gray-6→gray-7` для titled-тостов (AA), консистентно с theme.ts. - **Регрессий нет:** все 213 call-site идут через `notifications.show`; standalone `<Notification>` нет; in-app inbox юзает свой `notification.module.css`. Override трогает только тосты. - **Паттерн верный:** `*-overrides.css` в `styles/` точно следует прецеденту `a11y-overrides.css` (не theme-config — tint нотификейшн-скоупед). **Гейт:** client tsc чист (без ошибок от изменения), CSS-импорт резолвится. Для pure-CSS достаточно. Отличный фикс. Approve.
agent_reviewer added review/approved and removed review/needs labels 2026-07-12 03:19:15 +03:00
vvzvlad merged commit d608311cae into develop 2026-07-12 04:06:42 +03:00
Sign in to join this conversation.