HTML*, CSS*

Верстка для самых маленьких. Верстаем страницу по БЭМу

xnim 24 ноября 2013 в 20:20 264k
Недавно хабраюзер Mirantus написал статью «Как сверстать веб-страницу», в которой рассказывал о том, как же сверстать веб-страничку. В его статье было подробно рассмотрено, как выделить отдельные элементы из заданного шаблона, подобрать шрифты и т.п. Однако его подход к написанию, собственно, веб-страницы мне показался не очень хорошим, о чем я написал в комментариях.

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

  • BEM
  • Собственно пример — как сверстать страницу


BEM


БЭМ (Блок, Элемент, Модификатор) — методология разработанная внутри Яндекса предлагает следующую концепцию (если описать 1-2 предложениями):

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

Сайт методологии: ru.bem.info

Очень хорошее «руководство к действию» можно найти здесь: ru.bem.info/method/definitions

Предлагаю дать определения основным элементам:

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

Блок не отвечает за свое расположение. Он задает внутренние свойства (размеры, шрифты и т.д.)

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

Пример выделения блоков\элементов


Для примера возьмем блок «прямой эфир» на сайте Хабра:

Вот так он выглядит:



Если «организовать» его по методологии БЭМ, то данная часть будет являть собой блок, состоящий из элементов:



Соответственно, данный блок состоит из 3 разных типов элементов. (Здесь стоит сделать оговорку, что возможно сверстать с использованием только 1 типа элемента (который будет описывать только маргины)).

Элементы выделенные розовым цветом внутри себя содержат другой блок, назовем его «пост».

Соответственно, рассмотрим из чего состоит блок пост:



Таким образом, блок «пост» состоит из 5 элементов.

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

Теперь рассмотрим третью составляющую БЭМ — модификатор.
Модификатор может задавать как дополнительное поведение для блока\элемента, так и переопределять стандартное.
Самым ярким примером для понимания «модификатора» служит пример с кнопками.
Предположим, что в проекте используются кнопки типа:



Такая кнопка имеет некий padding слева и справа, шрифт и цвет background'а. Соответственно, мы представляем кнопку как .button и добавляем эти свойства ей.

Теперь, в проект на некоторых страницах необходимо добавить такие же кнопки, но, скажем, с background-color:red;
Решить данную задачу с использованием БЭМ можно очень просто:
Создать модификатор для блока: .button_red, для которой добавить заданное свойство.

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

Стиль описания БЭМ


БЭМ не декларирует «особого» стиля описания классов. Однако, де-факто используется следующий стиль:

1) Несколько слов в одном названии разделяются дефисом (например, блок main-page или my-super-main-list)
2) Элементы отделяются от блоков с использованием двух символов подчеркивания "__" (например, main-page__header или my-super-main-list__item)
3) Модификаторы отделяются одним символом подчеркивания "_" (например, main-page__header_strong или my-super-main-list_blue)

Префиксы


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

Например:
g- (global) префикс для глобальных классов. (Например, для задания невидимых элементов g-hidden.)
b- (block) префикс для выделения элементов, относящихся к структуре документа.
js- (JavaScript) префикс для выделения элементов для селекторов js.

Разметка страницы


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

Почему использование каскада — плохо?


У людей, впервые для себя открывших БЭМ может возникнуть вопрос — почему элементы описываются такими длинными цепочками? не проще ли:
.main-page
.main-page .header
и т.д.

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

.main-page
 		.header
 		.item
 			.article
 				.header
 				.text
 		.item
 		////


В этом случае .main-page .header будет применен не только к нужному .header, но и .item .article .header, что является ненужным. БЭМ предполагает уход от каскадных стилей (типа div .someClass li), которые:
1) повышают специфичность веб-страниц;
2) нарушают независимость блоков. (в отличие от БЭМ).

Именно поэтому, корректной структурой для БЭМ будет:


 	.main-page
 		.main-page__header
 		.main-page__item
 			.article
 				.article__header
 				.article__text
 		.main-page__item
 		////



Какие плюсы дает БЭМ?


0) Независимость блоков — за счет ухода от каскадности и отказа от описания в блоке «своего позиционирования»;
1) Повторяемость блоков — любой независимый блок можно повторять на любых страницах проекта. Возможно создать базу блоков, вследствие чего новые страницы будут создаваться подобно конструктору Лего;
2) Простота поддержки;
3) Структурированность кода.

