Skip to main content

ITCSS (Inverted Triangle CSS): The Complete Guide

ITCSS was created by Harry Roberts (csswizardry.com) around 2015 and is considered the most sophisticated CSS architecture methodology. Unlike the others, it's not primarily a naming convention — it's a mental model and file organization system that manages specificity, the cascade, and code reuse at scale.

ITCSS was designed to solve the single biggest problem in large CSS codebases: cascading specificity conflicts — the "war" where later styles can't override earlier ones because of specificity fights.

1. The Core Concept: The Inverted Triangle

ITCSS organizes CSS into layers arranged in an inverted triangle shape. As you move down the triangle:

  • Reach decreases: From globally affecting everything → affecting a single element
  • Specificity increases: From * (zero specificity) → !important utilities (max specificity)
  • Explicitness increases: From abstract settings → very concrete one-off declarations
┌────────────────────────────────────────────────┐
│ 1. SETTINGS │ Widest reach, zero specificity
│ (variables, tokens, config) │ No CSS output
├────────────────────────────────────────────────┤
│ 2. TOOLS │
│ (mixins, functions, helpers) │ No CSS output (Sass only)
├──────────────────────────────────────────┤
│ 3. GENERIC │
│ (reset, normalize, box-sizing) │ First CSS output
├──────────────────────────────────────┤
│ 4. ELEMENTS │
│ (bare HTML element styles) │ Low specificity
├──────────────────────────────┤
│ 5. OBJECTS │
│ (layout patterns, structure) │ Medium specificity
├─────────────────────────┤
│ 6. COMPONENTS │
│ (designed UI components) │ Higher specificity
├──────────────────┤
│ 7. UTILITIES │
│ (overrides, helpers) │ Highest specificity (may use !important)
└──────────────────────────────────────────────────┘
Wide reach ←───────────────────── Narrow reach
Low specificity ←──────────────── High specificity

2. Layer 1: Settings

Purpose: Project-wide configuration — design tokens, variables, maps. Produces no CSS output on its own.

/* ── Layer 1: Settings ── */
/* CSS Custom Properties / Design Tokens */

:root {
/* ── Color Palette ── */
--hue-primary: 245;
--palette-primary-50: hsl(var(--hue-primary), 80%, 95%);
--palette-primary-100: hsl(var(--hue-primary), 80%, 88%);
--palette-primary-300: hsl(var(--hue-primary), 80%, 75%);
--palette-primary-500: hsl(var(--hue-primary), 80%, 62%); /* Main */
--palette-primary-700: hsl(var(--hue-primary), 80%, 45%);
--palette-primary-900: hsl(var(--hue-primary), 80%, 25%);

/* ── Semantic Color Tokens ── */
--color-primary: var(--palette-primary-500);
--color-primary-dark: var(--palette-primary-700);
--color-primary-light: var(--palette-primary-300);
--color-bg: #0f0f0f;
--color-surface: #1a1a1a;
--color-surface-2: #242424;
--color-border: rgba(255, 255, 255, 0.08);
--color-text: #f0f0f0;
--color-text-muted: #888888;
--color-danger: hsl(0, 80%, 55%);
--color-success: hsl(145, 65%, 42%);
--color-warning: hsl(38, 90%, 55%);

/* ── Typography ── */
--font-body: 'Inter', system-ui, -apple-system, sans-serif;
--font-heading: 'Playfair Display', Georgia, 'Times New Roman', serif;
--font-mono: 'Fira Code', 'Cascadia Code', 'Courier New', monospace;

--text-xs: clamp(0.64rem, 0.5rem + 0.25vw, 0.75rem);
--text-sm: clamp(0.8rem, 0.7rem + 0.25vw, 0.875rem);
--text-base: clamp(1rem, 0.9rem + 0.4vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
--text-xl: clamp(1.25rem, 1rem + 1vw, 2rem);
--text-2xl: clamp(1.75rem, 1.5rem + 1.5vw, 2.5rem);
--text-hero: clamp(2.5rem, 3rem + 3vw, 6rem);

--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-black: 900;

/* ── Spacing Scale ── */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
--space-24: 6rem; /* 96px */

/* ── Border Radius ── */
--radius-xs: 2px;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 20px;
--radius-2xl: 28px;
--radius-full: 9999px;

/* ── Shadows ── */
--shadow-xs: 0 1px 2px rgba(0,0,0,0.1);
--shadow-sm: 0 1px 4px rgba(0,0,0,0.15);
--shadow-md: 0 4px 12px rgba(0,0,0,0.25);
--shadow-lg: 0 8px 32px rgba(0,0,0,0.4);
--shadow-xl: 0 20px 60px rgba(0,0,0,0.6);
--shadow-primary: 0 4px 20px rgba(108, 99, 255, 0.35);

/* ── Motion ── */
--duration-fast: 150ms;
--duration-base: 250ms;
--duration-slow: 400ms;
--duration-slower: 700ms;
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
--ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
--ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);

/* ── Z-Index Scale ── */
--z-base: 1;
--z-raised: 10;
--z-dropdown: 100;
--z-sticky: 200;
--z-overlay: 300;
--z-modal: 400;
--z-toast: 500;
--z-max: 9999;

/* ── Breakpoints (reference only — use in media queries) ── */
/* sm: 480px md: 768px lg: 1024px xl: 1280px 2xl: 1536px */
}

