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

Поле

Текстове поле форми. Найбільший атом системи, бо саме тут живуть майже всі стани, які в продукті взагалі є. Нижче - усі шість форм поля, які файл уміє, справжніми класами: сторінка, яка малює поле інлайновими стилями, показує не систему, а свій малюнок.

рівень 1design/system/components/field.css286 рядків137 екранів
Анатомія
+380
Не отримали код? Надіслати ще (0:45)
Розмітка з wireframes/_nav.js, рядки з продукту, жодного інлайнового стилю. Клітинка коду країни - частина поля, а не окремий компонент: вона ділить з ним рамку, тому фокус підсвічує їх разом.
1Рамка - несе стан: спокій, ховер, фокус, заповнене, обов'язкове, помилка
2Підпис - чорнило, напівжирний; помітка «необов'язково» - приглушена, бо це виняток, а не правило
3Клітинка коду - .cc - незмінна частина значення, на приглушеній підкладці й моноширинним
4Значення - моноширинний для номера і коду, звичайний для тексту: цифри мають стояти в колонку
5Підказка - приглушений - те, чого людина ще не ввела
Набір: дві висоти й одна коробка

Крок 7. Обхід 39 кольорових екранів знайшов 101 поле, 14 імен, 14 виглядів - і 66 зі 101 набраних Arial. Поле не успадковує font-family, це треба сказати вголос, і сказали це три файли з чотирнадцяти. Пошук у шапці й підписка в підвалі - два найчастіші поля продукту - були не Inter, а їхнє чорнило - чистий #000, якого в палітрі немає. Висот було дев'ять: 37 · 40 · 44 · 45 · 46 · 47 · 60 · 61 · 64.

поле44 - мінімум пальця
field
field field--s · 40
моношириннезначення це число або код
field field--mono
field field--s field--mono · спробуйте набрати літеру
з приставкоюкоробка переїжджає на оправу
+380
field-grp > field
field-grp field-grp--s · 40
станирамка несе всі п'ять
has · заповнене
.err · текст лишається читним
багаторядковене третій розмір, а мінімум висоти
textarea · 80 = два щаблі по 40
select · коробка й каретка наші, список - системний
вимкненестан, не варіант
[disabled] · до кроку 7 такого стану не було ніде
[disabled] · те саме на textarea
Select - це поле, а не «дропдаун». Його значення йде разом із формою, тому він носить ту саму коробку. Каретку з кроку 7.1 малюємо ми: браузерна відрізнялася на кожній платформі й стояла поза драбиною позначки, а на екрані тренера вона висіла за 150px від нашої каретки на виборі міста - два контроли «оберіть щось» із двома різними знаками. Розкритий список лишається системним навмисно: його не стилізує жоден CSS, і саме він працює колесом на телефоні та відповідає читачеві екрана. Ми володіємо закритим станом, платформа - відкритим. Меню, яке відкриває кнопка - сортування, мова, кабінет, місто - на цій сторінці не показане і не має бути: воно нічого не віддає формі, а міняє вигляд або веде. Це меню вибору, окремий атом - і з кроку 7.2 пікер клієнта побудований саме ним, із самого цього select, який лишився значенням.

Драбина 40 / 44 і третього щабля немає, бо в продукті його ніщо не займає. 44 - не нове число: це мінімум пальця й та сама коробка, яку носить іконкова кнопка, тому поле і контрол поруч із ним збігаються. Після кроку 7 усі 101 поле міряють 40 · 44 · 60 (OTP, свій атом) · 80 (textarea), шрифтів два - Inter і моноширинний там, де значення це число, - а чорнило одне.
Варіанти і розміри

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

