Компонент · рівень 1

Чекбокс

Незалежний вибір: фільтр у рейці, згода на умови. Кожен стоїть сам за себе. 201 штука - і до кроку 7.38 їх малював не цей файл.

рівень 1design/system/components/checkbox.css236 рядків201 чекбокс15 екранів
Анатомія
1Квадрат .cb - квадрат, а не коло - форма одразу каже «можна кілька»
2Підпис - назва фільтра
3Лічильник - скільки товарів знайдеться - його бачать до натискання
Атом перемальовувала молекула

201 чекбокс: .fopt 200 у панелі фільтрів, .optin 1 у діалозі реєстрації. filter-group.css - рівень 2 - писав .fopt .cb, .optin .cb: тягнувся повз власний компонент і перефарбовував клас атома на ім'я. Тому все, що цей файл казав про квадрат, було мертве. Заміряно на HEAD по 39 кольорових екранах: 18px, рамка --line-strong і галочка, які людина бачить, належали filter-group; власні 18px, рамка й галочка checkbox.css не рендерилися ніде, на жодному з двох імен.

А галочок було дві редакції, і малювалася одна:

ДеГалочкаКолірПосадкаРендерилась
checkbox.cssнадрукований ✓, 12px--text-oninversetop:-2 left:3, на оконіколи
filter-group.cssнамальована: 5×9, борти 0/2/2/0, поворот 45°--line-onactiontranslate(-50%,-58%)завжди

Те саме сімейство, що крапка наявності на 7.35 і зірка бейджа на 7.37 - надрукований знак проти намальованої форми. Тут вони існували одночасно, і намальована перемагала за порядком файлів, а це не рішення. По суті вона й правильна: надрукований ✓ читається вголос, не має товщини штриха і сідає туди, куди його поставить шрифт, а не туди, де його хоче квадрат.

Злиття вийшло піксель у піксель. A/B проти HEAD: 108 994 елементи, 40 сторінок × 2 ширини - нуль змін на всіх 201 чекбоксі. Дефект був не в тому, що щось виглядало не так, а в тому, що система казала три речі й малювала одну.

Варіанти і розміри

Матриця осей, а не перелік входжень: кожен рядок це питання, на яке відповідає розмітка, і правило, за яким обирають.

ВісьЗначенняПравило вибору
Контекстфільтр · згодаУ фільтрі поруч стоїть кількість, у згоді - посилання на документ. Різний вміст, той самий квадрат.
Коли вживати

У Група фільтра і в Форма чекауту для згоди. Правило: якщо варіанти можна поєднувати - чекбокс, якщо ні - Радіо.

Правило і антиправило

Правило

Показуй кількість заздалегідь. Фільтр з нулем поруч нікого не заведе в порожній список.

Антиправило

Не роби чекбокс для взаємовиключних варіантів. Квадрат обіцяє, що можна поставити два. Обіцянку, яку інтерфейс не виконує, людина запам'ятає.
Стани, що є в коді

Три: .on тримається на класі, тому він у списку класів; :hover і :active - у списку станів. Обидві редакції квадрата стоять поруч, бо їх дві і в коді.

Зверху дві опції фільтра (.fopt, 175 екземплярів у продукті), знизу згода на розсилку (.optin, один). Квадрат той самий, оголошений двічі - див. нижче
Натиск крок 7.33

Тиснуть на підпис, відповідає квадрат. :active не дістає до нащадків, тож .cb:active спрацювало б лише від прямого влучання у 18px - а це радше сама проблема цього контрола, ніж її розв'язок: мішень - увесь рядок, і палець приземляється саме на рядок. Та сама форма, яку :hover уже використовує рядком вище.

ЩоУ спокоїПід пальцем
порожній квадратпрозорий#FFF9F5
позначений .cb.on#FF5A00#E85200

Обидва імені контейнера, одне правило. .fopt належить filter-group.css і названий у checkbox.css тому, що саме там 175 із 176 екземплярів. Так само роблять stepper.css і stack-action.css, і з тієї самої причини: імена живуть і в замороженому сірому шарі, тож додати безпечно, а перейменувати - ні.

Атом оголошено двічі

Тут він прив'язаний до .optin - один екземпляр, згода на розсилку в діалозі реєстрації. Решта 175 - панель фільтрів, оголошена ще раз у filter-group.css, 9 правил, файл рівня 2. Один квадрат 18px, два власники, і файл рівня 1 тримає рідкісніший. Та сама форма, що й рядок чипів, який жив у link-row.css до 7.30b.

А панель фільтрів не мала ні клавіатури, ні скрінрідера закрито на 7.34

Прочитано з живого DOM, бо панель будує скрипт: <label class="fopt"><span class="cb"></span> В наявності</label>. Немає <input>. На listing.html - 50 опцій фільтра і 11 досяжних елементів у всій панелі, жоден з них не чекбокс. Контрол при цьому живий: клік переводить клас cb -> cb on. Це той самий дефект, що мав .ord-tab на 7.31 і .sw на 7.32. Розділ нижче - що з цим зробив крок 7.34.
Зміряно дотиком: порожній квадрат прозорий -> #FFF9F5, позначений #FF5A00 -> #E85200
Панель фільтрів отримала клавіатуру крок 7.34