3. Layer 2: Tools

Purpose: Sass/CSS mixins, functions, and utilities that generate no CSS output on their own. Layer 2 only applies if using a preprocessor.

// Available in Sass — generates no CSS output

// ── Responsive breakpoint mixin ──
@mixin respond-to($bp) {
$breakpoints: (
'sm': 480px, 'md': 768px,
'lg': 1024px, 'xl': 1280px, '2xl': 1536px
);
@if map-has-key($breakpoints, $bp) {
@media (min-width: map-get($breakpoints, $bp)) { @content; }
}
}

// ── Fluid typography mixin ──
@mixin fluid-type($min-size, $max-size, $min-vw: 320px, $max-vw: 1440px) {
font-size: clamp(#{$min-size}, #{$min-size} + (#{$max-size} - #{$min-size}) * ((100vw - #{$min-vw}) / (#{$max-vw} - #{$min-vw})), #{$max-size});
}

// ── Truncate mixin ──
@mixin truncate($lines: 1) {
@if $lines == 1 {
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
} @else {
display: -webkit-box; -webkit-line-clamp: $lines;
-webkit-box-orient: vertical; overflow: hidden;
}
}

// ── Focus ring mixin ──
@mixin focus-ring($color: var(--color-primary), $offset: 3px, $size: 2px) {
&:focus-visible {
outline: #{$size} solid #{$color};
outline-offset: $offset;
}
}

// ── Visually hidden (accessible) ──
%visually-hidden {
position: absolute; width: 1px; height: 1px;
padding: 0; margin: -1px; overflow: hidden;
clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

4. Layer 3: Generic

Purpose: Very broad, low-specificity rules — resets, normalizers, box-sizing. The first layer that actually emits CSS.

/* ── Layer 3: Generic ── */

/* Modern CSS Reset */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}

/* Prevent font size inflation */
html { -moz-text-size-adjust: none; -webkit-text-size-adjust: none; text-size-adjust: none; }

/* Smooth scrolling */
html:focus-within { scroll-behavior: smooth; }

/* Body defaults */
body { min-height: 100vh; min-height: 100dvh; }

/* Media defaults */
img, picture, video, canvas, svg { display: block; max-width: 100%; height: auto; }

/* Form elements inherit fonts */
input, button, textarea, select { font: inherit; }

/* Remove list styles by default */
ul[role="list"], ol[role="list"] { list-style: none; }

/* Avoid text overflows */
p, h1, h2, h3, h4, button, input, label { overflow-wrap: break-word; }

/* Root stacking context */
#root, #__next { isolation: isolate; }

/* Remove default reduced motion */
@media (prefers-reduced-motion: reduce) {
html:focus-within { scroll-behavior: auto; }
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

5. Layer 4: Elements

Purpose: Default styling for bare HTML elements — no classes, no IDs. Styles what comes out of a CMS WYSIWYG without any classes.

/* ── Layer 4: Elements ── */

body {
font-family: var(--font-body);
font-size: var(--text-base);
line-height: 1.6;
color: var(--color-text);
background-color: var(--color-bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

/* Headings */
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-heading);
font-weight: var(--font-weight-bold);
line-height: 1.2;
color: var(--color-text);
}
h1 { font-size: var(--text-2xl); margin-bottom: var(--space-6); }
h2 { font-size: var(--text-xl); margin-bottom: var(--space-4); }
h3 { font-size: var(--text-lg); margin-bottom: var(--space-3); }
h4 { font-size: var(--text-base); font-weight: var(--font-weight-semibold); }

/* Paragraphs */
p { line-height: 1.7; margin-bottom: var(--space-4); color: var(--color-text-muted); }
p:last-child { margin-bottom: 0; }

/* Links */
a { color: var(--color-primary); text-decoration: none; transition: color var(--duration-fast) ease; }
a:hover { color: var(--color-primary-dark); }
a:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: 2px; }

/* Lists */
ul, ol { padding-left: 1.5em; list-style: revert; }
li { margin-bottom: var(--space-1); line-height: 1.6; }

/* Inline elements */
strong, b { font-weight: var(--font-weight-bold); color: var(--color-text); }
em, i { font-style: italic; }
small { font-size: var(--text-sm); }

/* Code */
code {
font-family: var(--font-mono);
font-size: 0.875em;
background: rgba(255,255,255,0.06);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
padding: 0.1em 0.4em;
}

pre {
font-family: var(--font-mono);
font-size: var(--text-sm);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-6);
overflow-x: auto;
-webkit-overflow-scrolling: touch;
margin-bottom: var(--space-6);
}
pre code { background: none; border: none; padding: 0; font-size: inherit; }

