Выдержка из книги Form Design Patterns: Регистрационная форма

Опубликовано: 2022-03-10
Краткое резюме ↬ Мы рады представить новую книгу Адама Сильвера «Шаблоны проектирования форм». В этом отрывке из книги Адам рассматривает основополагающие качества хорошо продуманной формы и то, как о них думать. Вы также можете получить книгу прямо сейчас →

Начнем с регистрационной формы. Большинство компаний хотят долгосрочных отношений со своими пользователями. Для этого им нужно, чтобы пользователи зарегистрировались. А для этого они должны взамен дать пользователям ценность. Никто не хочет подписываться на вашу услугу — они просто хотят получить доступ к тому, что вы предлагаете, или обещание более быстрого опыта при следующем посещении.

Несмотря на базовый внешний вид регистрационной формы, необходимо учитывать множество моментов: примитивные элементы, из которых состоит форма (метки, кнопки и поля ввода), способы уменьшения усилий (даже на таких небольших формах), как форма. Проверка.

Выбирая такую ​​простую форму, мы можем выделить основные качества хорошо продуманных форм.

## Как это может выглядеть

Форма состоит из четырех полей и кнопки отправки. Каждое поле состоит из элемента управления (ввод) и связанной с ним метки.

Регистрационная форма с четырьмя полями: имя, фамилия, адрес электронной почты и пароль.
Регистрационная форма с четырьмя полями: имя, фамилия, адрес электронной почты и пароль.

Вот HTML:

 <form> <label for="firstName">First name</label> <input type="text" name="firstName"> <label for="lastName">Last name</label> <input type="text" name="lastName"> <label for="email">Email address</label> <input type="email" name="email"> <label for="password">Create password</label> <input type="password" name="password" placeholder="Must be at least 8 characters"> <input type="submit" value="Register"> </form>

Этикетки — это то, с чего начинается наше обсуждение.

## Ярлыки

В книге « Доступность для всех » Лаура Калбаг выделяет четыре основных параметра, которые улучшают взаимодействие с пользователем для всех:

  • Визуальный : сделайте так, чтобы его было легко увидеть.
  • Слух : сделать так, чтобы было легко слышать.
  • Мотор : с ним легко взаимодействовать.
  • Познавательный : сделать его легким для понимания.

Рассматривая ярлыки с каждой из этих точек зрения, мы можем увидеть, насколько важны ярлыки. Зрячие пользователи могут читать их, пользователи с нарушениями зрения могут слышать их с помощью программы чтения с экрана, а пользователям с двигательными нарушениями легче сфокусироваться на поле благодаря большей зоне поражения. Это связано с тем, что щелчок по метке устанавливает фокус на соответствующий элемент формы.

Метка увеличивает зону поражения поля.
Метка увеличивает зону поражения поля.

По этим причинам каждый элемент управления, принимающий ввод, должен иметь вспомогательную <label> . Кнопки отправки не принимают ввод, поэтому им не нужна вспомогательная метка — атрибут value , отображающий текст внутри кнопки, действует как доступная метка.

Чтобы связать вход с меткой, id входа и атрибут метки for должны совпадать и быть уникальными для страницы. В случае с полем электронной почты значение равно «email»:

 html < label for = "email" > Email address </ label > < input id = "email" >

Отсутствие ярлыка означает игнорирование потребностей многих пользователей, в том числе с физическими и когнитивными нарушениями. Сосредоточив внимание на признанных барьерах для людей с ограниченными возможностями, мы можем сделать наши формы проще и надежнее для всех.

Например, большая площадь поражения имеет решающее значение для пользователей с двигательными нарушениями, но и тем, у кого нет нарушений, ее легче поразить.

## Заполнители

Атрибут placeholder предназначен для хранения подсказки. Это дает пользователям дополнительные рекомендации при заполнении поля — особенно полезно для полей со сложными правилами, таких как поле пароля.

Поскольку текст-заполнитель не является реальным значением, он отображается серым цветом, чтобы его можно было отличить от введенных пользователем значений.

Низкоконтрастный серый текст заполнителя плохо читается.
Низкоконтрастный серый текст заполнителя плохо читается.

В отличие от меток, подсказки необязательны и не должны использоваться как само собой разумеющееся. Тот факт, что атрибут placeholder существует, не означает, что мы должны его использовать. Вам не нужен заполнитель «Введите ваше имя», когда метка «Имя» — это ненужное дублирование.

Текст метки и заполнителя имеет одинаковое содержание, поэтому заполнитель не нужен.
Текст метки и заполнителя имеет одинаковое содержание, поэтому заполнитель не нужен.

Заполнители привлекательны своей минималистичной эстетикой, позволяющей экономить место. Это связано с тем, что текст-заполнитель помещается внутри поля. Но это проблематичный способ дать пользователям подсказку.

Во-первых, они исчезают, когда пользователь печатает. Исчезающий текст трудно запомнить, что может привести к ошибкам, если, например, пользователь забудет выполнить одно из правил пароля. Пользователи часто ошибочно принимают текст-заполнитель за значение, в результате чего поле пропускается, что снова может привести к ошибкам позже. Серому тексту на белом не хватает контраста, что затрудняет его чтение. В довершение всего, некоторые браузеры не поддерживают заполнители, некоторые программы чтения с экрана их не объявляют, а длинный текст подсказки может быть обрезан.

Текст-заполнитель обрезается, так как он шире текстового поля.
Текст-заполнитель обрезается, так как он шире текстового поля.

Это много проблем для того, что по сути является просто текстом. Весь контент, особенно подсказка формы, не должен считаться приятным. Поэтому вместо использования заполнителей лучше расположить текст подсказки над элементом управления следующим образом:

Текст подсказки размещается над текстовым полем вместо текста-заполнителя внутри него.
Текст подсказки размещается над текстовым полем вместо текста-заполнителя внутри него.
 <div class="field"> <label for="password"> <span class="field-label">Password</span> <span class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</span> </label> <input type="password" name="password"> </div>

Подсказка помещается внутри метки и внутри <span> , поэтому ее можно оформить по-разному. Поместив его внутри метки, он будет прочитан программами чтения с экрана и еще больше увеличит область попадания.

Как и в большинстве вещей в дизайне, это не единственный способ достичь этой функциональности. Мы могли бы использовать атрибуты ARIA, чтобы связать подсказку с вводом:

 <div class="field"> <label for="password">Password</label> <p class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</p> <input type="password" name="password"> </div>

