13 July 2015

По-настоящему адаптивные письма. Часть… снова первая

CSSHTMLEmail layout


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

К чему мы приходим:
— Использование внешних стилей и последующий прогон кода через инлайнер. Из-за усложнения кода это стало целесообразным
— Улучшение семантики через именование классов и сокращение участков кода
— Частичная поддержка the Bat! Хотя Николь на него забила.
— Полная поддержка всех мобильных почтовых клиентов
— Использование ранее опасных конструкций. Благодаря усердному тестированию проблемы решены.

Обертка


Давайте обрисуем новую обертку письма(Github):
Скрытый текст
<meta charset="utf-8">
<style>
	body {
		margin:0;
	}
	/* здесь будут общие стили нашего письма */
    .content {
        background-color:#ffffff;
        padding:30px;
    }
	.newsletter {
		margin: 0 auto;
		max-width: 600px;
	}
	.wrapper {
		width: 100%;
		background-color:#f4f4f4;
		table-layout: fixed;
		-webkit-text-size-adjust: 100%;
		-ms-text-size-adjust: 100%;
	}
	@media only screen and (max-width: 400px) {
        /* причесываем верстку на малых мобильных экранах */
	}
	@media screen and (min-width: 401px) and (max-width: 600px) {
		/* причесываем верстку на средних мобильных экранах */
	}
	@media screen and (min-width: 600px) {
		.newsletter { width:600px !important; }
	}
</style>
<!--[if (gte mso 9)|(IE)]>
    <style>
        /* здесь будут располагаться хаки для аутлука */
    </style>
<![endif]-->
<div align="center" class="wrapper">
    <!--[if (gte mso 9)|(IE)]>
    <table align="center" width="600" cellpadding="0" cellspacing="0" style="border-collapse:collapse;"><tr><td>
    <![endif]-->
	<table class="newsletter" align="center" width="100%" cellpadding="30" cellspacing="0">
    <tr>
		<td class="content" style="width:600px !important;">
            контент письма
        </td>
	</tr>
    </table>
    <!--[if (gte mso 9)|(IE)]></td></tr></table><![endif]-->
</div>

А теперь я расскажу что в ней и как:

— В первую очередь стоит отметить, что нам не нужны doctype, head и body. Без доктайпа поведение писем аналогично везде и не приносит сюрпризов. Head просто бесполезен. Ну а body все равно будет вырезан большинством почтовых вебморд. Именно по этой причине мы не задаем общий фон письма через body.

— Блок .wrapper является общей оберткой с указанием цвета фона, посему задаем ему фон и несколько css свойств для избежания непредсказуемого поведения.

— Таблица .newsletter — непосредственно отцентрированное письмо. Для его родителя .wrapper мы указали значение center для атрибута align, чтобы в yahoo mail наша таблица была отцентрирована. Стоит понимать, что в будушем для выравнивания любых таблиц необходимо оборачивать их дивами с указанным выравниванием. При этом не забываем указывать выравнивание для самой таблицы. Для .newsletter мы указали ширину в 100% через инлайн для верности, при этом указали max-width:600px для получения адаптивности. Но это свойство не работает в аутлуке и в the bat! Для решения проблемы с аутлуком мы нарисовали дополнительную таблицу, которая является родителем нашего .newsletter.

— Ячейка .content уже является частью письма. Для нее мы указали background-color:#ffffff в сss для задания белого фона письма в отличие от серого фона подложки. Мы явно указали width:600px !important; для решения проблемы the bat!, который не поддерживает max-width, получив 600px ширину письма в бате.



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

Контент


