Files
houseplan-card/docs/specs/217-text-shell-outline.md
2026-08-20 13:07:49 +03:00

15 KiB
Raw Permalink Blame History

Issue #217 — внешняя рамка Text-маркера должна быть капсулой

  • Issue: https://github.com/Matysh/houseplan-card/issues/217
  • Связанные завершённые задачи: #179, #211, #213
  • Нормативный дизайн: demo/srv/reference/device-icons/Light/Text Default.svg и demo/srv/reference/device-icons/Dark/Text Default.svg из пакета #179
  • Тип: bug / polish, обычный трек
  • Приоритет: P1
  • Пользовательское изменение: да

1. Сценарий и персона

Персона: домочадец или гость, который читает текстовое состояние устройства в View/kiosk, и администратор, который проверяет тот же маркер в редакторе.

Сценарий: пользователь видит Text-маркер с широким значением, например 498 ppm, и ожидает, что внешний shell повторяет капсульную форму внутреннего белого core. После #213 core остаётся капсулой, но внешняя рамка стала эллипсом: вместо прямого среднего участка верхняя и нижняя границы изгибаются по всей ширине.

2. Что человек увидит до и после

До исправления широкий Text-маркер окружён овальной рамкой, которая визуально не совпадает ни с core, ни с дизайн-пакетом #179. После исправления внешняя рамка имеет форму stadium: торцы являются полуокружностями, а между ними идут прямые горизонтальные участки. Размер, положение и интерактивная область маркера не меняются.

3. Подтверждённая причина

src/styles.ts задаёт правильный общий fallback border-radius: 9999px для .device-shell-frame, но затем правило .device-shell:not(.with-values) .device-shell-frame заменяет его на 50%. Text-маркер имеет класс .text-shell, но не .with-values, поэтому для широкой рамки 50% вычисляется отдельно по ширине и высоте и даёт эллипс.

Регрессия появилась в реализации #213. Проверки её не поймали, потому что:

  1. smoke textCoreIsStadium проверяет только внутренний core;
  2. capsule smoke проверяет hover/action, а не форму внешней рамки;
  3. существующий golden показывает Text-маркер слишком мелко, поэтому неверная кривизна не была заметна при визуальном review.

4. Нормативные источники и приоритет

При расхождении применяются:

  1. решения владельца в #217;
  2. это ТЗ после зелёного SPEC-REVIEW;
  3. Light/Dark Text Default.svg из принятого пакета #179;
  4. контракты #213 для размеров, inset, соосности и полной hover/action-области;
  5. текущая реализация как compatibility baseline для поведения, которое это ТЗ явно не меняет.

Reference SVG задаёт stadium как для core, так и для внешней рамки Text.

5. Цели

  1. Вернуть внешней рамке Text-маркера форму stadium во всех поверхностях.
  2. Не изменить размеры, inset, anchor, цвета и поведение маркера.
  3. Добавить автоматическую проверку именно внешней рамки, падающую при 50%.
  4. Сделать дефект заметным в Light/Dark golden review.

6. Scope

В задачу входят:

  • общий Text device face в full View, kiosk, Device editor/preview и houseplan-space-card;
  • Light/Dark и цветной фон плана;
  • короткие и длинные Text-значения на поддерживаемой матрице размеров;
  • CSS-правило формы внешнего .device-shell-frame;
  • source/unit contract, targeted browser smoke и крупный golden-сценарий;
  • оба changelog и testing-документация.

7. Не входит в задачу

  • изменение ширины, высоты, inset, stroke, shadow, цвета или opacity;
  • изменение сохранённой координаты, icon_size, marker size или badge side;
  • изменение core, MDI-глифа, LQI, pulse, availability или virtual-state;
  • изменение Icon-only, Double/value/legacy capsule и opening Lock/Unlock;
  • изменение hover/action target, tooltip, z-index, keyboard или touch поведения;
  • новые настройки, строки, schema или миграция данных;
  • откат остальных изменений #213;
  • принятие golden baseline на Windows или выпуск без команды владельца.

8. Визуальный контракт

8.1. Text

Для .device-shell.text-shell .device-shell-frame:

  • внешний shell имеет stadium-форму при любой ширине больше высоты;
  • радиус каждого внешнего угла визуально равен половине итоговой высоты рамки с допуском browser layout/raster quantum;
  • верхняя и нижняя границы между торцами имеют прямой горизонтальный участок;
  • рамка окружает core с существующим --device-shell-inset;
  • core и shell сохраняют текущие размеры, центр и относительное положение;
  • CSS border-radius: 50% не применяется к широкой Text-рамке.

Нормативный CSS-подход может использовать saturating radius (9999px) либо эквивалентное height-derived значение. Запрещены фиксированные pixel-компенсации по размеру, viewport или DPR.

8.2. Неизменяемые варианты

  • Icon-only shell остаётся окружностью;
  • внутренний Text core остаётся stadium;
  • Double/value/legacy shell сохраняет существующую capsule geometry;
  • opening Lock/Unlock остаётся круглым;
  • внешний footprint и hit area всех вариантов не меняются.

8.3. Матрица

Контракт проверяется минимум на:

  • длинном значении (498 ppm либо более длинной локализованной строке);
  • effective base 24, 32, 56, 96 и 112 CSS px;
  • DPR 1, 1.25, 1.5 и 2;
  • Light и Dark;
  • full, preview и static/space-card parity surfaces.

9. UX, accessibility и touch

Вся внешняя Text-капсула по-прежнему является одной hover- и action-областью, как принято в #213. Минимальная интерактивная область остаётся не меньше 44×44 CSS px. Focus-visible, tooltip, accessible name, tab order, confirmation и действие не меняются. Touch/pen не получают новый hover; unavailable сохраняет текущее поведение.