Атрибут aria-describedby используется для подключения подсказки по ее id — так же, как атрибут for для меток, но наоборот. Он добавляется к метке элемента управления и считывается после короткой паузы. В этом примере «пароль [пауза] должен содержать восемь плюс символов, по крайней мере, одну цифру и одну заглавную букву».

Есть и другие отличия. Во-первых, щелчок по подсказке (в данном случае <p> ) не сфокусирует элемент управления, что уменьшит область попадания. Во-вторых, несмотря на растущую поддержку ARIA, она никогда не будет так хорошо поддерживаться, как нативные элементы. В данном конкретном случае Internet Explorer 11 не поддерживает aria-describedby . Вот почему первое правило ARIA — не использовать ARIA:

«Если вы можете использовать собственный HTML-элемент или атрибут с уже встроенной семантикой и поведением, которые вам нужны, вместо того, чтобы переназначать элемент и добавлять роль, состояние или свойство ARIA, чтобы сделать его доступным, сделайте это ».

Плавающие этикетки

Шаблон плавающей метки Мэтта Смита — это метод, в котором метка используется в качестве заполнителя. Метка начинается внутри элемента управления, но всплывает над элементом управления по мере того, как пользователь вводит текст, отсюда и название. Эту технику часто хвалят за ее причудливые, минималистичные и компактные качества.

Шаблон плавающей этикетки. Слева в несфокусированном текстовом поле показана метка внутри; справа, когда текстовое поле получает фокус, метка перемещается над полем.
Шаблон плавающей этикетки. Слева в несфокусированном текстовом поле показана метка внутри; справа, когда текстовое поле получает фокус, метка перемещается над полем.

К сожалению, у этого подхода есть несколько проблем. Во-первых, для подсказки нет места, потому что метка и подсказка — одно и то же. Во-вторых, их трудно читать из-за плохого контраста и мелкого текста, как это обычно делается. (Необходима меньшая контрастность, чтобы пользователи могли отличить реальное значение от заполнителя.) В-третьих, как и заполнители, они могут быть ошибочно приняты за значение и могут быть обрезаны.

И плавающие метки на самом деле не экономят место. Этикетке в первую очередь нужно место для перемещения. Даже если они экономили место, вряд ли это веская причина, чтобы уменьшить удобство использования форм.

«Похоже, что это требует больших усилий, когда вы можете просто пометить входные данные и получить все преимущества / ни одной проблемы».
- Люк Вроблевски на этикетках с плавающей запятой.

Причудливые и минималистичные интерфейсы не заставляют пользователей чувствовать себя великолепно — это делают очевидные, инклюзивные и надежные интерфейсы. Искусственное уменьшение высоты таких форм и неубедительно, и проблематично.

Вместо этого вы должны в первую очередь выделить место для вездесущей, легко доступной метки (и подсказки, если необходимо) в начале процесса проектирования. Таким образом, вам не придется втискивать контент в маленькое пространство.

Вскоре мы обсудим несколько менее искусственных методов уменьшения размера форм.

## Протокол вопросов

Одним из мощных и естественных способов уменьшить размер формы является использование протокола вопросов. Это помогает убедиться, что вы знаете, почему вы задаете каждый вопрос или включаете поле формы.

Нужно ли регистрационной форме собирать имя, фамилию, адрес электронной почты и пароль? Существуют ли лучшие или альтернативные способы запроса этой информации, которые упрощают работу?

По всей вероятности, вам не нужно спрашивать имя и фамилию пользователя для регистрации. Если вам по какой-либо причине понадобится эта информация позже, попросите ее сейчас. Удалив эти поля, мы можем вдвое уменьшить размер формы. Все, не прибегая к новым и проблематичным шаблонам.

### Вход без пароля

Один из способов не запрашивать у пользователей пароль — использовать шаблон входа без пароля . Он работает, используя безопасность электронной почты (которая уже требует пароля). Пользователи вводят только свой адрес электронной почты, а сервис отправляет специальную ссылку на их почтовый ящик. После этого пользователь сразу же входит в сервис.

Экран входа без пароля на Medium.
Экран входа без пароля на Medium.

Это не только уменьшает размер формы до одного поля, но и избавляет пользователей от необходимости запоминать еще один пароль. Хотя это упрощает форму отдельно, в других отношениях это добавляет дополнительную сложность для пользователя.

Во-первых, пользователи могут быть менее знакомы с этим подходом, и многие люди беспокоятся о безопасности в Интернете. Во-вторых, переход от приложения к вашей учетной записи электронной почты занимает много времени, особенно для пользователей, которые знают свой пароль или используют менеджер паролей.

Дело не в том, что одна техника всегда лучше другой. Дело в том, что протокол вопросов побуждает нас думать об этом как о части процесса проектирования. В противном случае вы бы бездумно добавили в форму поле пароля и покончили бы с этим.

### Парольные фразы

Пароли, как правило, короткие, их трудно запомнить и легко взломать. Пользователям часто приходится создавать пароль длиной более восьми символов, состоящий как минимум из одной прописной и одной строчной буквы, а также цифры. Такое микровзаимодействие едва ли можно назвать идеальным.

«Извините, но ваш пароль должен содержать заглавную букву, цифру, хайку, знак банды, иероглиф и кровь девственницы».
— Анонимный интернет-мем

Вместо пароля мы могли бы запросить у пользователей парольную фразу. Парольная фраза — это набор слов, таких как «monkeysinmygarden» (извините, это первое, что приходит на ум). Как правило, их легче запомнить, чем пароли, и они более безопасны из-за своей длины — парольная фраза должна быть не менее 16 символов.

Недостатком является то, что парольные фразы используются реже и, следовательно, незнакомы. Это может вызвать беспокойство у пользователей, которые уже беспокоятся о безопасности в Интернете.

Будь то шаблон входа без пароля или парольные фразы, мы должны отойти от условностей только после того, как проведем тщательное и разнообразное исследование пользователей. Вы не хотите менять один набор проблем на другой по незнанию.

## Стиль поля

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

### Позиция метки

Тесты отслеживания взгляда Маттео Пенцо показали, что размещение метки выше (а не рядом) с элементом управления формой работает лучше всего.

«Размещение метки прямо над полем ввода позволило пользователям захватывать оба элемента одним движением глаза».

