28

Instead of having the page include a style tag with a link where to get the css from, which I could add to my view using rails' stylesheet_link_tag helper method, I want to have the css inline directly inside the page.

This is what I came up with so far:

%style(type="text/css")=File.read(physical_asset_path("email.css"))

But I can't find any rails' helper method which gives me the physical path of an asset - physical_asset_path is just a dummy method invented by me.

Anybody knows how to get the physical path of an asset when using rails 3.2.x?

Is there an easier/ better way to get stylesheets - from css files inside the common rails assets paths - inline?

Use case: most email clients don't access external sources (like css, images) without user confirmation. So to get the emails properly displayed I need to embed the CSS inside the emails' HTML.

9 Answers 9

63

Rails.application.assets.find_asset('email').to_s will return the compiled asset as a string.

Sign up to request clarification or add additional context in comments.

6 Comments

Unforunately, the asset will not be compressed
That works! but it was necessary to add a .html_safe so it does not escape quotes: Rails.application.assets.find_asset('email').to_s.html_safe
This is the missing link - by using %style{type:"text/css"}= Rails.application.assets.find_asset('email_stylesheet').to_s in the head section of your email.html.haml file along with the premailer-rails gem, the css is found, compiled and inlined - yay! working html emails!
In production: File.read("#{Rails.root}/public/assets/#{Rails.application.assets_manifest.files.collect{|k, v| k if v['logical_path'] == 'email.css' }.compact.first}").html_safe
|
15

Use premailer or premailer-rails3

https://github.com/fphilipe/premailer-rails3 or https://github.com/alexdunae/premailer

Joe's Nerd Party say:

We also used the Premailer gem to automatically inline the linked stylesheet in the email views. Our email layout looks something like:

%html
  %head
    = stylesheet_link_tag 'email'

    %style{:type => "text/css"}
      :sass
        @media all and (max-width: 480px)
          table#container
            width: auto !important
            max-width: 600px !important
         ... and so on for the mobile code

    %body 
      Email body here.
      %table
        Lots of tables.

We include a stylesheet in the HTML. Premailer downloads it, processes it, and inserts the css rules inline in the HTML.

The @media rules need to be inline in the email layout, since Premailer can’t handle those being in a separate css file yet.

We use premailer-rails3 to integrate Premailer into Rails 3. Unfortunately, we found a bunch of bugs in premailer and premailer-rails3. Our forks of the projects are at https://github.com/joevandyk/premailer and https://github.com/joevandyk/premailer-rails3. The forks fix some encoding bugs, remove some weird css processing stuff done by premailer-rails3, allow premailer to not strip out embedded rules in the email layouts, and some other things.

We also found a bug in sass-rails, where you can’t embed image-urls in inline sass code. See https://github.com/rails/sass-rails/issues/71 Premailer-rails3 hooks into ActionMailer when the email actually being delivered, not just generated. When running tests, email is not actually sent, so the premailer-rails3 hooks don’t get ran during tests. I haven’t spent the time to see if it’s possible to get the premailer processing to run during tests, but that would be a nice thing to do.

Also, our forks on premailer-rails3 assume that you want premailer to go out and actually download the linked CSS files. It should be possible to use the Rails 3.1 asset pipeline to get the processed css without downloading it. A very special thanks goes to Jordan Isip who did the super annoying job of making sure the emails look great in all the different clients out there. Writing that CSS/HTML did not look fun.

Update:

Roadie appears to be a better option. Thanks to Seth Bro for pointing it out.

4 Comments

Your fork doesn't work for me, so I end up create my own fork and also change the approach: github.com/phuongnd08/premailer-rails3
I've had better luck long term with Roadie. Last I checked (a couple years ago), there were some quirks to premailer-rails.
@SethBro, Roadie appears to be a clean solution.
Inlining the styles into elements (which both roadie and premailer do well) does make the styles not responsive as the inlined style will always have higher precendence. imho
11

