От автора: При реализации шрифтов-иконок нужно быть особенно аккуратным, чтобы гарантировать отличное впечатление для всех пользователей. Что происходит, когда ваш шрифт не загружается? Что случается, когда в браузере не поддерживается @font-face? Мы покажем вам, как выполнить «пуленепробиваемые» шрифты-иконки.
В нескончаемом квесте на построение рациональных и эффективных сайтов для клиентов, скромняга-шрифт много раз предлагался в качестве альтернативной легкой реализации векторных иконок. Хотя обычно мы предпочитаем (и рекомендуем) применять для векторных иконок SVG из тех соображений, которые задокументировал в посте своего блога Айэн Февер (Ian Feather ) из Lonelyplanet.com, иногда мы сотрудничаем с другими командами, которые уже занимались внедрением шрифтов-иконок. На такие случаи мы решили изучить, каким наилучшим и общедоступным способом применяются шрифты-иконки.
А вы знали о том, что большинство методов применения шрифтов-иконок плохо согласуются с скринридер-ами, а для нормальной работы и общедоступности им требуются шаблоны специальной разметки? А еще мы обнаружили, что многие старые (и новые!) мобильные браузеры—особенно Opera Mini, браузер Nokia XPress, Blackberry 4–5, Android 2.1 и Windows Phone 7–7.8— вообще не поддерживают @font-face. Поизучав вопрос еще немного, мы удивились, узнав, что (на момент написания текста) эти браузеры служат по крайней мере 370 миллионам пользователей во всем мире.
В этой статье мы обсудим, где шрифты-иконки работают хорошо, а где плохо, что происходит с ними, когда не поддерживается @font-face (или когда шрифт не грузится), и как решать такие проблемы во время выполнения квеста по поиску устойчивого метода доставки шрифтов-иконок.
Проблемы шрифтов-иконок
ДОСТУПНОСТЬ
Рассмотрим следующую разметку:
1 |
<span class="icon-star">Favorite</span> |
Множество популярных библиотек пользуются вышеприведенной разметкой и вводят шрифты-иконки с помощью свойств CSS :before или :after. Однако это содержимое не скрыто от скринридер-ов и может прочитываться вслух. «Но это же Unicode!», скажете вы, «Неужели скринридеры вообще трогают символы Unicode?». Да, трогают (даже некоторые знаки в «пользовательских» областях Private Use, но об этом позже).
Например, если применить следующий CSS к нашему примеру интервала:
1 |
.icon-star:before { content: "★ "; } |
полученная в результате страница появится в большинстве браузеров вот такой, и будет смотреться просто отлично:
Но VoiceOver (в Mac OS X) выразит ее словами «Black Star Favorite». Далековато от идеала, да?
К сожалению, не существует надежного подхода с применением одного лишь CSS, способного спрятать от скринридеров лишний вербальный контент (поддержка speak: none ограничена). Самый верный способ скрыть ненужное словесное содержимое от скринридера (но не от зрячего пользователя) – это воспользоваться aria-hidden=»true». Такой подход потребует от нас использования для иконок отдельного элемента HTML, что исключает применение к родительскому элементу :before или :after.
На нашу долю остается лишь следующий шаблон HTML и CSS:
1 2 3 4 |
<style> .icon-star:before { content: "★ "; } </style> <span><span class="icon-star" aria-hidden="true"></span>Favorite</span> |
VoiceOver читает это как «Favorite», а нужные нам визуальные стили сохраняются. Заметки на полях: Известны открытые вопросы Chrome и Firefox, касающиеся скрытого в областях контента внутри элемента button, но даже в таких случаях aria-hidden остается самым удачным способом скрыть содержимое от скринридеров.
НАДЕЖНЫЕ АЛЬТЕРНАТИВЫ
Далее давайте рассмотрим, что происходит, когда браузер не поддерживает @font-face и/или файл шрифта не загружается. Два привычных способа реализации пользовательских иконок в качестве шрифтов включают преобразование вышеупомянутых иконок в символы Unicode и их отображение специальными лигатурами из символьных строк. Тестирование показало, что у обоих методов имеются свои трудности.
Реализуйте Unicode; запасной вариант для смайлика или пробела
В Unicode есть три не определенных спецификацией общедоступных раздела. Называемые «пользовательскими» областями (Private Use Areas) (или «PUA»), они позволяют пользовательским шрифтам вставлять символы, не имеющие официального значения в Unicode. Применение первых PUA (иногда называемых «BMP PUA») допустимо с кодированием UTF-8.
Многие популярные решения проблемы шрифтов-иконок включают отображение своих символов совокупностью PUA во избежание конфликта с существующими определениями Unicode. Например, вам не захотелось бы заменить определение Unicode для Black Star на Rainbow. (Ну, может и захотелось бы, но не следует этого делать).
Применение PUA позволяет избегать семантических коллизий, но визуальные проблемы остаются все равно. Например, шрифты по умолчанию одной операционной системы определяют свои символы в PUA. Если какие-либо из ваших иконок отображаются символом с символом по умолчанию и запрос шрифта выполняется неудачно, то будет показан символ по умолчанию. Вот небольшой пример PUA в iOS7:
Рассмотрим предыдущий пример. Предположим, что мы преобразуем новую пользовательскую иконку черной звездочки в символ Unicode PUA. Разметка будет примерно такой:
1 2 3 4 |
<style> .icon-star:before { content: "\e001"; } </style> <span><span class="icon-star" aria-hidden="true"></span>Favorite</span> |
Когда не удается сделать запрос шрифта-иконки, вместо нее отображается знак Unicode по умолчанию, в iOS это будет показано так:
Когда конфликтующий символ PUA определен не подробно, это не значит, что ожидается пустой знак—вместо недостающего символа может быть показан символ по умолчанию. Зачастую это простой прямоугольник (тот самый ужасный глиф по умолчанию):
Иногда все сложнее:
Шрифты-иконки часто полагаются на появление невидимого текста (Flash of Invisible Text, или FOIT – почитайте об альтернативе WebKit’а для появления текста без стилей (Flash of Unstyled Text, или FOUT) для скрытия таких альтернативных вариантов, пока производится запрос @font-face. FOIT – предмет разногласий в среде веб-разработчиков. При маловероятном развитии событий, когда в будущем браузер переключится на использование FOUT вместо FOIT, шрифтам-иконкам понадобится дополнительная защита для скрытия альтернативных символов на время выполнения запросов @font-face.
Реализуйте лигатуры; альтернативный вариант для Real Text
Вместо того, чтобы в запасном варианте полагаться на специальные символы, у кого-то возникла грандиозная идея применять для альтернативного текста лигатуры. Лигатура получается, когда две или более буквы соединяются в один глиф. Это дает вам возможность подставлять глифы, когда в исходном тексте встречаются предопределенные сочетания символов.
Например, лигатура может подставить ♥, когда вы печатаете «love»:
Когда лигатуры не поддерживаются (или не грузится шрифт), то показывается альтернативный текст. Теоретически это великолепно, но есть некоторые ограничения.
Какое-то время на многих ресурсах для подключения к лигатурам рекомендовалось использовать text-rendering: optimizeLegibility;, что действительно является частью спецификации SVG (не стандартом CSS). Это решение реально кишит багами.
Некоторые предлагали альтернативный способ включения лигатур: font-feature-settings. Поддержка этого более надежного подхода, к сожалению, невелика даже в браузерах десктопов: пользовательские шрифты-иконки заметно терялись в IE9 и ниже, Android 4.3 и ниже, во всех браузерах до BlackBerry 10 и в текущих версиях IE Mobile. На время написания данного текста это означает, что альтернативы доставляются до 30% от общего количества пользователей. (Тьфу.)
Еще одна проблема лигатур в том, что вы привязываете контент к визуальному представлению. Это может вызывать проблемы интернационализации; например, любому тексту понадобится отдельная лигатура для каждого языка, что вызывает интересные проблемы поддержки сайтов и рабочего процесса. Также интересно измерить разницу размеров для шрифта с одним языком и десятью разными языками. Достаточно ли гарантировать создание отдельных шрифтов для каждого языка? Наверное, нет—хотя бы потому, что скачиваются и не используются дополнительные байты.
Наконец, стоит отметить, что лигатуры не способны содержать пробелы, что исключает множественные слова для одной иконки. (Ларри Фокс (Larry Fox) любезно указал, что лигатуры не могут поддерживать пробелы, хотя Киамун (Keyamoon) обращает наше внимание на то, что в Chrome имеется открытый баг лигатур пробела.)
Итак, что теперь? Шрифты-иконки – это неудачное предприятие?
На самом деле нет. Несмотря на вышеупомянутые опасности, шрифты-иконки можно применять! Просто придется уделить им немного больше внимания, нежели просто вставить блок @font-face в CSS и счесть дело оконченным. Перед тем, как удариться в подробности, мы разработали два разных решения на основе двух сценариев применения шрифтов-иконок:
Декоративные иконки: это те случаи, когда иконка служит чисто декоративной деталью и не несет особого значения или функциональности. Если не показывается глиф иконки, это нормально, но для нормального центрирования/выравнивания соседнего содержимого альтернатива не должна занимать «недвижимость» экрана.
Необходимые иконки: в таких случаях иконка необходима либо для восприятия, либо для назначения сайта, и должен показываться либо глиф иконки, либо альтернативное содержимое на его месте. Существует два варианта насущных иконок: альтернатива текста (меняемой длины, без ограничений «недвижимости») и альтернатива другого глифа, либо надежного эквивалента Unicode (ОБРАТИТЕ ВНИМАНИЕ: существует очень мало надежных кроссплатформенных/кроссбраузерных глифов, к счастью, Джон Холт Рипли (John Holt Ripley) собрал матрицу поддержки символов Unicode) или изображения (вероятно, битового PNG для совместимости).
Чтобы уменьшить сложность этой реализации, мы сконцентрировали суть использованных в этих примерах CSS и JS в библиотеку многократного пользования под названием Font Garde. Мы выложили ее на GitHub для того, чтобы вы смогли применять эти шаблоны устойчивых шрифтов-иконок в своей работе. Включите два индивидуальных теста свойств Modernizr, afontgarde.css и afontgrade.js, и вы готовы к гонкам.
ДЕКОРАТИВНЫЕ ИКОНКИ
Реализовать декоративные иконки проще всего. Иконка просто отображается в тех случаях, когда поддерживается @font-face, и скрывается, когда нет. «На отлично» все будет выглядеть так:
А браузеры-«троечники» отобразят вот такое:
Требуется выполнить совсем небольшие требования шаблона разметки:
1 2 |
<span class="icon icon-twitter" aria-hidden="true"></span> Share on Twitter (Sibling Text) |
Во-первых, применим отдельный элемент span для решения проблемы доступности скринридерам. После стандартного блока @font-face добавим свой CSS для шрифтов-иконок:
1 2 3 4 5 6 |
.supports-fontface.icomoon .icon:before { font-family: icomoon; } .supports-fontface.icomoon .icon-twitter:before { content: "\e604"; } |
Обратите внимание, что для подачи класса supports-fontface мы пользуемся тестом свойств Modernizr. Класс icomoon поставляется afontgarde.js (который делает тестирование путем измерения и сравнения размеров глифа с шрифтом по умолчанию для того, чтобы гарантировать успешную загрузку шрифта).
JS
1 |
AFontGarde( 'icomoon', '\uE600\uE601\uE602\uE605' ); |
Здесь для измерения мы передаем в утилиту семейство шрифтов и некоторые глифы-иконки. Вам понадобится всего лишь вызвать эту утилиту по разу к каждому шрифту, а в дальнейшем мы будем полагаться на класс icomoon. Так как CSS требуется добавлять эти два класса для отображения шрифта-иконки, то решаются вышеупомянутые проблемы. Великолепно! Этот случай употребления нам подходит.
НЕОБХОДИМЫЕ ИКОНКИ
Для них нужно обеспечить появление хоть чего-нибудь, если вдруг не удастся отобразить шрифт-иконку. Такие случаи обычно делятся на два лагеря: те, где нужно заменить изображение-глиф текстом, и те, где мы применяем альтернативный глиф.
Насущные иконки с текстовой альтернативой
Высший балл
«Тройка»
HTML
1 2 3 4 |
<span class="icon-fallback-text"> <span class="icon icon-twitter" aria-hidden="true"></span> <span class="text">Twitter</span> </span> |
CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.icon-fallback-text .icon { display: none; } .supports-fontface.supports-generatedcontent.icomoon .icon-fallback-text .icon { display: inline-block; } .supports-fontface.supports-generatedcontent.icomoon .icon-fallback-text .text { /* общий способ визуально скрыть контент, который при этом остается доступным для скринридеров (h5bp.com) */ clip: rect(0 0 0 0); overflow: hidden; position: absolute; height: 1px; width: 1px; } |
Выше приведен библиотечный код, включенный с помощью afontgarde.css
Здесь применяется тот же принцип тестирования свойств, за исключением того, что мы добавляем дополнительный класс supports-generatedcontent, чтобы убедиться в поддержке :before и :after. Если это не так, то показывается альтернативный текст.
Необходимые иконки с альтернативным глифом или изображением
Высший класс
Альтернативный глиф-середнячок
Альтернативное изображение на «троечку»
HTML
1 2 3 4 |
<span class="icon-fallback-glyph"><!— or "icon-fallback-img" —> <span class="icon icon-hamburger" aria-hidden="true"></span> <span class="text">Menu</span> </span> |
CSS
Библиотечный код из afontgarde.css для краткости был пропущен.
1 2 3 4 5 6 7 8 9 10 11 12 |
/*************************** * альтернативный глиф ***************************/ .icon-fallback-glyph .icon-hamburger:before { content: "\2261"; /* Hamburger */ font-size: 2em; line-height: .5; } /* высший класс */ .supports-fontface.icomoon .icon-fallback-glyph .icon-hamburger:before { content: "\e601"; } |
Убедитесь, что тщательно отобрали альтернативный символ-глиф. Кроссбраузерная/кроссплатформенная совместимость может отличаться. Сверьтесь с таблицами совместимости Джона Холта Рипли (John Holt Ripley).
Также заметьте, что мы подгоняем font-size и line-height альтернативного глифа так, чтобы он как можно точнее соответствовал шрифту-иконке. Когда (и только когда) загрузился шрифт, мы устанавливаем CSS-содержимое нового глифа шрифта-иконки.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/*************************** * альтернативное битовое изображение ***************************/ .icon-fallback-img .icon-hamburger { width: 1em; height: 1em; background: url("fonts/png/hamburger.png") no-repeat; } /* высший класс */ .supports-fontface .icon-fallback-img .icon-hamburger:before { font-family: icomoon; content: "\e601"; } |
Из-за условий состязания межу тестированием свойств и фоновыми изображениями CSS случай применения альтернативного изображения менее надежен, чем остальные подходы. Если для выполнения скрипту нужно ожидать проверки загрузки шрифта (и добавьте класс icomoon), запрос фонового изображения запустится преждевременно, несмотря на успешную загрузку шрифта. Вторая половина этого компромисса означает, что если HTTP-запрос шрифта будет неуспешен, то мы покажем символ Unicode по умолчанию.
Заметьте, мы регулируем ширину и высоту элемента альтернативного изображения так, чтобы он как можно точнее соответствовал шрифту-иконке. Если хотите подогнать размер фонового изображения, примите во внимание ограничения браузерной поддержки свойства CSS background-size.
Уффф!
Наверно, это кажется слишком сложным заданием. Но именно поэтому мы упаковали все в легко размещаемую библиотеку, которая позволяет неоднократно использовать эти шаблоны с небольшими затратами. Тесты свойств – ключевой момент применения шрифтов-иконок, убедитесь, что пользуетесь ими благоразумно в целях защиты своего контента от непреднамеренных альтернативных практик.
Автор: Zach
Источник: //filamentgroup.com/
Редакция: Команда webformyself.