CSS Modules composes not working - css

I'm trying to setup css modules with postcss + cssnext. It all seems to be working fine, except that the composes keyword is simply not working. The rule vanishes when the bundle is compiled.
Here's my webpack config file:
'use strict'
const path = require('path')
const webpack = require('webpack')
const HtmlPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'inline-source-map',
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
path.join(__dirname, 'src', 'index')
],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]-[hash].js',
publicPath: ''
},
plugins: [
// new DashboardPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new HtmlPlugin({
title: 'Github App',
template: path.join(__dirname, 'src', 'html', 'template-dev.html')
})
],
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
include: /src/,
use: 'babel-loader'
}, {
test: /\.css$/,
exclude: /node_modules/,
include: /src/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[local]--[hash:base64:5]'
}
}, {
loader: 'postcss-loader',
options: {
ident: 'postcss'
}
}]
}]
},
resolve: {
alias: {
Src: path.join(__dirname, 'src'),
Components: path.join(__dirname, 'src', 'components')
}
}
}
I'm using style-loader for this dev environment so I can use hot reloading. The css file is being imported like this: import './app.css'
app.css:
:global{
.app {
float: left;
padding: 10px;
width: 100%;
}
}
.className {
color: green;
background: red;
}
.otherClassName{
composes: className;
color: yellow;
}
this results in:
My postcss.config.js file:
module.exports = {
plugins: {
'postcss-import': {},
'postcss-cssnext': {
browsers: ['last 2 versions', '> 5%']
},
'postcss-nested': {}
}
}
Am I missing something to get composes to work?

Looks like this is fine:
The implementation of webpack's css_loader is to add both classes when exporting the styles (see https://github.com/webpack-contrib/css-loader#composing)
This also makes more sense, since it will ultimately render out less CSS code.
Try importing the styles and apply them to an HTML node and you will see it should receive both classes.
In your example it would have done something like:
exports.locals = {
className: 'className-2yqlI',
otherClassName: 'className-2yqlI otherClassName-1qAvb'
}
So when you do:
import styles from '../app.css'
// ...
<div className={styles.otherClassName} />
The div gets both classes.

Related

How to correctly load .otf/.tff fonts in a React app via a Webpack pipeline?

I'm having a hell of a time making this happen and it's possible the various guide I've been following are outdated or I messed something up. I know this has been a common question, and I've played with awesome font, loading it via scss, etc... and it hasn't worked, plus I found it overly complicated where the below approach is also supposed to work and more straight forward.
The webpack error (at the bottom) is thrown by css-loader which isn't the file-loader or url-loader (I tried both), so I suspect the problem is the css-loader is trying to import/find by .otf font file and can't.
If anyone has a clue, it would really help. I need to pack in the font file so my app can run offline.
Here's my file structure:
./webkacp.config.js
./src/
./fonts/AvenirLTStd-Roman.otf
./index.css
./index.jsx
Here's my webpack.config.js:
var HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}
],
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'#': path.resolve(__dirname, 'src/'),
}
},
plugins: [new HtmlWebpackPlugin({
template: './src/index.html'
})],
devServer: {
historyApiFallback: true
},
externals: {
// global app config object
config: JSON.stringify({
apiUrl: 'http://127.0.0.1:4000'
})
}
}
Here's my index.css:
#font-face {
font-family: "MyFont";
src: url("./fonts/AvenirLTStd-Roman.otf") format("otf");
/* Add other formats as you see fit */
}
html, body {
font-family: 'MyFont', sans-serif;
}
Here's my index.jsx:
import React from 'react';
import { render } from 'react-dom';
import { App } from './App';
import 'bootstrap/dist/css/bootstrap.min.css';
import '#/index.css';
render(
<App />,
document.getElementById('app')
);
Finally, here's the webpack error:
ERROR in ./src/index.css (./node_modules/css-loader/dist/cjs.js!./src/index.css)
Module not found: Error: Can't resolve './fonts/AvenirLTStd-Roman.otf' in '/var/sphyrna/csdpac-services/images/csdpac-unified/frontend/src'
# ./src/index.css (./node_modules/css-loader/dist/cjs.js!./src/index.css) 4:36-76
# ./src/index.css
# ./src/index.jsx
Got it.
Two changes were needed:
Skip the urls in css-loader so it doesn't try to import/resolve the file.
Remove the san-serif in my css.
Works now. Hope this helps someone else. None of the online guides mention skipping the url.
Here's my updated css:
#font-face {
font-family: "MyFont";
src: url("./fonts/AvenirLTStd-Roman.otf") format("otf");
/* Add other formats as you see fit */
}
html, body {
font-family: 'MyFont';
}
The Webpack.config.js:
var HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader'
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {url:false}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}
],
}
]
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'#': path.resolve(__dirname, 'src/'),
}
},
plugins: [new HtmlWebpackPlugin({
template: './src/index.html'
})],
devServer: {
historyApiFallback: true
},
externals: {
// global app config object
config: JSON.stringify({
apiUrl: 'http://127.0.0.1:4000'
})
}
}

