22 February 2009

Танцы с бубном для блондинок, или о tabbed menus и хитрой работе с графикой

CSS
Собственно, перепал мне не так давно небольшой заказец. Вроде и несложный — а потанцевать с бубном немного пришлось. А всё из-за того, что заказчик оказался немного «падкой на дизайн блондинкой» (образно говоря), и требовал строгого соответствия конечного результата макету. Требовал попиксельно, и его совершенно не волновали такие вопросы, как валидность, семантичность и всё такое. «Хоть таблицами сверстай, а сделай». И сегодня речь пойдёт о том, как в таких условиях сделать весьма хитровыделанно нарисованное меню табами. Как и за свой код не устыдиться, и не пасть в грязь лицом перед заказчиком, вот в чём вопрос?

Собственно, вот фрагмент эскиза, отвечающий за то самое меню:
image

А вот как оно должно выглядеть, когда выбран другой пункт:
image

Неудобно, правда? Мало того, что не получится создать единый background для пунктов меню (из-за этого несчастного градиента и не только), а придётся назначать стили каждому пункту индивидуально (пришлось взять с заказчика честное пионерское слово, что структура меню впредь останется неизменной), так ещё и приходится думать о том, как правильно отобразить «нахлёсты» на другие пункты до и после активного. Но нет ничего невозможного, и для реализации подобной вещи никаких таблиц нам не понадобится, и «костылей» будет минимум. Вся суть — в хитрой работе с графикой.

Но перед тем, как стилизовать меню, нам нужно позаботиться о блоке под ним. Задаём ему однопиксельный border (тот самый, который мы видим под неактивными пунктами) и бэкграунд в виде «уголка» в левом верхнем углу. Мы не будем думать о том, как этот «уголок» отображать или нет в самом меню — пусть он лежит и кушать не просит в блоке под ним, а в случае необходимости мы его просто закроем.

Теперь для каждого пункта меню мы должны нарезать по две картинки — соответственно, неактивный и активный пункт меню. А для всех пунктов меню, кроме первого, мы должны ещё предусмотреть вариант «неактивный-после-активного», у которого слева отсутствует нижняя кромка. Собственно, вот первый пункт меню в неактивном виде:
image

А вот он же, но активный:
image

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

С другими же пунктами — всё чуть сложнее. Разберём на примере второго пункта меню. Неактивный пункт делается по тому же принципу, как и первый:
image

«Неактивный-после-активного» отличается от него только отсутствием нижнего бордера слева:
image

А для активного пункта меню мы должны предусмотреть «захлёст» слева, чуть «оттяпав» от предыдущего пункта:
image

Теперь, нарезав графику, принимаемся за код. Учитывая, что реализовано это меню в конечном итоге на Drupal с помощью модуля menu-attributes (или как его там), а соответственно, это накладывает свою специфику, то стилизовать мы будем не тэг li, а вложенные в него ссылки. Сам HTML-код за вычетом CMSного мусора у нас вот такой:

<div id="topnav">
<ul class="menu"><li class="leaf first active-trail "><a href="#" id="apartments" title="Жилые квартиры">Квартиры</a></li>
<li class="leaf"><a href="#" id="houses" title="Частные дома, коттеджи, дачные постройки">Дома и дачи</a></li>
<li class="leaf"><a href="#" id="ground" title="Участки под индивидуальное строительство">Земельные участки</a></li>
<li class="leaf last"><a href="#" id="business" title="Склады, офисы и т.п.">Промышленная недвижимость</a></li>
</ul>
</div>


Как видите, практически ничего лишнего, и не нарушена семантика. Все «гламурности» и «рюшечки» мы сделаем в CSS. У нас будет два файла стилей — под нормальные браузеры и под ослика IE (надеюсь, мне не надо вас учить, как подключать этот файл через условные комментарии). Сразу оговорю, что под «осликом IE» имею в виду седьмую версию — ибо ввиду массового распространения Висты и третьего сервиспака под WinXP шестой уже сдаёт свои позиции… so screw this nightmare. :) В любом случае, даже в шестом «ослике» вёрстка не поедет, и из функционала меню не сработает разве что реализованный на селекторах «неактивный-после-активного». Так что пофигу. Поехали!

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

