Website development
25 April 2009

«Резина» (fluid grids)

Original author: Этан Маркотте (Ethan Marcotte)
Translation
image

В этом году я делал редизайн веб-сайта с большим количеством контента. Требования к дизайну были простые: клиент попросил сохранить существующий логотип компании, улучшить плотность печати и увеличить читабельность. Так что, в самом начале разработки дизайна, я потратил значительное количество времени на планирование хорошо структурированной сетки для библиотеки информационных блоков.
Последние несколько лет такой образ мышления стал более распространенным. Благодаря Марку Балтону (Mark Boulton), Кхои Винху (Khoi Vinh), и другим, мы видим возрождение интереса к типографской сетке, и того, как использовать ее в сети. И, откровенно, идея была сногсшибательным хитом: миллионы CSS фреймворков расцвели множеством дополняющих их инструментов, каждый из которых создан для того, чтобы сделать основанный на сетке дизайн еще более доступным для среднестатистического дизайнера/верстальщика. И почему бы и нет? После нескольких минут мышления в категориях сетки достоинства становятся очевидны: дизайнеры получают рациональный, структурированный фреймворк для образования структуры информации и пользователи получают хорошо структурированные, читабельные сайты.
Между тем, наш клиент выдвинул еще одно, убойное, требование: дизайн должен быть тянущимся и изменять размеры вместе с окном браузера. Обычно это заставило бы меня шумно и смущенно радоваться. «Резина» — недооцененная парадигма в веб-дизайне. Она отдает контроль над дизайном в руки пользователей и их привычек веб-серфинга. А еще ее возможности абсолютно не соответствуют фантазии веб-дизайнеров.


Минимальное разрешение экрана: маленькая невинная ложь.


Вместо того, чтобы исследовать достоинства «резины», мы полагаемся на меленькую невинную ложь: «минимальное разрешение экрана». В этих трех словах содержится могущественная магия, под покровом которой мы создаем дизайны с фиксированной шириной, один за другим, возможно лишь увеличивая ширину дизайна каждые несколько лет, когда это становится безопасным. «Минимальное разрешение экрана» позволяет ограниченному подмножеству пользователей видеть сайта таким, как предполагает бог и Photoshop. Эти пользователи всегда просматривают сайты с развернутым на весь экран окном браузера и разрешением 1024х768, и никогда не используют, скажем, OLPC laptop, или мониторы старше четырех лет отроду. Если пользователь не соответствует требованиям «минимального разрешения экрана», ну, для них есть полоса прокрутки, верно?
Конечно, когда я кодил сайт, мне было не до роскоши написания резких обличительных речей на тему недостатков дизайна с фиксированной шириной. Вместо этого я остался наедине с отрезвляющим фактом: пока мы разрабатывали достаточно сложную сетку, чтобы удовлетворить запросы контента клиента, клиент — и в дополнение ко всему пользователи — хотели «резину». Почти все основанные на сетке дизайны, которые я могу перечислить, обладали жестко заданной шириной, и я остался со сложным вопросом: как сделать «резину»?
Как оказалось, это просто вопрос контекста.

Я действительно должен быть благодарен IE?


Столкнувшись с непреодолимой проблемой, я сделал то, что у меня получается лучше всего: обошел ее. Временно отложив в сторону вопрос «как» заставить сетку вести себя как «резина», я делал то, что умел: сначала стили для цвета и фона, затем определил шрифт.
Вы можете уже знать о хорошо документированной проблеме IE с изменением размера шрифтов, если размер определен в пикселях — или, скорее, решительно отказывается это делать. Установите шрифт размером 16 пикселей Georgia и, неважно как пользователь старается увеличить или уменьшить размер текста, в IE он останется размером в 16 пикселей. IE7 и старше позволяет пользователю масштабировать всю страницу, но простое изменение размера шрифта, размер которого определен в пикселях, в IE по-прежнему табу. Так что, чтобы дать пользователям больше гибкости, мы, стандарто-мыслящие дизайнеры предпочитаем полностью обходится без пикселей, и использовать для определения размера шрифта относительные единицы, будь то ключевые слова, проценты или мои любимые, em.
Если вы когда-нибудь работали с относительными единицами измерения, такими как em, вы понимаете, что все дело в контексте: другими словами, реальный размер em вычисляется относительно размера шрифта родительского элемента. Например, скажем, мы работаем со следующим макетом:

image
Пример простого определения размера текста с помощью пикселей.

Ничего смешного: параграфы определены как 16px Helvetica, несортированный список немного уменьшен до 14px и h1 увеличен до 24px Georgia. Сексуальненько? Нет?
И что вдвойне сексуальненько, это то, что одно простое правило позволяет нам получать все это сразу:
  1. body{
  2.     font: normal 100% Helvetica, Arial, sans-serif;
  3. }

С размером шрифта 100%, размер всех элементов на нашей странице определен относительно установленного в браузере по умолчанию размера шрифта, который в большинстве случаев 16px. Благодаря таблице стиле браузера, h1 большой, полужирный, красивый — но все еще Helvetica и слишком большой. Для того чтобы решить проблему достаточно просто с помощью font-family убрать Helvetica, но как изменить размер шрифта текста до 24px? Или точно уменьшить размер шрифта списка?
С em это сделать просто. Мы берем целевое значение font-size для каждого элемента и делим на размер шрифта (font-size) его контейнера (это и есть его контекст). В результате мы получаем желаемый размер шрифта (font-size) выраженный в относительных единицах (em). Или, короче говоря:

target ÷ context = result

Если предположить, что размер шрифта тега body по умолчанию 16px, мы можем описать этой формулой любое нужное нам значение размера шрифта (font-size). Так что, чтобы получить требуемое соответствие размера заголовка нашим вычислениям, мы делим целевое значение (24px) на размер шрифта (font-size) контейнера (16px):

24 ÷ 16 = 1.5

Заголовок в 1,5 раза больше размера шрифта body, или 1,5em, и это значение мы можем внести в таблицу стилей.
  1. h1 {
  2.     font-family: Georgia, serif;
  3.     font-size: 1.5em;        /* 24px / 16px = 1.5em */
  4. }

Что бы перевести размер списка в em эквивалент 14px, мы можем использовать ту же формулу. Предполагая, что размер шрифта (font-size) строго 16px, мы просто делим целевое значение на его контекст.

14 ÷ 16 = 0.875

И получаем значение 0.875em, которое, опять-таки, можно внести в CSS.
  1. ul {
  2.     font-size: 0.875em;      /* 14px / 16px = 0.875em */
  3. }

С этими двумя правилами страничка с примером выглядит много ближе к запланированной и будет практически идеальной после простеньких дополнений. И все это с помощью формулы target ÷ context = result.
Так что после нескольких часов проведенных за разгребанием размеров я осознал, что спотыкаюсь об ответ. Если мы можем рассматривать размер шрифта не как пиксели, а как пропорцию, вычисленную относительно контейнера, то тоже самое можно применить и к другим элементам сетки.

В конце-концев, это не «Золотой Пиксель»


Как и ранее, давайте начнем с четкой несексуальненькой простой разметки:

image
Наша базовая разметка

Конечно, наш «дизайн» довольно скромненький. Но простые стили наложены на четко определенную сетку: точнее, семь колонок по 124px каждая, разделенные 20px пробельным материалом, и все это вместе имеет ширину 988px. Но давайте забудем об этих мерзостных пикселях. Пропорции — наше все, да? Даешь «резину», детка!

image
Наша базовая разметка с наложенной поверх нее сеткой

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

image
Определяем информационные блоки.

