How do I create dynamic CSS in Rails? - ruby-on-rails

what is the best/most efficient way of creating dynamic CSS with Rails. I am developing an admin area on a site, where I would like a user to be able to customize the style of their profiles(Colour mostly), which will also be saved.
Would you just embed ruby script in the css file?
Would you need to change the file extension from css?
Thanks.

In Rails 3.1, you can have your stylesheets pre-processed by erb.
Now let's say you have some dynamic styling called dynamic.css.scss.erb (the .erb at the end is important!) in app/assets/stylesheets. It will be processed by erb (and then by Sass), and as such can contain stuff like
.some_container {
<% favorite_tags do |tag, color| %>
.tag.<%= tag %=> {
background-color: #<%= color %>;
}
<% end %>
}
You can include it like any stylesheet.
How dynamic should it be?
Note that it will be only processed once, though, so if the values changes, the stylesheet won't.
I don't think there is a super efficient way to do have it completely dynamic yet, but it is still possible to generate the CSS for all requests. With this caveat in mind, here's a helper for that in Rails 3.1:
def style_tag(stylesheet)
asset = YourApplication::Application.assets[stylesheet]
clone = asset.class.new(asset.environment, asset.logical_path, asset.pathname, {})
content_tag("STYLE", clone.body.html_safe, type:"text/css")
end
Here's how to use it:
First, copy the above helper in app/helpers/application_helper.rb.
You can then include it in your page as follows:
<% content_for :head do %>
<%= style_tag "dynamic.css" %>
<% end %>
The rest of your page.
Make sure that your layout uses the content :head. For example, your layout/application.html.erb could look like:
...
<HEAD>
....
<%= yield :head %>
</HEAD>
...
I found this out thanks to this post.

You can use ERB with CSS, you just need to render css in the controller. However, for such a heavily requested resource, I do not recommend generating this every time. I would store the users stylesheet in memcached or redis, and recall from it when the page loads, rather than rerendering the file each time. When they update their style, you can expire the cache, just make sure it gets rebuilt when the page renders.

