iTranslated by AI
Compiling Sass with npm scripts (Dart Sass Shared Management, Stylelint, and PostCSS)
Requirements
- Target Sass files in the
srcdirectory for compilation and output to thehtdocsdirectory. - Apply PostCSS to the CSS files in the
htdocsdirectory, and execute optimization with cssnano only for production builds. - Use Dart Sass and share variables and functions so they can be used with
@use. - Run Stylelint (with auto-fix) every time a Sass file is saved.
Installing Packages
Install the necessary packages related to CSS.
npm i -D autoprefixer cssnano postcss postcss-cli prettier sass stylelint stylelint-config-standard-scss stylelint-prettier stylelint-scss
Install libraries to manage reset CSS and media queries (optional).
npm i normalize.css sass-mq
Also install npm-run-all, which allows for concise cross-platform descriptions, and onchange, which provides file watching functionality.
npm i -D npm-run-all onchange
Directory Structure
The project is configured with the following directory structure.
.
├── postcss.config.js
└── src
└── assets
└── css
├── components
│ ├── _Button.scss
│ ├── _LinkText.scss
│ └── _index.scss
├── global
│ ├── _base.scss
│ ├── _index.scss
│ ├── function
│ │ ├── _index.scss
│ │ ├── _rem.scss
│ │ └── _strip-unit.scss
│ ├── mixin
│ │ ├── _hack.scss
│ │ ├── _index.scss
│ │ └── _sr-only.scss
│ └── variable
│ ├── _easing.scss
│ ├── _global.scss
│ ├── _index.scss
│ └── _mq.scss
├── namespace
│ └── common
│ ├── _Br.scss
│ ├── _Button.scss
│ ├── _LinkText.scss
│ ├── _SrOnly.scss
│ └── _index.scss
└── site.scss
-
global-
function: Site-wide functions -
variable: Site-wide variables -
mixin: Site-wide mixins -
_base.scss: Site-wide default styles
-
-
components: Styles for components converted into mixins -
namespace: Site-wide styles or styles per category
Setting up Dart Sass
site.scss
In site.scss, we use @use to load the actual declarations to be output, excluding variables and functions.
@charset "UTF-8";
@use "../../../node_modules/normalize.css/normalize";
@use "global/_base";
@use "namespace/common";
namespace/common
namespace/common/_index.scss forwards site-wide styles as follows:
@forward "_Br";
@forward "_Button";
@forward "_Delimiter";
@forward "_LinkText";
@forward "_SrOnly";
For example, _Delimiter.scss assigns class names starting with .common- as follows:
.common-Delimiter {
display: inline-block;
}
For a product page, you would create something like _List.scss in namespace/product/ and assign class names like .product-List.
global
global/_index.scss loads variables, functions, and libraries as follows:
@forward "function";
@forward "variable";
@forward "mixin";
@import "../../../../node_modules/sass-mq/mq";
For function, variable, and mixin, each file is imported via @forward in their respective _index.scss files.
@forward "_rem";
@forward "_strip-unit";
@forward "_global";
@forward "_easing";
@forward "_mq";
@forward "_hack";
@forward "_sr-only";
The path ../../../../node_modules/sass-mq/mq imports "sass-mq" directly from node_modules.
_mq.scss is configured as follows:
$mq-breakpoints: (
sm: 375px,
md: 768px,
lg: 1024px,
xl: 1440px,
);
@import "../../../../../node_modules/sass-mq/_mq";
components
components/_index.scss loads styles for components as follows:
@forward "_Button";
@forward "_LinkText";
For example, you can define default styles in components/_Button.scss like this:
@use "../global" as g;
@mixin Button() {
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
max-width: 100%;
margin: 0;
padding: g.rem(15) g.rem(44) g.rem(14);
text-align: center;
text-decoration: none;
font-family: inherit;
font-size: g.rem(15);
line-height: g.div(21, 15);
border: 1px solid transparent;
border-radius: g.rem(999);
background: transparent;
color: inherit;
cursor: pointer;
transition-timing-function: g.$ease;
transition-duration: g.$transition-duration;
appearance: none;
&[type="button"],
&[type="reset"],
&[type="submit"] {
appearance: none;
}
}
Using global and components with @use
Here is an example of using them in namespace/common/_Button.scss.
- Refer to
globalasgandcomponentsascusing@use. - Include the button component with
@include c.Button;. - Convert px to rem using site-wide functions, such as
padding-top: g.rem(14);.
@use "../../global" as g;
@use "../../components" as c;
.common-Button {
@include c.Button;
}
.common-Button.-full {
width: 100%;
max-width: none;
}
.common-Button.-auto {
width: auto;
min-width: auto;
}
.common-Button.-large {
min-height: g.rem(70);
padding-top: g.rem(14);
padding-bottom: g.rem(14);
font-size: g.rem(17);
border-width: 2px;
}
.common-Button.-small {
padding: g.rem(12) g.rem(28) g.rem(11);
font-size: g.rem(13);
}
This concludes the Dart Sass configuration.
Setting up Stylelint
Create .stylelintrc.
touch .stylelintrc
I have configured a minimal setup as follows:
{
"extends": [
"stylelint-config-standard-scss",
"stylelint-prettier/recommended"
],
"syntax": "scss",
"plugins": [
"stylelint-scss",
"stylelint-prettier"
],
"rules": {
"prettier/prettier": [
true,
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"singleQuote": false,
"trailingComma": "all",
"bracketSpacing": false
}
],
"selector-class-pattern": null,
"scss/selector-no-union-class-name": true,
"scss/at-mixin-pattern": null
}
}
- Since Stylelint v14, a configuration file with SCSS extensions is required, so I'm using "stylelint-config-standard-scss" (you can use Sass just by adding settings to
.stylelintrc, but I chose this for better clarity). - Since Stylelint v15, formatting-related rules have been deprecated, so I've removed the settings listed in the Deprecated list.
- The rules are as follows:
-
selector-class-pattern: Since class names other than kebab-case cause errors, I've set it to
nullto remove restrictions (you could configure this in detail using regular expressions depending on the project). -
scss/selector-no-union-class-name: Setting this to
trueprohibits generating new class names like&_Element. -
scss/at-mixin-pattern: Since mixin names other than kebab-case cause errors, I've set it to
nullto remove restrictions (regular expressions are also available for project-specific configurations).
-
selector-class-pattern: Since class names other than kebab-case cause errors, I've set it to
Setting up PostCSS
Create postcss.config.js.
touch postcss.config.js
I have configured a minimal setup as follows:
module.exports = (ctx) => {
return {
plugins: {
autoprefixer: {},
cssnano: ctx.env === "production" ? {} : false,
},
};
};
-
autoprefixer: Executes with default values. -
cssnano: Executes ifNODE_ENV=productionis passed via npm scripts; otherwise (e.g.,development), it does not execute.
Setting up npm scripts
Here are the parts specifically related to CSS.
"start": "run-s -c dev watch",
"build": "npm-run-all -s clean -p build:*",
"watch": "run-p watch:*",
"watch:css": "onchange \"src/**/*.scss\" -- npm run dev:css",
"dev": "run-p dev:*",
"dev:css": "NODE_ENV=development run-s -c css:*",
"build:css": "NODE_ENV=production run-s -c css:*",
"css:stylelint": "stylelint \"src/**/*.scss\" --fix",
"css:sass": "sass src/assets/css/site.scss htdocs/assets/css/site.css",
"css:postcss": "postcss htdocs/assets/css/site.css -o htdocs/assets/css/site.css",
-
devis for development builds, andbuildis for production builds. -
run-sruns commands sequentially, and the-coption ensures that processing continues to the end even if an error occurs. - Using
run-s -c css:*runs scripts inscriptsstarting withcss:in order (in this case: stylelint → sass → postcss). -
stylelint \"src/**/*.scss\" --fixtargets all Sass files and forces automatic fixes. -
sass src/assets/css/site.scss htdocs/assets/css/site.cssspecifies the source file for compilation and the output destination. -
postcss htdocs/assets/css/site.css -o htdocs/assets/css/site.cssspecifies the source file and the output destination (-o). - The
onchange \"src/**/*.scss\" -- npm run dev:csspart monitors Sass file saves and starts the sequence of processes whenever a file is overwritten.
Discussion