На самом высоком уровне у нас есть заглавие вверху страницы, информационная область, которая растягивается на шесть колонок и некая зависимая от контекста информация в самой левой колонке. Из этой схемы мы можем выделить скелет разметки, который соответствует нашему информационному множеству и с точки зрения структуры и с точки зрения семантики:
  1. <div id="page">
  2.     <h1>The Ratio Revolution Will Not Be Televised</h1>
  3.  
  4.     <div class="entry">
  5.         <h2>Anyone else tired of Helvetica?</h2>
  6.  
  7.         <h3 class="info">A <a href="#">Blog</a> Entry:</h3>
  8.  
  9.         <div class="content">
  10.             <div class="main">
  11.                 <p>Main content goes here. Lorem ipsum etc., etc.</p>
  12.             </div><!-- /end .main -->
  13.  
  14.             <div class="meta">
  15.                 <p>Posted on etc., etc.</p>
  16.             </div><!-- /end .meta -->
  17.         </div><!-- /end .content -->
  18.     </div><!-- /end .entry -->
  19. </div><!-- /end #page -->

И, применив некоторые правила типографики, мы получаем прилично выглядящую точку отсчета. Однако, контейнер (#page) не содержит никаких ограничений, так что наше информационное наполнение будет сформировано таким образом, чтобы соответствовать ширине окна браузера. Давайте попробуем проконтролировать длину строк:
  1. #page {
  2.     margin: 40px auto;
  3.     padding: 0 1em;
  4.     max-width: 61.75em;      /* 988px / 16px = 61.75em */
  5. }
  6.  

Мы использовали margin и padding, чтобы внести ограничения и получить пробельный материал между информационным наполнением и границами окна. Но последняя строчка нашего правила использует разновидность нашей формулы (для расчета font-size) для определения максимальной ширины нашего дизайна. Разделив ширину макета (988px) на базовый размер шрифта (font-size) (16px) мы можем установить максимальный размер в em для определения приблизительного соответствия пиксельной ширине нашего макета, что предотвратит увеличение размера макета сверх идеальных 988px. Но так как мы используем em для определения максимального размера, то максимальная ширина (max-width) будет масштабироваться, если пользователь увеличит размер шрифта в браузере – модный маленький приемчик, который работает даже в старых версиях IE, если применить маленький CSS хак.
Теперь, когда наш дизайн соответствующим образом ограничен, давайте поработаем с каждым элементом нашего дизайна отдельно, начиная с заголовка страницы. В макете он занимает пять колонок и четыре пробельных материала, общей шириной в 700px. Так же он отделен от левой границы страницы одной парой колонка/пробельный материал, что дает милый отступ в 144px. И, если бы мы работали с дизайном фиксированной ширины, наша работа была бы достаточно прямолинейна:
  1. h1 {
  2.     margin-left: 144px;
  3.     width: 700px;
  4. }

Но, так как мы работаем с «резиной», фиксированные единицы не совсем подходят. И, так как я работал с относительными размерами, до меня дошло: каждый элемент сетки — и элементы которые на ней расположены — могут быть выражены как пропорция относительно их контейнера. Другими словами, как и в нашей задаче с изменением размера шрифта, мы рассматриваем не только нужный нам размер элемента, но и отношение этого размера к размеру контейнера. Это позволит нам преобразовать ширину элементов, основанную на пикселях, в проценты и сохранить пропорции сетки даже при изменении ее размера.
Короче, у нас будет «резина».

Все новое — хорошо забытое старое


Так с чего начать то?

target ÷ context = result

Правильно: возвращаемся к нашей формуле расчета размера шрифта. Мы можем использовать туже пропорцию для преобразования ширины колонки в пикселях в определенные в процентах, гибкие значения. Так что мы, отталкиваясь от целевых 700px, для получения размера заголовка в процентах — но контейнер заголовка имеет размер 988px;

image
Преобразуем значение размера заголовка в пикселях в проценты.

В результате, мы просто делим 700px (наше целевое значение) на 988 (его контекст (размер контейнера заголовка)) вот таким образом:

700 ÷ 988 = 0.7085

И вот: 0.7085 превращается в 70.85%, ширину, которую можно заносить в таблицу стилей:
  1. h1 {
  2.     width: 70.85%;           /* 700px / 988px = 0.7085 */
  3. }

