How to inline CSS in Rails Header with Webpacker assets - ruby-on-rails

I would like to inline some CSS within my html. I have done this in the past with Sprockets putting something like that in my layouts/application.html.erb
<style>
<%= Rails.application.assets["application.css"].to_s.html_safe %>
</style>
Now I would like to do the same but with CSS assets bundled by Webpacker.
I am able to get the path of the file with Webpacker.manifest.lookup("application.css"), but I am missing the latest part to get the content and embed it into the layout
The rationale behind this:
To improve page speed I want to embed my critical CSS into the header of the HTML. Using Webpack and PostCss tools (PurgeCss). I am able to get a very compact very of my CSS for the above the fold of my home page.
The rest of the CSS is loading asynchronously with the usual packs helpers
Update 1
Here is the link to the article I wrote thanks to the answer.
https://dev.to/adrienpoly/critical-css-with-rails-and-webpacker-sprocketsless-part-1-2bck

Let's consider having a line in app/javascript/packs/application.js
import `../css/application.css'
and a file app/javascript/css/application.css with some useful content.
Then you spell
<style>
<%= File.read(File.join(Rails.root, 'public', Webpacker.manifest.lookup('application.css'))).html_safe %>
</style>
and it simply works.
Don't forget:
To set extract_css: true in config/webpacker.yml
Exec rake webpacker:compile after changing these .css and/or .js files. Or use webpack-dev-server for a things like Hot Module Replacement
Turn on (though this is default setting for development) config.public_file_server.enabled = true (as #Adrien mentioned in comments) for serving .js resources by puma

Related

When to use stylesheet_pack_tag instead stylesheet_link_tag with Rails 6

When creating a new rails project with Rails 6, it creates an application.html.erb with stylesheet_link_tag to load CSS and javascript_pack_tag for javascript files.
Now, rails 6 also provide a stylesheet_pack_tag, so my question is, when to use it? And if we use it, do we need to add all CSS files in app/javascript folder?
What is the best practice to load css, images with rails 6 and webpacker?
You should use stylesheet_pack_tag if you are importing any CSS in Webpack AND if you have enabled extract_css: true for any environment in config/webpacker.yml.
Given the following directory structure:
app/
javascript/
packs/
application.js
styles/
site.css
And the following code in application.js:
import '../styles/site.css'
You would use <%= stylesheet_pack_tag 'application' %> in your view, i.e., the name of the stylesheet matches the name of the "pack".
At this point, I also recommend renaming app/javascript to something like app/frontend. So, the key changes in config/webpacker.yml:
source_path: app/frontend
extract_css: true
app/
frontend/
packs/
application.js
styles/
site.css
Just to try to clarify this a little. This is based on my current understanding, which seems to work well enough but still might not be fully accurate.
CSS processed by the asset pipeline gets a css_link_tag and css that is imported via a Webpacker javascript bundle is referenced with css_pack_tag.
Asset pipeline css is in app/assets/stylesheets. Webpack css is in app/javascripts/wherever_you_want.
So in webpack, all of the css imported into a javascript bundle is eventually extracted to a servable file that can be referenced via the same name as the js bundle.
So if in app/javascripts/application.js you have:
import 'app/javascripts/css/one.css'
import 'app/javascripts/css/two.css'
import 'app/javascripts/css/three.css'
These will be referenced with
css_pack_tag 'application'
This comes out looking like this in my deploy logs
Entrypoints:
application (430 KiB)
css/application-9d82d487.css
js/application-9f45d7ff735a7a203f28.js
It also bears noting, as was mentioned above that this behavior is affected by the extract_css setting.
Presumably this is false in development by default and true in production. One big GOTCHA with this is that, at least in my case, the css_pack_tag wasn't actually "required" in development, in the sense that removing it had no effect because it wasn't extracted locally. It still "worked" because the css was loaded in the javascript and seemed to be applied somehow that way. So I removed these tags thinking they were unnecessary before my understanding improved. Unfortunately when I deployed to production on heroku some time later, none of my css was working and it took me a while to figure out why and remember that I had removed these css_pack_tag lines.

how to get webpacker to load css if extract_css is false

I have the following pack file:
// app/javascript/packs/styles.js
import 'bootstrap/dist/css/bootstrap'
Unless I set extract_css: true in config/webpack.yml I do not see how to get these styles to load.
If I set extract_css: true and include <%= stylesheet_pack_tag 'styles' %> in the layout everything seems to work.
But then what is the point of extract_css: false why would you ever NOT want this? Because its the default webpacker configuration I feel as if I am missing something important.
With extract_css: false, the styles are provided by the webpack js runtime as blob urls and injected into the document <head> inline (assuming you have a corresponding javascript_pack_tag).
One use case for this approach in development is to better support hot module reloading for CSS.
I'm not 100% sure, but I think if you extract the css, webpack will generate that standalone css file so you can link it. If you don't extract it, the CSS is bundled insde the js file and webpack will process it when the page loads and inserts the CSS in a style tag in the head tag of the document.
To extract the CSS from the js file it uses the mini-css-extract plugin.
https://webpack.js.org/guides/asset-management/#loading-css
In some cases you may want to keep the css on the bundle if you want to have all the js and css with only one request.

How to use Galleria plugin with Rails 4 Pipeline

I've recently had a trouble making Galleria plugin work with Rails 4 Pipeline and it took me a while to figure out how to make it work, so I wanted to share the solution in case somebody has the similar problem.
1) After downloading the plugin, put galleria-1.3.3.js ( it's the current version on the day I write it ) and galleria.classic.js ( or other style js file ) to vendor/assets/javascripts
2) Put galleria.classic.css ( or other theme stylesheet) to vendor/assets/stylesheets
3)Add //= require galleria-1.3.3 and //= require galleria.classic to your application.js file and *= require galleria.classic to application.css file
4) Asset pipeline adds fingerprints to the images and that makes it difficult to access them via usual css, so it's needed to add 'scss' to your galleria stylesheet, like this galleria.classic.css.scss and change url(classic-map.png) to asset_url("classic-map.png");and the same with the second image.
5)Open galleria.classic.js file and find there css:"galleria.classic.css" or something like that, and change it to css: false
6) Now you just need to add Galleria.run('#galleria'); or something different for other elements in some js file` and that should work :)
PS I am a noob in Rails and might have made some foolish mistakes, but I hope this will be helpful for anybody :)
I've just used this in Rails 4.1 as well with Galleria version 1.3.5. Copy the files from the main galleria folder into the various parts. Note that I'm using the classic/default (free) theme.
# app/assets/images
classic-loader.gif
classic-map.png
# app/assets/javascripts
galleria-1.3.5.js
galleria.classic.js
# app/assets/stylesheets (rename with .scss extension)
galleria.classic.css.scss
Open up galleria.classic.js and edit the line css: '...' to be css: false (around line 17 if you don't alter the code).
That's all I had to do in order to get it working with Rails 4. I played around with placing the files in the vendor/assets folders, but that became a nightmare fairly quickly. I found the above solution to be the simplest/cleanest.
Update: 2016-02-02
I'm still using this same setup with a Ruby 2.2.3, Rails 4.2.4, Galleria 1.4.2 setup.
Make sure in your galleria.classic.scss stylesheet you use the image-url("classic-map.png") helpers. No problems with this current setup.
Thank you this was absolutely amazing and exactly what I needed (I wish I'd found this before the 18 hours of hair pulling trying to get this to freaking work)!
I'll add some more things that might be helpful to whomever:
1) I had to change to galleria.css.scss.erb, so that it would interpolate the class-loader like this: background: url(<%= asset_path "/galleria/classic-loader.gif" %>) (not sure why, but the asset_url didn't work for me)
2) Related, I also had to take the classic-map and loader images and put them in app/assets/images
3) Per Galleria, the files I moved were the min.js files, not regular .js
4) In the header of the view, I added:
<script src="<%= asset_path "/galleria-1.3.5.min.js" %>" ></script>
<script src="<%= asset_path "/galleria.flickr.min.js" %>" ></script>
<script src="<%= asset_path "/galleria.classic.min.js" %>" ></script>
5) In the script on each view to load Galleria, I modified their instructions like such:
Galleria.loadTheme("<%= asset_path "/galleria.classic.min.js" %>");

