Table of Contents
- Introduction to Frontend Tooling
- Package Managers
- CSS Preprocessors
- CSS Postprocessors
- CSS Frameworks and Libraries
- Build Tools and Bundlers
- Task Runners
- Development Servers and Live Reload
- CSS Architecture Methodologies
- CSS-in-JS Solutions
- Code Quality and Linting
- Testing Tools
- Deployment and CI/CD
- Real-World Workflow Examples
- Best Practices and Tool Selection
Introduction to Frontend Tooling
Modern frontend development relies heavily on tools and automation to improve productivity, maintain code quality, and optimize production output. This guide covers the essential tools and workflows for HTML and CSS development.
Why Use Frontend Tools?
// Without tools // - Manual CSS prefixing (vendor prefixes) // - Manual file concatenation // - Manual minification // - No live reload // - No CSS preprocessing // With tools // - Automatic vendor prefixing (Autoprefixer) // - CSS variables, nesting, mixins (Sass/Less) // - Automatic optimization and minification // - Live reload during development // - Consistent code formatting (Prettier) // - Error detection (ESLint, Stylelint)
The Modern Frontend Toolchain
Source Code → Build Tools → Optimization → Production ↓ ↓ ↓ ↓ HTML/CSS Webpack/ Minify/ Deploy to JavaScript Vite/Rollup Optimize CDN
Package Managers
npm (Node Package Manager)
# Initialize a project npm init -y # Install dependencies npm install --save-dev sass postcss autoprefixer npm install --save lodash react # Install globally npm install -g live-server # Update packages npm update # Remove packages npm uninstall sass # Run scripts defined in package.json npm run build npm start
package.json Configuration
{
"name": "my-project",
"version": "1.0.0",
"description": "My frontend project",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint:css": "stylelint \"src/**/*.css\"",
"format": "prettier --write \"src/**/*.{css,html,js}\""
},
"devDependencies": {
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0",
"sass": "^1.55.0",
"stylelint": "^14.0.0",
"vite": "^4.0.0"
},
"browserslist": [
"> 0.5%",
"last 2 versions",
"not dead"
]
}
Yarn
# Install Yarn npm install -g yarn # Initialize project yarn init -y # Install dependencies yarn add --dev sass postcss autoprefixer yarn add lodash # Install all dependencies yarn install # Run scripts yarn dev yarn build
pnpm (Performant npm)
# Install pnpm npm install -g pnpm # Initialize project pnpm init # Install dependencies (uses symlinks, saves disk space) pnpm add --save-dev sass postcss pnpm add lodash # Run scripts pnpm run dev
CSS Preprocessors
Sass/SCSS
// variables.scss
// Variables
$primary-color: #3498db;
$secondary-color: #2ecc71;
$font-stack: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
$breakpoint-mobile: 768px;
$breakpoint-desktop: 1024px;
// Maps
$theme-colors: (
'primary': $primary-color,
'secondary': $secondary-color,
'danger': #e74c3c,
'warning': #f39c12,
'success': #27ae60
);
// Mixins
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin responsive($breakpoint) {
@if $breakpoint == mobile {
@media (max-width: $breakpoint-mobile) { @content; }
}
@if $breakpoint == desktop {
@media (min-width: $breakpoint-desktop) { @content; }
}
}
@mixin button-variant($color) {
background-color: $color;
border: 1px solid darken($color, 10%);
&:hover {
background-color: darken($color, 10%);
}
}
// Functions
@function px-to-rem($px) {
@return $px / 16px * 1rem;
}
// Extends/Placeholders
%card-base {
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: px-to-rem(20px);
margin-bottom: px-to-rem(16px);
}
// Nesting
.nav {
background: $primary-color;
&__list {
display: flex;
list-style: none;
&-item {
margin-right: 20px;
a {
color: white;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
}
// Component styling
.button {
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
@include flex-center;
// Loop through map
@each $name, $color in $theme-colors {
&--#{$name} {
@include button-variant($color);
}
}
}
// Card component
.card {
@extend %card-base;
transition: transform 0.2s;
&:hover {
transform: translateY(-2px);
}
&__title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
color: $primary-color;
}
&__content {
color: #666;
line-height: 1.5;
}
}
// Responsive design
.hero {
padding: 60px 20px;
@include responsive(mobile) {
padding: 30px 15px;
}
@include responsive(desktop) {
padding: 100px 20px;
}
}
Less
// variables.less
@primary-color: #3498db;
@secondary-color: #2ecc71;
@font-stack: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
// Mixins
.flex-center() {
display: flex;
align-items: center;
justify-content: center;
}
.button-variant(@color) {
background-color: @color;
border: 1px solid darken(@color, 10%);
&:hover {
background-color: darken(@color, 10%);
}
}
// Nesting
.nav {
background: @primary-color;
&__list {
display: flex;
list-style: none;
&-item {
margin-right: 20px;
a {
color: white;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
}
// Operations
@base-spacing: 8px;
@padding-large: @base-spacing * 3;
@padding-small: @base-spacing * 2;
.card {
padding: @padding-large;
margin-bottom: @padding-small;
border-radius: 4px;
&__title {
color: @primary-color;
}
}
// Functions
.px-to-rem(@px) {
@rem: @px / 16px;
return: @rem * 1rem;
}
Stylus
// variables.styl
primary-color = #3498db
secondary-color = #2ecc71
font-stack = -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif
// Mixins
flex-center()
display flex
align-items center
justify-content center
// No braces, no semicolons
.nav
background primary-color
&__list
display flex
list-style none
&-item
margin-right 20px
a
color white
text-decoration none
&:hover
text-decoration underline
// Functions
px-to-rem(px)
return (px / 16px) * 1rem
// Interpolation
$color = 'blue'
.{$color}-text
color blue
Converting SCSS to CSS
# Install Sass npm install -g sass # Watch SCSS files and compile to CSS sass --watch src/scss:dist/css # Compile once sass src/scss/main.scss dist/css/main.css # With source maps sass src/scss/main.scss dist/css/main.css --source-map # Minify output sass src/scss/main.scss dist/css/main.min.css --style compressed
CSS Postprocessors
PostCSS
// postcss.config.js
module.exports = {
plugins: [
require('postcss-import'),
require('postcss-nested'),
require('autoprefixer'),
require('cssnano')({
preset: 'default',
}),
require('postcss-preset-env')({
stage: 3,
features: {
'nesting-rules': true,
'custom-properties': true,
}
})
]
}
/* input.css */
@import 'variables.css';
.card {
--padding: 20px;
&__title {
color: var(--primary);
font-size: clamp(1.2rem, 4vw, 2rem);
}
&__content {
padding: var(--padding);
backdrop-filter: blur(10px);
}
}
/* output.css (processed) */
.card {
--padding: 20px;
}
.card__title {
color: #3498db;
font-size: clamp(1.2rem, 4vw, 2rem);
}
.card__content {
padding: var(--padding);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
Autoprefixer
// package.json
{
"browserslist": [
"> 0.5%",
"last 2 versions",
"Firefox ESR",
"not dead",
"not ie <= 11"
]
}
/* Input CSS */
.container {
display: flex;
user-select: none;
backdrop-filter: blur(10px);
}
/* Output CSS (after autoprefixer) */
.container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
cssnano (Minification)
// postcss.config.js with cssnano
module.exports = {
plugins: [
require('cssnano')({
preset: ['default', {
discardComments: {
removeAll: true,
},
normalizeWhitespace: true,
reduceIdents: false,
zindex: false,
}]
})
]
}
/* Before minification */
.button {
background-color: #3498db;
color: #ffffff;
padding: 10px 20px;
border-radius: 4px;
transition: all 0.3s ease;
}
/* After minification */
.button{background-color:#3498db;color:#fff;padding:10px 20px;border-radius:4px;transition:all .3s ease}
CSS Frameworks and Libraries
Tailwind CSS
<!-- Install Tailwind -->
<!-- tailwind.config.js -->
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {
colors: {
primary: '#3498db',
secondary: '#2ecc71',
},
spacing: {
'72': '18rem',
'84': '21rem',
},
animation: {
'fade-in': 'fadeIn 0.5s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
},
},
},
plugins: [],
}
<!-- Using Tailwind classes -->
<div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
<div class="md:flex">
<div class="md:flex-shrink-0">
<img class="h-48 w-full object-cover md:h-full md:w-48" src="/img/logo.jpg" alt="Logo">
</div>
<div class="p-8">
<div class="uppercase tracking-wide text-sm text-primary font-semibold">Company</div>
<a href="#" class="block mt-1 text-lg leading-tight font-medium text-black hover:underline">Product Title</a>
<p class="mt-2 text-gray-500">Product description goes here with Tailwind CSS classes.</p>
</div>
</div>
</div>
<!-- Custom CSS with @apply -->
<style>
.btn {
@apply px-4 py-2 rounded font-semibold transition duration-300;
}
.btn-primary {
@apply bg-primary text-white hover:bg-primary-dark;
}
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
</style>
Bootstrap
<!-- Include Bootstrap via CDN or npm --> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- Customize Bootstrap with Sass -->
scss
// custom-bootstrap.scss
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
// Override variables
$primary: #3498db;
$secondary: #2ecc71;
$border-radius: 0.5rem;
// Custom theme
$theme-colors: (
"primary": $primary,
"secondary": $secondary,
"success": $success,
"danger": $danger,
"warning": $warning,
"info": $info
);
@import "bootstrap/scss/bootstrap";
// Custom components
.custom-navbar {
@extend .navbar;
@extend .navbar-dark;
background-color: $primary;
}
.custom-button {
@extend .btn;
@extend .btn-primary;
border-radius: $border-radius * 2;
}
### Bulma
html
Welcome
Modern CSS framework
Card Title
Card subtitle
Card content goes here.Learn More
### Material UI (React-based)
jsx
// Using Material-UI with React
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { Button, Card, Typography } from '@mui/material';
const theme = createTheme({
palette: {
primary: {
main: '#3498db',
},
secondary: {
main: '#2ecc71',
},
},
typography: {
fontFamily: '-apple-system, BlinkMacSystemFont, sans-serif',
},
});
function App() {
return (
Material UI Modern React component library Learn More
);
}
--- ## Build Tools and Bundlers ### Vite
javascript
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
css: {
preprocessorOptions: {
scss: {
additionalData: @import "@/styles/variables.scss";
}
},
modules: {
localsConvention: 'camelCase'
}
},
build: {
minify: 'terser',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
},
},
},
},
server: {
port: 3000,
open: true,
},
})
html
Vite App
### Webpack
javascript
// webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
module: {
rules: [
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
],
},
{
test: /.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
generator: {
filename: 'images/[hash][ext][query]'
}
},
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'fonts/[hash][ext][query]'
}
}
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true,
},
}),
],
optimization: {
minimizer: [...,
new CssMinimizerPlugin(),
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
devServer: {
static: './dist',
hot: true,
open: true,
port: 3000,
},
};
### Rollup
javascript
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import terser from '@rollup/plugin-terser';
import serve from 'rollup-plugin-serve';
export default {
input: 'src/index.js',
output: [
{
file: 'dist/bundle.js',
format: 'iife',
name: 'MyApp',
sourcemap: true,
},
{
file: 'dist/bundle.esm.js',
format: 'esm',
sourcemap: true,
},
],
plugins: [
resolve(),
commonjs(),
postcss({
extract: true,
minimize: true,
modules: true,
use: ['sass'],
}),
terser(),
serve({
contentBase: 'dist',
port: 3000,
open: true,
}),
],
};
### Parcel
html
Parcel App
javascript
// package.json
{
"scripts": {
"dev": "parcel index.html",
"build": "parcel build index.html --dist-dir dist --public-url ./"
}
}
--- ## Task Runners ### npm Scripts (No extra tools needed)
json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"watch:css": "sass --watch src/scss:dist/css",
"build:css": "sass src/scss/main.scss dist/css/main.min.css --style compressed",
"lint:css": "stylelint \"src//.{css,scss}\"", "format": "prettier --write \"src//.{css,scss,html,js}\"",
"test": "jest",
"deploy": "npm run build && gh-pages -d dist",
"precommit": "lint-staged"
}
}
### Gulp
javascript
// gulpfile.js
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const imagemin = require('gulp-imagemin');
const browserSync = require('browser-sync').create();
// Compile SCSS to CSS
function styles() {
return gulp.src('src/scss/*/.scss')
.pipe(sass().on('error', sass.logError))
.pipe(postcss([autoprefixer(), cssnano()]))
.pipe(concat('main.min.css'))
.pipe(gulp.dest('dist/css'))
.pipe(browserSync.stream());
}
// Minify JavaScript
function scripts() {
return gulp.src('src/js/*/.js')
.pipe(concat('main.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/js'))
.pipe(browserSync.stream());
}
// Optimize images
function images() {
return gulp.src('src/images/*/')
.pipe(imagemin([
imagemin.gifsicle({ interlaced: true }),
imagemin.mozjpeg({ quality: 80, progressive: true }),
imagemin.optipng({ optimizationLevel: 5 }),
imagemin.svgo({ plugins: [{ removeViewBox: true }] })
]))
.pipe(gulp.dest('dist/images'));
}
// Watch files
function watch() {
browserSync.init({
server: {
baseDir: './',
},
port: 3000,
open: true,
});
gulp.watch('src/scss//.scss', styles); gulp.watch('src/js//.js', scripts);
gulp.watch('*.html').on('change', browserSync.reload);
}
// Build task
const build = gulp.parallel(styles, scripts, images);
// Default task
exports.default = gulp.series(build, watch);
exports.build = build;
exports.images = images;
--- ## Development Servers and Live Reload ### Vite Dev Server
javascript
// vite.config.js with dev server config
import { defineConfig } from 'vite'
export default defineConfig({
server: {
port: 3000,
open: true,
cors: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
},
hmr: {
overlay: true
}
},
preview: {
port: 5000,
open: true
}
})
### Webpack Dev Server
javascript
// webpack.config.js devServer config
module.exports = {
devServer: {
static: './dist',
hot: true,
open: true,
port: 3000,
historyApiFallback: true,
proxy: {
'/api': 'http://localhost:8080'
},
client: {
overlay: true,
progress: true,
},
devMiddleware: {
writeToDisk: true,
},
},
}
### BrowserSync
javascript
// browser-sync configuration
const browserSync = require('browser-sync').create();
browserSync.init({
server: {
baseDir: './',
index: 'index.html'
},
port: 3000,
open: true,
notify: false,
files: [
'.html', 'css//.css',
'js//.js', 'images//'
],
ghostMode: {
clicks: true,
forms: true,
scroll: true
}
});
--- ## CSS Architecture Methodologies ### BEM (Block Element Modifier)
scss
// BEM naming convention
// Block
.card {
background: white;
border-radius: 8px;
padding: 20px;
// Element
&__title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
&__content {
color: #666;
line-height: 1.5;
}
&__button {
background: #3498db;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
// Modifier
&--primary {
background: #2ecc71;
}
&--danger {
background: #e74c3c;
}
&--disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
}
html
Card Title
Card content hereClick MeDelete
### SMACSS (Scalable and Modular Architecture for CSS)
project/
├── base/
│ ├── _reset.scss
│ ├── _typography.scss
│ └── _variables.scss
├── layout/
│ ├── _header.scss
│ ├── _footer.scss
│ ├── _grid.scss
│ └── _sidebar.scss
├── modules/
│ ├── _button.scss
│ ├── _card.scss
│ ├── _form.scss
│ └── _navigation.scss
├── state/
│ ├── _is-active.scss
│ ├── _is-disabled.scss
│ └── _is-hidden.scss
└── theme/
├── _light.scss
├── _dark.scss
└── _custom.scss
scss
// base/_reset.scss
- {
margin: 0;
padding: 0;
box-sizing: border-box;
}
// layout/_grid.scss
.l-grid {
display: grid;
gap: 20px;
grid-template-columns: repeat(12, 1fr);
}
.l-grid__col--3 {
grid-column: span 3;
}
// modules/_card.scss
.card {
background: white;
border-radius: 8px;
padding: 20px;
&--featured {
border: 2px solid gold;
}
}
// state/_is-hidden.scss
.is-hidden {
display: none !important;
}
.is-visible {
display: block !important;
}
### ITCSS (Inverted Triangle CSS)
scss
// 1. Settings - Global variables, config
$primary-color: #3498db;
$breakpoint-md: 768px;
// 2. Tools - Mixins, functions
@mixin responsive($breakpoint) {
@if $breakpoint == md {
@media (min-width: $breakpoint-md) { @content; }
}
}
// 3. Generic - Reset, normalize
- {
margin: 0;
padding: 0;
box-sizing: border-box;
}
// 4. Elements - Bare HTML elements
body {
font-family: sans-serif;
line-height: 1.6;
}
h1, h2, h3 {
font-weight: 600;
}
// 5. Objects - Layout classes
.o-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
// 6. Components - UI components
.c-button {
display: inline-block;
padding: 8px 16px;
border-radius: 4px;
background: $primary-color;
color: white;
}
// 7. Utilities - Helper classes
.u-text-center {
text-align: center;
}
.u-mt-2 {
margin-top: 20px;
}
--- ## CSS-in-JS Solutions ### Styled Components
jsx
import styled, { ThemeProvider, css } from 'styled-components';
// Theme object
const theme = {
colors: {
primary: '#3498db',
secondary: '#2ecc71',
text: '#333',
},
spacing: {
sm: '8px',
md: '16px',
lg: '24px',
},
breakpoints: {
mobile: '768px',
},
};
// Styled components
const Button = styled.button`
background: ${props => props.primary ? props.theme.colors.primary : 'white'};
color: ${props => props.primary ? 'white' : props.theme.colors.primary};
padding: ${props => props.theme.spacing.sm} ${props => props.theme.spacing.md};
border: 2px solid ${props => props.theme.colors.primary};
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
${props => props.large && css padding: ${props.theme.spacing.md} ${props.theme.spacing.lg}; font-size: 18px;}
`;
const Card = styled.div`
background: white;
border-radius: 8px;
padding: ${props => props.theme.spacing.lg};
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
max-width: 300px;
@media (min-width: ${props => props.theme.breakpoints.mobile}) {
max-width: 500px;
}
`;
const Title = styled.h2 color: ${props => props.theme.colors.text}; margin-bottom: ${props => props.theme.spacing.md};;
function App() {
return (
Styled Components Primary Button Large Button
);
}
### Emotion
jsx
/** @jsxImportSource @emotion/react */
import { css, Global, ThemeProvider } from '@emotion/react';
const theme = {
colors: {
primary: '#3498db',
success: '#2ecc71',
danger: '#e74c3c',
},
};
const buttonStyles = (primary) => css`
background: ${primary ? theme.colors.primary : 'white'};
color: ${primary ? 'white' : theme.colors.primary};
padding: 8px 16px;
border: 2px solid ${theme.colors.primary};
border-radius: 4px;
cursor: pointer;
&:hover {
transform: translateY(-2px);
}
`;
const globalStyles = css`
- {
margin: 0;
padding: 0;
box-sizing: border-box;
} body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: #f5f5f5;
}
`;
function Button({ children, primary }) {
return (
{children}
);
}
function App() {
return (
PrimarySecondary
);
}
--- ## Code Quality and Linting ### Stylelint
javascript
// .stylelintrc.js
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-sass-guidelines',
'stylelint-config-prettier',
],
plugins: [
'stylelint-order',
'stylelint-scss',
],
rules: {
'color-no-hex': true,
'color-named': 'never',
'declaration-no-important': true,
'max-nesting-depth': 3,
'selector-max-id': 0,
'selector-no-qualifying-type': true,
'selector-class-pattern': '^[a-z][a-zA-Z0-9-]+$',
'order/order': [
'custom-properties',
'declarations',
'rules',
],
'order/properties-order': [
'display',
'position',
'top',
'right',
'bottom',
'left',
'width',
'height',
'margin',
'padding',
'border',
'background',
'color',
'font',
'text-align',
'opacity',
'transition',
],
},
}
json
// package.json scripts
{
"scripts": {
"lint:css": "stylelint 'src//.{css,scss}'", "lint:css:fix": "stylelint 'src//.{css,scss}' --fix"
}
}
### Prettier
javascript
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf"
}
json
// package.json
{
"scripts": {
"format": "prettier --write \"src//.{css,scss,html,js,json}\"", "format:check": "prettier --check \"src//.{css,scss,html,js,json}\""
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
".{css,scss}": [ "stylelint --fix", "prettier --write" ], ".{html,js}": [
"prettier --write"
]
}
}
### ESLint
javascript
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'prettier',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
plugins: ['react', 'react-hooks', 'jsx-a11y'],
rules: {
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
settings: {
react: {
version: 'detect',
},
},
};
--- ## Testing Tools ### Jest (Unit Testing)
javascript
// button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
test('renders with children', () => {
render(Click me);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(Click me);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
test('applies correct styles based on props', () => {
render(Click me);
const button = screen.getByText('Click me');
expect(button).toHaveStyle('background-color: #3498db');
});
});
### Testing Library
javascript
// form.test.js
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ContactForm from './ContactForm';
describe('ContactForm', () => {
test('submits form with valid data', async () => {
const handleSubmit = jest.fn();
render();
await userEvent.type(screen.getByLabelText(/name/i), 'John Doe'); await userEvent.type(screen.getByLabelText(/email/i), '[email protected]'); await userEvent.type(screen.getByLabelText(/message/i), 'Hello world'); fireEvent.click(screen.getByRole('button', { name: /submit/i })); await waitFor(() => { expect(handleSubmit).toHaveBeenCalledWith({ name: 'John Doe', email: '[email protected]', message: 'Hello world', }); });
});
});
### Visual Regression Testing
javascript
// visual.test.js
import { test } from '@playwright/test';
test('homepage visual regression', async ({ page }) => {
await page.goto('/');
// Take screenshot and compare with baseline
await expect(page).toHaveScreenshot('homepage.png');
});
test('responsive design', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/');
await expect(page).toHaveScreenshot('mobile.png');
await page.setViewportSize({ width: 1024, height: 768 });
await expect(page).toHaveScreenshot('tablet.png');
await page.setViewportSize({ width: 1920, height: 1080 });
await expect(page).toHaveScreenshot('desktop.png');
});
--- ## Deployment and CI/CD ### GitHub Actions
yaml
.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint CSS
run: npm run lint:css
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
### Netlify Configuration
toml
netlify.toml
[build]
command = "npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "18"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/static/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
[[headers]]
for = "/*.css"
[headers.values]
Cache-Control = "public, max-age=86400"
### Vercel Configuration
json
// vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "vite",
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
}
]
}
]
}
--- ## Real-World Workflow Examples ### Example 1: Simple Static Site Setup
json
// package.json
{
"name": "static-site",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint:css": "stylelint 'src//.{css,scss}'", "format": "prettier --write 'src//.{css,scss,html,js}'"
},
"devDependencies": {
"autoprefixer": "^10.4.0",
"postcss": "^8.4.0",
"prettier": "^2.8.0",
"sass": "^1.55.0",
"stylelint": "^14.0.0",
"stylelint-config-standard-scss": "^6.0.0",
"vite": "^4.0.0"
}
}
scss
// src/styles/main.scss
@import 'abstracts/variables';
@import 'abstracts/mixins';
@import 'base/reset';
@import 'base/typography';
@import 'components/button';
@import 'components/card';
@import 'layout/header';
@import 'layout/footer';
@import 'pages/home';
@import 'themes/dark';
html
Static Site
... …
...
### Example 2: Component Library Development
javascript
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js',
output: [
{
file: 'dist/index.js',
format: 'cjs',
sourcemap: true,
},
{
file: 'dist/index.esm.js',
format: 'esm',
sourcemap: true,
},
],
external: ['react', 'react-dom'],
plugins: [
resolve(),
commonjs(),
babel({
exclude: 'node_modules/**',
babelHelpers: 'bundled',
}),
postcss({
extract: 'styles.css',
minimize: true,
modules: true,
}),
terser(),
],
};
json
// package.json for component library
{
"name": "@mycompany/ui-components",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"style": "dist/styles.css",
"files": ["dist"],
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.0.0",
"@storybook/react": "^7.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"rollup": "^3.0.0"
}
}
### Example 3: Full-Stack Application with CSS Modules
javascript
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
css: {
modules: {
localsConvention: 'camelCase',
generateScopedName: '[name][local][hash:base64:5]',
},
preprocessorOptions: {
scss: {
additionalData: @import "@/styles/variables.scss";
}
}
},
resolve: {
alias: {
'@': '/src',
'@components': '/src/components',
'@styles': '/src/styles',
}
}
});
jsx
// src/components/Button/Button.jsx
import styles from './Button.module.scss';
export const Button = ({ children, variant = 'primary', size = 'medium', onClick }) => {
return (
${styles.button} ${styles[variant]} ${styles[size]}} onClick={onClick} > {children}
);
};
scss
// src/components/Button/Button.module.scss
.button {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
}
}
.primary {
background: var(--primary-color);
color: white;
}
.secondary {
background: var(--secondary-color);
color: white;
}
.small {
padding: 4px 8px;
font-size: 12px;
}
.medium {
padding: 8px 16px;
font-size: 14px;
}
.large {
padding: 12px 24px;
font-size: 16px;
}
--- ## Best Practices and Tool Selection ### Tool Selection Guide | Task | Tools | |------|-------| | **Package Management** | npm, yarn, pnpm | | **Build/Bundling** | Vite (new projects), Webpack (complex apps), Parcel (simple apps) | | **CSS Preprocessing** | Sass/SCSS (most popular), Less, Stylus | | **CSS Postprocessing** | PostCSS, Autoprefixer, cssnano | | **CSS Frameworks** | Tailwind CSS (utility-first), Bootstrap (component-based), Bulma (modern) | | **CSS-in-JS** | Styled Components (React), Emotion, Vanilla Extract | | **Code Quality** | Stylelint (CSS), Prettier (formatting), ESLint (JS) | | **Testing** | Jest (unit), Testing Library (component), Playwright (E2E) | | **Development Server** | Vite, Webpack Dev Server, BrowserSync | | **Deployment** | Vercel, Netlify, GitHub Pages | ### Project Structure Best Practices
my-project/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── .husky/
│ └── pre-commit
├── .vscode/
│ ├── settings.json
│ └── extensions.json
├── public/
│ ├── favicon.ico
│ └── robots.txt
├── src/
│ ├── assets/
│ │ ├── images/
│ │ ├── fonts/
│ │ └── icons/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.jsx
│ │ │ ├── Button.module.scss
│ │ │ └── Button.test.jsx
│ │ └── Card/
│ │ ├── Card.jsx
│ │ ├── Card.module.scss
│ │ └── Card.test.jsx
│ ├── styles/
│ │ ├── abstracts/
│ │ │ ├── _variables.scss
│ │ │ └── _mixins.scss
│ │ ├── base/
│ │ │ ├── _reset.scss
│ │ │ └── _typography.scss
│ │ ├── layout/
│ │ │ ├── _header.scss
│ │ │ └── _footer.scss
│ │ ├── pages/
│ │ │ ├── _home.scss
│ │ │ └── _about.scss
│ │ └── main.scss
│ ├── utils/
│ │ ├── helpers.js
│ │ └── constants.js
│ ├── App.jsx
│ └── main.jsx
├── .eslintrc.js
├── .prettierrc
├── .stylelintrc.js
├── index.html
├── package.json
├── vite.config.js
└── README.md
### Development Workflow
bash
1. Start development server
npm run dev
2. Make changes to CSS/HTML/JS
- Browser auto-reloads (hot module replacement)
- Stylelint runs on save
- Prettier formats on save
3. Run tests
npm test
npm run test:watch
4. Before commit
npm run lint:css
npm run format
5. Build for production
npm run build
6. Preview production build
npm run preview
7. Deploy
npm run deploy
```
Conclusion
Modern HTML and CSS tooling and automation have transformed frontend development, making it more efficient, maintainable, and scalable.
Key Takeaways
- Package Managers (npm, yarn, pnpm) manage dependencies
- Build Tools (Vite, Webpack) bundle and optimize assets
- Preprocessors (Sass, Less) add powerful features to CSS
- Postprocessors (PostCSS) add vendor prefixes and optimize
- CSS Frameworks provide design systems and components
- Code Quality Tools ensure consistent, error-free code
- Testing Tools verify functionality and prevent regressions
- CI/CD Pipelines automate building, testing, and deployment
Recommended Stack for Different Projects
| Project Type | Recommended Tools |
|---|---|
| Simple Static Site | Vite + Sass + PostCSS |
| React Application | Vite + Sass + Styled Components + Jest |
| Component Library | Rollup + Sass + Storybook + Jest |
| Full-Stack App | Vite + Tailwind CSS + React Testing Library |
| Enterprise App | Webpack + Sass + CSS Modules + Jest + Playwright |
Final Checklist
✅ Choose appropriate tools for your project
✅ Set up build pipeline for development
✅ Configure linting and formatting
✅ Implement testing strategy
✅ Set up CI/CD for automated deployment
✅ Optimize production builds
✅ Monitor performance in production
The right toolchain can dramatically improve productivity and code quality. Start with the basics and gradually add tools as your project grows!