How to Add CSS: Methods, Priority, and Best Practices
There are multiple ways to add CSS to an HTML document. Choosing the right method depends on your project type, team size, and whether you have a build step. This page covers all methods with their real-world tradeoffs.
1. External CSS (Recommended for Production)
An external stylesheet is a separate .css file linked from the HTML <head>. This is the standard method for all production websites.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Site</title>
<!-- Link external stylesheet -->
<link rel="stylesheet" href="style.css">
<!-- Multiple stylesheets (loaded in order) -->
<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="components.css">
<link rel="stylesheet" href="utilities.css">
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
/* style.css */
:root {
--color-primary: #6c63ff;
--font-body: 'Inter', sans-serif;
}
body {
font-family: var(--font-body);
background: var(--color-bg);
color: var(--color-text);
}
Why External CSS Is Best
✅ One CSS file → controls the entire site (change once → updates everywhere)
✅ Browser caches the CSS file — faster repeat page loads
✅ Clean separation of HTML structure and CSS presentation
✅ Works with all CSS methodologies (BEM, ITCSS, SMACSS)
✅ Compatible with all build tools (Sass, PostCSS, Vite)
✅ Enables DevTools CSS inspection and editing
Performance Tip: Preload
<!-- Preload critical CSS — fetches earlier in page load sequence -->
<link rel="preload" href="critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="critical.css"></noscript>
2. Internal CSS (<style> in <head>)
CSS written inside a <style> tag within the HTML document's <head> section:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
:root { --color-primary: #6c63ff; }
body { font-family: 'Inter', sans-serif; margin: 0; }
.hero { min-height: 100dvh; display: flex; align-items: center; }
</style>
</head>
<body>...</body>
</html>
When to Use Internal CSS
✅ Single-page applications with no external file system (e.g., a CodePen demo)
✅ Email HTML (email clients don't fetch external CSS reliably)
✅ Print-specific pages that are standalone
✅ Critical CSS inlining (performance optimization — rare without build tools)
❌ Don't use for multi-page sites — duplicated on every page, no caching
❌ Don't use when you have a build step — defeats the tooling
3. Inline CSS (style="" Attribute)
CSS written directly on an HTML element using the style attribute:
<!-- Inline style on a single element -->
<h1 style="color: #6c63ff; font-size: 3rem; margin-bottom: 1rem;">Hello</h1>
<div style="display: flex; gap: 1rem; align-items: center; padding: 1.5rem;">
Content here
</div>
[!WARNING] Inline styles have the highest specificity (overrides all class-based CSS), and they're impossible to override without
!important. They cannot be cached, themed, or updated globally.
The Acceptable Uses for Inline Styles
<!-- ✅ JavaScript-set dynamic values that change at runtime -->
<div style="transform: translateX(0px);" class="js-slider"> <!-- JS updates this -->
<!-- ✅ CSS custom property values set dynamically per element -->
<div style="--columns: 3;" class="o-grid"> <!-- Grid adapts per instance -->
<div style="--delay: 0.2s;" class="c-reveal"> <!-- Animation delay per card -->
<!-- ✅ Generated content where location isn't known at build time -->
<div style="background-image: url('<?php echo $hero_image; ?>');">
<!-- ❌ Don't use for permanent styles — use classes instead -->
<button style="background: #6c63ff; color: white; padding: 12px 24px; border-radius: 100px;">
<!-- This should be: <button class="c-btn c-btn--primary"> -->
4. WordPress-Specific CSS Methods
WordPress has several unique places where CSS can be added:
Method A: Child Theme style.css
/*
Theme Name: My Child Theme
Template: generatepress
Version: 1.0.0
*/
/* Your custom CSS here — loaded after parent theme */
:root { --color-primary: #6c63ff; }
.c-hero { min-height: 100dvh; }
// functions.php — enqueue both parent + child correctly
function theme_enqueue_styles() {
wp_enqueue_style(
'parent-style',
get_template_directory_uri() . '/style.css'
);
wp_enqueue_style(
'child-style',
get_stylesheet_uri(),
['parent-style'],
filemtime(get_stylesheet_directory() . '/style.css')
);
}
add_action('wp_enqueue_scripts', 'theme_enqueue_styles');
Method B: Enqueue a Separate CSS File (Recommended for Large Projects)
// functions.php — enqueue compiled CSS from Sass
function theme_enqueue_custom_css() {
wp_enqueue_style(
'theme-components',
get_stylesheet_directory_uri() . '/assets/css/main.css',
['parent-style'],
filemtime(get_stylesheet_directory() . '/assets/css/main.css')
);
}
add_action('wp_enqueue_scripts', 'theme_enqueue_custom_css');
Method C: Customizer "Additional CSS"
WordPress Admin → Appearance → Customize → Additional CSS
/* Good for: quick overrides on an existing theme */
/* Bad for: large stylesheets — no version control, no caching, no organization */
/* What to put here (small amounts): */
:root { --color-primary: #6c63ff; }
.site-title { font-size: 2rem; }
Method D: Gutenberg theme.json (Block Themes / FSE)
{
"version": 2,
"settings": {
"color": {
"palette": [
{ "name": "Primary", "slug": "primary", "color": "#6c63ff" },
{ "name": "Background", "slug": "bg", "color": "#0f0f0f" }
]
},
"typography": {
"fontFamilies": [
{ "name": "Inter", "slug": "inter", "fontFamily": "'Inter', sans-serif" }
]
}
}
}
Method E: Builder-Specific CSS (Bricks / GenerateBlocks)
Bricks Builder:
Element → Style panel → Custom CSS tab → .brxe-{id} { CSS here }
Global Styles → Custom CSS → site-wide classes
GenerateBlocks:
Block → Advanced → Custom CSS → targeting the block's auto-generated class
Customizer → GeneratePress → Custom CSS (applies globally)
5. The @import Rule
Import one CSS file from within another:
/* main.css — importing other CSS files */
@import url('reset.css');
@import url('typography.css');
@import url('components/buttons.css');
@import url('components/cards.css');
/* ⚠ @import blocks parallel loading — CSS files load sequentially, not simultaneously */
/* ✅ Use multiple <link> tags in HTML for parallel loading in production */
/* ✅ Use Sass @use (not @import) for development file organization */
When @import Is Actually Fine
/* ✅ Google Fonts @import — load fonts into your CSS */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Playfair+Display:wght@700&display=swap');
/* ✅ Inside @layer — wrapping third-party CSS */
@layer third-party {
@import url('/path/to/plugin.css');
}
6. CSS Load Order and Priority
When CSS comes from multiple sources, this is the priority order:
Priority (highest first):
──────────────────────────────────────────────────────────────
1. Inline style="" style="color: red" → Beats everything
2. !important rules color: red !important → Very high
3. @layer order utilities > components > reset
4. Specificity #id > .class > element
5. Source order Last rule wins when equal specificity
6. Internal <style> In <head> section
7. External <link> Linked stylesheets (order matters)
8. @import Imported files (loaded last in that file)
9. Browser default User-agent stylesheet
──────────────────────────────────────────────────────────────
WordPress-specific load order:
1. Parent theme style.css
2. Child theme style.css (or enqueued files)
3. Plugin CSS (varies by priority)
4. Customizer "Additional CSS" (always last → always wins)
7. Method Comparison Reference
| Method | Scope | Cacheable | Maintainable | Use Case |
|---|---|---|---|---|
External .css file | Site-wide | ✅ Yes | ✅ Best | All production sites |
<link> multiple files | Per-file | ✅ Yes | ✅ Good | Modular CSS without build step |
<style> in <head> | Page-only | ❌ No | ⚠ Limited | Email, demos, CodePen |
style="" attribute | Element | ❌ No | ❌ Worst | JS-driven values only |
| WordPress child theme | Site-wide | ✅ Yes | ✅ Good | WP customization |
| WP Customizer CSS | Site-wide | ⚠ Limited | ⚠ Poor | Quick overrides only |
theme.json | Site-wide | ✅ Yes | ✅ Good | Block themes (FSE) |
Sass @use + compile | Site-wide | ✅ Yes | ✅ Best | Projects with build step |
8. The <link> Element in Detail
<link
rel="stylesheet" <!-- Required: defines relationship as stylesheet -->
href="style.css" <!-- Path to CSS file (relative or absolute) -->
type="text/css" <!-- Optional in HTML5 — default is text/css -->
media="screen" <!-- Optional: target media type -->
>
<!-- Print-specific stylesheet -->
<link rel="stylesheet" href="print.css" media="print">
<!-- Responsive-specific loading -->
<link rel="stylesheet" href="mobile.css" media="(max-width: 767px)">
<link rel="stylesheet" href="desktop.css" media="(min-width: 768px)">
<!-- Preconnect to Google Fonts domain before downloading -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
9. Quick Decision Guide: Which Method to Use?
Are you building a WordPress site?
→ Yes, child theme → Use enqueued CSS file in functions.php
→ Yes, block theme (FSE) → theme.json + style.css
→ Yes, quick override only → Customizer Additional CSS (< 50 lines)
Are you prototyping / vibe coding?
→ Static HTML prototype → <style> block in <head> for speed
→ Long-term project → External file from the start
Are you using a build step (Sass/Vite/Webpack)?
→ Yes → Compile to one external CSS file
→ No → Multiple <link> tags or one external file
Are you writing for email?
→ Yes → Internal <style> + inline fallbacks
→ No → Never use inline styles for production