/* Blockquote */
blockquote {
border-left: 4px solid var(--color-primary);
padding: var(--space-4) var(--space-6);
margin: var(--space-8) 0;
font-size: var(--text-lg);
font-style: italic;
color: var(--color-text-muted);
background: rgba(108, 99, 255, 0.04);
border-radius: 0 var(--radius-md) var(--radius-md) 0;
}
blockquote cite {
display: block;
margin-top: var(--space-2);
font-size: var(--text-sm);
font-style: normal;
opacity: 0.7;
}

/* Tables */
table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-sm);
margin-bottom: var(--space-6);
}
thead { background: var(--color-surface); }
th {
padding: var(--space-3) var(--space-4);
text-align: left;
font-weight: var(--font-weight-semibold);
border-bottom: 2px solid var(--color-border);
color: var(--color-text);
}
td {
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--color-border);
color: var(--color-text-muted);
}
tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: rgba(255,255,255,0.02); }

/* Horizontal rule */
hr {
border: none;
border-top: 1px solid var(--color-border);
margin: var(--space-8) 0;
}

/* Figure and images */
figure { margin: var(--space-8) 0; }
figcaption {
margin-top: var(--space-2);
font-size: var(--text-sm);
color: var(--color-text-muted);
text-align: center;
}

6. Layer 5: Objects

Purpose: Layout patterns and structural design patterns without any decoration — the "skeleton" classes. Following OOCSS's structure/skin separation, objects are skin-neutral.

/* ── Layer 5: Objects ── */

/* Object: container */
.o-container {
width: 100%;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
padding-inline: clamp(1rem, 4vw, 2rem);
}
.o-container--narrow { max-width: 720px; }
.o-container--wide { max-width: 1440px; }

/* Object: section */
.o-section {
display: block;
padding-block: clamp(3rem, 8vw, 7rem);
}

/* Object: stack (vertical spacing) */
.o-stack > * + * { margin-top: var(--stack-gap, 1.5rem); }
.o-stack--sm { --stack-gap: 0.75rem; }
.o-stack--lg { --stack-gap: 2.5rem; }
.o-stack--xl { --stack-gap: 4rem; }

/* Object: cluster (horizontal group) */
.o-cluster {
display: flex;
flex-wrap: wrap;
gap: var(--cluster-gap, 1rem);
align-items: center;
}
.o-cluster--sm { --cluster-gap: 0.5rem; }
.o-cluster--lg { --cluster-gap: 1.5rem; }

/* Object: grid */
.o-grid {
display: grid;
gap: var(--grid-gap, 1.5rem);
grid-template-columns: var(--grid-cols, repeat(auto-fill, minmax(280px, 1fr)));
}
.o-grid--2 { --grid-cols: repeat(2, 1fr); }
.o-grid--3 { --grid-cols: repeat(3, 1fr); }
.o-grid--4 { --grid-cols: repeat(4, 1fr); }
.o-grid--gap-sm { --grid-gap: 1rem; }
.o-grid--gap-lg { --grid-gap: 2rem; }

/* Object: flex */
.o-flex { display: flex; }
.o-flex--center { align-items: center; justify-content: center; }
.o-flex--between { align-items: center; justify-content: space-between; }
.o-flex--col { flex-direction: column; }

/* Object: media (image + text) */
.o-media { display: flex; align-items: flex-start; gap: 1.25rem; }
.o-media__figure { flex-shrink: 0; }
.o-media__body { flex: 1; min-width: 0; }
.o-media--center { align-items: center; }
.o-media--reverse { flex-direction: row-reverse; }