div#topnav ul.menu {
list-style-type: none;
margin: 0 0 -1px 0;
line-height: 44px;
font-size: 0; /* это чтобы переводы строки между пунктами не отображались как пробелы */
padding-left:1px
}
div#topnav ul.menu li {
display: inline;
}
div#topnav ul.menu a {
font-size: 14px;
padding: 15px 24px 15px 15px;
height: 25px;
margin-left:-1px;
}


Сразу же — поправляемя всю эту кухню в таблице для IE (ибо есть свои нюансы отображения)

div#topnav ul.menu {
position: relative;
top: 3px;
list-style-type: none;
margin: 0px;
line-height: 44px;
font-size: 0;
padding-left: 1px;
}
div#topnav ul.menu li {
display: inline;
}
div#topnav ul.menu a {
font-size: 14px;
margin: 0 0 0 -1px;
padding: 15px 24px 15px 15px;
height: 25px;
}


Теперь прописываем бэкграунды и размеры для активных и неактивных пунктов меню. Алгоритм такой: для первого пункта увеличиваем высоту, если пункт активен (закрываем уголок). Прочие пункты, если активны, сдвигаем на 6 пикселей влево (помним про нахлёсты).

div#topnav ul.menu li a#apartments {
width: 80px;
background: url('images/menu-1-inactive.gif') top left no-repeat;
}
div#topnav ul.menu li a#houses {
width: 94px;
background: url('images/menu-2-inactive.gif') top left no-repeat;
padding-right:26px;
}
div#topnav ul.menu li a#ground {
width: 150px;
background: url('images/menu-3-inactive.gif') top left no-repeat;
padding-right:26px;
}
div#topnav ul.menu li a#business {
width: 240px;
padding: 15px 60px 15px 15px;
background: url('images/menu-4-inactive.gif') top left no-repeat;
}
div#topnav ul.menu li.active-trail a#apartments {
background: url('images/menu-1-active.gif') top left no-repeat;
padding-bottom: 25px;
}
div#topnav ul.menu li.active-trail a#houses {
width: 101px;
margin-left: -6px;
padding-left: 22px;
background: url('images/menu-2-active.gif') top left no-repeat;
}
div#topnav ul.menu li.active-trail a#ground{
width: 157px;
margin-left: -6px;
padding-left: 22px;
background: url('images/menu-3-active.gif') top left no-repeat;
}
div#topnav ul.menu li.active-trail a#business {
width: 247px;
margin-left: -6px;
padding-left: 22px;
background: url('images/menu-4-active.gif') top left no-repeat;
}


Ну и теперь — используем магию селекторов CSS для того, чтобы сменить картинку тому неактивному пункту меню, который идёт после активного:

/* this is not supported in IE 6, so screw IE6, we want some CSS2 beauty*/
div#topnav ul.menu li.active-trail+li>a#houses{
background: url('images/menu-2-inactive-after-active.gif') top left no-repeat;
}
div#topnav ul.menu li.active-trail+li>a#ground{
background: url('images/menu-3-inactive-after-active.gif') top left no-repeat;
}
div#topnav ul.menu li.active-trail+li>a#business{
background: url('images/menu-4-inactive-after-active.gif') top left no-repeat;
}


И вот, уже скриншот с реальной страницы в браузере:
image

Собственно, всё. И это с минимальными изменениями можно экстраполировать на любую похожую задачу. Главное — запомнить алгоритм. А если интересно — в соответствующем блоге расскажу про то, как всё это грамотно и логично реализовать на внутренней структуре Drupal Раз уж интересно — то вот как оно на Друпале реализуется: habrahabr.ru/blogs/drupal/52601 ;)
Tags:tabbed menucss-селекторыcss-хакиweb-графикаtutorial
Hubs: CSS
+51
1.2k 84
Comments 99
Web-программист
from 60,000 to 100,000 ₽V&CoМосква
Senior JS developer on Block-Based editor
from 3,000 to 3,500 $Nimbus WebRemote job
Web-разработчик (PHP)
from 30,000 to 40,000 ₽OpenTrade CommerceRemote job
Top of the last 24 hours