Радіо
Вибір одного з кількох, і вибір іде в замовлення. Саме це відрізняє радіо від чипа: чип фільтрує вигляд, радіо обирає значення. Натиснув чип - змінилось, що ти бачиш; натиснув радіо - змінилось, що ти купуєш.
Третій блок тут - .co-sub: те, що строка відкриває під собою, коли її обрали. Рядок .co-opt:not(.on) + .co-sub ховає його, поки строка над ним не обрана - тому в демо його не видно. Натисніть другу строку, і він з'явиться.
.co-radio - 18×18, лише індикатор. Крапка всередині - ::after, а не другий елемент, бо розмітку пише спільний скрипт і ми її не змінюємо.otn - що обираєш.otd і ціна .op - строк і гроші, тобто те, чим насправді обираютьБраузер обійшов 40 кольорових екранів на 1280 і 390: 83 елементи. Одну роботу писали три файли, і кожен розходився з двома іншими майже в усьому.
| Вісь | .co-opt · 21 | .vopt · 17 | .pf-lang · 2 |
|---|---|---|---|
| файл до 7.29 | radio.css | buy-box.css | checkout-form.css |
| радіус | 12 | 8 | 12 |
| падінг | 12/16 | 8/12 | 16/16 |
| край у спокої | --line-hair | --line-strong | --line-strong |
| наведення | --line-strong | --line-inverse | немає |
| обраний край | --line-action | --line-action | --line-inverse |
| обраний ґрунт | --bg-action-soft | --bg-page | --bg-sunken |
| кружечок | 18×18 | немає | 20×20 |
| недоступно | немає | .off | немає |
| справжній input | ні | ні | так |
Дві сходинки справжні, решта була дрейфом. Радіо, що стоїть саме в стовпчику, із заголовком і причиною під ним - це строка: вона несе те, чим обирають, тому їй потрібен кружечок, і тихий край, бо сторінка в неї своя. Радіо, що стоїть плечем до плеча в наборі з чотирьох, - це компакт: на кружечок місця немає, стан несе сам край, тому він від початку на крок темніший.
Остання - .vopt.off: смак, якого нема в наявності. Перекреслений, пунктирний край, приглушене чорнило, курсор не міняється. Це єдиний стан «недоступно» в усій родині, і в атома чипа такого досі немає - пункт Кроку 6, і копіювати треба звідси.
На щаблі ще двоє, і сторінка їх не показувала. .opt-tile прийшов на 7.96 - рід тренера на coach-verify, єдиний член родини, який називає себе і пояснює себе другим рядком. .q-pill прийшов на 12.11 разом із квізом: піл, що обирає формат або бюджет, обирає ЗНАЧЕННЯ, а не вигляд, тож він радіо, а не чип. Обидва беруть радіус щабля, а не власний.
| Що | Було | Стало |
|---|---|---|
.pf-lang у спокої | --line-strong, падінг 16/16, без фону | --line-hair, 12/16, --bg-page |
.pf-lang.on | чорнильний край + --bg-sunken | акцентний край + --bg-action-soft |
.pf-lang.on .rc | 20×20, чорнильна заливка з білим кільцем | 18×18, акцентний край + акцентна крапка |
Дві сім'ї з трьох казали, що обране - акцентне, і принцип каже те саме: акцент це єдиний колір дії. Отже третя приходить до них, а не навпаки. Два контроли, обидва в діалозі клієнта тренера.
Виміряно на 40 екранах: жоден із 38 контролів не був досяжний з клавіатури. .co-opt це <label>, який не вказує на жоден input; .vopt це <span>. Ні role, ні tabindex, нічого не оголошується. Чекаут неможливо було пройти без миші.
Продукт уже мав відповідь у двох місцях і не вживав її в жодному: вибір мови тримає справжній input[type=radio] всередині лейбла, а зірковий вибір оцінки будується з role="radiogroup" у wireframes/_nav.js. Узято друге - перше потребує нової розмітки, а розмітка належить замороженому шару.
| Клавіша | Що робить | Перевірено |
|---|---|---|
| Tab | заходить у групу і виходить з неї цілком - один стоп на набір, а не по одному на варіант | Tab з групи веде на кнопку міста |
| ↓ ↑ → ← | рухають фокус і разом з ним вибір, по колу | 0→1→2→0, aria-checked іде слідом |
| Пробіл, Enter | обирають те, на чому стоїш | на .off не роблять нічого |
.off | пропускається стрілками, aria-disabled, tabindex="-1" | обхід 0→1→2→0→1, індекс 3 не відвіданий |
Вибір іде через .click(), а не через клас - тому все, що сторінка вже робить на клік, працює й з клавіатури. Доказ у браузері: стрілка вниз на чекауті міняє доставку і перераховує суму - 3 999 → 4 019 → 3 939 ₴, точно як миша.
Кільце фокуса - --ring-focus-control, який існує з кроку 6.2 і якого жодне радіо не читало, бо читати його не було кому. :focus-visible, а не :focus, щоб миша його не запалювала.
ПОВЕДІНКА НА ШИРИНІ - етап 10, крок 4
Від якого порогу: жодного. Що саме міняється: група переносить варіанти черезflex-wrap. Плинне з самого початку.Один із кількох, і вибір іде в замовлення: спосіб доставки, спосіб оплати, смак, розмір, мова листів. Якщо варіантів більше семи або вони не впливають на замовлення - це Меню. Якщо вибір фільтрує вигляд - це Чип.
Правило
Уся строка натискається. 599×71 проти 18×18 - різниця між «влучив» і «промазав» на телефоні. І пояснення обов'язкове там, де варіанти різняться ціною або строком, тобто майже завжди.Антиправило
Не роби радіо з того, що не йде в замовлення. Вкладки товару, фільтри каталогу, сортування - там теж «обери одне», але вибір міняє вигляд, а не покупку. Це Чип і Меню, у них свої атоми.| Пара | Контраст | Вирок |
|---|---|---|
заголовок --text-body на --bg-page | 15.9:1 | проходить |
пояснення --text-secondary на --bg-page | 6.8:1 | проходить AA на 12px |
обраний край --line-action | 3.13:1 | межа, не текст - для нетекстового контрасту поріг 3:1, проходить |
обраний ґрунт --bg-action-soft | - | підкладка під тим самим чорнилом: заголовок лишається 15.4:1 |
Обране ніколи не тримається на одному кольорі: край, ґрунт і крапка в кружечку кажуть це втричі. Тому вибір видно і в чорно-білому друку, і людині, яка не розрізняє помаранчевий.
Кожен стан цього сімейства рухав рамку, а рамка - це відповідь курсору: 1px, що змінює відтінок, читається під вістрям миші й невидиме під великим пальцем, який її ж і накрив. 38 екземплярів на телефоні - спосіб доставки, спосіб оплати, смак - і жоден не підтверджував натиск.
| Що | Під пальцем | Чому |
|---|---|---|
.vopt .co-opt .pf-lang, не обрані | #FFF9F5 | --bg-action-soft, як у всього набору |
обрані .on | нічого | .co-opt.on і .pf-lang.on у спокої вже лежать на --bg-action-soft: натиску нема куди їх вести. Тиснути на вже обране - дія без наслідку, і вдавати інше не варто |
.vopt.off | нічого | розпродано, cursor: default - з тієї ж причини, що й інертні клітинки перемикача вигляду |
.vopt прозорий -> #FFF9F5, .co-opt #FFFFFF -> #FFF9F5Сторінка звіряє себе з файлом: 12 власних класів і 4 стани оголошено в radio.css.
Робота: ВІДПОВІДЬ. Рухається: border-color, background, box-shadow, color. Тривалість --dur-fast (150ms), крива --ease-standard. Значення читаються через var() з tokens.css: підміна токена в браузері веде за собою кожне з них, тобто числа у файлі немає. При prefers-reduced-motion рух зникає, а стан лишається.
| Семантичні ролі (12) | Примітиви (14) |
|---|---|
--bg-action --bg-action-soft --bg-page --line-action --line-hair --line-inverse --line-strong --ring-focus-control --text-body --text-muted --text-primary --text-secondary | --font-mono --fs-12 --fs-14 --fw-bold --fw-medium --fw-semibold --radius-8 --radius-12 --radius-circle --space-2 --space-4 --space-8 --space-12 --space-16 |
| – | --dur-fast --ease-standard |
Одне число без токена - 18px кружечка. Воно назване рішенням у самому файлі: 18 у 21 місці проти 20 у двох.
role, tabindex і aria-checked дописує design/_nav.js на кожній сторінці - у розмітці їх немає і не буде, доки її власником лишається заморожений сірий шар. Рядок .co-opt:not(.on) + .co-sub ховає те, що строка відкрила б, якби її обрали.
Якорі: .co-opt .vopt .pf-lang
5 екранів: checkout.html, checkout-loggedin.html, checkout-noaddr.html (по 2 групи, 7 контролів), product.html, product-coach.html (по 3 групи, 12 контролів), product-oos.html (1 група), account-profile.html (вибір мови в діалозі).