Новая настройка app_settings.indicator_mode (color | accessible), переключается на /setup. В режиме accessible сигналы, которые раньше держались только на цвете, переходят на форму/текст: - качество цены (0-9) — 5-ступенчатый signal-meter вместо градиентной точки/числа; единый модуль web/static/price-quality.js, ветвление по window.QF_INDICATOR_MODE; - вкладка «Ценообразование»: ведущая колонка-meter вместо заливки строк, пометка W вместо амбер-подсветки world-цен, ⚠ вместо красного «загрязнённого» итога. - GET/PUT /api/settings/ui (без рестарта), регистрируется в обоих наборах роутов. /setup переустроен: две колонки одной высоты, кнопка «Вернуться в приложение», исправлен <title>. Документация: bible-local 02/03/04 + decisions/2026-08-31-accessible-indicator-mode.md Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LbRvQgPZpM4SJTaX3iLrXk
3.7 KiB
Decision: indicator_mode — a colour-optional rendering mode for UI signals
Date: 2026-08-31 Status: active
Context
Several UI signals were encoded by hue alone and unreadable with a colour-vision deficiency:
price_quality(0–9) as a red→yellow→green gradient — a dot in the configurator search dropdown and inline beforelot_name, a coloured number in the pricelist "Качество" column, a full-row background tint on the "Ценообразование" tab;- the amber
world-fallback price cell tint (2026-07-10-world-pricelist-fallback.md); - the red "contaminated total" in the pricing footer (2026-07-24-pricing-total-world-share.md).
The bible has no dedicated accessibility contract, but table-management §Icon Semantics,
controls-selection §Buttons, go-code-style §Business Logic Placement and build-version-display
say the same thing: meaning is never carried by one ambiguous channel — back it with
shape / number / explicit title+aria-label, and keep the indicator visually subordinate.
Decision
One per-client preference, app_settings.indicator_mode = color (default) | accessible,
set on /setup ("Настройки") via GET/PUT /api/settings/ui (internal/handlers/settings.go)
— no restart. Deliberately named for the concern (how indicators are rendered), not for the
current widget, so future colour-only signals attach to the same switch.
internal/handlers/web.go render() and internal/handlers/setup.go inject the mode for every
page; base.html publishes it as window.QF_INDICATOR_MODE. web/static/price-quality.js is
still the single source of the price-quality scale and branches on the mode internally, so its
call sites do not change.
In accessible mode:
price_qualityrenders as a 5-step signal-strength meter (priceQualityLevel=min(4, floor(score/2))→0-1 / 2-3 / 4-5 / 6-7 / 8-9), five bars in one neutral hue, each instance carryingtitle/aria-label=Качество цены: N/9.qualityDotHtmlandqualityBadgeHtmlreturn the meter;qualityRowStylereturns''— the pricing tab instead shows the meter in a narrow leading column (conditional<th>/colspaninindex.html, per-sub-row<td>);- the
world-fallback amber class is dropped for aWsuperscript text marker on the price (text, not a colour class —applyCustomPrice's colour-stripping regex is a non-issue); _setPricingTotaldropstext-red-600for a leading⚠glyph, on the same trigger as the red (worldShare > 0). The hover popup — including the "prices for N of M positions" coverage line — is unchanged in both modes; per 2026-07-24 there is still no colour-coded coverage-only signal, andaccessiblemode does not add one.
color mode is byte-for-byte the previous behaviour.
Consequences
- Default unchanged; nothing moves until a user opts in on
/setup. web/static/price-quality.jsstays the only place that knows the 0–9 scale. Any new colour-coded quality/price signal adds a branch there keyed onisAccessible(), never a local reimplementation, and itsaccessibleform must be shape/text, not another hue.- The pricing-tab tables have a mode-dependent column count (8 / 9).
index.htmlguards the<th>, the empty-statecolspan, thetfoot"Итого:"colspanand the JS row template onACCESSIBLE_MODE/{{ eq .IndicatorMode "accessible" }}. CSV export is untouched — it readsdata-*row attributes, not cell positions. - New routes
GET/PUT /api/settings/ui, registered in both the normal and setup-mode route sets incmd/qfs/main.go.render()nil-guardslocalDBfor tests.