I'm finishing an online course about some web dev tools.
In the course, somehow, the instructor doesn't run into a problem with webpack while using a .jpg url in the .css. After searching on StackOverflow and Google I've managed to find the answer in using the url-loader webpack plugin.
This worked fine while I was running a local dev environment.
However, when I built the website for deployment, the .css file was sized in 200kb. After running the file through a beautifier, I've found that it was because of the .jpg code being injected into the .css. After deleting that portion, the file size dropped down to less than 16kb.
How can I configure my webpack so when it builds, it replaces the inserted .jpg to a url pointing towards the actual image file?
Here are:
The repo: https://github.com/sethalberdier/fictional-travel-site
The folder containing the huge css file (and also the one that is live): https://github.com/sethalberdier/fictional-travel-site/tree/master/docs
The code bits that matter, imo:
css:
#mixin atLarge {
background: url('/app/assets/images/testimonials-bg.jpg') top center;
}
inside webpack.config I have this rule for the exports module:
{
test: /\.(png|jpg)$/,
loader: 'url-loader',
},
As per url-loader docs, it seems like you need to specify a limit in your webpack config file. For instance:
{
loader: 'url-loader',
options: {
limit: 8192
}
}
If you want url-loader to never transform images into base64 strings, specify {limit: false} or use file-loader instead.
This should be simple, but I'm kind of stumped over how complex it's turning out to be.
What I would like to do is have a simple reference to my CSS in my Webpack config's entry list:
entry: {
'app.js': './src/app/index.js',
'app.css': './src/app/app.css',
},
output: {
filename: 'web/[name]',
},
app.css can import other files through the standard CSS syntax: #import url('./morestuff.css'); I would like these to be inlined as Webpack does for JS imports.
I would like that to be exported as normal. For the config above that means outputting it into 'dist/web/app.css'.
I don't want:
To import/require CSS in my JS
To have it output a JS file that either includes the CSS as a string, or dynamically loads the CSS file itself
To do any HTML rewriting
What I've tried/considered:
Using css-loader outputs JS instead of CSS.
Using css-loader along with mini-css-extract-plugin will import/concat the files as I'd like, but the actual entrypoint output file is JS, and the CSS file it makes is in the wrong folder and renamed to app.css.css. If I try mini-css-extract-plugin without css-loader then it can't handle # characters (I think it's trying to load the file as JS.)
Using the sequence of loaders ['file-loader', 'extract-loader', 'css-loader'] as indicated in the extract-loader documentation, but it behaves as above (except the CSS is output with a hashed filename.)
Lots of old SO questions refer to extract-text-webpack-plugin, but that is now an archived project.
style-loader is about HTML rewriting, not what I want.
Now this may not be the "proper" way to handle CSS in Webpack, which does seem to be based on importing everything from your JS, but it's surely a common enough pattern. Any ideas?
I wanted to include prebuilt theme for angular app. I included below line in app.component.css.
#import "../../node_modules/#angular/material/prebuilt-themes/indigo-pink.css";
I was surprised it didn't apply the theme to my app. Then from docs I inferred I should include, now it works but I am curious why?
#import "#angular/material/prebuilt-themes/indigo-pink.css";
Inside common stylesheet, style.css not app.component.css! and the path (../../node_modules/#angular/material/prebuilt-themes/indigo-pink.css) makes more sense than "~#angular/material/prebuilt-themes/indigo-pink.css"
I have following questions,
1.What does it needs import only in style.css an why not inside appcomponent.css?
2.Though the path ~#angular/material/prebuilt-themes/indigo-pink.cs leads to nothing, how is the angular-material could pick the theme?
3.What does '~' mean in the above path?
To give more info, I have included project structure
All the imports here are referenced relatively. It can be a hassle to remember how many folders to jump into and out of.
If you move your files around, you'll have to update all your import paths.
Let's look at how we can reference imports absolutely so that TypeScript always looks at the root /src folder when finding items.
Our goal for this will be to reference things like so:
import { HeaderComponent } from '#app/components/header/header.component';
import { FooterComponent } from '#app/components/footer/footer.component';
import { GifService } from '#app/core/services/gif.service';
This is similar to how Angular imports are referenced using #angular like #angular/core or #angular/router.
Setting Up Absolute Paths
Since TypeScript is what is in charge of transpiling our Angular apps, we'll make sure to configure our paths in tsconfig.json.
In the tsconfig.json, we'll do two things by using two of the compiler options:
baseUrl: Set the base folder as /src
paths: Tell TypeScript to look for #app in the /src/app folder
baseUrl will be the base directory that is used to resolve non-relative module names. paths is an array of mapping entries for module names to locations relative to the baseUrl.
Here's the original tsconfig.json that comes with a new Angular CLI install. We'll add our two lines to compilerOptions.
{
"compileOnSave": false,
"compilerOptions": {
...
"baseUrl": "src",
"paths": {
"#app/*": ["app/*"]
}
}
}
With that in our tsconfig.json, we can now reference items absolutely!
import { HeaderComponent } from '#app/components/header/header.component';
import { FooterComponent } from '#app/components/footer/footer.component';
import { GifService } from '#app/core/services/gif.service';
This is great because we can now move our files around and not have to worry about updating paths everywhere.
based on this:
/ - Site root
~/ - Root directory of the application
this can be useful too;
I am using Webpack 2 and webpack-dev-server together with Sass loader, actual configuration:
{
test: /\.scss/,
loaders: [
"style",
{ loader: "css", query: { modules: false, sourceMap: true } },
{ loader: "resolve-url" },
{ loader: "sass", query: { sourceMap: true } }
]
}
This works quite well and the image referenced in background: url() is processed by webpack and also replaced in the style for something like background-somehash.jpg, this file can be accessed by typing http://localhost:8080/background-somehash.jpg. It also works when I provide whole url (including localhost) in a style background definition using developer tools...
The only thing that don't work is the original css produced by webpack which looks like background: url(background-somehash.jpg). I also tried various urls like ./, ../, ../../ or ./images/ to try out if root was set somehow differently. What I don't get is that the file is readily available at the root...
EDIT:
When used together with extract-text-webpack-plugin which extracts styles into separate real styles.css file it works just fine. The question is why it doesn't work when final css is being served from javascript bundle ?
CLARIFICATION:
Everything is referenced correctly, the image is available, it all works when I extract css into separate file using extract-text-webpack-plugin it just doesn't work when the completely same css is served from bundle.js which is then referenced in the index.html like <link href="blob:..." rel="stylesheet">
Things you should check:
Is the referenced image recognized by webpack?
Just delete the image and check if the webpack build fails. If that's the case, it is not an issue with your loader configuration.
Check the requested URL with your browser developer tools
If the request is terminated with a 404:
Check if output.publicPath (webpack) / contentBase (webpack-dev-server) point to the same location. This is from the browser's perspective (=no absolute file paths)
If you're using a <base>-tag, you need to ensure that it does replace the base URL correctly.
Is there anyway to import a regular CSS file with Sass's #import command? While I'm not using all of the SCSS syntax from sass, I do still enjoy it's combining/compressing features, and would like to be able to use it without renaming all of my files to *.scss
After having the same issue, I got confused with all the answers here and the comments over the repository of sass in github.
I just want to point out that as December 2014, this issue has been resolved. It is now possible to import css files directly into your sass file. The following PR in github solves the issue.
The syntax is the same as now - #import "your/path/to/the/file", without an extension after the file name. This will import your file directly. If you append *.css at the end, it will translate into the css rule #import url(...).
In case you are using some of the "fancy" new module bundlers such as webpack, you will probably need to use use ~ in the beginning of the path. So, if you want to import the following path node_modules/bootstrap/src/core.scss you would write something like #import "~bootstrap/src/core".
NOTE:
It appears this isn't working for everybody. If your interpreter is based on libsass it should be working fine (checkout this). I've tested using #import on node-sass and it's working fine. Unfortunately this works and doesn't work on some ruby instances.
This was implemented and merged starting from version 3.2 (pull #754 merged on 2 Jan 2015 for libsass, issues originaly were defined here: sass#193 #556, libsass#318).
To cut the long story short, the syntax in next:
to import (include) the raw CSS-file the syntax is **without `.css`** extension at the end (results in actual read of partial `s[ac]ss|css` and include of it inline to SCSS/SASS):
#import "path/to/file";
to import the CSS-file in a traditional way syntax goes in traditional way, **with `.css` extension** at the end (results to `#import url("path/to/file.css");` in your compiled CSS):
#import "path/to/file.css";
And it is damn good: this syntax is elegant and laconic, plus backward compatible! It works excellently with libsass and node-sass.
__
To avoid further speculations in comments, writing this explicitly: Ruby based Sass still has this feature unimplemented after 7 years of discussions. By the time of writing this answer, it's promised that in 4.0 there will be a simple way to accomplish this, probably with the help of #use. It seems there will be an implementation very soon, the new "planned" "Proposal Accepted" tag was assigned for the issue #556 and the new #use feature.
UPD: on 26 October 2020 lib-sass was deprecated, therefore issue #556 was immediately closed.
__
answer might be updated, as soon as something changes.
Looks like this is unimplemented, as of the time of this writing:
https://github.com/sass/sass/issues/193
For libsass (C/C++ implementation), import works for *.css the same way as for *.scss files - just omit the extension:
#import "path/to/file";
This will import path/to/file.css.
See this answer for further details.
See this answer for Ruby implementation (sass gem)
You must prepend an underscore to the css file to be included, and switch its extension to scss (ex: _yourfile.scss). Then you just have to call it this way:
#import "yourfile";
And it will include the contents of the file, instead of using the CSS standard #import directive.
Good news everyone, Chris Eppstein created a compass plugin with inline css import functionality:
https://github.com/chriseppstein/sass-css-importer
Now, importing a CSS file is as easy as:
#import "CSS:library/some_css_file"
If you have a .css file which you don't wish to modify, neither change its extension to .scss (e.g. this file is from a forked project you don't maintain), you can always create a symlink and then import it into your .scss.
Creates a symlink:
ln -s path/to/css/file.css path/to/sass/files/_file.scss
Imports symlink file into a target .scss:
#import "path/to/sass/files/file";
Your target output .css file is going to hold contents from imported symlink .scss file, not a CSS import rule (mentioned by #yaz with highest comment votes). And you don't have duplicated files with different extensions, what means any update made inside initial .css file immediately gets imported into your target output.
Symbolic link (also symlink or soft link) is a special type of file
that contains a reference to another file in the form of an absolute
or relative path and that affects pathname resolution.
– http://en.wikipedia.org/wiki/Symbolic_link
You can use a third-party importer to customise #import semantics.
node-sass-import-once, which works with node-sass (for Node.js) can inline import CSS files.
Example of direct usage:
var sass = require('node-sass');,
importOnce = require('node-sass-import-once');
sass.render({
file: "input.scss",
importer: importOnce,
importOnce: {
css: true,
}
});
Example grunt-sass config:
var importOnce = require("node-sass-import-once");
grunt.loadNpmTasks("grunt-sass");
grunt.initConfig({
sass: {
options: {
sourceMap: true,
importer: importOnce
},
dev: {
files: {
"dist/style.css": "scss/**/*.scss"
}
}
});
Note that node-sass-import-once cannot currently import Sass partials without an explicit leading underscore. For example with the file partials/_partial.scss:
#import partials/_partial.scss succeeds
#import * partials/partial.scss fails
In general, be aware that a custom importer could change any import semantics. Read the docs before you start using it.
If I am correct css is compatible with scss so you can change the extension of a css to scss and it should continue to work. Once you change the extension you can import it and it will be included in the file.
If you don't do that sass will use the css #import which is something you don't want.
I figured out an elegant, Rails-like way to do it. First, rename your .scss file to .scss.erb, then use syntax like this (example for highlight_js-rails4 gem CSS asset):
#import "<%= asset_path("highlight_js/github") %>";
Why you can't host the file directly via SCSS:
Doing an #import in SCSS works fine for CSS files as long as you explicitly use the full path one way or another. In development mode, rails s serves assets without compiling them, so a path like this works...
#import "highlight_js/github.css";
...because the hosted path is literally /assets/highlight_js/github.css. If you right-click on the page and "view source", then click on the link for the stylesheet with the above #import, you'll see a line in there that looks like:
#import url(highlight_js/github.css);
The SCSS engine translates "highlight_js/github.css" to url(highlight_js/github.css). This will work swimmingly until you decide to try running it in production where assets are precompiled have a hash injected into the file name. The SCSS file will still resolve to a static /assets/highlight_js/github.css that was not precompiled and doesn't exist in production.
How this solution works:
Firstly, by moving the .scss file to .scss.erb, we have effectively turned the SCSS into a template for Rails. Now, whenever we use <%= ... %> template tags, the Rails template processor will replace these snippets with the output of the code (just like any other template).
Stating asset_path("highlight_js/github") in the .scss.erb file does two things:
Triggers the rake assets:precompile task to precompile the appropriate CSS file.
Generates a URL that appropriately reflects the asset regardless of the Rails environment.
This also means that the SCSS engine isn't even parsing the CSS file; it's just hosting a link to it! So there's no hokey monkey patches or gross workarounds. We're serving a CSS asset via SCSS as intended, and using a URL to said CSS asset as Rails intended. Sweet!
To import a regular CSS file into Sass:
Official Sass Documentation: Import CSS into Sass
Simple workaround:
All, or nearly all css file can be also interpreted as if it would be scss. It also enables to import them inside a block. Rename the css to scss, and import it so.
In my actual configuration I do the following:
First I copy the .css file into a temporary one, this time with .scss extension. Grunt example config:
copy: {
dev: {
files: [
{
src: "node_modules/some_module/some_precompiled.css",
dest: "target/resources/some_module_styles.scss"
}
]
}
}
Then you can import the .scss file from your parent scss (in my example, it is even imported into a block):
my-selector {
#import "target/resources/some_module_styles.scss";
...other rules...
}
Note: this could be dangerous, because it will effectively result that the css will be parsed multiple times. Check your original css for that it contains any scss-interpretable artifact (it is improbable, but if it happen, the result will be hard to debug and dangerous).
to Import css file in to scss simply use the this:
#import "src/your_file_path";
without using extension .css at the end
It is now possible using:
#import 'CSS:directory/filename.css';
I can confirm this works:
class CSSImporter < Sass::Importers::Filesystem
def extensions
super.merge('css' => :scss)
end
end
view_context = ActionView::Base.new
css = Sass::Engine.new(
template,
syntax: :scss,
cache: false,
load_paths: Rails.application.assets.paths,
read_cache: false,
filesystem_importer: CSSImporter # Relevant option,
sprockets: {
context: view_context,
environment: Rails.application.assets
}
).render
Credit to Chriss Epstein:
https://github.com/sass/sass/issues/193
Simple.
#import "path/to/file.css";