How do I use Controller specific stylesheets in Rails 3.2.1?

Using Rails 3.2.1
I created a simple controller called Home using the command:
rails g controller Home index
And it created a new controller and view for me:
Notice how there are two stylesheets, one "Application" and one "Home". I can't find any documentation to support this assumption but I'm guessing you put styles that will only be applied to the "Home" views, in the Home.css.scss file, correct?
So as a test, I added in some global styles to Application.css.scss.erb and ran the application.
The styles applied as expected.
Next, I added in some rules to the Home.css.scss file and I visited a "Home/index" view, yet the style in that file wasn't attached, neither as a seperate CSS reference link, or even appended to the single Application.css.scss file. This is highly confusing to me, since the comments say:
// Place all the styles related to the Home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
Why aren't the rules written in Home.css.scss applied to my website?
It can work this way and Marek is quite correct, the answer is in the guide.
In the introduction to section 2.1:
For example, if you generate a ProjectsController, Rails will also add a new file at app/assets/javascripts/projects.js.coffee and another at app/assets/stylesheets/projects.css.scss. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as <%= javascript_include_tag params[:controller] %> or <%= stylesheet_link_tag params[:controller] %>.
So to set your application up to load controller specific stylesheets:
First, disable the default loading of all stylesheets by removing any extra requires in the application.css manifest.
Typically you'll see an entry like this:
*= require_tree .
If you still want to load some common css files, you can move them to a subdirectory and do something like this:
*= require_tree ./common
Second, In your application's layout add the suggested stylesheet_link_tag eg
<%= stylesheet_link_tag "application", :media => "all" %>
<%= stylesheet_link_tag params[:controller] %>
In this example we first load the application css file, we then load any css file that matches the current controller name.
I've solved this problem with a simple solution. I add to body the controller name as a class, editing views/layouts/application.html.slim:
body class=controller.controller_name
Or views/layouts/application.html.erb:
<body class="<%= controller.controller_name%>">
And then in my css I just use body.controller_name as a namespace:
/* example for /users/ */
body.users {
color: #000;
}
body.users a {
text-decoration: none;
}
For small projects I think it's fine.
I don't think it works that way (Home.css being applied only to Home controller actions). The different files are just for separation, to make it clearer what are the CSS rules describing. You can read this guide about the asset pipeline. I'm guessing you altered the default application.css.scss and removed the line importing all CSS files from app/assets/stylesheets.
TL;DR:
Ignore the comment, it's not made by Sass. But put:
#import "*";
into your application.css.scss file, and it will automatically import all the controller scss files.
Full read:
Disclaimer: This is my current understanding of the asset pipeline flow with and without Sass.
I think this comment is written by the standard Rails Asset pipeline (sprockets), and not by Sass:
// Place all the styles related to the Home controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
The standard pipeline will handle scss files but doesn't presume an application.css.scss file. But if you create such a file with Sass, then Sass will compile it to the application.css file.
If you use the normal Rails asset pipeline, without Sass, then sprockets would load the css file into the application.css file automatically (if that file has the default *= require_tree . line in it).
When you use Sass, with an application.css.scss file, Sass will compile this file into a application.css file. (I assume it would overwrite or take precedence over any application.css file you already had).
To get your home.css.scss file (and other controller files) automatically included, put this line into your application.css.scss file:
#import "*";
For reference, see this question:
Is it possible to import a whole directory in sass using #import?