Combining tailwind css with sass using webpack

I'm struggling a bit getting Tailwind CSS to work with SASS and Webpack. It seems like the postcss configuration for tailwind doesn't really do anything in terms of processing #tailwind preflight, #tailwind components and #tailwind utilities
My set up is as follows:
layout.scss
#import "~tailwindcss/preflight.css";
#import "~tailwindcss/components.css";
.my-class {
#apply text-blue;
#apply border-red;
}
#import "~tailwindcss/utilities.css";
entry.js
import '../css/src/layout.scss';
postcss.config.js
const tailwindcss = require('tailwindcss');
const purgecss = require('#fullhuman/postcss-purgecss');
const cssnano = require('cssnano');
const autoprefixer = require('autoprefixer');
module.exports = {
plugins: [
tailwindcss('./tailwind.js'),
cssnano({
preset: 'default',
}),
purgecss({
content: ['./views/**/*.cshtml']
}),
autoprefixer
]
}
webpack.config.js
// NPM plugins
const autoprefixer = require('autoprefixer');
const WebpackNotifierPlugin = require('webpack-notifier');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: {
main: './scripts/entry.js'
},
output: {
filename: '[name].bundle.js',
publicPath: './'
},
watch: false,
externals: {
jquery: 'jQuery'
},
mode: 'development',
plugins: [
// Notify when build succeeds
new WebpackNotifierPlugin({ alwaysNotify: true }),
// Extract any CSS from any javascript file to process it as LESS/SASS using a loader
new MiniCssExtractPlugin({
fileame: "[name].bundle.css"
}),
// Minify CSS assets
new OptimizeCSSAssetsPlugin({}),
// Use BrowserSync plugin for file changes. I.e. if a CSS/SASS/LESS file changes, the changes will be injected directly in the browser with no page load
new BrowserSyncPlugin({
proxy: 'mysite.local',
open: 'external',
host: 'mysite.local',
port: 3000,
files: ['./dist/main.css', './views', './tailwind.js']
},
{
// disable reload from the webpack plugin since browser-sync will handle CSS injections and JS reloads
reload: false
})
],
module: {
rules: [
{
// Transpile ES6 scripts for browser support
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
use: [
{
loader: 'file-loader'
}
]
},
{
// Extract any SCSS content and minimize
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
plugins: () => [autoprefixer()]
}
},
{
loader: 'sass-loader',
options: {
plugins: () => [autoprefixer()]
}
}
]
},
{
// Extract any CSS content and minimize
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{ loader: 'postcss-loader' }
]
}
]
}
};
When I run Webpack, everything runs just fine, but the content of /dist/main.css is:
#tailwind preflight;#tailwind components;#tailwind utilities;.my-class{#apply text-blue;#apply border-red}
I suspect it's related to the (order of) loaders, but I can't seem to figure out why it's not getting processed correctly.
Does anyone know what I'm doing wrong here? :-)
Thanks in advance.
Wow, so after fiddling around with the loaders even more, I made it work :-) For future reference:
I added options: { importLoaders: 1 } to the css-loader for SCSS files and removed: plugins: () => [autoprefixer()] from the postcss-loader in my webpack.config.js file.
Full, updated webpack.config.js file:
// NPM plugins
const autoprefixer = require('autoprefixer');
const WebpackNotifierPlugin = require('webpack-notifier');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: {
main: './scripts/entry.js'
},
output: {
filename: '[name].bundle.js',
publicPath: './'
},
watch: false,
externals: {
jquery: 'jQuery'
},
mode: 'development',
plugins: [
// Notify when build succeeds
new WebpackNotifierPlugin({ alwaysNotify: true }),
// Extract any CSS from any javascript file to process it as LESS/SASS using a loader
new MiniCssExtractPlugin({
fileame: "[name].bundle.css"
}),
// Minify CSS assets
new OptimizeCSSAssetsPlugin({}),
// Use BrowserSync plugin for file changes. I.e. if a CSS/SASS/LESS file changes, the changes will be injected directly in the browser with no page load
new BrowserSyncPlugin({
proxy: 'mysite.local',
open: 'external',
host: 'mysite.local',
port: 3000,
files: ['./dist/main.css', './views', './tailwind.js']
},
{
// disable reload from the webpack plugin since browser-sync will handle CSS injections and JS reloads
reload: false
})
],
module: {
rules: [
{
// Transpile ES6 scripts for browser support
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
use: [
{
loader: 'file-loader'
}
]
},
{
// Extract any SCSS content and minimize
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader',
options: {
plugins: () => [autoprefixer()]
}
}
]
},
{
// Extract any CSS content and minimize
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{ loader: 'postcss-loader' }
]
}
]
}
};
There is an extension called tailwindcss-transpiler which compiles your layout.tailwind.scss files into pure CSS files.It also optimizing features.I hope it will be useful.
For VS Code
https://marketplace.visualstudio.com/items?itemName=sudoaugustin.tailwindcss-transpiler
For Atom
https://atom.io/packages/tailwindcss-transpiler