/* Object: ratio (aspect ratio container) */
.o-ratio {
position: relative;
overflow: hidden;
}
.o-ratio::before { content: ''; display: block; padding-bottom: var(--ratio, 56.25%); }
.o-ratio > * { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.o-ratio--16-9 { --ratio: 56.25%; }
.o-ratio--4-3 { --ratio: 75%; }
.o-ratio--1-1 { --ratio: 100%; }
.o-ratio--21-9 { --ratio: 42.86%; }

7. Layer 6: Components

Purpose: Fully designed, visual UI components. This is where most CSS work happens — styled, themed, ready to use.

/* ── Layer 6: Components ── */

/* Component: .c-btn */
.c-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding: 0.7rem 1.5rem;
border: 2px solid transparent;
border-radius: var(--radius-full);
font-family: var(--font-body);
font-size: var(--text-base);
font-weight: var(--font-weight-semibold);
line-height: 1;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
transition: all var(--duration-base) var(--ease-standard);
background: transparent;
color: var(--color-text);
}
.c-btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: var(--space-1); }
.c-btn:disabled { opacity: 0.5; pointer-events: none; cursor: not-allowed; }

.c-btn--primary { background: var(--color-primary); color: white; }
.c-btn--primary:hover { background: var(--color-primary-dark); box-shadow: var(--shadow-primary); }
.c-btn--outline { border-color: var(--color-primary); color: var(--color-primary); }
.c-btn--outline:hover { background: var(--color-primary); color: white; }
.c-btn--ghost { color: var(--color-text-muted); }
.c-btn--ghost:hover { background: rgba(255,255,255,0.06); color: var(--color-text); }
.c-btn--sm { padding: 0.4rem 1rem; font-size: var(--text-sm); }
.c-btn--lg { padding: 1rem 2rem; font-size: var(--text-lg); }

/* Component: .c-card */
.c-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
overflow: hidden;
transition: transform var(--duration-slow) var(--ease-standard),
box-shadow var(--duration-slow) var(--ease-standard);
}
.c-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }

.c-card__image { width: 100%; aspect-ratio: 16/9; object-fit: cover; display: block; transition: transform var(--duration-slower) var(--ease-decelerate); }
.c-card:hover .c-card__image { transform: scale(1.04); }
.c-card__body { padding: var(--space-6); }
.c-card__label { font-size: var(--text-xs); font-weight: var(--font-weight-bold); text-transform: uppercase; letter-spacing: 0.12em; color: var(--color-primary); }
.c-card__title { font-size: var(--text-xl); font-family: var(--font-heading); margin-block: var(--space-2); line-height: 1.3; color: var(--color-text); }
.c-card__excerpt { font-size: var(--text-sm); line-height: 1.6; color: var(--color-text-muted); }
.c-card__footer { padding: var(--space-4) var(--space-6); border-top: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; }

.c-card--featured { border-color: var(--color-primary); box-shadow: 0 0 0 1px var(--color-primary), var(--shadow-md); }
.c-card--glass { background: rgba(255,255,255,0.05); backdrop-filter: blur(20px); border-color: rgba(255,255,255,0.1); }

8. Layer 7: Utilities

Purpose: Single-purpose helper classes with high specificity. These are the last resort overrides that always win. Use !important intentionally here.

/* ── Layer 7: Utilities ── */
/* These ALWAYS win. Apply directly. Cannot be overridden. */

/* Display */
.u-hidden { display: none !important; }
.u-block { display: block !important; }
.u-flex { display: flex !important; }
.u-sr-only {
position: absolute !important;
width: 1px !important; height: 1px !important;
padding: 0 !important; margin: -1px !important;
overflow: hidden !important; clip: rect(0,0,0,0) !important;
white-space: nowrap !important; border: 0 !important;
}

/* Spacing overrides */
.u-mt-0 { margin-top: 0 !important; }
.u-mb-0 { margin-bottom: 0 !important; }
.u-mt-auto { margin-top: auto !important; }
.u-mx-auto { margin-left: auto !important; margin-right: auto !important; }

/* Width */
.u-w-full { width: 100% !important; }
.u-min-w-0 { min-width: 0 !important; }

/* Text */
.u-text-center { text-align: center !important; }
.u-text-left { text-align: left !important; }
.u-truncate { white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; }
.u-break-word { overflow-wrap: break-word !important; word-break: break-word !important; }

