Dynamically Generating CSS with Ember.js and Rails

I’m currently working on an Ember.js app that requires the color scheme to be customizable to match the branding of our client’s clients. Since our application’s CSS is being compiled by the Rails Asset Pipeline, and the colors would be fetched over an API call, this posed an interesting challenge.

Originally, there were many unknowns around how we would accomplish this. How would we recompile our SCSS? Would we wrap the Ember app under a Rails route that would insert customized CSS on the page? If we loaded the CSS via Ember, how would we insert it on the DOM?

Happily, the details all fell into place quite nicely. Here’s how we pulled it off:

1. Use the Application Route to Load the Custom Theme

Or use any other resource’s route. When loading the model for the route, we first make an API call to determine which colors to use, and then pull out the theme information and make a request to the Rails app that processes and returns the themed CSS as text. Then we create a model out of the two.

Loading the CSS as part of the route’s model ensures that we will have the CSS before any content displays on the page. When the page displays, it will be styled correctly immediately.

2. Insert the CSS into the DOM

It might be tempting to insert the CSS directly inside of a style tag in your template:

<style type=“text/css”>{{themeCSS}}>/style>

Unfortunately, this will break, because Ember will insert little <script> tags inside your <style> tag to keep track of the data binding. Conveniently, this will break the CSS, at least some of the time.

To fix this, I added a computed property to my controller:

themeStyleTag: (->
	“<style type=\”text/css\”>#{@get “themeCSS”}</style>”
).property “themeCSS”

And then in the template:

{{{themeStyleTag}}}

Be sure to use the triple-stache (or double ==, if using Emblem). This will prevent the HTML characters from being escaped, so you end up creating a valid DOM element.

3. Process and Return the SCSS within Rails

We solved this by creating a rails route that is responsible for accepting the primary/secondary colors and injecting them into some SCSS, which is then compiled. Our rails controller simply loads a view, templates the colors in via ERB, and then compiles the SCSS:

def themed
	scss = render_to_string(“themed”, locals: {
			primary_color: params[:primary_color],
		secondary_color: params[:secondary_color]
	}, formats: :scss)

	css = Sass::Engine.new(scss, syntax: :scss).render

	render text: css, content_type: Mime::CSS
end

Our view, themed.scss.erb, looks something like:

$primary-color: <%= primary_color %>;
$secondary-color: <%= secondary_color %>;

.thing-to-color { color: $primary-color; }

There are some caveats, however. You must be careful to sanitize the color input. You may want to implement caching. And finally, helpers from the asset pipeline, such as image-url(), are unavailable. For a few reasons, you’ll want this file to be as minimal as possible, only containing styles needed to customize colors, etc.

Conclusion

This approach has worked pretty well for us. It’s not perfect, but suits our needs. And it works nicely with Ember’s asynchronous routing to prevent any glitches from displaying on the screen as the theme CSS is loaded.