Но есть и другие причины поставить метку над полем. На маленьких видовых экранах нет места рядом с управлением. А на больших экранах увеличение масштаба увеличивает вероятность того, что текст исчезнет с экрана.

Кроме того, некоторые метки содержат много текста, из-за чего он переносится на несколько строк, что может нарушить визуальный ритм, если разместить его рядом с элементом управления.

Хотя вы должны стремиться к тому, чтобы метки были краткими, это не всегда возможно. Использование шаблона, вмещающего различное содержимое — путем размещения меток над элементом управления — является хорошей стратегией.

### Внешний вид, размер и пространство

Поля формы должны выглядеть как поля формы. Но что это означает?

Это означает, что текстовое поле должно выглядеть как текстовое поле. Пустые коробки означают «заполните меня» в силу того, что они пусты, как книжка-раскраска. Это одна из причин, по которой заполнители бесполезны. Они устраняют предполагаемую доступность, которую в противном случае обеспечивало бы пустое текстовое поле.

Это также означает, что пустое пространство должно быть заключено в рамку (окаймлено). Удаление границы или наличие только нижней границы, например, удаляет воспринимаемые аффордансы. Нижняя граница может сначала показаться разделителем. Даже если вы знаете, что вам нужно что-то заполнить, выходит ли значение выше или ниже строки?

Пространственно метка должна быть ближе всего к элементу управления формой, а не к элементу управления предыдущего поля. Вещи, которые кажутся близко друг к другу, предполагают, что они принадлежат друг другу. Равное расстояние могло бы улучшить эстетику, но это было бы за счет удобства использования.

Наконец, метка и само текстовое поле должны быть достаточно большими, чтобы их можно было читать и нажимать. Это, вероятно, означает размер шрифта не менее 16 пикселей, а в идеале общий размер тапа не менее 44 пикселей.

### Стили фокуса

Стили фокуса — более простая перспектива. По умолчанию браузеры выделяют контур вокруг элемента, находящегося в фокусе, чтобы пользователи, особенно те, кто использует клавиатуру, знали, где они находятся. Проблема со стилем по умолчанию заключается в том, что он часто бледный, плохо различимый и несколько уродливый.