(Sorry this answer is in html, not HAML… but that shouldn't be a problem for HAML fans)

I found this question when looking for a way to inline Sass compiled as css into html for creating html email templates.

Combining the above advice, I used the following code in the head of my html page:

<style type="text/css">
  <%= Rails.application.assets['path/to/sass/file'].to_s.html_safe %>
</style>

This code compiles Sass as CSS and then inserts the css into a <style> tag. The html_safe ensures that any quotes (' and ") or angle brackets (> and <) used in the css are not escaped.

The path/to/sass/file is the same as you would use when creating a stylesheet link tag:

<%= stylesheet_link_tag 'path/to/sass/file', :media => 'all' %>

Comments

10

Rails.application.assets['asset.js'] will work only in local environment, as rails asset compilation is disabled in both production and staging environment.

Rails.application.assets_manifest.find_sources('asset.js').first.to_s.html_safe should be used to inline css when using rails asset pipeline.

1 Comment

Really the only one that works in production. Unless premailer/roadie is preferred, but you can't be responsive with those
5

Can't add comment to Seth Bro's answer. You better use #[] instead of #find_asset: Rails.application.assets["email"].to_s.

Re "asset will not be compressed". It's not true. It will be compressed if you have compressors enabled (in rails config):

Rails.application.configure do
  # ...
  config.assets.css_compressor = :sass
  config.assets.js_compressor  = :uglify
end

Notice, that by default this is enabled in production environment (config/environments/production.rb).

3 Comments

Can you give an example of the compressors you speak of? I cannot get it to compress.
Added an example to reply.
Doesn't seem to work in production. Any work around? assets seems to be nil there.
4

Had the same problem, solved it using @phlegx's answer to a similar issue in Premailer.

For an environment-safe solution you need to use

(Rails.application.assets || ::Sprockets::Railtie.build_environment(Rails.application)).find_asset('email.css').to_s

I've packaged it into a helper in my app:

# app/helpers/application_helper.rb
# Returns the contents of the compiled asset (CSS, JS, etc) or an empty string
def asset_body(name)
   (Rails.application.assets || ::Sprockets::Railtie.build_environment(Rails.application)).find_asset(name).to_s
end

Comments

3

I was trying to inline css for use in google amp compatible pages with rails. I found the following helper from vyachkonovalov which was the only thing for me working in production and locally.

Add the following to the erb template:

<style amp-custom>
  <%= asset_to_string('application.css').html_safe %>
</style>

And the helper to ApplicationHelper. It works perfectly locally and in production.

module ApplicationHelper
  def asset_to_string(name)
    app = Rails.application
    if Rails.configuration.assets.compile
      app.assets.find_asset(name).to_s
    else
      controller.view_context.render(file: File.join('public/assets', app.assets_manifest.assets[name]))
    end
  end

Comments

1

tl;dr (without Roadie):

%style(type="text/css")
  = render template: '../assets/stylesheets/email_responsive.css'

For actually applying the CSS as inline styles, I recommend roadie-rails (which is a Rails wrapper for Roadie). It also has other neat features like absolutizing hrefs, srcs etc.

A usage combining both inlined (email.scss) and non-inlined (email_responsive.css) stylesheets, both residing in app/assets/stylesheets:

-# This will be inlined and applied to HTML elements.
-# Note that you need to include this in your asset config, e.g.:
-# Rails.application.config.assets.precompile += %w(... email.css)
-# (You need to list it as `email.css` even if it's actually `email.scss`.)

= stylesheet_link_tag 'email'


-# E.g. for media queries which can't be inlined - yeah, some iOS devices support them.
-# This will not be inlined and will be included as is (thanks to `data-roadie-ignore`).
-# `template:` marks it as a full template rather than a partial and disables `_` prefix.
-# We need to add the extension (`.css`) since it's non-standard for a view.

%style(type="text/css" data-roadie-ignore)
  = render template: '../assets/stylesheets/email_responsive.css'

Comments

0

You can use this:

Rails.root.join('public', ActionController::Base.helpers.asset_path("email.css")[1..-1]).read.html_safe

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.