/* Color overrides */
.u-color-primary { color: var(--color-primary) !important; }
.u-color-muted { color: var(--color-text-muted) !important; }
.u-color-danger { color: var(--color-danger) !important; }
.u-color-success { color: var(--color-success) !important; }

/* Animation */
.u-no-transition { transition: none !important; }
.u-no-animation { animation: none !important; }

/* Print */
@media print {
.u-print-hidden { display: none !important; }
.u-print-visible { display: block !important; }
}

9. ITCSS File Structure

scss/ (or css/)
├── 1-settings/
│ ├── _colors.scss
│ ├── _typography.scss
│ ├── _spacing.scss
│ ├── _motion.scss
│ └── _breakpoints.scss

├── 2-tools/ ← (Sass only, no output)
│ ├── _mixins.scss
│ ├── _functions.scss
│ └── _placeholders.scss

├── 3-generic/
│ ├── _reset.scss
│ └── _box-sizing.scss

├── 4-elements/
│ ├── _headings.scss
│ ├── _links.scss
│ ├── _lists.scss
│ ├── _images.scss
│ ├── _tables.scss
│ ├── _forms.scss
│ └── _typography.scss

├── 5-objects/
│ ├── _container.scss ← .o-container
│ ├── _grid.scss ← .o-grid
│ ├── _stack.scss ← .o-stack
│ ├── _cluster.scss ← .o-cluster
│ ├── _media.scss ← .o-media
│ └── _ratio.scss ← .o-ratio

├── 6-components/
│ ├── _btn.scss ← .c-btn
│ ├── _card.scss ← .c-card
│ ├── _nav.scss ← .c-nav
│ ├── _hero.scss ← .c-hero
│ ├── _modal.scss ← .c-modal
│ ├── _form.scss ← .c-form
│ └── _footer.scss ← .c-footer

├── 7-utilities/
│ └── _utilities.scss ← .u-hidden, .u-text-center, etc.

└── main.scss ← @import all in order 1—7

10. ITCSS + @layer (Modern CSS Integration)

ITCSS maps perfectly onto CSS @layer — you can implement ITCSS natively without Sass:

/* Declare all layers in order (weakest → strongest) */
@layer settings, generic, elements, objects, components, utilities;

@layer settings {
:root { --color-primary: hsl(245, 80%, 62%); }
}

@layer generic {
*, *::before, *::after { box-sizing: border-box; margin: 0; }
}

@layer elements {
body { font-family: var(--font-body); }
h1, h2, h3 { font-family: var(--font-heading); }
}

@layer objects {
.o-container { max-width: 1200px; margin: 0 auto; }
.o-grid { display: grid; gap: 1.5rem; }
}

@layer components {
.c-btn { display: inline-flex; padding: 0.7rem 1.5rem; border-radius: 9999px; }
.c-btn--primary { background: var(--color-primary); color: white; }
.c-card { background: var(--color-surface); border-radius: var(--radius-xl); }
}

@layer utilities {
.u-hidden { display: none !important; }
.u-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; }
}

This is the most powerful combination in modern CSS — ITCSS specificity management + @layer's cascade control means you can import third-party stylesheets into a generic or elements layer without them ever overriding your components.


11. ITCSS vs Other Methodologies

AspectITCSSBEMSMACSSOOCSS
Core focusSpecificity managementNaming conventionCategory organizationReuse principles
Best forLarge-scale projectsComponent librariesTeam clarityUI systems
Naming conventionUses o-, c-, u- prefixesBlock__elem--modl-, is-, has-Structure + skin
CSS output control✅ Precise layer controlPartialPartialMinimal
Learning curveHighLow–MediumMediumLow
Pairs well withBEM (components), @layerSMACSS (states)ITCSS (layers)BEM

12. ITCSS Quick Reference

Layer Prefix Output? Specificity Example CSS
──────────────────────────────────────────────────────────
1. Settings (none) No CSS None :root { --color: ... }
2. Tools (none) No CSS None @mixin, @function
3. Generic (none) ★ 0,0,0 *, html, body reset
4. Elements (none) ★★ 0,0,1 h1, a, p, ul
5. Objects o- ★★★ 0,1,0 .o-container, .o-grid
6. Components c- ★★★★ 0,1,0 – 0,2,0 .c-btn, .c-card
7. Utilities u- ★★★★★ Use !important .u-hidden, .u-sr-only

The Rule: NEVER reach up the triangle.
Utilities can override Components.
Components can use Objects.
Objects use Elements as context.
Elements are styled in Generic defaults.
Settings are consumed everywhere.