Половина контракту була написана, і це читається гірше, ніж нічого. uivCheckboxes() уже писав aria-checked - і не писав ролі. aria-checked на елементі без ролі браузер відкидає: скрінрідеру не кажуть узагалі нічого, а файл при цьому має вигляд опрацьованого. Прочитано з дерева доступності, а не з домислу:

БулоСтало
опція фільтра{ role: "LabelText", name: "", checked: undefined }{ role: "checkbox", name: "В наявності 71", checked: false }
позначена опціяте саме{ role: "checkbox", name: "Optimum Nutrition 12", checked: true }
заголовок групи<div>, ролі немає{ role: "button", name: "Тип протеїну", expanded: true }

І писалось воно тільки на клік. З 34 опцій, які рендеряться вже позначеними, жодна не оголошувала цього, поки на щось не натиснуть. Тепер aria-checked ставиться на завантаженні.

ЩоСкількиЩо отримало
.fopt + .optin351 на 8 екранахrole="checkbox" · tabindex="0" · aria-checked · Space
.fgroup > .fh140role="button" · aria-expanded · Enter і Space
.fgroup140role="group" + ім'я зі слів заголовка

Space, а не Enter

Чекбокс відповідає на Space. Enter належить головній дії, а в мобільному шиті ця дія - «Застосувати». Тумблер на 7.32 узяв обидві клавіші, бо в його рядку більше нікому взяти Enter. Один файл, два прив'язки, і різницю робить те, біля чого стоїть контрол.

Мандрівного tabindex немає, і тут сім'я розходиться з радіо

uivRadioGroups() дає групі один крок, бо радіогрупа - це одне значення. Двадцять п'ять фільтрів - це двадцять п'ять незалежних відповідей, тож кожен бере власний крок: рейка йде з 11 до 46. Це ціна того, чим панель є, а не дефект, який треба підкрутити.

Заголовок групи був складанкою, яка нікому про це не сказала. Десять .fh на панель, cursor: pointer, і клік справді згортає групу. Поведінка була повна, обіцянки не було: <div> без ролі, без кроку табуляції і без aria-expanded. Сірий шар цей рух уже знає - toggleCab() і toggleLang() у тому самому файлі пишуть aria-expanded своїм тригерам. Дві складанки з трьох були названі, фільтрова - ні. Клас перемикає сірий шар, тут його тільки перечитують: дві редакції одного згортання - це рівно той дефект, який цей етап прибирає.

І закритий шит тримав 14 елементів у порядку табуляції. Знайдено тим, що крок 7.34 спочатку зробив гірше: transform: translateY(100%) зсуває панель за нижній край і лишає її видимою для всього, що не є оком. Зміряно на HEAD, 390, listing.html із закритим шитом: 14 досяжних елементів усередині - хрестик, шість чипів, чотири числові поля, «+ ще», «Скинути» і «Застосувати». Нові кроки табуляції довели це до 49. Кроки правильні, а шит був неправильний - і був таким ще до цього етапу. visibility: hidden з відкладеним переходом - та сама редакція, яку .wf-drawer уже носить у nav-drawer.css з тієї самої причини. Тепер закритих шарів із досяжним вмістом у продукті нуль: перевірено по всіх оверлеях, шухлядах і діалогах.

Доказ, що нічого не зрушило: A/B проти HEAD, 108 994 елементи, 40 сторінок × два в'юпорти. 2 100 розбіжностей, усі 2 100 усередині #fsheet і усі - visibility. За межами шита - нуль. Кольору, геометрії й типу не торкнулись
Холостий контроль

Сторінка звіряє себе з файлом: 3 власних класів і 1 станів оголошено в checkbox.css. Нижче - ті, яких на цій сторінці немає ні в демо, ні в розмітці. Чужий контекст (його тут немає) не рахується: у цих селекторах компонент не є підметом, він лише стоїть усередині когось.

Рух

Робота: ВІДПОВІДЬ. Рухається: color, border-color, background. Тривалість --dur-fast (150ms), крива --ease-standard. Значення читаються через var() з tokens.css: підміна токена в браузері веде за собою кожне з них, тобто числа у файлі немає. При prefers-reduced-motion рух зникає, а стан лишається.

Токени

Прочитані з checkbox.css, не переписані руками. Колір іде лише через роль, геометрія - напряму з примітива.

Семантичні ролі (10)Примітиви (5)
--bg-action --bg-action-pressed --bg-action-soft --bg-page --line-action --line-onaction --line-strong --ring-focus-control --text-action --text-secondary--fs-14 --radius-4 --space-12 --space-2 --space-8
–--dur-fast --ease-standard
Розмітка
<label class="fopt"><span class="cb on"></span> Протеїн <span>(124)</span></label>
Де вживається

Якорі: .cb .optin

2 екранів у wireframes/: content-guarantee.html, search-suggest.html

Немає в коді -> етап 09

Чого бракує

  • Немає невизначеного стану для «частина групи обрана»
  • Немає :focus - клавіатурою по фільтрах не пройти

Не домальовано наперед і не замовчано: етап 09 стартує з цього списку, зведеного в design/kit/docs/architecture.md.