ВісьЗначенняПравило вибору
Стан рамкиспокій · ховер · фокус · заповнене · обов'язкове · помилка · вимкненеСім станів, кожен зі своєю роллю лінії. Помилка й вимкнене прийшли на кроці 7: до нього перше жило в двох чужих файлах, а другого не було ніде.
Типодин рядок · телефон з кодом · багаторядковий · число · OTPТип диктує не вигляд, а клавіатуру й перевірку: телефон отримує inputmode=tel, число - моноширинний і центрування, бо його порівнюють із сусіднім.
Що можна ввестибудь-що · тільки цифриВирішує значення, а не вигляд: якщо значення поля - це число (код зі SMS, ціна, телефон), поле приймає лише цифри. Поле «Відділення / поштомат - номер або назва» цифру приймає, але його значення числом не є, і обмежувати його не можна.
Форма номераз коміркою +380 · повний номер у поліОдне правило, дві оправи: якщо код країни стоїть окремою клітинкою, поле тримає дев'ять цифр і показує «67 123 45 67»; якщо поле тримає весь номер, воно саме друкує «+380». Людина може набрати «67…», «067…», «380 67…» або вставити номер із дужками й тире - усі чотири записи означають один номер, і поле зводить їх до одного.
Оболонка.fld у діалозі · .cef у формі клієнта · .frow у фільтрі · .pfield у профіліОдне поле, чотири оправи. Різниця лише в тому, що поруч: підпис зверху, підпис збоку, два поля в рядок або значення тільки для читання з кнопкою «змінити».
Ціна, ₴ - два числових поля в рядок
Профіль: значення для читання, зміна - окремою дією
Ім'я
Телефон
+380 ** *** 45 67
Тільки цифри, і номер групується сам. Спробуйте набрати літеру, набрати «380 67…» або вставити «тел. +38 (067) 123-45-67» - у поле потрапить те саме.
Обов'язкове - єдиний стан-попередження, який поле має
Обов'язкове - попередження, а не помилка: воно каже «сюди ще треба», а не «тут не так». Помилку поле має з кроку 7 - див. набір вище.
Що зведено на кроці 5.5

Рішення живе там, де на нього дивляться. Перепис показав дрейф, крок 5.5 його зняв - і цей блок каже, що саме обрали, що лишили і чому, на сторінці самого компонента, а не тільки в журналі.

ВісьБулоСталоЧому
Радіус7 різних4Ті самі 4/8/12/16: 9, 10 і 11 сіли на 8 і 12.
Кегль на мобільному136 зі 166 під 16px0 з 6616px - нижня межа, під якою iOS зумить сторінку на фокусі.
Фокустри редакції ореолу--ring-focusКрок 6.0: один ореол на всі поля, і він не на драбині висот.
Кегль числового поля13.3px браузерний--fs-14, на мобільному 16Поле без кегля бере дефолт браузера - значення, якого ніхто не вирішував.

ПОВЕДІНКА НА ШИРИНІ - етап 10, крок 4

Від якого порогу: жодного. Що саме міняється: поле займає всю ширину свого контейнера і їде разом із ним. Плинне з самого початку, крок 4 нічого тут не змінив.
Коли вживати

Скрізь, де людина вводить текст: чекаут, профіль, адреси, вхід, фільтр ціни. Рішення 5 - вхід без пароля, тому поля пароля в системі немає і не буде.

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

Правило

Стан живе на рамці, а обмеження - на значенні. Якщо значення поля це число, поле приймає лише цифри - і не підказкою клавіатури, а насправді: inputmode нічого не забороняє, type=tel взагалі текстовий, а type=number пропускає e, +, - і крапку. Правило виконує design/system/fields.js на двох подіях, якими символ узагалі потрапляє в поле: натиск і вставка.

І номер групується сам. Раніше це вміло одне поле з чотирьох, бо код був прив'язаний до id="wfa-phone" у скрипті екрана. Поведінка була правильна, адреса - ні: правило, прив'язане до одного id, це не правило компонента, а приватна звичка того екрана.

Антиправило