А можно тоже сделать с отступом в 144px? Обожаю наводящие вопросы:

144 ÷ 988 = 0.14575

И снова, мы берем 0.14575, или 14.575%, и добавляем в нашу таблицу стилей в качестве значения margin-left для заголовка:
  1. h1 {
  2.     margin-left: 14.575%;    /* 144px / 988px = 0.14575 */
  3.     width: 70.85%;           /* 700px / 988px = 0.7085 */
  4. }

И voilà. Измерив отступ заголовка и ширину относительно контейнера, мы успешно перевели пропорцию нашей сетки в проценты. Пропорции заголовка всегда останутся верными, даже если он масштабируется, чтобы соответствовать размеру окна браузера.
Мы можем применить столь же простое преобразование для определения относительных размеров блока, содержащего основное информационное наполнение, он обладает размером 844px и отступом слева 124px шириной. То есть:

844 ÷ 988 = 0.85425

И для информационной колонки:

124 ÷ 988 = 0.12551

Эти два преобразования дают нам значения в процентах, которые мы можем внести в таблицу стилей и сделать нашу разметку еще более гибкой.
  1. .entry h2,
  2. .entry .content {
  3.     float: right;
  4.     width: 85.425%;          /* 844px / 988px = 0.85425 */
  5. }
  6.  
  7. .entry .info {
  8.     float: left;
  9.     width: 12.551%;          /* 124px / 988px = 0.12551 */
  10. }
  11.  

И таким образом продвигаемся на шаг вперед в получении «резины».

Изменяем контекст


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

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

Тогда как ранее мы вычисляли процентное отношение относительно ширины 988 px контейнера #page, сейчас мы работаем с контейнером .entry .content, который значительно меньше. В результате нам надо изменить наш контекст и использовать ширину .entry .content как точку отсчета. Так что, чтобы определить основанную на процентах ширину элемента «main copy» мы берем его ширину (700px) и делим на ширину родительского контейнера (844px)

700 ÷ 844 = 0.82938

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

124 ÷ 844 = 0.14692

Теперь мы можем взять результаты этих вычислений и внести их в CSS:
  1. .entry .main {
  2.     float: left;
  3.     width: 82.938%;          /* 700px / 844px = 0.82938 */
  4. }
  5.  
  6. .entry .meta {
  7.     float: right;
  8.     width: 14.692%;          /* 124px / 844px = 0.14692 */
  9. }
  10.  

Собственно, на этом работа закончена, «резина» закончена.

Заключение


Как можно предположить из отсутствия хаков в CSS, у меня было почти не было проблем с кросс-браузерностью. Очень рекомендую статейку Джона Ресига (John Resig) на тему суб-пиксельных проблем в CSS. Она объясняет как различные браузеры обрабатывают ширину указанную в процентах и механизм, с помощью которого согласовываются суб-пиксельные измерения.
Как Джон объясняет в своей статье, если современные браузеры работают с четырьмя элементами 25% ширины в контейнере шириной 50px, они не могут отрисовать элементы 12.5 пикселей шириной; вместо этого большинство браузеров округлит ширину колонки до большего или меньшего целого для наилучшего соответствия разметке. Internet Explorer, как всегда, просто округляет до большего целого, что разрывает разметку.
Если вы работаете с достаточно общими отступами в вашей сетке, это не проблема. Но если IE вызывает нежелательные разрывы в разметке, перенося колонки с шириной указанной в процентах, может помочь уменьшение на один пиксель целевой ширины. Так что, если, например, наш левый отступ слишком велик для IE, можно изменить расчет с:

124 ÷ 988 = 0.12551

на меньшую ширину 123px:

123 ÷ 988 = 0.12449

Внесите ширину 12.449% в таблицу стилей специфичную для IE и все будет в порядке.

Сетка на все случаи жизни


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

Дополнительная литература


Как вы мо

+103
39.3k 432
Comments 71
Top of the day