Как сделать обалденный почтовый шаблон

14 апреля 2011

Недавно я был озабочен версткой (и подключением к Rails) целого комплекта шаблонов рассылок и почтовых уведомлений и решил извлечь из этого процесса наибольшую пользу.

Ниже мои наблюдения, в произвольном порядке — разумеется, со привкусом рельсы.

Общие рекомендации

Мой собственный опыт

Шаблон должен «работать» без картинок

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

Graceful degradation в применении к письмамGraceful degradation в применении к письмам

В данном случае кнопка сделана табличкой из единственной ячейки, грубо говоря, так:

<table>
  <tr><td width="100" height="50" bgcolor="red" background="button.png">
    <a href="#" style="width:100; height:50;padding:0;margin:0">Заказать</a>
  </td></tr>
</table>

Товарищи верстальщики, не приходилось еще верстать кнопку табличкой?

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

При создании этих логотипов не пострадал ни один пиксельПри создании этих логотипов не пострадал ни один пиксель

Используй хелперы!

Хелперы — решение самой страшной проблемы почтовых шаблонов: встроенного CSS! Достаточно на каждый привычный класс определать по хелперу, где-то таким образом:

module MailHelper
  def mail_h1(&block)
    content_tag(:div, {
      :style => 'font-size: 18px; font-weight: bold; margin-bottom: 21px; line-height: 21px'
    }, &block)
  end

  def mail_p(&block)
    content_tag(:div, {
      :style => 'font-size: 14px; margin-bottom: 21px; line-height: 21px'
    }, &block)
  end
end

Если приглядеться, то это решение можно упростить посредством метапрограммирования — в таком виде код настолько похож на CSS, что даже верстальщики смогут его дополнять:

module MailHelper
  TAGS = {
    :h1 => 'font-size: 18px; font-weight: bold; margin-bottom: 21px; line-height: 21px'
    :p => 'font-size: 14px; margin-bottom: 21px; line-height: 21px'
  }

  TAGS.keys.each do |tag_name|
    define_method "mail_#{tag_name}" do |&block|
      content_tag(:div, {:style => MailHelper::TAGS[tag_name]}, &block)
    end
  end
end

А ведь можно теги и в YAML-файл вынести, или даже написать более утонченный парсер — что лично я считаю абсолютно ненужным.

С такими хелперами в шаблоны возвращаются порядок и читабельность:

=mail_h1 do
  My Header

=mail_p do
  My Paragraph

Верстай DIV-ами (нет, не в том смысле)

Поскольку использовать CSS Reset природными путями в почте не выйдет, то проще всего отталкиваться от элемента, наименее обремененного стилями по умолчанию – от DIV. Семантичность почтовой верстки совершенно никому не нужна.

…А вот макетирование, колонки, выравнивание и т.п. надежнее как раз верстать таблицами.

Ссылки с указанием домена

Все абсолютно ссылки должны указывать домен. То есть быть указаны с помощью _url-методов, а не _path-методов. То есть вместо

link_to @item.title, @item

нужно писать

link_to @item.title, item_url(@item)

Отслеживание кампаний

Вещь первой необходимости, такая же, как подсчет посетителей на сайте. Скорее всего, тебе это нужно делать через Google Analytics. Для этого к ссылкам нужно навешивать ряд специальных параметров — можно это делать через default_url_options, а можно — небольшим хелпером. Мне первый вариант не нравится тем, что он заставляет вешать параметры на все ссылки.

# mail_helper.rb
def utm_url(url, utm_content='')
  utm_params = "utm_medium=email&utm_campaign=#{@campaign_name}&utm_source=#{@campaign_source||@campaign_name}"
  utm_params += "utm_content=#{utm_content}" unless utm_content.blank?
  if url.include? '?'
    url += '&' + utm_params
  else
    url += '?' + utm_params
  end
  url
end

# my_mailer.rb
def promo
  @campaign_name = 'promo_campaign'
  @campaign_source = 'promo_campaign_april'
end

# promo.html.haml
=link_to 'Click here plz ok', utm_url(item_url(@item), 'view_item')

Отписка

В трех словах — отписка должна быть. И работать. Дешево и сердито — отписка посредством плагина bounces-handler, который, при должной настройке, сможет обрабатывать и ошибки в доставке почты.

Проверка шаблонов

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

Проверять шаблоны особым контроллером

class MailerTestController
  layout 'mailer'

  def registration
    @user = Factory(:user)
    render :text => MyMailer.registration(@user).body
  end

  # ... more actions
end

# routes.rb
get '/mail_test/:action', :controller => 'mailer_test'

Генерировать письма Rake-командой

task :mail_test => :environment do
  @user = Factory(:user)
  File.open('tmp/mail_test/registration.html','w'){|f| f.puts MyMailer.registration(@user).body}
  # ... more messages
end

Первый вариант лучше тем, что можно отрисовывать шаблон по нажатию F5, а второй — тем, что его можно запускать в тестовом окружении и не портить базу.

Я использую второй.

Да, для таких фабрик, разумеется, удобно будет использовать Выдумщика.

Письма все равно нужно проверять в почтовом клиенте!

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



Восемь комментариев. Напиши еще один
  1. 91913f6ab8085bab0f2aa43995ba8ca2 # 16 апреля 2011 Bogdan Gusiev (gusiev.com) написал:

    А почему нельзя сделать хелпер который пропишит все стили которые определены в цсс файле инлайново?

    Сегодня думал на данную тему и не нашел ниодного проблемного случая.

    1. 777894ea5153122bfa6b83f5bbf23622 # 16 апреля 2011 Леонид Шевцов (автор) написал:

      Придется либо упрощать правила, либо кодить практически весь CSS-движок браузера; если парсеры CSS существуют, то «применятелей» стилей вроде как нет. А применятель CSS – не такая и простая штука; придется обрабатывать наследованные стили, вложенность и т.п.

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

      1. 3b06e5ebb7914180d6f3aa964cc26027_normal # 16 апреля 2011 maniac (@pzskc383) написал:

        https://github.com/imedo/awesome_email этот гем умеет инлайнить css

        1. 777894ea5153122bfa6b83f5bbf23622 # 16 апреля 2011 Леонид Шевцов (автор) написал:

          Ой, действительно. Он, правда, морально устарел, но самого кода по применению CSS не так много.

          Ну ладно, тогда единственное что мне не нравится — это необходимость каждое письмо распарсивать, а потом еще и прогонять по нему набор селекторов.

          Кроме того, если уж парсить, то в JavaScript есть такая замечательная функция как element.getComputedStyle(), которой можно вытянуть стили через, например, PhantomJS.

          Было бы здорово делать это на уровне haml/erb, конечно.

          1. 91913f6ab8085bab0f2aa43995ba8ca2 # 17 апреля 2011 Bogdan Gusiev (gusiev.com) написал:

            1. 777894ea5153122bfa6b83f5bbf23622 # 17 апреля 2011 Леонид Шевцов (автор) написал:

              Ну круто. Только опять-таки он с хтмлом работает.

              1. 91913f6ab8085bab0f2aa43995ba8ca2 # 17 апреля 2011 Bogdan Gusiev (gusiev.com) написал:

                А с чем он должен еще работать?

                В готовом сгенеренном хтмл-е он проинлайнит все стайлшити…. В чем недостаток такого варианта?

                1. 777894ea5153122bfa6b83f5bbf23622 # 17 апреля 2011 Леонид Шевцов (автор) написал:

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

                  Для массовых рассылок, впрочем, то что надо.

(нужна разметка?)

  • **жирный**
  • > цитата

отменить