Страница, сверстанная с использованием БЭМ может выглядеть больше, чем страницы, сверстанные без использования данной методологии, однако, представьте, если вы работаете с сайтом, где >20-30 уникальных страниц? В таком случае возможность повторного использования блоков и единая концепция позволяет:
1) намного быстрее принимать решения о модернизации страниц\блоков сайта;
2) уменьшает порог вступления в проект новых разработчиков.

Верстаем страницу


В качестве шаблона воспользуемся Corporate Blue.
Верстать будем главную страницу:



Изначально определим разметку страницы:



Здесь блок имеет 6 элементов.
По порядку:
1) Верхняя линия
2) Header
3) Меню
4) Слайдер
5) Main
6) Footer

Запишем эту структуру:

<!DOCTYPE html>
<html>
<head>
    <title>BEM-example</title>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="styles/style.css">    
</head>
<body class="b-page">
    <div class="b-page__head-line">
        
    </div>
    <div class="b-page__line">
        
    </div>
    <div class="b-page__line">
        
    </div>
    <div class="b-page__line">
        
    </div>
    <div class="b-page__line">
      
    </div>
    <div class="b-page__footer">
       
    </div>
</body>
</html>


Зададим стили для блока и элементов:

.b-page
{
    width:100%;
    margin:0;
    background-color:#f7f7f7;
    background:url("../img/bg.png");
    font-family:Oswald,Tahoma;
    font-size:12px;
}

    

.b-page__head-line
{
    background-color:#7e7e7e;
    height:5px;
    
    width:100%;
}

.b-page__line
{

    width: 960px;
    margin: 0 auto;
    margin-top:27px;    
}


Разберем header страницы


Он состоит из двух элементов:

1) Лого:



2) Форма поиска:



Выделим картинку и зададим HTML-структуру:

<div class="b-page__line">
        <div class="b-head">
            <div class="b-head__logo">
                <div class="b-logo">
                    <a href="#"><img class="b-logo__img" src="img/Logo.png"/></a>
                </div>
            </div>
            <div class="b-head__search">
                <div class="b-search">
                    <div class="b-search__input">
                        <input type="text" class="b-input b-input_search" placeholder="Search"> 
                    </div><div class="b-search__input"><div class="b-button">GO</div>
                    </div>
                </div>
            </div>
        </div>
    </div>



HINT: второй div .b-search__input располагаем «впритык» к концу первого. Если так не сделать, то между двумя элементами будет пустое место. Его можно убрать либо изменив отступы, либо убрав в html коде пробелы.

Соответствующие стили:

Для блока лого:

.b-head
{
    height:36px;
}
    

.b-head__logo
{
    float: left;    
}

Для поиска:

.b-head__search
{
    float:right;
}

.b-search__input
{
    display:inline-block;
}

.b-input
{
    background-color: #f3f3f3;
    border:1px solid #e7e7e7;    
}

.b-input_search
{
    height:32px;
    padding:0 10px;
    width:135px;
    border-right:none;
}
.b-button
{
    color:#fefefe;
    height:34px;
    padding:0 12px;
    line-height:33px;
    cursor:pointer;
    background-color:#29c5e6;
}

В данном блоке мы расположили элементы блока b-head слева и справа, а для input поиска задали стиль отображения inline-block.

Реакцию на работу кнопок\ссылок создавать не будем, так как цель статьи — показать на примере как можно верстать, используя БЭМ.

header готов.

Переходим к меню




Заметим, что каждый элемент можно представить как самостоятельный блок (состоящий только из себя) и являющийся ссылкой. Заведем сущность ссылка (b-link) и определим для нее обычный стиль и шрифт для ссылки:

.b-link
{
    color:#525252;
    font-size:12px;
        
}


Стили ссылок для меню определим в модификаторе .b-link_menu. Создадим HTML код:
<div class="b-page__line">
        <div class="b-menu">
            <a href="#" class='b-link b-link_menu b-link_menu_active' >HOME</a>
            <a href="#" class='b-link b-link_menu' >ABOUT US</a>
            <a href="#" class='b-link b-link_menu'>SERVICES</a>
            <a href="#" class='b-link b-link_menu'>PARTNERS</a>
            <a href="#" class='b-link b-link_menu'>CUSTOMERS</a>
            <a href="#" class='b-link b-link_menu'>PROJECTS</a>
            <a href="#" class='b-link b-link_menu'>CAREERS</a>
            <a href="#" class='b-link b-link_menu'>CONTACT</a>
        </div>
    </div>