Webpack 4: mini-css-extract-plugin + file-loader not loading assets

I'm trying to move assets (images and fonts) used in one of my .scssfiles, but it seems that they get ignored:
This is my .scss file:
#font-face {
font-family: 'myfont';
src: url('../../assets/fonts/myfont.ttf') format('truetype');
font-weight: 600;
font-style: normal;
}
body {
color: red;
font-family: 'myfont';
background: url('../../assets/images/bg.jpg');
}
And this is my webpack.config.js:
const path = require('path');
const { CheckerPlugin } = require('awesome-typescript-loader');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
target: 'node',
entry: path.resolve(__dirname, 'server.tsx'),
output: {
filename: 'server_bundle.js',
path: path.resolve(__dirname, 'build'),
publicPath: '/build'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
module: {
rules: [{
test: /\.(tsx|ts)?$/,
loader: 'awesome-typescript-loader',
options: {
jsx: 'react'
}
},
{
test: /\.(scss|sass|css)$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
loader: 'file-loader',
options: { outputPath: 'public/images' }
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader',
options: { outputPath: 'public/fonts' }
}
]
},
plugins: [
new CheckerPlugin(),
new MiniCssExtractPlugin({
filename: 'public/styles_bundle.css',
chunkFilename: "public/styles/[id].css"
})
]
}
I'm getting this .css file in my browser as the output (Note the name of the image):
body {
color: red;
background: url("../../assets/images/myimage.jpg");
}
And in my public directory I get this:
public/
styles_bundle.css
There are two problems here:
Fonts are not compiled (No public/fonts/etc...)
Images are not compiled
I've been trying everything, but I don't know what may be happening here... Any Ideas?
I have just fixed a similar issue. If you change the url option to true, you might see failed image URL references.
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
Or you can manual check whether the path reference is correct.
url('../../assets/images/bg.jpg')
I think the images folder doesn't get created because all the image resource links are incorrect.
For the problem I was fixing, the references were all wrong and I couldn't fix them so I just used this webpack copy plugin to copy files to the right dist folder location.
you need url-loader
{
test: /\.(jpg|png|gif|woff|eot|ttf|svg)/,
use: {
loader: 'url-loader', // this need file-loader
options: {
limit: 50000
}
}
}
try the below configuration
{
test: /\.s[ac]ss$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: ''
}
},
{
loader: "css-loader",
options: { sourceMap: true}
},{
loader: "sass-loader",
options: { sourceMap: true }
}
]
},
{
test: /\.(png|jpg|jpeg|gif)$/i,
loader: 'file-loader',
options: { outputPath: 'assets/images', publicPath: '../images', useRelativePaths: true }
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
loader: 'file-loader',
options: { outputPath: 'assets/fonts', publicPath: '../fonts', useRelativePaths: true }
}
After this my scss file started accepting the relative URL and the compiled files also mapped accordingly using relative path.
The mini-css-extract-plugin has a 'publicPath' option (see here). It basically tells the generated css where the external resources like fonts, images, etc are to be found. In my case, setting it to '../' and configuring all the file loaders to their proper directories, this worked perfectly.
Basically looks like:
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '/public/path/to/', // <-- This is what was helping.
},
},
'css-loader',
],
},
],
I had this issue too. I was using the following versions:
package.json
"mini-css-extract-plugin": "^0.10.1",
"webpack": "^5.3.1",
For me everything was compiling just fine until I added the following into a scss file:
.xmas {
background-image: url("./img/Xmas2020/SurveyBanner.png");
height: 150px;
}
The backgound-image url was the problem. When I changed it to an absolute path it worked:
.xmas {
background-image: url("/img/Xmas2020/SurveyBanner.png");
height: 150px;
}
webpack.config.js
This is just to show how I was putting images into the output directory. I guess there are different ways to do this, but I was using CopyWebpackPlugin.
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: './src/static/img', to: './img', force: true },
]
}),
]
This link helped me https://github.com/webpack-contrib/mini-css-extract-plugin/issues/286#issuecomment-455917803
Had the exact problem with webpack 5. The answer is in Asset Modules
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'images/[hash][ext][query]' //set the pattern for your filename here
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource' //replaces file-loader, url-loader etc
}
]
},
};