А теперь давайте добавим немного контента нашему письму(Github)
Скрытый текст
<meta charset="utf-8">
<style>
    body {
        margin:0;
    }
    a {
        color:#0077cc !important;
    }
        a img {
            border:0;
        }
        a span {
            color:#0077cc;
        }
    table {
        border-collapse:collapse;
    }
    p {
        margin:1em 0;
		font-family: arial;
        font-size:14px;
        color:#666666;
        line-height:1.4em;
        text-align: left;
    }
	    .paragraph {
	        font-family:arial;
	        font-size:14px;
	        color:#666666;
	        line-height:1.4em;
            text-align: left;
	    }
    .button {
        display:inline-block;
        vertical-align:top;
        width:150px;
    }
		.button table {
			border-collapse: separate !important;
			border:#ffffff 5px solid;
		}
        .button div {
            font-family:arial;
            font-size:14px;
        }
        .button a, .button span {
            color:#ffffff !important;
            text-decoration:none;
        }
    .content {
        background-color:#ffffff;
        padding:30px;
    }
    .h1 {
        font-family: arial;
        font-size: 24px;
        color:#666666;
        line-height:1.2em;
    }
    .newsletter {
        margin: 0 auto;
        max-width: 600px;
    }
    .wrapper {
        width: 100%;
        background-color:#f4f4f4;
        table-layout: fixed;
        -webkit-text-size-adjust: 100%;
        -ms-text-size-adjust: 100%;
    }
    @media only screen and (max-width: 400px) {
        /* причесываем верстку на малых мобильных экранах */
    }
    @media screen and (min-width: 401px) and (max-width: 600px) {
        /* причесываем верстку на средних мобильных экранах */
    }
    @media screen and (min-width: 600px) {
        .newsletter { width:600px !important; }
    }
</style>
<!--[if (gte mso 9)|(IE)]>
    <style>
        /* здесь будут располагаться хаки для аутлука */
    </style>
<![endif]-->
<div align="center" class="wrapper">
    <!--[if (gte mso 9)|(IE)]>
    <table align="center" width="600" cellpadding="0" cellspacing="0" style="border-collapse:collapse;"><tr><td>
    <![endif]-->
    <table class="newsletter" align="center" width="100%" cellpadding="30" cellspacing="0">
    <tr>
        <td class="content" style="width:600px !important;">
            <div class="h1">Paragraph Heading</div>
            <p><a href="#" target="_blank"><span>Lorem ipsum dolor</span></a> sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
            <div align="center">
                <div class="button">
                    <table cellpadding="10" cellspacing="0">
                        <tr>
                            <td bgcolor="#666666" style="padding:10px 20px; border-radius:50px;">
                                <div><b><a href="#" target="_blank"><span>Button Example</span></a></b></div>
                            </td>
                        </tr>
                    </table>
                </div>
                <div class="button">
                    <table cellpadding="10" cellspacing="0">
                        <tr>
                            <td bgcolor="#666666" style="padding:10px 20px;">
                                <div><b><a href="#" target="_blank"><span>Button Example</span></a></b></div>
                            </td>
                        </tr>
                    </table>
                </div>
				<div class="button">
                    <table cellpadding="10" cellspacing="0">
                        <tr>
                            <td bgcolor="#666666" style="padding:10px 20px; border-radius:50px;">
                                <div><b><a href="#" target="_blank"><span>Button Example</span></a></b></div>
                            </td>
                        </tr>
                    </table>
                </div>
            </div>
            <br>
            <div class="h1">Markered List</div>
            <p>–  Lorem ipsum dolor sit amet, consectetur adipiscing elit</p>
            <p>–  Lorem ipsum dolor sit amet, consectetur adipiscing elit</p>
            <p>–  Lorem ipsum dolor sit amet, consectetur adipiscing elit</p>
            <p>–  Lorem ipsum dolor sit amet, consectetur adipiscing elit</p>
        </td>
    </tr>
    </table>
    <!--[if (gte mso 9)|(IE)]></td></tr></table><![endif]-->
</div>



И разберем все по порядку.

Заголовок


.h1 {
        font-family: arial;
        font-size: 24px;
        color:#666666;
        line-height:1.2em;
    }

<div class="h1">Paragraph Heading</div>


Используется именно div без задания margin для избежания появления лишних отступов для блоков, у которых указан верхний padding.

Абзац


