Part of my css file:
#accordion1 .slide_handle {
background:url(/assets/handles-1.png);
bottom:0;
cursor:pointer;
left:0;
position:absolute;
top:0;
width:40px;
}
Shows a accordion with text writen in handles.1.png. I am developing the site in 4 languages. How can I change the png file based on the language (locale) parameter? Is this possible? or load a complete new css file based on the language? (not suggested)
It's probably not a good idea to make your base CSS file dynamic based on user/session/environment. You'll be busting your cache unnecessarily.
What you need in your base css is the just the default. The very nature of the CSS allows following declarations to override the previous.
So, in you application's layout file you would load the standard base CSS with background image handling the default locale.
For locale specific overrides, you would include a minimal CSS file for that specific locale OR if the amount of configuration per locale is minimal include the style inline in the header.
views/layouts/application.html.erb
<html>
<head>
<%= stylesheet_link_tag :defaults %> # Load standard base css here
<% if user_locale.present? %>
<style type="text/css">
#accordion .slide_handle {
background: url(<%= asset_url("handles-#{user_locale}.png") %>);
}
</style>
<% end %>
</head>
<body>
Yada Yada Yada
<%= yield %>
</body>
</html>
I seriously suggest you to follow a convention which allows your user's locale identifier to be the only decision maker for assets per locale.
If your locales are en,de,fr. Don't name your assets handle-1.png, handle-2.png but ensure they are handle-en.png, handle-de.png *handle-fr.png*. Following a convention will save you a bundle of pain of unnatural and brittle configuration to manage down the track.
Summary, Go for base default CSS, and override minimal css in the header section after including the base css. Alternatively you could include a locale specific css file which ONLY has overrides.
<head>
<%= stylesheet_link_tag :defaults %>
<%= stylesheet_link_tag "base-#{user_locale}" if user_locale.present? %>
</head>
Updated with another alternate solution:
<body class="<%= user_locale.presence || 'en' %>">
</body>
In your CSS list out all your overrides per locale with body parent like the following
body.en #accordion .slide_handle {
background: ....
}
body.de #accordion .slide_handle {
background: ...
}
You could split them into multiple locale specific files of keep it all together in one css, whichever you prefer. But this local overrides would be automatically applied to you layout without any further changes and all locales are always available at all times in your css (you may or may not find it appealing).
I would prefer to separate your css files in following ways
A main.css file which will contain all general classes.
Some language related css files which will contain your language related classes.
The these language related css files can be named like: style-en.css, style-fr.css so on.
Now based on the language selection you should load proper style-*.css file along with the main.css
Related
I have a project where each page needs to import an extra css or JS file for only that page. Is it a good practise to use a different layout file for each view? (I don't want my view files to contain references to css/JS files).
I mean i can have a different layout for each view but i don't know if it is the optimal way to do it.
Best strategy:
Don't worry about it and just let your app handle this for you. Minified CSS and JS file delivery isn't really that slow/big and Rails will compile your assets into single files in production.
If you're determined, it is possible (your mileage may vary depending on your Rails version, and usage of Sprockets or Webpacker)
And you'll really want to understand the Rails asset pipeline (for your version of Rails) before you go nerfing these settings.
CSS inclusion/exclusion
You can ignore the asset pipeline and manually choose which CSS files to include, but you'll need to be diligent about doing this everywhere.
In your app/views/layouts/application.html file, you have a line like this inside the <head> tag:
<%= stylesheet_link_tag 'application', media: 'all' ... %>
This is loading application.css, which is a compiled version of app/assets/stylesheets/application.css and will include all CSS from all other files inside app/assets/stylesheets
Depending on your version of Rails, you'll need to turn off asset pre-compilation in either config/initializers/production.rb or config/initializers/assets.rb:
Rails.application.config.assets.precompile = []
Warning: this will turn off ALL asset compilation, including JS. Be sure you have a strategy to re-include JS files (based upon your asset pipeline)
You will now need to implement some logic to handle what CSS is included when. This could be in app/helpers/application_helper.rb. It could use the view helper [controller_name], (https://apidock.com/rails/ActionController/Metal/controller_name/class), for example, to determine which page you are on and fetch the correct CSS file.
Really rough example:
# app/helpers/application_helper.rb
module ApplicationHelper
def css_file_chooser
"/app/assets/stylesheets/#{controller_name}.css"
end
end
Then in your application.html file, you can do something like this:
<head>
<%= stylesheet_link_tag css_file_chooser, media: 'all' ... %>
...
</head>
The catch here is you'd need to have your specific CSS files named according to your controllers. E.g. if controller_name returns 'posts', you'd need to have a file: 'app/assets/stylesheets/posts.css'
If you do have some site-wide CSS that needs to be applied, you'd need to include a second stylesheet_link_tag:
In this case, if you put all your global styles into app/assets/stylesheets/global.css:
<head>
<%= stylesheet_link_tag 'global', media: 'all' ... %>
<%= stylesheet_link_tag css_file_chooser, media: 'all' ... %>
...
</head>
JS inclusion/exclusion
It's a bit dusty, but this article outlines a strategy that only loads a JS function or file if the <body> has certain class tags. I used to use it on my Rails 4 and 5 projects.
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.
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)" }
Instead of the page making another request for the CSS, I would like to have the Rails view render the CSS file in the page, so it's only 1 request.
Is this possible?
Altough the typical way to include CSS is using stylesheet_link_tag (in particular so it gets cached by the client), it is possible to put it directly in your <HEAD> in Rails 3.1:
<HEAD>
....
<STYLE type="text/css">
<%= YouAppName::Application.assets["your_stylesheet.css"].to_s.html_safe %>
</STYLE>
</HEAD>
I adapted this from this post.
<%= stylesheet_link_tag :all %> assuming your CSS file is in your public/stylesheets folder (which is the conventional place to store stylesheets). Of course, instead of :all, you can specify a specific file.
in your page's block.
I usually do this in my application.html.erb (which is a layout in your app/views/layouts) folder. But you can do this in any view file with a block.
Good luck!
Just wrap your CSS in
<style></style>
within your view.
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