Update UI Framework CSS for new Web Components

I'm using Grommet as a UI Framework for my React page. For web components not covered by Grommet (e.g. a web chat control) what is the easiest way to style the additional web components not covered by the Grommet CSS. Would I just add my own custom.scss?
#import "includes/colors";
#import "includes/settings";
#import "includes/card-size";
webchat {
body .wc-app, .wc-app button, .wc-app input, .wc-app textarea {
font-family: 'Segoe UI', sans-serif;
font-size: 15px;
}
...
}
In my app.js file I have the folllowing import call...
import webachatcss from '../app/custom.scss';
Then under render() I have the following code
<Chat classname='webchatcss' directLine=.... />
UPDATE: I have installed the SASS-loader and updated my webpack.config.js which now looks like this (rules section is the latest change).
var path = require('path');
var webpack = require('webpack');
module.exports = {
context: path.join(__dirname, 'app'),
entry: ['./index.html','./app.js'],
  output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
  module: {
    loaders: [
{
test: /.js?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
},
{
test: /\.html$/,
loader: "file-loader?name=[name].[ext]",
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
rules: [{
test: /\.scs$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "sass-loader"
}]
}]
},
{
test: /\.png$/,
loader: "url-loader?limit=100000"
},
{
test: /\.jpg$/,
loader: "file-loader"
}
],
  },
resolve: {
alias: {
react: path.resolve('node_modules/react'),
},
},
devServer: {
historyApiFallback: true
}
};
The location of my error has now changed with the error now appearing in the scss (which appears to be a step closer). However I still get the error "You may need an appropriate loader to handle this file type"
You can import css files to your code and use those styles or you can create styles with react.
import '/path/to/your/css/style.css';
// and on your component
<WebChat className="webchat" />
// or
const styles = {
webchat: {
fontSize: 14,
color: '#303030'
}
};
<WebChat style={ styles.webchat } />

reactjs, getting sperated css output using webpack ExtractTextPlugin

Hope someone has an idea.
I want to have two files, one for mobile and one for desktop/tablet. I haven't yet found any solution with webpack.
At the moment I include my css over the js-code like this:
import * as React from "react";
import {Component} from "react";
require('./image.scss');
And this is my current webpack config:
let path = require("path");
let webpack = require("webpack");
let nodeExternals = require("webpack-node-externals");
let ExtractTextPlugin = require('extract-text-webpack-plugin');
const desktopCSS = new ExtractTextPlugin({
filename: 'css/application-desktop.min.css',
allChunks: true
});
const mobileCSS = new ExtractTextPlugin({
filename: 'css/application-mobile.min.css',
allChunks: true
});
module.exports = [
{
entry: './src/client/Client.tsx',
output: {
path: __dirname + '/public/assets',
filename: 'js/application.min.js'
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.scss$/,
include: [
path.resolve(__dirname, 'src/common/elements')
],
use: desktopCSS.extract({
use: [
{
loader: "css-loader"
}, {
loader: "sass-loader",
options: {
data: "$isWide: true;$isMobile: false;"
}
}]
})
},
{
test: /\.scss$/,
include: [
path.resolve(__dirname, 'src/common/elements')
],
use: mobileCSS.extract({
use: [
{
loader: "css-loader"
}, {
loader: "sass-loader",
options: {
data: "$isWide: false;$isMobile: true;"
}
}]
})
},
{
test: /\.(png|jpg|svg)$/,
loader: 'file-loader?name=[path][name].[ext]&context=src/'
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.scss', '.png', '.jpg', '.svg']
},
plugins: [
new webpack.LoaderOptionsPlugin({
debug: true
}),
desktopCSS,
mobileCSS
],
externals: [{
'react': 'React',
'react-dom': 'ReactDOM',
'react-redux': 'ReactRedux',
'redux': 'Redux'
}, 'bowser', nodeExternals()],
}
];
here is a example scss file:
#import "../../data/colors";
.organism-header
{
.top
{
border-bottom: 2px solid $color-golden;
#if $isWide
{
height: 105px;
}
#if $isMobile
{
height: 70px;
.element-icon.type-burger
{
top: 17px;
margin-left: 20px;
}
.element-icon.type-logo
{
position: absolute;
top: 10px;
right: 20px;
}
}
}
}
I want to use $isWide and $isMobile inside my sass files to separate the output. But with the current way I only got one file application-desktop.min.css
Any Idea or a another/better way? Thanks!
Greetings
crazyx13th

Resources