Хотя это так, не поддавайтесь искушению удалить его, потому что это значительно ухудшит взаимодействие с пользователем для тех, кто перемещается по экрану с помощью клавиатуры. Мы можем переопределить стиль по умолчанию, чтобы сделать его более четким и эстетичным.

 input:focus { outline: 4px solid #ffbf47; }
## Поле электронной почты

Несмотря на его простой внешний вид, в конструкции поля есть несколько важных деталей, которые влияют на опыт.

Поле электронной почты.
Поле электронной почты.

Как отмечалось ранее, некоторые поля имеют подсказку в дополнение к метке, поэтому метка находится внутри дочернего диапазона. Класс field-label позволяет нам стилизовать его с помощью CSS.

 <div class="field"> <label for="email"> <span class="field-label">Email address</span> </label> <input type="email" name="email"> </div>

Сама метка — «Адрес электронной почты», и в ней используется регистр предложений. В статье «Обоснование буквенного регистра» Джон Сайто объясняет, что регистр предложений (в отличие от регистра заглавий), как правило, легче читать, он дружелюбнее и облегчает поиск существительных. Прислушаетесь ли вы к этому совету, зависит от вас, но какой бы стиль вы ни выбрали, обязательно используйте его последовательно.

Атрибут type ввода имеет значение email , что запускает экранную клавиатуру для электронной почты на мобильных устройствах. Это дает пользователям легкий доступ к @ и . (точка) символы, которые должен содержать каждый адрес электронной почты.

Экранная клавиатура Android для поля электронной почты.
Экранная клавиатура Android для поля электронной почты.

Люди, использующие неподдерживающий браузер, увидят стандартный текстовый ввод ( <input type="text"> ). Это форма прогрессивного улучшения, которая является краеугольным камнем разработки инклюзивного опыта.

### Прогрессивное улучшение

Прогрессивное улучшение касается пользователей. Это просто делает нашу жизнь как дизайнеров и разработчиков проще. Вместо того, чтобы идти в ногу с набором браузеров и устройств (что невозможно!), мы можем просто сосредоточиться на функциях.

Прежде всего, прогрессивное улучшение заключается в том, чтобы всегда предоставлять пользователям разумный опыт, независимо от их браузера, устройства или качества соединения. Когда что-то пойдет не так — а так оно и будет, — пользователи не пострадают, потому что они все равно смогут добиться своей цели.

Есть много способов, которыми опыт может пойти не так. Возможно, таблица стилей или скрипт не загружаются. Может быть, все загружается, но браузер пользователя не распознает HTML, CSS или JavaScript. Что бы ни случилось, использование прогрессивного улучшения при разработке интерфейса избавляет пользователей от особенно плохих времен.

Он начинается с HTML для структуры и содержания. Если CSS или JavaScript не загружаются, ничего страшного, потому что контент есть.

Если все загружается нормально, возможно, различные элементы HTML не распознаются. Например, некоторые браузеры не понимают <input type="email"> . Это нормально, потому что вместо этого пользователи получат текстовое поле ( <input type="text"> ). Пользователи по-прежнему могут вводить адрес электронной почты; у них просто нет клавиатуры для электронной почты на мобильном телефоне.

Может быть, браузер не понимает какой-нибудь причудливый CSS и просто проигнорирует его. В большинстве случаев это не проблема. Допустим, у вас есть кнопка с border-radius: 10px . Браузеры, которые не распознают это правило, будут отображать кнопку со скошенными углами. Возможно, воспринимаемая доступность кнопки снижается, но пользователи остаются невредимыми. В других случаях может быть полезно использовать функциональные запросы.

Затем есть JavaScript, который сложнее. Когда браузер попытается разобрать методы, которые он не распознает, он начнет шипеть. Это может привести к сбою других (действительных и поддерживаемых) сценариев. Если ваш скрипт сначала не проверяет существование методов (обнаружение функций) и их работу (тестирование функций) перед их использованием, пользователи могут получить неработающий интерфейс. Например, если обработчик нажатия кнопки вызывает метод, который не распознан, кнопка не будет работать. Это плохо.

Вот как вы улучшаете. Но что лучше, вообще не нуждается в улучшении. HTML с небольшим количеством CSS может дать пользователям отличный опыт. Важен контент, и для этого вам не нужен JavaScript. Чем больше вы можете полагаться на контент (HTML) и стиль (CSS), тем лучше. Я не могу не подчеркнуть этого: очень часто базовый опыт оказывается лучшим и наиболее эффективным. Нет смысла улучшать что-то, если это не добавляет ценности (см. принцип инклюзивного дизайна 7).

Конечно, бывают случаи, когда базовый опыт не так хорош, как мог бы быть, — тогда пришло время улучшить его. Но если мы будем следовать описанному выше подходу, когда фрагмент CSS или JavaScript не распознается или не выполняется, все будет работать.

Прогрессивное улучшение заставляет нас задуматься о том, что происходит, когда что-то терпит неудачу. Это позволяет нам создавать опыт с заложенной устойчивостью. Но в равной степени это заставляет нас задуматься о том, нужно ли улучшение вообще; и если да, то как лучше поступить.

## Поле пароля

Мы используем ту же разметку, что и для поля электронной почты, о котором говорилось ранее. Если вы используете язык шаблонов, вы сможете создать компонент, который поддерживает оба типа полей. Это помогает обеспечить соблюдение третьего принципа инклюзивного дизайна — быть последовательным .

Поле пароля с использованием текстового шаблона подсказки.
Поле пароля с использованием текстового шаблона подсказки.
 <div class="field"> <label for="password"> <span class="field-label">Choose password</span> <span class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</span> </label> <input type="password" name="password"> </div>

Поле пароля содержит подсказку. Без него пользователи не поймут требования, что может привести к ошибке при попытке продолжить.

Атрибут type="password" маскирует вводимые значения, заменяя то, что вводит пользователь, маленькими черными точками. Это мера безопасности, которая не позволяет людям видеть то, что вы набрали, если они находятся рядом.

### Раскрытие пароля

Скрытие значения при вводе пользователем затрудняет исправление опечаток. Поэтому, когда одна сделана, часто бывает проще удалить всю запись и начать заново. Это расстраивает, так как большинство пользователей не используют компьютер, когда человек заглядывает им через плечо.

Из-за повышенного риска опечаток некоторые регистрационные формы содержат дополнительное поле «Подтвердите пароль». Это мера предосторожности, которая требует от пользователя ввести один и тот же пароль дважды, что удваивает усилия и ухудшает работу пользователя. Вместо этого лучше позволить пользователям раскрывать свой пароль, что соответствует принципам 4 и 5, давать контроль и предлагать выбор соответственно. Таким образом, пользователи могут раскрыть свой пароль, когда они знают, что никто не смотрит, что снижает риск опечаток.

Последние версии Internet Explorer и Microsoft Edge обеспечивают такое поведение изначально. Поскольку мы будем создавать собственное решение, мы должны подавить эту функцию с помощью CSS следующим образом:

 input[type=password]::-ms-reveal { display: none; }
Поле пароля с кнопкой «Показать пароль» рядом с ним.
Поле пароля с кнопкой «Показать пароль» рядом с ним.

Во-первых, нам нужно внедрить кнопку рядом с полем ввода. Элемент <button> должен быть вашим основным элементом для изменения чего-либо с помощью JavaScript, за исключением изменения местоположения, для чего и нужны ссылки. При нажатии он должен переключать атрибут type между password и text ; и метка кнопки между «Показать» и «Скрыть».

 function PasswordReveal(input) { // store input as a property of the instance // so that it can be referenced in methods // on the prototype this.input = input; this.createButton(); }; PasswordReveal.prototype.createButton = function() { // create a button this.button = $('<button type="button">Show password</button>'); // inject button $(this.input).parent().append(this.button); // listen to the button's click event this.button.on('click', $.proxy(this, 'onButtonClick')); }; PasswordReveal.prototype.onButtonClick = function(e) { // Toggle input type and button text if(this.input.type === 'password') { this.input.type = 'text'; this.button.text('Hide password'); } else { this.input.type = 'password'; this.button.text('Show password'); } };
#### Синтаксис JavaScript и примечания по архитектуре

Поскольку существует множество разновидностей JavaScript и различные способы проектирования компонентов, мы рассмотрим варианты, используемые для создания компонента раскрытия пароля, и все будущие компоненты в книге.

Во-первых, мы используем конструктор. Конструктор — это функция, обычно написанная в верхнем верблюжьем регистре — PasswordReveal , а не passwordReveal . Он инициализируется ключевым словом new , что позволяет использовать один и тот же код для создания нескольких экземпляров компонента:

 var passwordReveal1 = new PasswordReveal(document.getElementById('input1')); var passwordReveal2 = new PasswordReveal(document.getElementById('input2'));

Во-вторых, методы компонента определены в прототипе — например, PasswordReveal.prototype.onButtonClick . Прототип — это наиболее эффективный способ совместного использования методов несколькими экземплярами одного и того же компонента.

В-третьих, jQuery используется для создания и извлечения элементов, а также для прослушивания событий. Хотя jQuery может быть необязательным или предпочтительным, его использование означает, что эта книга может сосредоточиться на формах, а не на сложностях кросс-браузерных компонентов.

Если вы дизайнер, который немного кодирует, то повсеместное распространение jQuery и низкий порог входа должны быть вам полезны. Точно так же, если вы предпочитаете не использовать jQuery, у вас не возникнет проблем с рефакторингом компонентов в соответствии с вашими предпочтениями.

Возможно, вы также заметили использование функции $.proxy . Это реализация jQuery Function.prototype.bind . Если бы мы не использовали эту функцию для прослушивания событий, то обработчик событий вызывался бы в контексте элемента ( this ). В приведенном выше примере this.button будет неопределенным. Но вместо этого мы хотим, чтобы this был объект раскрытия пароля, чтобы мы могли получить доступ к его свойствам и методам.

#### Альтернативные варианты интерфейса

Интерфейс отображения пароля, который мы создали выше, переключает метку кнопки между «Показать пароль» и «Скрыть пароль». Некоторые пользователи программ чтения с экрана могут запутаться, когда изменится метка кнопки; как только пользователь сталкивается с кнопкой, он ожидает, что эта кнопка сохранится. Несмотря на то, что кнопка является постоянной, изменение метки делает ее недействительной.

Если ваши исследования показывают, что это проблема, вы можете попробовать два альтернативных подхода.

Во-первых, используйте флажок с постоянной меткой «Показать пароль». О состоянии будет сигнализировать checked атрибут. Пользователи программ чтения с экрана услышат «Показать пароль, флажок, флажок» (или подобное). Зрячие пользователи увидят галочку. Проблема с этим подходом заключается в том, что флажки предназначены для ввода данных, а не для управления интерфейсом. Некоторые пользователи могут подумать, что их пароль будет раскрыт системе.

Или, во-вторых, изменить state кнопки, а не метку. Чтобы передать состояние пользователям программ чтения с экрана, вы можете переключать атрибут aria-pressed между true (нажато) и false (не нажато).

 <button type="button" aria-pressed="true"> Show password </button>

При фокусировке на кнопке средства чтения с экрана объявят: «Показать пароль, кнопка-переключатель, нажата» (или подобное). Для зрячих пользователей вы можете оформить кнопку таким образом, чтобы она выглядела нажатой или ненажатой, используя селектор атрибутов следующим образом:

 [aria-pressed="true"] { box-shadow: inset 0 0 0 0.15rem #000, inset 0.25em 0.25em 0 #fff; }

Просто убедитесь, что непрессованный и прессованный стили очевидны и различаются, иначе зрячие пользователи могут с трудом отличить их друг от друга.

### Микрокопия

Метка установлена ​​​​на «Выберите пароль», а не на «Пароль». Последнее несколько сбивает с толку и может побудить пользователя ввести пароль, который у него уже есть, что может быть проблемой безопасности. Более тонко, это может означать, что пользователь уже зарегистрирован, заставляя пользователей с когнитивными нарушениями думать, что они входят в систему.

Там, где «Пароль» неоднозначен, «Выберите пароль» обеспечивает ясность.

## Стили кнопок

Что такое кнопка? Мы называем многие различные типы компонентов на веб-странице кнопкой. На самом деле, я уже рассмотрел два разных типа кнопок, не называя их. Давайте сделаем это сейчас.

Кнопки, которые отправляют формы, являются «кнопками отправки», и они обычно кодируются как <input type="submit"> или <button type="submit"> . Элемент <button> более податлив, поскольку внутрь него можно вкладывать другие элементы. Но редко в этом есть необходимость. Большинство кнопок отправки содержат только текст.

Примечание. В старых версиях Internet Explorer при наличии нескольких <button type="submit"> форма отправляет на сервер значения всех кнопок, независимо от того, какая из них была нажата. Вам нужно знать, какая кнопка была нажата, чтобы вы могли определить правильный порядок действий, поэтому этого элемента следует избегать.

Другие кнопки внедряются в интерфейс для улучшения работы с JavaScript — так же, как мы сделали с компонентом раскрытия пароля, который обсуждался ранее. Это тоже была <button> но ее type был установлен как button (не submit ).

В обоих случаях первое, что нужно знать о кнопках, это то, что они не являются ссылками. Ссылки обычно подчеркнуты (в соответствии со стилями пользовательского агента) или специально расположены (на панели навигации), чтобы их можно было отличить от обычного текста. При наведении курсора на ссылку курсор изменится на указатель. Это связано с тем, что, в отличие от кнопок, ссылки имеют слабую воспринимаемую доступность.

В книге « Устойчивый веб-дизайн » Джереми Кейт обсуждает идею материальной честности. Он говорит: «Один материал не должен использоваться вместо другого. В противном случае конечный результат обманчив». Делать ссылку похожей на кнопку — нечестно с материальной точки зрения. Он сообщает пользователям, что ссылки и кнопки одинаковы, когда это не так.

Ссылки могут делать то, чего не могут кнопки. Например, ссылки можно открыть в новой вкладке или добавить в закладки на потом. Поэтому кнопки не должны выглядеть как ссылки и не должны иметь курсор-указатель. Вместо этого мы должны сделать кнопки похожими на кнопки, которые, естественно, обладают сильной воспринимаемой доступностью. Имеют ли они закругленные углы, тени и границы, решать вам, но в любом случае они должны выглядеть как кнопки.

Кнопки по-прежнему могут давать обратную связь при наведении (и при фокусе), например, изменяя цвет фона.

### Размещение

Кнопки отправки обычно располагаются внизу формы: в большинстве форм пользователи заполняют поля сверху вниз, а затем отправляют. Но должна ли кнопка быть выровнена слева, справа или по центру? Чтобы ответить на этот вопрос, нам нужно подумать о том, где пользователи будут его искать.

Метки полей и элементы управления формы выравниваются по левому краю (в языках с чтением слева направо) и располагаются сверху вниз. Пользователи будут искать следующее поле под последним. Естественно, тогда кнопка отправки также должна располагаться в том же месте: слева и непосредственно под последним полем. Это также помогает пользователям, которые увеличивают масштаб, поскольку кнопка, выровненная по правому краю, может легче исчезнуть за пределами экрана.

### Текст

Текст кнопки так же важен, как и ее стиль. Текст должен явно описывать предпринимаемые действия. И поскольку это действие, оно должно быть глаголом. Мы должны стремиться использовать как можно меньше слов, потому что так быстрее читается. Но мы не должны удалять слова за счет ясности.

Точные слова могут соответствовать тону голоса вашего бренда, но не меняйте ясность на причудливость.

Простой и понятный язык понятен каждому. Точные слова будут зависеть от типа услуги. Для нашей регистрационной формы подойдет «Зарегистрироваться», но в зависимости от вашего сервиса «Присоединиться» или «Зарегистрироваться» может быть более подходящим.

## Проверка

Несмотря на наши усилия по созданию инклюзивной, простой и удобной регистрации, мы не можем исключить человеческий фактор. Люди совершают ошибки, и когда они это делают, мы должны максимально упростить их исправление.

Когда дело доходит до проверки формы, необходимо учитывать ряд важных деталей. От выбора времени предоставления отзыва до способа отображения этого отзыва и до формулировки хорошего сообщения об ошибке — все эти вещи необходимо принимать во внимание.

### Проверка HTML5

Проверка HTML5 существует уже некоторое время. Добавив всего несколько атрибутов HTML, поддерживающие браузеры будут помечать ошибочные поля при отправке формы. Неподдерживающие браузеры возвращаются к проверке на стороне сервера.

Обычно я бы рекомендовал использовать функциональные возможности, предоставляемые браузером бесплатно, потому что они зачастую более производительны, надежны и доступны. Не говоря уже о том, что он становится более привычным для пользователей по мере того, как все больше сайтов начинают использовать стандартную функциональность.

Хотя поддержка проверки HTML5 достаточно хороша, она не реализована единообразно. Например, обязательный атрибут может с самого начала помечать поля как недействительные, что нежелательно. Некоторые браузеры, такие как Firefox 45.7, будут показывать ошибку «Пожалуйста, введите адрес электронной почты», даже если пользователь что-то ввел в поле, в то время как Chrome, например, говорит: «Пожалуйста, включите «@» в адрес электронной почты». что полезнее.

Мы также хотим предоставить пользователям одинаковый интерфейс независимо от того, перехватываются ли ошибки на сервере или на клиенте. По этим причинам мы разработаем собственное решение. Первое, что нужно сделать, это отключить проверку HTML5: <form novalidate>

### Обработка отправки

Когда пользователь отправляет форму, нам нужно проверить, нет ли ошибок. Если они есть, нам нужно запретить отправку данных из формы на сервер.

 function FormValidator(form) { form.on('submit', $.proxy(this, 'onSubmit')); } FormValidator.prototype.onSubmit = function(e) { if(!this.validate()) { e.preventDefault(); // show errors } };

Обратите внимание, что мы прослушиваем событие отправки формы, а не событие нажатия кнопки. Последнее не позволит пользователям отправить форму, нажав Enter , когда фокус находится в одном из полей. Это также известно как неявное представление формы .

### Отображение обратной связи

Это все очень хорошо обнаруживает наличие ошибок, но на данный момент пользователи не мудрее. Есть три несопоставимые части интерфейса, которые необходимо обновить. О каждом из них мы сейчас и поговорим.

#### Название документа

<title> документа — это первая часть веб-страницы, которую читают программы чтения с экрана. Таким образом, мы можем использовать его, чтобы быстро информировать пользователей о том, что с их отправкой что-то пошло не так. Это особенно полезно, когда страница перезагружается после запроса сервера.

Несмотря на то, что мы улучшаем взаимодействие с пользователем, перехватывая ошибки на клиенте с помощью JavaScript, не все ошибки можно отловить таким образом. Например, проверить, что адрес электронной почты еще не занят, можно только на сервере. И в любом случае JavaScript подвержен сбоям, поэтому мы не можем полагаться исключительно на его доступность.

Там, где исходный заголовок страницы мог бы читаться как «Зарегистрироваться для [услуги]», в случае ошибки он должен читаться как «(2 ошибки) Зарегистрироваться для [службы]» (или подобное). Точная формулировка несколько зависит от мнения.

Следующий JavaScript обновляет заголовок:

 document.title = "(" + this.errors.length + ")" + document.title;

Как отмечалось выше, это в первую очередь предназначено для пользователей программ чтения с экрана, но, как это часто бывает с инклюзивным дизайном, то, что помогает одной группе пользователей, помогает и всем остальным. На этот раз обновленный заголовок действует как уведомление на вкладке.

Заголовок вкладки браузера с префиксом «(2 ошибки)» действует как квази-уведомление.
Заголовок вкладки браузера с префиксом «(2 ошибки)» действует как квази-уведомление.
Сводка ошибок

По сравнению с титровальным элементом сводка ошибок более заметна, что говорит зрячим пользователям, что что-то пошло не так. Но он также отвечает за то, чтобы пользователи могли понять, что пошло не так и как это исправить.

Он расположен в верхней части страницы, поэтому пользователям не нужно прокручивать страницу вниз, чтобы увидеть его после обновления страницы (в случае возникновения ошибки на сервере). Обычно ошибки окрашиваются в красный цвет. Однако, полагаясь только на цвет, можно исключить пользователей с дальтонизмом. Чтобы привлечь внимание к сводке, рассмотрите возможность использования положения, размера, текста и значков.

Панель сводки ошибок расположена ближе к верхней части экрана.
Панель сводки ошибок расположена ближе к верхней части экрана.

Панель включает заголовок «Есть проблема», указывающий на проблему. Обратите внимание, что здесь нет слова «Ошибка», что не очень дружелюбно. Представьте, что вы заполняли свои данные для покупки автомобиля в автосалоне и допустили ошибку. Продавец не сказал бы «Ошибка» — на самом деле было бы странно, если бы он это сказал.

 <div class="errorSummary" role="group" tabindex="-1" aria-labelledby="errorSummary-heading"> <h2>There's a problem</h2> <ul> <li><a href="#emailaddress">Enter an email address</a></li> <li><a href="#password">The password must contain an uppercase letter</a></li> </ul> </div>

Контейнер имеет role group , которая используется для группировки набора элементов интерфейса: в данном случае заголовка и ссылок ошибок. tabindex имеет значение -1 , поэтому его можно сфокусировать программно с помощью JavaScript (когда форма отправляется с ошибками). Это обеспечивает прокрутку панели сводки ошибок. В противном случае интерфейс будет казаться невосприимчивым и сломанным при отправке.

Примечание. Использование tabindex="0" означает, что он будет постоянно фокусироваться с помощью клавиши Tab , что является ошибкой WCAG 2.4.3 Порядок фокусировки. Если пользователи могут перейти к чему-то, они ожидают, что это действительно что-то сделает.

 FormValidator.prototype.showSummary = function () { // ... this.summary.focus(); };

Ниже находится список ссылок на ошибки. Щелчок по ссылке установит фокус на ошибочное поле, что позволит пользователям быстро перейти к форме. Атрибут href ссылки устанавливается на идентификатор элемента управления, которого в некоторых браузерах достаточно, чтобы установить на нем фокус. Однако в других браузерах нажатие на ссылку просто прокручивает ввод в поле зрения, не фокусируя его. Чтобы исправить это, мы можем явно сфокусировать ввод.

 FormValidator.prototype.onErrorClick = function(e) { e.preventDefault(); var href = e.target.href; var id = href.substring(href.indexOf("#"), href.length); $(id).focus(); };

При отсутствии ошибок сводная панель должна быть скрыта. Это гарантирует, что на странице всегда будет только одна сводная панель, и что она постоянно отображается в одном и том же месте, независимо от того, отображаются ли ошибки клиентом или сервером. Чтобы скрыть панель, нам нужно добавить класс hidden .

 <div class="errorSummary hidden" ...></div>
 .hidden { display: none; }

Примечание. Вы можете использовать hidden атрибут/свойство для переключения видимости элемента, но его поддержка меньше. Инклюзивный дизайн — это принятие решений, которые, как вы знаете, вряд ли исключат людей. Использование класса соответствует этой философии.

#### Встроенные ошибки

Нам нужно поместить соответствующее сообщение об ошибке прямо над полем. Это позволяет пользователям прокручивать страницу вверх и вниз, чтобы проверить сообщение об ошибке, и заставляет их двигаться вниз по форме. Если бы сообщение было размещено под полем, мы увеличили бы вероятность того, что оно будет скрыто панелью автозаполнения браузера или экранной клавиатурой.

Встроенный шаблон ошибки с красным текстом ошибки и значком предупреждения прямо над полем.
Встроенный шаблон ошибки с красным текстом ошибки и значком предупреждения прямо над полем.
 <div class="field"> <label for="blah"> <span class="field-error"> <svg width="1.5em" height="1.5em"><use xmlns:xlink="https://www.w3.org/1999/xlink" xlink:href="#warning-icon"></use></svg> Enter your email address. </span> <span class="field-error">Enter an email address</span> </label> </div>

Как и в упомянутом ранее шаблоне подсказки, сообщение об ошибке вставляется внутрь метки. Когда поле сфокусировано, пользователи средств чтения с экрана услышат сообщение в контексте, поэтому они смогут свободно перемещаться по форме, не обращаясь к сводке.

Сообщение об ошибке имеет красный цвет и использует значок предупреждения SVG, чтобы привлечь внимание пользователей. Если бы мы использовали только изменение цвета для обозначения ошибки, это исключило бы пользователей с цветовой слепотой. Так что это очень хорошо работает для зрячих пользователей, но как насчет пользователей программ чтения с экрана?

Чтобы предоставить как зрячим, так и незрячим пользователям одинаковый опыт, мы можем использовать хорошо поддерживаемый атрибут aria-invalid . Когда пользователь фокусирует ввод, теперь он объявляет «Недействительный» (или аналогичный) в программах чтения с экрана.

 <input aria-inval>

Примечание . Регистрационная форма состоит только из ввода текста. В главе 3 «Форма бронирования рейса» мы рассмотрим, как легко вводить ошибки для групп полей, таких как переключатели.

### Повторная отправка формы

При отправке формы во второй раз нам необходимо удалить существующие ошибки из поля зрения. В противном случае пользователи могут увидеть повторяющиеся ошибки.

 FormValidator.prototype.onSubmit = function(e) { this.resetPageTitle(); this.resetSummaryPanel(); this.removeInlineErrors(); if(!this.validate()) { e.preventDefault(); this.updatePageTitle(); this.showSummaryPanel(); this.showInlineErrors(); } };
### Инициализация

Закончив определение компонента FormValidator , мы готовы его инициализировать. Чтобы создать экземпляр FormValidator , вам нужно передать элемент формы в качестве первого параметра.

 var validator = new FormValidator(document.getElementById('registration'));

Например, чтобы проверить поле электронной почты, вызовите метод addValidator() :

 validator.addValidator('email', [{ method: function(field) { return field.value.trim().length > 0; }, message: 'Enter your email address.' },{ method: function(field) { return (field.value.indexOf('@') > -1); }, message: 'Enter the 'at' symbol in the email address.' }]);

Первый параметр — это name элемента управления, а второй — массив объектов правила. Каждое правило содержит два свойства: method и message . method представляет собой функцию, которая проверяет различные условия, чтобы вернуть значение true или false . False переводит поле в состояние ошибки, которое используется для заполнения интерфейса ошибками, как обсуждалось ранее.

#### Прощение банальных ошибок

В «Дизайне повседневных вещей » Дон Норман говорит о дизайне с учетом ошибок. Он говорит о том, как люди разговаривают:

«Если человек говорит что-то, что мы считаем ложью, мы подвергаем сомнению и спорим. Мы не подаем предупредительный сигнал. Мы не сигналим. Мы не выдаем сообщения об ошибках. […] В обычных разговорах между двумя друзьями искажения воспринимаются как нормальное явление, как приближение к тому, что на самом деле имелось в виду».

В отличие от людей, машины недостаточно разумны, чтобы определить смысл большинства действий, но они часто гораздо менее прощают ошибки, чем это необходимо. Джаред Спул шутит по этому поводу в статье «Дизайн метрически противоположен?» (около 42 минут):

«Требуется одна строка кода, чтобы взять номер телефона и удалить все тире, скобки и пробелы, и десять строк кода, чтобы написать сообщение об ошибке, в котором вы их оставили».

Метод addValidator (показан выше) демонстрирует, как разработать правила проверки, чтобы они прощали тривиальные ошибки. Первое правило, например, обрезает значение перед проверкой его длины, уменьшая нагрузку на пользователя.

### Текущая встроенная проверка

Встроенная проверка в реальном времени дает пользователям обратную связь по мере ввода или выхода из поля ( onblur ). Есть некоторые свидетельства того, что динамическая встроенная проверка повышает точность и сокращает время заполнения длинных форм. Частично это связано с предоставлением отзывов пользователям, когда требования поля еще свежи в памяти пользователей. Но живая встроенная проверка (или, для краткости, живая проверка) создает несколько проблем.

Для записей, требующих определенного количества символов, первое нажатие клавиши всегда будет считаться недействительной записью. Это означает, что пользователи будут прерваны раньше, что может привести к переключению мыслительного контекста с ввода информации на ее исправление.

В качестве альтернативы мы могли бы подождать, пока пользователь не введет достаточно символов, прежде чем отобразить ошибку. Но это означает, что пользователи получают обратную связь только после того, как ввели правильное значение, что несколько бессмысленно.

Мы могли бы подождать, пока пользователь покинет поле ( onblur ), но это слишком поздно, поскольку пользователь мысленно подготовился (и часто начал вводить) в следующее поле. Более того, некоторые пользователи переключают окна или используют менеджер паролей при использовании формы. Это вызовет событие размытия, в результате чего ошибка будет отображаться до того, как пользователь завершит работу. Все очень расстраивает.

Помните, что нет проблем с предоставлением отзывов пользователям без обновления страницы. Нет проблем и с размещением сообщений об ошибках в строке (рядом с полями) — мы это уже сделали. Проблема обратной связи в режиме реального времени заключается в том, что она прерывает пользователей либо слишком рано, либо слишком поздно, что часто приводит к неприятному опыту.

Если пользователи часто видят ошибки, вероятно, где-то что-то не так. Сосредоточьтесь на сокращении формы и предоставлении лучшего руководства (хорошая маркировка и текст подсказки). Таким образом, пользователи не должны видеть ничего, кроме нечетной ошибки. Мы рассмотрим более длинные формы в следующей главе.

### Шаблон утверждения контрольного списка

Разновидность проверки в реальном времени включает в себя отметку правил (отметку их как завершенных) по мере ввода пользователем. Это менее инвазивно, чем проверка в реальном времени, но подходит не для каждого типа поля. Вот пример формы регистрации MailChimp, в которой используется этот метод для поля пароля.

Поле пароля MailChimp с инструкциями, которые отмечаются, когда пользователь соответствует требованиям.
Поле пароля MailChimp с инструкциями, которые отмечаются, когда пользователь соответствует требованиям.

Вы должны поместить правила над полем. В противном случае экранная клавиатура может скрыть обратную связь. В результате пользователи могут перестать печатать и скрыть клавиатуру, чтобы проверить отзыв.

### Примечание об отключении кнопок отправки

Некоторые формы предназначены для отключения кнопки отправки до тех пор, пока все поля не станут действительными. Есть несколько проблем с этим.

Во-первых, пользователи задаются вопросом, что на самом деле не так с их записями. Во-вторых, отключенные кнопки не могут быть сфокусированы, что затрудняет обнаружение кнопки слепыми пользователями, перемещающимися с помощью клавиши Tab . В-третьих, отключенные кнопки плохо читаются, поскольку они выделены серым цветом.

Поскольку мы предоставляем пользователям четкую обратную связь, когда пользователь этого ожидает, нет веских причин отнимать у пользователя контроль, отключая кнопку в любом случае.

### Создание хорошего сообщения об ошибке

Нет ничего важнее содержания. Пользователи приходят на ваш сайт не для того, чтобы насладиться дизайном. Они приходят, чтобы насладиться контентом или результатом использования услуги.

Даже самый продуманный, инклюзивный и красиво оформленный опыт ничего не стоит, если мы игнорируем слова, используемые для создания сообщений об ошибках. Одно исследование показало, что отображение пользовательских сообщений об ошибках увеличивает конверсию на 0,5%, что соответствует более чем 250 000 фунтов стерлингов годового дохода.

«Контент — это пользовательский опыт».
— Джинни Редиш

Подобно меткам, подсказкам и любому другому содержимому, хорошее сообщение об ошибке обеспечивает ясность с помощью как можно меньшего количества слов. Обычно мы должны управлять дизайном интерфейса на основе контента, а не наоборот. Но в этом случае понимание того, как и почему вы показываете сообщения об ошибках, влияет на дизайн слов. Вот почему Джаред Спул говорит, что «контент и дизайн — неразлучные партнеры по работе».

Мы показываем сообщения в сводке вверху экрана и рядом с полями. Поддерживать две версии одного и того же сообщения — это навязчивая реклама неубедительной выгоды. Вместо этого создайте сообщение об ошибке, которое будет работать в обоих случаях. «Введите символ «at» требует контекста из метки поля, чтобы иметь смысл. «Вашему адресу электронной почты нужен символ «at»» хорошо работает в обоих случаях.

Избегайте любезностей, например, начинайте каждое сообщение об ошибке с «Пожалуйста». С одной стороны, это кажется вежливым; с другой — мешает и подразумевает выбор.

Какой бы подход вы ни выбрали, будет некоторое повторение из-за характера содержания. И тестирование обычно включает в себя отправку формы вообще без ввода какой-либо информации. Это делает повторение явно очевидным, что может заставить нас сходить с ума. Но как часто это бывает? Большинство пользователей не пытаются сломать интерфейс.

Сводка ошибок, содержащая стену сообщений об ошибках, делает начало слов слишком повторяющимся.
Сводка ошибок, содержащая стену сообщений об ошибках, делает начало слов слишком повторяющимся.

Разные ошибки требуют разного форматирования. Инструкции типа «Введите свое имя» естественны. Но фраза «Введите имя длиной не более 35 символов» длиннее, многословнее и менее естественно, чем описание вроде «Имя должно состоять не более чем из 35 символов».

Вот контрольный список:

  • Будьте лаконичны. Не используйте больше слов, чем необходимо, но и не опускайте слова за счет ясности.
  • Быть последовательным. Используйте один и тот же тон, одни и те же слова и одни и те же знаки препинания.
  • Быть конкретной. Если вы знаете, почему что-то пошло не так, так и скажите. «Электронная почта недействительна». неоднозначен и создает нагрузку на пользователя. «В письме нужен символ 'at'» — это ясно.
  • Будьте человеком, избегайте жаргона. Не используйте такие слова, как недействительный, запрещенный и обязательный.
  • Используйте простой язык. Сообщения об ошибках — это не возможность продвигать юмористический тон вашего бренда.
  • Используйте активный залог. Когда ошибка — это инструкция, и вы говорите пользователю, что делать. Например, «Введите свое имя», а не «Необходимо ввести имя».
  • Не обвиняйте пользователя. Сообщите им, что пошло не так и как это исправить.
## Резюме

В этой главе мы решили несколько фундаментальных задач дизайна форм, применимых не только к простой регистрационной форме. Во многих отношениях эта глава была как о том, чего не следует делать, так и о том, что нам следует делать. Избегая новых и искусственных шаблонов для экономии места, чтобы сосредоточиться на сокращении количества полей, которые мы включаем, мы избегаем нескольких недостатков удобства использования, одновременно делая формы более приятными.

### Чего следует избегать
  • Использование атрибута placeholder в качестве механизма для хранения метки и текста подсказки.
  • Использование неправильных типов ввода.
  • Оформление кнопок и ссылок одинаковое.
  • Проверка полей по мере ввода пользователем.
  • Отключение кнопок отправки.
  • Использование сложного жаргона и микротекста под влиянием бренда.

Вот и все. Если вам понравилась эта первая глава Form Design Patterns , вы можете получить книгу прямо сейчас. Приятного чтения!

Книга Form Design Patterns представляет собой книгу в твердом переплете с желтой обложкой и черным текстом.

электронная книга

$19 Получить электронную книгу

PDF, ePUB, Киндл. Бесплатно для участников Smashing.

Твердый переплет

$39 Получить распечатку (включая электронную книгу)

Печатный, качественный твердый переплет. Бесплатная доставка авиапочтой по всему миру.