Текст над строкой

Простые задачи в CSS часто имеют не самые очевидные решения. С одним таким случаем мне пришлось недавно столкнуться. Задача была очень простой — добавить на сайт знак ®.

Задача

В отличие от знака торговой марки ™, знак ® не поднят над строкой. Тем не менее, общепринятая практика — располагать его таким же образом, чуть уменьшив в размере.

В HTML есть подходящий элемент — <sup>. По умолчанию, у него есть нужные CSS-свойства: vertical-align:super и font-size:smaller.

<sup>&reg;</sup>

Супер!

В идеальном мире история на этом должна закончиться. В реальности все только начинается.

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

Проблема

То, что тег <sup> со стилями по умолчанию не является решением стало понятно сразу.

Наличие выравнивания vertical-align:super приводит к увеличению высоты строки, и к скачущему интерлиньяжу. А font-size:smaller — не самый надежный способ уменьшить размер текста относительно контекста. Реальное значение будет зависеть от контекста не напрямую, а будет вычислено с оглядкой на таблицу абсолютных значений согласно спецификации. В целом браузеры справляются, хотя и по-разному, но есть и исключения. Где-то кегль будет уменьшен недостаточно, а где-то слишком сильно. Привет, IE.

Кроме того, Webkit-браузеры разрывают стандартное подчеркивание (text-decoration: underline), встретив vertical-align:super внутри строки (на самом деле не поэтому, но как один из случаев). Не знаю логичное это поведение или нет, но ссылка с разорванным подчеркиванием выглядит уродливо.

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

Нагуглить готовое решение не получилось. Неплохая попытка — https://gist.github.com/unruthless/413930, но не решена проблема с подчеркиванием. Обращу внимание на хороший комментарий со ссылкой на тест-кейсы для разных знаков.

Решение

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

Имитировать text-decoration:underline другими CSS-свойствами в общем случае невозможно. Толщина и положение линии подчеркивания зависят от шрифтовой гарнитуры, кегля, браузера, ОС и режима масштабирования.

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

Суть решения

  1. Стилизовать не элемент <sup>, а псевдо-элемент с таким же содержимым.

  2. Содержимое псевдо-элемента (свойство content) задать через data-атрибут. Так дублирование контента будет независимым от CSS и приемлимым с точки зрения HTML-семантики.

  3. Добавить обертку и назначить псевдо-элемент на нее. Это нужно, чтобы спрятать оригинальное содержимое с помощью color:transparent, тем самым, оставив его в потоке и резервируя место под стилизованный псевдо-элемент.

  4. Заменить sup на span. (опционально) Объяснение ниже.

  5. Добавить слезы единорога пробел нулевой длины (опционально). Объяснение ниже.

DEMO

Чистое решение.

Сравнение с эталонами — показывает ход решения.

Тестирование решения на разных кеглях.

Детали

Span vs. sup

В IE нельзя задать для sup размер шрифта. Серьезно. IE лучше знает как нужно — тест. Плохо это тем, что ширина площадки, которая резервируется оригинальным содержимым, в IE будет меньше, чем в других браузерах. Для знака ® можно заменить <sup> на <span>, но это недопустимо в других случаях, когда важно сохранить семантику: цифры сносок, степень числа и т.д.

Магические числа vs. font-size:smaller

Font-size:smaller по-разному вычисляется в браузерах, поэтому трудно подобрать компенсирующее смещение для резервирующей площадки.

Магические числа зависят только от ширины гарнитуры. Для распространенных системных шрифтов эти числа колеблются около 0.75em для кегля и 0.25em для смещения.

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

Zero-width space. WTF?

В Webkit-браузерах найден странный баг.

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

Проще это увидеть. Тест.

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

Хаки для старых IE

В IE8 не работает color:transparent, вместо него можно безопасно использовать visibility:hidden — подчеркивание сохранится;

Для IE7 можно написать экспрешны для before и для color:inherit или упростить стили.

Кроссбраузерность

Решение кроссбраузерное. Тестировалось в:

Использовались Windows, Linux и OS X (лень расписывать соответствие версиям браузеров).

Недостатки решения

Итог

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

BTW

Во время работы над задачей родился пример использования несуществующего селектора ::last-letter. С помощью него было бы удобно убирать нижнее подчеркивание для последнего знака над строкой. Но ребята из W3C уже давно решили, что этому не бывать.

P.S.

@lerarunge:

— Если ты встаешь на путь извращений, ты должен быть готов идти до конца — говорит один фронтендер другому, глядя в код