Активный пункт меню выделим как .b-link_menu_active.

Остается только добавить стили и меню готово:
.b-menu
{
    margin:0;
    padding:0;
    list-style:none; 
    width:100%;
    display:table;
    table-layout: fixed;   

}

.b-link_menu
{
    text-align:center;
    color:#bfbfbf;
    cursor:pointer;
    font-size:14px;
    height:38px;
    background-color:#f3f3f3;
    line-height:38px;
    border: 1px solid #e7e7e7;
    display:table-cell;
    text-decoration: none;
}

.b-link_menu_active
{
    color:#fefefe;
    background-color:#29c5e6;
    border:1px solid #29c5e6;
}


HINT: Если стоит задача сверстать набор элементов на полную ширину родителя, причем все элементы должны занимать одинаковое место, то решением можно считать:
1) Родителю задаем display: table; table-layout:fixed;
2) Дочерним элементам задаем display: table-cell;
Такое моделирование таблицы позволяет успешно решить поставленную задачу.


Переходим к слайдеру:




Здесь текст на слайдере необходимо вывести на картинке. Соответственно, для этого можно использовать картинку (на которой написать вручную текст), а можно воспользоваться особенностью position, либо z-index.
Второй способ гласит, что элементы с position: relative\ absolute отрисовываются отдельно от «остальных» элементов. Соответственно, эти элементы будут отображены «выше» остальных.
Третий способ: z-index задается для сестринских элементов. Представляет собой отображение по оси 0Z. Соответственно, тот элемент, у которого z-index выше, будет перекрывать собой те, у которого z-index ниже.

Зададим HTML структуру:

<div class="b-page__line">
        <div class="b-slider">
            <div class="b-slider__current">
                <div class="b-slide">
                    <div class="b-slide__text">
                        <div class="b-slide-text">
                            <div class="b-slide__header  ">  
                                FUSCE VITAE NIBN QUIS DIAM FERMENTUM
                            </div>
                            <div class="b-slide__subtext">
                                Etiam adipscing ultricies commodo.
                            </div>
                        </div>                       
                    </div>
                    <div class="b-slide__image">
                        <img class="b-image b-image_slider" src="img/slider.png">
                    </div>
                </div>
            </div>
            <div class="b-slider__list">
                <ul class="b-slide-list">
                    <li class="b-slide-list__item">
                        <div class=b-slider-case-element>
                            <span class="b-slider-case-element__number b-slider-case-element__number_active">1</span>
                            <span class="b-slider-case-element__text b-slider-case-element__text_active">LOREM IPSUM DOLOP</span>
                        </div>
                    </li>
                    <li class="b-slide-list__item">
                        <div class=b-slider-case-element>
                            <span class="b-slider-case-element__number">2</span>
                            <span class="b-slider-case-element__text">ULTRICIES PELLENTESQUE</span>
                        </div>
                    </li>
                    <li class="b-slide-list__item">
                        <div class=b-slider-case-element>
                            <span class="b-slider-case-element__number">3</span>
                            <span class="b-slider-case-element__text">ALIQUAM IPSUM</span>
                        </div>
                    </li>
                    <li class="b-slide-list__item">
                        <div class=b-slider-case-element>
                            <span class="b-slider-case-element__number">4</span>
                            <span class="b-slider-case-element__text">NULLAM SED MAURIS UT</span>
                        </div>
                    </li>
                </ul>
            </div>
        </div>
    </div>


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

CSS:
.b-slide__text
{
    position:relative; 
    top:40px;
    left:35px;
    z-index:10;
}

.b-slide__header
{
    font-size:42px;
    color:#5a5a5a;    
}

.b-slide__subtext
{
    font-size:20px;
    color:#b0b0b0;
}

.b-slide__image
{
    z-index: 0;
    margin-top:-62px;
}

.b-slider__list
{
    height:50px;
    background-color:#f3f3f3;
    width:100%;
    margin-top:-2px;
    border-bottom: 1px solid #e7e7e7;
}

.b-slide-list
{
    margin:0;
    padding:0;
    list-style:none; 
    width:100%;    
    display:table;
    table-layout:fixed;    
    font-size:16px;
    color:#8f8f8f;
}

.b-slide-list__item
{
    display:table-cell;
}

