How to Create Dynamic CSS in Rails

How do I create dynamic CSS in Rails?

Currently there is a lot of options to generate dynamic css in rails.

You can use less css - is an extension to CSS with extra features.

Gem Less css for rails provides integration for Rails projects using the Less stylesheet language in the asset pipeline.

If you are using twitter bootstrap you may check this out less rails bootstrap.

Also you can use one more CSS extension language Sass for generating CSS. Here is a Saas rails gem.

Check out Dynamic CSS in Rails and Render Rails assets to string blog posts and article about Asset Pipeline

Related SO questions:

  • Best way to handle dynamic css in a rails app
  • Dynamic CSS in Rails asset pipeline, compile on fly
  • Rails: change CSS property dynamically?

How to use 'dynamic CSS' in Rails

Seems like an inline style would work fine here. In your ERB, in your student divs, just do:

<% @students.each do |student| %>
<div style="height: <%= student.height %>px; width: <%= student.width %>px;">
<!-- other student stuff -->
</div>
<% end %>

It's either this or generating a unique CSS class for every student first, then using it in each div.

ruby rails dynamic css class

The above scenario can be handled the following way:

<% positionRight||= false %>
<section class=<%= positionRight ? "custom-css-class" : "" %>>

and then

<%= render 'headers', positionRight: true, ...}  %>
OR
<%= render 'headers', ...} %>

How to create dynamic CSS based on user input

You can make your RoomsController respond to the CSS format as well in order to get this to work:

# app/controllers/rooms_controller.rb
class RoomsController
def show
@room = Room.find(params[:id])

respond_to do |format|
format.html
format.css
end
end
end

Then you need to implement a template to be rendered for the CSS format:

/* app/views/rooms/show.css.erb */
.room {
background-color: <%= @room.color1 %>;
color: <%= @room.color2 %>;
}

Note how this is very similar to a regular template. You need to make sure that this results in valid CSS.

You need to make sure the stylesheet is included when the user visits the page. Let's say a user can browse their room design when they visit /rooms/1. This will render a HTML template, which we could define as follows:

<!-- app/views/rooms/show.html.erb -->
<% content_for :stylesheet_includes do %>
<%= stylesheet_link_tag(room_path(@room, format: :css)) %>
<% end %>

<div class="room">
Room Contents Here
</div>

Notice that I've used content_for around the stylesheet link tag. We can use this to make sure the stylesheet link tag is rendered nicely in the head of the layout:

<!-- app/views/layouts/application.html.erb -->
<head>
<%= yield :stylesheet_includes %>
</head>

Of course you'll need to fill in the details yourself, but this would be the most logical approach for the problem.

How do I create dynamic CSS in Rails depending on boolean values?

In your view:

<% if task.important and task.urgent %>
<div class="background-color-red">
...
</div>
<% elsif task.important and !task.urgent %>
<div class="background-color-blue">
...
</div>
<% elsif !task.important and task.urgent %>
<div class="background-color-green">
...
</div>
<% elsif !task.important and !task.urgent %>
<div class="background-color-grey">
...
</div>
<%end%>

Then you can define the background-color in your css file with the classs above.

Dynamic CSS in Rails asset pipeline, compile on fly

Here is the solution I finally landed on:

  • I ended up switching over to bootstrap-sass instead https://github.com/thomas-mcdonald/bootstrap-sass
  • Made the following changes to my application.rb file to ensure that the :asset group is always included despite the environment:

    if defined?(Bundler)
    # If you precompile assets before deploying to production, use this line
    # Bundler.require(*Rails.groups(:assets => %w(development test)))
    # If you want your assets lazily compiled in production, use this line
    Bundler.require(:default, :assets, Rails.env)
    end
  • Used the concepts provided by Manuel Meure (Thank you Manuel!) of Kraut Computing found at http://www.krautcomputing.com/blog/2012/03/27/how-to-compile-custom-sass-stylesheets-dynamically-during-runtime/ .

    • I made some adjustments to suit my own needs, but the core concepts illustrated by Manuel were the foundation for my compilation process.
  • In my model (lets call it "Site"), I have a snippet of code that looks like this:

    # .../app/models/site.rb
    ...

    BASE_STYLE = "
    @import \"compass/css3\";

    <ADDITIONAL_STYLES>

    @import \"bootstrap\";
    @import \"bootstrap-responsive\";
    ".freeze

    # Provides the SASS/CSS content that would
    # be included into your base SASS content before compilation
    def sass_content
    "
    $bodyBackground: #{self.body_background};
    $textColor: #{self.text_color};
    " + self.css # Any additional CSS/SASS you would want to add
    end

    def compile_css(test_only = false, force_recompile = false)

    # SassCompiler is a modification of the information made available at the Kraut Computing link
    compiler = SassCompiler.new("#{self.id}/site.css", {:syntax => :scss, :output_dir => Rails.root.join('app', 'assets', 'sites')})

    # Bail if we're already compiled and we're not forcing recompile
    return if compiler.compiled? && !force_recompile && !test_only

    # The block here yields the content that will be rendered
    compiler.compile(test_only) {
    # take our base styles, slap in there some vars that we make available to be customized by the user
    # and then finally add in our css/scss that the user updated... concat those and use it as
    # our raw sass to compile
    BASE_STYLE.gsub(/<ADDITIONAL_STYLES>/, self.sass_content)
    }
    end

I hope this helps. I know its a deviation from the original post, but its deviated because this seemed to be the most attainable solution to the problem.

If I haven't answered a specific question you have, feel free to comment so I can expand where possible.

Thanks!

Create dynamic attributes and CSS classes in Rails

There are multiple ways to do this, but to start with what's probably the easiest:

  1. Make a helper that returns all the dynamic data required for your form. Looking at your code, it seems there are 3 things required for each checkbox - a title, a parameter name, and a full name. So, you could write a helper method like so:

    def services_array
    [
    { title: "pets", param_name: :pets_allowed, full_name: "Pets Allowed" },
    { title: "people", param_name: :allow_visitors, full_name: "Visits Allowed" },
    { title: "smoking_rooms", param_name: :permitted_smoking, full_name: "Smoking Allowed" }
    ]
    end
  2. In your view, loop over this:

       <%= form.fields_for :rules do |rules_form| %>
    <% services_array.each do |service| %>
    <div class="col l4 m2 s4">
    <label>
    <div class="input-field">
    <i class="material-icons-two-tone"><%= service[:title] %></i>
    <%= rules_form.check_box(service[:param_name], {}) %>
    <span for="<%= service[:param_name] %>"><%= service[:full_name] %></span>
    </div>
    </label>
    </div>
    <% end %>
    <% end %>

Generating dynamic CSS using templates

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.



Related Topics



Leave a reply



Submit