p {
        margin:1em 0;
		font-family: arial;
        font-size:14px;
        color:#666666;
        line-height:1.4em;
        text-align: left;
    }
	    .paragraph {
	        font-family:arial;
	        font-size:14px;
	        color:#666666;
	        line-height:1.4em;
            text-align: left;
	    }

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

<div class="paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>

Обращаю внимание на то, что для тега абзаца мы явно указываем margin. Это необходимо для пересчета отступов в outlook.com. Там они кардинально отличаются от дефолтных. Тег параграфа идеально использовать вместе с предыдушим вариантом заголовка.

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

Ссылка


    a {
        color:#0077cc !important;
        text-decoration:underline;
    }
        a img {
            border:0;
        }
        a span {
            color:#0077cc;
        }

<a href="#" target="_blank"><span>Lorem ipsum dolor</span></a>

Конструкция ссылки всегда должна содержать внутри span, если ссылкой является текст. Это костыль для аутлука. Мы задаем цвет ссылки и такой же цвет для внутреннего span. Явное задание декорирования цвета необходимо для мобильного gmail, который снимает подчеркивания у ссылок. Для изображений ссылок мы сразу устанавливаем нулевой border.

Кнопка


.button {
        display:inline-block;
        vertical-align:top;
        width:150px;
    }
		.button table {
			border-collapse: separate !important;
			border:#ffffff 5px solid;
		}
        .button div {
            font-family:arial;
            font-size:14px;
        }
        .button a, .button span {
            color:#ffffff !important;
            text-decoration:none;
        }

<div class="button">
    <table cellpadding="10" cellspacing="0">
        <tr>
            <td bgcolor="#666666" style="padding:10px 20px; border-radius:50px;">
                <div><b><a href="#" target="_blank"><span>Button Example</span></a></b></div>
            </td>
        </tr>
    </table>
</div>

Конструкция кнопки немного громоздка, но тем не менее железобетонна. В первую очередь наша кнопка — это инлайновый, а не блочный элемент, что позволяет нам размещать несколько кнопок рядом друг с другом. Для того, чтобы иметь возможность использовать адекватный border-radius для кнопки таблицу внутри кнопки надо обязательно сепарировать. Остальное по примеру ссылки. Немного офрормления и кнопка готова. Важное замечание. Наша кнопка имеет свойство схлопываться в две строчки, если ее название состоит из двух слов, поэтому я использую неразрывный пробел для лечения этой проблемы. В качестве альтернативы можно использовать фиксированную ширину ячейки таблицы. Для выравнивания кнопок используется родительский div с явно заданным выравниванием.

И наконец, список


О нем я уже писал в предыдущем топике И не буду повторять подробности. Я лишь просто покажу как его оформлять лучшим образом. Никакого лишнего CSS. Все предельно просто.

Так как хабрапарсер жрет юникод символы, я вставлю пример кода скриншотом.



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

Стэй тюнд.

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

UPD2: Всплыли баги в мобильном yahoo клиенте. О решении обязательно напишу апдейтом к этому топику или в следующих частях.

UPD3: Баги Yahoo решены. Мобильное приложение yahoo не понимает свойства cellpadding у таблиц, следовательно мы должны указывать отступы ячеек через стили. Также в yahoo скукоживались инлайн кнопки. В качестве панацеи используется фиксированная ширина для кнопки. Не так круто, как хотелось бы, но что поделать.

В рамках нашего сегодняшнего материала необходимо добавить следующий CSS

.content { padding:30px; }
.button { width:150px; }
Tags:верстка писемemailадаптивные письмарассылкиmedia_queries
Hubs: CSS HTML Email layout
+20
25.7k 345
Comments 25
Popular right now
Разработчик-репетитор на курс по вёрстке email-рассылок
from 4,000 to 8,000 ₽HTML AcademyСанкт-ПетербургRemote job
Junior email-маркетолог
from 40,000 ₽Retail RocketМоскваRemote job
Full-Stack Web Developer
from 50,000 to 200,000 ₽RapidWeb.meRemote job
Frontend разработчик
from 60,000 ₽МедиасетьКостромаRemote job
Top of the last 24 hours