.b-slider-case-element__number
{
    text-align:center;
    display:inline-block;
    width:22px;
    margin-left:12px;
    margin-top:12px;
    height:22px;
    background-color: #8f8f8f;
    color:#f3f3f3;
}

.b-slider-case-element
{
    cursor:pointer;
}

.b-slider-case-element__number_active
{
    background-color:#29c5e6;
    color:#fefefe;
}

.b-slider-case-element__text_active
{
    color:#29c5e6;
}


Main-блок


Основной блок состоит из 6 элементов, каждый из которых содержит блок, наполненый:
а) картинками
б) текстом
в) ссылками



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

.b-main__item
{
    float:left;
    margin-left:10px;
    width:460px;
}

.b-main__item_small
{
    width:300px
}

.b-main__item_long
{
    width:920px;
}

Отдельно взятый блок будет выглядеть так:

  <div class="b-company">
                    <div class="b-company__header">
                        <div class="b-item-head">
                            <div class=b-item-head__img>
                                <img  src="img/sq.png">
                            </div><div class="b-item-head__text">ABOUT WHITESQUARE</div>                         
                        </div>
                    </div>
                    <div class="b-company__content">
                        <div class="b-content">
                            <img class="b-content__img" src="img/middle.png"/>
                            <div class="b-content__text">
                                In ultricies pellentesque massa a porta. Aliquam ipsum enim, hendrerit ut porta nec, ullamcorper et nulla. In eget mi dui, sit amet scelerisque nunc. Aenean augue arcu, convallis ac vulputate vitae, mollis non lectus. Donec hendrerit lacus ac massa ornare hendrerit sagittis lacus tempor. Vivamus et dui et neque accumsan varius et ut erat. Enean augue arcu, convallis ac ultrices. 
                            </div>
                            <div class="b-content__link">
                                <p><a href="#" class="b-link">Read more</a></p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

Со стилями:

.b-item-head__img
{
    display:inline-block;
    background: url("../img/bg.png");
    padding:2px;
    
}

.b-item-head__text
{
    display:inline-block;
}

.b-item-head
{
    line-height:16px;
    height:19px;
    background:url('../img/hbg.png') repeat;
}


.b-item-head__text
{
    display:inline;
    font-size:16px;   
    color:#8f8f8f;
    padding:3px;    
    padding-right:10px;  
    background: url("../img/bg.png");
}

.b-company
{
    width:100%;
}

.b-company__content
{
    margin-top:15px;
}
.b-content__img
{
    float:left;
    margin-right:20px;
}
.b-content__text
{
    color:#8f8f8f;
    font-size:12px;
}
.b-link,.b-content__link
{
    color:#525252;
    font-size:12px;
        
}


Аналогично отображаются другие блоки. (Меняем стили только внутренних элементов)

Переходим к footer


footer состоит из трех расположенных последовательно вложенных блоков:
1) Twitter
2) SiteMap
3) Social Networks
И четвертого, выровненного по правому краю — логотипу:


Разберем верстку каждого из этих блоков:

Twitter


Состоит из времени, заголовка и текста.

<div class="b-twitter">
           <div class="b-twitter__header  ">
                            TWITTER FEED
           </div>
           <div class="b-twitter__time">
                            22 oct
          </div>
          <div class="b-twitter__text">
                         In ultricies pellentesque massa a porta. Aliquam ipsum enim, hendrerit ut porta nec, ullamcorper et nulla. In eget mi dui, sit amet scelerisque nunc. 
          </div>
</div>


В стилях задаем размер блока, цвета и прочие стили:

.b-twitter
{
    width:300px;
}

.b-twitter__header,.b-sitemap__header,.b-networks__header
{
    color:#fff;
    font-size:14px;
    width:100%;
    border-bottom:1px solid #878787;
}

.b-twitter__time
{
    margin-top:5px;
    font-size:11px;
    color:#b4aeae;
    text-decoration:underline;
}

.b-twitter__text
{
    margin-top:5px;
    font-size:11px;
    color:#dbdbdb;
}


Здесь же задали и стили заголовков для остальных блоков (т.к. они одинаковы)

Sitemap


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

Эффект 2 колонок получаем за счет указания размера блока + одной группе ссылок задаем float:left, другой float: right:

