Dynamic CSS with Rails

24 Oct 2008

I’ve sometimes wondered why CSS isn’t more dynamic.

Take, for example, the styles applied to the sub-menu on the services page of this site. To achieve the highlighting of the current service, I combine a Rails view and CSS as follows:

The Rails View

<div id="<%= @current_service.name %>">
  <div id="secondaryNav">
    <ul>
      <% @services.each do |service| %>
        <li class="<%= service.name %>"><%= link_to service.heading, service_path(service) %><li>
      <% end %>
    <ul>
  <div>
<div>

The CSS

#agileweb #secondaryNav .agileweb a,
#java #secondaryNav .java a,
#coaching #secondaryNav .coaching a,
#mentoring #secondaryNav .mentoring a,
#overview #secondaryNav .overview a,
#advice #secondaryNav .advice a { 
  color: #99CC00;
  text-decoration: underline;
}

Handling a new service

That does the trick nicely until I decide to add a new service. Wouldn’t it be nice to have that CSS regenerated dynamically whenever I create, update or delete a service?

Enter a new Ruby module:

# libdynamic_css.rb  
module DynamicCss  
  def generate_services_nav_links_css  
    return if RAILS_ENV == "test"  
    FileUtils.cd File.expand_path("publicstylesheets", RAILS_ROOT)  
    File.open("servicesnav.css", "w") do |out|  
      service_names = []  
      services = Service.find :all  
      services.each { |s| service_names << s.name }  
      service_names.each_with_index do |name, i|  
        out.print "##{name} #secondaryNav .#{name} a"  
        if i + 1 < service_names.size  
          out.puts ","  
        else  
          out.puts " {"  
        end  
      end  
      out.puts " color: #99CC00;"  
      out.puts " text-decoration: underline;"  
      out.puts "}"  
    end  
  end  
end  

Then a small adjustment to invoke the css regeneration via a filter in my admin services controller:

class Admin::ServicesController < AdminLayoutController  
  include DynamicCss

  after_filter :generate_services_nav_links_css, :only => [:create, :update, :destroy]

# remainder of controller
end  

Lastly, to ensure that the servicesnav.css file exists by the time one of the public services pages is requested:

# config/initializers/services_nav_css.rb

include DynamicCss

generate_services_nav_links_css  

Admittedly this is a specific case, but this example shows that it is relatively straightforward to dynamically generate CSS within a Rails app if required.

Other posts

Previous post: Code Syntax Highlighting

More recently: OSDC 2008 Earlybird registration closing this Friday

© 2024 Keith Pitty, all rights reserved.