10. Данные, migration и i18n

Config, layout, localStorage, backend schema и сериализация не меняются. Миграция не нужна; Open → Save не материализует новые поля. Новых пользовательских строк и i18n-ключей нет.

11. Архитектура и зоны изменений

Исправление должно оставаться в общем device face и применяться ко всем поверхностям без второго renderer:

device presentation
  → shared renderDeviceFace classes
  → common device styles
  → Text-only stadium shell / Icon-only circle

Ожидаемые зоны изменений:

  • src/styles.ts — сузить circular override так, чтобы он не захватывал Text;
  • test/device-marker-polish-contract.test.mjs — source regression contract;
  • demo/smoke_device_icon_design.mjs — computed-style/geometry matrix внешней Text-рамки и неизменённой круглой Icon-рамки;
  • golden/reference matrix — крупный Text marker в Light/Dark;
  • docs/TESTING.md, docs/CHANGELOG.md, docs/CHANGELOG.ru.md.

12. Performance и security

Исправление является CSS-only geometry change. Нельзя добавлять per-frame JS, ResizeObserver, media listener, отдельный DOM-слой, backdrop-filter или новую анимацию. Число paint/compositor layers не увеличивается. Action и security paths не меняются.

13. Acceptance criteria

  1. AC1 — stadium shell. Длинный Text-маркер имеет внешнюю stadium-рамку с прямым средним участком в Light/Dark. Доказательство: browser smoke и reviewed крупный golden.
  2. AC2 — защита от 50%. Regression test проверяет внешнюю рамку и краснеет на documented mutant, возвращающем border-radius: 50% для Text. Доказательство: source/computed-style smoke + mutant result.
  3. AC3 — матрица размеров. Контракт Text shell выполняется на §8.3 без per-size/DPR исключений. Доказательство: browser matrix.
  4. AC4 — геометрическая совместимость. До/после совпадают outer rect, inset, core rect и anchor с допуском одного browser layout quantum; меняется только corner shape. Доказательство: browser rect assertions.
  5. AC5 — соседние варианты. Icon-only остаётся кругом, внутренний Text core и Double/value/legacy capsule не меняются. Доказательство: targeted smoke и existing device-face tests.
  6. AC6 — interaction parity. Hover и действие по всей Text-капсуле, 44×44 minimum target, mouse/touch modality и unavailable остаются как в #213. Доказательство: existing capsule hover/action smoke.
  7. AC7 — surface/theme parity. Full View/kiosk, preview и static/space-card показывают одинаковую форму в Light/Dark. Доказательство: parity smoke и golden.
  8. AC8 — data/i18n/security/performance. Нет schema, storage, i18n, action, observer, animation или compositor changes. Доказательство: unit/source review и fast performance sanity.
  9. AC9 — release artifacts. Оба changelog, testing docs, smoke/golden fixtures и visual fingerprints актуальны. Доказательство: docs diff, check-docs и release review.

14. План автотестов

14.1. Цикл реализации

npm run typecheck
npm test
npm run build
node scripts/check-docs.mjs --external

Source/unit guard должен явно различать Text и Icon-only selector contract. Browser smoke получает внешнюю рамку через .device-shell-frame, проверяет Text saturating radius и круглую Icon-рамку, сравнивает rect/inset до и после смены размеров и фиксирует documented mutant 50% как failing case.

14.2. Targeted smoke перед S7

  • node demo/smoke_device_icon_design.mjs;
  • node demo/smoke_device_preview_parity.mjs;
  • node demo/smoke_static_icon.mjs;
  • capsule hover/action smoke из #213;
  • Light/Dark reference/golden capture с крупным длинным Text-маркером.

Golden на Windows используется только диагностически. Канонический baseline принимается из reviewed Linux CI artifact перед beta.

15. Риски и меры

Риск Мера
Слишком широкое правило перестанет округлять Icon-only Явная Icon-vs-Text source/browser проверка
Исправление изменит footprint или anchor До/после rect/inset assertions
Double/value capsule получит новый радиус Отдельный unchanged regression check
Дефект снова потеряется на мелком golden Выделенный крупный long-Text сценарий в Light/Dark
Появится surface-specific CSS Проверка shared face parity без второго renderer

16. Откат

Откат одного implementation commit возвращает прежний selector и visual fixtures. Данных и миграций нет. Golden baseline возвращается только через нормативный Linux artifact, а не ручным принятием локального Windows capture.

17. Release-артефакты

В user-visible implementation commit одновременно обновляются:

  • docs/CHANGELOG.md и docs/CHANGELOG.ru.md со ссылкой на #217;
  • docs/TESTING.md с новым Text-shell regression contract;
  • browser smoke и крупный Light/Dark golden/reference сценарий;
  • screenshot/golden fingerprints, если их источник изменён.

Release note: внешняя рамка текстовых маркеров снова соответствует капсульной форме дизайн-пакета вместо овала. Сначала beta; stable — только по отдельной команде владельца.

18. Принятые предположения

  1. Дизайн-пакет #179 однозначно задаёт stadium для внешней рамки Text, поэтому дополнительного продуктового вопроса не требуется.
  2. Исправляется только форма внешней Text-рамки; её размеры, inset и цвета остаются текущими.
  3. Icon-only остаётся кругом, Double/value/legacy и opening lock не меняются.
  4. Новых настроек, строк и миграций нет.
  5. Для визуального review используется отдельный крупный длинный Text marker, а не только существующий общий мелкий golden.