This guide summarizes practical rules that keep your CSS predictable, scalable, accessible, and fast.
CSS Best Practices
1) Use a Light Reset & Universal Box Sizing
/* Normalize margins and set border-box so padding/border don't expand size */
*, *::before, *::after{ box-sizing: border-box; }
html, body, h1, h2, h3, h4, p, ul, ol{ margin: 0; padding: 0; }
img, video{ max-width: 100%; display: block; }
`border-box` prevents layout surprises when adding padding/borders.
2) Organize with @layer (Cascade Layers)
@layer reset, base, components, utilities;
@layer reset{ /* minimal reset here */ }
@layer base{ body{ font: 16px/1.5 system-ui; } }
@layer components{ .btn{ padding: .6rem 1rem; } }
@layer utilities{ .mt-2{ margin-top: .5rem; } }
Layers set a predictable override order: reset → base → components → utilities.
3) Control Specificity (BEM-style Naming)
/* BEM example: .block__element--modifier */
.card{ border: 1px solid #e5e7eb; border-radius: 10px; }
.card__title{ font-weight: 700; }
.card--featured{ border-color: #90CAF9; background: #E3F2FD; }
Avoid deep selectors like header nav ul li a. Keep selectors short and class-based.
4) Use CSS Variables for Tokens
:root{
--color-primary: #1E88E5;
--space-1: 8px; --space-2: 12px;
}
.btn{
background: var(--color-primary);
padding: var(--space-1) var(--space-2);
}
Tokens via variables
5) Responsive Sizing with min/max/clamp & Container Queries
h1{ font-size: clamp(20px, 2.8vw, 36px); }
/* Container Query: style based on parent width */
.card{ container-type: inline-size; }
@container (min-width: 380px){
.card__title{ font-size: 20px; }
}
Resize container
Title grows after 380px container width.
Narrow card
Smaller container keeps smaller title.
6) Accessibility: Focus, Motion, Contrast
/* Visible focus ring only when keyboard focusing */
:focus-visible{ outline: 3px solid #ff9800; outline-offset: 3px; }
/* Respect users who prefer less motion */
@media (prefers-reduced-motion: reduce){
.anim{ animation: none !important; transition: none !important; }
}
7) Use Logical Properties for RTL/LTR
.card{
padding-inline: 16px; margin-block: 8px;
}
Logical properties adapt to writing directions (RTL/LTR) automatically.
8) Performance: Animate Cheap Properties
/* Prefer transform/opacity for animations */
.card:hover{ transform: translateY(-4px); opacity: .98; }
/* Use will-change sparingly before interaction */
.card{ will-change: transform; }
Avoid animating layout-affecting properties (width/height/top/left) when possible.
9) Keep Utilities Last to Override Safely
@layer ..., utilities;
@layer utilities{
.mt-2{ margin-top: .5rem; }
.text-center{ text-align: center; }
}
Utility classes should safely override components.
10) Debugging Helpers
* { outline: 1px solid rgba(0,0,0,.06); } /* temporary layout outlines */
html{ scroll-behavior: smooth; } /* UX polish (disable under reduced motion) */
Use outlines locally for layout debugging; remove before production.
Quick Checklist:
- Reset margins, set
box-sizing: border-box. - Structure CSS with
@layeror clear partials (reset/base/components/utilities). - Prefer classes (BEM) and keep selectors shallow.
- Use variables for colors/spacing/typography tokens.
- Make sizing fluid with
clamp()and container queries where useful. - Ensure visible focus, adequate color contrast, and respect reduced-motion.
- Animate
transform/opacity, avoid heavy repaint/reflow. - Adopt logical properties for internationalization.