2

I want to generate dynamic CSS using templates and Tilt andSASS in Rails 4.

  1. Let's say I have the following css.sass template:

    $class_name: test
    $width: 329 
    $height: 425 
    
    .view-#{$class_name}
    
      width: #{$width}px
      height: #{$height}px
    
  2. I need master.css.sass.erb (I am not sure about the format) in which I am going to render my template as many times as I like with different parameters.

  3. In application.css, I am going to have something like this

    *= require master.css.sass.erb
    

Each time I use the template with different parameters, I am going to add a line in my master.css.sass.erb file. I do not know how to pass parameters using Tilt to the css.sass template. Could anyone assist and tell if this is possible?


This is what I have till now:

  1. The template test.css.sass.erb:

    $color: <%= color %>
    body
      background: #{$color} !important
    
  2. The master.css.sass.erb file:

    <%
        require 'erb'
        config_path = File.expand_path("../test.css.sass.erb", __FILE__)
        template = Tilt.new(config_path)
        template.render(self, :color => 'yellow')
    
    %> .thisisrendered
        color: red
    

Note that the two files in one folder. The issue is that only the following css is renrered:

.thisisrendered
    color: red
Ajay Barot
  • 1,681
  • 1
  • 21
  • 37
gotqn
  • 42,737
  • 46
  • 157
  • 243

1 Answers1

3

To answer your question: when erb is compiled, it only outputs ruby code contained in a <%= code %> wrapper. Your current code is probably running fine (I'm not familiar with Tilt or straight SASS), but you aren't telling it to output the result to the sass file. Change the first line of master.css.sass.erb from <% to <%= and then you can debug from there.

That being said, I would recommend against this process. You're going to be wasting resources compiling the stylesheet every time it is called.

An alternate method would be to just keep your stylesheets static as in your opening example so they can be precompiled and cacheable, and then create a partial like layouts/_conditional_styles.html.erb using stock html and css like:

<% unless @your_sanitized_style_object.nil? %>
  <style>
    body{
      background: <%= @your_sanitized_style_object.background_color.html_safe %> !important;
    }
  </style>
<% end %>

Which could override the application stylesheet by being rendered after the application css file in your layouts/application.html.erb file such as:

<%= stylesheet_link_tag "application" %>
<%= render "layouts/conditional_styles" %>

To answer your question as to why you use less resources using precompiled assets and then overriding them, consider your example test.css.sass.erb

$color: <%= color %>
body
  background:$color !imporant

Assuming the color variable is red, the process would go something like this... First your application will run through it with its erb compiler and give you a test.css.sass file such as:

$color: #ff0000
body
  background:$color !important

Then your application will run through the code again with its sass compiler to give you a test.css file such as:

body{ background:#ff0000 !important; }

And after all of that, it will serve up the file. In your development environment you will not see that much of a performance difference as your application defaults to rebuilding the assets for every request. The difference comes when it's time to serve your application to the web in production.

If your assets aren't dependent on variables in the application, the stylesheets can be precompiled. What this means is that your application compiles the asset once, and after it compiles the asset you get a stylesheet like test-f25ab2b1286feb7cc98375sac732f7d0.css.

The stylesheets will be identical but what is unique is all that jargon that rails throws on the end of the file name when it precompiles something. That jargon is known as a fingerprint and its purpose is to tell the server on an incoming request whether or not there is a newer version of the file. If the file hasn't been modified, and the system making the request has already downloaded that file to its cache, then your browser will use the cached version instead of downloading the stylesheet over again.

Now lets say your test.css.sass.erb file is something huge like 50kB for example's sake.

  • Without precompilation your resource cost is:

    1. having to traverse that 50kB file 2 times on every request to compile it from erb > sass > css

    2. having to serve up that 50kB file on every request.

  • With precompiled assets that are overridden by conditional styles embedded in the html of the layout as explained in my first answer:

    1. Your compilation cost goes down to almost zero because the stylesheet is precompiled so there's nothing to do to it and your whatever.html.erb template that contains the conditional styles is already being compiled by your erb compiler so you're just adding the work of rendering variables.

    2. You only serve up the precompiled stylesheet once meaning you save ~50kB in bandwidth on every request, using only the bytes needed for the rendered dependent styles.

Hope this helps, for more information on how all of this works I would recommend starting with the Asset Pipeline Rails Guide.

makewavesnotwar
  • 975
  • 3
  • 8
  • 13
  • Thanks for the anser @h4pless ! So, is it not the same to waste resources for `erb` and `sass` pre-processing (my idea) and `erb` only in your way? Why only the later way (your way) pre-compiled and cacaeable? I am understanding your idea to build the dynamic CSS file using HTML style tags and html.erb file, but is this technique a good rails design (messing up css and html)? – gotqn May 06 '14 at 15:36
  • Added a second section to answer your question. – makewavesnotwar May 07 '14 at 13:57
  • +1, I have understand your idea. Unfortunately, I cannot see how to use it, because in my real case, at the top of dynamic CSS rules is dynamic `ID`. Also, I can translate my `SASS` to `CSS` but the result will be huge amount of `CSS` rules, difficult to change and maintain. Anyway, changing the `<%` to `<%=` solve the initial issue. Thanks. – gotqn May 07 '14 at 17:55