How do I use CSS with a ruby on rails application?

How do I use CSS with RoR? When I link externally, I'm never able to see the files. I cp'd the .css file to every folder I could think of...views, controller, template, and nothing seems to work.
What do I need to do to enable external CSS files with a rails application? I'm new to rails, so forgive me if this is basic.
Put the CSS files in public/stylesheets and then use:
<%= stylesheet_link_tag "filename" %>
to link to the stylesheet in your layouts or erb files in your views.
Similarly you put images in public/images and javascript files in public/javascripts.
If you are using rails > 3 version, then there is a concept called asset pipeline. You could add your CSS to
app/assets/stylesheets
then it will automatically be picked up by the app. (this is useful as rails will automatically compress the CSS files)
read more here about the asset pipeline
Use the rails style sheet tag to link your main.css like this
<%= stylesheet_link_tag "main" %>
Go to
config/initializers/assets.rb
Once inside the assets.rb add the following code snippet just below the Rails.application.config.assets.version = '1.0'
Rails.application.config.assets.version = '1.0'
Rails.application.config.assets.precompile += %w( main.css )
Restart your server.
I did the following...
place your css file in the app/assets/stylesheets folder.
Add the stylesheet link <%= stylesheet_link_tag "filename" %> in your default layouts file (most likely application.html.erb)
I recommend this over using your public folder. You can also reference the stylesheet inline, such as in your index page.
The original post might have been true back in 2009, but now it is actually incorrect now, and no linking is even required for the stylesheet as I see mentioned in some of the other responses. Rails will now do this for you by default.
Place any new sheet .css (or other) in app/assets/stylesheets
Test your server with rails-root/scripts/rails server and you'll see the link is added by rails itself.
You can test this with a path in your browser like testserverpath:3000/assets/filename_to_test.css?body=1
To add to the above, the most obvious place to add stylesheet_link_tag is in your global application layout - application.html.erb.
With Rails 6.0.0, create your "stylesheet.css" stylesheet at app/assets/stylesheets.
Have you tried putting it in your public folder? Whenever I have images or the like that I need to reference externally, I put it all there.

Resources