Не обмежуй поле за тим, як воно виглядає. «Відділення / поштомат - номер або назва» повне цифр, але його значення не число, і фільтр там відрізав би половину адрес. Обмежує значення, а не сусідство з цифрою.

І не показуй помилку самим лише червоним. Червона рамка без тексту не каже, що саме не так, і не існує для читача екрана. Текст помилки стоїть під полем, а не в тості: тост зникає, а виправляти треба тут.
Стани, що є в коді

Спокій · ховер (--line-control-hover) · фокус (--line-action плюс --ring-focus, ніколи не браузерний обвід) · заповнене .has (--line-control-filled) · обов'язкове .req (--line-warning) · помилка .err (--line-danger плюс --ring-danger) · вимкнене [disabled]. ::placeholder має власну роль --text-muted.

Крок 7 додав два останні. До нього ховер мали 6 полів зі 101, колір підказки - 9, власний фокус - 72 (решті 29 малював браузер), а вимкненого не було в жодного. Помилка існувала двічі й обидва рази в чужому файлі - .otp .box.err і .pm-b input.err: два компоненти вигадали її окремо, а поле, яким набирають телефон і адресу, показати її не вміло. Тепер це один стан у field.css. Крок 6 забрав друге ім'я: селектор ніс .field--err поряд з .field.err, і замір показав, що охайне ім'я не носить ніхто, а те, що пише рантайм, носять. Стан не змінився ні на оголошення - зникло лише друге ім'я для нього.

Холостий контроль

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

Рух

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

Токени

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

Семантичні ролі (16)Примітиви (15)
--bg-page --bg-sunken --bg-surface --field-caret --line-action --line-control-filled --line-control-hover --line-danger --line-hair --line-strong --mark-disabled --ring-danger --ring-focus --text-body --text-muted --text-secondary--font-mono --fs-12 --fs-14 --fs-16 --fw-bold --fw-semibold --lh-snug --radius-12 --size-20 --size-40 --size-44 --space-12 --space-16 --space-2 --space-8
–--dur-fast --ease-standard
Розмітка
<div class="fld"><label for="ph">Номер телефону</label> <div class="ph-field"><span class="cc">+380</span> <input id="ph" type="tel" inputmode="tel" placeholder="67 123 45 67"></div></div> <div class="fld"><label>E-mail <span class="opt">- необов'язково</span></label> <input class="txt-field" type="email" placeholder="you@email.com"></div>
Де вживається

Якорі: .fld .cef .txt-field .pfield .ph-field .frow .resend

7 екранів у wireframes/: account-profile-delete.html, account-profile-email.html, account-profile-lang.html, account-profile-phone.html, account-profile-withemail.html, account-profile.html, coach-verify.html

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

Чого бракує

  • Форматування телефону дублюється - wfAuthDigits() у сірому прототипі досі форматує своє поле по id. Система тепер робить це раніше й для всіх, тому результат однаковий, але два коди на одну роботу лишаються, поки сірий шар заморожений
  • Вставка коду не розкладається по комірках - вставили «Ваш код 4821» у першу комірку, лишилась одна цифра. Це робота OTP-комірки, а не поля: поле своє вже зробило, відкинувши все, крім цифр
  • У поля немає стану помилки - field.css не має жодного правила для невалідного значення. Червона рамка в продукті є двічі, і обидва рази її завів собі чужий компонент: otp.css і review-modal.css
  • Жодне поле не позначене - required 0, aria-invalid 0, disabled 0 на 196 полях. Рамка змінює колір, а читач екрана про це не дізнається
  • Немає :disabled - стану, якого немає в продукті, немає і в коді; щойно з'явиться перше вимкнене поле, його треба буде вирішити, а не домалювати
  • Підпис і поле пов'язані не скрізь - у чотирьох оправах з шести є for/id, у .frow і .pfield зв'язок тримається на aria-label
  • Немає лічильника символів для багаторядкового поля, хоча відгук має ліміт

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