What is CSS?
CSS stands for Cascading Style Sheets. It is the standard language used to control how HTML elements look on screen. Every colour, font, spacing decision, layout arrangement and animation on the web is defined by CSS.
Where HTML describes what content is — a heading, a paragraph, an image — CSS describes how it appears: what colour that heading is, how large the font is, how much space surrounds the paragraph and whether the image floats to the left or sits in a grid. That separation is the core of web development. HTML provides structure, CSS provides presentation and JavaScript provides behaviour. Each layer has one job, and keeping them separate makes pages easier to build, test and maintain.
The best way to think about CSS is as the paint and furniture of a page. HTML is the house — the walls, rooms and doorways — and CSS decides what colour the walls are, what furniture goes where and how the whole thing looks. Without CSS every page would be plain black text on a white background. CSS is what makes the web visually rich.
How CSS works
CSS works through a system of selectors, properties and values. A selector targets an HTML element, a property is the aspect you want to change and a value is what you set it to:
/* style.css */
h1 {
color: blue;
font-size: 2rem;
}
Here, h1 is the selector — it tells the browser which element to style. color and font-size are properties — the things you can change. blue and 2rem are the values — what you set them to. The whole block from the opening brace to the closing brace is called a declaration block or rule set.
When a browser loads a page it parses the HTML into the DOM and then applies CSS rules to it. Each rule says “find these elements and make them look like this.” The browser evaluates every rule, resolves conflicts through the cascade and specificity, and renders the final result. That process happens every time the page loads and again whenever styles change.
Ways to add CSS
There are three ways to include CSS in a web page, each with its own use case:
Inline styles
Apply CSS directly to an element using the style attribute:
<!-- index.html -->
<p style="color: red; font-size: 1.2rem;">This text is red.</p>
Inline styles are useful for quick experiments or one-off overrides, but they mix presentation with structure and are hard to maintain at scale. Avoid them in production.
Internal stylesheet
Place a <style> block inside the <head> of your HTML document:
<!-- index.html -->
<head>
<style>
h1 {
color: navy;
}
</style>
</head>
Internal styles work well for single-page prototypes or when a page needs unique styles that no other page uses. They are still scoped to one document, so sharing styles across pages requires repetition.
External stylesheet
Link a separate .css file using the <link> element:
<!-- index.html -->
<head>
<link rel="stylesheet" href="styles.css" />
</head>
This is the standard approach for production websites. One CSS file can style every page, the browser caches it after the first load and your HTML stays clean. Most projects use external stylesheets.
CSS selectors
Selectors are how you tell CSS which elements to style. Mastering selectors is one of the highest-leverage skills in CSS because precise selectors mean less markup, fewer overrides and cleaner stylesheets.
Element selector
Targets all instances of an HTML element:
/* style.css */
p {
line-height: 1.6;
}
Class selector
Targets elements with a specific class attribute. Classes are reusable and are the most common way to style groups of elements:
/* style.css */
.card {
padding: 1rem;
border: 1px solid #e2e8f0;
}
ID selector
Targets a single element with a specific id. IDs are unique within a page and have higher specificity than classes:
/* style.css */
#hero {
background: linear-gradient(135deg, #667eea, #764ba2);
}
Attribute selector
Targets elements based on their attributes or attribute values:
/* style.css */
a[target="_blank"] {
color: #dc2626;
}
input[type="email"] {
border-color: #3b82f6;
}
Pseudo-class selector
Targets elements in a specific state — hovered, focused, the first child, or the nth item in a list:
/* style.css */
a:hover {
text-decoration: underline;
}
li:first-child {
font-weight: bold;
}
input:focus {
outline: 2px solid #3b82f6;
}
Combined selectors
Selectors can be combined for precision. A space targets descendants, a > targets direct children and a + targets the next sibling:
/* style.css */
nav a {
color: white;
}
nav > ul {
list-style: none;
}
h2 + p {
margin-top: 0;
}
The cascade and specificity
When multiple CSS rules target the same element, the browser needs a way to decide which one wins. That process is called the cascade, and it follows a clear order of priority.
Specificity hierarchy
Specificity is calculated from four levels, evaluated from left to right:
- Inline styles —
style="..."— highest priority - IDs —
#header - Classes, attributes and pseudo-classes —
.nav,[type="text"],:hover - Elements and pseudo-elements —
div,::before— lowest priority
A rule with an ID selector always beats a rule with only a class selector, no matter how many class selectors the second rule has. When specificity is equal, the rule that appears later in the source wins.
The cascade
Beyond specificity, the cascade considers:
- Origin — browser defaults, author styles and user styles each have a level. Author styles beat browser defaults;
!importantin user styles can override author styles. - Inheritance — some properties like
color,font-familyandline-heightare inherited by child elements. Others likemargin,paddingandborderare not. !important— adding!importantto a declaration overrides all other declarations with the same property, regardless of specificity. Use it sparingly because it breaks the natural cascade and makes debugging harder.
Practical tips
- Keep specificity low by preferring classes over IDs.
- Avoid
!importantunless you are creating a utility layer or overriding a third-party stylesheet. - When a style does not apply, check the cascade: inspect the element, see which rules match and which one wins.
Box model basics
Every HTML element is a rectangular box, and the box model describes how its size is calculated. There are four layers from the inside out:
- Content — the text, image or child elements inside the box.
- Padding — transparent space between the content and the border.
- Border — a visible line surrounding the padding.
- Margin — transparent space outside the border, pushing the element away from its neighbours.
By default, width and height set the content area only. Adding padding and border increases the total size of the box. The box-sizing: border-box property changes this so the width and height include padding and border, which makes layout far more predictable:
/* reset.css */
*,
*::before,
*::after {
box-sizing: border-box;
}
For a deeper dive into how the box model affects layout, see the dedicated box model guide.
Flexbox basics
Flexbox is a one-dimensional layout system designed for arranging items in a row or column. It makes alignment, spacing and distribution of space between items straightforward, even when their sizes are unknown or dynamic.
To activate flexbox, set display: flex on a container:
/* style.css */
.container {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
The key properties are:
flex-direction— row, row-reverse, column or column-reverse.justify-content— alignment along the main axis (start, end, center, space-between, space-around).align-items— alignment along the cross axis (start, end, center, stretch).gap— space between flex items without margins.flex-wrap— whether items wrap to a new line when they overflow.
Flexbox is ideal for navigation bars, card rows, centering content and any layout where items sit in a single line or column. For two-dimensional layouts, CSS Grid is the better choice. See the dedicated flexbox guide for more.
CSS Grid basics
CSS Grid is a two-dimensional layout system that handles both rows and columns simultaneously. It is the most powerful layout tool in CSS and is designed for page-level structures and complex component layouts.
To activate grid, set display: grid on a container and define columns and rows:
/* style.css */
.page {
display: grid;
grid-template-columns: 250px 1fr 250px;
grid-template-rows: auto 1fr auto;
gap: 2rem;
}
The key properties are:
grid-template-columns— defines the column tracks. Usefrunits,repeat()andauto-fit/auto-fillfor responsive grids.grid-template-rows— defines the row tracks.gap— space between grid items.grid-area— place items into named regions of the grid.grid-columnandgrid-row— span items across multiple tracks.
Grid pairs well with media queries to create responsive layouts that reflow naturally. For a full walkthrough, see the dedicated CSS Grid guide.
Responsive design with media queries
Responsive design means your page looks and works well on every device — phones, tablets, laptops and large monitors. Media queries are the tool CSS uses to apply styles based on the viewport width, height or other device characteristics.
/* responsive.css */
/* Base styles for mobile */
.container {
padding: 1rem;
}
/* Tablet and above */
@media (min-width: 768px) {
.container {
padding: 2rem;
max-width: 720px;
margin: 0 auto;
}
}
/* Desktop */
@media (min-width: 1024px) {
.container {
max-width: 960px;
}
}
The approach above is called mobile-first: you write base styles for small screens and progressively enhance them with min-width queries. This produces leaner CSS, better performance on mobile and a natural progression from simple to complex layouts.
Other useful media query features include:
max-width— apply styles below a breakpoint.prefers-color-scheme— detect dark mode.prefers-reduced-motion— respect user preferences for animations.orientation— respond to portrait vs landscape.
Pair media queries with flexible units like %, rem, em and fr for layouts that adapt fluidly.
CSS custom properties
CSS custom properties — also called CSS variables — let you store reusable values and reference them throughout your stylesheet. They are declared with -- and accessed with var():
/* variables.css */
:root {
--color-primary: #2563eb;
--color-text: #1e293b;
--space-md: 1rem;
--font-body: system-ui, sans-serif;
}
body {
color: var(--color-text);
font-family: var(--font-body);
}
.button {
background: var(--color-primary);
padding: var(--space-md);
}
Custom properties are scoped to the element they are declared on and cascade down to children, which makes them ideal for theming. You can override them inside a media query, a class or a data attribute to switch themes without duplicating rules:
/* variables.css */
[data-theme="dark"] {
--color-primary: #60a5fa;
--color-text: #f1f5f9;
}
They also support fallback values: var(--color-primary, #2563eb). Custom properties are one of the most practical features in modern CSS and should be part of every project.
Best practices
Good CSS is not about memorising every property — it is about writing styles that are predictable, maintainable and easy to extend. These habits apply to any project size:
- Use external stylesheets for anything beyond a quick prototype.
- Prefer class selectors over element or ID selectors for styling.
- Keep specificity low so overrides are rare and intentional.
- Use CSS custom properties for colours, spacing and typography.
- Follow a consistent naming convention — BEM, utility classes or a system like Tailwind.
- Write mobile-first responsive styles with
min-widthmedia queries. - Use
box-sizing: border-boxon every project. - Avoid
!important— it hides bugs instead of fixing them. - Organise styles logically: base styles, components, utilities.
- Test in multiple browsers and at multiple viewport sizes.
- Use the browser DevTools inspector to debug layout issues quickly.
Common mistakes
- Using inline styles instead of classes for one-off tweaks.
- Over-relying on
!importantto force styles to apply. - Writing selectors that are too specific and impossible to override.
- Not setting
box-sizing: border-box, leading to unpredictable widths. - Duplicating colour and spacing values instead of using custom properties.
- Ignoring responsive design and assuming all users are on a desktop.
- Writing desktop-first styles with
max-widthinstead of mobile-first. - Mixing naming conventions or having no naming system at all.
- Forgetting to test in dark mode or with reduced motion enabled.
- Leaving unused styles in the stylesheet, increasing file size for no reason.
Is CSS still worth learning in 2026?
Absolutely. CSS is not going anywhere — every website, web app and progressive web app renders through it. Modern CSS has container queries, the :has() selector, subgrid, cascade layers and native nesting. It is more powerful than ever. Frameworks like Tailwind, Bootstrap and CSS-in-JS libraries abstract CSS but never replace it; understanding the fundamentals makes you faster and more effective with any of them. As a first styling language it is unbeatable: you see visual results within minutes, the learning curve is gentle and every later skill in frontend development benefits from solid CSS knowledge. Learn it properly once and you will use it for the rest of your career.
How to learn CSS
CSS is approachable to start and endlessly deep to master. A practical path:
- Learn the box model, selectors and the cascade.
- Build a simple page with colours, fonts and spacing.
- Learn flexbox and use it for navbars, card rows and centering.
- Learn CSS Grid for page layouts and two-dimensional designs.
- Add responsive design with media queries.
- Use custom properties for a consistent design system.
- Study accessibility — focus styles, contrast and reduced motion.
- Build projects: a landing page, a blog layout, a dashboard — until the patterns feel automatic.
From there, explore animations, advanced Grid, container queries and modern features. Our interactive CSS tutorial walks you through every step with hands-on exercises.