There has been a lot of development over the years, and I recently figured out how to do what this question is asking. And since it has been about 9-10 years since someone has answered this, I thought that I would put in my 2 cents.
As others have said, it is not good practice, and thus can not be done, to put ruby code directly into the CSS file as the CSS is precompiled and is not able to be dynamically changed within the file.... BUT, it can be dynamically changed outside the file!
I will need to give a quick synopsis of CSS variables in case future readers do not know how to use them.
CSS has the use of variables within its coding language to make it easier to change a lot of elements at one time. You put these variables at the top of the CSS file in a root section. Like this:
:root {
--primary: #0061f2;
--secondary: #6900c7;
}
Now, anytime you want to style an element one of those colors, you can simply put var(--variableName) like this:
.btn{
color: var(--secondary);
background-color: var(--primary);
}
.h1 {
color: var(--primary);
}
You can see how it would then be much easier to change the variable in the root section and thus change all other instances within the CSS.
Now for the dynamic Ruby part.
In the <head> section of your application file (or in the case of this question the file that holds the template for the admin's dashboard), you will need to redeclare the CSS variables with your dynamic variables and mark them as important. For example, let's say that you allow your user to choose primary and secondary colors for their dashboard and they are stored in the user's profile called like: user.primary_color and user.secondary_color. You will need to add this to your <head> section:
<style>
:root{
--primary: <%= user.primary_color %> !important;
--secondary: <%= user.secondary_color %> !important;
}
</style>
This !important tag will override the variables found in the CSS file thus dynamically allowing the user to change the CSS and view of their dashboard and have it remain persistent (as the values are saved in their profile).
I hope that this helps future developers.
Happy Coding!

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?

I just built this for another site. I have a controller action and a view that pulls color values out of the database, then renders a customized CSS based on the current user's account. To optimize, I am using the built in Rails page caching, which stores a copy on disk and serves it as a static asset. Nice and fast.
Here's an example from the ERB code
#header { background: <%= #colors["Header Stripe Background"] %>; border: 1px solid <%= #colors["Header Stripe Border"] %>; }
#header h1 {color: <%= #colors["Headline Color"] %>; }
#header p a { background: <%= #colors["Control Link Background"] %>; color: <%= #colors["Control Links"] %>;}
#header p a:hover {background: <%= #colors["Control Link Hover"] %>; text-decoration:underline;}

This solution defines some constants in config/site_settings.rb, which can then be used throughout the Rails application, as well as for automatically generating the CSS files whenever the Rails app starts and the CSS input files have been modified..
http://unixgods.org/~tilo/Ruby/Using_Variables_in_CSS_Files_with_Ruby_on_Rails.html

Related

how to use rails model attributes inside sass?

I want to use a string value (hex) that's submitted through a form as the background-color for a div class.
The form is:
<%= f.text_field :backgroundcolor %>
The html is:
<div class="bottle"></bottle>
And the css is:
.bottle {
background-color: <%= color.backgroundcolor %>
}
But I just get an invalid css error. How do I use these attributes in the sass? I could use them as inline css, but would prefer not to.
The only way you'd get the result you want is if you persisted the data (stored in the DB).
CSS
If the color var was available from the db, or some other source, you'd be able to call it into the CSS:
#app/assets/stylesheets/application.sass
.bottle
background-color: <%= Option.find_by(title: "color").value %>
This will give you access to the value stored in our fictitious model... however, it would not update on the fly (IE form submit).
Whenever you push your code to "production", Rails will expect to "precompile" the assets.
Precompilation is where all the assets are concatenated into single files (typically application.css). This process makes your assets static. Indeed, SASS / SCSS are just preprocessors for this process (they run before the minifier).
Whilst you can make your assets dynamic in production (as they are in dev), it drastically slows down your web app (it has to compile the assets for EACH call).
--
To resolve your issue, you'd best put your custom styling either into the <head> of the page, or inline on one of the elements:
#app/views/layouts/application.html.erb
<head>
<style>
.bottle { background-color: "<%= Color.find_by(name: 'bottle').value %>" }
</style>
</head>
...or...
<%= content_tag :div, style: "background-color: #{color.background-color}" %>
No, I don't like it either, but if you want dynamic values, that's what would have to be done. There are some alternatives, but you'll have to hack them together from the backend.

How can I access Rails objects in Sass?

In a Rails 3.1.0 project, I have Companies with a few customizable attributes like background_color and link_color. I want to be able to set some Sass variables like so:
$background_color: <%= #company.background_color %>
$link_color: <%= #company.link_color
...
This doesn't work because #company is nil when Sass does its thing. I'm not sure how to go about solving this in a way that's dynamic (companies can be created and colors can be changed and the views update immediately). Any suggestions?
I can think of a couple approaches off the top of my head:
Serve your stylesheet through a controller.
Use CSS classes to configure the colors and serve just that CSS through a controller, inlined partial, or a CSS #import.
Serving your stylesheet through a controller is pretty straightforward so there's not much to say. This might be a bit ugly and cumbersome.
For the second one, you'd add a couple extra CSS classes:
.custom-bg {
background-color: some-default-bg;
}
.link-fg {
color: some-default-fg;
}
/*...*/
Then any element that need to use the custom background color would need their usual CSS classes and custom-bg; similar shenanigans would be needed for the other configurable values. To supply the customized CSS, you could inline a <style> element into your HTML using a standard ERB partial or you could serve the CSS through a controller (either through <style src="..."> or #import). So you'd fake the SASSy goodness with old school multiple CSS classes in your HTML.
There's also JavaScript. You'd need some way to identify the elements that need their colors adjusted and then adjust them directly with things like this:
$('.need-custom-background').css('background-color', '...');
I think you might be able to do something just like what you have there, but you need to change the extensions of the files to '.css.scss.erb'
To follow up on this, I did create a stylesheet controller but it was rather contrived to get Sass parsing and asset pipeline load paths all working correctly. I ended up dumping that and reorganizing the styles so I could generate a static stylesheet for each company which gets regenerated and uploaded to S3 on company update.
Well, if you mean a dynamic object like a model loaded via a controller, you can't really, at least not very easily. This is because unlike HTML ERB templates, the SASS ones are generally rendered once and served statically unless something changes in the code or they are re-precompiled via rake (depending on your environment configs). But you can access some helper methods, global objects, and use some ruby in there by renaming the file with an "erb" extension e.g. application.css.scss.erb. See
https://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets
How can I use Ruby/Rails variables inside Sass?)
If you need styles based on dynamically loaded objects, like models, you can...
Write CSS styles literally in the template
Compile the stylesheets dynamically. See the top-rated answer here: How do I create dynamic CSS in Rails?
For some use cases you might accomplish the same thing by leveraging Rails/SASS's import path hierarchy (i.e. SASS #import 'partial_name_with_no_path' will search the importing SASS files folder first and then fall back to the top level - You can configure this as well).

No pics in my PDF created with PdfKit on heroku

I've an app which works fine in development and on my current production server.
I want to move it to FREE heroku (basic config: 1 dyno, 1 worker).
Unfortunately, the pdf generation (using PdfKit) is ok BUT without the pictures defined in my CSS.
I've followed a lot of tips including:
http://blog.mattgornick.com/using-pdfkit-on-heroku
http://jguimont.com/post/2627758108/pdfkit-and-its-middleware-on-heroku
http://code-fu.pl/blog/2011/05/17/pdfkit-heroku
Thoughts?
Found a workaround but I am still eager to know a better option:
I duplicated my view: one dedicated for html, another for pdf.
I removed all css using pics and put it in a separate file, included only in the view dedicated for html
finally, I inserted the css in the view dedicated to the pdf:
.foo { background-image:url(<%= Rails.root %>/public/images/bar.png) }
Very Ugly but works so please tell me if you've better
It's probably an issue with the way the url's are specified in the css. As I recall, they should be file system absolute paths. What does your css look like?
Here is how I answered my needs with:
Just one single view file
Just one css file
The trick was to pass the proper base_url to the css file dynamically, given I expected a pdf or html.
I decided to use LESS. Style compiles css in a different manner, given the base-url I provide in the DOM. This base-url is generated by a helper.
Here were my steps:
changed my style.css to style.less
Added to my view:
<%= stylesheet_link_tag "style.less", :rel => "stylesheet/less" %>
<script id="base_url" type="text/javascript" data="<%= assets_path %>"></script>
<%= javascript_include_tag "less.min.js" %>
In my helper:
def assets_path
if request.fullpath.include? ".pdf"
"#{Rails.root.join('public',"images","pictos")}"
else
"#{request.protocol}#{request.host_with_port}/images/pictos"
end
end
and in my style.less:
#base_url: `document.getElementById('base_url').getAttribute('data')`;
.foo { background-image:~"url(#{base_url}/bar.png)" }

How to write ruby code in css file

I am having an rails application,in which I need a css file in such a way that its property can be changed by ruby code.
ex. background_color :<%= ruby code that return background color %>
So a user can set its css property and will be applicable to only that user like a theme.
Thanks!
You can't/shouldn't do this. You will precompile your assets so there is no way of dynamically adapting to changing variables. It is a bad pattern to use.
A much easier approach is to either add a class .active .inactive on your elements (body). Or output inline css in your head for things like custom colors etc depending on users that are signed in.
What are you trying to do? It sounds like something you'd do to check whether you are in production or development? In which case you could do something like:
<body class='<%= "development" if Rails.env == 'development' %>'>
or even
<body <%= "style='background-color: red;'" if Rails.env == 'development' %>
You should never need to use ruby in your css and javascript, if you find yourself doing it you are probably approaching it in the wrong way. At least that is what I have found after many attempts to do this nicely.
You can do this in your head:
<style>
.user .name{
color: <%= current_user.chosen_color %>;
}
</style>
p.s. data-attributes are a very effective way of passing variables, etc to javascript
p.s.2. this is an adaptation of my answer here: Creating scss variable in config.rb for scss files I think it is relevant for anyone who comes here too, so I answered here too.
If you just need something simple, then you could do in your layout:
<head>
....
<% unless current_user.theme.nil? %>
<style>
body{
background:<%= current_user.theme.background_color%>;
}
</style>
</head>
<% end %>
If you're starting a new project, SASS is the way to go, and probably is the way to go if you have sufficient time. If just a couple of entries, it might not be bad to do it in the HTML.
Note: I feel a little dirty about this, but it'll work.

Possible to use stylesheet.css.erb in Rails?

Hey, I'm new to Rails and Ruby in general. I was wondering if it's possible to use an embedded ruby css file (css.erb), similar to using html.erb files for views.
For example, I'm using
<%= stylesheet_link_tag "main" %>
to link to my main.css file in public/stylesheets, but when I change the file extension from main.css to main.css.erb, it no longer renders the css..
Is this even possible, or is there a better way?
By the time this question was answered there was indeed no way to use .css.erb files in rails properly.
But the new rails 3.1 asset pipeline enables you to use asset helpers inside your css file. The css parsers is not binded a controller/action scope, but the ruby parser is now able to resolve some issues like image path references
.class { background-image: url(<%= asset_path 'image.png' %>) }
or embed an image directly into your css
#logo { background: url(<%= asset_data_uri 'logo.png' %>) }
source: http://guides.rubyonrails.org/asset_pipeline.html
You can also generate a "stylesheets" controller
./script/generate controller stylesheets main admin maintenance
You get something like this:
exists app/controllers/
exists app/helpers/
create app/views/stylesheets
exists test/functional/
exists test/unit/helpers/
create app/controllers/stylesheets_controller.rb
create test/functional/stylesheets_controller_test.rb
create app/helpers/stylesheets_helper.rb
create test/unit/helpers/stylesheets_helper_test.rb
create app/views/stylesheets/main.html.erb
create app/views/stylesheets/admin.html.erb
create app/views/stylesheets/maintenance.html.erb
And you can later use the app/views/stylesheets/ files as dynamically rendered css files.
The same method works for javascript files (javascripts controller)
I dont think so. Whats your intention - to use variables and have them be evaluated at runtime, or "compile" time (meaning like deploy time?). Also, what would be the ERB binding? Would it bind to the controller, like views and helpers are, so that ERB instance would have access to the instance variables set in the controller? I just pose this question as more of a theoretical exercise.
If you want to use variables in your CSS than you can use Haml's SASS. You dont get access to the controller's scope but you do get basic variables and looping. Plus other cool stuff like mixins.

Resources