<div class="b-sitemap">
                    <div class="b-sitemap__header  ">
                        SITEMAP
                    </div>
                    <div class="b-sitemap__links">
                        <a href="#" class="b-link b-link_white b-link_block b-link_undecorate">Home</a>
                        <a href="#" class="b-link b-link_white b-link_block b-link_undecorate">About</a>
                        <a href="#" class="b-link b-link_white b-link_block b-link_undecorate">Services</a>
                    </div>
                    <div class="b-sitemap__links_right">
                        <a href="#" class="b-link b-link_white b-link_block b-link_undecorate">Partners</a>
                        <a href="#" class="b-link b-link_white b-link_block b-link_undecorate">Support</a>
                        <a href="#" class="b-link b-link_white b-link_block b-link_undecorate">Contact</a>
                    </div>
                </div>


CSS:

.b-sitemap__links
{
    float:left;
}

.b-link_white
{
    color:#dbdbdb;    
}

.b-link_undecorate
{
    text-decoration:none;
}

.b-link_block
{
    margin-top:8px;
    display:block;
}

.b-sitemap__links_right
{
    float:right;
    margin-right:20px;
}


Social


Здесь воспользуемся спрайтами. (Спрайт -картинка, которая содержит набор других картинок) Соответственно, с помощью Background-image + background-position для каждого элемента блока выбираем необходимые части картинки:


<div class="b-networks">
                   <div class="b-networks__header  ">
                        SOCIAL NETWORKS
                    </div>
                   <div class="b-networks__icon b-networks__icon_twitter"></div> 
                   <div class="b-networks__icon b-networks__icon_facebook"></div> 
                   <div class="b-networks__icon b-networks__icon_google"></div> 
                   <div class="b-networks__icon b-networks__icon_small  b-networks__icon_vimeo"></div> 
                   <div class="b-networks__icon b-networks__icon_small  b-networks__icon_youtube"></div> 
                   <div class="b-networks__icon b-networks__icon_small  b-networks__icon_flickr"></div> 
                   <div class="b-networks__icon b-networks__icon_small  b-networks__icon_instagram"></div> 
                   <div class="b-networks__icon b-networks__icon_small  b-networks__icon_rss"></div> 
               </div>


.b-networks__icon
{
    background:url("../img/social.png") 0 0;
    width:30px;
    height:30px;
    display: inline-block;
    margin-top:10px;
    margin-right:10px;
}

.b-networks__icon_small
{
    width:16px;
    height:16px;
    margin-right:5px;
}

.b-networks__icon_twitter
{
    background-position:0 0;
}

.b-networks__icon_facebook
{
    background-position: -40px 0;
}

.b-networks__icon_google
{
    background-position:-80px 0;
}

.b-networks__icon_vimeo
{
    background-position:0 -38px;
}

.b-networks__icon_youtube
{
    background-position: -23px -38px;
}

.b-networks__icon_flickr
{
    background-position: -46px -38px;
}

.b-networks__icon_instagram
{
    background-position: -69px -38px;
}

.b-networks__icon_rss
{
    background-position: -92px -38px;
}


Малые иконки от «стандартных» отличаются только модификатором, задающим размер.

Footer logo


Для лого в футере странице воспользуемся уже готовым блоком b-logo. Вставим данный блок внутрь элемента, который будет отформатирован с помощью float:right;

<div class="b-footer-logo">
                    <div class="b-footer-logo__img">
                        <div class="b-logo">
                            <a href="#"><img class="b-logo__img" src="img/fooLogo.png"/></a>
                        </div>
                    </div>
                    <div class="b-footer-logo__copyright">Copyright  2012 Whitesquare. A <a href="#" class="b-link b-link_small b-link_white">pcklab</a> creation
                    </div>
                </div>


.b-footer__logo
{
    float:right;
    margin-top:23px;
    margin-right:40px;
}

.b-footer-logo__img
{
    float:right;
}

.b-footer-logo__copyright
{
    clear:right;
}

.b-link_small
{
    font-size:10px;    
}

.b-footer-logo__copyright
{
    font-size:10px;
    color:#dbdbdb;
}


Подведем итог


В данном обучающем посте мной была поставлена задача показать, что использование методологий (таких как БЭМ) позволяет упростить разработку веб-страниц за счет «единого словаря терминологий» и единой структуры страницы.
Также, данный пост является дополнением моего комментария (в начале поста) в котором я говорил о том, почему использовать стили на id — есть bad practices.

Соответственно, ссылка на git-pages и github:
xnimorz.github.io/ex-habr
github.com/xnimorz/ex-habr

Благодарю за внимание!
Проголосовать:
+112
Сохранить: