Три колонки. Одна, фиксированной ширины, для элементов навигации, другая для Google Ads или ваших фотографий на Flickr, и изменяемой ширины центр для содержания сайта. Такая разметка широко применяется в наш золотой век блогинга, а учитывая сложность такой разметки она получила название Святой Грааль.

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

В последнем проекте я закончил свои поиски Грааля. Техника, которую я собираюсь показать, позволяет вам создавать разметку Святого Грааля без компромиссов в коде или гибкости.

Ее достоинства:

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

На плечах гигантов

Техника, представленная здесь, была создана под впечатлением блестящей статьи Алекса Робинсона One True Layot

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

Также были сделаны выводы из статьи Эрика Майера Adaptation который использовал различные типы единиц измерения. Его пример также содержит три колонки, фиксированные боковые панели и центра переменной ширины.

Достаточно слов — давайте код

Используемый HTML код выглядит интуитивно и элегантно.

(Для ясности, при демонстрации этой техники, умышленно используются не семантические идентификаторы center, left, и right. Мы рекомендуем использовать семантические идентификаторы при применении этой техники — Ред.)

<div id="header"></div>

<div id="container">
    <div id="center" class="column"></div>
    <div id="left" class="column"></div>
    <div id="right" class="column"></div>
</div>

<div id="footer"></div>

Один дополнительный div охватывающий колонки это все, что нам нужно и это соответствует моим привычкам написания кода.

Стили почти такие же простые. Представим, что нам нужно, чтобы ширина левой колонки была 200 пикселей, а правой 150 пикселей. Чтобы упростить комментарии, я буду называть левую, правую и центральную колонки соответственно LC, RC, CC. Необходимый код CSS выглядит так:

body {
    min-width: 550px; /* 2x LC width + RC width */
}

#container {
    padding-left: 200px; /* LC width */
    padding-right: 150px; /* RC width */
}

#container .column {
    position: relative;
    float: left;
}

#center {
    width: 100%;
}

#left {
    width: 200px; /* LC width */
    right: 200px; /* LC width */
    margin-left: -100%;
}

#right {
    width: 150px; /* RC width */
    margin-right: -150px; /* RC width */
}

#footer {
    clear: both;
}

/*** IE6 Fix ***/
* html #left {
    left: 150px; /* RC width */
}

Измените размеры, и Грааль ваш. Эта техника работает во всех современных браузерах: Safari, Opera, FireFox и IE6 (с небольшим хаком). Для работы в IE5.5 необходимо добавить, box-model хак, который останется как упражнение читателям.

Святой Грааль в действии

Как это работает

Стратегия проста, div, который включает в себя колонки, имеет центр переменной ширины и фиксированные отступы по краям. Вся хитрость в том, чтобы разместить левую колонку в левом отступе, а правую в правом, после чего останется добавить центральную колонку переменной ширины.

Давайте проследим за созданием разметки, шаг за шагом.

Шаг 1: Создание фрейма

Начнем с заголовка, футера и контейнера.

<div id="header"></div>

<div id="container"></div>

<div id="footer"></div>

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

#container {
    padding-left: 200px; /* LC width */
    padding-right: 150px; /* RC width */
}

Сейчас разметка выглядит так:

Шаг 2: Добавляем колонки

Когда у нас есть базовый фрейм, мы можем добавить к нему колонки.

<div id="header"></div>

<div id="container">
    <div id="center" class="column"></div>
    <div id="left" class="column"></div>
    <div id="right" class="column"></div>
</div>

<div id="footer"></div>

Дальше нам нужно присвоить им ширину и установить float, чтобы они выстроились в одну линию. Также нам нужно установить clear для футера чтобы он оставался под колонками, у которых установлено свойство float.

#container.column {
    float: left;
}

#center {
    width: 100%;
}

#left {
    width: 200px; /* LC width */
}

#right {
    width: 150px; /* RC width */
}

#footer {
    clear: both;
}

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

Сейчас колонки готовы выстроиться в линию, но поскольку центральная колонка занимает 100% доступного места, левая и правая колонки оказываются под ней.

Шаг 3: Размещаем левую колонку.

Все, что нам осталось сделать это выстроить колонки в одну линию, переместив их в отступы элемента container. Центральная колонка находиться именно там где нам нужно, поэтому сфокусируемся на левой колонке.

Сначала нам нужно разместить ее перед центральной колонкой, для этого мы установим левое поле −100%. Вспомните, что 100% это ширина элемента container, которая также равна ширине центральной колонки.

#left {
    width: 200px; /* LC width */
    margin-left: -100%;
}

Теперь левая колонка перекрывает центральную колонку, начиная с ее левой границы. Правая колонка с установленным свойством float: left должна расположиться около правого края центральной колонки (но продолжает переноситься).

Сейчас наша разметка выглядит так:

Чтобы разместить левую колонку в отступе элемента container, используем относительное позиционирование со смещением равным ширине левой колонки.

#container.columns {
    float: left;
    position: relative;
}

#left {
    width: 200px; /* LC width */
    margin-left: -100%;
    right: 200px; /* LC width */
}

Свойство right перемещает колонку на 200 пикселей от правого края, то есть влево. Сейчас левая колонка разместилась точно над левым отступом элемента контейнер.

Шаг 4. Размещаем правую колонку

Последнее что осталось сделать это переместить правую колонку на ее место. Все что нам нужно чтобы сделать это вынести ее из контейнера в его отступ. Снова используем отрицательные поля.

#right {
    width: 150px; /* RC width */
    margin-right: -150px; /* RC width */
}

Теперь все элементы находятся на своих местах.

Шаг 5: Надежность

Если окно браузера уменьшается настолько, что центральная колонка становиться меньше левой, разметка ломается (в браузерах поддерживающих стандарты). Свойство min — width для элемента body сохранит расположение колонок. Но к несчастью IE 6 не поддерживает свойство min — width.

body {
    min-width: 550px; /* 2x LC width + RC width */
}

Конечно ни одна техника разметки не может считаться полной если не использует заплаток и обходных путей для IE. Отрицательные поля сдвигают левую колонку слишком далеко в IE 6 (на полную ширину окна браузера). Чтобы вернут ее обратно нам необходимо подвинуть ее вправо на расстояние равное ширине правой колонки. Используем star-html хак чтобы скрыть код от других браузеров.

* html #left {
    left: 150px; /* RC width */
}

Я не хочу вдаваться в детали, почему нужно использовать именно ширину правой колонки, объяснение этого факта не представляет сложности, вы можете провести эти вычисление самостоятельно или просто считать это еще одним сюрпризом IE.

Отступы

Я не дизайнер, но вид такой разметки угнетает мое чувство прекрасного. Колонки без отступов плохо смотрятся, и усложняют чтение. Нам нужно добавить пространство.

Один из недостатков использования процентов в One True Layout для создания резиновой разметки в том, что создание отступов для колонок становиться несколько мудреным. Процентные отступы плохо смотрятся на некоторых разрешениях экрана, а фиксированные отступы можно добавить, только используя дополнительный блок внутри каждой колонки.

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

#left {
    width: 180px; /* LC fullwidth - padding */
    padding: 0 10px;
    right: 200px; /* LC fullwidth */
    margin-left: -100%;
}

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

Отступы вместе со 100%-ной шириной приводят к тому, что центральная колонка расширяется за пределы отступов контейнера. Чтобы предотвратить это, мы должны увеличить правый отступ контейнера на суммарную величину отступов центрального элемента. Это гарантирует, что центральная колонка будет именно такого размера как мы ожидали.

Также поскольку теперь центральная колонка стала шире, левую колонку нужно сдвигать на большее расстояние чтобы она оказалась на своем месте. Увеличение смещения на суммарную величину отступов центральной колонки не представляет труда.

Чтобы конкретизировать сказанное. Я изменил пример добавив отступ в 10 пикселей для боковых колонок (в сумме 20 пикселей) и 20 пикселей с каждой стороны центральной (в сумме 40 пикселей). Новый код выглядит так:

body {
    min-width: 630px;     /* 2x (LC fullwidth +
                             CC padding) + RC fullwidth */
}

#container {
    padding-left: 200px;  /* LC fullwidth */
    padding-right: 190px; /* RC fullwidth + CC padding */
}

#container.column {
    position: relative;
    float: left;
}

#center {
    padding: 10px 20px;   /* CC padding */
    width: 100%;
}

#left {
    width: 180px;         /* LC width */
    padding: 0 10px;      /* LC padding */
    right: 240px;         /* LC fullwidth + CC padding */
    margin-left: -100%;
}

#right {
    width: 130px;         /* RC width */
    padding: 0 10px;      /* RC padding */
    margin-right: -190px; /* RC fullwidth + CC padding */
}

#footer {
    clear: both;
}

/*** IE Fix ***/
* html #left {
    left: 150px;          /* RC fullwidth */
}

Конечно отступы сверху и снизу могут быть добавлены без затруднений. Полная версия шаблона с отступами.

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

Колонки одинаковой высоты

Эта техника позволяет легко создавать колонки одинаковой высоты. Я использую адаптированный метод из One True Layout, не хочу вдаваться в детали его функционирования, чтобы создать колонки одинаковой высоты просто используйте следующий код:

#container {
    overflow: hidden;
}

#container.column {
    padding-bottom: 20010px;        /* X + padding-bottom */
    margin-bottom: -20000px;  /* X */
}

#footer {
    position: relative;
}

Я также добавил отступ 10 пикселей внизу колонок.

Хочу вас предостеречь. Будьте осторожны Opera 8 имеет баг при работе с overflow: hidden который оставляет ваши колонки огромными. Способ преодоление этой проблемы рассмотрен в статье One True Layout, вы можете использовать его или подождать выхода Opera 9 в которой этот баг исправлен.

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

Если на вашей странице такая проблема проявляется, не бойтесь, это тоже исправимо, но потребуется один дополнительный div.

Добавьте footer-wrapper как показано ниже:

<div id="footer-wrapper">
<div id="footer"></div>
</div>

Чтобы footer — wrapper занимал все оставшееся место в окне браузера, используйте тот же способ, что и для колонок.

* html body {
    overflow: hidden;
}

* html #footer-wrapper {
    float: left;
    position: relative;
    width: 100%;
    padding-bottom: 10010px;
    margin-bottom: -10000px;
    background: #fff; /* Same as body background */
}

Пример с колонками одинаковой высоты и решение этой проблемы вы можете увидеть здесь.

Да, и еще одна вещь

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

Поэтому, как бонус, я представляю вам Святой Грааль без дополнительного div`а во всей красе его минимализма. Один div для каждой секции кода, не больше не меньше. Семантическое блаженство почти достойное титула «Святой Грааль».

Принципы заложенные в CSS те же самые, что и в разметке представленной выше. Отступы добавленные к элементу body, делают любые дополнительные контейнеры излишними. Отрицательные поля растягивают header и footer чтобы гарантировать что они занимают все предоставленное пространство.

Эта версия работает во всех вышеупомянутых браузерах, включая (почти чудо) IE. Колонки одинаковой высоты, конечно, не работают, и эта разметка ломается в окне очень маленькой ширины. Используйте с осторожностью.

Что дальше?

Пока это применение Святого Грааля остается специфичным, техника может быть сильно модифицирована. Почему нет двух резиновых колонок? Почему нельзя изменять порядок колонок? Эти вопросы лежат за пределами этой статьи, но достижимы без значительных изменений кода. Используйте Грааль разумно, и он может быть очень удобным (и надежным) дополнением вашего набора трюков CSS.

Translated with the permission of A List Apart Magazine and the author[s].

Дело исчезающей колонки

В последней версии разметки, мы преодолели проблему в FireFox `е (увеличением минимальной ширины).

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

Я счастлив сообщить вам, что проблема решена.

Решение

Замените эти правила:

#left {
    width: 200px; /* LC width */
    right: 200px; /* LC width */
    margin-left: -100%;
}

* html #left {
    left: 150px; /* RC width */
}

на эти:

#left {
    width: 200px; /* LC width */
    margin-left: -100%;
    left: 150px; /* RC width for IE6 */
}

#container > #left {
    left: -200px; /* -LC width for others */
}

в случае Грааля с отступами, используйте следующий код:

#left {
    width: 180px; /* LC width */
    padding: 0 10px; /* LC padding */
    margin-left: -100%;
    left: 150px; /* RC fullwidth for IE6 */
}

#container > #left {
    left: -240px; /* -(LC fullwidth + CC padding) */
}

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

Дело исчезающей колонки на английском

Примечание переводчика

Опытным путем установлено, что для корректного отображения этой разметки в IE 6 должен быть установлен DOCTYPE XHTM 1.0 и body {margin: 0;} обычно это стандартные установки.