CSS Architecture and Methodology
1. CSS Naming Conventions (BEM, OOCSS, SMACSS)
Methodology
Pattern
Description
Example
BEM (Block Element Modifier)
.block__element--modifier
Strict naming convention for components
.card__title--large
BEM Block
.block
Standalone component/entity
.menu, .button
BEM Element
.block__element
Part of block, no standalone meaning
.menu__item, .button__icon
BEM Modifier
.block--modifier
Different state or version of block
.button--primary, .menu--vertical
OOCSS (Object Oriented CSS)
Separate structure and skin
Reusable visual patterns as objects
.media, .button
SMACSS (Scalable Modular Architecture)
5 categories of rules
Base, Layout, Module, State, Theme
.l-sidebar, .is-active
ITCSS (Inverted Triangle CSS)
Layered architecture
Generic to specific, low to high specificity
Settings → Tools → Generic → Elements → Objects → Components → Utilities
CUBE CSS
Composition, Utility, Block, Exception
Modern methodology with design tokens
Combine composition classes with utilities
Example: BEM naming convention
/* Block: standalone component */
.card {
display : flex ;
flex-direction : column ;
background : white ;
border-radius : 8 px ;
padding : 1 rem ;
}
/* Elements: parts of the block */
.card__header {
display : flex ;
justify-content : space-between ;
margin-bottom : 1 rem ;
}
.card__title {
font-size : 1.5 rem ;
font-weight : bold ;
}
.card__subtitle {
font-size : 0.875 rem ;
color : #666 ;
}
.card__body {
flex : 1 ;
line-height : 1.6 ;
}
.card__footer {
display : flex ;
gap : 0.5 rem ;
margin-top : 1 rem ;
padding-top : 1 rem ;
border-top : 1 px solid #eee ;
}
.card__button {
padding : 0.5 rem 1 rem ;
border : none ;
border-radius : 4 px ;
cursor : pointer ;
}
/* Modifiers: variations */
.card--featured {
border : 2 px solid #007acc ;
box-shadow : 0 4 px 8 px rgba ( 0 , 122 , 204 , 0.2 );
}
.card--compact {
padding : 0.5 rem ;
}
.card__title--large {
font-size : 2 rem ;
}
.card__button--primary {
background : #007acc ;
color : white ;
}
.card__button--secondary {
background : transparent ;
border : 1 px solid #007acc ;
color : #007acc ;
}
/* HTML structure */
/*
<div class="card card--featured">
<div class="card__header">
<h2 class="card__title card__title--large">Title</h2>
</div>
<div class="card__body">Content</div>
<div class="card__footer">
<button class="card__button card__button--primary">Action</button>
<button class="card__button card__button--secondary">Cancel</button>
</div>
</div>
*/
Example: OOCSS (Object Oriented CSS)
/* Separate structure from skin */
/* Structure: layout and positioning */
.media {
display : flex ;
align-items : flex-start ;
}
.media__figure {
margin-right : 1 rem ;
}
.media__body {
flex : 1 ;
}
/* Skin: visual appearance */
.skin-default {
background : white ;
border : 1 px solid #ddd ;
border-radius : 4 px ;
padding : 1 rem ;
}
.skin-primary {
background : #007acc ;
color : white ;
padding : 1 rem ;
}
/* Combine structure and skin */
/*
<div class="media skin-default">
<div class="media__figure">...</div>
<div class="media__body">...</div>
</div>
*/
/* Separate container from content */
.container {
max-width : 1200 px ;
margin : 0 auto ;
padding : 0 1 rem ;
}
.box {
padding : 1 rem ;
margin-bottom : 1 rem ;
}
/* Content is independent of container */
.box-content {
line-height : 1.6 ;
}
/* Reusable button object */
.btn {
display : inline-block ;
padding : 0.5 rem 1 rem ;
border : none ;
border-radius : 4 px ;
cursor : pointer ;
font-size : 1 rem ;
text-align : center ;
text-decoration : none ;
}
/* Skin variations */
.btn-primary {
background : #007acc ;
color : white ;
}
.btn-secondary {
background : #6c757d ;
color : white ;
}
.btn-large {
padding : 0.75 rem 1.5 rem ;
font-size : 1.125 rem ;
}
Example: SMACSS categories
/* 1. Base: defaults, no classes */
* {
box-sizing : border-box ;
}
body {
font-family : sans-serif ;
line-height : 1.6 ;
color : #333 ;
}
a {
color : #007acc ;
text-decoration : none ;
}
/* 2. Layout: major page sections (prefix: l-) */
.l-header {
position : fixed ;
top : 0 ;
width : 100 % ;
z-index : 100 ;
}
.l-sidebar {
width : 250 px ;
float : left ;
}
.l-main {
margin-left : 250 px ;
}
.l-footer {
clear : both ;
padding : 2 rem 0 ;
}
/* 3. Module: reusable components */
.card {
background : white ;
border : 1 px solid #ddd ;
border-radius : 4 px ;
padding : 1 rem ;
}
.button {
padding : 0.5 rem 1 rem ;
border : none ;
border-radius : 4 px ;
}
.nav {
list-style : none ;
padding : 0 ;
}
.nav-item {
display : inline-block ;
margin-right : 1 rem ;
}
/* 4. State: describe behavior (prefix: is- or has-) */
.is-active {
font-weight : bold ;
color : #007acc ;
}
.is-hidden {
display : none ;
}
.is-disabled {
opacity : 0.5 ;
pointer-events : none ;
}
.has-error {
border-color : #dc3545 ;
}
.is-loading {
cursor : wait ;
opacity : 0.6 ;
}
/* 5. Theme: color schemes and typography */
.theme-dark {
background : #1a1a1a ;
color : #f0f0f0 ;
}
.theme-dark .card {
background : #2d2d2d ;
border-color : #404040 ;
}
.theme-large-text {
font-size : 1.125 rem ;
}
Note: BEM is most popular for component-based development. Use
__ for elements, -- for modifiers. OOCSS focuses on reusability. SMACSS provides
categorization. Choose based on team preference and project needs.
2. CSS-in-JS vs CSS Modules vs Utility-first
Approach
Description
Pros
Cons
CSS-in-JS
Styles written in JavaScript (styled-components, Emotion)
Scoped by default, dynamic styling, no separate files
Runtime overhead, larger bundle, no caching
CSS Modules
Locally scoped CSS files (styles.module.css)
Automatic scoping, standard CSS, build-time processing
Extra build step, separate files
Utility-first (Tailwind)
Pre-defined utility classes in HTML
Fast development, small bundle, consistency
HTML clutter, learning curve, harder customization
Traditional CSS
Global stylesheets with naming conventions
Standard, cacheable, no build needed
Global scope, naming conflicts, larger files
CSS Preprocessors (Sass/Less)
Extended CSS with variables, nesting, mixins
DRY, powerful features, compiles to CSS
Build step required, can generate bloat
Atomic CSS
Single-purpose utility classes
Highly reusable, small CSS, fast
Many classes in HTML, specificity issues
Example: CSS-in-JS (styled-components style)
/* JavaScript/TypeScript file */
/*
import styled from 'styled-components';
// Styled component
const Button = styled.button`
padding: 0.75rem 1.5rem;
background: ${props => props.primary ? '#007acc' : '#6c757d'};
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
&:hover {
background: ${props => props.primary ? '#005a9e' : '#5a6268'};
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
// Dynamic styles
const Card = styled.div`
background: white;
padding: ${props => props.compact ? '0.5rem' : '1rem'};
border-radius: 8px;
box-shadow: ${props => props.elevated ? '0 4px 8px rgba(0,0,0,0.1)' : 'none'};
`;
// Extending styles
const PrimaryButton = styled(Button)`
background: #007acc;
font-weight: bold;
`;
// Using theme
const ThemedCard = styled.div`
background: ${props => props.theme.cardBg};
color: ${props => props.theme.textColor};
padding: 1rem;
`;
// Usage in JSX
function App() {
return (
<Card elevated>
<Button primary>Primary</Button>
<Button>Secondary</Button>
<PrimaryButton>Important</PrimaryButton>
</Card>
);
}
*/
/* Advantages:
- Automatic scoping (no class name conflicts)
- Dynamic styling based on props
- Colocated with component logic
- Dead code elimination
Disadvantages:
- Runtime overhead
- No style caching between renders
- Larger JavaScript bundle
- Debugging can be harder */
Example: CSS Modules
/* styles.module.css */
.card {
background : white ;
padding : 1 rem ;
border-radius : 8 px ;
box-shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.1 );
}
.card-title {
font-size : 1.5 rem ;
margin-bottom : 0.5 rem ;
color : #333 ;
}
.card-body {
line-height : 1.6 ;
color : #666 ;
}
.button {
padding : 0.5 rem 1 rem ;
border : none ;
border-radius : 4 px ;
cursor : pointer ;
}
.button-primary {
background : #007acc ;
color : white ;
}
/* Composes (reuse other classes) */
.button-large {
composes : button;
padding : 0.75 rem 1.5 rem ;
font-size : 1.125 rem ;
}
/* Global styles */
:global( .global-class ) {
/* Not scoped */
}
/* JavaScript usage */
/*
import styles from './styles.module.css';
function Card() {
return (
<div className={styles.card}>
<h2 className={styles.cardTitle}>Title</h2>
<p className={styles.cardBody}>Content</p>
<button className={styles.buttonPrimary}>Action</button>
</div>
);
}
// Generated HTML:
// <div class="styles_card__Ab3D">
// <h2 class="styles_cardTitle__Xy2Z">Title</h2>
// </div>
*/
/* Advantages:
- Automatic local scoping
- Standard CSS syntax
- Build-time processing
- Works with preprocessors
Disadvantages:
- Build step required
- Import in every component
- Separate files to maintain */
Example: Utility-first (Tailwind CSS style)
/* Configuration (tailwind.config.js) */
/*
module.exports = {
theme: {
extend: {
colors: {
primary: '#007acc',
}
}
}
}
*/
/* HTML with utility classes */
/*
<div class="bg-white rounded-lg shadow-md p-4 max-w-md mx-auto">
<h2 class="text-2xl font-bold mb-2 text-gray-800">Card Title</h2>
<p class="text-gray-600 leading-relaxed mb-4">
Card content with consistent spacing and typography.
</p>
<div class="flex gap-2">
<button class="bg-primary text-white px-4 py-2 rounded hover:bg-blue-700 transition">
Primary
</button>
<button class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600 transition">
Secondary
</button>
</div>
</div>
<!-- Responsive utilities -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Content -->
</div>
<!-- State variants -->
<button class="bg-blue-500 hover:bg-blue-700 active:bg-blue-800 focus:ring-2 focus:ring-blue-300">
Button
</button>
*/
/* Custom utilities (if needed) */
@layer utilities {
.text-balance {
text-wrap : balance ;
}
.bg-grid {
background-image :
linear-gradient ( #e5e5e5 1 px , transparent 1 px ),
linear-gradient ( 90 deg , #e5e5e5 1 px , transparent 1 px );
background-size : 20 px 20 px ;
}
}
/* Component extraction */
@layer components {
.btn-primary {
@ apply bg-primary text-white px- 4 py- 2 rounded hover :bg-blue-700 transition;
}
.card {
@ apply bg-white rounded-lg shadow-md p- 4;
}
}
/* Advantages:
- Very fast development
- Small CSS bundle (unused purged)
- Consistent design system
- No naming required
Disadvantages:
- Many classes in HTML
- Learning curve for syntax
- Harder to read HTML
- Customization requires config */
Example: Comparison of approaches
/* Traditional CSS */
.button {
padding : 0.5 rem 1 rem ;
background : #007acc ;
color : white ;
border : none ;
border-radius : 4 px ;
}
/* HTML: <button class="button">Click</button> */
/* CSS Modules (button.module.css) */
.button {
padding : 0.5 rem 1 rem ;
background : #007acc ;
color : white ;
border : none ;
border-radius : 4 px ;
}
/* JS: import styles from './button.module.css' */
/* HTML: <button className={styles.button}>Click</button> */
/* CSS-in-JS (styled-components) */
/*
const Button = styled.button`
padding: 0.5rem 1rem;
background: #007acc;
color: white;
border: none;
border-radius: 4px;
`;
HTML: <Button>Click</Button>
*/
/* Utility-first (Tailwind) */
/* HTML: <button class="px-4 py-2 bg-blue-600 text-white rounded">Click</button> */
/* Decision matrix:
- Large team, consistency → Utility-first (Tailwind)
- React/component focus → CSS-in-JS or CSS Modules
- Simple sites, fast load → Traditional CSS
- Build complexity tolerance → Any modern approach
- Dynamic theming needs → CSS Variables + any approach */
Warning: CSS-in-JS has runtime cost. CSS Modules require build step. Utility-first creates HTML
bloat. Choose based on team, project size, and performance needs. Modern approaches: CSS Modules + CSS
Variables, or Tailwind with @apply for extraction.
3. Component-scoped CSS Strategies
Strategy
Technique
Scope Mechanism
Use Case
Shadow DOM
Web Components encapsulation
Browser-native isolation
True component isolation, framework-agnostic
CSS Modules
Build-time class name hashing
Unique class names per component
React, Vue, Angular projects
Scoped Styles (Vue)
Add unique attribute to elements
Attribute selectors
Vue single-file components
CSS-in-JS
Generate unique class names at runtime
Automatic hashing
React applications
@scope NEW
Native CSS scoping rule
Lexical scoping boundaries
Modern browsers, framework-agnostic
BEM Convention
Strict naming methodology
Manual namespacing
Any project, no build required
CSS Cascade Layers
@layer for specificity control
Layer-based isolation
Organize component styles by priority
Example: Shadow DOM component scoping
/* Web Component with Shadow DOM */
/*
class MyCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
/* Styles are completely isolated */
:host {
display : block ;
background : white ;
border-radius : 8 px ;
padding : 1 rem ;
box-shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.1 );
}
.title {
font-size : 1.5 rem ;
margin-bottom : 0.5 rem ;
}
.content {
line-height : 1.6 ;
}
::slotted( button ) {
padding : 0.5 rem 1 rem ;
background : #007acc ;
color : white ;
border : none ;
border-radius : 4 px ;
}
</ style >
< div class="card" >
< h2 class="title" > < slot name="title" > </ slot > </ h2 >
< div class="content" > < slot > </ slot > </ div >
< slot name="actions" > </ slot >
</ div >
`;
}
}
customElements.define(' my-card ', MyCard);
// Usage:
< my-card >
< span slot="title" > Card Title</ span >
< p > Card content</ p >
< button slot="actions" > Action</ button >
</ my-card >
* /
/* Advantages:
- True encapsulation
- No style leaking
- Framework-agnostic
- Browser-native
Challenges:
- Cannot style from outside
- Limited global theme support
- Learning curve */
Example: CSS @scope (modern native scoping)
/* Native CSS scoping (Chrome 118+) */
/* Scope styles to .card component */
@scope (.card) {
/* Only applies inside .card */
h2 {
font-size : 1.5 rem ;
margin-bottom : 0.5 rem ;
}
p {
line-height : 1.6 ;
color : #666 ;
}
button {
padding : 0.5 rem 1 rem ;
background : #007acc ;
color : white ;
border : none ;
border-radius : 4 px ;
}
}
/* Scope with exclusion boundary */
@scope (.card) to (.nested-card) {
/* Applies inside .card but NOT inside .nested-card */
p {
color : #333 ;
}
}
/* Multiple scopes */
@scope (.header) {
nav {
display : flex ;
gap : 1 rem ;
}
}
@scope (.footer) {
nav {
display : block ; /* Different nav styling */
}
}
/* Using :scope pseudo-class */
@scope (.card) {
:scope {
/* Styles the .card itself */
background : white ;
padding : 1 rem ;
border-radius : 8 px ;
}
:scope > h2 {
/* Direct child h2 of .card */
border-bottom : 2 px solid #007acc ;
}
}
/* Practical example: Modal component */
@scope (.modal) {
:scope {
position : fixed ;
inset : 0 ;
display : flex ;
align-items : center ;
justify-content : center ;
background : rgba ( 0 , 0 , 0 , 0.5 );
}
.modal-content {
background : white ;
padding : 2 rem ;
border-radius : 8 px ;
max-width : 500 px ;
width : 90 % ;
}
h2 {
margin-top : 0 ;
}
button {
margin-top : 1 rem ;
}
}
Example: Vue scoped styles
/* Vue Single File Component */
/*
<template>
<div class="card">
<h2 class="card-title">{{ title }}</h2>
<p class="card-content">{{ content }}</p>
<button class="card-button">Action</button>
</div>
</template>
<style scoped>
.card {
background: white;
padding: 1rem;
border-radius: 8px;
}
.card-title {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.card-content {
line-height: 1.6;
color: #666;
}
.card-button {
padding: 0.5rem 1rem;
background: #007acc;
color: white;
border: none;
border-radius: 4px;
}
/* Generated HTML:
<div class="card" data-v-f3f3eg9>
<h2 class="card-title" data-v-f3f3eg9>...</h2>
</div>
Generated CSS:
.card[data-v-f3f3eg9] { ... }
.card-title[data-v-f3f3eg9] { ... }
*/
/* Deep selector for child components */
.card :deep( .child-component ) {
margin-top : 1 rem ;
}
/* Slotted content */
.card :slotted( button ) {
margin-left : 0.5 rem ;
}
/* Global styles within scoped */
:global( .global-class ) {
/* Not scoped */
}
</ style >
* /
Example: Cascade layers for component organization
/* Define layer order (low to high priority) */
@layer reset, base, components, utilities, overrides;
/* Reset layer (lowest priority) */
@layer reset {
* {
margin : 0 ;
padding : 0 ;
box-sizing : border-box ;
}
}
/* Base layer */
@layer base {
body {
font-family : sans-serif ;
line-height : 1.6 ;
color : #333 ;
}
a {
color : #007acc ;
}
}
/* Components layer */
@layer components {
.card {
background : white ;
padding : 1 rem ;
border-radius : 8 px ;
box-shadow : 0 2 px 4 px rgba ( 0 , 0 , 0 , 0.1 );
}
.button {
padding : 0.5 rem 1 rem ;
border : none ;
border-radius : 4 px ;
cursor : pointer ;
}
.button-primary {
background : #007acc ;
color : white ;
}
}
/* Utilities layer */
@layer utilities {
.text-center {
text-align : center ;
}
.mt-1 {
margin-top : 0.5 rem ;
}
.hidden {
display : none ;
}
}
/* Overrides layer (highest priority) */
@layer overrides {
.force-visible {
display : block !important ;
}
}
/* Unlayered styles have highest priority */
.emergency-override {
color : red ;
}
/* Import layers */
@import url ( 'reset.css' ) layer(reset);
@import url ( 'components.css' ) layer(components);
/* Nested layers */
@layer components {
@layer cards, buttons, forms;
@layer cards {
.card { /* ... */ }
}
@layer buttons {
.button { /* ... */ }
}
}
Note: Use Shadow DOM for true isolation, CSS Modules for React/build tools, @scope for modern
native scoping. Cascade layers organize component priority. BEM works everywhere without tooling.
4. CSS Reset vs Normalize vs Modern-normalize
Approach
Philosophy
Size
Use Case
CSS Reset (Meyer/Universal)
Remove ALL browser defaults
~1-2 KB
Start from absolute zero, full control
Normalize.css
Preserve useful defaults, fix bugs
~8 KB
Consistent cross-browser styling
Modern-normalize
Normalize for modern browsers only
~2 KB
Modern browsers (no IE support)
Sanitize.css
Normalize + opinionated defaults
~10 KB
Good defaults out of the box
Custom Reset
Minimal project-specific reset
<1 KB
Tailored to specific needs
Example: CSS Reset (Meyer style)
/* Classic CSS Reset - removes everything */
* {
margin : 0 ;
padding : 0 ;
border : 0 ;
font-size : 100 % ;
font : inherit ;
vertical-align : baseline ;
}
/* HTML5 display-role reset */
article , aside , details , figcaption , figure ,
footer , header , hgroup , menu , nav , section {
display : block ;
}
body {
line-height : 1 ;
}
ol , ul {
list-style : none ;
}
blockquote , q {
quotes : none ;
}
blockquote :before , blockquote :after ,
q :before , q :after {
content : '' ;
content : none ;
}
table {
border-collapse : collapse ;
border-spacing : 0 ;
}
/* Modern universal reset */
* , * ::before , * ::after {
box-sizing : border-box ;
}
* {
margin : 0 ;
padding : 0 ;
}
html , body {
height : 100 % ;
}
body {
line-height : 1.5 ;
-webkit-font-smoothing : antialiased ;
}
img , picture , video , canvas , svg {
display : block ;
max-width : 100 % ;
}
input , button , textarea , select {
font : inherit ;
}
p , h1 , h2 , h3 , h4 , h5 , h6 {
overflow-wrap : break-word ;
}
/* Advantages:
- Complete control
- Minimal file size
- No surprises
Disadvantages:
- Must rebuild everything
- Removes useful defaults */
Example: Modern-normalize (modern browsers)
/* Modern-normalize highlights (simplified) */
/* Document */
* , * ::before , * ::after {
box-sizing : border-box ;
}
html {
line-height : 1.15 ;
-webkit-text-size-adjust : 100 % ;
tab-size : 4 ;
}
body {
margin : 0 ;
font-family : system-ui , -apple-system , sans-serif ;
}
/* Sections */
h1 {
font-size : 2 em ;
margin : 0.67 em 0 ;
}
/* Grouping content */
hr {
height : 0 ;
color : inherit ;
}
/* Text-level semantics */
abbr [ title ] {
text-decoration : underline dotted ;
}
b , strong {
font-weight : bolder ;
}
code , kbd , samp , pre {
font-family : ui-monospace , monospace ;
font-size : 1 em ;
}
small {
font-size : 80 % ;
}
/* Forms */
button , input , optgroup , select , textarea {
font-family : inherit ;
font-size : 100 % ;
line-height : 1.15 ;
margin : 0 ;
}
button , select {
text-transform : none ;
}
button , [ type = 'button' ], [ type = 'reset' ], [ type = 'submit' ] {
-webkit-appearance : button;
}
/* Interactive */
summary {
display : list-item ;
}
/* Advantages:
- Keeps useful defaults
- Cross-browser consistency
- Well-documented
- Small size for modern browsers */
Example: Custom minimal reset (modern approach)
/* Minimal modern reset - best practices */
/* Box sizing */
* , * ::before , * ::after {
box-sizing : border-box ;
}
/* Remove default margin */
* {
margin : 0 ;
}
/* Body defaults */
body {
line-height : 1.5 ;
-webkit-font-smoothing : antialiased ;
}
/* Media defaults */
img , picture , video , canvas , svg {
display : block ;
max-width : 100 % ;
}
/* Form element font inheritance */
input , button , textarea , select {
font : inherit ;
}
/* Text overflow */
p , h1 , h2 , h3 , h4 , h5 , h6 {
overflow-wrap : break-word ;
}
/* Remove list styles on ul, ol with role="list" */
ul [ role = 'list' ], ol [ role = 'list' ] {
list-style : none ;
}
/* Root stacking context */
#root , #__next {
isolation : isolate ;
}
/* Focus visible */
:focus-visible {
outline : 2 px solid currentColor ;
outline-offset : 2 px ;
}
/* Remove animations for people who've turned them off */
@media (prefers-reduced-motion: reduce) {
* , * ::before , * ::after {
animation-duration : 0.01 ms !important ;
animation-iteration-count : 1 !important ;
transition-duration : 0.01 ms !important ;
scroll-behavior : auto !important ;
}
}
/* Additional useful resets */
a {
color : inherit ;
text-decoration : none ;
}
button {
cursor : pointer ;
background : none ;
border : none ;
padding : 0 ;
}
/* This reset:
- Minimal but effective
- Modern best practices
- Accessibility-aware
- Easy to customize */
Example: Comparison and decision guide
/* Decision matrix:
1. Use CSS Reset (Meyer/Universal) when:
- You want absolute control
- Building from scratch
- Don't need semantic defaults
- Want smallest possible base
2. Use Normalize.css when:
- Need cross-browser consistency
- Want to preserve useful defaults
- Supporting older browsers
- Standard approach
3. Use Modern-normalize when:
- Only supporting modern browsers
- Want normalize benefits but smaller
- No IE11 support needed
- Modern project (2020+)
4. Use Custom/Minimal Reset when:
- Have specific requirements
- Want to understand every line
- Very small footprint needed
- Project-specific needs
5. Use Sanitize.css when:
- Want opinionated defaults
- Need forms to look consistent
- Want normalize + extras
- Don't want to set common styles
*/
/* Recommended modern approach: */
/* 1. Start with modern-normalize or minimal reset */
/* 2. Add project-specific base styles */
/* 3. Use CSS custom properties for tokens */
/* 4. Build component styles on top */
/* Example base after reset */
:root {
--font-sans : system-ui , -apple-system , sans-serif ;
--font-mono : ui-monospace , monospace ;
--color-text : #1a1a1a ;
--color-bg : #ffffff ;
--spacing-unit : 0.5 rem ;
}
body {
font-family : var ( --font-sans );
color : var ( --color-text );
background : var ( --color-bg );
}
h1 , h2 , h3 , h4 , h5 , h6 {
font-weight : 600 ;
line-height : 1.2 ;
}
code , pre {
font-family : var ( --font-mono );
}
a {
color : #007acc ;
text-decoration : none ;
}
a :hover {
text-decoration : underline ;
}
Warning: Don't use both reset and normalize together. CSS Reset removes useful defaults that
you'll need to rebuild. Modern projects should use modern-normalize or custom
minimal reset. Always include box-sizing: border-box.
5. Critical CSS and Above-fold Optimization
Technique
Description
Impact
Implementation
Critical CSS
Inline above-fold styles in HTML
Eliminates render-blocking CSS
Extract and inline critical styles
CSS Splitting
Separate critical from non-critical
Faster initial render
Build tool configuration
Async CSS Loading
Load non-critical CSS asynchronously
Non-blocking page load
Media query trick or JS
Preload
Hint browser to load CSS early
Faster CSS discovery
<link rel="preload">
CSS Containment
Isolate subtrees for rendering
Better paint performance
contain: layout paint
Content-visibility
Skip rendering off-screen content
Faster initial render
content-visibility: auto
Example: Critical CSS inline pattern
<!DOCTYPE html >
< html >
< head >
< meta charset="UTF-8" >
< meta name="viewport" content="width= device-width , initial-scale =1.0" >
<!-- Critical CSS inlined -- >
< style >
/* Only above-the-fold styles */
body {
margin : 0 ;
font-family : system-ui , sans-serif ;
line-height : 1.6 ;
}
.header {
background : #fff ;
border-bottom : 1 px solid #e0e0e0 ;
padding : 1 rem 2 rem ;
}
.hero {
min-height : 60 vh ;
display : flex ;
align-items : center ;
justify-content : center ;
background : linear-gradient ( 135 deg , #667eea 0 % , #764ba2 100 % );
color : white ;
}
.hero h1 {
font-size : clamp ( 2 rem , 5 vw , 4 rem );
margin : 0 ;
}
/* Loading state for below-fold */
.below-fold {
min-height : 100 px ;
background : #f5f5f5 ;
}
</ style >
<!-- Preload full stylesheet -- >
< link rel="preload" href="/styles/ main .css" as="style" >
<!-- Async load non-critical CSS -- >
< link rel="stylesheet" href="/styles/ main .css" media="print" onload="this.media='all'" >
< noscript > < link rel="stylesheet" href="/styles/ main .css" > </ noscript >
</ head >
< body >
< header class="header" >
<!-- Header content -- >
</ header >
< section class="hero" >
< h1 > Welcome</ h1 >
</ section >
< main class=" below-fold " >
<!-- Rest of content -- >
</ main >
</ body >
</ html >
/* Tools for extracting critical CSS:
- Critical (npm package)
- PurgeCSS
- Critters (Next.js/Angular)
- Penthouse
- Online tools: criticalcss.com
*/
Example: Async CSS loading patterns
<!-- Method 1: Media query trick (most compatible) -- >
< link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'" >
< noscript > < link rel="stylesheet" href="styles.css" > </ noscript >
<!-- Method 2: JavaScript loading -- >
< script >
function loadCSS(href) {
const link = document . createElement (' link ');
link . rel = ' stylesheet ';
link . href = href ;
document . head . appendChild ( link );
}
// Load when DOM is ready
if (document .readyState === 'complete') {
loadCSS ('/ styles / main . css ');
} else {
window . addEventListener (' load ', () => loadCSS ('/ styles / main . css '));
}
</ script >
<!-- Method 3: Preload + async apply -- >
< link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'" >
< noscript > < link rel="stylesheet" href="styles.css" > </ noscript >
<!-- Method 4: LoadCSS library (polyfill) -- >
< script >
/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
(function(w){
" use strict ";
var loadCSS = function ( href , before , media ){
var doc = w . document ;
var ss = doc . createElement (" link ");
var ref ;
if ( before ){
ref = before ;
} else {
var refs = ( doc . body || doc . getElementsByTagName (" head ")[0]). childNodes ;
ref = refs [ refs . length - 1];
}
var sheets = doc.styleSheets;
ss .rel = "stylesheet";
ss .href = href;
ss .media = "only x";
function ready(cb){
if ( doc . body ){
return cb ();
}
setTimeout(function(){
ready ( cb );
});
}
ready(function(){
ref . parentNode . insertBefore ( ss , ( before ? ref : ref.nextSibling));
});
var onloadcssdefined = function(cb){
var resolvedHref = ss . href ;
var i = sheets . length ;
while ( i-- ){
if ( sheets [ i ]. href === resolvedHref ){
return cb ();
}
}
setTimeout(function(){
onloadcssdefined ( cb );
});
};
function loadCB(){
if ( ss . addEventListener ){
ss . removeEventListener (" load ", loadCB );
}
ss .media = media || "all";
}
if(ss .addEventListener ){
ss . addEventListener (" load ", loadCB );
}
ss .onloadcssdefined = onloadcssdefined;
onloadcssdefined(loadCB);
return ss;
};
if(typeof exports !== "undefined"){
exports . loadCSS = loadCSS ;
} else {
w . loadCSS = loadCSS ;
}
}(typeof global !== "undefined" ? global : this));
// Usage
loadCSS("/path/to/stylesheet.css");
</ script >
Example: Content-visibility optimization
/* Optimize rendering of off-screen content */
/* Skip rendering until near viewport */
.article-section {
content-visibility : auto ;
contain-intrinsic-size : auto 500 px ; /* Estimated height */
}
/* Critical above-fold content */
.hero {
content-visibility : visible ; /* Always render */
}
/* Below-fold content */
.comments-section {
content-visibility : auto ;
contain-intrinsic-size : auto 800 px ;
}
.related-articles {
content-visibility : auto ;
contain-intrinsic-size : auto 400 px ;
}
/* Long list optimization */
.long-list-item {
content-visibility : auto ;
contain-intrinsic-size : auto 100 px ;
}
/* Performance gains:
- Faster initial render
- Reduced layout calculation
- Lower memory usage
- Smoother scrolling
*/
/* CSS Containment for isolated components */
.card {
contain : layout paint;
/* Layout and paint isolated from rest of page */
}
.widget {
contain : layout style paint;
/* Even more isolated */
}
/* Full containment */
.independent-component {
contain : strict ;
/* Maximum isolation, but requires size */
width : 300 px ;
height : 400 px ;
}
/* Use for:
- Independent widgets
- List items
- Cards
- Modals
- Sidebars
*/
Example: Build-time optimization strategies
/* webpack.config.js or similar build config */
/*
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin(),
],
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
type: 'css/mini-extract',
chunks: 'all',
enforce: true,
},
},
},
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
],
};
*/
/* PostCSS configuration */
/*
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: ['default', {
discardComments: {
removeAll: true,
},
}],
}),
require('@fullhuman/postcss-purgecss')({
content: ['./src/!**!/!*.html', './src/!**!/!*.js'],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
}),
],
};
*/
/* Critical CSS extraction (Node.js) */
/*
const critical = require('critical');
critical.generate({
base: 'dist/',
src: 'index.html',
target: 'index-critical.html',
inline: true,
width: 1300,
height: 900,
minify: true,
});
*/
/* Performance checklist:
1. Inline critical CSS (<14KB gzipped)
2. Async load non-critical CSS
3. Remove unused CSS (PurgeCSS)
4. Minify CSS (cssnano)
5. Use content-visibility for long pages
6. Add CSS containment to components
7. Preload key stylesheets
8. Split CSS by route/page
9. Use HTTP/2 server push (optional)
10. Monitor CSS bundle size
*/
CSS Architecture Best Practices
Choose naming convention early: BEM for components, prefixes for SMACSS
CSS Modules or @scope for automatic scoping in modern apps
Utility-first (Tailwind) for rapid development, extract components with @apply
Use modern-normalize or custom minimal reset for modern browsers
Inline critical CSS (<14KB), async load rest for best performance
Apply content-visibility: auto to off-screen sections
Use contain: layout paint on independent components
Organize with cascade layers (@layer) for predictable specificity
Split CSS by route/page, preload critical resources
Monitor bundle size, remove unused CSS with PurgeCSS