Выравнивание иконок по базовой линии шрифта

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

    image



    Не долго думая, пишем простую разметку и css к ней:

    HTML
    <div class="user">
    	<span class="user-icon"></span>
    	<span class="user-name">User Name</span>
    </div>
    

    CSS
    .user-icon {
    	width: 23px; height: 20px;
    	background: url('person.png');
    	display: inline-block;
    	margin-right: 3px;
    }
    .user-name {
    	font-size: 14px;
    }
    

    После чего открываем страницу во всех браузерах и видим, что всё везде работает как надо, так как все инлайн и инлайн-блок элементы по умолчанию выравниваются по базовой линии шрифта. Однако IE как обычно преподносит сюрприз. Я понимаю, что IE6 уже дышит на ладан, но вот с IE7 пока приходится считаться, и в нем как раз вышеприведенный код рендрится следующим образом:

    image

    Не уверен точно, в чем причина подобного поведения, но скорее всего это связано с некорректной работой свойства display: inline-block в IE6-7.
    При этом если вместо спана с фоном поставить обычную картинку, то все прекрасно выравнивается, однако это решение нас не устраивает, так как все иконки зашиты в спрайт, что не позволяет нам использовать обычные картинки.
    Решение пришло довольно быстро: нужно всего лишь запихнуть в спан еще один пустой спан, который насильственно заставляет весь блок, какого бы он ни был размера, выравниваться по базовой линии.
    И наш HTML тогда начинает выглядить следующим образом:

    <div class="user">
    	<span class="user-icon"><span></span></span>
    	<span class="user-name">User Name</span>
    </div>
    

    На этом всё. Спасибо за внимание.

    Upd. 1



    Для тех, кто не понимает, зачем ставить иконку отдельным элементом, а не просто фоном спану с текстом, объясняю:

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

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

    Комментарии 54

      +23
      Странно, мне обычно нужен именно второй вариант.
        0
        Да, чаще всего иконки выравниваются по средней линии. Но вот в макете над которым я сейчас работаю, большая часть иконок поставлена дизайнером на базовую линию.
          0
          Тоже самое хотел написать. Обычно надо чтобы выравнивался текст по центру, а не наоборот.
            –1
            Кстати, сейчас нашел таки просто способ сделать второй вариант, о котором я лично не знал:

            <div>
                <img src="user.png" align="absmiddle" />
                Username
            </div>
          • НЛО прилетело и опубликовало эту надпись здесь
              +12
              опять перепутали с двачем?
              • НЛО прилетело и опубликовало эту надпись здесь
                  +1
                  Это, видимо, пришла волна новых пользователей, которые только проникаются чудесами верстки и спешат поделиться такими открытиями, например, как float.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      X, справа от него Y. Внешний блок должен иметь ширину, чтобы влезли и X и Y, если это поззволяет ширина родителя.
                      • НЛО прилетело и опубликовало эту надпись здесь
                          +1
                          Там, в спецификации, написано, что если указано width: auto, то используется shrink-to-fit, а этот алгоритм выбирает максимально возможную ширину для блока (но не шире родителя). Соответственно, если длина строки X и строки Y в сумме не превышают ширины родителя, то никаких оснований делать перенос строки нет.

                          Проблема в Webkit/Gecko, как я понимаю (я код не смотрел, но догадываюсь :) ) в том, что при предварительном расчете максимальной ширины (preferred width) Webkit не учитывает тот факт, что флоат сдвинет текст Y вбок, а просто берет ширину текста, и получается блок меньшей ширины, чем хотелось бы. И это больше все же похоже на баг.

                          Справедливости ради, сам алгоритм shrink-to-fit не такой уж и простой, когда у нас внутри например есть несколько вложенных дивов без указанной ширины, с паддингами и маргинами, и флоаты, не так-то просто рассчитать требуемую ширину. А спецификация CSS оставляет разработчиков браузеров наедине с этой проблемой.
                          • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              Кстати, я проверил — заменяем Y на несколько букв с пробелами, например Y Z A B C — родительский контейнер принимает ширину, равную длине этой строки. Так что это именно баг ( видимо из-за упрощенного алогоритма расчета) в расчете preferred width.
                                0
                                Кто сможет описать внятно баг и загнать его для WebKit и Gecko (если ещё не создан)?
                                • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          float должен минимизироваться по ширине, если не указано другое.
                          Минимальная ширина — когда в две строки.

                          Всё по честному?
                          • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      Ваш первый выпад был на тему: что здесь делают трюки для верстальщиков. За что и был заминусован. Теперь вы съезжаете по линии: не очень глубокие мысли. Но это и без вас решит толпа, воспринимайте как данность. Например, данный топик набрал 6 баллов, что в общем-то и говорит о его слабости.
                      • НЛО прилетело и опубликовало эту надпись здесь
                  +11
                  >>Решение пришло довольно быстро: нужно всего лишь ( только для IE ) запихнуть в спан однопиксельный гиф…

                  Это не решение, а уродливый костыль.

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

                  Кроме того, в данном вам случае хватило бы   внутри span c увеличенным font-size и желаемым padding-left.
                    –1
                    1. Не очень понял про «Спрайт нужно складывать вертикально»
                    2. Да, действительно, сейчас проверил, достаточно внутрь положить просто любой инлайновый элемент, а не обязательно картинку (причем font-size и padding тоже не нужны). Сейчас обновлю статью.
                      0
                      c вертикально сложенными спрайтами (как светофор) для всех элментов высотой в иконку досточно было бы background c нужной позицией 0 -65px например. Если сверху «срежет» иконку, то увеличить line-height строке.
                        –1
                        Я очень не люблю решения, где участвует свойство line-height. Так как практика показывает, что высота строки разными браузерами рассчитывается по-разному.
                          +5
                          Ну добавьте верхнего padding, но уверяю, это — вымысел, нормально line-height везде работает, не придумывайте и пишите, где понадобится.
                          Вы из-за подобных надуманных фобий только будете изобретать неоправданно странные решения.
                            –3
                            Это не фобия. Пару раз я даже сталкивался с тем, что высота строки изменялась в зависимости от высоты блока и его положения на странице. К примеру что-то динамически появилось над блоком, блок съехал немного вниз, смотришь, а у него межстрочное расстояние вдруг стало больше (или меньше). Это баг браузера понятно, но такое встречается. Поэтому я предпочту поставить иконку отдельным элементом, чтобы быть на 100% процентов уверенным в том, что ничего никуда не съедет и не обрежется. И кстати с вашим решением попробуйте изменить в браузере размер шрифта. Все тут же сломается.
                              +2
                              Тут с вами не поспоришь. Это не фобия, это — непрофессионализм.
                      0
                      * хватило бы nbsp внутри спан. // хабрапарсер.
                        0
                        Нет, nbsp не работает
                          +1
                          Забавно, на сколько я понял вы предлагаете использовать один единственный спан с nbsp, у которого будет задан padding и увеличенный шрифт. Я попробовал, но это решение не сработало. IE не выровнял по базовой линии. Однако, из-за хабрапарсера, я сначала подумал, что вы предлагаете запихнуть внутрь просто ещё один спан. И вот это как раз сработало.))
                        +3
                        100 картинок, 100 раз вставлять какой то сомнительный код. Не проще ли .ie7 .user-icon{vertical-align:XXpx}?
                          0
                          Обновил статью. Теперь решение без картинки.
                            0
                            Лучше, но всё одно лишний спан, так что упрёк от winbackgo о лишнем коде справедлив.

                            Впрочем, и его «.ie7» имеет тот другой недостаток, что он навешивается (по всей вероятности) скриптом, так что отвалится при отключении JavaScript.
                              +4
                              > Впрочем, и его «.ie7» имеет тот другой недостаток, что он навешивается (по всей вероятности) скриптом, так что отвалится при отключении JavaScript.

                              Он навешивается с помощью условных комментариев:
                              <!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
                              <!--[if IE 7]>    <html class="no-js ie7 oldie" lang="en"> <![endif]-->
                              <!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en"> <![endif]-->
                              <!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->

                              Однако, в любом случае, это медленный CSS, который вынужденны хавать все браузеры. Я предпочитаю просто подключать отдельный CSS для IE.
                                0
                                Именно это и имелось ввиду. По желанию можно извратиться css хаками.
                          +1
                          Одного span недостаточно?
                          • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              Во-первых, решение с фоном зависит от высоты строки. Если высота иконки будет больше высоты строки, она будет обрезаться.

                              Вообще не понял. Ведь если нам надо будет затолкать иконку 20*20пк в строку, то в любом случае надо иметь строку высотой в 20пк. В случае с бэкраундом мы ее можем зафиксить в css, а здесь сама картинка растянет ее.
                              И второе. Пока background-position ни разу не подводил.
                                0
                                Поясните (желательно на примерах) последние два пассажа на счет высоты строки.
                                Что конкретно может поломаться при масштабировании, если у вас заданы line-height и background-position?
                                  –1
                                  А vertical-align: middle;?
                                    0
                                    Опередил :)
                                    0
                                    А чем плоха конструкция img + span?
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                        0
                                        А какие проблемы были с их использованием при такой конструкции?
                                      –1
                                      Это просто наркоманство какое то! Все гораздо проще:

                                      vertical-align:middle;


                                      для обоих элементов решает все проблемы. Без фиксов и прочего гемороя.
                                        0
                                        Элемент будет выровнен не по базовой линии шрифта, а по центру, относительно него.
                                          0
                                          vertical-align: baseline;
                                        0
                                        Поэтому если пользователь вдруг у себя в настройках изменил размер шрифта, то все ваше позиционирование тут же слетит к черту

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

                                        В конце концов, кому-то может прийти в голову увеличить базовый размер шрифт на 200% и как этому противостоять?
                                          +1
                                          > так как все иконки зашиты в спрайт, что не позволяет нам использовать обычные картинки.

                                          <img src="[path_to_spacer]" />

                                          img {
                                            width: ww;
                                            height: hh;
                                            background: url(sprite.png) no-repeat -xx -yy;
                                          }
                                            0
                                            Извините, но считаю крайне не эффективным использование трех элементов, если можно обойтись одним. А если подобных блоков user будет 30, то уже 60 лишних элементов.

                                            Вы заранее пытаетесь сделать результат слишком гибким и по-пиксельно идентичным.

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

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

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

                                            Делайте эффективно, незачем решать надуманные проблемы.
                                              +2
                                              > Взгляните ещё раз на результат, отображаемый браузером, так, будто вы никогда не видели макета от дизайнера. Плохо выглядит? Исправляйте. Иначе оставьте как есть.

                                              И получите потом письмо с длинным перечнем несоответствий вёрстки макету, которые требуется исправить…
                                              И поверьте, сразу сделать «как надо было» — значительно экономнее (в плане времени), нежели лазить потом по всему макету и исправлять такие вот «мелочи».
                                                0
                                                Речь шла о том, что мелкое несоответствие будет в каком-то конкретном браузере и никак не повлияет на пользователя. А исправление несоответствия только ухудшит верстку.

                                                Легче и быстрей объяснить заказчику, что оно того не стоит. Даже если он не согласится, у вас уже есть сэкономленный запас времени.
                                              0
                                              Чем плох такой вариант?

                                              Usename

                                              .user{ position:relative; padding-left:25px; }
                                              .user span{ position:absolute; left:0; top:-3px; width:20px; height:20px; }
                                                0
                                                Т.е. так)
                                                <div class="user"><span></span>Usename</div>

                                                .user{ position:relative; padding-left:25px; }
                                                .user span{ position:absolute; left:0; top:-3px; width:20px; height:20px; }

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

                                                Самое читаемое