Посты с тэгом «верстка»

  • CSS-свойство content: копировать или нет?

    Решил я для очередного своего проекта воспользоваться модным CSS-свойством content, чтобы немного облегчить страницу и сделать настройку внешнего вида более гибкой. Так как проект ориентирован на веб-разработчиков, об обратной совместимости со старыми браузерами (IE6—7) можно было не беспокоится. Но, к сожалению, меня ожидало большое разочарование от использования этого свойства. Нет, всё отображалось правильно, но конечному пользователю было бы неудобно пользоваться результатом.

    Что такое CSS-свойство content

    Кому ещё не удалось познакомиться с этим замечательным свойством, кратко расскажу о нём. Само название этого свойства говорит о том, что оно управляет неким содержимым. Согласно спецификации CSS2 это свойство применяется только к псевдо-элементам :before и :after, а с версии CSS3 станет доступно и для обычных элементов (небольшой реверанс в сторону Opera, которая это уже поддерживает).

    С помощью свойства content мы можем через CSS задавать текстовое содержимое для (псевдо-)элементов. Классический пример применения этого свойства — вывод содержимого ссылки рядом с элементом в версии сайта для печати:

    <style type="text/css">
    	a:after {
    		content: ' (' attr(href) ')';
    	}
    </style>
    <p>В нашем <a href="/catalog/">каталоге</a> вы найдёте много чего интересного.</p>
    

    С помощью псевдо-элемента :after мы задали некое содержимое после тэга <a>. Этим содержимым является результат конкатенации строк и функции attr(), которая выводит содержимое атрибута контекстного элемента. Браузер, полностью поддерживающий CSS2, изобразит этот код примерно так:

    content-example

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

    Проблема

    Так как для конечного пользователя результат работы свойства content выглядит как обычный текст, возникает вопрос: а может ли пользователь выделить и скопировать такой текст?

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

    <style type="text/css">
    	.tag:before {
    		content:'<';
    	}
    
    	.tag:after {
    		content:'>';
    	}
    
    	.attr-value {
    		quotes:'"' '"';
    	}
    
    	.attr-value:before {
    		content:open-quote;
    	}
    
    	.attr-value:after {
    		content:close-quote;
    	}
    </style>
    <div class="tag">div <span class="attr-name">class</span>=<q class="attr-value">demo</q></div>
    

    Плюсы такого подхода: потребуется гораздо меньше элементов для раскраски тэгов (через псевдо-элементы :before и :after я могу задать произвольный цвет у угловых скобок). Для значений атрибутов я воспользовался тэгом <q> и CSS-свойством quotes, через которое определяются открывающие и закрывающие кавычки. Кому-то нравится использовать двойные кавычки в коде, кому-то — ординарные, при таком подходе их можно на лету поменять у всего документа. Как оказалось, выбор этого тэга и CSS-свойства стал важной частью эксперимента.

    В браузерах этот код выглядит замечательно:

    content-example2

    Но XML-документ должен не только красиво выглядеть, но и правильно работать: пользователь имеет право без проблем выделить и скопировать фрагмента документа, чтобы воспользоваться им, например, в своём любимом редакторе. И тут меня ожидало полное разочарование от использования свойства content. Я проверил результат копирования в своих браузерах — Safari 4, Opera 10, Firefox 3.5, IE8 — и получил вот такой результат:

    • Safari: div class=demo
    • Opera: <div class="demo">
    • Firefox: div class="demo"
    • IE8: <div class="demo">

    Как видите, все скопировали текст по-разному: Safari не скопировал content-данные в принципе, Opera и IE8 скопировали всё, а Firefox скопировал только кавычки вокруг атрибута.

    Затем я решил вместо вместо элемента <q> написать обычный <span>, и получил вот такой результат:

    • Safari: div class=demo
    • Opera: <div class="demo">
    • Firefox: div class=demo
    • IE8: <div class="demo">

    Всё то же самое, но Firefox уже не скопировал кавычки.

    Выводы

    Из этого небольшого эксперимента я сделал для себя следующие выводы:

    • Safari в принципе не понимает CSS-свойство quotes. То, что браузер отобразил кавычки вокруг <q> элемента — исключительно стандартная реакция на него. Кавычки нельзя будет поменять через свойство quotes, например, на ординарные — они так и останутся двойными. А если элемент переименовать в <span>, то и вовсе пропадут.
    • Firefox при копировании текста обращает внимание на название элемента: если это <q> — кавычки скопируются, для другого элемента получите пустоту.
    • Firefox всегда копирует двойные кавычки для тэга <q>, даже если вы измените их на что-нибудь другое (на «ёлочки», например). То есть сделать трюк с управлением копирования символов у вас не получится. Либо двойные кавычки, либо ничего.
    • IE8 при копировании обращает внимание на тип элемента: например, если прописать тэгу display: list-item, то скопируется буллит (хотя на странице он не будет отображаться).

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

  • Вёрстка растягивающихся сайтов

    В этой статье я поделюсь способом вёрстки растягивающихся сайтов (а других, как оказалось, я верстать не умею), которым пользуюсь последние 2—3 года. Этот способ применяется для сложных модульных сеток; он лёгок в применении, но сложен в понимании и у него есть ряд недостатков. В целом, представленная здесь информация пригодится и веб-дизайнерам, так как сам макет сайта должен быть правильно подготовлен.

    Начнём с простого. Предположим, у нас есть вот такой макет растягивающегося сайт:

    01

    На первый взгляд всё довольно просто: типовая трёхколоночная сетка (ширина колонки — 25%), одна колонка — меню, две колонки — контент, и всё это располагается по центру. Типовая вёрсткой такого макета может быть следующей: заключаем оба блока в контейнер шириной 75%; меню в этом случае будет шириной 33%, контент — 67%. Саму обёртку выравниваем по центру с помощью margin: 0 auto, либо просто смещаем влево. Первый способ предпочтительнее, так как ведёт себя адекватно, если мы ограничим минимальную ширину обёртки. Визуализация этого решения:

    02

    Проблема

    Казалось бы, ничего сверхъестественного, задача решена. Но вот дизайнер вдруг захотел красиво оформить информацию в одном из разделов сайта, нарисовав выносной блок за пределами контента:

    03

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

    Тут мы приходим к самому главному правилу верстки макетов: контент должен находиться в едином потоке. Я не раз замечал, что ребята начинают верстать новые макеты, если контент вдруг становится многоколоночным или многоблочным. Начинают выделять новые информационные поля в админке, которые обязательно должны быть заполнены, чтобы страница получилось такой, какой была задумана. А если эти блоки вдруг располагаются в другом порядке — делают новые макеты и поля в админке, превращая сайт в помойку ещё до его выпуска.

    Чтобы такого не получилось, нужно уделять максимальное внимание модульной сетке ещё на этапе создания первого макета. Считайте, что вы закладываете фундамент, от которого зависит, переживёт ли здание землетрясение, либо провалится после первого дождя. И обязательно добейтесь того, чтобы весь контент располагался в одном блоке, а не был размазан по всему макету: под меню, над футером, после шапки и так далее. Добьётесь этого — и стилизация контента станет минутным делом: в одном и том же макете собираете, как из конструктора, разные блоки в разном порядке, дописываете дополнительные стили и страница готова.

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

    Ширина контента — 67%, меню — 33%. Чтобы блок встал ровно, его нужно сместить на 49.25% (33 / 67 * 100 ≈ 49.25). Во-первых, наш результат получился приблизительным, во-вторых, все браузеры (кроме Firefox 3) довольно своеобразно работают с дробными процентами (да и с процентами вообще), а некоторые вообще их не воспринимают. В итоге получается, что у нас нет кросс-браузерного способа совместить эти блоки при выбранной разметке.

    Поиск решения

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

    01

    Внимательный читатель обратит внимание на то, что ширина меню ровно в 2 раза меньше ширины контента (50 / 25 = 2). Деление получается целочисленным (нет дробей), а значит мы можем создать кросс-браузерное и — самое главное — точное решение проблемы.

    Поступим следующим образом. Завернём оба блока в контейнер, ширина которого будет не 75%, как раньше, а 50% (то есть равна ширине контента). Сам контент будет шириной 100%, а меню — 50%, при этом смещено влево на свою ширину.

    04

    Приблизительный код этого примера:

    <style type="text/css">
    	#page {
    		position:relative;
    		margin:0 auto;
    		width:50%;
    		left:10%;
    	}
    
    	#menu {
    		width:50%;
    		margin-right:-50%; /* чтобы правильно в IE работало */
    		position:relative;
    		left:-50%;
    		float:left;
    	}
    
    	#content {
    		width:100%;
    		float:left; /* Новый контекст форматирования */
    	}
    </style>
    <div id="page">
    	<div id="menu"></div>
    	<div id="content"></div>
    </div>
    

    С помощью такой разметки мы можем легко решить нашу задачу с выносным блоком: делаем его шириной 50% и на эту же величину смещаем влево. Более того, с помощью такой разметки можно сделать более интересную вёрстку с выравниванием по основным трём колонкам:

    05

    Этот очень простой пример должен обратить внимание два ключевых момента такого способа вёрстки: выбор размера базовой колонки и выбор размера контейнера (точка отсчёта). Рассмотрим эти параметры чуть подробнее.

    Усложняем задачу

    Чтобы лучше раскрыть потенциал этого способа, попробуем сверстать шестиколоночный макет:

    06

    Проблема в том, что 100 не делится на 6 без остатка (100 / 6 ≈ 16.66666), а дизайнер по-прежнему хочет выравнивать контентные блоки по границам основных колонок.

    Одним из решений этой проблемы может быть следующее: сделаем размер контейнера равным ширине двух колонок. Размер внутреннего блока в этом случае будет равен 50%, что соответствует размеру основной колонки. А указав этому блоку margin-right:-50% мы уберём его влияние на поток по горизонтали: это позволит нам создать сколько угодно блоков в контейнере, каждый из которых будет влиять на его высоту, но не на ширину.

    07

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

    Проблема с расчётом процентных значений

    Если в предыдущем примере поиграться с размером окна браузера, то можно заметить, что во всех браузерах, кроме Firefox 3, появляются однопиксельные зазоры между некоторыми блоками:

    screenshot

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

    Проценты — это относительная величина, которая пересчитывается в абсолютную — пиксели (ну хорошо, пиксели — это тоже относительная величина, которая зависит, например, от DPI экрана). То есть когда браузер видит, например, процентную ширину у блока, он должен рассчитать значение в пикселях относительно контейнера этого блока. Предположим, ширина контейнера равна 60 пикселям, а внутреннего блока — 50%. Ширина в пикселях этого блока будет равна: 60 × 0.5 = 30px. Значения красиво умножились/поделились. Но что будет, если ширина контейнера равна 61px? 61 × 0.5 = 30.5px — браузер не может отрисовать половину пикселя, поэтому он округляет это значение в меньшую или большую сторону.

    На самом деле алгоритм гораздо сложнее: например, 4 блока подряд шириной 25% в современном браузере должны занимать 100% ширины контейнера; какие-то блоки будут меньше, какие-то больше. Мы же рассматриваем самый простой случай.

    Итак, ширина блока получилась 30.5px, браузер для себя округляет её до 30px. Что будет, если в этот блок мы вложим ещё один, шириной 200%? По идее его ширина должна быть равна ширине контейнера, то есть 61px. Но так не получится, потому что абсолютная ширина промежуточного блока равна 30px, соответственно: 30 × 2 = 60px. Получили расхождение в 1 пиксель. Из всех современных браузеров правильное значение даст только Firefox 3: судя по всему, он где-то внутри хранит истинное значение ширины, от которого делает все расчёты.

    Но! Обратите внимание, что абсолютная ширина нашего нового внутреннего блока всегда будет чётной. Например, если ширина промежуточного блока будет рассчитана как 31px, то внутренний блок будет шириной 31 × 2 = 62px. А чётные числа, как известно, делятся на 2 без остатка.

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

    Выводы

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

    1. Определяем ширину колонки модульной сетки. Тут, конечно, многое зависит от дизайнера: если он будет лепить блоки как попало, то никакие стандартные способы не подойдут. Мне очень повезло, что в САЛ практически у всех дизайнеров есть голова на плечах. Перед тем, как что-то нарисовать, они разбивали макет на несколько колонок одинаковой ширины, по которым выравнивали блоки. Если же макет не укладывался в мою схему, я немного раздвигал блоки, выравнивая их по моим колонкам и показывал дизайнеру — он был не против. Важно понимать, что ширина вашей колонки не всегда будет равна ширине дизайнерской колонки. Это можно увидеть в самом первом примере, где ширина свёрстанной колонки равна двум дизайнерским. Обычно, когда я получаю макет от дизайнера, я использую его гайды исключительно для того, чтобы увидеть принципы выравнивания блоков, после чего я создаю свою разбивку на колонки.
    2. Определяем ширину основного контейнера. Этот контейнер я называю точкой отсчёта (хоть это и не точка вовсе). Важно, чтобы ширина точки отсчёта позволяла создавать ширину колонки, которая делит 100 без остатка: 100%, 50%, 20%, 10%, 5%. Создав колонки такого размера, можно точно позиционировать и раздвигать выносные блоки внутри контента.
    3. Добавляем ещё обёртку для точного округления процентов. Это если вам необходимо точное выравнивание по блокам с видимыми границами. В примере я показал, как создать блок, ширина которого всегда кратна 2. Точно так же можно создавать обёртки, ширина которых кратна, например, 4 (25%/400%).

    Самое сложное в этом способе — определить правильный размер колонки и точки отсчёта. Чтобы нагляднее продемонстрировать преимущества этого способа, покажу несколько примеров его применения:

    1. Фотогалерея Imperia Private Banking. Блок с галереей растягивается точно (±1 пискель) по размерам общего контейнера, при этом сам контентный блок занимает половину ширины и располагается по центру контейнера. Галерей на странице может быть сколько угодно (как и контента между ними), при этом они будут находится в нормальном потоке документа. На этом же сайте можно увидеть ещё несколько примеров того, как контентные выносные блоки выравниваются по колонкам макета.
    2. Банк AB.LV. Сайт довольно большой, содержит много разного контента и едва заметных декораций (однопиксельная тень вокруг основного блока, например). На странице «О банке» можно увидеть, как четвёртая колонка в контенте выравнивается точно по основному меню (это чистой воды дрочерство, но дизайнер был доволен, да и мне было не сложно такое сделать).
    3. (примеров, на самом деле, много, но не могу вспомнить самые выразительные).

    Недостатки

    Куда же без них. Самый главный — способ совершенно не подходит для макетов, которые должны растягиваться на всю ширину окна браузера. Из-за тех самых округлений процентных значений есть большой риск того, что какой-нибудь блок выйдет на 1px за пределы окна, создав тем самым горизонтальный скролл. Ещё кому-то может может показаться недостатком то, что размеры блоков меняются с шагом, больше одного пикселя. Ну и не забываем, что дизайнер тоже должен включить мозг перед тем, как что-то нарисовать. Хотя, конечно, если постараться, то можно сверстать и совсем экзотические варианты, выбрав размер колонки по-меньше (например, 10%).

    PS: не стоит относиться к этому способу как к решению всех бед. Если есть паттерны программирования, придуманные для решения определённого круга задач, то описанный способ — паттерн проектирования, также направленный на решение определённого круга задач.

    Метки: , ,
  • Религиозные войны

    Не ожидал, что статья про верстку двух колонок одинаковой высоты вызовет целый холивар на Хабре. В комментариях дискуссия велась по двум вопросам: зачем вообще нужно так извращаться и почему верстать таблицами — дурной тон. Если с первым вопросом более-менее разобрались (правдивые комментарии оставили errr, Jenek и ArtyV), со вторым вопросом все довольно грустно. Одни кричат, что таблицы для нетабличных данных — не семантично и не круто, вторые — что таблицами все делается проще и быстрее. И те, и другие в корне не правы.

    Верстка сайта, как и дизайн, это решение поставленной задачи, а не очередной способ понтануться перед коллегами-гиками или схалявить. Я еще ни разу не получал задания «написать html-код», но постоянно получаю «сделать сайт», а это — не одно и то же. Сайт — это живой механизм со своим внутренним миром, своими проблемами и планами на будущее. От того, как вы его спроектируете, зависит то, рассыпется ли он при малейшем дуновении ветра или выдержит удар ядерной боеголовки (дружно вспоминаем сказку про трех поросят).

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

    С семантикой тоже не все замечательно. Я не отрицаю, что это здорово и правильно, но вот скажите, если ли официальная спецификация семантичной верстки? Или же все это — частное мнение отдельных личностей, которое разнится от человека к человеку? Как, например, мне сверстать шахматную доску, чтобы любой user agent понял, что это именно шахматная доска, а не бессмысленное нагромождение элементов?

    Семантичность того или иного решения в первую очередь должна определятся решаемой задачей, а не частным мнением людей со стороны. Вот типичный пример, на котором я ловлю большинство идеологов семантичной верстки. Как у любого ИТ-специалиста, у этих ребят в своих блогах есть листинги кода. И этот листинг обычно сопровождается нумерацией строк, что очень даже удобно при описании принципов работы кода. И как же эти листинги оформляются? Правильно, через <ol>/<li> конструкции. Семантично? — вроде да. А я считаю, что нет. Нумерация — это декоративная составляющая листинга, которая никак не связана с самим кодом. А с помощью <ol>/<li> нумерация вдруг стала смысловой составляющей. Что выливается в то, что 95% пользователей при попытке скопировать этот код или его часть получат нумерацию строк впридачу. А ведь этот код вывешивается именно с целью дать посетителю скопировать и опробовать его. Нарушение семантики налицо. Некоторые, конечно, догадываются об этой проблеме, но решают они ее не отдельной колонкой с нумерацией, а кнопкой «view plain». То есть пользователю предлагается потанцевать с бубном только ради того, чтобы скопировать текст.

    Резюмируя все вышесказанное. При выборе «сверстать таблицами или блоками», равно как и в любой другой подобной задаче, нужно руководствоваться тщательным анализом той среды, в которой будет работать то или иное решение, а не идиотскими стереотипами или собственной ленью. Если 1% аудитории сайта — это десятки тысяч пользователей, приносящих реальный доход, то мой выбор — таблицы. Если нужно делать сложный презентационный макет с кучей уникальных по дизайну страниц, то я выбираю блоки. Как-то так.