CSS 59 💻 1 – 58 Full
1. Introduction to CSS
CSS (Cascading Style Sheets) is the language used to control the visual presentation of HTML documents. It allows you to style everything from colors and fonts to layouts and animations.
What is CSS?
CSS specifies how HTML elements should be displayed on screen, paper, or in other media. It separates content (HTML) from presentation (CSS), making websites easier to maintain and update.
Key Benefits:
| Benefit | Description |
|---|---|
| Separation of concerns | Content and design are kept separate |
| Reusability | One CSS file can style multiple HTML pages |
| Consistency | Uniform design across an entire website |
| Flexibility | Easy to update and maintain |
Three Ways to Use CSS
| Method | Description | Example |
|---|---|---|
| Inline | CSS applied directly to an HTML element using the style attribute | <h1 style="color:blue">Hello</h1> |
| Internal | CSS defined inside the <style> element in the <head> section | <style>h1 { color: blue; }</style> |
| External | CSS defined in a separate .css file and linked with <link> | <link rel="stylesheet" href="styles.css"> |
1. Inline CSS
<h1 style="color:blue">CSS Introduction</h1>
<p style="font-size: 20px;">This is a paragraph.</p>
Pros: Quick and easy for single elements
Cons: Not reusable, hard to maintain
2. Internal CSS
<!DOCTYPE html>
<html>
<head>
<style>
p {
color: red;
}
h1 {
font-size: 24px;
}
</style>
</head>
<body>
<p>This paragraph is red.</p>
</body>
</html>
Pros: Good for single-page sites
Cons: Not reusable across multiple pages
3. External CSS
<!-- HTML file -->
<head>
<link rel="stylesheet" href="styles.css">
</head>
<!-- styles.css file -->
p {
color: red;
}
span {
color: green;
}
Pros: Reusable, easy to maintain, best practice
Cons: Requires an extra HTTP request
CSS Basic Syntax
A CSS rule consists of a selector and a declaration block.
selector {
property: value;
property: value;
}
Example:
p {
color: red;
font-size: 16px;
margin: 10px;
}
Components:
| Component | Description | Example |
|---|---|---|
| Selector | The HTML element(s) to style | p, .class, #id |
| Property | The style characteristic to change | color, font-size, margin |
| Value | The value assigned to the property | red, 16px, 10px |
Common Selectors
| Selector | Example | Description |
|---|---|---|
| Element | p { } | Selects all <p> elements |
| Class | .myClass { } | Selects elements with class="myClass" |
| ID | #myId { } | Selects the element with id="myId" |
| Attribute | [ type="text" ] { } | Selects elements with a specific attribute |
| Universal | * { } | Selects all elements |
| Grouping | h1, h2, p { } | Selects multiple elements |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Introduction</title>
<!-- ====== INTERNAL CSS ====== -->
<style>
/* Element Selector */
p {
color: red;
font-size: 18px;
}
/* Class Selector */
.highlight {
background-color: yellow;
font-weight: bold;
}
/* ID Selector */
#main-heading {
color: blue;
text-align: center;
font-size: 2em;
}
/* Grouping Selector */
h1, h2, h3 {
font-family: Arial, sans-serif;
}
</style>
<!-- ====== EXTERNAL CSS ====== -->
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- ====== INLINE CSS ====== -->
<h1 style="color:blue; text-align:center;">CSS Introduction</h1>
<p>Cascading Style Sheets</p>
<p class="highlight">This paragraph has a yellow background.</p>
<span style="color: green;">Inline styled span</span>
<div id="main-heading">Styled with an ID selector</div>
</body>
</html>
External CSS File (styles.css):
span {
color: green;
font-weight: bold;
}
body {
background-color: #f8f9fa;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
}
.highlight {
background-color: #ffc107;
padding: 5px 10px;
border-radius: 4px;
}
Quick Reference
| Concept | Example | Description |
|---|---|---|
| Inline CSS | <h1 style="color:red"> | Applied directly to an element |
| Internal CSS | <style>h1{color:red;}</style> | Defined in the <head> |
| External CSS | <link rel="stylesheet" href="style.css"> | Linked from a separate file |
| Selector | p { } | Targets HTML elements |
| Property | color | What to change |
| Value | red | The new value |
| Declaration | color: red; | Property + Value |
Best Practices
✅ Do This:
/* Use external CSS for maintainability */
<link rel="stylesheet" href="styles.css">
/* Use meaningful class names */
.button-primary { }
/* Group related properties */
h1 {
color: blue;
font-size: 2em;
text-align: center;
}
❌ Don’t Do This:
/* Don't use inline CSS for styling multiple elements */
<h1 style="color:red">Title 1</h1>
<h1 style="color:red">Title 2</h1>
/* Use a class instead */
/* Don't use IDs for styling reusable components */
#button1 { }
#button2 { }
/* Use classes instead: .button { } */
/* Don't use vague class names */
.a { }
.b { }
Pro Tip: Always prefer external CSS for anything more than a single page. It makes your code cleaner, more maintainable, and faster to load (browsers cache external CSS files). Use classes for reusable styles and IDs only for unique elements!
2. Comments and Selectors
CSS selectors are patterns used to select and style HTML elements. Understanding selectors is essential for applying styles effectively. CSS also allows comments to document your code.
CSS Comments
Comments are used to add notes or explanations in your CSS code. They are ignored by the browser.
Syntax:
/* This is a single-line comment */
/*
This is a
multi-line comment
*/
Types of Selectors
| Selector Type | Symbol/Pattern | Example | Description |
|---|---|---|---|
| Element | element | p { } | Targets all elements with a specific tag name |
| Class | .classname | .button { } | Targets all elements with a specific class |
| ID | #idname | #header { } | Targets only one element with a specific ID |
| Attribute | [ attribute="value" ] | [ type="text" ] { } | Targets elements with specific attributes |
| Pseudo-element | ::pseudo | ::placeholder { } | Styles parts of an element |
| Pseudo-class | :pseudo | :hover { } | Targets elements based on their state |
| Child | > | nav > ul { } | Targets direct children of an element |
| Descendant | (space) | .container .content { } | Targets descendants of a parent element |
| Adjacent sibling | + | .header + .footer { } | Targets the immediate sibling element |
| General sibling | ~ | .header ~ .footer { } | Targets all siblings following an element |
| Grouping | , | h1, h2 { } | Targets multiple elements at once |
1. Element Selector
Targets all elements with a specific tag name.
p {
font-size: 16px;
}
<p>This is a paragraph.</p>
2. Class Selector
Targets all elements with a specific class. Classes start with a dot (.).
.button {
background-color: #FFC;
}
<button class="button">Click me!</button>
3. ID Selector
Targets only one element with a specific ID. IDs start with a hash (#).
#header {
color: red;
}
<h1 id="header">Header</h1>
4. Attribute Selector
Targets elements with specific attributes or attribute values.
/* Exact match */
[type="text"] {
border: none;
}
/* Attribute value starts with "img" */
[class^="img"] { }
/* Attribute value contains "logo" */
[class*="logo"] { }
/* Attribute value ends with "-btn" */
[class$="-btn"] { }
/* Attribute value is not equal to "hidden" */
[type!="hidden"] { }
<input type="text" placeholder="Enter your text here">
5. Pseudo-element Selector
Styles specific parts of an element.
input::placeholder {
color: gray;
}
| Pseudo-element | Description |
|---|---|
::before | Inserts content before the element |
::after | Inserts content after the element |
::first-line | Selects the first line of a block-level element |
::first-letter | Selects the first letter of a block-level element |
::placeholder | Selects the placeholder of an input or textarea |
<input type="text" placeholder="Enter some text">
6. Child Selector
Targets only direct children of an element using >.
nav > ul {
list-style-type: none;
}
<nav>
<ul> <!-- Selected -->
<li><a href="#">Home</a></li>
</ul>
</nav>
7. Descendant Selector
Targets any descendant of a parent element using a space.
.container .content {
background-color: #FFC;
}
<div class="container">
<p class="content">This is some content.</p>
</div>
8. Adjacent Sibling Selector
Targets the sibling element that directly follows another element using +.
.header + .footer {
text-align: center;
}
<h2 class="header">Header</h2>
<footer class="footer">Footer</footer>
9. General Sibling Selector
Targets all siblings following a specific element using ~.
.header ~ .footer {
margin-top: 10px;
}
<h2 class="header">Header</h2>
<div class="content"></div>
<footer class="footer">Footer</footer>
10. Pseudo-class Selector
Targets elements based on their state.
a:hover {
text-decoration: underline;
}
| Pseudo-class | Description |
|---|---|
:hover | When the user hovers over an element |
:active | When the user clicks on an element |
:focus | When an element has focus |
:visited | When a link has been visited |
<a href="#">Click me!</a>
11. Group Selector
Targets multiple elements at once using a comma.
h1, h2 {
background-color: red;
}
<h1>Header</h1>
<h2>Header 2</h2>
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Selectors</title>
<style>
/* ====== ELEMENT SELECTOR ====== */
p {
font-size: 16px;
color: #333;
}
/* ====== CLASS SELECTOR ====== */
.button {
background-color: #ffc107;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.button:hover {
background-color: #e0a800;
}
/* ====== ID SELECTOR ====== */
#header {
color: blue;
text-align: center;
}
/* ====== ATTRIBUTE SELECTOR ====== */
[type="text"] {
border: 2px solid #ddd;
padding: 8px;
border-radius: 4px;
width: 200px;
}
[type="text"]:focus {
border-color: #007bff;
outline: none;
}
/* ====== PSEUDO-ELEMENT SELECTOR ====== */
input::placeholder {
color: #999;
font-style: italic;
}
/* ====== CHILD SELECTOR ====== */
nav > ul {
list-style-type: none;
padding: 0;
display: flex;
gap: 20px;
}
nav > ul > li > a {
text-decoration: none;
color: #007bff;
}
/* ====== DESCENDANT SELECTOR ====== */
.container .content {
background-color: #fff3cd;
padding: 15px;
border-radius: 4px;
border: 1px solid #ffc107;
}
/* ====== ADJACENT SIBLING SELECTOR ====== */
.header + .footer {
text-align: center;
margin-top: 20px;
color: #6c757d;
}
/* ====== GENERAL SIBLING SELECTOR ====== */
.header ~ .sibling {
color: #28a745;
font-weight: bold;
}
/* ====== GROUP SELECTOR ====== */
h1, h2 {
font-family: Arial, sans-serif;
}
/* ====== COMBINATION EXAMPLE ====== */
.container p.highlight {
background-color: #ffc107;
padding: 5px;
}
/* ====== MULTILINE COMMENT ====== */
/*
This is a multiline comment.
It can span multiple lines.
Useful for documenting complex sections.
*/
</style>
</head>
<body>
<!-- ====== ELEMENT SELECTOR ====== -->
<h1 id="header">Header</h1>
<p>This is a paragraph.</p>
<!-- ====== CLASS SELECTOR ====== -->
<button class="button">Click me!</button>
<!-- ====== ATTRIBUTE SELECTOR ====== -->
<input type="text" placeholder="Enter your text here">
<!-- ====== CHILD SELECTOR ====== -->
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<!-- ====== DESCENDANT SELECTOR ====== -->
<div class="container">
<p class="content">This is some content.</p>
</div>
<!-- ====== ADJACENT SIBLING SELECTOR ====== -->
<h2 class="header">Header 2</h2>
<footer class="footer">Footer</footer>
<!-- ====== GENERAL SIBLING SELECTOR ====== -->
<h2 class="header">Another Header</h2>
<div class="sibling">This is a sibling div.</div>
<div class="sibling">This is another sibling div.</div>
<!-- ====== COMBINATION EXAMPLE ====== -->
<div class="container">
<p class="highlight">This is highlighted text.</p>
</div>
</body>
</html>
Quick Reference
| Selector | Syntax | Example | Description |
|---|---|---|---|
| Element | element | p { } | All <p> elements |
| Class | .class | .button { } | Elements with class="button" |
| ID | #id | #header { } | Element with id="header" |
| Attribute | [ attr="value" ] | [ type="text" ] { } | Elements with type="text" |
| Child | parent > child | nav > ul { } | Direct children of nav |
| Descendant | parent child | .container p { } | All <p> inside .container |
| Adjacent sibling | prev + next | h2 + p { } | <p> directly after <h2> |
| General sibling | prev ~ next | h2 ~ p { } | All <p> after <h2> |
| Pseudo-class | :state | a:hover { } | Link on hover |
| Pseudo-element | ::part | ::placeholder { } | Input placeholder |
| Group | a, b | h1, h2 { } | All <h1> and <h2> |
Best Practices
✅ Do This:
/* Use meaningful class names */
.button-primary { }
/* Use comments to document sections */
/* ====== HEADER STYLES ====== */
/* Group related selectors */
h1, h2, h3 {
font-family: 'Arial', sans-serif;
}
/* Use child selectors to avoid over-styling */
nav > ul { } /* Only direct children */
❌ Don’t Do This:
/* Don't use IDs for reusable styles */
#button1 { } /* Use .button instead */
/* Don't use overly specific selectors */
body > div > div > p { } /* Overly specific */
/* Don't use vague class names */
.a { } /* Not descriptive */
Pro Tip: Use the specificity hierarchy to understand which styles take precedence:
- ID > Class/Attribute/Pseudo-class > Element/Pseudo-element
- Inline styles have the highest specificity
- Use classes for most styling to keep your CSS maintainable and reusable
3. Units in CSS
CSS units are used to specify various properties like width, height, margin, padding, font-size, and more. Choosing the right unit is essential for creating responsive and accessible designs.
Overview of CSS Units
| Category | Units | Description |
|---|---|---|
| Absolute | px, cm, mm, in, pt, pc | Fixed size, not responsive |
| Relative (font) | em, rem, ex, ch, lh, rlh | Relative to font size or line height |
| Relative (viewport) | vw, vh, vmin, vmax | Relative to the viewport size |
| Relative (container) | % | Relative to the parent element |
| Angles | deg, rad, grad, turn | For rotations and gradients |
| Grid | fr | Fractional unit for CSS Grid |
1. Absolute Units
Absolute units have fixed sizes and do not scale with the viewport or font size.
| Unit | Full Name | Equivalent | Use Case |
|---|---|---|---|
px | Pixels | 1/96th of an inch | Most common for screen design |
cm | Centimeters | 1cm = 37.8px | Print styles |
mm | Millimeters | 1mm = 3.78px | Print styles |
in | Inches | 1in = 96px | Print styles |
pt | Points | 1pt = 1/72nd of an inch | Print styles, typography |
pc | Picas | 1pc = 12pt | Print styles |
.box {
width: 200px;
padding: 10px;
border: 2px solid black;
font-size: 14pt;
}
When to use: When you need fixed, predictable sizing that doesn’t change (e.g., borders, print styles).
2. Relative (Font-Based) Units
These units are relative to font sizes, making them great for typography and accessibility.
| Unit | Description | Example |
|---|---|---|
em | Relative to the current element’s font size | 2em = 2 × current font size |
rem | Relative to the root element’s (<html>) font size | 2rem = 2 × root font size |
ex | Relative to the x-height of the font | Approximately 0.5 of font size |
ch | Width of the ‘0’ character in the font | Useful for text width limits |
lh | Relative to the line height of the element | 2lh = 2 × line height |
rlh | Relative to the root line height | 2rlh = 2 × root line height |
html {
font-size: 16px; /* Base font size: 1rem = 16px */
}
h1 {
font-size: 2.5em; /* 2.5 × 16px = 40px */
}
p {
line-height: 1.6; /* 1.6 × font size */
}
.container {
padding: 1rem; /* 16px */
max-width: 60ch; /* Approximately 60 characters wide */
}
em vs rem:
emis relative to the parent element’s font size — can cause compounding issuesremis relative to the root (<html>) font size — more predictable
/* Example of em compounding */
div {
font-size: 1.2em; /* 1.2 × parent */
}
/* Example of rem consistency */
div {
font-size: 1.2rem; /* 1.2 × root (16px = 19.2px) */
}
3. Relative (Viewport) Units
These units are relative to the viewport (browser window) size.
| Unit | Description | Example |
|---|---|---|
vw | 1% of viewport width | 50vw = 50% of screen width |
vh | 1% of viewport height | 50vh = 50% of screen height |
vmin | 1% of the smaller dimension | Useful for mobile |
vmax | 1% of the larger dimension | Useful for desktop |
.hero {
height: 100vh; /* Full viewport height */
width: 100vw; /* Full viewport width */
}
.text {
font-size: 5vw; /* Scales with viewport width */
}
.square {
width: 50vmin; /* 50% of the smaller dimension */
height: 50vmin;
}
4. Relative (Parent) Unit — Percentage (%)
Percentage is relative to the parent element’s size.
.container {
width: 80%; /* 80% of parent's width */
margin: 0 auto; /* Auto margins for centering */
}
.child {
width: 50%; /* 50% of .container's width */
padding: 10%; /* 10% of parent's width */
}
5. Angle Units
Used for rotations, gradients, and transformations.
| Unit | Description | Example |
|---|---|---|
deg | Degrees (360° = full circle) | transform: rotate(45deg) |
rad | Radians (2π = full circle) | transform: rotate(3.14rad) |
grad | Gradians (400 = full circle) | transform: rotate(100grad) |
turn | Turns (1 = full circle) | transform: rotate(0.5turn) |
.spinner {
transform: rotate(360deg);
transition: transform 2s;
}
.gradient {
background: linear-gradient(45deg, red, blue);
}
6. Grid Unit — fr
The fr (fraction) unit is used in CSS Grid to distribute available space.
.grid {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
/* 1:2:1 ratio */
gap: 10px;
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Units</title>
<style>
/* ====== ROOT SETUP ====== */
html {
font-size: 16px; /* 1rem = 16px */
scroll-behavior: smooth;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
/* ====== SECTION STYLING ====== */
section {
background: white;
padding: 1.5rem;
margin: 1.5rem 0;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 0.5rem;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 1rem;
}
.demo-box {
background: #e9ecef;
padding: 1rem;
border-radius: 4px;
margin: 0.5rem 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
margin: 0.5rem 0;
}
/* ====== UNIT DEMONSTRATIONS ====== */
/* ---- Absolute Units ---- */
.absolute-demo {
width: 300px;
height: 100px;
background: #007bff;
color: white;
padding: 10px;
border: 2px solid #0056b3;
border-radius: 4px;
}
/* ---- Percentage ---- */
.percent-demo {
width: 80%;
max-width: 600px;
background: #28a745;
color: white;
padding: 1rem;
border-radius: 4px;
}
.percent-child {
width: 50%;
background: #1e7e34;
padding: 0.5rem;
border-radius: 4px;
text-align: center;
}
/* ---- EM & REM ---- */
.em-demo {
font-size: 1.2em;
padding: 1em;
background: #ffc107;
border-radius: 4px;
}
.em-child {
font-size: 1.5em;
padding: 0.5em;
background: #e0a800;
border-radius: 4px;
}
.rem-demo {
font-size: 1.2rem;
padding: 1rem;
background: #6c5ce7;
color: white;
border-radius: 4px;
}
.rem-child {
font-size: 1.5rem;
padding: 0.5rem;
background: #4a2a9e;
border-radius: 4px;
}
/* ---- Viewport Units ---- */
.viewport-demo {
height: 30vh;
width: 50vw;
min-height: 100px;
min-width: 200px;
background: linear-gradient(135deg, #dc3545, #c92a2a);
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
font-weight: bold;
}
/* ---- ch Unit ---- */
.ch-demo {
max-width: 60ch;
background: #17a2b8;
color: white;
padding: 1rem;
border-radius: 4px;
margin: 0.5rem 0;
}
/* ---- Angle Units ---- */
.angle-demo {
width: 100px;
height: 100px;
background: #ffc107;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
margin: 0.5rem;
transition: transform 0.5s;
}
.angle-demo:hover {
transform: rotate(45deg);
}
/* ---- Grid with fr ---- */
.grid-demo {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 10px;
background: #e9ecef;
padding: 1rem;
border-radius: 4px;
}
.grid-demo > div {
background: #007bff;
color: white;
padding: 1rem;
text-align: center;
border-radius: 4px;
}
.grid-demo > div:nth-child(2) {
background: #28a745;
}
.grid-demo > div:nth-child(3) {
background: #dc3545;
}
/* ---- Combined Responsive Example ---- */
.responsive-box {
width: 80%;
max-width: 600px;
margin: 1rem auto;
padding: 1.5rem;
background: #f8f9fa;
border: 2px solid #007bff;
border-radius: 8px;
text-align: center;
}
.responsive-box h3 {
font-size: clamp(1.2rem, 4vw, 2.5rem);
margin: 0 0 0.5rem 0;
}
.responsive-box p {
font-size: 1rem;
line-height: 1.6;
}
.responsive-box img {
width: 100%;
max-width: 400px;
height: auto;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>CSS Units</h1>
<!-- ====== 1. ABSOLUTE UNITS ====== -->
<section>
<h2>1. Absolute Units (px)</h2>
<div class="absolute-demo">
<strong>Width: 300px</strong><br>
Height: 100px<br>
Border: 2px
</div>
<p><small>Fixed size — does not scale with font size or viewport.</small></p>
</section>
<!-- ====== 2. PERCENTAGE ====== -->
<section>
<h2>2. Percentage (%)</h2>
<div class="percent-demo">
<strong>Parent: width: 80% of container</strong>
<div class="percent-child">
Child: width: 50% of parent
</div>
</div>
<p><small>Relative to the parent element's size.</small></p>
</section>
<!-- ====== 3. EM ====== -->
<section>
<h2>3. em — Relative to Current Font Size</h2>
<div class="em-demo" style="font-size: 16px;">
<strong>Parent font-size: 16px</strong> (1.2em = 19.2px)
<div class="em-child">
<strong>Child font-size: 1.5em</strong> (1.5 × 19.2px = 28.8px)
</div>
<p style="font-size: 0.8em; padding: 0.5em; background: #d39e00; border-radius: 4px;">
This text uses 0.8em (relative to parent)
</p>
</div>
<p><small>⚠️ <code>em</code> compounds — each nested level multiplies the font size.</small></p>
</section>
<!-- ====== 4. REM ====== -->
<section>
<h2>4. rem — Relative to Root Font Size</h2>
<div class="rem-demo" style="font-size: 16px;">
<strong>Root font-size: 16px</strong> (1.2rem = 19.2px)
<div class="rem-child">
<strong>Child font-size: 1.5rem</strong> (1.5 × 16px = 24px)
</div>
<p style="font-size: 0.8rem; padding: 0.5rem; background: #3a1a7e; border-radius: 4px;">
This text uses 0.8rem (always relative to root)
</p>
</div>
<p><small>✅ <code>rem</code> is consistent — always relative to the root font size.</small></p>
</section>
<!-- ====== 5. VIEWPORT UNITS ====== -->
<section>
<h2>5. Viewport Units (vw, vh)</h2>
<div class="viewport-demo">
<div>
<strong>30vh × 50vw</strong><br>
(min 100px × 200px)
</div>
</div>
<p><small>Relative to the viewport (browser window) size.</small></p>
</section>
<!-- ====== 6. ch UNIT ====== -->
<section>
<h2>6. ch — Character Width</h2>
<div class="ch-demo">
<strong>max-width: 60ch</strong>
<p style="margin-top: 0.5rem;">
This text is limited to approximately 60 characters per line,
which improves readability. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
</p>
</div>
<p><small>1<code>ch</code> ≈ the width of the '0' character.</small></p>
</section>
<!-- ====== 7. ANGLE UNITS ====== -->
<section>
<h2>7. Angle Units (deg, turn, rad, grad)</h2>
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
<div class="angle-demo" style="transform: rotate(0deg);">
0deg
</div>
<div class="angle-demo" style="transform: rotate(45deg);">
45deg
</div>
<div class="angle-demo" style="transform: rotate(90deg);">
90deg
</div>
<div class="angle-demo" style="transform: rotate(0.25turn);">
0.25turn
</div>
<div class="angle-demo" style="transform: rotate(0.5turn);">
0.5turn
</div>
</div>
<p><small>Hover over any box to rotate it 45 degrees.</small></p>
</section>
<!-- ====== 8. GRID — fr ====== -->
<section>
<h2>8. Grid: fr (Fractional Unit)</h2>
<div class="grid-demo">
<div>1fr</div>
<div>2fr</div>
<div>1fr</div>
</div>
<p><small>Columns are in ratio <strong>1:2:1</strong> using <code>fr</code> units.</small></p>
</section>
<!-- ====== 9. RESPONSIVE EXAMPLE ====== -->
<section>
<h2>9. Responsive Example</h2>
<div class="responsive-box">
<h3>Responsive Typography</h3>
<p>
This heading uses <code>clamp()</code> for responsive font sizing.
The image scales to fit the container using <code>max-width: 100%</code>.
</p>
<img src="https://via.placeholder.com/600x200/007bff/ffffff?text=Responsive+Image" alt="Example Image">
<p style="font-size: 0.9rem; color: #6c757d; margin-top: 0.5rem;">
Image uses <code>width: 100%; max-width: 400px;</code>
</p>
</div>
</section>
<!-- ====== REFERENCE TABLE ====== -->
<section>
<h2>10. Units Reference</h2>
<h3>Absolute Units</h3>
<table style="width: 100%; border-collapse: collapse; margin: 10px 0;">
<tr style="background: #007bff; color: white;">
<th style="padding: 8px; border: 1px solid #ddd;">Unit</th>
<th style="padding: 8px; border: 1px solid #ddd;">Description</th>
</tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>px</code></td><td style="padding: 8px; border: 1px solid #ddd;">1/96th of an inch</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>cm</code></td><td style="padding: 8px; border: 1px solid #ddd;">Centimeters (1cm = 37.8px)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>mm</code></td><td style="padding: 8px; border: 1px solid #ddd;">Millimeters (1mm = 3.78px)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>in</code></td><td style="padding: 8px; border: 1px solid #ddd;">Inches (1in = 96px)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>pt</code></td><td style="padding: 8px; border: 1px solid #ddd;">Points (1pt = 1/72in)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>pc</code></td><td style="padding: 8px; border: 1px solid #ddd;">Picas (1pc = 12pt)</td></tr>
</table>
<h3>Relative Units</h3>
<table style="width: 100%; border-collapse: collapse; margin: 10px 0;">
<tr style="background: #28a745; color: white;">
<th style="padding: 8px; border: 1px solid #ddd;">Unit</th>
<th style="padding: 8px; border: 1px solid #ddd;">Relative To</th>
</tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>%</code></td><td style="padding: 8px; border: 1px solid #ddd;">Parent element</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>em</code></td><td style="padding: 8px; border: 1px solid #ddd;">Current element's font size</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>rem</code></td><td style="padding: 8px; border: 1px solid #ddd;">Root element's font size</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>vw</code></td><td style="padding: 8px; border: 1px solid #ddd;">Viewport width (1% = 1% of viewport width)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>vh</code></td><td style="padding: 8px; border: 1px solid #ddd;">Viewport height (1% = 1% of viewport height)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>vmin</code></td><td style="padding: 8px; border: 1px solid #ddd;">Smaller of vw and vh</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>vmax</code></td><td style="padding: 8px; border: 1px solid #ddd;">Larger of vw and vh</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>ch</code></td><td style="padding: 8px; border: 1px solid #ddd;">Width of the '0' character</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>ex</code></td><td style="padding: 8px; border: 1px solid #ddd;">x-height of the font</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>fr</code></td><td style="padding: 8px; border: 1px solid #ddd;">Available space in CSS Grid</td></tr>
</table>
<h3>Angle Units</h3>
<table style="width: 100%; border-collapse: collapse; margin: 10px 0;">
<tr style="background: #dc3545; color: white;">
<th style="padding: 8px; border: 1px solid #ddd;">Unit</th>
<th style="padding: 8px; border: 1px solid #ddd;">Description</th>
</tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>deg</code></td><td style="padding: 8px; border: 1px solid #ddd;">Degrees (360 = full circle)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>rad</code></td><td style="padding: 8px; border: 1px solid #ddd;">Radians (2π = full circle)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>grad</code></td><td style="padding: 8px; border: 1px solid #ddd;">Gradians (400 = full circle)</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><code>turn</code></td><td style="padding: 8px; border: 1px solid #ddd;">Turns (1 = full circle)</td></tr>
</table>
</section>
</body>
</html>
Quick Reference
| Category | Units | Description |
|---|---|---|
| Absolute | px, cm, mm, in, pt, pc | Fixed size, not responsive |
| Relative (font) | em, rem, ex, ch, lh, rlh | Relative to font or line height |
| Relative (viewport) | vw, vh, vmin, vmax | Relative to viewport size |
| Relative (parent) | % | Relative to parent element |
| Angle | deg, rad, grad, turn | For rotations and gradients |
| Grid | fr | Fractional unit for CSS Grid |
Best Practices
✅ Do This:
/* Use rem for typography */
html { font-size: 16px; }
h1 { font-size: 2rem; } /* 32px */
p { font-size: 1rem; } /* 16px */
/* Use em for component-based spacing */
.card {
padding: 1.5em; /* Relative to card's font size */
}
/* Use vw/vh for full-screen sections */
.hero {
height: 100vh;
min-height: 500px;
}
/* Use ch for readable text */
.article {
max-width: 65ch; /* Optimal reading width */
}
/* Use % for fluid layouts */
.container {
width: 90%;
max-width: 1200px;
}
❌ Don’t Do This:
/* Don't use px for font sizes */
p { font-size: 16px; } /* Doesn't scale with user preferences */
/* Don't use vw for body text */
body { font-size: 2vw; } /* Too small on mobile, too large on desktop */
/* Don't overuse em for nesting */
ul { font-size: 0.9em; } /* Can cause compounding issues */
ul ul { font-size: 0.9em; } /* Even smaller */
Pro Tip: A common best practice is to use rem for typography (font sizes, spacing), % for layout (widths, heights), em for component-based spacing, and vw/vh for full-screen or viewport-dependent elements. Use ch for readable text widths (approximately 60–70 characters per line is optimal for readability).
4. Colors and Color Properties
CSS provides multiple ways to specify colors, allowing you to style text, backgrounds, borders, and more. Understanding color formats and properties is essential for creating visually appealing web pages.
Color Formats
| Format | Example | Description |
|---|---|---|
| Named Colors | red, blue, yellow | Predefined color names |
| Hexadecimal | #FF0000, #F00 | 6-digit or 3-digit hex values |
| RGB | rgb(255, 0, 0) | Red, Green, Blue (0–255) |
| RGBA | rgba(255, 0, 0, 0.5) | RGB with opacity (0–1) |
| HSL | hsl(0, 100%, 50%) | Hue, Saturation, Lightness |
| HSLA | hsla(0, 100%, 50%, 0.5) | HSL with opacity |
1. Named Colors
CSS supports over 140 predefined color names.
p {
color: red;
}
.container {
background-color: lightblue;
}
.warning {
color: orange;
background-color: black;
}
Common Named Colors:red, blue, green, yellow, orange, purple, pink, brown, black, white, gray, cyan, magenta, lime, navy, olive, teal, violet, gold, silver
2. Hexadecimal Colors
Hex values use a # followed by 6 characters (0–9, A–F) or 3 characters (shorthand).
/* Full hex (6 digits) */
.container {
background-color: #123456;
}
/* Shorthand hex (3 digits) */
.box {
background-color: #F00; /* Same as #FF0000 (red) */
}
/* RGB */
body {
background-color: rgb(255, 0, 0); /* Red */
}
/* RGBA (with opacity) */
div {
background-color: rgba(255, 0, 0, 0.5); /* 50% opacity */
}
| Shorthand | Full | Color |
|---|---|---|
#F00 | #FF0000 | Red |
#0F0 | #00FF00 | Green |
#00F | #0000FF | Blue |
#FFF | #FFFFFF | White |
#000 | #000000 | Black |
3. RGB and RGBA
RGB uses values from 0 to 255 for each color channel. RGBA adds an alpha channel for opacity.
div {
background-color: rgb(255, 0, 0); /* Red */
color: rgb(0, 255, 0); /* Green text */
border-color: rgb(0, 0, 255); /* Blue border */
}
.transparent {
background-color: rgba(255, 0, 0, 0.5); /* 50% transparent red */
}
4. HSL and HSLA
HSL stands for Hue, Saturation, and Lightness.
- Hue: 0–360 (color wheel)
- Saturation: 0–100% (0% = gray, 100% = vibrant)
- Lightness: 0–100% (0% = black, 100% = white)
.hsl-blue {
background-color: hsl(240, 100%, 50%); /* Blue */
}
.hsl-pastel {
background-color: hsl(0, 50%, 80%); /* Soft red */
}
.hsla-transparent {
background-color: hsla(22, 55%, 64%, 0.7); /* With opacity */
}
Color Properties
| Property | Description | Example |
|---|---|---|
color | Sets text color | color: #333; |
background-color | Sets background color | background-color: rgba(255, 0, 0, 0.5); |
border-color | Sets border color | border-color: hsl(0, 100%, 75%); |
text-shadow | Adds shadow to text | text-shadow: 2px 2px 4px rgba(0,0,0,0.5); |
box-shadow | Adds shadow to element | box-shadow: 2px 2px 4px rgba(0,0,0,0.3); |
opacity | Element opacity (0–1) | opacity: 0.7; |
caret-color | Sets input cursor color | caret-color: red; |
Color Formats Comparison
| Format | Example | Use Case |
|---|---|---|
| Named | red | Quick prototyping |
| Hex | #FF0000 | Most common, compact |
| RGB | rgb(255, 0, 0) | Easy to adjust values |
| RGBA | rgba(255, 0, 0, 0.5) | Transparent colors |
| HSL | hsl(0, 100%, 50%) | Easy to adjust hue/saturation |
| HSLA | hsla(0, 100%, 50%, 0.5) | Transparent HSL |
CSS Gradients
Gradients allow you to create smooth transitions between colors.
/* Linear Gradient (left to right) */
.linear {
background-image: linear-gradient(to right, red, blue);
}
/* Linear Gradient (top to bottom) */
.linear-vertical {
background-image: linear-gradient(to bottom, red, blue);
}
/* Linear Gradient (diagonal) */
.linear-diagonal {
background-image: linear-gradient(45deg, red, blue);
}
/* Linear Gradient (multiple colors) */
.linear-multi {
background-image: linear-gradient(to right, red, orange, yellow, green, blue, purple);
}
/* Radial Gradient (circle) */
.radial {
background-image: radial-gradient(circle, red, blue);
}
/* Radial Gradient (ellipse) */
.radial-ellipse {
background-image: radial-gradient(ellipse, red, blue);
}
/* Conic Gradient */
.conic {
background-image: conic-gradient(from red, blue);
}
/* Repeating Linear Gradient */
.repeating-linear {
background-image: repeating-linear-gradient(45deg, red 10%, blue 20%);
}
/* Repeating Radial Gradient */
.repeating-radial {
background-image: repeating-radial-gradient(circle, red 10%, blue 20%);
}
CSS Filters
Filters apply graphical effects to elements.
.filter-demo {
/* Hue rotation */
filter: hue-rotate(180deg);
/* Brightness */
filter: brightness(50%);
/* Contrast */
filter: contrast(200%);
/* Grayscale (0-100%) */
filter: grayscale(100%);
/* Saturation (0-100%) */
filter: saturate(200%);
/* Invert (0-100%) */
filter: invert(100%);
/* Sepia (0-100%) */
filter: sepia(70%);
/* Multiple filters */
filter: brightness(150%) contrast(120%) saturate(150%);
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Colors and Color Properties</title>
<style>
/* ====== GLOBAL SETUP ====== */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
text-align: center;
}
.grid-3 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.grid-3 .demo-box {
min-height: 100px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== COLOR PROPERTY DEMOS ====== */
/* ---- Named Colors ---- */
.named-demo {
background-color: lightblue;
color: navy;
border: 2px solid blue;
}
/* ---- Hex Colors ---- */
.hex-demo {
background-color: #123456;
color: #FFFFFF;
border: 2px solid #FFD700;
}
/* ---- RGB Colors ---- */
.rgb-demo {
background-color: rgb(255, 0, 0);
color: rgb(255, 255, 255);
border: 2px solid rgb(0, 255, 0);
}
/* ---- RGBA Colors ---- */
.rgba-demo {
background-color: rgba(255, 0, 0, 0.5);
color: #333;
border: 2px solid rgba(0, 0, 255, 0.5);
}
/* ---- HSL Colors ---- */
.hsl-demo {
background-color: hsl(240, 100%, 50%);
color: hsl(0, 0%, 100%);
border: 2px solid hsl(0, 100%, 75%);
}
/* ---- HSLA Colors ---- */
.hsla-demo {
background-color: hsla(22, 55%, 64%, 0.7);
color: #333;
border: 2px solid hsla(22, 55%, 64%, 0.3);
}
/* ---- Text Shadow ---- */
.text-shadow-demo {
font-size: 2rem;
font-weight: bold;
color: #007bff;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
/* ---- Box Shadow ---- */
.box-shadow-demo {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
/* ---- Opacity ---- */
.opacity-demo {
background: #007bff;
color: white;
padding: 15px;
opacity: 0.7;
}
/* ---- Caret Color ---- */
.caret-demo {
caret-color: red;
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
width: 100%;
max-width: 300px;
font-size: 1rem;
}
.caret-demo:focus {
border-color: red;
outline: none;
}
/* ====== GRADIENT DEMOS ====== */
.gradient-linear {
background-image: linear-gradient(to right, red, blue);
}
.gradient-linear-multi {
background-image: linear-gradient(to right, red, orange, yellow, green, blue, purple);
}
.gradient-radial {
background-image: radial-gradient(circle, red, blue);
}
.gradient-conic {
background-image: conic-gradient(red, blue, green, red);
}
.gradient-repeating {
background-image: repeating-linear-gradient(45deg, red 10%, blue 20%);
}
/* ====== FILTER DEMOS ====== */
.filter-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin: 15px 0;
}
.filter-grid .demo-box {
min-height: 100px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
background: #007bff;
color: white;
border: none;
}
.filter-hue {
filter: hue-rotate(180deg);
}
.filter-brightness {
filter: brightness(50%);
}
.filter-contrast {
filter: contrast(200%);
}
.filter-grayscale {
filter: grayscale(100%);
}
.filter-saturate {
filter: saturate(200%);
}
.filter-invert {
filter: invert(100%);
}
.filter-sepia {
filter: sepia(70%);
}
.filter-multiple {
filter: brightness(150%) contrast(120%) saturate(150%);
}
/* ---- Mix Blend Mode ---- */
.blend-container {
background-image: linear-gradient(to right, #ff6b6b, #4ecdc4);
padding: 20px;
border-radius: 8px;
margin: 10px 0;
}
.blend-demo {
color: white;
font-size: 2rem;
font-weight: bold;
text-align: center;
mix-blend-mode: multiply;
}
</style>
</head>
<body>
<h1>Colors and Color Properties</h1>
<!-- ====== COLOR FORMATS ====== -->
<section>
<h2>1. Color Formats</h2>
<h3>Named Colors</h3>
<div class="demo-box named-demo">Named Color: lightblue / navy</div>
<h3>Hexadecimal Colors</h3>
<div class="demo-box hex-demo">Hex: #123456 / #FFFFFF / #FFD700</div>
<h3>RGB Colors</h3>
<div class="demo-box rgb-demo">RGB: rgb(255, 0, 0) / rgb(0, 255, 0)</div>
<h3>RGBA Colors (with opacity)</h3>
<div class="demo-box rgba-demo">RGBA: rgba(255, 0, 0, 0.5)</div>
<h3>HSL Colors</h3>
<div class="demo-box hsl-demo">HSL: hsl(240, 100%, 50%)</div>
<h3>HSLA Colors (with opacity)</h3>
<div class="demo-box hsla-demo">HSLA: hsla(22, 55%, 64%, 0.7)</div>
<div class="code-block">
/* Named Colors */
background-color: lightblue;
color: navy;
/* Hexadecimal */
background-color: #123456;
color: #FFFFFF;
/* RGB */
background-color: rgb(255, 0, 0);
color: rgb(255, 255, 255);
/* RGBA */
background-color: rgba(255, 0, 0, 0.5);
/* HSL */
background-color: hsl(240, 100%, 50%);
/* HSLA */
background-color: hsla(22, 55%, 64%, 0.7);
</div>
</section>
<!-- ====== COLOR PROPERTIES ====== -->
<section>
<h2>2. Color Properties</h2>
<div class="grid-3">
<div class="demo-box" style="background: #007bff; color: white;">
<code>color</code><br>Sets text color
</div>
<div class="demo-box" style="background: #28a745; color: white;">
<code>background-color</code><br>Sets background color
</div>
<div class="demo-box" style="background: white; border: 3px solid #dc3545;">
<code>border-color</code><br>Sets border color
</div>
</div>
<h3>Text Shadow</h3>
<div class="demo-box text-shadow-demo">Text Shadow</div>
<h3>Box Shadow</h3>
<div class="demo-box box-shadow-demo">Box Shadow</div>
<h3>Opacity</h3>
<div class="demo-box opacity-demo">Opacity: 0.7</div>
<h3>Caret Color</h3>
<input type="text" class="caret-demo" placeholder="Caret is red">
<div class="code-block">
/* Text Properties */
color: #333;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
/* Background Properties */
background-color: rgba(255, 0, 0, 0.5);
/* Border Properties */
border-color: hsl(0, 100%, 75%);
/* Box Shadow */
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
/* Opacity */
opacity: 0.7;
/* Caret Color */
caret-color: red;
</div>
</section>
<!-- ====== GRADIENTS ====== -->
<section>
<h2>3. Gradients</h2>
<div class="grid-3">
<div class="demo-box gradient-linear">Linear Gradient</div>
<div class="demo-box gradient-linear-multi">Multi-color Linear</div>
<div class="demo-box gradient-radial">Radial Gradient</div>
<div class="demo-box gradient-conic">Conic Gradient</div>
<div class="demo-box gradient-repeating">Repeating Linear</div>
</div>
<div class="code-block">
/* Linear Gradient (left to right) */
background-image: linear-gradient(to right, red, blue);
/* Linear Gradient (multiple colors) */
background-image: linear-gradient(to right, red, orange, yellow, green, blue, purple);
/* Radial Gradient (circle) */
background-image: radial-gradient(circle, red, blue);
/* Conic Gradient */
background-image: conic-gradient(red, blue, green, red);
/* Repeating Linear Gradient */
background-image: repeating-linear-gradient(45deg, red 10%, blue 20%);
</div>
</section>
<!-- ====== FILTERS ====== -->
<section>
<h2>4. Filters</h2>
<div class="filter-grid">
<div class="demo-box filter-hue">hue-rotate(180deg)</div>
<div class="demo-box filter-brightness">brightness(50%)</div>
<div class="demo-box filter-contrast">contrast(200%)</div>
<div class="demo-box filter-grayscale">grayscale(100%)</div>
<div class="demo-box filter-saturate">saturate(200%)</div>
<div class="demo-box filter-invert">invert(100%)</div>
<div class="demo-box filter-sepia">sepia(70%)</div>
<div class="demo-box filter-multiple">Multiple Filters</div>
</div>
<div class="code-block">
/* Individual Filters */
filter: hue-rotate(180deg);
filter: brightness(50%);
filter: contrast(200%);
filter: grayscale(100%);
filter: saturate(200%);
filter: invert(100%);
filter: sepia(70%);
/* Multiple Filters */
filter: brightness(150%) contrast(120%) saturate(150%);
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>Color Formats</h3>
<table class="reference-table">
<tr>
<th>Format</th>
<th>Example</th>
<th>Description</th>
</tr>
<tr>
<td><code>Named</code></td>
<td><code>red</code></td>
<td>Predefined color names</td>
</tr>
<tr>
<td><code>Hex</code></td>
<td><code>#FF0000</code></td>
<td>6-digit or 3-digit hex values</td>
</tr>
<tr>
<td><code>RGB</code></td>
<td><code>rgb(255, 0, 0)</code></td>
<td>Red, Green, Blue (0–255)</td>
</tr>
<tr>
<td><code>RGBA</code></td>
<td><code>rgba(255, 0, 0, 0.5)</code></td>
<td>RGB with opacity (0–1)</td>
</tr>
<tr>
<td><code>HSL</code></td>
<td><code>hsl(0, 100%, 50%)</code></td>
<td>Hue, Saturation, Lightness</td>
</tr>
<tr>
<td><code>HSLA</code></td>
<td><code>hsla(0, 100%, 50%, 0.5)</code></td>
<td>HSL with opacity</td>
</tr>
</table>
<h3>Color Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>color</code></td>
<td>Sets text color</td>
<td><code>color: #333;</code></td>
</tr>
<tr>
<td><code>background-color</code></td>
<td>Sets background color</td>
<td><code>background-color: rgba(255, 0, 0, 0.5);</code></td>
</tr>
<tr>
<td><code>border-color</code></td>
<td>Sets border color</td>
<td><code>border-color: hsl(0, 100%, 75%);</code></td>
</tr>
<tr>
<td><code>text-shadow</code></td>
<td>Adds shadow to text</td>
<td><code>text-shadow: 2px 2px 4px rgba(0,0,0,0.5);</code></td>
</tr>
<tr>
<td><code>box-shadow</code></td>
<td>Adds shadow to element</td>
<td><code>box-shadow: 2px 2px 4px rgba(0,0,0,0.3);</code></td>
</tr>
<tr>
<td><code>opacity</code></td>
<td>Element opacity (0–1)</td>
<td><code>opacity: 0.7;</code></td>
</tr>
<tr>
<td><code>caret-color</code></td>
<td>Sets input cursor color</td>
<td><code>caret-color: red;</code></td>
</tr>
</table>
<h3>Filters</h3>
<table class="reference-table">
<tr>
<th>Filter</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>hue-rotate</code></td>
<td>Rotates the color wheel</td>
<td><code>hue-rotate(180deg)</code></td>
</tr>
<tr>
<td><code>brightness</code></td>
<td>Changes brightness</td>
<td><code>brightness(50%)</code></td>
</tr>
<tr>
<td><code>contrast</code></td>
<td>Changes contrast</td>
<td><code>contrast(200%)</code></td>
</tr>
<tr>
<td><code>grayscale</code></td>
<td>Converts to grayscale</td>
<td><code>grayscale(100%)</code></td>
</tr>
<tr>
<td><code>saturate</code></td>
<td>Changes saturation</td>
<td><code>saturate(200%)</code></td>
</tr>
<tr>
<td><code>invert</code></td>
<td>Inverts colors</td>
<td><code>invert(100%)</code></td>
</tr>
<tr>
<td><code>sepia</code></td>
<td>Converts to sepia</td>
<td><code>sepia(70%)</code></td>
</tr>
</table>
</section>
</body>
</html>
Quick Reference
| Format | Example | Use Case |
|---|---|---|
| Named | red | Quick prototyping |
| Hex | #FF0000 | Most common, compact |
| RGB | rgb(255, 0, 0) | Easy to adjust values |
| RGBA | rgba(255, 0, 0, 0.5) | Transparent colors |
| HSL | hsl(0, 100%, 50%) | Easy to adjust hue/saturation |
| HSLA | hsla(0, 100%, 50%, 0.5) | Transparent HSL |
Best Practices
✅ Do This:
/* Use rgba/hsla for transparency */
background-color: rgba(255, 0, 0, 0.5);
/* Use hex for solid colors (compact) */
color: #007bff;
/* Use hsl for color variations */
background: hsl(200, 80%, 50%);
border-color: hsl(200, 80%, 40%);
/* Use text-shadow for readability on images */
text-shadow: 0 0 10px rgba(0, 0, 0, 0.7);
/* Use opacity for hover effects */
.button:hover { opacity: 0.8; }
❌ Don’t Do This:
/* Don't use too many color formats inconsistently */
h1 { color: red; }
h2 { color: #00F; }
h3 { color: rgb(0, 255, 0); }
/* Use one format consistently */
/* Don't use too low contrast */
color: #999;
background: #fff; /* Hard to read */
/* Don't overuse opacity on text */
opacity: 0.5; /* Might be hard to read */
Pro Tip: Use HSL when you need to create color variations (e.g., hover states). HSLA is great for transparent overlays. For solid colors, hex is the most common and compact. For transparency, use rgba or hsla. Always consider contrast and accessibility when choosing colors!
5. Background-Related Properties
CSS provides a rich set of properties to control the background of any element. You can set colors, images, gradients, and control how they are positioned, repeated, scaled, and clipped.
Overview of Background Properties
| Property | Description | Example |
|---|---|---|
background-color | Sets the background color | background-color: red; |
background-image | Sets the background image or gradient | background-image: url("image.jpg"); |
background-repeat | Controls how the image repeats | background-repeat: no-repeat; |
background-position | Sets the position of the image | background-position: center top; |
background-attachment | Controls scrolling behavior | background-attachment: fixed; |
background-clip | Defines the clipping area | background-clip: padding-box; |
background-origin | Sets the positioning origin | background-origin: border-box; |
background-size | Controls the scaling of the image | background-size: cover; |
1. background-color
Sets the color behind the content of the element.
/* Named colors */
.red-background {
background-color: red;
}
/* Hexadecimal */
.hex-background {
background-color: #123456;
}
/* RGB/RGBA */
.rgb-background {
background-color: rgb(255, 0, 0);
}
.rgba-background {
background-color: rgba(255, 0, 0, 0.5);
}
/* HSL/HSLA */
.hsl-background {
background-color: hsl(0, 100%, 50%);
}
.hsla-background {
background-color: hsla(0, 100%, 50%, 0.5);
}
2. background-image
Sets the background to an image or a gradient.
/* Image */
.image-background {
background-image: url("path/to/image.jpg");
}
/* Linear Gradient */
.gradient-background {
background-image: linear-gradient(to right, red, orange);
}
/* Radial Gradient */
.radial-background {
background-image: radial-gradient(circle, red, blue);
}
/* Multiple backgrounds */
.multiple-background {
background-image:
url("top-image.png"),
url("bottom-image.png");
}
3. background-repeat
Controls how the background image repeats.
| Value | Description | Example |
|---|---|---|
repeat | Repeats both horizontally and vertically (default) | background-repeat: repeat; |
no-repeat | Does not repeat | background-repeat: no-repeat; |
repeat-x | Repeats only horizontally | background-repeat: repeat-x; |
repeat-y | Repeats only vertically | background-repeat: repeat-y; |
space | Repeats with space between images | background-repeat: space; |
round | Repeats and stretches to fit | background-repeat: round; |
.repeated-background {
background-image: url("path/to/image.jpg");
background-repeat: repeat;
}
.no-repeat-background {
background-image: url("path/to/image.jpg");
background-repeat: no-repeat;
}
.repeat-x-background {
background-image: url("path/to/image.jpg");
background-repeat: repeat-x;
}
.repeat-y-background {
background-image: url("path/to/image.jpg");
background-repeat: repeat-y;
}
4. background-position
Sets the position of the background image relative to its container.
| Values | Description | Example |
|---|---|---|
left, center, right | Horizontal position | background-position: center; |
top, center, bottom | Vertical position | background-position: top; |
x% y% | Percentage values | background-position: 50% 50%; |
xpx ypx | Pixel values | background-position: 100px 50px; |
.positioned-background {
background-image: url("path/to/image.jpg");
background-repeat: no-repeat;
background-position: center top;
}
/* Center the background */
.centered-background {
background-position: center center;
}
/* Offset from the top-left */
.offset-background {
background-position: 20px 30px;
}
/* Using percentages */
.percent-background {
background-position: 75% 25%;
}
5. background-attachment
Controls whether the background image scrolls with the content.
| Value | Description | Example |
|---|---|---|
scroll | Scrolls with the page (default) | background-attachment: scroll; |
fixed | Fixed to the viewport | background-attachment: fixed; |
local | Scrolls with the element’s content | background-attachment: local; |
/* Fixed background (parallax effect) */
.fixed-background {
background-image: url("path/to/image.jpg");
background-repeat: no-repeat;
background-attachment: fixed;
}
/* Scrolls with the element */
.scroll-background {
background-attachment: scroll;
}
/* Local scrolling */
.local-background {
background-attachment: local;
}
6. background-clip
Defines the area where the background is visible.
| Value | Description | Example |
|---|---|---|
border-box | Extends to the outer edge of the border (default) | background-clip: border-box; |
padding-box | Extends to the inside of the border | background-clip: padding-box; |
content-box | Extends only to the content area | background-clip: content-box; |
text | Clips to the text (experimental) | background-clip: text; |
.clipped-background {
background-image: url("path/to/image.jpg");
background-repeat: no-repeat;
background-clip: padding-box;
}
/* Clip to text (needs text color transparent) */
.text-clip {
background-image: linear-gradient(to right, red, blue);
background-clip: text;
color: transparent;
}
7. background-origin
Sets the origin point for positioning the background image.
| Value | Description | Example |
|---|---|---|
border-box | Origin at the upper-left corner of the border | background-origin: border-box; |
padding-box | Origin at the upper-left corner of the padding (default) | background-origin: padding-box; |
content-box | Origin at the upper-left corner of the content | background-origin: content-box; |
.origin-background {
background-image: url("path/to/image.jpg");
background-repeat: no-repeat;
background-origin: border-box;
}
8. background-size
Controls the scaling of the background image.
| Value | Description | Example |
|---|---|---|
auto | Original size (default) | background-size: auto; |
cover | Fills the entire container (may crop) | background-size: cover; |
contain | Fits the entire image in the container | background-size: contain; |
x% y% | Percentage of the container | background-size: 100% 50%; |
xpx ypx | Pixel values | background-size: 300px 200px; |
/* Cover the entire element */
.scaled-background {
background-image: url("path/to/image.jpg");
background-repeat: no-repeat;
background-size: cover;
}
/* Contain the entire image */
.contain-background {
background-size: contain;
}
/* Specific dimensions */
.specific-size {
background-size: 300px 200px;
}
/* Percentage of container */
.percent-size {
background-size: 100% 50%;
}
The Shorthand background Property
You can combine multiple background properties into a single background shorthand.
/* Shorthand: image, position, size, repeat, attachment, color */
.background-shorthand {
background:
url("image.jpg")
center center / cover
no-repeat
fixed
#f8f9fa;
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Background Properties</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
height: 150px;
border-radius: 8px;
margin: 10px 0;
padding: 15px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
border: 5px solid #333;
}
.demo-box.light-text {
color: #333;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== BACKGROUND DEMOS ====== */
/* ---- background-color ---- */
.bg-color-demo {
background-color: #007bff;
}
.bg-color-rgba {
background-color: rgba(255, 0, 0, 0.5);
color: #333;
}
/* ---- background-image (gradients) ---- */
.bg-gradient-linear {
background-image: linear-gradient(to right, #007bff, #6c5ce7);
}
.bg-gradient-radial {
background-image: radial-gradient(circle, #28a745, #1e7e34);
}
.bg-gradient-conic {
background-image: conic-gradient(#ffc107, #dc3545, #007bff);
}
/* ---- background-image (image) ---- */
.bg-image-demo {
background-image: url('https://via.placeholder.com/400x200/007bff/ffffff?text=Background+Image');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
/* ---- background-repeat ---- */
.bg-repeat-repeat {
background-image: url('https://via.placeholder.com/50x50/ff6b6b/ffffff?text=A');
background-repeat: repeat;
background-size: 50px 50px;
}
.bg-repeat-no-repeat {
background-image: url('https://via.placeholder.com/50x50/4ecdc4/ffffff?text=B');
background-repeat: no-repeat;
background-position: center;
}
.bg-repeat-repeat-x {
background-image: url('https://via.placeholder.com/50x50/ffc107/333333?text=C');
background-repeat: repeat-x;
background-size: 50px 50px;
}
.bg-repeat-repeat-y {
background-image: url('https://via.placeholder.com/50x50/6c5ce7/ffffff?text=D');
background-repeat: repeat-y;
background-size: 50px 50px;
}
/* ---- background-position ---- */
.bg-position-center {
background-image: url('https://via.placeholder.com/100x100/007bff/ffffff?text=Center');
background-repeat: no-repeat;
background-position: center center;
}
.bg-position-top-left {
background-image: url('https://via.placeholder.com/100x100/28a745/ffffff?text=TL');
background-repeat: no-repeat;
background-position: top left;
}
.bg-position-bottom-right {
background-image: url('https://via.placeholder.com/100x100/dc3545/ffffff?text=BR');
background-repeat: no-repeat;
background-position: bottom right;
}
/* ---- background-attachment ---- */
.bg-attachment-fixed {
background-image: url('https://via.placeholder.com/400x300/6610f2/ffffff?text=Fixed');
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
min-height: 250px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: white;
text-shadow: 0 0 10px rgba(0,0,0,0.5);
}
/* ---- background-clip ---- */
.bg-clip-border {
background-image: url('https://via.placeholder.com/400x200/ffc107/333333?text=Border+Box');
background-repeat: no-repeat;
background-size: cover;
background-clip: border-box;
border: 10px dashed rgba(0,0,0,0.3);
}
.bg-clip-padding {
background-image: url('https://via.placeholder.com/400x200/17a2b8/ffffff?text=Padding+Box');
background-repeat: no-repeat;
background-size: cover;
background-clip: padding-box;
border: 10px dashed rgba(0,0,0,0.3);
}
.bg-clip-content {
background-image: url('https://via.placeholder.com/400x200/dc3545/ffffff?text=Content+Box');
background-repeat: no-repeat;
background-size: cover;
background-clip: content-box;
border: 10px dashed rgba(0,0,0,0.3);
padding: 20px;
}
.bg-clip-text {
background-image: linear-gradient(to right, #007bff, #dc3545);
background-clip: text;
color: transparent;
font-size: 3rem;
font-weight: bold;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
height: 150px;
}
/* ---- background-origin ---- */
.bg-origin-border {
background-image: url('https://via.placeholder.com/100x100/28a745/ffffff?text=Border');
background-repeat: no-repeat;
background-origin: border-box;
border: 20px solid rgba(0,0,0,0.1);
padding: 20px;
background-color: #f8f9fa;
}
.bg-origin-padding {
background-image: url('https://via.placeholder.com/100x100/007bff/ffffff?text=Pad');
background-repeat: no-repeat;
background-origin: padding-box;
border: 20px solid rgba(0,0,0,0.1);
padding: 20px;
background-color: #f8f9fa;
}
.bg-origin-content {
background-image: url('https://via.placeholder.com/100x100/dc3545/ffffff?text=Content');
background-repeat: no-repeat;
background-origin: content-box;
border: 20px solid rgba(0,0,0,0.1);
padding: 20px;
background-color: #f8f9fa;
}
/* ---- background-size ---- */
.bg-size-cover {
background-image: url('https://via.placeholder.com/800x200/007bff/ffffff?text=Cover');
background-repeat: no-repeat;
background-size: cover;
}
.bg-size-contain {
background-image: url('https://via.placeholder.com/800x200/28a745/ffffff?text=Contain');
background-repeat: no-repeat;
background-size: contain;
}
.bg-size-50 {
background-image: url('https://via.placeholder.com/800x200/dc3545/ffffff?text=50%');
background-repeat: no-repeat;
background-size: 50% 50%;
background-position: center;
}
/* ---- Shorthand ---- */
.bg-shorthand {
background:
url('https://via.placeholder.com/400x200/6610f2/ffffff?text=Shorthand')
center center / cover
no-repeat
fixed
#f8f9fa;
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
text-shadow: 0 0 10px rgba(0,0,0,0.5);
}
</style>
</head>
<body>
<h1>Background Properties</h1>
<!-- ====== 1. background-color ====== -->
<section>
<h2>1. background-color</h2>
<div class="grid-2">
<div class="demo-box bg-color-demo">background-color: #007bff</div>
<div class="demo-box bg-color-rgba">background-color: rgba(255, 0, 0, 0.5)</div>
</div>
<div class="code-block">
background-color: #007bff;
background-color: rgba(255, 0, 0, 0.5);
</div>
</section>
<!-- ====== 2. background-image (Gradients) ====== -->
<section>
<h2>2. background-image (Gradients)</h2>
<div class="grid-2">
<div class="demo-box bg-gradient-linear">Linear Gradient</div>
<div class="demo-box bg-gradient-radial">Radial Gradient</div>
<div class="demo-box bg-gradient-conic">Conic Gradient</div>
</div>
<div class="code-block">
background-image: linear-gradient(to right, #007bff, #6c5ce7);
background-image: radial-gradient(circle, #28a745, #1e7e34);
background-image: conic-gradient(#ffc107, #dc3545, #007bff);
</div>
</section>
<!-- ====== 3. background-image (Image) ====== -->
<section>
<h2>3. background-image (Image)</h2>
<div class="demo-box bg-image-demo">Background Image</div>
<div class="code-block">
background-image: url('path/to/image.jpg');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
</div>
</section>
<!-- ====== 4. background-repeat ====== -->
<section>
<h2>4. background-repeat</h2>
<div class="grid-2">
<div class="demo-box bg-repeat-repeat">repeat</div>
<div class="demo-box bg-repeat-no-repeat">no-repeat</div>
<div class="demo-box bg-repeat-repeat-x">repeat-x</div>
<div class="demo-box bg-repeat-repeat-y">repeat-y</div>
</div>
<div class="code-block">
background-repeat: repeat;
background-repeat: no-repeat;
background-repeat: repeat-x;
background-repeat: repeat-y;
</div>
</section>
<!-- ====== 5. background-position ====== -->
<section>
<h2>5. background-position</h2>
<div class="grid-2">
<div class="demo-box bg-position-center">center center</div>
<div class="demo-box bg-position-top-left">top left</div>
<div class="demo-box bg-position-bottom-right">bottom right</div>
</div>
<div class="code-block">
background-position: center center;
background-position: top left;
background-position: bottom right;
</div>
</section>
<!-- ====== 6. background-attachment ====== -->
<section>
<h2>6. background-attachment</h2>
<div class="demo-box bg-attachment-fixed">Fixed Background (scroll the page!)</div>
<div class="code-block">
background-attachment: fixed;
</div>
</section>
<!-- ====== 7. background-clip ====== -->
<section>
<h2>7. background-clip</h2>
<div class="grid-2">
<div class="demo-box bg-clip-border" style="color: #333;">border-box</div>
<div class="demo-box bg-clip-padding" style="color: #333;">padding-box</div>
<div class="demo-box bg-clip-content" style="color: #333; background-color: white;">content-box</div>
<div class="demo-box bg-clip-text" style="background: none;">Gradient Text</div>
</div>
<div class="code-block">
background-clip: border-box; /* Default */
background-clip: padding-box;
background-clip: content-box;
background-clip: text; /* Experimental */
</div>
</section>
<!-- ====== 8. background-origin ====== -->
<section>
<h2>8. background-origin</h2>
<div class="grid-2">
<div class="demo-box bg-origin-border" style="color: #333; min-height: 150px;">border-box</div>
<div class="demo-box bg-origin-padding" style="color: #333; min-height: 150px;">padding-box</div>
<div class="demo-box bg-origin-content" style="color: #333; min-height: 150px;">content-box</div>
</div>
<div class="code-block">
background-origin: border-box;
background-origin: padding-box; /* Default */
background-origin: content-box;
</div>
</section>
<!-- ====== 9. background-size ====== -->
<section>
<h2>9. background-size</h2>
<div class="grid-2">
<div class="demo-box bg-size-cover">cover</div>
<div class="demo-box bg-size-contain">contain</div>
<div class="demo-box bg-size-50">50% 50%</div>
</div>
<div class="code-block">
background-size: cover; /* Fills container (may crop) */
background-size: contain; /* Fits image in container */
background-size: 50% 50%; /* Percentage of container */
</div>
</section>
<!-- ====== 10. Shorthand ====== -->
<section>
<h2>10. Background Shorthand</h2>
<div class="demo-box bg-shorthand">Shorthand</div>
<div class="code-block">
background:
url('image.jpg')
center center / cover
no-repeat
fixed
#f8f9fa;
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>11. Reference Tables</h2>
<h3>Background Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>background-color</code></td>
<td>Sets the background color</td>
<td><code>background-color: red;</code></td>
</tr>
<tr>
<td><code>background-image</code></td>
<td>Sets the background image or gradient</td>
<td><code>background-image: url("image.jpg");</code></td>
</tr>
<tr>
<td><code>background-repeat</code></td>
<td>Controls image repetition</td>
<td><code>background-repeat: no-repeat;</code></td>
</tr>
<tr>
<td><code>background-position</code></td>
<td>Sets the image position</td>
<td><code>background-position: center top;</code></td>
</tr>
<tr>
<td><code>background-attachment</code></td>
<td>Controls scrolling behavior</td>
<td><code>background-attachment: fixed;</code></td>
</tr>
<tr>
<td><code>background-clip</code></td>
<td>Defines the clipping area</td>
<td><code>background-clip: padding-box;</code></td>
</tr>
<tr>
<td><code>background-origin</code></td>
<td>Sets the positioning origin</td>
<td><code>background-origin: border-box;</code></td>
</tr>
<tr>
<td><code>background-size</code></td>
<td>Controls image scaling</td>
<td><code>background-size: cover;</code></td>
</tr>
</table>
<h3>background-repeat Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr><td><code>repeat</code></td><td>Repeats both horizontally and vertically (default)</td></tr>
<tr><td><code>no-repeat</code></td><td>Does not repeat</td></tr>
<tr><td><code>repeat-x</code></td><td>Repeats only horizontally</td></tr>
<tr><td><code>repeat-y</code></td><td>Repeats only vertically</td></tr>
<tr><td><code>space</code></td><td>Repeats with space between images</td></tr>
<tr><td><code>round</code></td><td>Repeats and stretches to fit</td></tr>
</table>
<h3>background-size Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr><td><code>auto</code></td><td>Original size (default)</td></tr>
<tr><td><code>cover</code></td><td>Fills the entire container (may crop)</td></tr>
<tr><td><code>contain</code></td><td>Fits the entire image in the container</td></tr>
<tr><td><code>x% y%</code></td><td>Percentage of the container</td></tr>
<tr><td><code>xpx ypx</code></td><td>Pixel values</td></tr>
</table>
<h3>background-position Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr><td><code>left</code>, <code>center</code>, <code>right</code></td><td>Horizontal position</td></tr>
<tr><td><code>top</code>, <code>center</code>, <code>bottom</code></td><td>Vertical position</td></tr>
<tr><td><code>x% y%</code></td><td>Percentage values</td></tr>
<tr><td><code>xpx ypx</code></td><td>Pixel values</td></tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
background-color | Sets background color | red, #FF0000, rgb(255,0,0) |
background-image | Sets background image | url("image.jpg"), linear-gradient(...) |
background-repeat | Controls repetition | repeat, no-repeat, repeat-x, repeat-y |
background-position | Sets image position | center, top left, 50% 50% |
background-attachment | Controls scrolling | scroll, fixed, local |
background-clip | Defines clipping area | border-box, padding-box, content-box |
background-origin | Sets positioning origin | border-box, padding-box, content-box |
background-size | Controls scaling | cover, contain, auto, 100% |
Best Practices
✅ Do This:
/* Always set a fallback background-color when using images */
background-color: #f8f9fa;
background-image: url("image.jpg");
/* Use cover for hero sections */
.hero {
background-image: url("hero.jpg");
background-size: cover;
background-position: center;
}
/* Use contain for logos */
.logo {
background-image: url("logo.png");
background-size: contain;
background-repeat: no-repeat;
}
❌ Don’t Do This:
/* Don't use images without a fallback color */
background-image: url("image.jpg");
/* If the image fails to load, the background is transparent */
/* Don't use cover with important content that might be cropped */
.important-image {
background-size: cover;
}
/* Use contain instead for important content */
Pro Tip: Always set a background-color fallback when using background-image. This ensures readability if the image fails to load. Use background-size: cover for hero/header sections and background-size: contain for logos and icons. The background shorthand is powerful but can be confusing — use it when you’re comfortable with the syntax!
6. Fonts
Fonts play a crucial role in web design, affecting readability, user experience, and overall aesthetics. CSS provides several properties to customize the appearance of text, including font family, size, weight, style, and more.
Overview of Font Properties
| Property | Description | Example |
|---|---|---|
font-family | Specifies the font family | font-family: Arial, sans-serif; |
font-size | Specifies the font size | font-size: 16px; |
font-weight | Specifies the boldness | font-weight: bold; |
font-style | Specifies the style (italic, oblique) | font-style: italic; |
font-variant | Specifies small-caps or normal | font-variant: small-caps; |
line-height | Specifies the height of a line | line-height: 1.6; |
letter-spacing | Specifies space between characters | letter-spacing: 2px; |
word-spacing | Specifies space between words | word-spacing: 4px; |
text-align | Aligns text (left, right, center, justify) | text-align: center; |
text-decoration | Adds decoration (underline, overline, line-through) | text-decoration: underline; |
text-transform | Transforms text (uppercase, lowercase, capitalize) | text-transform: uppercase; |
1. font-family
Specifies the font family to be used for a text element. You can provide multiple font names as a fallback system.
.custom-font {
/* Primary font: Arial, fallback: sans-serif */
font-family: Arial, sans-serif;
/* Multiple fallbacks */
font-family: 'Times New Roman', Times, serif;
/* Google Font (imported) */
font-family: 'Open Sans', Arial, sans-serif;
}
Key Points:
- Font names with spaces should be enclosed in quotes (e.g.,
"Times New Roman") - Multiple fonts are separated by commas (the browser will try each one in order)
- Always end with a generic font family (
serif,sans-serif,monospace, etc.)
2. font-size
Specifies the size of the font. You can use absolute units (px, pt), relative units (em, rem, %), or keywords.
/* Pixels (absolute) */
.pixel-size {
font-size: 16px;
}
/* Ems (relative to parent) */
.em-size {
font-size: 1.2em; /* 1.2 × parent font size */
}
/* Rems (relative to root) */
.rem-size {
font-size: 1.2rem; /* 1.2 × root font size */
}
/* Percentage (relative to parent) */
.percent-size {
font-size: 120%; /* 120% of parent font size */
}
/* Keywords */
.keyword-size {
font-size: medium; /* xx-small, x-small, small, medium, large, x-large, xx-large */
font-size: smaller; /* relative to parent */
font-size: larger; /* relative to parent */
}
/* Viewport-based (responsive) */
.vw-size {
font-size: 2vw; /* 2% of viewport width */
}
3. font-weight
Specifies the boldness of the font. You can use keywords or numerical values.
| Value | Description | Example |
|---|---|---|
normal | Normal weight (400) | font-weight: normal; |
bold | Bold weight (700) | font-weight: bold; |
bolder | One level bolder than parent | font-weight: bolder; |
lighter | One level lighter than parent | font-weight: lighter; |
100–900 | Numerical values (100 = thin, 900 = black) | font-weight: 300; |
.custom-font {
font-weight: bold; /* 700 */
}
.thin-text {
font-weight: 100; /* Thin */
}
.light-text {
font-weight: 300; /* Light */
}
.regular-text {
font-weight: 400; /* Normal */
}
.semi-bold-text {
font-weight: 600; /* Semi-bold */
}
.black-text {
font-weight: 900; /* Black (very bold) */
}
4. font-style
Specifies the style of the font.
| Value | Description | Example |
|---|---|---|
normal | Normal style (default) | font-style: normal; |
italic | Italic style | font-style: italic; |
oblique | Oblique style (slanted) | font-style: oblique; |
.custom-font {
font-style: italic;
}
.normal-style {
font-style: normal;
}
.oblique-style {
font-style: oblique;
}
5. font-variant
Specifies whether the text should be displayed in small-caps.
.small-caps {
font-variant: small-caps;
}
.normal-variant {
font-variant: normal;
}
6. Additional Text Properties
line-height
Controls the height of a line of text.
.text {
line-height: 1.6; /* Unitless — 1.6 × font size */
line-height: 24px; /* Fixed pixel value */
line-height: 150%; /* Percentage of font size */
}
letter-spacing
Controls the space between characters.
.text {
letter-spacing: 2px; /* Adds space between letters */
letter-spacing: -1px; /* Reduces space between letters */
}
word-spacing
Controls the space between words.
.text {
word-spacing: 4px; /* Adds space between words */
}
text-align
Aligns text horizontally.
.text {
text-align: left; /* Left-aligned (default) */
text-align: right; /* Right-aligned */
text-align: center; /* Center-aligned */
text-align: justify; /* Justified (spread across line) */
}
text-decoration
Adds decoration to text.
.text {
text-decoration: underline; /* Underline */
text-decoration: overline; /* Overline */
text-decoration: line-through; /* Strikethrough */
text-decoration: underline overline; /* Multiple decorations */
}
text-transform
Transforms the case of text.
.text {
text-transform: uppercase; /* ALL CAPS */
text-transform: lowercase; /* all lowercase */
text-transform: capitalize; /* First Letter Capitalized */
text-transform: none; /* Original case */
}
The Shorthand font Property
You can combine multiple font properties into a single font shorthand.
/* Shorthand: style variant weight size/line-height family */
.font-shorthand {
font: italic small-caps bold 16px/1.6 Arial, sans-serif;
}
/* Minimal shorthand: size family */
.font-shorthand-minimal {
font: 16px Arial, sans-serif;
}
Order:
font-style(optional)font-variant(optional)font-weight(optional)font-size(required)/ line-height(optional)font-family(required)
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Font Properties</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
background: #e9ecef;
border-left: 4px solid #007bff;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.grid-2 .demo-box {
border-left: 4px solid #28a745;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== FONT DEMOS ====== */
/* ---- font-family ---- */
.ff-arial {
font-family: Arial, sans-serif;
}
.ff-times {
font-family: 'Times New Roman', Times, serif;
}
.ff-courier {
font-family: 'Courier New', monospace;
}
.ff-georgia {
font-family: Georgia, 'Times New Roman', serif;
}
/* ---- font-size ---- */
.fs-px {
font-size: 16px;
}
.fs-em {
font-size: 1.5em;
}
.fs-rem {
font-size: 1.5rem;
}
.fs-vw {
font-size: 2vw;
}
.fs-small {
font-size: small;
}
.fs-large {
font-size: large;
}
/* ---- font-weight ---- */
.fw-thin {
font-weight: 100;
}
.fw-light {
font-weight: 300;
}
.fw-normal {
font-weight: 400;
}
.fw-bold {
font-weight: 700;
}
.fw-black {
font-weight: 900;
}
/* ---- font-style ---- */
.fs-normal {
font-style: normal;
}
.fs-italic {
font-style: italic;
}
.fs-oblique {
font-style: oblique;
}
/* ---- font-variant ---- */
.fv-smallcaps {
font-variant: small-caps;
}
/* ---- text-decoration ---- */
.td-underline {
text-decoration: underline;
}
.td-overline {
text-decoration: overline;
}
.td-line-through {
text-decoration: line-through;
}
.td-combined {
text-decoration: underline overline;
}
/* ---- text-transform ---- */
.tt-uppercase {
text-transform: uppercase;
}
.tt-lowercase {
text-transform: lowercase;
}
.tt-capitalize {
text-transform: capitalize;
}
/* ---- letter-spacing & word-spacing ---- */
.ls-wide {
letter-spacing: 4px;
}
.ls-tight {
letter-spacing: -1px;
}
.ws-wide {
word-spacing: 8px;
}
/* ---- text-align ---- */
.ta-left {
text-align: left;
}
.ta-center {
text-align: center;
}
.ta-right {
text-align: right;
}
.ta-justify {
text-align: justify;
}
/* ---- line-height ---- */
.lh-tight {
line-height: 1.2;
}
.lh-normal {
line-height: 1.6;
}
.lh-loose {
line-height: 2.5;
}
/* ---- Shorthand ---- */
.font-shorthand {
font: italic bold 18px/1.8 'Georgia', serif;
}
</style>
</head>
<body>
<h1>Font Properties</h1>
<!-- ====== 1. font-family ====== -->
<section>
<h2>1. font-family</h2>
<div class="grid-2">
<div class="demo-box ff-arial"><strong>font-family:</strong> Arial, sans-serif</div>
<div class="demo-box ff-times"><strong>font-family:</strong> 'Times New Roman', Times, serif</div>
<div class="demo-box ff-courier"><strong>font-family:</strong> 'Courier New', monospace</div>
<div class="demo-box ff-georgia"><strong>font-family:</strong> Georgia, 'Times New Roman', serif</div>
</div>
<div class="code-block">
font-family: Arial, sans-serif;
font-family: 'Times New Roman', Times, serif;
font-family: 'Courier New', monospace;
font-family: Georgia, 'Times New Roman', serif;
</div>
</section>
<!-- ====== 2. font-size ====== -->
<section>
<h2>2. font-size</h2>
<div class="grid-2">
<div class="demo-box fs-px"><strong>font-size:</strong> 16px</div>
<div class="demo-box fs-em"><strong>font-size:</strong> 1.5em (1.5 × parent)</div>
<div class="demo-box fs-rem"><strong>font-size:</strong> 1.5rem (1.5 × root)</div>
<div class="demo-box fs-vw"><strong>font-size:</strong> 2vw (2% of viewport width)</div>
<div class="demo-box fs-small"><strong>font-size:</strong> small (keyword)</div>
<div class="demo-box fs-large"><strong>font-size:</strong> large (keyword)</div>
</div>
<div class="code-block">
font-size: 16px;
font-size: 1.5em;
font-size: 1.5rem;
font-size: 2vw;
font-size: small;
font-size: large;
</div>
</section>
<!-- ====== 3. font-weight ====== -->
<section>
<h2>3. font-weight</h2>
<div class="grid-2">
<div class="demo-box fw-thin">font-weight: 100 (Thin)</div>
<div class="demo-box fw-light">font-weight: 300 (Light)</div>
<div class="demo-box fw-normal">font-weight: 400 (Normal)</div>
<div class="demo-box fw-bold">font-weight: 700 (Bold)</div>
<div class="demo-box fw-black">font-weight: 900 (Black)</div>
</div>
<div class="code-block">
font-weight: 100; /* Thin */
font-weight: 300; /* Light */
font-weight: 400; /* Normal */
font-weight: 700; /* Bold */
font-weight: 900; /* Black */
</div>
</section>
<!-- ====== 4. font-style ====== -->
<section>
<h2>4. font-style</h2>
<div class="grid-2">
<div class="demo-box fs-normal">font-style: normal</div>
<div class="demo-box fs-italic">font-style: italic</div>
<div class="demo-box fs-oblique">font-style: oblique</div>
</div>
<div class="code-block">
font-style: normal;
font-style: italic;
font-style: oblique;
</div>
</section>
<!-- ====== 5. font-variant ====== -->
<section>
<h2>5. font-variant</h2>
<div class="demo-box fv-smallcaps">font-variant: small-caps</div>
<div class="code-block">
font-variant: small-caps;
</div>
</section>
<!-- ====== 6. text-decoration ====== -->
<section>
<h2>6. text-decoration</h2>
<div class="grid-2">
<div class="demo-box td-underline">text-decoration: underline</div>
<div class="demo-box td-overline">text-decoration: overline</div>
<div class="demo-box td-line-through">text-decoration: line-through</div>
<div class="demo-box td-combined">text-decoration: underline overline</div>
</div>
<div class="code-block">
text-decoration: underline;
text-decoration: overline;
text-decoration: line-through;
text-decoration: underline overline;
</div>
</section>
<!-- ====== 7. text-transform ====== -->
<section>
<h2>7. text-transform</h2>
<div class="grid-2">
<div class="demo-box tt-uppercase">text-transform: uppercase</div>
<div class="demo-box tt-lowercase">TEXT-TRANSFORM: lowercase</div>
<div class="demo-box tt-capitalize">text-transform: capitalize</div>
</div>
<div class="code-block">
text-transform: uppercase; /* ALL CAPS */
text-transform: lowercase; /* all lowercase */
text-transform: capitalize; /* First Letter Capitalized */
</div>
</section>
<!-- ====== 8. letter-spacing & word-spacing ====== -->
<section>
<h2>8. letter-spacing & word-spacing</h2>
<div class="grid-2">
<div class="demo-box ls-wide">letter-spacing: 4px (wide)</div>
<div class="demo-box ls-tight">letter-spacing: -1px (tight)</div>
<div class="demo-box ws-wide">word-spacing: 8px (wide words)</div>
</div>
<div class="code-block">
letter-spacing: 4px;
letter-spacing: -1px;
word-spacing: 8px;
</div>
</section>
<!-- ====== 9. text-align ====== -->
<section>
<h2>9. text-align</h2>
<div class="grid-2">
<div class="demo-box ta-left">text-align: left</div>
<div class="demo-box ta-center">text-align: center</div>
<div class="demo-box ta-right">text-align: right</div>
<div class="demo-box ta-justify" style="width: 300px;">
text-align: justify — this text will be spread across the entire line width.
</div>
</div>
<div class="code-block">
text-align: left;
text-align: center;
text-align: right;
text-align: justify;
</div>
</section>
<!-- ====== 10. line-height ====== -->
<section>
<h2>10. line-height</h2>
<div class="grid-2">
<div class="demo-box lh-tight" style="width: 300px;">
line-height: 1.2 (tight) <br>
This is a second line.
</div>
<div class="demo-box lh-normal" style="width: 300px;">
line-height: 1.6 (normal) <br>
This is a second line.
</div>
<div class="demo-box lh-loose" style="width: 300px;">
line-height: 2.5 (loose) <br>
This is a second line.
</div>
</div>
<div class="code-block">
line-height: 1.2;
line-height: 1.6;
line-height: 2.5;
</div>
</section>
<!-- ====== 11. Shorthand ====== -->
<section>
<h2>11. font Shorthand</h2>
<div class="demo-box font-shorthand">
font: italic bold 18px/1.8 'Georgia', serif;
</div>
<div class="code-block">
/* Shorthand: style variant weight size/line-height family */
font: italic small-caps bold 16px/1.6 Arial, sans-serif;
font: italic bold 18px/1.8 'Georgia', serif;
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>12. Reference Tables</h2>
<h3>Font Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>font-family</code></td>
<td>Specifies the font family</td>
<td><code>font-family: Arial, sans-serif;</code></td>
</tr>
<tr>
<td><code>font-size</code></td>
<td>Specifies the font size</td>
<td><code>font-size: 16px;</code></td>
</tr>
<tr>
<td><code>font-weight</code></td>
<td>Specifies the boldness</td>
<td><code>font-weight: bold;</code></td>
</tr>
<tr>
<td><code>font-style</code></td>
<td>Specifies the style</td>
<td><code>font-style: italic;</code></td>
</tr>
<tr>
<td><code>font-variant</code></td>
<td>Specifies small-caps</td>
<td><code>font-variant: small-caps;</code></td>
</tr>
<tr>
<td><code>line-height</code></td>
<td>Specifies the line height</td>
<td><code>line-height: 1.6;</code></td>
</tr>
<tr>
<td><code>letter-spacing</code></td>
<td>Specifies space between characters</td>
<td><code>letter-spacing: 2px;</code></td>
</tr>
<tr>
<td><code>word-spacing</code></td>
<td>Specifies space between words</td>
<td><code>word-spacing: 4px;</code></td>
</tr>
<tr>
<td><code>text-align</code></td>
<td>Aligns text horizontally</td>
<td><code>text-align: center;</code></td>
</tr>
<tr>
<td><code>text-decoration</code></td>
<td>Adds decoration</td>
<td><code>text-decoration: underline;</code></td>
</tr>
<tr>
<td><code>text-transform</code></td>
<td>Transforms text case</td>
<td><code>text-transform: uppercase;</code></td>
</tr>
</table>
<h3>font-weight Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr><td><code>100</code></td><td>Thin / Hairline</td></tr>
<tr><td><code>200</code></td><td>Extra Light (Ultra Light)</td></tr>
<tr><td><code>300</code></td><td>Light</td></tr>
<tr><td><code>400</code></td><td>Normal (default)</td></tr>
<tr><td><code>500</code></td><td>Medium</td></tr>
<tr><td><code>600</code></td><td>Semi Bold (Demi Bold)</td></tr>
<tr><td><code>700</code></td><td>Bold</td></tr>
<tr><td><code>800</code></td><td>Extra Bold (Ultra Bold)</td></tr>
<tr><td><code>900</code></td><td>Black (Heavy)</td></tr>
<tr><td><code>normal</code></td><td>Equivalent to 400</td></tr>
<tr><td><code>bold</code></td><td>Equivalent to 700</td></tr>
</table>
<h3>Generic Font Families</h3>
<table class="reference-table">
<tr>
<th>Generic Family</th>
<th>Description</th>
<th>Example Fonts</th>
</tr>
<tr><td><code>serif</code></td><td>Fonts with decorative strokes</td><td>Times New Roman, Georgia, Garamond</td></tr>
<tr><td><code>sans-serif</code></td><td>Fonts without decorative strokes</td><td>Arial, Helvetica, Verdana, Open Sans</td></tr>
<tr><td><code>monospace</code></td><td>Fonts with fixed character width</td><td>Courier New, Consolas, Monaco</td></tr>
<tr><td><code>cursive</code></td><td>Fonts that resemble handwriting</td><td>Brush Script, Comic Sans</td></tr>
<tr><td><code>fantasy</code></td><td>Decorative fonts</td><td>Impact, Papyrus</td></tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
font-family | Font family | Arial, sans-serif |
font-size | Font size | 16px, 1rem, 1.2em |
font-weight | Boldness | normal, bold, 100–900 |
font-style | Style | normal, italic, oblique |
font-variant | Variant | normal, small-caps |
line-height | Line height | 1.6, 24px, 150% |
letter-spacing | Character spacing | 2px, -1px |
word-spacing | Word spacing | 4px |
text-align | Horizontal alignment | left, center, right, justify |
text-decoration | Decoration | none, underline, overline, line-through |
text-transform | Case transformation | none, uppercase, lowercase, capitalize |
Best Practices
✅ Do This:
/* Use rem for font sizes (accessibility) */
html { font-size: 16px; }
h1 { font-size: 2rem; } /* 32px */
p { font-size: 1rem; } /* 16px */
/* Always provide fallback fonts */
font-family: 'Open Sans', Arial, sans-serif;
/* Use line-height for readability */
body { line-height: 1.6; }
/* Use relative units for responsive typography */
h1 { font-size: clamp(1.5rem, 4vw, 3rem); }
❌ Don’t Do This:
/* Don't use px for font sizes (accessibility issue) */
font-size: 16px; /* Prefer rem */
/* Don't forget fallback fonts */
font-family: 'CustomFont'; /* If font fails, browser default is used */
/* Don't set line-height to a fixed unit for body text */
body { line-height: 24px; } /* Prefer unitless (1.6) */
Pro Tip: Use rem for font sizes to ensure accessibility (users can change the root font size in their browser settings). Use em for component-based sizing (e.g., within a card or button). Always provide fallback fonts and end with a generic family. Use line-height: 1.6 for optimal readability on the web!
7. line-height and text-decoration
Line height and text decoration are essential CSS properties for controlling the spacing and visual styling of text. They enhance readability and allow you to add decorative elements to your text.
line-height
The line-height property specifies the space between lines of text. It’s one of the most important properties for readability.
p {
line-height: 1.5em; /* Adjust for desired spacing */
}
Values
| Value Type | Example | Description |
|---|---|---|
| Number | 1.5 | Multiplied by the current font size (recommended) |
| Length | 24px | Fixed pixel value |
| Percentage | 150% | Percentage of the current font size |
| Keyword | normal | Browser default (usually 1.2) |
/* Different line-height values */
.lh-number {
line-height: 1.5; /* 1.5 × font size — recommended */
}
.lh-pixel {
line-height: 30px; /* Fixed pixel value */
}
.lh-percent {
line-height: 150%; /* 150% of font size */
}
.lh-normal {
line-height: normal; /* Browser default ~1.2 */
}
.lh-tight {
line-height: 1.2; /* Tighter spacing */
}
.lh-loose {
line-height: 2.0; /* Looser spacing */
}
text-decoration
The text-decoration property adds decorative lines to text. It’s commonly used for links, headings, and emphasizing content.
h1 {
text-decoration: underline; /* Underlines Headers 1-3 */
}
h2 {
text-decoration: overline dotted green;
}
h3 {
text-decoration: overline underline purple;
}
Values
| Value | Description | Example |
|---|---|---|
none | No decoration (default) | text-decoration: none; |
underline | Line below the text | text-decoration: underline; |
overline | Line above the text | text-decoration: overline; |
line-through | Line through the text | text-decoration: line-through; |
underline overline | Multiple decorations | text-decoration: underline overline; |
Style and Color
You can also specify the style and color of the decoration.
/* Style: solid (default), dotted, dashed, wavy, double */
.text-decoration-style {
text-decoration: underline dotted red;
text-decoration: overline wavy blue;
text-decoration: line-through double green;
}
/* Separate properties */
.text-decoration-separate {
text-decoration-line: underline;
text-decoration-style: wavy;
text-decoration-color: #ff6b6b;
}
| Style | Description | Example |
|---|---|---|
solid | Single solid line (default) | text-decoration: underline solid red; |
dotted | Dotted line | text-decoration: underline dotted blue; |
dashed | Dashed line | text-decoration: underline dashed green; |
wavy | Wavy line | text-decoration: underline wavy purple; |
double | Double line | text-decoration: underline double orange; |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>line-height and text-decoration</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
background: #e9ecef;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.grid-2 .demo-box {
border-left: 4px solid #28a745;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== LINE HEIGHT DEMOS ====== */
.lh-tight {
line-height: 1.2;
}
.lh-normal {
line-height: 1.6;
}
.lh-loose {
line-height: 2.5;
}
.lh-pixel {
line-height: 30px;
}
.lh-percent {
line-height: 200%;
}
/* ====== TEXT DECORATION DEMOS ====== */
.td-none {
text-decoration: none;
}
.td-underline {
text-decoration: underline;
}
.td-overline {
text-decoration: overline;
}
.td-line-through {
text-decoration: line-through;
}
.td-combined {
text-decoration: underline overline;
}
.td-solid {
text-decoration: underline solid #007bff;
}
.td-dotted {
text-decoration: underline dotted #28a745;
}
.td-dashed {
text-decoration: underline dashed #dc3545;
}
.td-wavy {
text-decoration: underline wavy #ffc107;
}
.td-double {
text-decoration: underline double #6c5ce7;
}
.td-underline-color {
text-decoration: underline #dc3545;
}
.td-overline-dotted {
text-decoration: overline dotted #28a745;
}
.td-overline-underline {
text-decoration: overline underline #6c5ce7;
}
.td-wavy-underline {
text-decoration: underline wavy #dc3545;
}
.td-no-link {
text-decoration: none;
}
/* ====== COMBINED DEMOS ====== */
.combined-demo {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin: 10px 0;
}
.combined-demo h3 {
margin-top: 0;
}
</style>
</head>
<body>
<h1>line-height and text-decoration</h1>
<!-- ====== 1. LINE HEIGHT ====== -->
<section>
<h2>1. line-height</h2>
<div class="grid-2">
<div class="demo-box lh-tight">
<strong>line-height: 1.2 (Tight)</strong><br>
This is the first line of text.<br>
This is the second line of text.<br>
This is the third line of text.
</div>
<div class="demo-box lh-normal">
<strong>line-height: 1.6 (Normal)</strong><br>
This is the first line of text.<br>
This is the second line of text.<br>
This is the third line of text.
</div>
<div class="demo-box lh-loose">
<strong>line-height: 2.5 (Loose)</strong><br>
This is the first line of text.<br>
This is the second line of text.<br>
This is the third line of text.
</div>
<div class="demo-box lh-pixel">
<strong>line-height: 30px (Fixed)</strong><br>
This is the first line of text.<br>
This is the second line of text.<br>
This is the third line of text.
</div>
<div class="demo-box lh-percent">
<strong>line-height: 200% (Percentage)</strong><br>
This is the first line of text.<br>
This is the second line of text.<br>
This is the third line of text.
</div>
</div>
<div class="code-block">
/* Number (recommended) */
line-height: 1.5; /* 1.5 × font size */
/* Pixel */
line-height: 30px;
/* Percentage */
line-height: 150%;
/* Keyword */
line-height: normal; /* Browser default ~1.2 */
</div>
</section>
<!-- ====== 2. TEXT DECORATION ====== -->
<section>
<h2>2. text-decoration</h2>
<h3>Decoration Lines</h3>
<div class="grid-2">
<div class="demo-box td-none"><strong>text-decoration:</strong> none (no decoration)</div>
<div class="demo-box td-underline"><strong>text-decoration:</strong> underline</div>
<div class="demo-box td-overline"><strong>text-decoration:</strong> overline</div>
<div class="demo-box td-line-through"><strong>text-decoration:</strong> line-through</div>
<div class="demo-box td-combined"><strong>text-decoration:</strong> underline overline</div>
</div>
<h3>Decoration Styles & Colors</h3>
<div class="grid-2">
<div class="demo-box td-solid"><strong>text-decoration:</strong> underline solid blue</div>
<div class="demo-box td-dotted"><strong>text-decoration:</strong> underline dotted green</div>
<div class="demo-box td-dashed"><strong>text-decoration:</strong> underline dashed red</div>
<div class="demo-box td-wavy"><strong>text-decoration:</strong> underline wavy yellow</div>
<div class="demo-box td-double"><strong>text-decoration:</strong> underline double purple</div>
</div>
<h3>Multiple Decorations</h3>
<div class="grid-2">
<div class="demo-box td-overline-dotted">
<strong>text-decoration:</strong> overline dotted green
</div>
<div class="demo-box td-overline-underline">
<strong>text-decoration:</strong> overline underline purple
</div>
<div class="demo-box td-wavy-underline">
<strong>text-decoration:</strong> underline wavy red
</div>
</div>
<div class="code-block">
/* Basic decorations */
text-decoration: none;
text-decoration: underline;
text-decoration: overline;
text-decoration: line-through;
text-decoration: underline overline;
/* With style and color */
text-decoration: underline solid blue;
text-decoration: underline dotted green;
text-decoration: underline dashed red;
text-decoration: underline wavy yellow;
text-decoration: underline double purple;
/* Multiple decorations */
text-decoration: overline dotted green;
text-decoration: overline underline purple;
</div>
</section>
<!-- ====== 3. HEADER EXAMPLES ====== -->
<section>
<h2>3. Header Examples (from the lesson)</h2>
<div class="demo-box">
<h1 style="text-decoration: underline; color: #007bff;">
This is a Header 1 (underline)
</h1>
<h2 style="text-decoration: overline dotted green; color: #28a745;">
This is a Header 2 (overline dotted green)
</h2>
<h3 style="text-decoration: overline underline purple; color: #6c5ce7;">
This is a Header 3 (overline underline purple)
</h3>
</div>
<div class="code-block">
h1 {
text-decoration: underline; /* Underline only */
}
h2 {
text-decoration: overline dotted green; /* Overline dotted green */
}
h3 {
text-decoration: overline underline purple; /* Overline and underline purple */
}
</div>
</section>
<!-- ====== 4. PARAGRAPH EXAMPLES ====== -->
<section>
<h2>4. Paragraph Example (line-height)</h2>
<div class="demo-box">
<p style="line-height: 1.6; color: #333;">
<strong>line-height: 1.6</strong><br>
This is a paragraph with some example text. Notice how the lines are spaced out according to the `line-height` property value set to 1.6. You can change this value to adjust the spacing as needed. Proper line height improves readability and makes your content more accessible.
</p>
<p style="line-height: 2.0; color: #333;">
<strong>line-height: 2.0</strong><br>
This is a paragraph with a looser line height. The lines are more spaced out, which can be useful for certain design styles or when you want more breathing room between lines of text.
</p>
<p style="line-height: 1.2; color: #333;">
<strong>line-height: 1.2</strong><br>
This is a paragraph with a tighter line height. The lines are closer together, which can be useful for headings or when you want to save vertical space.
</p>
</div>
</section>
<!-- ====== 5. COMBINED EXAMPLES ====== -->
<section>
<h2>5. Combined Examples</h2>
<div class="combined-demo">
<h3 style="text-decoration: underline wavy #007bff; line-height: 1.8;">
Wavy Underline Heading
</h3>
<p style="line-height: 1.6; font-size: 1rem;">
This paragraph has a <span style="text-decoration: line-through dashed #dc3545;">strikethrough</span> and
<span style="text-decoration: underline wavy #28a745;">wavy underline</span> for emphasis.
You can combine different <span style="text-decoration: underline overline dotted #6c5ce7;">decorations</span>
on different parts of your text.
</p>
</div>
<div class="combined-demo" style="background: #1e1e1e; color: #d4d4d4;">
<h3 style="text-decoration: underline wavy #ffc107; color: #ffc107; line-height: 1.8;">
Dark Theme Example
</h3>
<p style="line-height: 1.8; font-size: 1rem;">
<span style="text-decoration: underline solid #4ecdc4;">Underlined</span> text with
<span style="text-decoration: overline dotted #ff6b6b;">overlined</span> text and
<span style="text-decoration: line-through wavy #ffc107;">strikethrough</span>.
</p>
</div>
</section>
<!-- ====== 6. LINK EXAMPLE ====== -->
<section>
<h2>6. Links (text-decoration)</h2>
<div class="demo-box">
<p>
<a href="#" style="text-decoration: none; color: #007bff;">
Link with no underline (text-decoration: none)
</a>
<br><br>
<a href="#" style="text-decoration: underline; color: #28a745;">
Link with underline (text-decoration: underline)
</a>
<br><br>
<a href="#" style="text-decoration: underline wavy #dc3545; color: #dc3545;">
Link with wavy underline (text-decoration: underline wavy red)
</a>
</p>
</div>
<div class="code-block">
/* Remove underline from links */
a {
text-decoration: none;
}
/* Custom link underline */
a:hover {
text-decoration: underline wavy #007bff;
}
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>line-height Values</h3>
<table class="reference-table">
<tr>
<th>Value Type</th>
<th>Example</th>
<th>Description</th>
</tr>
<tr>
<td><strong>Number</strong></td>
<td><code>1.5</code></td>
<td>Multiplied by font size — <strong>recommended</strong></td>
</tr>
<tr>
<td><strong>Length</strong></td>
<td><code>24px</code></td>
<td>Fixed pixel value</td>
</tr>
<tr>
<td><strong>Percentage</strong></td>
<td><code>150%</code></td>
<td>Percentage of font size</td>
</tr>
<tr>
<td><strong>Keyword</strong></td>
<td><code>normal</code></td>
<td>Browser default (~1.2)</td>
</tr>
</table>
<h3>text-decoration Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>No decoration (default)</td>
<td><code>text-decoration: none;</code></td>
</tr>
<tr>
<td><code>underline</code></td>
<td>Line below the text</td>
<td><code>text-decoration: underline;</code></td>
</tr>
<tr>
<td><code>overline</code></td>
<td>Line above the text</td>
<td><code>text-decoration: overline;</code></td>
</tr>
<tr>
<td><code>line-through</code></td>
<td>Line through the text</td>
<td><code>text-decoration: line-through;</code></td>
</tr>
<tr>
<td><code>underline overline</code></td>
<td>Multiple decorations</td>
<td><code>text-decoration: underline overline;</code></td>
</tr>
</table>
<h3>text-decoration Styles</h3>
<table class="reference-table">
<tr>
<th>Style</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>solid</code></td>
<td>Single solid line (default)</td>
<td><code>text-decoration: underline solid red;</code></td>
</tr>
<tr>
<td><code>dotted</code></td>
<td>Dotted line</td>
<td><code>text-decoration: underline dotted blue;</code></td>
</tr>
<tr>
<td><code>dashed</code></td>
<td>Dashed line</td>
<td><code>text-decoration: underline dashed green;</code></td>
</tr>
<tr>
<td><code>wavy</code></td>
<td>Wavy line</td>
<td><code>text-decoration: underline wavy purple;</code></td>
</tr>
<tr>
<td><code>double</code></td>
<td>Double line</td>
<td><code>text-decoration: underline double orange;</code></td>
</tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
line-height | Space between lines | 1.5, 1.6, 24px, 150%, normal |
text-decoration | Adds decoration to text | none, underline, overline, line-through |
text-decoration-style | Style of the decoration | solid, dotted, dashed, wavy, double |
text-decoration-color | Color of the decoration | red, #ff0000, rgb(255,0,0) |
Best Practices
✅ Do This:
/* Use unitless values for line-height */
body {
line-height: 1.6; /* Recommended for body text */
}
/* Use line-height for headings */
h1 { line-height: 1.2; }
h2 { line-height: 1.3; }
p { line-height: 1.6; }
/* Remove underline from links when not needed */
a { text-decoration: none; }
/* Add custom underline on hover */
a:hover { text-decoration: underline; }
❌ Don’t Do This:
/* Don't use fixed pixel line-height for body text */
body { line-height: 24px; } /* Use unitless instead */
/* Don't use line-height: normal for body text (too tight) */
body { line-height: normal; } /* ~1.2 is too tight for readability */
/* Don't overuse text-decoration */
text-decoration: underline; /* Use sparingly for emphasis */
Pro Tip: Use unitless numbers for line-height (e.g., 1.6) because they scale with the font size and work well with inheritance. For body text, 1.6 is often considered optimal for readability. For headings, tighter values like 1.2–1.3 work better. Use text-decoration sparingly — underline for links, overline for headings, and line-through for deleted content!
8. text-align, letter-spacing, word-spacing, and text-transform
These CSS properties control the alignment, spacing, and case transformation of text, helping you create visually appealing and well-structured content.
Overview of Properties
| Property | Description | Common Values |
|---|---|---|
text-align | Aligns text horizontally within its parent | left, center, right, justify |
letter-spacing | Sets space between characters | normal, 2px, 0.1em |
word-spacing | Sets space between words | normal, 4px, 0.2em |
text-transform | Transforms text case | none, uppercase, lowercase, capitalize |
1. text-align
The text-align property specifies how text is aligned horizontally within its parent element.
/* Left-aligned */
div.left-aligned {
text-align: left;
}
/* Center-aligned */
div.center-aligned {
text-align: center;
}
/* Right-aligned */
div.right-aligned {
text-align: right;
}
/* Justified (spread across the line) */
div.justified {
text-align: justify;
}
Values
| Value | Description | Example |
|---|---|---|
left | Aligns text to the left (default for LTR languages) | text-align: left; |
center | Centers text | text-align: center; |
right | Aligns text to the right | text-align: right; |
justify | Spreads text across the full line width | text-align: justify; |
start | Aligns to the start of the text direction | text-align: start; |
end | Aligns to the end of the text direction | text-align: end; |
2. letter-spacing
The letter-spacing property sets the space between characters in a text.
/* Normal spacing (default) */
p.normal-spacing {
letter-spacing: normal;
}
/* Expanded spacing */
p.expanded-spacing {
letter-spacing: 2px;
}
/* Tight spacing */
p.tight-spacing {
letter-spacing: -1px;
}
/* Relative spacing */
p.relative-spacing {
letter-spacing: 0.1em;
}
Values
| Value | Description | Example |
|---|---|---|
normal | Default spacing (browser default) | letter-spacing: normal; |
length | Fixed or relative spacing | letter-spacing: 2px; |
negative | Tighter spacing | letter-spacing: -1px; |
3. word-spacing
The word-spacing property sets the space between words in a text.
/* Normal spacing (default) */
p.normal-spacing {
word-spacing: normal;
}
/* Expanded spacing */
p.expanded-spacing {
word-spacing: 4px;
}
/* Relative spacing */
p.relative-spacing {
word-spacing: 0.2em;
}
Values
| Value | Description | Example |
|---|---|---|
normal | Default spacing (browser default) | word-spacing: normal; |
length | Fixed or relative spacing | word-spacing: 4px; |
4. text-transform
The text-transform property changes the case of text.
/* All uppercase */
p.uppercase {
text-transform: uppercase;
}
/* All lowercase */
p.lowercase {
text-transform: lowercase;
}
/* Capitalize first letter of each word */
p.capitalize {
text-transform: capitalize;
}
/* Original case (default) */
p.none {
text-transform: none;
}
Values
| Value | Description | Example |
|---|---|---|
none | Original case (default) | text-transform: none; |
uppercase | All letters in uppercase | text-transform: uppercase; |
lowercase | All letters in lowercase | text-transform: lowercase; |
capitalize | First letter of each word capitalized | text-transform: capitalize; |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>text-align, letter-spacing, word-spacing, text-transform</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 2px solid #ddd;
background: #f8f9fa;
}
.demo-box.bordered {
border: 2px solid #ddd;
width: 70%;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== TEXT ALIGN DEMOS ====== */
.ta-left {
text-align: left;
}
.ta-center {
text-align: center;
}
.ta-right {
text-align: right;
}
.ta-justify {
text-align: justify;
}
/* ====== LETTER SPACING DEMOS ====== */
.ls-normal {
letter-spacing: normal;
}
.ls-expanded {
letter-spacing: 2px;
}
.ls-tight {
letter-spacing: -1px;
}
.ls-wide {
letter-spacing: 4px;
}
/* ====== WORD SPACING DEMOS ====== */
.ws-normal {
word-spacing: normal;
}
.ws-expanded {
word-spacing: 4px;
}
.ws-wide {
word-spacing: 8px;
}
/* ====== TEXT TRANSFORM DEMOS ====== */
.tt-uppercase {
text-transform: uppercase;
}
.tt-lowercase {
text-transform: lowercase;
}
.tt-capitalize {
text-transform: capitalize;
}
</style>
</head>
<body>
<h1>text-align, letter-spacing, word-spacing, text-transform</h1>
<!-- ====== 1. TEXT ALIGN ====== -->
<section>
<h2>1. text-align</h2>
<div class="demo-box bordered ta-left">
<strong>text-align: left</strong><br>
This text is left-aligned within the div.
</div>
<div class="demo-box bordered ta-center" style="width: 70%; margin: 10px auto;">
<strong>text-align: center</strong><br>
This text is centered within the div.
</div>
<div class="demo-box bordered ta-right">
<strong>text-align: right</strong><br>
This text is right-aligned within the div.
</div>
<div class="demo-box bordered ta-justify">
<strong>text-align: justify</strong><br>
This text is justified — it is spread across the full line width. The browser adds extra space between words so that both the left and right edges of the text align with the container's edges.
</div>
<div class="code-block">
text-align: left; /* Aligns text to the left (default) */
text-align: center; /* Centers text */
text-align: right; /* Aligns text to the right */
text-align: justify; /* Spreads text across the full line */
</div>
</section>
<!-- ====== 2. LETTER SPACING ====== -->
<section>
<h2>2. letter-spacing</h2>
<div class="grid-2">
<div class="demo-box ls-normal">
<strong>letter-spacing: normal</strong><br>
This text has normal letter spacing.
</div>
<div class="demo-box ls-expanded">
<strong>letter-spacing: 2px</strong><br>
This text has expanded letter spacing.
</div>
<div class="demo-box ls-wide">
<strong>letter-spacing: 4px</strong><br>
This text has wide letter spacing.
</div>
<div class="demo-box ls-tight">
<strong>letter-spacing: -1px</strong><br>
This text has tight letter spacing.
</div>
</div>
<div class="code-block">
letter-spacing: normal; /* Default */
letter-spacing: 2px; /* Expanded spacing */
letter-spacing: 4px; /* Wide spacing */
letter-spacing: -1px; /* Tight spacing */
</div>
</section>
<!-- ====== 3. WORD SPACING ====== -->
<section>
<h2>3. word-spacing</h2>
<div class="grid-2">
<div class="demo-box ws-normal">
<strong>word-spacing: normal</strong><br>
This text has normal word spacing between words.
</div>
<div class="demo-box ws-expanded">
<strong>word-spacing: 4px</strong><br>
This text has expanded word spacing between words.
</div>
<div class="demo-box ws-wide">
<strong>word-spacing: 8px</strong><br>
This text has wide word spacing between words.
</div>
</div>
<div class="code-block">
word-spacing: normal; /* Default */
word-spacing: 4px; /* Expanded word spacing */
word-spacing: 8px; /* Wide word spacing */
</div>
</section>
<!-- ====== 4. TEXT TRANSFORM ====== -->
<section>
<h2>4. text-transform</h2>
<div class="grid-2">
<div class="demo-box tt-uppercase">
<strong>text-transform: uppercase</strong><br>
This text is in uppercase.
</div>
<div class="demo-box tt-lowercase">
<strong>text-transform: lowercase</strong><br>
THIS TEXT IS IN LOWERCASE.
</div>
<div class="demo-box tt-capitalize">
<strong>text-transform: capitalize</strong><br>
this text is capitalized. (first letter of each word)
</div>
<div class="demo-box" style="text-transform: none;">
<strong>text-transform: none</strong><br>
This text retains its original case.
</div>
</div>
<div class="code-block">
text-transform: uppercase; /* ALL CAPS */
text-transform: lowercase; /* all lowercase */
text-transform: capitalize; /* First Letter Of Each Word Capitalized */
text-transform: none; /* Original case (default) */
</div>
</section>
<!-- ====== 5. COMBINED EXAMPLES ====== -->
<section>
<h2>5. Combined Examples</h2>
<div class="demo-box" style="text-align: center; letter-spacing: 2px; text-transform: uppercase; color: #007bff;">
<strong>Combined: center + letter-spacing: 2px + uppercase</strong><br>
This text is centered, has expanded letter spacing, and is in uppercase.
</div>
<div class="demo-box" style="text-align: justify; word-spacing: 6px; letter-spacing: 0.5px; text-transform: capitalize;">
<strong>Combined: justify + word-spacing: 6px + capitalize</strong><br>
this text is justified, has wide word spacing, and each word is capitalized.
</div>
<div class="demo-box" style="text-align: right; letter-spacing: -0.5px; word-spacing: 2px; text-transform: lowercase; color: #28a745;">
<strong>Combined: right + tight letter-spacing + lowercase</strong><br>
THIS TEXT IS RIGHT-ALIGNED, HAS TIGHT LETTER SPACING, AND IS IN LOWERCASE.
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>text-align Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>left</code></td>
<td>Aligns text to the left (default for LTR languages)</td>
<td><code>text-align: left;</code></td>
</tr>
<tr>
<td><code>center</code></td>
<td>Centers text</td>
<td><code>text-align: center;</code></td>
</tr>
<tr>
<td><code>right</code></td>
<td>Aligns text to the right</td>
<td><code>text-align: right;</code></td>
</tr>
<tr>
<td><code>justify</code></td>
<td>Spreads text across the full line</td>
<td><code>text-align: justify;</code></td>
</tr>
<tr>
<td><code>start</code></td>
<td>Aligns to the start of the text direction</td>
<td><code>text-align: start;</code></td>
</tr>
<tr>
<td><code>end</code></td>
<td>Aligns to the end of the text direction</td>
<td><code>text-align: end;</code></td>
</tr>
</table>
<h3>letter-spacing Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>Default spacing</td>
<td><code>letter-spacing: normal;</code></td>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed spacing (px, em, etc.)</td>
<td><code>letter-spacing: 2px;</code></td>
</tr>
<tr>
<td><code>negative</code></td>
<td>Tighter spacing</td>
<td><code>letter-spacing: -1px;</code></td>
</tr>
</table>
<h3>word-spacing Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>Default spacing</td>
<td><code>word-spacing: normal;</code></td>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed spacing (px, em, etc.)</td>
<td><code>word-spacing: 4px;</code></td>
</tr>
</table>
<h3>text-transform Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Original case (default)</td>
<td><code>text-transform: none;</code></td>
</tr>
<tr>
<td><code>uppercase</code></td>
<td>All letters in uppercase</td>
<td><code>text-transform: uppercase;</code></td>
</tr>
<tr>
<td><code>lowercase</code></td>
<td>All letters in lowercase</td>
<td><code>text-transform: lowercase;</code></td>
</tr>
<tr>
<td><code>capitalize</code></td>
<td>First letter of each word capitalized</td>
<td><code>text-transform: capitalize;</code></td>
</tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
text-align | Aligns text horizontally | left, center, right, justify |
letter-spacing | Space between characters | normal, 2px, -1px |
word-spacing | Space between words | normal, 4px |
text-transform | Transforms text case | none, uppercase, lowercase, capitalize |
Best Practices
✅ Do This:
/* Use justify for large text blocks */
.article {
text-align: justify;
}
/* Use center for headings and short text */
h1 { text-align: center; }
/* Use letter-spacing for headings */
h1 { letter-spacing: 2px; }
/* Use uppercase for emphasis */
.important { text-transform: uppercase; }
❌ Don’t Do This:
/* Don't center large blocks of text */
.long-text { text-align: center; } /* Hard to read */
/* Don't use excessive letter-spacing */
p { letter-spacing: 5px; } /* Makes text hard to read */
/* Don't use uppercase for long text */
.long-text { text-transform: uppercase; } /* Hard to read */
Pro Tip: Use text-align: justify for long articles and columns to create clean edges, but be mindful that it can create awkward gaps. For headings, use letter-spacing to give them a more refined look. Use text-transform sparingly — uppercase is great for buttons and headings, but hard to read in long paragraphs!
9. text-indent, vertical-align, text-align-last, direction
These CSS properties control the indentation, vertical alignment, last-line alignment, and text direction of elements, offering fine-grained control over text layout and readability.
Overview of Properties
| Property | Description | Common Values |
|---|---|---|
text-indent | Indents the first line of a block element | 40px, 2em, 10% |
vertical-align | Aligns inline/inline-block elements vertically | baseline, middle, top, bottom |
text-align-last | Aligns the last line of text in a block | left, center, right, justify |
direction | Sets text direction (LTR or RTL) | ltr, rtl |
1. text-indent
The text-indent property controls the indentation of the first line of a block-level element.
p.indented {
text-indent: 40px; /* Indent first line by 40px */
}
p.no-indent {
text-indent: 0; /* No indentation */
}
p.relative-indent {
text-indent: 2em; /* Indent by 2 × font size */
}
p.percentage-indent {
text-indent: 10%; /* Indent by 10% of parent width */
}
Values
| Value | Description | Example |
|---|---|---|
length | Fixed indentation | text-indent: 40px; |
percentage | Percentage of parent width | text-indent: 10%; |
0 / none | No indentation | text-indent: 0; |
2. vertical-align
The vertical-align property aligns inline, inline-block, and table-cell elements vertically within their parent.
.container {
vertical-align: middle; /* Centers content vertically */
}
.inline {
display: inline-block;
vertical-align: middle; /* Aligns inline-block elements */
}
/* Different alignment values */
.top-align {
vertical-align: top;
}
.bottom-align {
vertical-align: bottom;
}
.baseline-align {
vertical-align: baseline; /* Default */
}
Values
| Value | Description | Example |
|---|---|---|
baseline | Aligns to the baseline (default) | vertical-align: baseline; |
top | Aligns to the top of the parent | vertical-align: top; |
bottom | Aligns to the bottom of the parent | vertical-align: bottom; |
middle | Centers vertically | vertical-align: middle; |
text-top | Aligns to the top of the text | vertical-align: text-top; |
text-bottom | Aligns to the bottom of the text | vertical-align: text-bottom; |
sub | Aligns as subscript | vertical-align: sub; |
super | Aligns as superscript | vertical-align: super; |
3. text-align-last
The text-align-last property controls the alignment of the last line of text in a block-level element.
p.left-aligned-last {
text-align: justify;
text-align-last: left; /* Last line aligned left */
}
p.right-aligned-last {
text-align: justify;
text-align-last: right; /* Last line aligned right */
}
p.center-aligned-last {
text-align: justify;
text-align-last: center; /* Last line centered */
}
Values
| Value | Description | Example |
|---|---|---|
left | Aligns the last line to the left | text-align-last: left; |
center | Centers the last line | text-align-last: center; |
right | Aligns the last line to the right | text-align-last: right; |
justify | Justifies the last line | text-align-last: justify; |
start | Aligns to the start of the text direction | text-align-last: start; |
end | Aligns to the end of the text direction | text-align-last: end; |
4. direction
The direction property sets the text direction within a block-level element.
div.ltr {
direction: ltr; /* Left-to-right (default for English) */
}
div.rtl {
direction: rtl; /* Right-to-left (for Arabic, Hebrew) */
}
Values
| Value | Description | Example |
|---|---|---|
ltr | Left-to-right (default) | direction: ltr; |
rtl | Right-to-left | direction: rtl; |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>text-indent, vertical-align, text-align-last, direction</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
}
.demo-box.bordered {
border: 2px solid #ddd;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== TEXT INDENT DEMOS ====== */
.ti-40px {
text-indent: 40px;
}
.ti-2em {
text-indent: 2em;
}
.ti-10p {
text-indent: 10%;
}
.ti-none {
text-indent: 0;
}
/* ====== VERTICAL ALIGN DEMOS ====== */
.va-container {
border: 2px solid #ddd;
padding: 15px;
height: 80px;
background: #f8f9fa;
border-radius: 8px;
margin: 10px 0;
}
.va-container .va-child {
display: inline-block;
background: #007bff;
color: white;
padding: 5px 10px;
border-radius: 4px;
margin: 0 5px;
}
.va-top .va-child {
vertical-align: top;
}
.va-middle .va-child {
vertical-align: middle;
}
.va-bottom .va-child {
vertical-align: bottom;
}
.va-baseline .va-child {
vertical-align: baseline;
}
.va-text-top .va-child {
vertical-align: text-top;
}
.va-text-bottom .va-child {
vertical-align: text-bottom;
}
/* ====== TEXT ALIGN LAST DEMOS ====== */
.tal-left {
text-align: justify;
text-align-last: left;
}
.tal-center {
text-align: justify;
text-align-last: center;
}
.tal-right {
text-align: justify;
text-align-last: right;
}
.tal-justify {
text-align: justify;
text-align-last: justify;
}
/* ====== DIRECTION DEMOS ====== */
.dir-ltr {
direction: ltr;
}
.dir-rtl {
direction: rtl;
}
.dir-rtl p {
text-align: right;
}
</style>
</head>
<body>
<h1>text-indent, vertical-align, text-align-last, direction</h1>
<!-- ====== 1. TEXT INDENT ====== -->
<section>
<h2>1. text-indent</h2>
<div class="demo-box bordered ti-40px">
<strong>text-indent: 40px</strong><br>
This paragraph has a left indentation of 40 pixels. The first line is indented to create a traditional paragraph style commonly used in books and articles.
</div>
<div class="demo-box bordered ti-2em">
<strong>text-indent: 2em</strong><br>
This paragraph has an indentation of 2em (2 × the current font size), making it relative to the text size.
</div>
<div class="demo-box bordered ti-10p">
<strong>text-indent: 10%</strong><br>
This paragraph has an indentation of 10% of the parent container's width, making it responsive to the container size.
</div>
<div class="demo-box bordered ti-none">
<strong>text-indent: 0 (no indent)</strong><br>
This paragraph does not have any indentation. The first line starts exactly at the left edge of the container.
</div>
<div class="code-block">
text-indent: 40px; /* Fixed indentation */
text-indent: 2em; /* Relative to font size */
text-indent: 10%; /* Percentage of parent width */
text-indent: 0; /* No indentation */
</div>
</section>
<!-- ====== 2. VERTICAL ALIGN ====== -->
<section>
<h2>2. vertical-align</h2>
<h3>top vs middle vs bottom</h3>
<div class="va-container va-top">
<span class="va-child" style="height: 30px;">top</span>
<span class="va-child" style="height: 30px;">top</span>
<span style="display: inline-block; font-size: 14px;">All aligned <strong>top</strong></span>
</div>
<div class="va-container va-middle">
<span class="va-child" style="height: 30px;">middle</span>
<span class="va-child" style="height: 30px;">middle</span>
<span style="display: inline-block; font-size: 14px;">All aligned <strong>middle</strong></span>
</div>
<div class="va-container va-bottom">
<span class="va-child" style="height: 30px;">bottom</span>
<span class="va-child" style="height: 30px;">bottom</span>
<span style="display: inline-block; font-size: 14px;">All aligned <strong>bottom</strong></span>
</div>
<h3>baseline vs text-top vs text-bottom</h3>
<div class="va-container va-baseline">
<span class="va-child" style="height: 30px;">baseline</span>
<span style="display: inline-block; font-size: 24px;">Big Text</span>
<span class="va-child" style="height: 30px;">baseline</span>
<span style="display: inline-block; font-size: 14px;">Aligned to <strong>baseline</strong></span>
</div>
<div class="va-container va-text-top">
<span class="va-child" style="height: 30px;">text-top</span>
<span style="display: inline-block; font-size: 24px;">Big Text</span>
<span class="va-child" style="height: 30px;">text-top</span>
<span style="display: inline-block; font-size: 14px;">Aligned to <strong>text-top</strong></span>
</div>
<div class="va-container va-text-bottom">
<span class="va-child" style="height: 30px;">text-bottom</span>
<span style="display: inline-block; font-size: 24px;">Big Text</span>
<span class="va-child" style="height: 30px;">text-bottom</span>
<span style="display: inline-block; font-size: 14px;">Aligned to <strong>text-bottom</strong></span>
</div>
<div class="code-block">
vertical-align: top; /* Aligns to the top */
vertical-align: middle; /* Centers vertically */
vertical-align: bottom; /* Aligns to the bottom */
vertical-align: baseline; /* Aligns to the baseline (default) */
vertical-align: text-top; /* Aligns to the top of the text */
vertical-align: text-bottom; /* Aligns to the bottom of the text */
</div>
</section>
<!-- ====== 3. TEXT ALIGN LAST ====== -->
<section>
<h2>3. text-align-last</h2>
<div class="grid-2">
<div class="demo-box bordered tal-left">
<strong>text-align-last: left</strong><br>
This paragraph is justified, but the last line is aligned to the left. It's quite long and has a bunch of words to demonstrate how this property works. The last line will be left-aligned while the rest are justified.
</div>
<div class="demo-box bordered tal-center">
<strong>text-align-last: center</strong><br>
This paragraph is justified, but the last line is centered. It's quite long and has a bunch of words to demonstrate how this property works. The last line will be centered while the rest are justified.
</div>
<div class="demo-box bordered tal-right">
<strong>text-align-last: right</strong><br>
This paragraph is justified, but the last line is aligned to the right. It's quite long and has a bunch of words to demonstrate how this property works. The last line will be right-aligned while the rest are justified.
</div>
<div class="demo-box bordered tal-justify">
<strong>text-align-last: justify</strong><br>
This paragraph is justified, and the last line is also justified. It's quite long and has a bunch of words to demonstrate how this property works. The last line is spread across the full width.
</div>
</div>
<div class="code-block">
text-align-last: left; /* Last line aligned left */
text-align-last: center; /* Last line centered */
text-align-last: right; /* Last line aligned right */
text-align-last: justify; /* Last line justified */
</div>
</section>
<!-- ====== 4. DIRECTION ====== -->
<section>
<h2>4. direction</h2>
<div class="grid-2">
<div class="demo-box bordered dir-ltr">
<strong>direction: ltr (Left-to-Right)</strong><br>
This is a left-to-right language (e.g., English) block. The text reads from left to right, which is the default direction for most languages.
</div>
<div class="demo-box bordered dir-rtl">
<strong>direction: rtl (Right-to-Left)</strong><br>
هذا هو نص من اليمين إلى اليسار (مثل العربية أو العبرية). يتم محاذاة النص إلى اليمين ويقرأ من اليمين إلى اليسار.
<br><br>
<span style="font-size: 0.9rem; color: #6c757d;">
(This is right-to-left text, like Arabic or Hebrew. It is aligned to the right and reads from right to left.)
</span>
</div>
</div>
<div class="code-block">
direction: ltr; /* Left-to-right (default for English) */
direction: rtl; /* Right-to-left (for Arabic, Hebrew) */
</div>
</section>
<!-- ====== 5. COMBINED EXAMPLES ====== -->
<section>
<h2>5. Combined Examples</h2>
<div class="demo-box bordered" style="text-indent: 2em; text-align: justify; text-align-last: center; direction: ltr;">
<strong>Combined: text-indent: 2em + text-align: justify + text-align-last: center</strong><br>
This paragraph combines multiple text formatting properties. The first line is indented, the text is justified, and the last line is centered. This creates a unique and visually interesting layout.
</div>
<div class="demo-box bordered dir-rtl" style="text-indent: 2em; text-align: justify; text-align-last: center;">
<strong>Combined: RTL + text-indent + justify</strong><br>
هذا النص يجمع بين الاتجاه من اليمين إلى اليسار مع المسافة البادئة والتبرير والمحاذاة المركزية للسطر الأخير. هذا يوضح كيف تعمل هذه الخصائص معًا في اللغات ذات الاتجاه المختلف.
<br><br>
<span style="font-size: 0.9rem; color: #6c757d; direction: ltr;">
(This text combines RTL direction with indentation, justification, and centered last-line alignment.)
</span>
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>text-indent Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed indentation (px, em, etc.)</td>
<td><code>text-indent: 40px;</code></td>
</tr>
<tr>
<td><code>percentage</code></td>
<td>Percentage of parent width</td>
<td><code>text-indent: 10%;</code></td>
</tr>
<tr>
<td><code>0</code></td>
<td>No indentation</td>
<td><code>text-indent: 0;</code></td>
</tr>
</table>
<h3>vertical-align Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>baseline</code></td>
<td>Aligns to the baseline (default)</td>
<td><code>vertical-align: baseline;</code></td>
</tr>
<tr>
<td><code>top</code></td>
<td>Aligns to the top</td>
<td><code>vertical-align: top;</code></td>
</tr>
<tr>
<td><code>middle</code></td>
<td>Centers vertically</td>
<td><code>vertical-align: middle;</code></td>
</tr>
<tr>
<td><code>bottom</code></td>
<td>Aligns to the bottom</td>
<td><code>vertical-align: bottom;</code></td>
</tr>
<tr>
<td><code>text-top</code></td>
<td>Aligns to the top of the text</td>
<td><code>vertical-align: text-top;</code></td>
</tr>
<tr>
<td><code>text-bottom</code></td>
<td>Aligns to the bottom of the text</td>
<td><code>vertical-align: text-bottom;</code></td>
</tr>
<tr>
<td><code>sub</code></td>
<td>Aligns as subscript</td>
<td><code>vertical-align: sub;</code></td>
</tr>
<tr>
<td><code>super</code></td>
<td>Aligns as superscript</td>
<td><code>vertical-align: super;</code></td>
</tr>
</table>
<h3>text-align-last Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>left</code></td>
<td>Last line aligned left</td>
<td><code>text-align-last: left;</code></td>
</tr>
<tr>
<td><code>center</code></td>
<td>Last line centered</td>
<td><code>text-align-last: center;</code></td>
</tr>
<tr>
<td><code>right</code></td>
<td>Last line aligned right</td>
<td><code>text-align-last: right;</code></td>
</tr>
<tr>
<td><code>justify</code></td>
<td>Last line justified</td>
<td><code>text-align-last: justify;</code></td>
</tr>
</table>
<h3>direction Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>ltr</code></td>
<td>Left-to-right (default)</td>
<td><code>direction: ltr;</code></td>
</tr>
<tr>
<td><code>rtl</code></td>
<td>Right-to-left</td>
<td><code>direction: rtl;</code></td>
</tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
text-indent | Indents the first line of a block | 40px, 2em, 10% |
vertical-align | Aligns inline/inline-block elements vertically | baseline, middle, top, bottom |
text-align-last | Aligns the last line of a block | left, center, right, justify |
direction | Sets text direction | ltr, rtl |
Best Practices
✅ Do This:
/* Use text-indent for paragraphs in articles */
.article p {
text-indent: 2em;
}
/* Use vertical-align: middle for icons next to text */
.icon {
vertical-align: middle;
}
/* Use text-align-last for justified text blocks */
.justified-text {
text-align: justify;
text-align-last: left;
}
❌ Don’t Do This:
/* Don't use text-indent for all paragraphs */
p { text-indent: 2em; } /* May look odd for short paragraphs */
/* Don't use vertical-align for block elements */
div { vertical-align: middle; } /* Only works for inline/inline-block */
/* Don't use direction: rtl without proper text */
.english { direction: rtl; } /* Only use for RTL languages */
Pro Tip: Use text-indent to create traditional paragraph styles in long-form content. Use vertical-align: middle to align icons or small elements with text. Use text-align-last when working with justified text to control the appearance of the last line. Use direction: rtl only for languages that are naturally right-to-left, such as Arabic or Hebrew!
10. white-space, text-emphasis, and text-shadow
These CSS properties control the handling of whitespace, add emphasis marks to text, and create shadow effects for enhanced typography.
Overview of Properties
| Property | Description | Common Values |
|---|---|---|
white-space | Controls whitespace handling and wrapping | normal, nowrap, pre, pre-wrap, pre-line |
text-emphasis | Adds emphasis marks (shorthand for style + color) | fill, before "•", filled double-circle #ffb703 |
text-shadow | Adds shadow effects to text | 2px 2px 4px rgba(0,0,0,0.5) |
1. white-space
The white-space property controls how whitespace characters (spaces, tabs, newlines) are handled within an element.
p.normal-white-space {
white-space: normal; /* Default — wraps text, collapses spaces */
}
p.nowrap-white-space {
white-space: nowrap; /* Prevents wrapping, collapses spaces */
}
p.pre-white-space {
white-space: pre; /* Preserves spaces and line breaks, no wrapping */
}
p.preline-white-space {
white-space: pre-line; /* Collapses spaces, preserves line breaks, wraps */
}
p.pre-wrap-white-space {
white-space: pre-wrap; /* Preserves spaces and line breaks, wraps */
}
Values
| Value | Spaces | Line Breaks | Wrapping | Example |
|---|---|---|---|---|
normal | Collapses | Collapses | Wraps | Default behavior |
nowrap | Collapses | Collapses | No wrap | Single line text |
pre | Preserves | Preserves | No wrap | Like <pre> tag |
pre-wrap | Preserves | Preserves | Wraps | Code with line wrapping |
pre-line | Collapses | Preserves | Wraps | Poetry or formatted text |
2. text-emphasis
The text-emphasis property adds emphasis marks (dots, circles, symbols) to text. It’s a shorthand for text-emphasis-style and text-emphasis-color.
p.fill-emphasis {
text-emphasis: fill; /* Default — filled dot emphasis */
}
p.before-emphasis {
text-emphasis: before "•"; /* Mark placed before each word */
}
p.after-emphasis {
text-emphasis: after "•"; /* Mark placed after each word */
}
p.emphasis-combined {
text-emphasis: filled double-circle #ffb703; /* Style + color */
}
Syntax
text-emphasis: <style> [ <color> ] | before | after <string>
Values
| Value | Description | Example |
|---|---|---|
filled | Filled dot (default) | text-emphasis: filled; |
open | Open dot | text-emphasis: open; |
filled sesame | Filled sesame shape | text-emphasis: filled sesame; |
open sesame | Open sesame shape | text-emphasis: open sesame; |
before | Places mark before the text | text-emphasis: before "•"; |
after | Places mark after the text | text-emphasis: after "•"; |
string | Custom symbol | text-emphasis: "★" |
color | Sets the color | text-emphasis: filled #ff6b6b; |
3. text-shadow
The text-shadow property adds shadow effects to text. It accepts a comma-separated list of shadows.
p.default-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
p.custom-shadow {
text-shadow: -2px -2px 4px rgba(0, 0, 0, 0.3),
2px 2px 6px rgba(255, 255, 255, 0.7);
}
p.simple-shadow {
text-shadow: 3px 6px; /* Uses default color and blur radius */
}
p.colored-shadow {
text-shadow: blue 3px 6px; /* Sets color and offsets */
}
Syntax
text-shadow: <offset-x> <offset-y> <blur-radius> <color> | none
Values
| Value | Description | Example |
|---|---|---|
offset-x | Horizontal shadow offset | 2px, -2px |
offset-y | Vertical shadow offset | 2px, -2px |
blur-radius | Blur radius (optional) | 4px |
color | Shadow color (optional) | rgba(0,0,0,0.5) |
none | No shadow | text-shadow: none; |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>white-space, text-emphasis, text-shadow</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 2px solid #ddd;
background: #f8f9fa;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== WHITE SPACE DEMOS ====== */
.ws-normal {
white-space: normal;
}
.ws-nowrap {
white-space: nowrap;
}
.ws-pre {
white-space: pre;
}
.ws-pre-line {
white-space: pre-line;
}
.ws-pre-wrap {
white-space: pre-wrap;
}
/* ====== TEXT EMPHASIS DEMOS ====== */
.te-fill {
text-emphasis: fill;
}
.te-open {
text-emphasis: open;
}
.te-sesame {
text-emphasis: filled sesame;
}
.te-before {
text-emphasis: before "•";
}
.te-after {
text-emphasis: after "•";
}
.te-star {
text-emphasis: "★";
}
.te-combined {
text-emphasis: filled double-circle #ffb703;
}
/* ====== TEXT SHADOW DEMOS ====== */
.ts-default {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.ts-custom {
text-shadow: -2px -2px 4px rgba(0, 0, 0, 0.3),
2px 2px 6px rgba(255, 255, 255, 0.7);
}
.ts-heavy {
text-shadow: 0 0 10px rgba(0, 0, 0, 0.3),
0 0 20px rgba(0, 0, 0, 0.2);
}
.ts-glow {
text-shadow: 0 0 10px #007bff, 0 0 20px #007bff;
}
.ts-multiple {
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3),
2px 2px 4px rgba(0, 0, 0, 0.2),
3px 3px 6px rgba(0, 0, 0, 0.1);
}
.ts-neon {
text-shadow: 0 0 5px #ff6b6b,
0 0 15px #ff6b6b,
0 0 30px #ff6b6b;
}
</style>
</head>
<body>
<h1>white-space, text-emphasis, text-shadow</h1>
<!-- ====== 1. WHITE SPACE ====== -->
<section>
<h2>1. white-space</h2>
<div class="demo-box ws-normal">
<strong>white-space: normal (default)</strong><br>
This is some normal text with a bunch of spaces and words. Spaces are collapsed, and text wraps when needed.
</div>
<div class="demo-box ws-nowrap">
<strong>white-space: nowrap</strong><br>
This is some nowrap text, but the spaces are not preserved or wrapped. The text stays on a single line, and you can scroll to see the full content.
</div>
<div class="demo-box ws-pre">
<strong>white-space: pre</strong><br>
This is pre text, which preserves both spaces and line breaks,
but it doesn't wrap the text. Everything stays as written.
</div>
<div class="demo-box ws-pre-line">
<strong>white-space: pre-line</strong><br>
This is pre-line text, which preserves line breaks and wraps lines at word boundaries.
Multiple spaces are collapsed into a single space. This is useful for poetry or formatted text.
</div>
<div class="demo-box ws-pre-wrap">
<strong>white-space: pre-wrap</strong><br>
This is pre-wrap text, which preserves both spaces and line breaks, and also wraps the text when needed.
It combines the best of both pre and normal.
</div>
<div class="code-block">
white-space: normal; /* Default — wraps text, collapses spaces */
white-space: nowrap; /* Prevents wrapping, collapses spaces */
white-space: pre; /* Preserves spaces and line breaks, no wrapping */
white-space: pre-line; /* Collapses spaces, preserves line breaks, wraps */
white-space: pre-wrap; /* Preserves spaces and line breaks, wraps */
</div>
</section>
<!-- ====== 2. TEXT EMPHASIS ====== -->
<section>
<h2>2. text-emphasis</h2>
<div class="grid-2">
<div class="demo-box te-fill">
<strong>text-emphasis: fill</strong><br>
This is some fill emphasis text with the default filled dot emphasis mark.
</div>
<div class="demo-box te-open">
<strong>text-emphasis: open</strong><br>
This is some open emphasis text with an open dot emphasis mark.
</div>
<div class="demo-box te-sesame">
<strong>text-emphasis: filled sesame</strong><br>
This is some sesame emphasis text with filled sesame marks.
</div>
<div class="demo-box te-before">
<strong>text-emphasis: before "•"</strong><br>
This is some before emphasis text with a bullet mark placed before each word.
</div>
<div class="demo-box te-after">
<strong>text-emphasis: after "•"</strong><br>
This is some after emphasis text with a bullet mark placed after each word.
</div>
<div class="demo-box te-star">
<strong>text-emphasis: "★"</strong><br>
This is some star emphasis text with a custom star symbol.
</div>
<div class="demo-box te-combined">
<strong>text-emphasis: filled double-circle #ffb703</strong><br>
This is some combined emphasis text with a filled double-circle style and custom color.
</div>
</div>
<div class="code-block">
text-emphasis: fill; /* Filled dot (default) */
text-emphasis: open; /* Open dot */
text-emphasis: filled sesame; /* Filled sesame marks */
text-emphasis: before "•"; /* Mark before each word */
text-emphasis: after "•"; /* Mark after each word */
text-emphasis: "★"; /* Custom symbol */
text-emphasis: filled double-circle #ffb703; /* Style + color */
</div>
</section>
<!-- ====== 3. TEXT SHADOW ====== -->
<section>
<h2>3. text-shadow</h2>
<div class="grid-2">
<div class="demo-box ts-default" style="font-size: 1.2rem; font-weight: bold;">
<strong>text-shadow: 2px 2px 4px rgba(0,0,0,0.5)</strong><br>
Default shadow effect
</div>
<div class="demo-box ts-custom" style="font-size: 1.2rem; font-weight: bold;">
<strong>text-shadow: -2px -2px 4px rgba(0,0,0,0.3), 2px 2px 6px rgba(255,255,255,0.7)</strong><br>
Custom shadow with depth
</div>
<div class="demo-box ts-heavy" style="font-size: 1.2rem; font-weight: bold;">
<strong>text-shadow: 0 0 10px rgba(0,0,0,0.3), 0 0 20px rgba(0,0,0,0.2)</strong><br>
Heavy soft shadow
</div>
<div class="demo-box ts-glow" style="font-size: 1.2rem; font-weight: bold; color: #007bff;">
<strong>text-shadow: 0 0 10px #007bff, 0 0 20px #007bff</strong><br>
Glowing effect
</div>
<div class="demo-box ts-multiple" style="font-size: 1.2rem; font-weight: bold;">
<strong>Multiple Shadows</strong><br>
Three shadows for layered depth
</div>
<div class="demo-box ts-neon" style="font-size: 1.2rem; font-weight: bold; color: #ff6b6b;">
<strong>text-shadow: 0 0 5px #ff6b6b, 0 0 15px #ff6b6b, 0 0 30px #ff6b6b</strong><br>
Neon glow effect
</div>
</div>
<div class="code-block">
/* Basic shadow */
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
/* Multiple shadows */
text-shadow: -2px -2px 4px rgba(0, 0, 0, 0.3),
2px 2px 6px rgba(255, 255, 255, 0.7);
/* Glow effect */
text-shadow: 0 0 10px #007bff, 0 0 20px #007bff;
/* Neon effect */
text-shadow: 0 0 5px #ff6b6b, 0 0 15px #ff6b6b, 0 0 30px #ff6b6b;
</div>
</section>
<!-- ====== 4. COMBINED EXAMPLES ====== -->
<section>
<h2>4. Combined Examples</h2>
<div class="demo-box" style="white-space: pre-wrap; text-emphasis: before "★"; text-shadow: 2px 2px 4px rgba(0,0,0,0.2);">
<strong>Combined: pre-wrap + text-emphasis + text-shadow</strong><br>
This text combines multiple properties: white-space: pre-wrap,
text-emphasis: before "★", and a subtle text shadow.
All working together for a unique style.
</div>
<div class="demo-box" style="white-space: nowrap; text-emphasis: after "•"; text-shadow: 0 0 10px rgba(0,123,255,0.3); font-size: 1.1rem;">
<strong>Combined: nowrap + after emphasis + glow</strong><br>
This text is nowrap with a subtle glow and emphasis marks after each word.
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>white-space Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Spaces</th>
<th>Line Breaks</th>
<th>Wrapping</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>Collapses</td>
<td>Collapses</td>
<td>Wraps</td>
</tr>
<tr>
<td><code>nowrap</code></td>
<td>Collapses</td>
<td>Collapses</td>
<td>No wrap</td>
</tr>
<tr>
<td><code>pre</code></td>
<td>Preserves</td>
<td>Preserves</td>
<td>No wrap</td>
</tr>
<tr>
<td><code>pre-wrap</code></td>
<td>Preserves</td>
<td>Preserves</td>
<td>Wraps</td>
</tr>
<tr>
<td><code>pre-line</code></td>
<td>Collapses</td>
<td>Preserves</td>
<td>Wraps</td>
</tr>
</table>
<h3>text-emphasis Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>fill</code></td>
<td>Filled dot (default)</td>
<td><code>text-emphasis: fill;</code></td>
</tr>
<tr>
<td><code>open</code></td>
<td>Open dot</td>
<td><code>text-emphasis: open;</code></td>
</tr>
<tr>
<td><code>filled sesame</code></td>
<td>Filled sesame shape</td>
<td><code>text-emphasis: filled sesame;</code></td>
</tr>
<tr>
<td><code>before "•"</code></td>
<td>Mark before each word</td>
<td><code>text-emphasis: before "•";</code></td>
</tr>
<tr>
<td><code>after "•"</code></td>
<td>Mark after each word</td>
<td><code>text-emphasis: after "•";</code></td>
</tr>
<tr>
<td><code>"string"</code></td>
<td>Custom symbol</td>
<td><code>text-emphasis: "★";</code></td>
</tr>
</table>
<h3>text-shadow Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>offset-x offset-y blur-radius color</code></td>
<td>Basic shadow</td>
<td><code>text-shadow: 2px 2px 4px rgba(0,0,0,0.5);</code></td>
</tr>
<tr>
<td><code>offset-x offset-y</code></td>
<td>No blur, default color</td>
<td><code>text-shadow: 3px 6px;</code></td>
</tr>
<tr>
<td><code>color offset-x offset-y</code></td>
<td>Color with offsets</td>
<td><code>text-shadow: blue 3px 6px;</code></td>
</tr>
<tr>
<td><code>none</code></td>
<td>No shadow</td>
<td><code>text-shadow: none;</code></td>
</tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
white-space | Controls whitespace handling | normal, nowrap, pre, pre-wrap, pre-line |
text-emphasis | Adds emphasis marks | fill, before "•", filled double-circle #ffb703 |
text-shadow | Adds shadow effects | 2px 2px 4px rgba(0,0,0,0.5) |
Best Practices
✅ Do This:
/* Use white-space: pre for code blocks */
.code-block {
white-space: pre-wrap;
}
/* Use text-shadow for headings and emphasis */
h1 {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
}
/* Use text-emphasis for annotations */
.highlight {
text-emphasis: before "★";
}
/* Use multiple text-shadows for depth */
.text-depth {
text-shadow: 1px 1px 2px rgba(0,0,0,0.3),
2px 2px 4px rgba(0,0,0,0.2);
}
❌ Don’t Do This:
/* Don't use nowrap for long paragraphs */
p { white-space: nowrap; } /* May overflow the container */
/* Don't use text-shadow on large blocks of text */
p { text-shadow: 2px 2px 4px rgba(0,0,0,0.5); } /* Can strain readability */
/* Don't overuse text-emphasis on long text */
p { text-emphasis: before "•"; } /* Can be visually overwhelming */
Pro Tip: Use white-space: pre-wrap for code blocks to preserve formatting while allowing wrapping. Use text-shadow sparingly for headings, logos, or emphasis — too much shadow can hurt readability. text-emphasis is great for marking important words in educational or annotation contexts!
11. line-break and word-break Properties
These CSS properties control how text breaks across lines, making them essential for handling long words, URLs, or content in narrow containers.
Overview of Properties
| Property | Description | Common Values |
|---|---|---|
line-break | Controls line-breaking behavior | auto, normal, loose, strict, anywhere |
word-break | Controls word-breaking behavior | normal, break-all, keep-all |
1. line-break
The line-break property specifies how line breaks should be handled, particularly for CJK (Chinese, Japanese, Korean) text and punctuation.
p.line-break-auto {
line-break: auto; /* Uses the default break rule */
}
p.line-break-normal {
line-break: normal; /* Uses the most common break rule */
}
p.line-break-anywhere {
line-break: anywhere; /* Breaks at any point in the text block */
}
p.line-break-loose {
line-break: loose; /* Uses the least restrictive rule */
}
p.line-break-strict {
line-break: strict; /* Uses the most stringent rule */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Uses the default break rule (browser default) | line-break: auto; |
normal | Uses the most common break rule | line-break: normal; |
loose | Uses the least restrictive rule | line-break: loose; |
strict | Uses the most stringent rule | line-break: strict; |
anywhere | Allows line breaks at any point in the text block | line-break: anywhere; |
2. word-break
The word-break property controls how words break when they reach the end of a line.
p.word-break-normal {
word-break: normal; /* Default — breaks only at whitespace */
}
p.word-break-break-all {
word-break: break-all; /* Breaks at any point in the text block */
}
p.word-break-keep-all {
word-break: keep-all; /* Breaks only at whitespace, not in the middle */
}
Values
| Value | Description | Example |
|---|---|---|
normal | Default — words break only at whitespace characters | word-break: normal; |
break-all | Words can break at any point, even in the middle of a word | word-break: break-all; |
keep-all | Words break only at whitespace characters (not in the middle) | word-break: keep-all; |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>line-break and word-break Properties</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 2px solid #ddd;
background: #f8f9fa;
max-width: 400px;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 20px;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== LINE BREAK DEMOS ====== */
.lb-auto {
line-break: auto;
}
.lb-normal {
line-break: normal;
}
.lb-loose {
line-break: loose;
}
.lb-strict {
line-break: strict;
}
.lb-anywhere {
line-break: anywhere;
}
/* ====== WORD BREAK DEMOS ====== */
.wb-normal {
word-break: normal;
}
.wb-break-all {
word-break: break-all;
}
.wb-keep-all {
word-break: keep-all;
}
.note {
font-size: 0.85rem;
color: #6c757d;
margin-top: 5px;
}
</style>
</head>
<body>
<h1>line-break and word-break Properties</h1>
<!-- ====== 1. LINE BREAK ====== -->
<section>
<h2>1. line-break</h2>
<p style="margin-bottom: 5px;">These examples are displayed in a container with <strong>max-width: 400px</strong> to show how line breaks behave.</p>
<div class="grid-2">
<div class="demo-box lb-auto">
<strong>line-break: auto (default)</strong><br>
This is a very long sentence without any specified line breaks. By default, the browser will break the word at spaces or punctuation marks to fit within the container width.
</div>
<div class="demo-box lb-normal">
<strong>line-break: normal</strong><br>
This is a very long sentence with irregular words and symbols like "--" and "-". By setting the <code>line-break: normal;</code> property, we can allow line breaks between words or within a word. However, it may not work as expected in all browsers.
</div>
<div class="demo-box lb-loose">
<strong>line-break: loose</strong><br>
This is a very long sentence with irregular words and symbols like "--" and "-". Using the least restrictive rule allows more flexible line breaks, especially for CJK text.
</div>
<div class="demo-box lb-strict">
<strong>line-break: strict</strong><br>
This is a very long sentence with irregular words and symbols like "--" and "-". Using the most stringent rule restricts line breaks, maintaining stricter formatting for CJK text.
</div>
<div class="demo-box lb-anywhere">
<strong>line-break: anywhere</strong><br>
This is a very long sentence without any spaces or punctuation marks and no line breaks are allowed. Setting <code>line-break: anywhere;</code> will allow line breaks at any point in the text block, but it may not work as expected in some browsers.
</div>
</div>
<div class="code-block">
line-break: auto; /* Uses the default break rule */
line-break: normal; /* Uses the most common break rule */
line-break: loose; /* Uses the least restrictive rule */
line-break: strict; /* Uses the most stringent rule */
line-break: anywhere; /* Allows line breaks at any point */
</div>
</section>
<!-- ====== 2. WORD BREAK ====== -->
<section>
<h2>2. word-break</h2>
<p style="margin-bottom: 5px;">These examples are displayed in a container with <strong>max-width: 400px</strong> to show how word breaks behave.</p>
<div class="grid-2">
<div class="demo-box wb-normal">
<strong>word-break: normal (default)</strong><br>
This is a very long sentence without any specified word breaks. By default, the browser will break the word at whitespace characters to fit within the container width.
</div>
<div class="demo-box wb-break-all">
<strong>word-break: break-all</strong><br>
This is a very long sentence with irregular words and symbols like "--" and "-". By setting the <code>word-break: break-all;</code> property, we can force words to break at any point in the text block, even in the middle of a word.
</div>
<div class="demo-box wb-keep-all">
<strong>word-break: keep-all</strong><br>
This is a very long sentence without any spaces or punctuation marks and no word breaks are allowed. Setting <code>word-break: keep-all;</code> will prevent words from breaking in the middle of a word.
</div>
</div>
<div class="code-block">
word-break: normal; /* Default — breaks only at whitespace */
word-break: break-all; /* Words can break at any point */
word-break: keep-all; /* Words break only at whitespace */
</div>
</section>
<!-- ====== 3. COMPARISON: line-break vs word-break ====== -->
<section>
<h2>3. Comparison: line-break vs word-break</h2>
<div class="grid-2">
<div class="demo-box" style="max-width: 400px;">
<strong>line-break: anywhere</strong>
<p style="line-break: anywhere; border: 1px solid #ddd; padding: 10px; border-radius: 4px; margin-top: 5px;">
This very long string of text with no spaces will break at any point because of line-break: anywhere.
</p>
<p class="note">Breaks at any point in the text block</p>
</div>
<div class="demo-box" style="max-width: 400px;">
<strong>word-break: break-all</strong>
<p style="word-break: break-all; border: 1px solid #ddd; padding: 10px; border-radius: 4px; margin-top: 5px;">
This very long string of text with no spaces will break at any point because of word-break: break-all.
</p>
<p class="note">Breaks words at any point</p>
</div>
</div>
<div class="code-block">
/* line-break affects line break behavior for punctuation and CJK */
line-break: anywhere;
/* word-break affects how words break within a line */
word-break: break-all;
</div>
</section>
<!-- ====== 4. REFERENCE TABLES ====== -->
<section>
<h2>4. Reference Tables</h2>
<h3>line-break Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Uses the default break rule (browser default)</td>
<td><code>line-break: auto;</code></td>
</tr>
<tr>
<td><code>normal</code></td>
<td>Uses the most common break rule</td>
<td><code>line-break: normal;</code></td>
</tr>
<tr>
<td><code>loose</code></td>
<td>Uses the least restrictive rule</td>
<td><code>line-break: loose;</code></td>
</tr>
<tr>
<td><code>strict</code></td>
<td>Uses the most stringent rule</td>
<td><code>line-break: strict;</code></td>
</tr>
<tr>
<td><code>anywhere</code></td>
<td>Allows line breaks at any point in the text block</td>
<td><code>line-break: anywhere;</code></td>
</tr>
</table>
<h3>word-break Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>Default — words break only at whitespace characters</td>
<td><code>word-break: normal;</code></td>
</tr>
<tr>
<td><code>break-all</code></td>
<td>Words can break at any point, even in the middle of a word</td>
<td><code>word-break: break-all;</code></td>
</tr>
<tr>
<td><code>keep-all</code></td>
<td>Words break only at whitespace characters (not in the middle)</td>
<td><code>word-break: keep-all;</code></td>
</tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
line-break | Controls line-breaking behavior | auto, normal, loose, strict, anywhere |
word-break | Controls word-breaking behavior | normal, break-all, keep-all |
line-break vs word-break — When to Use Which
| Property | Best For | Example Use Case |
|---|---|---|
line-break | Controlling line breaks with punctuation and CJK text | International websites with Chinese/Japanese/Korean content |
word-break: break-all | Handling long words or URLs in narrow containers | Code blocks, URLs, user-generated content |
word-break: keep-all | Preventing mid-word breaks | Brand names, proper nouns, headlines |
Best Practices
✅ Do This:
/* Handle long URLs in narrow containers */
.url {
word-break: break-all;
}
/* Keep brand names intact */
.brand-name {
word-break: keep-all;
}
/* For CJK text, use line-break */
.chinese-text {
line-break: strict;
}
❌ Don’t Do This:
/* Don't use break-all on body text */
body {
word-break: break-all; /* Makes text hard to read */
}
/* Don't use word-break when overflow-wrap is more appropriate */
/* Use overflow-wrap: break-word instead for better readability */
Pro Tip: Use word-break: break-all for code blocks, URLs, or user-generated content where long words might overflow. Use word-break: keep-all to preserve brand names and proper nouns. For general text, word-break: normal (the default) is usually the best choice. If you need to break long words, overflow-wrap: break-word often provides better readability than word-break: break-all!
12. text-combine-upright, text-underline-offset, text-underline-position, text-overflow
These CSS properties control character combination, underline positioning, and overflow handling for text, offering fine-grained control over typography and layout.
Overview of Properties
| Property | Description | Common Values |
|---|---|---|
text-combine-upright | Combines characters into a single upright glyph | none, all |
text-underline-offset | Sets the distance of the underline | auto, 2px, 0.2em, 10% |
text-underline-position | Sets the position of the underline | auto, under, left, right |
text-overflow | Controls how hidden overflow is displayed | clip, ellipsis |
1. text-combine-upright
The text-combine-upright property controls how characters are combined into a single upright glyph — primarily used in vertical writing modes (e.g., for CJK text or numbers).
.text-combine-upright-none {
text-combine-upright: none; /* Default — no combining */
}
.text-combine-upright-all {
text-combine-upright: all; /* Combines all characters into one upright unit */
}
Values
| Value | Description | Example |
|---|---|---|
none | Default — does not combine characters | text-combine-upright: none; |
all | Combines all characters in a word into one upright unit | text-combine-upright: all; |
digits <n> | Combines up to N digits | text-combine-upright: digits 4; |
Use case: In vertical Japanese text, numbers like “2024” can be combined into a single upright block instead of being rotated.
2. text-underline-offset
The text-underline-offset property sets the distance between the text and its underline.
.text-underline-offset-auto {
text-decoration: underline;
text-underline-offset: auto; /* Default — browser decides */
}
.text-underline-offset-2px {
text-decoration: underline;
text-underline-offset: 2px; /* Moves underline down by 2px */
}
.text-underline-offset-relative {
text-decoration: underline;
text-underline-offset: 0.2em; /* Relative to font size */
}
.text-underline-offset-percent {
text-decoration: underline;
text-underline-offset: 10%; /* Percentage of font size */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Default — browser determines the offset | text-underline-offset: auto; |
length | Fixed offset (px, em, rem) | text-underline-offset: 2px; |
percentage | Percentage of font size | text-underline-offset: 10%; |
3. text-underline-position
The text-underline-position property specifies where the underline should appear.
.text-underline-position-auto {
text-decoration: underline;
text-underline-position: auto; /* Default — browser decides */
}
.text-underline-position-under {
text-decoration: underline;
text-underline-position: under; /* Below the text (avoids descenders) */
}
.text-underline-position-left {
text-decoration: underline;
text-underline-position: left; /* Left of the text (vertical writing) */
}
.text-underline-position-right {
text-decoration: underline;
text-underline-position: right; /* Right of the text (vertical writing) */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Default — browser decides | text-underline-position: auto; |
under | Places underline below the text (avoids descenders) | text-underline-position: under; |
left | Places underline to the left (vertical writing) | text-underline-position: left; |
right | Places underline to the right (vertical writing) | text-underline-position: right; |
4. text-overflow
The text-overflow property controls how hidden content is displayed when it overflows its container. It only works with white-space: nowrap and a constrained width.
.overflow-clip {
width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: clip; /* Cuts off the text */
}
.overflow-ellipsis {
width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis; /* Shows "..." */
}
Values
| Value | Description | Example |
|---|---|---|
clip | Cuts off the text at the container edge | text-overflow: clip; |
ellipsis | Shows “…” to indicate clipped text | text-overflow: ellipsis; |
string | Uses a custom string (e.g., “→”) | text-overflow: "→"; |
Global Values (for all properties)
| Value | Description |
|---|---|
inherit | Inherits the value from the parent element |
initial | Sets the property to its default value |
unset | Inherits if possible, otherwise uses initial |
revert | Reverts to the browser’s default styles |
revert-layer | Reverts to the value in the previous cascade layer |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>text-combine-upright, text-underline-offset, text-underline-position, text-overflow</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.8;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 2px solid #ddd;
background: #f8f9fa;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== TEXT COMBINE UPRIGHT ====== */
.tcu-none {
text-combine-upright: none;
writing-mode: vertical-rl;
height: 200px;
border: 1px solid #ddd;
padding: 10px;
}
.tcu-all {
text-combine-upright: all;
writing-mode: vertical-rl;
height: 200px;
border: 1px solid #ddd;
padding: 10px;
}
/* ====== UNDERLINE OFFSET ====== */
.tuo-auto {
text-decoration: underline;
text-underline-offset: auto;
}
.tuo-2px {
text-decoration: underline;
text-underline-offset: 2px;
}
.tuo-6px {
text-decoration: underline;
text-underline-offset: 6px;
}
.tuo-em {
text-decoration: underline;
text-underline-offset: 0.3em;
}
/* ====== UNDERLINE POSITION ====== */
.tup-auto {
text-decoration: underline;
text-underline-position: auto;
}
.tup-under {
text-decoration: underline;
text-underline-position: under;
}
.tup-left {
text-decoration: underline;
text-underline-position: left;
writing-mode: vertical-rl;
height: 150px;
border: 1px solid #ddd;
padding: 10px;
}
.tup-right {
text-decoration: underline;
text-underline-position: right;
writing-mode: vertical-rl;
height: 150px;
border: 1px solid #ddd;
padding: 10px;
}
/* ====== TEXT OVERFLOW ====== */
.to-clip {
width: 180px;
white-space: nowrap;
overflow: hidden;
text-overflow: clip;
border: 1px solid #ddd;
padding: 5px;
background: #f8f9fa;
border-radius: 4px;
}
.to-ellipsis {
width: 180px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid #ddd;
padding: 5px;
background: #f8f9fa;
border-radius: 4px;
}
.to-string {
width: 180px;
white-space: nowrap;
overflow: hidden;
text-overflow: "→";
border: 1px solid #ddd;
padding: 5px;
background: #f8f9fa;
border-radius: 4px;
}
.note {
font-size: 0.85rem;
color: #6c757d;
margin-top: 5px;
}
</style>
</head>
<body>
<h1>text-combine-upright, text-underline-offset, text-underline-position, text-overflow</h1>
<!-- ====== 1. TEXT COMBINE UPRIGHT ====== -->
<section>
<h2>1. text-combine-upright</h2>
<p>These examples use <code>writing-mode: vertical-rl</code> to demonstrate how characters can be combined in vertical text.</p>
<div class="grid-2">
<div>
<div class="demo-box tcu-none">
<strong>text-combine-upright: none</strong><br>
<span style="font-size: 1.2rem;">2024</span> 年
</div>
<p class="note">Characters are not combined — each digit is separate.</p>
</div>
<div>
<div class="demo-box tcu-all">
<strong>text-combine-upright: all</strong><br>
<span style="font-size: 1.2rem;">2024</span> 年
</div>
<p class="note">All characters combined into a single upright unit.</p>
</div>
</div>
<div class="code-block">
text-combine-upright: none; /* Default — no combining */
text-combine-upright: all; /* Combines all characters */
text-combine-upright: digits 4; /* Combines up to 4 digits */
</div>
</section>
<!-- ====== 2. TEXT UNDERLINE OFFSET ====== -->
<section>
<h2>2. text-underline-offset</h2>
<div class="grid-2">
<div class="demo-box tuo-auto">
<strong>text-underline-offset: auto</strong><br>
This is a sample text with an automatically placed underline below it.
</div>
<div class="demo-box tuo-2px">
<strong>text-underline-offset: 2px</strong><br>
This is a sample text with the underline offset down by 2 pixels.
</div>
<div class="demo-box tuo-6px">
<strong>text-underline-offset: 6px</strong><br>
This is a sample text with the underline offset down by 6 pixels.
</div>
<div class="demo-box tuo-em">
<strong>text-underline-offset: 0.3em</strong><br>
This is a sample text with the underline offset relative to the font size.
</div>
</div>
<div class="code-block">
text-underline-offset: auto; /* Default — browser decides */
text-underline-offset: 2px; /* Moves underline down by 2px */
text-underline-offset: 6px; /* Moves underline down by 6px */
text-underline-offset: 0.3em; /* Relative to font size */
</div>
</section>
<!-- ====== 3. TEXT UNDERLINE POSITION ====== -->
<section>
<h2>3. text-underline-position</h2>
<div class="grid-2">
<div class="demo-box tup-auto">
<strong>text-underline-position: auto</strong><br>
This is a sample text with an automatically placed underline.
</div>
<div class="demo-box tup-under">
<strong>text-underline-position: under</strong><br>
This is a sample text with the underline below the text, avoiding descenders like g, j, p, q, y.
</div>
</div>
<h3>Vertical Writing Modes</h3>
<div class="grid-2">
<div>
<div class="demo-box tup-left">
<strong>text-underline-position: left</strong><br>
This text is in vertical writing mode with underline on the left.
</div>
<p class="note">Underline appears to the left of vertical text.</p>
</div>
<div>
<div class="demo-box tup-right">
<strong>text-underline-position: right</strong><br>
This text is in vertical writing mode with underline on the right.
</div>
<p class="note">Underline appears to the right of vertical text.</p>
</div>
</div>
<div class="code-block">
text-underline-position: auto; /* Default — browser decides */
text-underline-position: under; /* Below the text (avoids descenders) */
text-underline-position: left; /* Left of text (vertical writing) */
text-underline-position: right; /* Right of text (vertical writing) */
</div>
</section>
<!-- ====== 4. TEXT OVERFLOW ====== -->
<section>
<h2>4. text-overflow</h2>
<p><code>text-overflow</code> only works with <code>white-space: nowrap</code> and <code>overflow: hidden</code>.</p>
<div class="grid-2">
<div>
<div class="to-clip">
<strong>text-overflow: clip</strong> — This text will be clipped.
</div>
<p class="note">Cuts off the text at the container edge.</p>
</div>
<div>
<div class="to-ellipsis">
<strong>text-overflow: ellipsis</strong> — This text will show an ellipsis.
</div>
<p class="note">Shows "..." to indicate clipped text.</p>
</div>
<div>
<div class="to-string">
<strong>text-overflow: "→"</strong> — This text will show an arrow.
</div>
<p class="note">Uses a custom string to indicate clipped text.</p>
</div>
</div>
<div class="code-block">
/* text-overflow only works with these conditions */
.container {
width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: clip; /* Cuts off the text */
text-overflow: ellipsis; /* Shows "..." */
text-overflow: "→"; /* Custom string */
}
</div>
</section>
<!-- ====== 5. COMBINED EXAMPLE ====== -->
<section>
<h2>5. Combined Example</h2>
<div class="demo-box" style="text-decoration: underline; text-underline-offset: 4px; text-underline-position: under;">
<strong>Combined: underline + offset + position</strong><br>
This text has an underline that is offset 4px below the text and positioned under (avoiding descenders).
</div>
<div class="demo-box" style="width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border: 1px solid #ddd; padding: 8px; border-radius: 4px;">
This is a long text that will be truncated with an ellipsis when it exceeds the container width.
</div>
<div class="code-block">
/* Combined underline styling */
text-decoration: underline;
text-underline-offset: 4px;
text-underline-position: under;
/* Overflow with ellipsis */
width: 250px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>text-combine-upright Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Default — does not combine characters</td>
<td><code>text-combine-upright: none;</code></td>
</tr>
<tr>
<td><code>all</code></td>
<td>Combines all characters into one upright unit</td>
<td><code>text-combine-upright: all;</code></td>
</tr>
<tr>
<td><code>digits <n></code></td>
<td>Combines up to N digits</td>
<td><code>text-combine-upright: digits 4;</code></td>
</tr>
</table>
<h3>text-underline-offset Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Default — browser determines the offset</td>
<td><code>text-underline-offset: auto;</code></td>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed offset (px, em, rem)</td>
<td><code>text-underline-offset: 2px;</code></td>
</tr>
<tr>
<td><code>percentage</code></td>
<td>Percentage of font size</td>
<td><code>text-underline-offset: 10%;</code></td>
</tr>
</table>
<h3>text-underline-position Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Default — browser decides</td>
<td><code>text-underline-position: auto;</code></td>
</tr>
<tr>
<td><code>under</code></td>
<td>Places underline below the text (avoids descenders)</td>
<td><code>text-underline-position: under;</code></td>
</tr>
<tr>
<td><code>left</code></td>
<td>Places underline to the left (vertical writing)</td>
<td><code>text-underline-position: left;</code></td>
</tr>
<tr>
<td><code>right</code></td>
<td>Places underline to the right (vertical writing)</td>
<td><code>text-underline-position: right;</code></td>
</tr>
</table>
<h3>text-overflow Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>clip</code></td>
<td>Cuts off the text at the container edge</td>
<td><code>text-overflow: clip;</code></td>
</tr>
<tr>
<td><code>ellipsis</code></td>
<td>Shows "..." to indicate clipped text</td>
<td><code>text-overflow: ellipsis;</code></td>
</tr>
<tr>
<td><code>string</code></td>
<td>Uses a custom string</td>
<td><code>text-overflow: "→";</code></td>
</tr>
</table>
<h3>Global Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>inherit</code></td>
<td>Inherits the value from the parent element</td>
</tr>
<tr>
<td><code>initial</code></td>
<td>Sets the property to its default value</td>
</tr>
<tr>
<td><code>unset</code></td>
<td>Inherits if possible, otherwise uses initial</td>
</tr>
<tr>
<td><code>revert</code></td>
<td>Reverts to the browser's default styles</td>
</tr>
<tr>
<td><code>revert-layer</code></td>
<td>Reverts to the value in the previous cascade layer</td>
</tr>
</table>
</section>
</body>
</html>
Quick Reference
| Property | Description | Common Values |
|---|---|---|
text-combine-upright | Combines characters into one upright glyph | none, all, digits <n> |
text-underline-offset | Distance of the underline | auto, 2px, 0.3em |
text-underline-position | Position of the underline | auto, under, left, right |
text-overflow | How hidden overflow is displayed | clip, ellipsis, "→" |
Best Practices
✅ Do This:
/* Use text-overflow: ellipsis for truncated content */
.truncate {
width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Use text-underline-offset for better readability */
a {
text-decoration: underline;
text-underline-offset: 3px;
}
/* Use text-underline-position: under for links with descenders */
a {
text-decoration: underline;
text-underline-position: under;
}
❌ Don’t Do This:
/* Don't use text-overflow without white-space: nowrap */
.container {
text-overflow: ellipsis; /* Won't work without nowrap */
/* Add white-space: nowrap and overflow: hidden */
}
/* Don't use text-combine-upright without vertical writing mode */
.text {
text-combine-upright: all; /* Only works in vertical writing modes */
}
Pro Tip: Use text-overflow: ellipsis for truncating long text in tables, cards, or navigation. Use text-underline-offset and text-underline-position: under for better readability of underlined links, especially those with descenders (g, j, p, q, y). text-combine-upright is primarily for CJK vertical text — use it when numbers or Latin characters need to fit into a single character space!
15. The position Property
The position property is one of the most powerful tools in CSS for controlling the layout and placement of elements. It determines how an element is positioned in the document flow and how it responds to the top, right, bottom, and left properties.
Overview of position Values
| Value | Description |
|---|---|
static | Default — element stays in its normal flow |
relative | Positioned relative to its normal position |
absolute | Removed from flow — positioned relative to nearest positioned ancestor |
fixed | Removed from flow — positioned relative to the viewport |
sticky | Hybrid — relative until a threshold, then fixed |
1. position: static
The default value. Elements remain in their normal document flow. The top, right, bottom, left, and z-index properties have no effect.
.static {
position: static; /* Default */
margin: 10px;
padding: 20px;
border: 1px solid #ddd;
}
2. position: relative
The element is positioned relative to its normal position. The top, right, bottom, and left properties offset it from where it would normally be.
.relative {
position: relative;
top: 20px; /* Moves down 20px from normal position */
left: 50px; /* Moves right 50px from normal position */
}
Key Points:
- The element still occupies its original space in the flow
- Other elements are not affected by the offset
- Creates a positioning context for absolutely positioned children
3. position: absolute
The element is removed from the normal flow and positioned relative to its nearest positioned ancestor (or the <html> root if none exists).
.absolute {
position: absolute;
top: 20px;
left: 50px;
}
Key Points:
- Removed from the normal flow (other elements ignore it)
- Positioned relative to nearest ancestor with
position: relative,absolute,fixed, orsticky - If no positioned ancestor exists, it’s positioned relative to the initial containing block (the viewport)
- Creates a positioning context for its children
4. position: fixed
The element is removed from the normal flow and positioned relative to the viewport. It stays in the same place even when the page is scrolled.
.fixed {
position: fixed;
top: 20px;
right: 20px;
}
Key Points:
- Removed from the normal flow
- Positioned relative to the viewport (not the document)
- Stays fixed when scrolling
- Commonly used for sticky headers, floating buttons, and modals
5. position: sticky
The element behaves like relative until it reaches a threshold (specified by top, bottom, left, or right), then it becomes fixed relative to its nearest scrolling ancestor.
.sticky {
position: sticky;
top: 20px; /* Sticks when it reaches 20px from the top */
}
Key Points:
- Hybrid between
relativeandfixed - Stays in normal flow until threshold is reached
- Sticks to the specified position within its parent container
- Commonly used for sticky headers, sidebars, and table headers
Comparison Table
| Value | In Flow? | Positioned Relative To | Scrolls? | Creates Context? |
|---|---|---|---|---|
static | ✅ Yes | Normal flow | ✅ Yes | ❌ No |
relative | ✅ Yes | Its normal position | ✅ Yes | ✅ Yes |
absolute | ❌ No | Nearest positioned ancestor | ✅ Yes | ✅ Yes |
fixed | ❌ No | Viewport | ❌ No | ✅ Yes |
sticky | ✅ Yes | Nearest scrolling ancestor | Until threshold | ✅ Yes |
The z-index Property
When elements overlap, z-index controls which appears on top. It only works on positioned elements (relative, absolute, fixed, sticky).
.element {
position: relative;
z-index: 10; /* Higher = on top */
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The position Property</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 2px solid #ddd;
background: #f8f9fa;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== POSITION DEMOS ====== */
.position-demo {
position: relative;
background: #e9ecef;
padding: 20px;
border-radius: 8px;
min-height: 300px;
border: 2px dashed #ccc;
}
.position-demo .label {
position: absolute;
top: 5px;
left: 10px;
font-size: 0.8rem;
font-weight: bold;
color: #666;
background: white;
padding: 2px 8px;
border-radius: 4px;
}
/* Static */
.static-box {
position: static;
background: #007bff;
color: white;
padding: 15px;
margin: 10px 0;
border-radius: 4px;
width: 200px;
}
/* Relative */
.relative-box {
position: relative;
top: 20px;
left: 50px;
background: #28a745;
color: white;
padding: 15px;
margin: 10px 0;
border-radius: 4px;
width: 200px;
}
/* Absolute */
.absolute-container {
position: relative;
background: #f8f9fa;
border: 2px dashed #007bff;
padding: 20px;
margin: 20px 0;
min-height: 200px;
border-radius: 8px;
}
.absolute-box {
position: absolute;
top: 20px;
right: 20px;
background: #dc3545;
color: white;
padding: 15px;
border-radius: 4px;
width: 180px;
}
/* Fixed */
.fixed-box {
position: fixed;
bottom: 20px;
right: 20px;
background: #6c5ce7;
color: white;
padding: 15px 25px;
border-radius: 50px;
box-shadow: 0 4px 15px rgba(108, 92, 231, 0.4);
z-index: 1000;
cursor: pointer;
transition: transform 0.3s;
}
.fixed-box:hover {
transform: scale(1.05);
}
/* Sticky */
.sticky-container {
background: #f8f9fa;
border: 2px dashed #ffc107;
padding: 20px;
margin: 20px 0;
border-radius: 8px;
height: 300px;
overflow-y: auto;
}
.sticky-header {
position: sticky;
top: 0;
background: #ffc107;
color: #333;
padding: 15px;
border-radius: 4px;
font-weight: bold;
z-index: 10;
}
.sticky-content {
padding: 15px;
}
.sticky-content p {
margin: 10px 0;
}
/* Overlap demo */
.overlap-container {
position: relative;
height: 150px;
background: #f8f9fa;
border: 2px dashed #ddd;
border-radius: 8px;
margin: 20px 0;
}
.overlap-box {
position: absolute;
width: 120px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
border-radius: 8px;
}
.overlap-box:nth-child(1) {
background: #007bff;
top: 20px;
left: 20px;
z-index: 1;
}
.overlap-box:nth-child(2) {
background: #28a745;
top: 40px;
left: 60px;
z-index: 2;
}
.overlap-box:nth-child(3) {
background: #dc3545;
top: 60px;
left: 100px;
z-index: 3;
}
</style>
</head>
<body>
<h1>The position Property</h1>
<!-- ====== 1. STATIC ====== -->
<section>
<h2>1. position: static (default)</h2>
<p>Elements remain in their normal flow. The <code>top</code>, <code>right</code>, <code>bottom</code>, <code>left</code>, and <code>z-index</code> properties have <strong>no effect</strong>.</p>
<div class="position-demo">
<span class="label">static container</span>
<div class="static-box">Static Box 1</div>
<div class="static-box">Static Box 2</div>
<div class="static-box">Static Box 3</div>
</div>
<div class="code-block">
.static {
position: static; /* Default */
/* top, left, z-index have NO effect */
}
</div>
</section>
<!-- ====== 2. RELATIVE ====== -->
<section>
<h2>2. position: relative</h2>
<p>The element is positioned relative to its <strong>normal position</strong>. It still occupies its original space in the flow.</p>
<div class="position-demo">
<span class="label">relative container</span>
<div class="static-box" style="opacity: 0.3;">Normal position (ghost)</div>
<div class="relative-box">Relative Box (offset: top 20px, left 50px)</div>
<div class="static-box">Normal Box</div>
</div>
<div class="code-block">
.relative {
position: relative;
top: 20px; /* Moves down 20px from normal position */
left: 50px; /* Moves right 50px from normal position */
}
</div>
</section>
<!-- ====== 3. ABSOLUTE ====== -->
<section>
<h2>3. position: absolute</h2>
<p>The element is removed from the normal flow and positioned relative to its <strong>nearest positioned ancestor</strong>.</p>
<div class="absolute-container">
<span class="label">absolute container (position: relative)</span>
<p>This container has <code>position: relative</code>, making it the positioning context for the absolute box.</p>
<div class="absolute-box">Absolute Box (top: 20px, right: 20px)</div>
<div style="height: 150px;"></div>
</div>
<div class="code-block">
.absolute-container {
position: relative; /* Creates positioning context */
}
.absolute {
position: absolute;
top: 20px;
right: 20px;
}
</div>
</section>
<!-- ====== 4. FIXED ====== -->
<section>
<h2>4. position: fixed</h2>
<p>The element is removed from the flow and positioned relative to the <strong>viewport</strong>. It stays in place when scrolling.</p>
<p>Scroll down the page — the purple button at the bottom-right stays fixed!</p>
<div class="code-block">
.fixed {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}
</div>
</section>
<!-- ====== 5. STICKY ====== -->
<section>
<h2>5. position: sticky</h2>
<p>The element behaves like <code>relative</code> until it reaches a threshold, then becomes <code>fixed</code> within its parent.</p>
<div class="sticky-container">
<div class="sticky-header">📌 Sticky Header (scroll inside this box)</div>
<div class="sticky-content">
<p>Scroll inside this container to see the sticky header stay at the top.</p>
<p>Line 1 — Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<p>Line 2 — Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Line 3 — Ut enim ad minim veniam, quis nostrud exercitation ullamco.</p>
<p>Line 4 — Duis aute irure dolor in reprehenderit in voluptate velit.</p>
<p>Line 5 — Excepteur sint occaecat cupidatat non proident.</p>
<p>Line 6 — Sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Line 7 — Sed ut perspiciatis unde omnis iste natus error sit voluptatem.</p>
<p>Line 8 — Accusantium doloremque laudantium, totam rem aperiam.</p>
<p>Line 9 — Eaque ipsa quae ab illo inventore veritatis et quasi architecto.</p>
<p>Line 10 — Beatae vitae dicta sunt explicabo.</p>
</div>
</div>
<div class="code-block">
.sticky {
position: sticky;
top: 0; /* Sticks when it reaches the top */
}
</div>
</section>
<!-- ====== 6. Z-INDEX ====== -->
<section>
<h2>6. z-index (stacking order)</h2>
<p>When elements overlap, <code>z-index</code> controls which appears on top. Higher values appear on top.</p>
<div class="overlap-container">
<div class="overlap-box">z-index: 1</div>
<div class="overlap-box">z-index: 2</div>
<div class="overlap-box">z-index: 3</div>
</div>
<div class="code-block">
.overlap-box:nth-child(1) { z-index: 1; }
.overlap-box:nth-child(2) { z-index: 2; }
.overlap-box:nth-child(3) { z-index: 3; }
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>position Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>In Flow?</th>
<th>Positioned Relative To</th>
<th>Scrolls?</th>
</tr>
<tr>
<td><code>static</code></td>
<td>✅ Yes</td>
<td>Normal flow</td>
<td>✅ Yes</td>
</tr>
<tr>
<td><code>relative</code></td>
<td>✅ Yes</td>
<td>Its normal position</td>
<td>✅ Yes</td>
</tr>
<tr>
<td><code>absolute</code></td>
<td>❌ No</td>
<td>Nearest positioned ancestor</td>
<td>✅ Yes</td>
</tr>
<tr>
<td><code>fixed</code></td>
<td>❌ No</td>
<td>Viewport</td>
<td>❌ No</td>
</tr>
<tr>
<td><code>sticky</code></td>
<td>✅ Yes</td>
<td>Nearest scrolling ancestor</td>
<td>Until threshold</td>
</tr>
</table>
<h3>Positional Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>top</code></td>
<td>Offset from the top</td>
<td><code>top: 20px;</code></td>
</tr>
<tr>
<td><code>right</code></td>
<td>Offset from the right</td>
<td><code>right: 20px;</code></td>
</tr>
<tr>
<td><code>bottom</code></td>
<td>Offset from the bottom</td>
<td><code>bottom: 20px;</code></td>
</tr>
<tr>
<td><code>left</code></td>
<td>Offset from the left</td>
<td><code>left: 20px;</code></td>
</tr>
<tr>
<td><code>z-index</code></td>
<td>Stacking order (higher = on top)</td>
<td><code>z-index: 10;</code></td>
</tr>
</table>
</section>
<!-- ====== BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>position: relative</code> to create a positioning context for absolute children</li>
<li>Use <code>position: fixed</code> for sticky headers and floating buttons</li>
<li>Use <code>position: sticky</code> for table headers and sidebars</li>
<li>Use <code>z-index</code> sparingly and with meaningful values</li>
<li>Use <code>position: absolute</code> for overlays, tooltips, and badges</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>position: absolute</code> without a positioned ancestor</li>
<li>Don't use <code>z-index: 9999</code> — use meaningful values</li>
<li>Don't use <code>position: fixed</code> for large elements (can block content)</li>
<li>Don't forget that <code>position: absolute</code> removes elements from the flow</li>
</ul>
</div>
</section>
<!-- ====== FIXED BUTTON ====== -->
<div class="fixed-box" onclick="window.scrollTo({top: 0, behavior: 'smooth'});">
↑ Back to Top
</div>
</body>
</html>
Quick Reference
| Value | In Flow? | Positioned Relative To | Scrolls? | Creates Context? |
|---|---|---|---|---|
static | ✅ Yes | Normal flow | ✅ Yes | ❌ No |
relative | ✅ Yes | Its normal position | ✅ Yes | ✅ Yes |
absolute | ❌ No | Nearest positioned ancestor | ✅ Yes | ✅ Yes |
fixed | ❌ No | Viewport | ❌ No | ✅ Yes |
sticky | ✅ Yes | Nearest scrolling ancestor | Until threshold | ✅ Yes |
Best Practices
✅ Do This:
/* Create a positioning context */
.card {
position: relative; /* Parent */
}
.card .badge {
position: absolute; /* Child */
top: 10px;
right: 10px;
}
/* Sticky header */
.header {
position: sticky;
top: 0;
z-index: 100;
}
/* Fixed floating button */
.back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}
❌ Don’t Do This:
/* Don't use absolute without a positioned parent */
.badge {
position: absolute;
top: 0;
/* Will position relative to the viewport, not the card */
}
/* Don't use z-index: 9999 */
.element {
z-index: 9999; /* Use meaningful values like 10, 100, 1000 */
}
/* Don't use fixed for large elements */
.overlay {
position: fixed;
width: 100%;
height: 100%;
/* May block content and break scrolling */
}
Pro Tip: Use position: relative on a parent to create a positioning context for absolutely positioned children. Use position: fixed for elements that should always be visible (like a back-to-top button). Use position: sticky for table headers or sidebars that should stay visible while scrolling. Remember that absolute and fixed elements are removed from the normal flow, so they don’t affect the layout of other elements!
16. The display Property
The display property is one of the most important CSS properties. It controls how an element is displayed on a webpage and how it behaves in the document flow.
Overview of display Values
| Value | Description | In Flow? | Dimensions? | Line Breaks? |
|---|---|---|---|---|
inline | Flows with text | ✅ Yes | ❌ No | ❌ No |
block | Starts on new line, full width | ✅ Yes | ✅ Yes | ✅ Yes |
inline-block | Flows with text, but respects dimensions | ✅ Yes | ✅ Yes | ❌ No |
flex | Block-level flex container | ✅ Yes | ✅ Yes | ✅ Yes |
inline-flex | Inline-level flex container | ✅ Yes | ✅ Yes | ❌ No |
grid | Block-level grid container | ✅ Yes | ✅ Yes | ✅ Yes |
inline-grid | Inline-level grid container | ✅ Yes | ✅ Yes | ❌ No |
none | Hides the element | ❌ No | ❌ No | ❌ No |
1. display: inline
The element takes up only as much horizontal space as necessary for its content. It does not generate line breaks before or after itself. You cannot set its width and height.
.inline {
display: inline;
background-color: #ddd;
margin: 5px;
padding: 10px;
}
Key Points:
- No line breaks before/after
- Width and height are ignored
- Padding and margin work horizontally but not vertically
- Common inline elements:
<span>,<a>,<strong>,<em>,<img>
2. display: block
The element starts on a new line and takes up the full width of its parent. You can set its width and height.
.block {
display: block;
background-color: #ddd;
margin: 5px;
padding: 10px;
}
Key Points:
- Starts on a new line
- Takes full width of parent
- Width and height work
- Common block elements:
<div>,<p>,<h1>–<h6>,<ul>,<li>
3. display: inline-block
The element behaves like a block element but maintains inline behavior — no line breaks before or after. You can set its width and height.
.inline-block {
display: inline-block;
background-color: #ddd;
margin: 5px;
padding: 10px;
}
Key Points:
- Flows with text (no line breaks)
- Width and height work
- Margin, border, and padding work on all sides
- Great for navigation menus, buttons, and image galleries
4. display: flex
The element behaves like a block element but lays out its contents according to the flexbox model. It arranges its children in a single row or column.
.flex {
display: flex;
background-color: #ddd;
margin: 5px;
padding: 10px;
}
Key Points:
- Block-level container
- Children become flex items
- Great for one-dimensional layouts (rows or columns)
- Allows flexible alignment, ordering, and spacing
5. display: grid
The element behaves like a block element but lays out its contents according to the grid model. It arranges its children in a two-dimensional grid.
.grid {
display: grid;
background-color: #ddd;
margin: 5px;
padding: 10px;
}
Key Points:
- Block-level container
- Children become grid items
- Great for two-dimensional layouts (rows and columns)
- Allows complex arrangements of elements
6. display: none
The element is completely removed from the document flow. It takes up no space and is not rendered.
.hidden {
display: none;
}
Key Points:
- Element is not displayed
- Takes up no space in the layout
- Can be toggled with JavaScript for show/hide functionality
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The display Property</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 2px dashed #ddd;
background: #f8f9fa;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== DISPLAY DEMOS ====== */
.inline-demo {
display: inline;
background-color: #007bff;
color: white;
padding: 5px 10px;
margin: 5px;
}
.block-demo {
display: block;
background-color: #28a745;
color: white;
padding: 10px 15px;
margin: 5px 0;
}
.inline-block-demo {
display: inline-block;
background-color: #dc3545;
color: white;
padding: 10px 15px;
margin: 5px;
width: 150px;
text-align: center;
}
.flex-demo {
display: flex;
background-color: #ffc107;
color: #333;
padding: 10px;
margin: 5px 0;
gap: 10px;
}
.flex-demo > div {
background: white;
padding: 10px 20px;
border-radius: 4px;
}
.grid-demo {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
background-color: #6c5ce7;
color: white;
padding: 10px;
margin: 5px 0;
}
.grid-demo > div {
background: rgba(255, 255, 255, 0.2);
padding: 15px;
border-radius: 4px;
text-align: center;
}
.none-demo {
display: none;
}
.hidden-note {
color: #dc3545;
font-style: italic;
}
</style>
</head>
<body>
<h1>The display Property</h1>
<!-- ====== 1. INLINE ====== -->
<section>
<h2>1. display: inline</h2>
<p>The element takes up only as much space as needed. Width and height are ignored. No line breaks.</p>
<div class="demo-box">
<div class="inline-demo">Inline 1</div>
<div class="inline-demo">Inline 2</div>
<span class="inline-demo">Inline 3</span>
<div class="inline-demo">Inline 4</div>
<p>Notice how the elements flow inline — they don't start on a new line.</p>
</div>
<div class="code-block">
.inline {
display: inline;
/* width and height are IGNORED */
}
</div>
</section>
<!-- ====== 2. BLOCK ====== -->
<section>
<h2>2. display: block</h2>
<p>The element starts on a new line and takes up the full width of its parent.</p>
<div class="demo-box">
<div class="block-demo">Block 1</div>
<div class="block-demo">Block 2</div>
<div class="block-demo">Block 3</div>
<p>Notice how each block starts on a new line and takes the full width.</p>
</div>
<div class="code-block">
.block {
display: block;
width: 100%; /* Full width of parent */
/* Starts on a new line */
}
</div>
</section>
<!-- ====== 3. INLINE-BLOCK ====== -->
<section>
<h2>3. display: inline-block</h2>
<p>Behaves like a block element but flows inline. Width and height work.</p>
<div class="demo-box">
<div class="inline-block-demo">Inline-Block 1</div>
<div class="inline-block-demo">Inline-Block 2</div>
<div class="inline-block-demo">Inline-Block 3</div>
<span class="inline-block-demo">Inline-Block 4</span>
<p>Notice how they flow inline but respect width and height.</p>
</div>
<div class="code-block">
.inline-block {
display: inline-block;
width: 150px; /* Width works */
height: 50px; /* Height works */
/* Flows inline — no line breaks */
}
</div>
</section>
<!-- ====== 4. FLEX ====== -->
<section>
<h2>4. display: flex</h2>
<p>The element becomes a flex container. Its children become flex items arranged in a row or column.</p>
<div class="demo-box">
<div class="flex-demo">
<div>Flex Item 1</div>
<div>Flex Item 2</div>
<div>Flex Item 3</div>
</div>
<p>Children are arranged in a row with equal spacing.</p>
</div>
<div class="code-block">
.flex {
display: flex;
gap: 10px; /* Space between items */
}
</div>
</section>
<!-- ====== 5. GRID ====== -->
<section>
<h2>5. display: grid</h2>
<p>The element becomes a grid container. Its children become grid items arranged in a two-dimensional grid.</p>
<div class="demo-box">
<div class="grid-demo">
<div>Grid Item 1</div>
<div>Grid Item 2</div>
<div>Grid Item 3</div>
<div>Grid Item 4</div>
<div>Grid Item 5</div>
<div>Grid Item 6</div>
</div>
<p>Children are arranged in a 3-column grid.</p>
</div>
<div class="code-block">
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3 equal columns */
gap: 10px;
}
</div>
</section>
<!-- ====== 6. NONE ====== -->
<section>
<h2>6. display: none</h2>
<p>The element is completely removed from the document flow. It takes up no space.</p>
<div class="demo-box">
<p>This text is visible.</p>
<p class="none-demo">This text has display: none — you shouldn't see it!</p>
<p>This text is also visible.</p>
<p class="hidden-note">The hidden paragraph takes up no space.</p>
</div>
<div class="code-block">
.none {
display: none; /* Completely removed from flow */
}
</div>
</section>
<!-- ====== 7. COMPARISON ====== -->
<section>
<h2>7. Side-by-Side Comparison</h2>
<h3>inline vs inline-block vs block</h3>
<div class="demo-box">
<div style="display: inline; background: #007bff; color: white; padding: 5px 10px; margin: 5px;">
inline
</div>
<div style="display: inline; background: #007bff; color: white; padding: 5px 10px; margin: 5px;">
inline
</div>
<div style="display: inline-block; background: #dc3545; color: white; padding: 10px 15px; margin: 5px; width: 120px; text-align: center;">
inline-block
</div>
<div style="display: inline-block; background: #dc3545; color: white; padding: 10px 15px; margin: 5px; width: 120px; text-align: center;">
inline-block
</div>
<div style="display: block; background: #28a745; color: white; padding: 10px 15px; margin: 5px 0;">
block
</div>
<div style="display: block; background: #28a745; color: white; padding: 10px 15px; margin: 5px 0;">
block
</div>
</div>
<div class="code-block">
/* inline — no width/height, no line breaks */
display: inline;
/* inline-block — width/height work, no line breaks */
display: inline-block;
/* block — width/height work, line breaks before/after */
display: block;
</div>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>display Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>In Flow?</th>
<th>Dimensions?</th>
<th>Line Breaks?</th>
<th>Common Elements</th>
</tr>
<tr>
<td><code>inline</code></td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>❌ No</td>
<td><code><span></code>, <code><a></code>, <code><strong></code></td>
</tr>
<tr>
<td><code>block</code></td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td><code><div></code>, <code><p></code>, <code><h1></code></td>
</tr>
<tr>
<td><code>inline-block</code></td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>—</td>
</tr>
<tr>
<td><code>flex</code></td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>—</td>
</tr>
<tr>
<td><code>inline-flex</code></td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>—</td>
</tr>
<tr>
<td><code>grid</code></td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>—</td>
</tr>
<tr>
<td><code>inline-grid</code></td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>—</td>
</tr>
<tr>
<td><code>none</code></td>
<td>❌ No</td>
<td>❌ No</td>
<td>❌ No</td>
<td>—</td>
</tr>
</table>
<h3>When to Use Which</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Best For</th>
</tr>
<tr>
<td><code>inline</code></td>
<td>Text-level elements, links, emphasis</td>
</tr>
<tr>
<td><code>block</code></td>
<td>Page structure, paragraphs, headings, divs</td>
</tr>
<tr>
<td><code>inline-block</code></td>
<td>Navigation items, buttons, image galleries</td>
</tr>
<tr>
<td><code>flex</code></td>
<td>One-dimensional layouts (rows/columns), navigation bars</td>
</tr>
<tr>
<td><code>grid</code></td>
<td>Two-dimensional layouts, page layouts, complex arrangements</td>
</tr>
<tr>
<td><code>none</code></td>
<td>Hiding elements (toggles with JavaScript)</td>
</tr>
</table>
</section>
<!-- ====== BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>inline</code> for text-level elements</li>
<li>Use <code>block</code> for structural elements</li>
<li>Use <code>inline-block</code> for horizontal navigation items</li>
<li>Use <code>flex</code> for one-dimensional layouts</li>
<li>Use <code>grid</code> for two-dimensional layouts</li>
<li>Use <code>display: none</code> to hide elements completely</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>inline</code> for elements that need width/height</li>
<li>Don't use <code>block</code> for text-level elements</li>
<li>Don't use <code>display: none</code> for accessibility — use <code>visibility: hidden</code> or <code>aria-hidden</code> instead</li>
<li>Don't use <code>inline-block</code> when <code>flex</code> would be cleaner</li>
<li>Don't use <code>display</code> to change the semantic meaning of elements</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Value | In Flow? | Dimensions? | Line Breaks? | Common Elements |
|---|---|---|---|---|
inline | ✅ Yes | ❌ No | ❌ No | <span>, <a>, <strong> |
block | ✅ Yes | ✅ Yes | ✅ Yes | <div>, <p>, <h1> |
inline-block | ✅ Yes | ✅ Yes | ❌ No | — |
flex | ✅ Yes | ✅ Yes | ✅ Yes | — |
inline-flex | ✅ Yes | ✅ Yes | ❌ No | — |
grid | ✅ Yes | ✅ Yes | ✅ Yes | — |
inline-grid | ✅ Yes | ✅ Yes | ❌ No | — |
none | ❌ No | ❌ No | ❌ No | — |
Best Practices
✅ Do This:
/* Use flex for one-dimensional layouts */
.nav {
display: flex;
gap: 20px;
}
/* Use grid for two-dimensional layouts */
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
}
/* Use inline-block for buttons in a row */
.btn {
display: inline-block;
padding: 10px 20px;
}
/* Hide elements completely */
.hidden {
display: none;
}
❌ Don’t Do This:
/* Don't use inline for elements that need dimensions */
.box {
display: inline;
width: 200px; /* Ignored! */
height: 100px; /* Ignored! */
}
/* Don't use block for text-level elements */
.highlight {
display: block; /* Breaks the text flow */
}
/* Don't use display: none for accessibility */
.screen-reader-only {
display: none; /* Screen readers will ignore it too */
/* Use position: absolute; left: -9999px; instead */
}
Pro Tip: Use display: flex for one-dimensional layouts (rows or columns) and display: grid for two-dimensional layouts. Use inline-block for horizontal navigation items and buttons. Remember that display: none removes the element completely — use it for show/hide functionality, but avoid it for accessibility purposes (use visibility: hidden or visually-hidden CSS instead)!
17. Margin, Padding, and Border Properties
These three properties are the foundation of the CSS Box Model. They control the spacing around elements (margin), the spacing inside elements (padding), and the outline of elements (border).
Overview of the Three Properties
| Property | Description | Where It Applies |
|---|---|---|
margin | Space outside the border | Between the element and its neighbors |
padding | Space inside the border | Between the content and the border |
border | Line around the padding and content | Surrounds the element |
Visual Representation
┌─────────────────────────────────────────────┐
│ MARGIN │
│ ┌───────────────────────────────────────┐ │
│ │ BORDER │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ PADDING │ │ │
│ │ │ ┌───────────────────────────┐ │ │ │
│ │ │ │ CONTENT │ │ │ │
│ │ │ └───────────────────────────┘ │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
1. Margin
The margin property specifies the space around an element (outside the border).
/* All sides */
.box {
margin: 20px;
}
/* Individual sides */
.box-individual {
margin-top: 10px;
margin-right: 20px;
margin-bottom: 30px;
margin-left: 40px;
}
Shorthand Values
| Values | Top | Right | Bottom | Left |
|---|---|---|---|---|
margin: 20px; | 20px | 20px | 20px | 20px |
margin: 10px 20px; | 10px | 20px | 10px | 20px |
margin: 10px 20px 30px; | 10px | 20px | 30px | 20px |
margin: 10px 20px 30px 40px; | 10px | 20px | 30px | 40px |
Values
| Value | Description | Example |
|---|---|---|
length | Fixed margin (px, em, rem) | margin: 20px; |
percentage | Percentage of parent width | margin: 10%; |
auto | Browser calculates the margin | margin: 0 auto; |
negative | Negative margin | margin: -10px; |
2. Padding
The padding property specifies the space between the border and the content (inside the border).
/* All sides */
.box-padding {
padding: 20px;
}
/* Individual sides */
.box-padding-individual {
padding-top: 10px;
padding-right: 20px;
padding-bottom: 30px;
padding-left: 40px;
}
Shorthand Values
| Values | Top | Right | Bottom | Left |
|---|---|---|---|---|
padding: 20px; | 20px | 20px | 20px | 20px |
padding: 10px 20px; | 10px | 20px | 10px | 20px |
padding: 10px 20px 30px; | 10px | 20px | 30px | 20px |
padding: 10px 20px 30px 40px; | 10px | 20px | 30px | 40px |
Values
| Value | Description | Example |
|---|---|---|
length | Fixed padding (px, em, rem) | padding: 20px; |
percentage | Percentage of parent width | padding: 10%; |
Important: Padding adds to the total size of the element (in the standard box model). Negative padding is not allowed.
3. Border
The border property is a shorthand for border-width, border-style, and border-color.
/* Shorthand: width | style | color */
.box-border {
border: 1px solid #000;
}
.box-border-dashed {
border: 2px dashed red;
}
/* Individual sides */
.box-border-individual {
border-top: 2px solid blue;
border-right: 3px dashed green;
border-bottom: 4px dotted orange;
border-left: 5px double purple;
}
Shorthand Order
border: <border-width> <border-style> <border-color>;
border-width Values
| Value | Description | Example |
|---|---|---|
thin | Thin border (1px) | border-width: thin; |
medium | Medium border (3px) | border-width: medium; |
thick | Thick border (5px) | border-width: thick; |
length | Custom width | border-width: 2px; |
border-style Values
| Style | Description | Example |
|---|---|---|
none | No border | border-style: none; |
hidden | Hidden border (like none) | border-style: hidden; |
dotted | Dotted line | border-style: dotted; |
dashed | Dashed line | border-style: dashed; |
solid | Solid line (default) | border-style: solid; |
double | Double line | border-style: double; |
groove | 3D grooved effect | border-style: groove; |
ridge | 3D ridged effect | border-style: ridge; |
inset | 3D inset effect | border-style: inset; |
outset | 3D outset effect | border-style: outset; |
border-color Values
| Value | Description | Example |
|---|---|---|
| Named | Color names | border-color: red; |
| Hex | Hexadecimal | border-color: #FF0000; |
| RGB/RGBA | RGB values | border-color: rgb(255, 0, 0); |
| HSL/HSLA | HSL values | border-color: hsl(0, 100%, 50%); |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Margin, Padding, and Border</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.demo-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 2px dashed #ddd;
background: #f8f9fa;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== MARGIN DEMOS ====== */
.margin-all {
width: 250px;
height: 80px;
background-color: #007bff;
color: white;
display: flex;
align-items: center;
justify-content: center;
margin: 20px;
border-radius: 4px;
}
.margin-individual {
width: 250px;
height: 80px;
background-color: #28a745;
color: white;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
margin-right: 20px;
margin-bottom: 30px;
margin-left: 40px;
border-radius: 4px;
}
/* ====== PADDING DEMOS ====== */
.padding-all {
width: 250px;
height: 80px;
background-color: #ffc107;
color: #333;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
border: 2px solid #d4a017;
border-radius: 4px;
}
.padding-individual {
width: 250px;
height: 80px;
background-color: #dc3545;
color: white;
display: flex;
align-items: center;
justify-content: center;
padding-top: 10px;
padding-right: 20px;
padding-bottom: 30px;
padding-left: 40px;
border: 2px solid #a71d2a;
border-radius: 4px;
}
/* ====== BORDER DEMOS ====== */
.border-solid {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 3px solid #000;
border-radius: 4px;
}
.border-dashed {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 3px dashed #dc3545;
border-radius: 4px;
}
.border-dotted {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 3px dotted #007bff;
border-radius: 4px;
}
.border-double {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 5px double #28a745;
border-radius: 4px;
}
.border-groove {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 5px groove #6c5ce7;
border-radius: 4px;
}
.border-ridge {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 5px ridge #ffc107;
border-radius: 4px;
}
.border-inset {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 5px inset #17a2b8;
border-radius: 4px;
}
.border-outset {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border: 5px outset #dc3545;
border-radius: 4px;
}
.border-individual {
width: 250px;
height: 80px;
background-color: #e9ecef;
display: flex;
align-items: center;
justify-content: center;
border-top: 3px solid #007bff;
border-right: 3px dashed #28a745;
border-bottom: 3px dotted #dc3545;
border-left: 3px double #ffc107;
border-radius: 4px;
}
/* ====== VISUAL BOX MODEL ====== */
.box-model-demo {
display: flex;
justify-content: center;
gap: 30px;
flex-wrap: wrap;
}
.box-model-item {
text-align: center;
}
.box-model-item .box {
width: 150px;
height: 100px;
background: #007bff;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
margin: 10px auto;
font-weight: bold;
}
.box-model-item .box.with-margin {
background: #28a745;
margin: 30px;
}
.box-model-item .box.with-padding {
background: #ffc107;
color: #333;
padding: 30px;
width: 150px;
height: 100px;
box-sizing: content-box;
}
.box-model-item .box.with-border {
background: #dc3545;
border: 8px solid #333;
}
</style>
</head>
<body>
<h1>Margin, Padding, and Border Properties</h1>
<!-- ====== 1. MARGIN ====== -->
<section>
<h2>1. Margin</h2>
<p>The <code>margin</code> property specifies the space <strong>outside</strong> the border.</p>
<h3>All Sides (margin: 20px)</h3>
<div class="demo-box">
<div class="margin-all">
margin: 20px (all sides)
</div>
<p>Space around the element is 20px on all sides.</p>
</div>
<h3>Individual Sides</h3>
<div class="demo-box">
<div class="margin-individual">
margin: top=10px | right=20px | bottom=30px | left=40px
</div>
<p>Each side has a different margin value.</p>
</div>
<div class="code-block">
/* All sides */
margin: 20px;
/* Vertical | Horizontal */
margin: 10px 20px;
/* Top | Horizontal | Bottom */
margin: 10px 20px 30px;
/* Top | Right | Bottom | Left */
margin: 10px 20px 30px 40px;
/* Individual sides */
margin-top: 10px;
margin-right: 20px;
margin-bottom: 30px;
margin-left: 40px;
/* Auto margins (centers block elements) */
margin: 0 auto;
</div>
</section>
<!-- ====== 2. PADDING ====== -->
<section>
<h2>2. Padding</h2>
<p>The <code>padding</code> property specifies the space <strong>inside</strong> the border.</p>
<h3>All Sides (padding: 20px)</h3>
<div class="demo-box">
<div class="padding-all">
padding: 20px (all sides)
</div>
<p>Space between content and border is 20px on all sides.</p>
</div>
<h3>Individual Sides</h3>
<div class="demo-box">
<div class="padding-individual">
padding: top=10px | right=20px | bottom=30px | left=40px
</div>
<p>Each side has a different padding value.</p>
</div>
<div class="code-block">
/* All sides */
padding: 20px;
/* Vertical | Horizontal */
padding: 10px 20px;
/* Top | Horizontal | Bottom */
padding: 10px 20px 30px;
/* Top | Right | Bottom | Left */
padding: 10px 20px 30px 40px;
/* Individual sides */
padding-top: 10px;
padding-right: 20px;
padding-bottom: 30px;
padding-left: 40px;
</div>
</section>
<!-- ====== 3. BORDER ====== -->
<section>
<h2>3. Border</h2>
<p>The <code>border</code> property is a shorthand for <code>border-width</code>, <code>border-style</code>, and <code>border-color</code>.</p>
<h3>Border Styles</h3>
<div class="demo-box">
<div style="display: flex; flex-wrap: wrap; gap: 15px; justify-content: center;">
<div class="border-solid">solid</div>
<div class="border-dashed">dashed</div>
<div class="border-dotted">dotted</div>
<div class="border-double">double</div>
<div class="border-groove">groove</div>
<div class="border-ridge">ridge</div>
<div class="border-inset">inset</div>
<div class="border-outset">outset</div>
</div>
</div>
<h3>Individual Sides</h3>
<div class="demo-box">
<div class="border-individual">
Different border on each side
</div>
</div>
<div class="code-block">
/* Shorthand: width | style | color */
border: 1px solid #000;
border: 2px dashed red;
border: 3px dotted blue;
border: 5px double green;
border: 5px groove purple;
border: 5px ridge orange;
/* Individual sides */
border-top: 2px solid blue;
border-right: 3px dashed green;
border-bottom: 4px dotted orange;
border-left: 5px double purple;
/* Individual properties */
border-width: 2px;
border-style: solid;
border-color: #000;
</div>
</section>
<!-- ====== 4. VISUAL BOX MODEL ====== -->
<section>
<h2>4. Visual Box Model</h2>
<p>Compare the same box with different margin, padding, and border values.</p>
<div class="box-model-demo">
<div class="box-model-item">
<h4>No Extra Space</h4>
<div class="box">150 × 100</div>
<p><small>margin: 0; padding: 0; border: none</small></p>
</div>
<div class="box-model-item">
<h4>With Margin</h4>
<div class="box with-margin">150 × 100</div>
<p><small>margin: 30px</small></p>
</div>
<div class="box-model-item">
<h4>With Padding</h4>
<div class="box with-padding">150 × 100</div>
<p><small>padding: 30px (content-box)</small></p>
</div>
<div class="box-model-item">
<h4>With Border</h4>
<div class="box with-border">150 × 100</div>
<p><small>border: 8px solid #333</small></p>
</div>
</div>
<div class="code-block">
/* No extra space */
.box {
width: 150px;
height: 100px;
margin: 0;
padding: 0;
border: none;
}
/* With margin */
.box {
margin: 30px;
}
/* With padding */
.box {
padding: 30px;
box-sizing: content-box; /* Padding adds to total size */
}
/* With border */
.box {
border: 8px solid #333;
}
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>Shorthand Order</h3>
<table class="reference-table">
<tr>
<th>Values</th>
<th>Top</th>
<th>Right</th>
<th>Bottom</th>
<th>Left</th>
</tr>
<tr>
<td><code>margin: 20px;</code></td>
<td>20px</td>
<td>20px</td>
<td>20px</td>
<td>20px</td>
</tr>
<tr>
<td><code>margin: 10px 20px;</code></td>
<td>10px</td>
<td>20px</td>
<td>10px</td>
<td>20px</td>
</tr>
<tr>
<td><code>margin: 10px 20px 30px;</code></td>
<td>10px</td>
<td>20px</td>
<td>30px</td>
<td>20px</td>
</tr>
<tr>
<td><code>margin: 10px 20px 30px 40px;</code></td>
<td>10px</td>
<td>20px</td>
<td>30px</td>
<td>40px</td>
</tr>
</table>
<h3>Border Styles</h3>
<table class="reference-table">
<tr>
<th>Style</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>solid</code></td>
<td>Single solid line</td>
<td><code>border: 2px solid black;</code></td>
</tr>
<tr>
<td><code>dashed</code></td>
<td>Dashed line</td>
<td><code>border: 2px dashed red;</code></td>
</tr>
<tr>
<td><code>dotted</code></td>
<td>Dotted line</td>
<td><code>border: 2px dotted blue;</code></td>
</tr>
<tr>
<td><code>double</code></td>
<td>Double line</td>
<td><code>border: 4px double green;</code></td>
</tr>
<tr>
<td><code>groove</code></td>
<td>3D grooved effect</td>
<td><code>border: 4px groove gray;</code></td>
</tr>
<tr>
<td><code>ridge</code></td>
<td>3D ridged effect</td>
<td><code>border: 4px ridge gray;</code></td>
</tr>
<tr>
<td><code>inset</code></td>
<td>3D inset effect</td>
<td><code>border: 4px inset gray;</code></td>
</tr>
<tr>
<td><code>outset</code></td>
<td>3D outset effect</td>
<td><code>border: 4px outset gray;</code></td>
</tr>
<tr>
<td><code>none</code></td>
<td>No border</td>
<td><code>border: none;</code></td>
</tr>
</table>
<h3>Border Width Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>thin</code></td>
<td>Thin border (1px)</td>
<td><code>border-width: thin;</code></td>
</tr>
<tr>
<td><code>medium</code></td>
<td>Medium border (3px)</td>
<td><code>border-width: medium;</code></td>
</tr>
<tr>
<td><code>thick</code></td>
<td>Thick border (5px)</td>
<td><code>border-width: thick;</code></td>
</tr>
<tr>
<td><code>length</code></td>
<td>Custom width</td>
<td><code>border-width: 2px;</code></td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>margin</code> for spacing between elements</li>
<li>Use <code>padding</code> for spacing inside elements</li>
<li>Use shorthand properties to write cleaner code</li>
<li>Use <code>margin: 0 auto</code> to center block elements</li>
<li>Use <code>box-sizing: border-box</code> for predictable sizing</li>
<li>Use <code>border-radius</code> to round corners</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>margin</code> for internal spacing (use padding)</li>
<li>Don't use <code>padding</code> for external spacing (use margin)</li>
<li>Don't forget that padding adds to the total size (standard box model)</li>
<li>Don't use negative padding (not allowed)</li>
<li>Don't mix up margin and padding — they serve different purposes</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Where It Applies |
|---|---|---|
margin | Space outside the border | Between the element and its neighbors |
padding | Space inside the border | Between the content and the border |
border | Line around the padding and content | Surrounds the element |
Shorthand Order
| Values | Top | Right | Bottom | Left |
|---|---|---|---|---|
property: A; | A | A | A | A |
property: A B; | A | B | A | B |
property: A B C; | A | B | C | B |
property: A B C D; | A | B | C | D |
Best Practices
✅ Do This:
/* Use margin for external spacing */
.card {
margin-bottom: 20px;
}
/* Use padding for internal spacing */
.card {
padding: 20px;
}
/* Center block elements */
.container {
margin: 0 auto;
max-width: 1200px;
}
/* Apply border-box globally */
*, *::before, *::after {
box-sizing: border-box;
}
❌ Don’t Do This:
/* Don't use padding for external spacing */
.card {
padding-bottom: 20px; /* Creates space inside, not outside */
}
/* Don't forget box-sizing */
.box {
width: 100%;
padding: 20px;
border: 5px solid black;
/* Total width exceeds 100%! */
}
/* Don't use negative padding */
.box {
padding: -10px; /* Invalid! */
}
Pro Tip: Remember: margin is outside, padding is inside. Use margin to space elements apart, and padding to give content room to breathe. Always use box-sizing: border-box to make sizing more predictable. Use margin: 0 auto to center block elements horizontally!
18. Grid Layout Introduction
CSS Grid is a two-dimensional layout system introduced in CSS3. It provides a powerful and flexible way to create complex, responsive layouts by defining rows and columns.
What is CSS Grid?
CSS Grid allows you to arrange elements into rows and columns, giving you precise control over the layout of your web page. Unlike Flexbox (which is one-dimensional), Grid works in two dimensions simultaneously.
Key Benefits:
| Benefit | Description |
|---|---|
| Two-dimensional | Controls both rows and columns at the same time |
| Precise control | Exact placement of items in the grid |
| Responsive | Easily adapts to different screen sizes |
| Flexible | Supports fractions, min/max sizes, and repeat patterns |
| Clean markup | No need for extra wrapper divs |
Basic Grid Setup
<div class="grid-container">
<div class="grid-item">Grid Item 1</div>
<div class="grid-item">Grid Item 2</div>
<div class="grid-item">Grid Item 3</div>
<div class="grid-item">Grid Item 4</div>
<div class="grid-item">Grid Item 5</div>
<div class="grid-item">Grid Item 6</div>
</div>
.grid-container {
display: grid;
grid-template-columns: 100px 200px 100px;
grid-gap: 10px;
}
What happens:
display: grid;— Turns the element into a grid containergrid-template-columns: 100px 200px 100px;— Creates three columns with specific widthsgrid-gap: 10px;— Sets the gap between rows and columns- Children automatically flow into the grid cells
1. grid-template-columns
Defines the number and size of columns in the grid.
/* Three fixed-width columns */
.grid-container {
grid-template-columns: 100px 200px 100px;
}
/* Two equal columns */
.grid-container {
grid-template-columns: 1fr 1fr;
}
/* Mixed units */
.grid-container {
grid-template-columns: 100px 1fr 200px;
}
2. grid-template-rows
Defines the number and size of rows in the grid.
/* Three fixed-height rows */
.grid-container {
grid-template-rows: 100px 200px 100px;
}
/* Two equal rows */
.grid-container {
grid-template-rows: 1fr 1fr;
}
3. The fr Unit (Fraction of Available Space)
The fr unit represents a fraction of the available space. The total space is divided equally among all fractional units.
/* 1fr : 2fr : 3fr ratio */
.grid-container {
grid-template-columns: 1fr 2fr 3fr;
}
Calculation:
- Total = 1 + 2 + 3 = 6 parts
- Column 1 = 1/6 of available space
- Column 2 = 2/6 (1/3) of available space
- Column 3 = 3/6 (1/2) of available space
4. Value Keywords
Grid columns and rows also accept keyword values for more flexible sizing.
| Keyword | Description | Example |
|---|---|---|
max-content | Takes up the maximum possible space | grid-template-columns: max-content; |
min-content | Takes up the minimum possible space | grid-template-columns: min-content; |
minmax(min, max) | Sets minimum and maximum dimensions | grid-template-columns: minmax(100px, 200px); |
auto | Takes up remaining space | grid-template-columns: auto; |
repeat(n, size) | Repeats a pattern | grid-template-columns: repeat(3, 100px); |
5. minmax()
The minmax() function specifies the minimum and maximum possible dimensions for a grid track.
.grid-container {
grid-template-columns: minmax(100px, 200px) auto;
}
What happens:
- First column: minimum 100px, maximum 200px
- Second column: takes up any remaining space
6. repeat()
The repeat() function creates columns or rows by repeating a pattern.
/* Three equal columns */
.grid-container {
grid-template-columns: repeat(3, 100px);
}
/* Three equal fractional columns */
.grid-container {
grid-template-columns: repeat(3, 1fr);
}
/* Repeating pattern */
.grid-container {
grid-template-columns: repeat(3, 100px 200px);
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Grid Layout Introduction</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== GRID DEMOS ====== */
.grid-container {
display: grid;
gap: 10px;
margin: 15px 0;
background: #e9ecef;
padding: 10px;
border-radius: 8px;
}
.grid-item {
background: #007bff;
color: white;
padding: 20px;
border-radius: 6px;
text-align: center;
font-weight: bold;
}
.grid-item:nth-child(even) {
background: #28a745;
}
.grid-item:nth-child(3n) {
background: #dc3545;
}
/* Fixed columns */
.grid-fixed {
grid-template-columns: 100px 200px 100px;
}
/* Fractional columns */
.grid-fr {
grid-template-columns: 1fr 2fr 1fr;
}
/* Equal columns */
.grid-equal {
grid-template-columns: repeat(3, 1fr);
}
/* Repeat pattern */
.grid-repeat {
grid-template-columns: repeat(3, 100px);
}
/* minmax */
.grid-minmax {
grid-template-columns: minmax(100px, 200px) auto;
}
/* Rows */
.grid-rows {
grid-template-columns: 1fr 1fr;
grid-template-rows: 100px 150px;
}
/* Responsive grid */
.grid-responsive {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
</style>
</head>
<body>
<h1>CSS Grid Layout Introduction</h1>
<!-- ====== 1. BASIC GRID ====== -->
<section>
<h2>1. Basic Grid (Fixed Columns)</h2>
<p>Three columns with fixed widths: <code>100px 200px 100px</code></p>
<div class="grid-container grid-fixed">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: 100px 200px 100px;
gap: 10px;
}
</div>
</section>
<!-- ====== 2. FRACTIONAL COLUMNS ====== -->
<section>
<h2>2. Fractional Columns (fr)</h2>
<p>Columns in ratio <code>1fr 2fr 1fr</code> — the middle column is twice as wide.</p>
<div class="grid-container grid-fr">
<div class="grid-item">1fr</div>
<div class="grid-item">2fr</div>
<div class="grid-item">1fr</div>
<div class="grid-item">1fr</div>
<div class="grid-item">2fr</div>
<div class="grid-item">1fr</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 10px;
}
</div>
</section>
<!-- ====== 3. EQUAL COLUMNS ====== -->
<section>
<h2>3. Equal Columns (repeat)</h2>
<p>Three equal columns using <code>repeat(3, 1fr)</code>.</p>
<div class="grid-container grid-equal">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
</div>
</section>
<!-- ====== 4. REPEAT WITH FIXED SIZE ====== -->
<section>
<h2>4. Repeat with Fixed Size</h2>
<p>Three columns of exactly 100px each using <code>repeat(3, 100px)</code>.</p>
<div class="grid-container grid-repeat">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: repeat(3, 100px);
gap: 10px;
}
</div>
</section>
<!-- ====== 5. MINMAX ====== -->
<section>
<h2>5. minmax()</h2>
<p>First column: <code>minmax(100px, 200px)</code>, second column: <code>auto</code>.</p>
<div class="grid-container grid-minmax">
<div class="grid-item">minmax(100px, 200px)</div>
<div class="grid-item">auto (remaining space)</div>
<div class="grid-item">1</div>
<div class="grid-item">2</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: minmax(100px, 200px) auto;
gap: 10px;
}
</div>
</section>
<!-- ====== 6. ROWS ====== -->
<section>
<h2>6. Grid with Rows</h2>
<p>Two columns and two rows with different heights.</p>
<div class="grid-container grid-rows">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 100px 150px;
gap: 10px;
}
</div>
</section>
<!-- ====== 7. RESPONSIVE GRID ====== -->
<section>
<h2>7. Responsive Grid (auto-fit)</h2>
<p>Columns automatically wrap based on available space using <code>repeat(auto-fit, minmax(150px, 1fr))</code>.</p>
<div class="grid-container grid-responsive">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
}
</div>
<p class="note">Resize the browser to see the columns wrap automatically.</p>
</section>
<!-- ====== REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>Grid Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>display</code></td>
<td>Enables grid layout</td>
<td><code>display: grid;</code></td>
</tr>
<tr>
<td><code>grid-template-columns</code></td>
<td>Defines columns</td>
<td><code>grid-template-columns: 1fr 1fr;</code></td>
</tr>
<tr>
<td><code>grid-template-rows</code></td>
<td>Defines rows</td>
<td><code>grid-template-rows: 100px auto;</code></td>
</tr>
<tr>
<td><code>gap</code> / <code>grid-gap</code></td>
<td>Sets gap between rows and columns</td>
<td><code>gap: 10px;</code></td>
</tr>
</table>
<h3>Grid Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed size (px, em, rem)</td>
<td><code>100px</code></td>
</tr>
<tr>
<td><code>percentage</code></td>
<td>Percentage of container</td>
<td><code>50%</code></td>
</tr>
<tr>
<td><code>fr</code></td>
<td>Fraction of available space</td>
<td><code>1fr</code>, <code>2fr</code></td>
</tr>
<tr>
<td><code>auto</code></td>
<td>Remaining space</td>
<td><code>auto</code></td>
</tr>
<tr>
<td><code>min-content</code></td>
<td>Minimum possible space</td>
<td><code>min-content</code></td>
</tr>
<tr>
<td><code>max-content</code></td>
<td>Maximum possible space</td>
<td><code>max-content</code></td>
</tr>
<tr>
<td><code>minmax(min, max)</code></td>
<td>Minimum and maximum dimensions</td>
<td><code>minmax(100px, 200px)</code></td>
</tr>
<tr>
<td><code>repeat(n, size)</code></td>
<td>Repeats a pattern</td>
<td><code>repeat(3, 1fr)</code></td>
</tr>
</table>
<h3>repeat() Variations</h3>
<table class="reference-table">
<tr>
<th>Example</th>
<th>Description</th>
</tr>
<tr>
<td><code>repeat(3, 1fr)</code></td>
<td>Three equal fractional columns</td>
</tr>
<tr>
<td><code>repeat(3, 100px)</code></td>
<td>Three fixed 100px columns</td>
</tr>
<tr>
<td><code>repeat(auto-fit, minmax(150px, 1fr))</code></td>
<td>Responsive columns that wrap</td>
</tr>
<tr>
<td><code>repeat(auto-fill, minmax(150px, 1fr))</code></td>
<td>Like auto-fit, but keeps empty tracks</td>
</tr>
</table>
</section>
<!-- ====== BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>fr</code> units for flexible, responsive columns</li>
<li>Use <code>repeat()</code> to avoid repetitive code</li>
<li>Use <code>minmax()</code> with <code>auto-fit</code> for responsive grids</li>
<li>Use <code>gap</code> instead of margins on grid items</li>
<li>Use <code>grid-template-areas</code> for complex layouts (next lesson)</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use fixed pixel widths for responsive layouts</li>
<li>Don't use margins on grid items (use gap instead)</li>
<li>Don't forget <code>display: grid</code> — nothing will work without it</li>
<li>Don't overcomplicate — Grid is powerful but start simple</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Example |
|---|---|---|
display: grid | Enables grid layout | display: grid; |
grid-template-columns | Defines columns | grid-template-columns: 1fr 2fr 1fr; |
grid-template-rows | Defines rows | grid-template-rows: 100px auto; |
gap / grid-gap | Gap between cells | gap: 10px; |
Grid Values
| Value | Description | Example |
|---|---|---|
length | Fixed size | 100px |
percentage | Percentage of container | 50% |
fr | Fraction of available space | 1fr, 2fr |
auto | Remaining space | auto |
min-content | Minimum possible space | min-content |
max-content | Maximum possible space | max-content |
minmax(min, max) | Min and max dimensions | minmax(100px, 200px) |
repeat(n, size) | Repeats a pattern | repeat(3, 1fr) |
Best Practices
✅ Do This:
/* Responsive grid that wraps automatically */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
/* Use fr for flexible columns */
.layout {
display: grid;
grid-template-columns: 200px 1fr;
gap: 15px;
}
❌ Don’t Do This:
/* Don't use fixed widths for responsive layouts */
.grid {
grid-template-columns: 300px 300px 300px;
/* Breaks on smaller screens */
}
/* Don't use margins on grid items */
.grid-item {
margin: 10px; /* Use gap on the container instead */
}
Pro Tip: CSS Grid is the most powerful layout system in CSS. Start with display: grid and grid-template-columns, then add rows and gaps. Use repeat(auto-fit, minmax(250px, 1fr)) for a responsive grid that automatically adjusts to the screen size. In the next lesson, you’ll learn about grid-template-areas for even more complex layouts!
19. grid-template-areas, grid-area, and grid-template
These three properties take CSS Grid to the next level, allowing you to create named grid areas, precisely position items, and use a shorthand for the entire grid definition.
Overview of Properties
| Property | Description |
|---|---|
grid-template-areas | Defines named grid areas using a visual map |
grid-area | Positions a grid item in the grid (or assigns it to a named area) |
grid-template | Shorthand for grid-template-columns, grid-template-rows, and grid-template-areas |
1. grid-template-areas
The grid-template-areas property defines named grid areas using a visual map of strings. Each string represents a row, and each word represents a cell.
.grid-container {
display: grid;
grid-template-columns: repeat(3, 200px);
grid-template-rows: 50px auto 70px;
grid-template-areas:
"header header header"
"sidebar main content"
"footer footer footer";
gap: 10px;
padding: 10px;
}
Visual Map:
┌─────────────────────────────────────┐
│ header │
├──────────┬──────────┬───────────────┤
│ sidebar │ main │ content │
├──────────┴──────────┴───────────────┤
│ footer │
└─────────────────────────────────────┘
Assigning Items to Areas
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.content { grid-area: content; }
.footer { grid-area: footer; }
Null Tokens
Use a sequence of full stops (.) to represent empty cells:
.grid-container {
grid-template-areas:
"header header header"
"sidebar . content"
"footer footer footer";
}
The middle cell (between sidebar and content) is empty.
2. grid-area
The grid-area property positions a grid item using either:
- A named area (from
grid-template-areas) - Row/column start and end positions
Syntax (Numeric):
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
| Position | Required? | Description |
|---|---|---|
| 1st value | Optional | Start row |
| 2nd value | Optional | Start column |
| 3rd value | Required | End row |
| 4th value | Optional | End column |
Examples:
/* Spans 3 rows, 1 column (starts at row 1, col 1) */
.item1 {
grid-area: 1 / 1 / span 3 / 2;
}
/* Spans 2 rows, 2 columns (starts at row 1, col 3) */
.item2 {
grid-area: 1 / 3 / span 2 / span 2;
}
/* Spans 1 row, 2 columns (starts at row 2, col 1) */
.item3 {
grid-area: 2 / 1 / 3 / 3;
}
Using span:
span 3— spans 3 tracks from the start positionspan 2— spans 2 tracks from the start position
3. grid-template (Shorthand)
The grid-template property is a shorthand for:
grid-template-columnsgrid-template-rowsgrid-template-areas
.grid-container-template {
display: grid;
grid-template:
"header header" auto
"sidebar main" 200px / 1fr 3fr;
gap: 10px;
padding: 10px;
}
Syntax:
grid-template:
"area area" <row-size>
"area area" <row-size> / <column-sizes>;
Breaking It Down:
| Part | Description |
|---|---|
"header header" auto | First row: header spans 2 columns, row height is auto |
"sidebar main" 200px | Second row: sidebar and main, row height is 200px |
/ 1fr 3fr | Column sizes: 1fr for sidebar, 3fr for main |
Visual Layout:
┌──────────────┬─────────────────────────┐
│ header (auto) │
├──────────────┼─────────────────────────┤
│ sidebar │ main │
│ (1fr) │ (3fr) │
│ (200px) │ (200px) │
└──────────────┴─────────────────────────┘
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid-template-areas, grid-area, grid-template</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== GRID AREA DEMO ====== */
.grid-container {
display: grid;
grid-template-columns: repeat(3, 200px);
grid-template-rows: 50px auto 70px;
grid-template-areas:
"header header header"
"sidebar main content"
"footer footer footer";
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
max-width: 650px;
}
.cell {
border: 2px solid #333;
padding: 15px;
border-radius: 4px;
font-weight: bold;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.header { grid-area: header; background-color: #f0e68c; }
.sidebar { grid-area: sidebar; background-color: #add8e6; }
.main { grid-area: main; background-color: #90ee90; }
.content { grid-area: content; background-color: #b0c4de; }
.footer { grid-area: footer; background-color: #f5deb3; }
/* ====== GRID AREA NUMERIC DEMO ====== */
.grid-container-area {
display: grid;
grid-template-columns: repeat(3, 200px);
grid-template-rows: 50px auto 70px;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
max-width: 650px;
}
.item1 {
grid-area: 1 / 1 / span 3 / 2;
background-color: #f0e68c;
}
.item2 {
grid-area: 1 / 3 / span 2 / span 2;
background-color: #add8e6;
}
.item3 {
grid-area: 2 / 1 / 3 / 3;
background-color: #90ee90;
}
/* ====== GRID TEMPLATE SHORTHAND ====== */
.grid-container-template {
display: grid;
grid-template:
"header header" auto
"sidebar main" 200px / 1fr 3fr;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
max-width: 650px;
}
.header, .sidebar {
background-color: #f0e68c;
}
.main {
background-color: #add8e6;
}
/* ====== NULL TOKEN DEMO ====== */
.grid-null {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 80px);
grid-template-areas:
"header header header"
"sidebar . content"
"footer footer footer";
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
max-width: 650px;
}
.grid-null .header { grid-area: header; background-color: #f0e68c; }
.grid-null .sidebar { grid-area: sidebar; background-color: #add8e6; }
.grid-null .content { grid-area: content; background-color: #b0c4de; }
.grid-null .footer { grid-area: footer; background-color: #f5deb3; }
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
text-align: center;
}
</style>
</head>
<body>
<h1>grid-template-areas, grid-area, and grid-template</h1>
<!-- ====== 1. GRID-TEMPLATE-AREAS ====== -->
<section>
<h2>1. grid-template-areas (Named Areas)</h2>
<p>Define a visual map of your layout using named areas in double quotes.</p>
<div class="grid-container">
<div class="cell header">Header</div>
<div class="cell sidebar">Sidebar</div>
<div class="cell main">Main Content</div>
<div class="cell content">Additional Content</div>
<div class="cell footer">Footer</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: repeat(3, 200px);
grid-template-rows: 50px auto 70px;
grid-template-areas:
"header header header"
"sidebar main content"
"footer footer footer";
gap: 10px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.content { grid-area: content; }
.footer { grid-area: footer; }
</div>
</section>
<!-- ====== 2. NULL TOKENS ====== -->
<section>
<h2>2. Null Tokens (Empty Cells)</h2>
<p>Use full stops (<code>.</code>) to represent empty cells in the grid.</p>
<div class="grid-null">
<div class="cell header">Header</div>
<div class="cell sidebar">Sidebar</div>
<div class="cell content">Content</div>
<div class="cell footer">Footer</div>
</div>
<p class="note">The middle cell (between sidebar and content) is empty.</p>
<div class="code-block">
.grid-null {
grid-template-areas:
"header header header"
"sidebar . content" /* . = empty cell */
"footer footer footer";
}
</div>
</section>
<!-- ====== 3. GRID-AREA (NUMERIC) ====== -->
<section>
<h2>3. grid-area (Numeric Positioning)</h2>
<p>Position items using row/column start and end values.</p>
<div class="grid-container-area">
<div class="cell item1">Item 1 (spans 3 rows)</div>
<div class="cell item2">Item 2 (spans 2 rows × 2 cols)</div>
<div class="cell item3">Item 3 (spans 1 row × 2 cols)</div>
</div>
<div class="code-block">
/* Starts at row 1, col 1 — spans 3 rows, 1 column */
.item1 { grid-area: 1 / 1 / span 3 / 2; }
/* Starts at row 1, col 3 — spans 2 rows, 2 columns */
.item2 { grid-area: 1 / 3 / span 2 / span 2; }
/* Starts at row 2, col 1 — spans 1 row, 2 columns */
.item3 { grid-area: 2 / 1 / 3 / 3; }
</div>
</section>
<!-- ====== 4. GRID-TEMPLATE (SHORTHAND) ====== -->
<section>
<h2>4. grid-template (Shorthand)</h2>
<p>Combines columns, rows, and areas into a single declaration.</p>
<div class="grid-container-template">
<div class="cell header">Header</div>
<div class="cell sidebar">Sidebar</div>
<div class="cell main">Main Content</div>
</div>
<div class="code-block">
.grid-container-template {
display: grid;
grid-template:
"header header" auto
"sidebar main" 200px / 1fr 3fr;
gap: 10px;
}
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>Grid Area Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>grid-template-areas</code></td>
<td>Defines named grid areas</td>
<td><code>"header header" "sidebar main"</code></td>
</tr>
<tr>
<td><code>grid-area</code></td>
<td>Positions an item (named or numeric)</td>
<td><code>grid-area: header;</code></td>
</tr>
<tr>
<td><code>grid-area</code></td>
<td>Positions an item (numeric)</td>
<td><code>grid-area: 1 / 1 / span 3 / 2;</code></td>
</tr>
<tr>
<td><code>grid-template</code></td>
<td>Shorthand for columns, rows, and areas</td>
<td><code>grid-template: "header" auto / 1fr 2fr;</code></td>
</tr>
</table>
<h3>grid-area Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Position</th>
<th>Required?</th>
</tr>
<tr>
<td>1st value</td>
<td>Start row</td>
<td>Optional</td>
</tr>
<tr>
<td>2nd value</td>
<td>Start column</td>
<td>Optional</td>
</tr>
<tr>
<td>3rd value</td>
<td>End row</td>
<td>Required</td>
</tr>
<tr>
<td>4th value</td>
<td>End column</td>
<td>Optional</td>
</tr>
</table>
<h3>grid-template Shorthand Breakdown</h3>
<table class="reference-table">
<tr>
<th>Part</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>"header header" auto</code></td>
<td>Row 1: named areas + row height</td>
<td>Header spans 2 columns, auto height</td>
</tr>
<tr>
<td><code>"sidebar main" 200px</code></td>
<td>Row 2: named areas + row height</td>
<td>Sidebar and main, 200px height</td>
</tr>
<tr>
<td><code>/ 1fr 3fr</code></td>
<td>Column sizes</td>
<td>Column 1 = 1fr, Column 2 = 3fr</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>grid-template-areas</code> for clear, readable layouts</li>
<li>Use meaningful area names (header, sidebar, main, footer)</li>
<li>Use <code>grid-template</code> shorthand for concise code</li>
<li>Use null tokens (<code>.</code>) for empty cells</li>
<li>Use <code>span</code> for spanning multiple tracks</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use numeric <code>grid-area</code> when named areas would be clearer</li>
<li>Don't forget that <code>grid-template-areas</code> must form a complete rectangle</li>
<li>Don't use <code>grid-template</code> shorthand without understanding the order</li>
<li>Don't mix named and numeric <code>grid-area</code> on the same item</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Example |
|---|---|---|
grid-template-areas | Defines named grid areas | "header header" "sidebar main" |
grid-area (named) | Assigns item to a named area | grid-area: header; |
grid-area (numeric) | Positions item by row/column | grid-area: 1 / 1 / span 3 / 2; |
grid-template | Shorthand for columns, rows, areas | grid-template: "header" auto / 1fr 2fr; |
grid-area Numeric Syntax
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
| Position | Required? | Description |
|---|---|---|
| 1st | Optional | Start row |
| 2nd | Optional | Start column |
| 3rd | Required | End row |
| 4th | Optional | End column |
Best Practices
✅ Do This:
/* Named areas — clear and readable */
.layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
/* Numeric positioning for precise control */
.item {
grid-area: 1 / 2 / span 2 / span 3;
}
❌ Don’t Do This:
/* Don't use incomplete area maps */
.layout {
grid-template-areas:
"header header"
"sidebar main"
"footer"; /* Invalid! Must form a complete rectangle */
}
/* Don't mix named and numeric on the same item */
.item {
grid-area: header; /* Named */
grid-area: 1 / 1 / 2 / 2; /* Numeric — overrides the named area */
}
Pro Tip: grid-template-areas is the most readable way to create complex layouts. Name your areas semantically (header, sidebar, main, footer) so the layout is self-documenting. Use null tokens (.) for empty cells. The grid-template shorthand combines everything into one declaration, but use it only when you’re comfortable with the syntax — it can be harder to read and maintain than separate properties!
20. grid-auto-columns, grid-auto-rows, and grid-auto-flow
These three properties control how auto-placed grid items are sized and positioned. They’re essential for handling grids where the number of items isn’t known in advance or where you want automatic sizing rules.
Overview of Properties
| Property | Description |
|---|---|
grid-auto-columns | Sets the default size for auto-placed columns |
grid-auto-rows | Sets the default size for auto-placed rows |
grid-auto-flow | Controls how auto-placed items flow into the grid |
1. grid-auto-columns
The grid-auto-columns property defines the default size for auto-placed columns — columns that are created implicitly when items are placed outside the explicitly defined grid.
.grid-container {
display: grid;
grid-template-rows: 50px auto 70px;
grid-auto-columns: 200px;
}
What happens:
- The grid has 3 explicit rows
- No explicit columns are defined
- Items are auto-placed, creating implicit columns
- Each implicit column is 200px wide
Values
| Value | Description | Example |
|---|---|---|
length | Fixed size (px, cm, em) | grid-auto-columns: 200px; |
percentage | Percentage of container | grid-auto-columns: 25%; |
fr | Fraction of available space | grid-auto-columns: 1fr; |
minmax(min, max) | Min and max dimensions | grid-auto-columns: minmax(100px, 1fr); |
min-content | Minimum possible space | grid-auto-columns: min-content; |
max-content | Maximum possible space | grid-auto-columns: max-content; |
fit-content | Fits content within limits | grid-auto-columns: fit-content(200px); |
auto | Automatic sizing | grid-auto-columns: auto; |
Important: This property only applies to auto-placed items, not to items that are explicitly positioned.
2. grid-auto-rows
The grid-auto-rows property defines the default size for auto-placed rows — rows that are created implicitly when items are placed outside the explicitly defined grid.
.grid-container-rows {
display: grid;
grid-template-columns: 200px auto;
grid-auto-rows: 50px;
}
What happens:
- The grid has 2 explicit columns
- No explicit rows are defined
- Items are auto-placed, creating implicit rows
- Each implicit row is 50px tall
Values
Same as grid-auto-columns:length, percentage, fr, minmax(), min-content, max-content, fit-content(), auto
3. grid-auto-flow
The grid-auto-flow property specifies how auto-placed items are placed in the grid.
.grid-container-flow {
display: grid;
grid-template-rows: repeat(3, 50px);
grid-template-columns: repeat(2, 200px);
grid-auto-flow: row dense;
}
Values
| Value | Description |
|---|---|
row | Items fill rows first (default) |
column | Items fill columns first |
row dense | Fills rows, and “dense” packs items to fill holes |
column dense | Fills columns, and “dense” packs items to fill holes |
row vs column
grid-auto-flow: row (default):
Item 1 Item 2 Item 3
Item 4 Item 5 Item 6
grid-auto-flow: column:
Item 1 Item 3 Item 5
Item 2 Item 4 Item 6
The dense Keyword
The dense keyword tells the grid to fill in holes left by items that span multiple tracks. Without dense, gaps may remain.
Without dense:
┌─────┬─────┬─────┐
│ 1 │ 2 │ 3 │
├─────┼─────┴─────┤
│ 4 │ 5 │
├─────┼─────┬─────┤
│ 6 │ │ │
└─────┴─────┴─────┘
↑ gap remains
With dense:
┌─────┬─────┬─────┐
│ 1 │ 2 │ 3 │
├─────┼─────┴─────┤
│ 4 │ 5 │
├─────┼─────┬─────┤
│ 6 │ 7 │ 8 │
└─────┴─────┴─────┘
↑ holes filled
Note: dense can change the visual order of items, which may affect accessibility. Use it carefully.
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid-auto-columns, grid-auto-rows, grid-auto-flow</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
/* ====== GRID DEMOS ====== */
.cell {
border: 2px solid #333;
padding: 15px;
border-radius: 4px;
font-weight: bold;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
background: #007bff;
color: white;
}
.cell:nth-child(even) {
background: #28a745;
}
.cell:nth-child(3n) {
background: #dc3545;
}
/* grid-auto-columns demo */
.grid-auto-cols {
display: grid;
grid-template-rows: 50px auto 70px;
grid-auto-columns: 200px;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
/* grid-auto-rows demo */
.grid-auto-rows {
display: grid;
grid-template-columns: 200px auto;
grid-auto-rows: 50px;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
/* grid-auto-flow: row */
.grid-flow-row {
display: grid;
grid-template-rows: repeat(3, 50px);
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: row;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
/* grid-auto-flow: column */
.grid-flow-column {
display: grid;
grid-template-rows: repeat(3, 50px);
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: column;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
/* grid-auto-flow: row dense */
.grid-flow-dense {
display: grid;
grid-template-rows: repeat(3, 60px);
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: row dense;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.grid-flow-dense .span-2 {
grid-column: span 2;
}
.grid-flow-dense .span-3 {
grid-column: span 3;
}
/* Non-dense for comparison */
.grid-flow-no-dense {
display: grid;
grid-template-rows: repeat(3, 60px);
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: row;
gap: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.grid-flow-no-dense .span-2 {
grid-column: span 2;
}
.grid-flow-no-dense .span-3 {
grid-column: span 3;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
</style>
</head>
<body>
<h1>grid-auto-columns, grid-auto-rows, and grid-auto-flow</h1>
<!-- ====== 1. GRID-AUTO-COLUMNS ====== -->
<section>
<h2>1. grid-auto-columns</h2>
<p>Defines the default size for <strong>auto-placed columns</strong>. Only one explicit property is set: <code>grid-template-rows</code>.</p>
<div class="grid-auto-cols">
<div class="cell">Item 1</div>
<div class="cell">Item 2</div>
<div class="cell">Item 3</div>
<div class="cell">Item 4</div>
<div class="cell">Item 5</div>
<div class="cell">Item 6</div>
</div>
<p class="note">Each auto-created column is <span class="highlight">200px</span> wide.</p>
<div class="code-block">
.grid-auto-cols {
display: grid;
grid-template-rows: 50px auto 70px;
grid-auto-columns: 200px;
gap: 10px;
}
</div>
</section>
<!-- ====== 2. GRID-AUTO-ROWS ====== -->
<section>
<h2>2. grid-auto-rows</h2>
<p>Defines the default size for <strong>auto-placed rows</strong>. Only one explicit property is set: <code>grid-template-columns</code>.</p>
<div class="grid-auto-rows">
<div class="cell">Item 1</div>
<div class="cell">Item 2</div>
<div class="cell">Item 3</div>
<div class="cell">Item 4</div>
<div class="cell">Item 5</div>
<div class="cell">Item 6</div>
</div>
<p class="note">Each auto-created row is <span class="highlight">50px</span> tall.</p>
<div class="code-block">
.grid-auto-rows {
display: grid;
grid-template-columns: 200px auto;
grid-auto-rows: 50px;
gap: 10px;
}
</div>
</section>
<!-- ====== 3. GRID-AUTO-FLOW: ROW ====== -->
<section>
<h2>3. grid-auto-flow: row (default)</h2>
<p>Items fill <strong>rows first</strong>, then wrap to the next row.</p>
<div class="grid-flow-row">
<div class="cell">1</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
</div>
<div class="code-block">
.grid-flow-row {
display: grid;
grid-template-rows: repeat(3, 50px);
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: row; /* Default */
gap: 10px;
}
</div>
</section>
<!-- ====== 4. GRID-AUTO-FLOW: COLUMN ====== -->
<section>
<h2>4. grid-auto-flow: column</h2>
<p>Items fill <strong>columns first</strong>, then wrap to the next column.</p>
<div class="grid-flow-column">
<div class="cell">1</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
</div>
<div class="code-block">
.grid-flow-column {
display: grid;
grid-template-rows: repeat(3, 50px);
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: column; /* Fills columns first */
gap: 10px;
}
</div>
</section>
<!-- ====== 5. GRID-AUTO-FLOW: DENSE ====== -->
<section>
<h2>5. grid-auto-flow: row dense</h2>
<p>When items span multiple tracks, <code>dense</code> fills in the gaps left behind.</p>
<h3>Without dense (gaps remain):</h3>
<div class="grid-flow-no-dense">
<div class="cell span-2">1 (span 2)</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell span-3">4 (span 3)</div>
<div class="cell">5</div>
<div class="cell">6</div>
</div>
<p class="note">Gap remains in the first row because item 3 doesn't fit.</p>
<h3>With dense (gaps filled):</h3>
<div class="grid-flow-dense">
<div class="cell span-2">1 (span 2)</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell span-3">4 (span 3)</div>
<div class="cell">5</div>
<div class="cell">6</div>
</div>
<p class="note">Item 3 moves up to fill the gap, making the grid more compact.</p>
<div class="code-block">
/* Without dense — gaps may remain */
.grid {
grid-auto-flow: row;
}
/* With dense — fills in holes */
.grid {
grid-auto-flow: row dense;
}
/* Column dense */
.grid {
grid-auto-flow: column dense;
}
</div>
</section>
<!-- ====== 6. COMPARISON: ROW vs COLUMN ====== -->
<section>
<h2>6. Side-by-Side Comparison</h2>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 30px;">
<div>
<h4>grid-auto-flow: row</h4>
<div class="grid-flow-row" style="grid-template-rows: repeat(2, 50px); grid-template-columns: repeat(3, 1fr);">
<div class="cell">1</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
</div>
<p class="note">Order: 1, 2, 3, 4, 5, 6 (left to right, top to bottom)</p>
</div>
<div>
<h4>grid-auto-flow: column</h4>
<div class="grid-flow-column" style="grid-template-rows: repeat(2, 50px); grid-template-columns: repeat(3, 1fr);">
<div class="cell">1</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
</div>
<p class="note">Order: 1, 2, 3, 4, 5, 6 (top to bottom, left to right)</p>
</div>
</div>
</section>
<!-- ====== 7. REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>Auto Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>grid-auto-columns</code></td>
<td>Default size for auto-placed columns</td>
<td><code>grid-auto-columns: 200px;</code></td>
</tr>
<tr>
<td><code>grid-auto-rows</code></td>
<td>Default size for auto-placed rows</td>
<td><code>grid-auto-rows: 50px;</code></td>
</tr>
<tr>
<td><code>grid-auto-flow</code></td>
<td>How auto-placed items flow</td>
<td><code>grid-auto-flow: row dense;</code></td>
</tr>
</table>
<h3>grid-auto-flow Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>row</code></td>
<td>Items fill rows first (default)</td>
</tr>
<tr>
<td><code>column</code></td>
<td>Items fill columns first</td>
</tr>
<tr>
<td><code>row dense</code></td>
<td>Fills rows and packs items to fill holes</td>
</tr>
<tr>
<td><code>column dense</code></td>
<td>Fills columns and packs items to fill holes</td>
</tr>
</table>
<h3>Values for grid-auto-columns and grid-auto-rows</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed size (px, cm, em)</td>
<td><code>200px</code></td>
</tr>
<tr>
<td><code>percentage</code></td>
<td>Percentage of container</td>
<td><code>25%</code></td>
</tr>
<tr>
<td><code>fr</code></td>
<td>Fraction of available space</td>
<td><code>1fr</code></td>
</tr>
<tr>
<td><code>minmax(min, max)</code></td>
<td>Min and max dimensions</td>
<td><code>minmax(100px, 1fr)</code></td>
</tr>
<tr>
<td><code>min-content</code></td>
<td>Minimum possible space</td>
<td><code>min-content</code></td>
</tr>
<tr>
<td><code>max-content</code></td>
<td>Maximum possible space</td>
<td><code>max-content</code></td>
</tr>
<tr>
<td><code>fit-content(limit)</code></td>
<td>Fits content within limits</td>
<td><code>fit-content(200px)</code></td>
</tr>
<tr>
<td><code>auto</code></td>
<td>Automatic sizing</td>
<td><code>auto</code></td>
</tr>
</table>
</section>
<!-- ====== 8. BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>grid-auto-rows</code> to control the height of auto-created rows</li>
<li>Use <code>grid-auto-columns</code> to control the width of auto-created columns</li>
<li>Use <code>grid-auto-flow: row dense</code> to fill gaps in complex layouts</li>
<li>Use <code>grid-auto-flow: column</code> for vertical layouts (e.g., navigation)</li>
<li>Test with different content sizes to ensure the grid behaves as expected</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>grid-auto-columns</code> when you've defined all columns explicitly</li>
<li>Don't use <code>dense</code> without considering the visual order change</li>
<li>Don't forget that <code>grid-auto-flow</code> only affects <strong>auto-placed</strong> items</li>
<li>Don't rely solely on <code>dense</code> for accessibility — visual order should match DOM order when possible</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Example |
|---|---|---|
grid-auto-columns | Default size for auto-placed columns | grid-auto-columns: 200px; |
grid-auto-rows | Default size for auto-placed rows | grid-auto-rows: 50px; |
grid-auto-flow | How auto-placed items flow | grid-auto-flow: row dense; |
grid-auto-flow Values
| Value | Description |
|---|---|
row | Items fill rows first (default) |
column | Items fill columns first |
row dense | Fills rows and packs items to fill holes |
column dense | Fills columns and packs items to fill holes |
Best Practices
✅ Do This:
/* Control the height of auto-created rows */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 100px; /* Each auto row is 100px */
}
/* Fill gaps with dense */
.grid {
grid-auto-flow: row dense;
}
/* Vertical layout with column flow */
.nav {
display: grid;
grid-auto-flow: column;
gap: 10px;
}
❌ Don’t Do This:
/* Don't use auto-columns when columns are defined */
.grid {
grid-template-columns: 1fr 1fr;
grid-auto-columns: 200px; /* Only affects extra columns */
}
/* Don't use dense without understanding the trade-offs */
.grid {
grid-auto-flow: dense; /* Can change visual order */
}
Pro Tip: grid-auto-rows and grid-auto-columns are essential when you don’t know how many items your grid will contain (e.g., dynamic content from a database). Use grid-auto-flow: row dense to compact your grid and fill holes — but be aware that dense can change the visual order of items, which may confuse users relying on screen readers. Always test your grid with different content sizes!
21. grid-row-start, grid-column-start, grid-row-end, and grid-column-end
These four properties give you precise control over where grid items are placed and how many tracks they span. They are the individual longhand properties that grid-area and grid-row/grid-column shorthand properties are based on.
Overview of Properties
| Property | Description |
|---|---|
grid-row-start | Specifies where a grid item starts within a row |
grid-row-end | Specifies where a grid item ends within a row |
grid-column-start | Specifies where a grid item starts within a column |
grid-column-end | Specifies where a grid item ends within a column |
1. grid-row-start
Specifies the starting row line for a grid item.
.first-item {
grid-row-start: 1; /* Starts at row line 1 */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Next available row (default) | grid-row-start: auto; |
integer | Specific row line number | grid-row-start: 2; |
span #n | Spans n rows from the start | grid-row-start: span 2; |
2. grid-row-end
Specifies the ending row line for a grid item.
.first-item {
grid-row-start: 1;
grid-row-end: 3; /* Ends at row line 3 (spans rows 1 and 2) */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Next available row (default) | grid-row-end: auto; |
integer | Specific row line number | grid-row-end: 4; |
span #n | Spans n rows from the start | grid-row-end: span 2; |
Important: The end line is exclusive — grid-row-end: 3 means the item ends before line 3 (i.e., it occupies rows 1 and 2).
3. grid-column-start
Specifies the starting column line for a grid item.
.second-item {
grid-column-start: 2; /* Starts at column line 2 */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Next available column (default) | grid-column-start: auto; |
integer | Specific column line number | grid-column-start: 3; |
span #n | Spans n columns from the start | grid-column-start: span 2; |
4. grid-column-end
Specifies the ending column line for a grid item.
.second-item {
grid-column-start: 2;
grid-column-end: 4; /* Ends at column line 4 (spans columns 2 and 3) */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Next available column (default) | grid-column-end: auto; |
integer | Specific column line number | grid-column-end: 4; |
span #n | Spans n columns from the start | grid-column-end: span 2; |
Visualizing Grid Lines
In a 3×2 grid, there are 4 column lines and 3 row lines:
Column Lines: 1 2 3 4
│ │ │ │
Row Line 1 ───┼────────┼────────┼────────┤
│ Cell 1 │ Cell 2 │ Cell 3 │
Row Line 2 ───┼────────┼────────┼────────┤
│ Cell 4 │ Cell 5 │ Cell 6 │
Row Line 3 ───┴────────┴────────┴────────┘
- A cell that starts at row line 1 and ends at row line 2 occupies 1 row
- A cell that starts at row line 1 and ends at row line 3 occupies 2 rows
Using span
Instead of specifying the end line, you can use span to indicate how many tracks the item should cover.
/* These two are equivalent */
.item {
grid-row-start: 1;
grid-row-end: 3;
}
.item {
grid-row-start: 1;
grid-row-end: span 2; /* Spans 2 rows from row line 1 */
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid-row-start, grid-column-start, grid-row-end, grid-column-end</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== GRID DEMOS ====== */
.grid-container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 100px);
gap: 5px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
width: fit-content;
}
.cell {
border: 2px solid #333;
padding: 10px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
font-size: 0.85rem;
background: #007bff;
color: white;
}
.cell:nth-child(even) {
background: #28a745;
}
.cell:nth-child(3n) {
background: #dc3545;
}
/* Demo 1: grid-row-start / grid-row-end */
.grid-rows {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
gap: 5px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
width: fit-content;
}
.grid-rows .span-2-rows {
grid-row-start: 1;
grid-row-end: 3;
background: #6c5ce7;
}
/* Demo 2: grid-column-start / grid-column-end */
.grid-columns {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
gap: 5px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
width: fit-content;
}
.grid-columns .span-2-cols {
grid-column-start: 2;
grid-column-end: 4;
background: #e17055;
}
/* Demo 3: Both row and column */
.grid-both {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
gap: 5px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
width: fit-content;
}
.grid-both .span-both {
grid-row-start: 1;
grid-row-end: 3;
grid-column-start: 2;
grid-column-end: 4;
background: #00b894;
}
/* Demo 4: span keyword */
.grid-span {
display: grid;
grid-template-columns: repeat(4, 80px);
grid-template-rows: repeat(2, 80px);
gap: 5px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px auto;
width: fit-content;
}
.grid-span .span-2 {
grid-column-start: 1;
grid-column-end: span 2;
background: #6c5ce7;
}
.grid-span .span-3 {
grid-column-start: 3;
grid-column-end: span 2;
background: #e17055;
}
/* Grid lines visualization */
.grid-lines {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 100px);
gap: 0;
padding: 0;
background: #fff;
border: 2px solid #333;
margin: 15px auto;
width: fit-content;
position: relative;
}
.grid-lines .cell {
border: 1px dashed #999;
border-radius: 0;
background: #f8f9fa;
color: #333;
font-size: 0.8rem;
}
.grid-lines .cell.line-label {
background: #ffc107;
font-weight: bold;
}
</style>
</head>
<body>
<h1>grid-row-start, grid-column-start, grid-row-end, grid-column-end</h1>
<!-- ====== 1. GRID-ROW-START / GRID-ROW-END ====== -->
<section>
<h2>1. grid-row-start & grid-row-end</h2>
<p>Control where an item <strong>starts</strong> and <strong>ends</strong> within rows.</p>
<div class="grid-rows">
<div class="cell span-2-rows">Spans 2 Rows<br>(start: 1, end: 3)</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
</div>
<div class="code-block">
.span-2-rows {
grid-row-start: 1; /* Starts at row line 1 */
grid-row-end: 3; /* Ends at row line 3 (spans rows 1 & 2) */
}
</div>
</section>
<!-- ====== 2. GRID-COLUMN-START / GRID-COLUMN-END ====== -->
<section>
<h2>2. grid-column-start & grid-column-end</h2>
<p>Control where an item <strong>starts</strong> and <strong>ends</strong> within columns.</p>
<div class="grid-columns">
<div class="cell">1</div>
<div class="cell span-2-cols">Spans 2 Columns<br>(start: 2, end: 4)</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
<div class="cell">8</div>
</div>
<div class="code-block">
.span-2-cols {
grid-column-start: 2; /* Starts at column line 2 */
grid-column-end: 4; /* Ends at column line 4 (spans cols 2 & 3) */
}
</div>
</section>
<!-- ====== 3. BOTH ROW AND COLUMN ====== -->
<section>
<h2>3. Combining Row and Column</h2>
<p>Use all four properties to position an item precisely.</p>
<div class="grid-both">
<div class="cell">1</div>
<div class="cell span-both">Spans 2 Rows × 2 Columns<br>(row: 1→3, col: 2→4)</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
</div>
<div class="code-block">
.span-both {
grid-row-start: 1;
grid-row-end: 3;
grid-column-start: 2;
grid-column-end: 4;
}
</div>
</section>
<!-- ====== 4. THE span KEYWORD ====== -->
<section>
<h2>4. Using the <code>span</code> Keyword</h2>
<p>Instead of specifying the end line, use <code>span</code> to indicate how many tracks to cover.</p>
<div class="grid-span">
<div class="cell span-2">span 2</div>
<div class="cell span-3">span 2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
</div>
<div class="code-block">
/* These two are equivalent */
.item {
grid-column-start: 1;
grid-column-end: span 2; /* Spans 2 columns from start */
}
.item {
grid-column-start: 1;
grid-column-end: 3; /* Same result */
}
</div>
</section>
<!-- ====== 5. GRID LINES VISUALIZATION ====== -->
<section>
<h2>5. Understanding Grid Lines</h2>
<p>In a 3×2 grid, there are <strong>4 column lines</strong> and <strong>3 row lines</strong>.</p>
<div style="display: flex; flex-direction: column; align-items: center;">
<!-- Column line labels -->
<div style="display: flex; gap: 0; margin-bottom: 5px; padding-left: 40px;">
<div style="width: 100px; text-align: center; font-weight: bold; color: #007bff;">Line 1</div>
<div style="width: 100px; text-align: center; font-weight: bold; color: #007bff;">Line 2</div>
<div style="width: 100px; text-align: center; font-weight: bold; color: #007bff;">Line 3</div>
<div style="width: 100px; text-align: center; font-weight: bold; color: #007bff;">Line 4</div>
</div>
<div style="display: flex; align-items: center;">
<!-- Row line labels -->
<div style="display: flex; flex-direction: column; margin-right: 5px;">
<div style="height: 100px; display: flex; align-items: center; font-weight: bold; color: #28a745;">Line 1</div>
<div style="height: 100px; display: flex; align-items: center; font-weight: bold; color: #28a745;">Line 2</div>
<div style="height: 100px; display: flex; align-items: center; font-weight: bold; color: #28a745;">Line 3</div>
</div>
<div class="grid-lines">
<div class="cell">Cell 1</div>
<div class="cell">Cell 2</div>
<div class="cell">Cell 3</div>
<div class="cell">Cell 4</div>
<div class="cell">Cell 5</div>
<div class="cell">Cell 6</div>
</div>
</div>
</div>
<p class="note" style="text-align: center; margin-top: 15px;">
<span class="highlight">Column lines</span> run vertically between columns (1, 2, 3, 4).<br>
<span class="highlight">Row lines</span> run horizontally between rows (1, 2, 3).
</p>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>Grid Placement Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>grid-row-start</code></td>
<td>Starting row line</td>
<td><code>grid-row-start: 1;</code></td>
</tr>
<tr>
<td><code>grid-row-end</code></td>
<td>Ending row line</td>
<td><code>grid-row-end: 3;</code></td>
</tr>
<tr>
<td><code>grid-column-start</code></td>
<td>Starting column line</td>
<td><code>grid-column-start: 2;</code></td>
</tr>
<tr>
<td><code>grid-column-end</code></td>
<td>Ending column line</td>
<td><code>grid-column-end: 4;</code></td>
</tr>
</table>
<h3>Values for All Four Properties</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Next available track (default)</td>
<td><code>grid-row-start: auto;</code></td>
</tr>
<tr>
<td><code>integer</code></td>
<td>Specific line number</td>
<td><code>grid-row-start: 2;</code></td>
</tr>
<tr>
<td><code>span #n</code></td>
<td>Spans n tracks from start</td>
<td><code>grid-row-end: span 2;</code></td>
</tr>
</table>
<h3>Shorthand Equivalents</h3>
<table class="reference-table">
<tr>
<th>Longhand</th>
<th>Shorthand</th>
<th>Example</th>
</tr>
<tr>
<td><code>grid-row-start</code> + <code>grid-row-end</code></td>
<td><code>grid-row</code></td>
<td><code>grid-row: 1 / 3;</code></td>
</tr>
<tr>
<td><code>grid-column-start</code> + <code>grid-column-end</code></td>
<td><code>grid-column</code></td>
<td><code>grid-column: 2 / 4;</code></td>
</tr>
<tr>
<td>All four</td>
<td><code>grid-area</code></td>
<td><code>grid-area: 1 / 2 / 3 / 4;</code></td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>span</code> when you only care about how many tracks an item covers</li>
<li>Use specific line numbers when you need exact placement</li>
<li>Combine row and column properties for precise positioning</li>
<li>Use shorthand <code>grid-row</code>, <code>grid-column</code>, or <code>grid-area</code> for cleaner code</li>
<li>Remember that end lines are <strong>exclusive</strong> (end: 3 means rows 1 and 2)</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't confuse line numbers with track numbers (lines are between tracks)</li>
<li>Don't forget that grid lines start at 1, not 0</li>
<li>Don't use explicit placement when auto-placement would work</li>
<li>Don't mix up <code>start</code> and <code>end</code> — start must be less than end</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Example |
|---|---|---|
grid-row-start | Starting row line | grid-row-start: 1; |
grid-row-end | Ending row line | grid-row-end: 3; |
grid-column-start | Starting column line | grid-column-start: 2; |
grid-column-end | Ending column line | grid-column-end: 4; |
Values
| Value | Description | Example |
|---|---|---|
auto | Next available track (default) | grid-row-start: auto; |
integer | Specific line number | grid-row-start: 2; |
span #n | Spans n tracks from start | grid-row-end: span 2; |
Shorthand Equivalents
| Longhand | Shorthand | Example |
|---|---|---|
grid-row-start + grid-row-end | grid-row | grid-row: 1 / 3; |
grid-column-start + grid-column-end | grid-column | grid-column: 2 / 4; |
| All four | grid-area | grid-area: 1 / 2 / 3 / 4; |
Best Practices
✅ Do This:
/* Use span when you only care about coverage */
.item {
grid-column: 1 / span 2; /* Spans 2 columns from line 1 */
}
/* Use specific lines for precise placement */
.item {
grid-row: 2 / 4; /* Starts at line 2, ends at line 4 */
}
/* Combine row and column for full control */
.item {
grid-area: 1 / 2 / 3 / 4;
}
❌ Don’t Do This:
/* Don't confuse lines with tracks */
.item {
grid-row-start: 0; /* Invalid! Lines start at 1 */
}
/* Don't mix up start and end */
.item {
grid-row-start: 3;
grid-row-end: 1; /* Invalid! Start must be less than end */
}
/* Don't use explicit placement unnecessarily */
.item {
grid-row: 1 / 2; /* Auto-placement would work fine */
}
Pro Tip: Think of grid lines as the borders between cells, not the cells themselves. In a 3-column grid, there are 4 column lines (1, 2, 3, 4). The grid-row-start and grid-column-start properties specify which line the item starts at, while grid-row-end and grid-column-end specify which line it ends before. Use the shorthand properties (grid-row, grid-column, grid-area) for cleaner, more readable code!
22. grid-row and grid-column Properties
The grid-row and grid-column properties are shorthand for the four longhand properties you learned in the previous lesson. They let you control where a grid item starts and ends in both rows and columns.
Overview of Properties
| Property | Description | Shorthand For |
|---|---|---|
grid-row | Specifies start and end row lines | grid-row-start + grid-row-end |
grid-column | Specifies start and end column lines | grid-column-start + grid-column-end |
1. grid-row
The grid-row property specifies where a grid item starts and ends within the grid rows.
.item1 {
grid-row: 1 / 3; /* Starts at row line 1, ends at row line 3 */
}
Syntax
grid-row: <start> / <end>;
grid-row: <start> / span <n>;
grid-row: <start>; /* Only start, end is auto */
Values
| Value | Description | Example |
|---|---|---|
auto | Auto placement (default) | grid-row: auto; |
integer / integer | Start line / End line | grid-row: 1 / 3; |
integer / span #n | Start line / Span n rows | grid-row: 1 / span 2; |
span #n / integer | Span n rows / End line | grid-row: span 2 / 4; |
integer | Start line only | grid-row: 2; |
2. grid-column
The grid-column property specifies where a grid item starts and ends within the grid columns.
.item1 {
grid-column: 2 / 4; /* Starts at column line 2, ends at column line 4 */
}
Syntax
grid-column: <start> / <end>;
grid-column: <start> / span <n>;
grid-column: <start>; /* Only start, end is auto */
Values
| Value | Description | Example |
|---|---|---|
auto | Auto placement (default) | grid-column: auto; |
integer / integer | Start line / End line | grid-column: 2 / 4; |
integer / span #n | Start line / Span n columns | grid-column: 2 / span 2; |
span #n / integer | Span n columns / End line | grid-column: span 2 / 4; |
integer | Start line only | grid-column: 2; |
How It Works
Remember: Grid lines are the borders between cells, not the cells themselves.
In a 4-column grid:
Column Lines: 1 2 3 4 5
│ │ │ │ │
│ Cell 1 │ Cell 2 │ Cell 3 │ Cell 4 │
│ │ │ │ │
grid-column: 2 / 4→ Starts at line 2, ends at line 4 (spans columns 2 and 3)grid-column: 2 / span 2→ Starts at line 2, spans 2 columns (same result)
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid-row and grid-column Properties</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== GRID DEMOS ====== */
.grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 80px);
gap: 10px;
padding: 15px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.cell {
border: 2px solid #333;
padding: 10px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
font-size: 0.85rem;
background: #007bff;
color: white;
}
.cell:nth-child(even) {
background: #28a745;
}
.cell:nth-child(3n) {
background: #dc3545;
}
/* Demo 1: grid-row */
.item-row {
grid-row: 1 / 3;
background: #6c5ce7 !important;
}
/* Demo 2: grid-column */
.item-col {
grid-column: 2 / 4;
background: #e17055 !important;
}
/* Demo 3: Both */
.item-both {
grid-row: 2 / 5;
grid-column: 1 / 3;
background: #00b894 !important;
}
/* Demo 4: span keyword */
.item-span-row {
grid-row: 1 / span 2;
background: #6c5ce7 !important;
}
.item-span-col {
grid-column: 2 / span 2;
background: #e17055 !important;
}
/* Demo 5: multiple items */
.grid-multi {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 80px);
gap: 10px;
padding: 15px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.header-item {
grid-column: 1 / 5;
background: #007bff !important;
}
.sidebar-item {
grid-row: 2 / 4;
grid-column: 1 / 2;
background: #6c5ce7 !important;
}
.main-item {
grid-row: 2 / 4;
grid-column: 2 / 5;
background: #00b894 !important;
}
.footer-item {
grid-column: 1 / 5;
grid-row: 4 / 5;
background: #e17055 !important;
}
/* Grid lines visualization */
.grid-lines {
display: grid;
grid-template-columns: repeat(4, 80px);
grid-template-rows: repeat(3, 80px);
gap: 0;
background: #fff;
border: 2px solid #333;
margin: 15px auto;
width: fit-content;
position: relative;
}
.grid-lines .cell {
border: 1px dashed #999;
border-radius: 0;
background: #f8f9fa;
color: #333;
font-size: 0.75rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.grid-lines .cell .line-num {
font-size: 0.6rem;
color: #007bff;
font-weight: bold;
}
</style>
</head>
<body>
<h1>grid-row and grid-column Properties</h1>
<!-- ====== 1. GRID-ROW ====== -->
<section>
<h2>1. grid-row</h2>
<p>Controls where an item starts and ends in <strong>rows</strong>.</p>
<div class="grid-container">
<div class="cell item-row">grid-row: 1 / 3</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
<div class="cell">8</div>
</div>
<div class="code-block">
.item-row {
grid-row: 1 / 3; /* Starts at row line 1, ends at row line 3 */
}
</div>
</section>
<!-- ====== 2. GRID-COLUMN ====== -->
<section>
<h2>2. grid-column</h2>
<p>Controls where an item starts and ends in <strong>columns</strong>.</p>
<div class="grid-container">
<div class="cell">1</div>
<div class="cell item-col">grid-column: 2 / 4</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
<div class="cell">8</div>
</div>
<div class="code-block">
.item-col {
grid-column: 2 / 4; /* Starts at column line 2, ends at column line 4 */
}
</div>
</section>
<!-- ====== 3. BOTH ROW AND COLUMN ====== -->
<section>
<h2>3. Combining grid-row and grid-column</h2>
<p>Use both properties to position an item precisely in the grid.</p>
<div class="grid-container">
<div class="cell">1</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell item-both">grid-row: 2 / 5<br>grid-column: 1 / 3</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
<div class="cell">8</div>
</div>
<div class="code-block">
.item-both {
grid-row: 2 / 5; /* Spans rows 2, 3, 4 */
grid-column: 1 / 3; /* Spans columns 1, 2 */
}
</div>
</section>
<!-- ====== 4. THE span KEYWORD ====== -->
<section>
<h2>4. Using <code>span</code></h2>
<p>Instead of specifying the end line, use <code>span</code> to indicate how many tracks to cover.</p>
<div class="grid-container">
<div class="cell item-span-row">grid-row: 1 / span 2</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell item-span-col">grid-column: 2 / span 2</div>
<div class="cell">6</div>
<div class="cell">7</div>
<div class="cell">8</div>
</div>
<div class="code-block">
.item-span-row {
grid-row: 1 / span 2; /* Starts at line 1, spans 2 rows */
}
.item-span-col {
grid-column: 2 / span 2; /* Starts at line 2, spans 2 columns */
}
/* These are equivalent to: */
/* grid-row: 1 / 3; */
/* grid-column: 2 / 4; */
</div>
</section>
<!-- ====== 5. PRACTICAL LAYOUT ====== -->
<section>
<h2>5. Practical Layout Example</h2>
<p>A classic header / sidebar / main / footer layout using <code>grid-row</code> and <code>grid-column</code>.</p>
<div class="grid-multi">
<div class="cell header-item">Header (grid-column: 1 / 5)</div>
<div class="cell sidebar-item">Sidebar<br>(grid-row: 2 / 4)<br>(grid-column: 1 / 2)</div>
<div class="cell main-item">Main Content<br>(grid-row: 2 / 4)<br>(grid-column: 2 / 5)</div>
<div class="cell footer-item">Footer (grid-column: 1 / 5)</div>
</div>
<div class="code-block">
.header-item {
grid-column: 1 / 5; /* Full width */
}
.sidebar-item {
grid-row: 2 / 4; /* Spans rows 2 and 3 */
grid-column: 1 / 2; /* First column */
}
.main-item {
grid-row: 2 / 4; /* Spans rows 2 and 3 */
grid-column: 2 / 5; /* Columns 2-4 */
}
.footer-item {
grid-column: 1 / 5; /* Full width */
}
</div>
</section>
<!-- ====== 6. GRID LINES VISUALIZATION ====== -->
<section>
<h2>6. Understanding Grid Lines</h2>
<p>In a 4-column × 3-row grid, there are <strong>5 column lines</strong> and <strong>4 row lines</strong>.</p>
<div style="display: flex; flex-direction: column; align-items: center;">
<!-- Column line labels -->
<div style="display: flex; gap: 0; margin-bottom: 5px; padding-left: 40px;">
<div style="width: 80px; text-align: center; font-weight: bold; color: #007bff;">Line 1</div>
<div style="width: 80px; text-align: center; font-weight: bold; color: #007bff;">Line 2</div>
<div style="width: 80px; text-align: center; font-weight: bold; color: #007bff;">Line 3</div>
<div style="width: 80px; text-align: center; font-weight: bold; color: #007bff;">Line 4</div>
<div style="width: 80px; text-align: center; font-weight: bold; color: #007bff;">Line 5</div>
</div>
<div style="display: flex; align-items: center;">
<!-- Row line labels -->
<div style="display: flex; flex-direction: column; margin-right: 5px;">
<div style="height: 80px; display: flex; align-items: center; font-weight: bold; color: #28a745;">Line 1</div>
<div style="height: 80px; display: flex; align-items: center; font-weight: bold; color: #28a745;">Line 2</div>
<div style="height: 80px; display: flex; align-items: center; font-weight: bold; color: #28a745;">Line 3</div>
<div style="height: 80px; display: flex; align-items: center; font-weight: bold; color: #28a745;">Line 4</div>
</div>
<div class="grid-lines">
<div class="cell">Cell 1<br><span class="line-num">1,1 → 2,2</span></div>
<div class="cell">Cell 2<br><span class="line-num">2,1 → 3,2</span></div>
<div class="cell">Cell 3<br><span class="line-num">3,1 → 4,2</span></div>
<div class="cell">Cell 4<br><span class="line-num">4,1 → 5,2</span></div>
<div class="cell">Cell 5<br><span class="line-num">1,2 → 2,3</span></div>
<div class="cell">Cell 6<br><span class="line-num">2,2 → 3,3</span></div>
<div class="cell">Cell 7<br><span class="line-num">3,2 → 4,3</span></div>
<div class="cell">Cell 8<br><span class="line-num">4,2 → 5,3</span></div>
<div class="cell">Cell 9<br><span class="line-num">1,3 → 2,4</span></div>
<div class="cell">Cell 10<br><span class="line-num">2,3 → 3,4</span></div>
<div class="cell">Cell 11<br><span class="line-num">3,3 → 4,4</span></div>
<div class="cell">Cell 12<br><span class="line-num">4,3 → 5,4</span></div>
</div>
</div>
</div>
<p class="note" style="text-align: center; margin-top: 15px;">
<span class="highlight">Column lines</span> run vertically between columns (1, 2, 3, 4, 5).<br>
<span class="highlight">Row lines</span> run horizontally between rows (1, 2, 3, 4).<br>
Each cell is defined by its start and end lines.
</p>
</section>
<!-- ====== 7. REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>grid-row and grid-column</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>grid-row</code></td>
<td>Shorthand for row start + end</td>
<td><code>grid-row: 1 / 3;</code></td>
</tr>
<tr>
<td><code>grid-column</code></td>
<td>Shorthand for column start + end</td>
<td><code>grid-column: 2 / 4;</code></td>
</tr>
</table>
<h3>Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Auto placement (default)</td>
<td><code>grid-row: auto;</code></td>
</tr>
<tr>
<td><code>integer / integer</code></td>
<td>Start line / End line</td>
<td><code>grid-row: 1 / 3;</code></td>
</tr>
<tr>
<td><code>integer / span #n</code></td>
<td>Start line / Span n tracks</td>
<td><code>grid-row: 1 / span 2;</code></td>
</tr>
<tr>
<td><code>span #n / integer</code></td>
<td>Span n tracks / End line</td>
<td><code>grid-row: span 2 / 4;</code></td>
</tr>
<tr>
<td><code>integer</code></td>
<td>Start line only</td>
<td><code>grid-row: 2;</code></td>
</tr>
</table>
<h3>Longhand vs Shorthand</h3>
<table class="reference-table">
<tr>
<th>Longhand</th>
<th>Shorthand</th>
<th>Example</th>
</tr>
<tr>
<td><code>grid-row-start: 1;<br>grid-row-end: 3;</code></td>
<td><code>grid-row</code></td>
<td><code>grid-row: 1 / 3;</code></td>
</tr>
<tr>
<td><code>grid-column-start: 2;<br>grid-column-end: 4;</code></td>
<td><code>grid-column</code></td>
<td><code>grid-column: 2 / 4;</code></td>
</tr>
<tr>
<td>All four</td>
<td><code>grid-area</code></td>
<td><code>grid-area: 1 / 2 / 3 / 4;</code></td>
</tr>
</table>
</section>
<!-- ====== 8. BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>grid-row</code> and <code>grid-column</code> shorthand instead of the four longhand properties</li>
<li>Use <code>span</code> when you only care about how many tracks an item covers</li>
<li>Use specific line numbers when you need exact placement</li>
<li>Combine row and column properties for precise positioning</li>
<li>Remember that end lines are <strong>exclusive</strong></li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't confuse line numbers with track numbers (lines are between tracks)</li>
<li>Don't forget that grid lines start at 1, not 0</li>
<li>Don't use explicit placement when auto-placement would work</li>
<li>Don't mix up <code>start</code> and <code>end</code> — start must be less than end</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Example |
|---|---|---|
grid-row | Shorthand for row start + end | grid-row: 1 / 3; |
grid-column | Shorthand for column start + end | grid-column: 2 / 4; |
Values
| Value | Description | Example |
|---|---|---|
auto | Auto placement (default) | grid-row: auto; |
integer / integer | Start line / End line | grid-row: 1 / 3; |
integer / span #n | Start line / Span n tracks | grid-row: 1 / span 2; |
span #n / integer | Span n tracks / End line | grid-row: span 2 / 4; |
integer | Start line only | grid-row: 2; |
Longhand vs Shorthand
| Longhand | Shorthand | Example |
|---|---|---|
grid-row-start + grid-row-end | grid-row | grid-row: 1 / 3; |
grid-column-start + grid-column-end | grid-column | grid-column: 2 / 4; |
| All four | grid-area | grid-area: 1 / 2 / 3 / 4; |
Best Practices
✅ Do This:
/* Use span when you only care about coverage */
.item {
grid-row: 1 / span 2; /* Spans 2 rows from line 1 */
}
.item {
grid-column: 2 / span 2; /* Spans 2 columns from line 2 */
}
/* Use specific lines for precise placement */
.item {
grid-row: 2 / 4; /* Starts at line 2, ends at line 4 */
}
/* Combine row and column for full control */
.item {
grid-row: 1 / 3;
grid-column: 2 / 4;
}
❌ Don’t Do This:
/* Don't confuse lines with tracks */
.item {
grid-row: 0 / 2; /* Invalid! Lines start at 1 */
}
/* Don't mix up start and end */
.item {
grid-row: 3 / 1; /* Invalid! Start must be less than end */
}
/* Don't use explicit placement unnecessarily */
.item {
grid-row: 1 / 2; /* Auto-placement would work fine */
}
Pro Tip: The grid-row and grid-column shorthand properties are the most commonly used ways to position grid items. Use span when you want to say “cover this many tracks” without calculating the exact end line. Use specific line numbers when you need precise control. Remember: end lines are exclusive — grid-row: 1 / 3 means the item occupies rows 1 and 2, not rows 1, 2, and 3!
23. row-gap, column-gap, and gap Properties
These properties control the spacing between grid tracks (rows and columns). They’re essential for creating clean, well-spaced grid layouts without using margins on individual items.
Overview of Properties
| Property | Description |
|---|---|
row-gap | Space between rows |
column-gap | Space between columns |
gap | Shorthand for row-gap and column-gap |
1. row-gap
The row-gap property specifies the space between each row in a grid.
.grid-container {
display: grid;
row-gap: 20px; /* 20px between each row */
}
Values
| Value | Description | Example |
|---|---|---|
length | Fixed size (px, em, rem) | row-gap: 20px; |
percentage | Percentage of container | row-gap: 5%; |
calc() | Calculated value | row-gap: calc(10px + 2%); |
normal | Browser default (usually 0) | row-gap: normal; |
2. column-gap
The column-gap property specifies the space between each column in a grid.
.grid-container {
display: grid;
column-gap: 10px; /* 10px between each column */
}
Values
Same as row-gap: length, percentage, calc(), normal
3. gap (Shorthand)
The gap property is a shorthand for row-gap and column-gap. It also applies to flex containers.
/* Both rows and columns */
.grid-container {
gap: 20px; /* 20px for both row and column gaps */
}
/* Row gap | Column gap */
.grid-container {
gap: 20px 10px; /* 20px row gap, 10px column gap */
}
Syntax
gap: <row-gap> <column-gap>;
gap: <row-gap>; /* Same for both */
How It Works
┌─────────┐ ← row-gap
│ Item │
├─────────┤ ← row-gap
│ Item │
└─────────┘
┌────┬────┬────┐
│ │ │ │
│ It │ It │ It │ ← column-gap between columns
│ │ │ │
└────┴────┴────┘
row-gapcreates vertical space between rowscolumn-gapcreates horizontal space between columnsgapcombines both
gap in Flexbox
The gap property also works in flex containers, making it easier to space flex items without margins.
.flex-container {
display: flex;
gap: 20px; /* 20px between flex items */
}
Before gap (using margins):
.flex-item {
margin-right: 20px;
}
.flex-item:last-child {
margin-right: 0;
}
With gap:
.flex-container {
display: flex;
gap: 20px; /* Much cleaner! */
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>row-gap, column-gap, and gap Properties</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== GRID DEMOS ====== */
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.grid-item {
background: #007bff;
color: white;
padding: 20px;
border-radius: 5px;
text-align: center;
font-weight: bold;
}
.grid-item:nth-child(even) {
background: #28a745;
}
.grid-item:nth-child(3n) {
background: #dc3545;
}
/* Gap variations */
.gap-both {
gap: 20px;
}
.gap-different {
row-gap: 30px;
column-gap: 10px;
}
.gap-shorthand {
gap: 30px 10px;
}
.gap-percentage {
gap: 5%;
}
.gap-calc {
gap: calc(10px + 2%);
}
/* Flexbox with gap */
.flex-container {
display: flex;
gap: 20px;
flex-wrap: wrap;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.flex-item {
background: #6c5ce7;
color: white;
padding: 20px;
border-radius: 5px;
text-align: center;
font-weight: bold;
flex: 1;
min-width: 150px;
}
/* Visual gap indicators */
.gap-visual {
display: flex;
justify-content: center;
gap: 30px;
margin: 20px 0;
flex-wrap: wrap;
}
.gap-visual-item {
text-align: center;
}
.gap-visual-item .box {
width: 100px;
height: 60px;
background: #007bff;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
}
.gap-visual-item .arrow {
font-size: 1.5rem;
color: #dc3545;
margin: 5px 0;
}
</style>
</head>
<body>
<h1>row-gap, column-gap, and gap Properties</h1>
<!-- ====== 1. ROW-GAP ====== -->
<section>
<h2>1. row-gap</h2>
<p>Specifies the space between <strong>rows</strong> in a grid.</p>
<div class="grid-container" style="row-gap: 20px;">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
<div class="grid-item">10</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
row-gap: 20px; /* 20px between rows */
}
</div>
</section>
<!-- ====== 2. COLUMN-GAP ====== -->
<section>
<h2>2. column-gap</h2>
<p>Specifies the space between <strong>columns</strong> in a grid.</p>
<div class="grid-container" style="column-gap: 30px;">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
<div class="grid-item">10</div>
</div>
<div class="code-block">
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
column-gap: 30px; /* 30px between columns */
}
</div>
</section>
<!-- ====== 3. GAP (SHORTHAND) ====== -->
<section>
<h2>3. gap (Shorthand)</h2>
<p>Sets both row and column gaps in one declaration.</p>
<h3>gap: 20px (same for both)</h3>
<div class="grid-container gap-both">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
<div class="grid-item">10</div>
</div>
<h3>gap: 30px 10px (row gap | column gap)</h3>
<div class="grid-container gap-shorthand">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
<div class="grid-item">10</div>
</div>
<div class="code-block">
/* Same for both */
gap: 20px;
/* Row gap | Column gap */
gap: 30px 10px;
/* Equivalent to: */
row-gap: 30px;
column-gap: 10px;
</div>
</section>
<!-- ====== 4. GAP IN FLEXBOX ====== -->
<section>
<h2>4. gap in Flexbox</h2>
<p>The <code>gap</code> property also works in <strong>flex containers</strong>, eliminating the need for margins.</p>
<div class="flex-container">
<div class="flex-item">Flex Item 1</div>
<div class="flex-item">Flex Item 2</div>
<div class="flex-item">Flex Item 3</div>
<div class="flex-item">Flex Item 4</div>
<div class="flex-item">Flex Item 5</div>
</div>
<div class="code-block">
/* Flexbox with gap */
.flex-container {
display: flex;
flex-wrap: wrap;
gap: 20px; /* Space between items */
}
/* Before gap, you needed margins: */
.flex-item {
margin-right: 20px;
}
.flex-item:last-child {
margin-right: 0;
}
</div>
</section>
<!-- ====== 5. VISUAL COMPARISON ====== -->
<section>
<h2>5. Visual Comparison</h2>
<h3>row-gap vs column-gap</h3>
<div class="gap-visual">
<div class="gap-visual-item">
<div class="box">Item 1</div>
<div class="arrow">↕</div>
<div class="box">Item 2</div>
<div class="arrow">↕</div>
<div class="box">Item 3</div>
<p class="note">row-gap</p>
</div>
<div class="gap-visual-item" style="display: flex; gap: 20px; align-items: center;">
<div class="box">Item 1</div>
<div class="arrow" style="transform: rotate(90deg);">↔</div>
<div class="box">Item 2</div>
<div class="arrow" style="transform: rotate(90deg);">↔</div>
<div class="box">Item 3</div>
<p class="note">column-gap</p>
</div>
</div>
</section>
<!-- ====== 6. ADVANCED VALUES ====== -->
<section>
<h2>6. Advanced Values</h2>
<h3>Percentage Gap</h3>
<div class="grid-container" style="gap: 5%;">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
</div>
<h3>calc() Gap</h3>
<div class="grid-container" style="gap: calc(10px + 2%);">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
</div>
<div class="code-block">
/* Percentage gap */
gap: 5%;
/* calc() gap */
gap: calc(10px + 2%);
/* Different row and column gaps */
gap: 30px 10px;
</div>
</section>
<!-- ====== 7. REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>Gap Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>row-gap</code></td>
<td>Space between rows</td>
<td><code>row-gap: 20px;</code></td>
</tr>
<tr>
<td><code>column-gap</code></td>
<td>Space between columns</td>
<td><code>column-gap: 10px;</code></td>
</tr>
<tr>
<td><code>gap</code></td>
<td>Shorthand for both</td>
<td><code>gap: 20px 10px;</code></td>
</tr>
</table>
<h3>Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed size (px, em, rem)</td>
<td><code>gap: 20px;</code></td>
</tr>
<tr>
<td><code>percentage</code></td>
<td>Percentage of container</td>
<td><code>gap: 5%;</code></td>
</tr>
<tr>
<td><code>calc()</code></td>
<td>Calculated value</td>
<td><code>gap: calc(10px + 2%);</code></td>
</tr>
<tr>
<td><code>normal</code></td>
<td>Browser default (usually 0)</td>
<td><code>gap: normal;</code></td>
</tr>
</table>
<h3>gap Shorthand</h3>
<table class="reference-table">
<tr>
<th>Syntax</th>
<th>Row Gap</th>
<th>Column Gap</th>
</tr>
<tr>
<td><code>gap: 20px;</code></td>
<td>20px</td>
<td>20px</td>
</tr>
<tr>
<td><code>gap: 30px 10px;</code></td>
<td>30px</td>
<td>10px</td>
</tr>
</table>
</section>
<!-- ====== 8. BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>gap</code> instead of margins on grid/flex items</li>
<li>Use <code>gap</code> for consistent spacing in both grid and flexbox</li>
<li>Use <code>row-gap</code> and <code>column-gap</code> for different spacing</li>
<li>Use percentage or <code>calc()</code> gaps for responsive spacing</li>
<li>Use <code>gap</code> to simplify your code — no more <code>:last-child</code> margin resets!</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use margins on grid items — use <code>gap</code> instead</li>
<li>Don't forget that <code>gap</code> creates space <strong>between</strong> tracks, not around them</li>
<li>Don't use <code>gap</code> on elements that aren't grid or flex containers</li>
<li>Don't mix <code>gap</code> and margins unnecessarily — choose one approach</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Example |
|---|---|---|
row-gap | Space between rows | row-gap: 20px; |
column-gap | Space between columns | column-gap: 10px; |
gap | Shorthand for both | gap: 20px 10px; |
Values
| Value | Description | Example |
|---|---|---|
length | Fixed size (px, em, rem) | gap: 20px; |
percentage | Percentage of container | gap: 5%; |
calc() | Calculated value | gap: calc(10px + 2%); |
normal | Browser default (usually 0) | gap: normal; |
gap Shorthand
| Syntax | Row Gap | Column Gap |
|---|---|---|
gap: 20px; | 20px | 20px |
gap: 30px 10px; | 30px | 10px |
Best Practices
✅ Do This:
/* Grid with gap */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* Much cleaner than margins! */
}
/* Flexbox with gap */
.flex {
display: flex;
gap: 15px;
}
/* Different row and column gaps */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px 10px; /* 30px rows, 10px columns */
}
❌ Don’t Do This:
/* Don't use margins on grid items */
.grid-item {
margin: 10px; /* Use gap on the container instead */
}
/* Don't forget gap only works on grid/flex containers */
p {
gap: 20px; /* Won't work — p is not a grid or flex container */
}
Pro Tip: The gap property is one of the best additions to CSS in recent years. It works in both Grid and Flexbox, eliminating the need for margin-based spacing hacks. Use gap on your grid and flex containers instead of margins on individual items — your code will be cleaner, more maintainable, and easier to reason about. Remember: gap creates space between tracks, not around the outer edges!
24. Flexbox Layout Introduction
Flexbox (Flexible Box Layout) is a one-dimensional layout system that allows you to arrange elements in rows or columns with flexible sizing and alignment. It’s perfect for responsive designs where items need to resize, reorder, or align based on available space.
What is Flexbox?
Flexbox is a layout model that distributes space among items in a container and provides powerful alignment capabilities. Unlike Grid (which is two-dimensional), Flexbox works in one dimension at a time — either a row or a column.
Key Benefits:
| Benefit | Description |
|---|---|
| One-dimensional | Layout items in a row or column |
| Flexible sizing | Items grow or shrink to fill space |
| Easy alignment | Center, space-between, space-around, etc. |
| Order control | Reorder items without changing HTML |
| Responsive | Adapts to different screen sizes automatically |
Flexbox Basics
<div class="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
.container {
display: flex;
}
.item {
background-color: lightblue;
padding: 10px;
margin: 5px;
border: 2px solid black;
}
What happens:
- The container becomes a flex container
- The children become flex items
- Items are laid out in a row (default direction)
- Items are placed side by side
The Main Axis and Cross Axis
Every flex container has two axes:
| Axis | Description | Default Direction |
|---|---|---|
| Main axis | The primary direction items are laid out | Horizontal (row) |
| Cross axis | Perpendicular to the main axis | Vertical (column) |
Main Axis (row):
┌─────────────────────────────────────────────────┐
│ Item 1 │ Item 2 │ Item 3 │ Item 4 │
└─────────────────────────────────────────────────┘
↑ Cross Axis (column)
Important: The main and cross axes change when you change flex-direction.
| flex-direction | Main Axis | Cross Axis |
|---|---|---|
row (default) | Horizontal | Vertical |
column | Vertical | Horizontal |
row-reverse | Horizontal (reversed) | Vertical |
column-reverse | Vertical (reversed) | Horizontal |
Flexbox Properties Overview
| Property | Applies To | Description |
|---|---|---|
flex-direction | Container | Defines the main axis (row or column) |
flex-wrap | Container | Allows items to wrap to the next line |
justify-content | Container | Aligns items along the main axis |
align-items | Container | Aligns items along the cross axis |
align-content | Container | Aligns wrapped lines along the cross axis |
flex-grow | Item | How much an item can grow |
flex-shrink | Item | How much an item can shrink |
flex-basis | Item | Initial size of an item |
flex | Item | Shorthand for grow, shrink, basis |
order | Item | Changes the order of items |
align-self | Item | Overrides align-items for one item |
1. flex-direction
Defines the main axis — the direction items are laid out.
.container {
display: flex;
flex-direction: row; /* Default — left to right */
}
.container {
display: flex;
flex-direction: column; /* Top to bottom */
}
.container {
display: flex;
flex-direction: row-reverse; /* Right to left */
}
.container {
display: flex;
flex-direction: column-reverse; /* Bottom to top */
}
2. flex-wrap
Controls whether items wrap to the next line when there isn’t enough space.
.container {
display: flex;
flex-wrap: nowrap; /* Default — no wrapping */
}
.container {
display: flex;
flex-wrap: wrap; /* Items wrap to next line */
}
.container {
display: flex;
flex-wrap: wrap-reverse; /* Items wrap in reverse */
}
3. flex-grow, flex-shrink, flex-basis
These properties control how flex items grow, shrink, and their initial size.
.item {
flex-grow: 0; /* Default — no growth */
flex-shrink: 1; /* Default — can shrink */
flex-basis: auto; /* Default — auto size */
}
/* Shorthand */
.item {
flex: 1; /* grow: 1, shrink: 1, basis: 0 */
}
.item {
flex: 1 1 200px; /* grow: 1, shrink: 1, basis: 200px */
}
| Property | Description | Default |
|---|---|---|
flex-grow | How much an item can grow | 0 |
flex-shrink | How much an item can shrink | 1 |
flex-basis | Initial size before distribution | auto |
4. flex (Shorthand)
The flex property combines flex-grow, flex-shrink, and flex-basis into one declaration.
/* Common patterns */
.item {
flex: 1; /* grow: 1, shrink: 1, basis: 0 */
}
.item {
flex: auto; /* grow: 1, shrink: 1, basis: auto */
}
.item {
flex: none; /* grow: 0, shrink: 0, basis: auto */
}
.item {
flex: 1 0 200px; /* grow: 1, shrink: 0, basis: 200px */
}
5. justify-content
Aligns items along the main axis.
.container {
display: flex;
justify-content: flex-start; /* Default — start */
justify-content: flex-end; /* End */
justify-content: center; /* Center */
justify-content: space-between; /* Equal space between */
justify-content: space-around; /* Equal space around */
justify-content: space-evenly; /* Equal space everywhere */
}
6. align-items
Aligns items along the cross axis.
.container {
display: flex;
align-items: stretch; /* Default — fill height */
align-items: flex-start; /* Top */
align-items: flex-end; /* Bottom */
align-items: center; /* Center */
align-items: baseline; /* Align to text baseline */
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox Layout Introduction</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
/* ====== FLEX CONTAINERS ====== */
.container {
display: flex;
background: #e9ecef;
padding: 10px;
border-radius: 8px;
margin: 15px 0;
min-height: 80px;
}
.item {
background: #007bff;
color: white;
padding: 15px 20px;
border-radius: 6px;
text-align: center;
font-weight: bold;
border: 2px solid #0056b3;
}
.item:nth-child(even) {
background: #28a745;
border-color: #1e7e34;
}
.item:nth-child(3n) {
background: #dc3545;
border-color: #a71d2a;
}
/* Direction demos */
.dir-row {
flex-direction: row;
}
.dir-column {
flex-direction: column;
}
.dir-row-reverse {
flex-direction: row-reverse;
}
.dir-column-reverse {
flex-direction: column-reverse;
}
/* Wrap demos */
.wrap-nowrap {
flex-wrap: nowrap;
}
.wrap-wrap {
flex-wrap: wrap;
}
.wrap-wrap .item {
width: 200px;
}
/* Grow/shrink demos */
.grow-demo .item.grow-1 {
flex-grow: 1;
}
.grow-demo .item.grow-2 {
flex-grow: 2;
}
/* Justify-content demos */
.justify-start {
justify-content: flex-start;
}
.justify-center {
justify-content: center;
}
.justify-end {
justify-content: flex-end;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
.justify-evenly {
justify-content: space-evenly;
}
/* Align-items demos */
.align-demo {
height: 150px;
}
.align-start {
align-items: flex-start;
}
.align-center {
align-items: center;
}
.align-end {
align-items: flex-end;
}
.align-stretch {
align-items: stretch;
}
.align-baseline {
align-items: baseline;
}
/* Axis visualization */
.axis-demo {
position: relative;
padding: 30px 20px;
}
.axis-demo .main-axis {
position: absolute;
top: 5px;
left: 50%;
transform: translateX(-50%);
font-weight: bold;
color: #dc3545;
font-size: 0.85rem;
}
.axis-demo .cross-axis {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
font-weight: bold;
color: #6c5ce7;
font-size: 0.85rem;
writing-mode: vertical-rl;
}
</style>
</head>
<body>
<h1>Flexbox Layout Introduction</h1>
<!-- ====== 1. BASIC FLEXBOX ====== -->
<section>
<h2>1. Basic Flexbox</h2>
<p>Setting <code>display: flex</code> on the container makes its children flex items, laid out in a row by default.</p>
<div class="container axis-demo">
<span class="main-axis">← Main Axis (row) →</span>
<span class="cross-axis">↕ Cross Axis</span>
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<div class="code-block">
.container {
display: flex; /* Makes this a flex container */
}
.item {
/* Flex items are laid out in a row by default */
}
</div>
</section>
<!-- ====== 2. FLEX-DIRECTION ====== -->
<section>
<h2>2. flex-direction</h2>
<p>Defines the main axis — the direction items are laid out.</p>
<h3>flex-direction: row (default)</h3>
<div class="container dir-row">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>flex-direction: column</h3>
<div class="container dir-column" style="min-height: 250px;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>flex-direction: row-reverse</h3>
<div class="container dir-row-reverse">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>flex-direction: column-reverse</h3>
<div class="container dir-column-reverse" style="min-height: 250px;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<div class="code-block">
.container { flex-direction: row; } /* Default — left to right */
.container { flex-direction: column; } /* Top to bottom */
.container { flex-direction: row-reverse; } /* Right to left */
.container { flex-direction: column-reverse; } /* Bottom to top */
</div>
</section>
<!-- ====== 3. FLEX-WRAP ====== -->
<section>
<h2>3. flex-wrap</h2>
<p>Controls whether items wrap to the next line when there isn't enough space.</p>
<h3>flex-wrap: nowrap (default — items shrink to fit)</h3>
<div class="container wrap-nowrap">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
<div class="item">Item 8</div>
</div>
<h3>flex-wrap: wrap (items wrap to next line)</h3>
<div class="container wrap-wrap">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
<div class="item">Item 8</div>
</div>
<div class="code-block">
.container { flex-wrap: nowrap; } /* Default — no wrapping */
.container { flex-wrap: wrap; } /* Items wrap */
.container { flex-wrap: wrap-reverse; } /* Items wrap in reverse */
</div>
</section>
<!-- ====== 4. FLEX-GROW ====== -->
<section>
<h2>4. flex-grow</h2>
<p>Defines how much an item can grow relative to other items.</p>
<div class="container grow-demo">
<div class="item">flex-grow: 0 (default)</div>
<div class="item grow-1">flex-grow: 1</div>
<div class="item grow-2">flex-grow: 2</div>
</div>
<div class="code-block">
.item { flex-grow: 0; } /* Default — no growth */
.item { flex-grow: 1; } /* Grows to fill available space */
.item { flex-grow: 2; } /* Grows twice as much as flex-grow: 1 */
</div>
</section>
<!-- ====== 5. JUSTIFY-CONTENT ====== -->
<section>
<h2>5. justify-content</h2>
<p>Aligns items along the <strong>main axis</strong>.</p>
<h3>flex-start (default)</h3>
<div class="container justify-start">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<h3>center</h3>
<div class="container justify-center">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<h3>flex-end</h3>
<div class="container justify-end">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<h3>space-between</h3>
<div class="container justify-between">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<h3>space-around</h3>
<div class="container justify-around">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<h3>space-evenly</h3>
<div class="container justify-evenly">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<div class="code-block">
.container { justify-content: flex-start; } /* Default — start */
.container { justify-content: flex-end; } /* End */
.container { justify-content: center; } /* Center */
.container { justify-content: space-between; } /* Space between */
.container { justify-content: space-around; } /* Space around */
.container { justify-content: space-evenly; } /* Equal space */
</div>
</section>
<!-- ====== 6. ALIGN-ITEMS ====== -->
<section>
<h2>6. align-items</h2>
<p>Aligns items along the <strong>cross axis</strong>.</p>
<h3>flex-start</h3>
<div class="container align-demo align-start">
<div class="item">1</div>
<div class="item" style="height: 80px;">2 (taller)</div>
<div class="item">3</div>
</div>
<h3>center</h3>
<div class="container align-demo align-center">
<div class="item">1</div>
<div class="item" style="height: 80px;">2 (taller)</div>
<div class="item">3</div>
</div>
<h3>flex-end</h3>
<div class="container align-demo align-end">
<div class="item">1</div>
<div class="item" style="height: 80px;">2 (taller)</div>
<div class="item">3</div>
</div>
<h3>stretch (default)</h3>
<div class="container align-demo align-stretch">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<div class="code-block">
.container { align-items: stretch; } /* Default — fill height */
.container { align-items: flex-start; } /* Top */
.container { align-items: flex-end; } /* Bottom */
.container { align-items: center; } /* Center */
.container { align-items: baseline; } /* Text baseline */
</div>
</section>
<!-- ====== 7. FLEX SHORTHAND ====== -->
<section>
<h2>7. flex (Shorthand)</h2>
<p>Combines <code>flex-grow</code>, <code>flex-shrink</code>, and <code>flex-basis</code>.</p>
<div class="container">
<div class="item" style="flex: 1;">flex: 1</div>
<div class="item" style="flex: 2;">flex: 2</div>
<div class="item" style="flex: 1;">flex: 1</div>
</div>
<div class="code-block">
.item { flex: 1; } /* grow: 1, shrink: 1, basis: 0 */
.item { flex: auto; } /* grow: 1, shrink: 1, basis: auto */
.item { flex: none; } /* grow: 0, shrink: 0, basis: auto */
.item { flex: 1 0 200px; } /* grow: 1, shrink: 0, basis: 200px */
</div>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>Container Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Values</th>
</tr>
<tr>
<td><code>display</code></td>
<td>Enables flexbox</td>
<td><code>flex</code>, <code>inline-flex</code></td>
</tr>
<tr>
<td><code>flex-direction</code></td>
<td>Main axis direction</td>
<td><code>row</code>, <code>column</code>, <code>row-reverse</code>, <code>column-reverse</code></td>
</tr>
<tr>
<td><code>flex-wrap</code></td>
<td>Wrapping behavior</td>
<td><code>nowrap</code>, <code>wrap</code>, <code>wrap-reverse</code></td>
</tr>
<tr>
<td><code>justify-content</code></td>
<td>Align on main axis</td>
<td><code>flex-start</code>, <code>flex-end</code>, <code>center</code>, <code>space-between</code>, <code>space-around</code>, <code>space-evenly</code></td>
</tr>
<tr>
<td><code>align-items</code></td>
<td>Align on cross axis</td>
<td><code>stretch</code>, <code>flex-start</code>, <code>flex-end</code>, <code>center</code>, <code>baseline</code></td>
</tr>
</table>
<h3>Item Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Default</th>
</tr>
<tr>
<td><code>flex-grow</code></td>
<td>How much an item can grow</td>
<td><code>0</code></td>
</tr>
<tr>
<td><code>flex-shrink</code></td>
<td>How much an item can shrink</td>
<td><code>1</code></td>
</tr>
<tr>
<td><code>flex-basis</code></td>
<td>Initial size</td>
<td><code>auto</code></td>
</tr>
<tr>
<td><code>flex</code></td>
<td>Shorthand for grow, shrink, basis</td>
<td>—</td>
</tr>
<tr>
<td><code>order</code></td>
<td>Visual order of items</td>
<td><code>0</code></td>
</tr>
<tr>
<td><code>align-self</code></td>
<td>Override align-items for one item</td>
<td><code>auto</code></td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>display: flex</code> for one-dimensional layouts</li>
<li>Use <code>gap</code> instead of margins on flex items</li>
<li>Use <code>justify-content</code> for main axis alignment</li>
<li>Use <code>align-items</code> for cross axis alignment</li>
<li>Use <code>flex-wrap: wrap</code> for responsive layouts</li>
<li>Use the <code>flex</code> shorthand for concise code</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use Flexbox for two-dimensional layouts (use Grid instead)</li>
<li>Don't use margins on flex items when gap works</li>
<li>Don't forget that <code>align-items</code> only works on the cross axis</li>
<li>Don't confuse <code>justify-content</code> (main axis) with <code>align-items</code> (cross axis)</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Applies To | Description |
|---|---|---|
display: flex | Container | Enables flexbox |
flex-direction | Container | Main axis (row/column) |
flex-wrap | Container | Wrapping behavior |
justify-content | Container | Align on main axis |
align-items | Container | Align on cross axis |
flex-grow | Item | Growth factor |
flex-shrink | Item | Shrink factor |
flex-basis | Item | Initial size |
flex | Item | Shorthand for grow, shrink, basis |
Main Axis vs Cross Axis
| flex-direction | Main Axis | Cross Axis |
|---|---|---|
row (default) | Horizontal → | Vertical ↓ |
column | Vertical ↓ | Horizontal → |
row-reverse | Horizontal ← | Vertical ↓ |
column-reverse | Vertical ↑ | Horizontal → |
Best Practices
✅ Do This:
/* Center everything perfectly */
.container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* Responsive flex with wrapping */
.container {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
/* Equal-width items */
.item {
flex: 1;
}
❌ Don’t Do This:
/* Don't use margins on flex items */
.item {
margin-right: 20px; /* Use gap on the container instead */
}
/* Don't confuse justify-content and align-items */
.container {
justify-content: center; /* Main axis */
align-items: center; /* Cross axis */
}
Pro Tip: Flexbox is perfect for one-dimensional layouts (rows or columns). Use display: flex on the container, then control alignment with justify-content (main axis) and align-items (cross axis). The flex: 1 shorthand makes items grow equally to fill available space. Remember: when you change flex-direction, the main and cross axes swap! In the next lessons, you’ll dive deeper into each Flexbox property.
25. flex-direction and flex-wrap Properties
These two container properties control the direction of the main axis and how items wrap when they exceed the container’s size. Together, they form the foundation of any Flexbox layout.
Overview of Properties
| Property | Description | Values |
|---|---|---|
flex-direction | Sets the direction of the main axis | row, row-reverse, column, column-reverse |
flex-wrap | Controls whether items wrap | nowrap, wrap, wrap-reverse |
1. flex-direction
The flex-direction property sets the direction of the main axis — which determines how items are laid out.
.container {
display: flex;
flex-direction: row; /* Default — left to right */
}
Values
| Value | Main Axis | Direction | Visual |
|---|---|---|---|
row | Horizontal | Left → Right (default) | [1] [2] [3] |
row-reverse | Horizontal | Right → Left | [3] [2] [1] |
column | Vertical | Top → Bottom | [1][2][3] |
column-reverse | Vertical | Bottom → Top | [3][2][1] |
Important: Changing flex-direction also changes which axis is the main axis and which is the cross axis.
| flex-direction | Main Axis | Cross Axis |
|---|---|---|
row | Horizontal → | Vertical ↓ |
row-reverse | Horizontal ← | Vertical ↓ |
column | Vertical ↓ | Horizontal → |
column-reverse | Vertical ↑ | Horizontal → |
2. flex-wrap
The flex-wrap property controls whether flex items wrap to the next line when they exceed the container’s size.
.container {
display: flex;
flex-wrap: nowrap; /* Default — no wrapping */
}
Values
| Value | Description | Visual |
|---|---|---|
nowrap | All items on one line (default) — items shrink to fit | [1][2][3][4][5] |
wrap | Items wrap to multiple lines | [1][2][3][4][5] |
wrap-reverse | Items wrap in reverse order (bottom to top) | [4][5][1][2][3] |
Key Points:
nowrapforces all items onto a single line (items may shrink)wrapallows items to flow onto multiple lineswrap-reverseinverts the cross-axis direction (lines stack in reverse)
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>flex-direction and flex-wrap</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
/* ====== FLEX CONTAINERS ====== */
.container {
display: flex;
border: 2px solid #333;
width: 200px;
margin: 10px 0;
padding: 5px;
background: #e9ecef;
border-radius: 8px;
}
.item {
width: 100px;
height: 50px;
background: #007bff;
margin: 5px;
color: white;
text-align: center;
line-height: 50px;
border-radius: 4px;
font-weight: bold;
flex-shrink: 0;
}
.item:nth-child(even) {
background: #28a745;
}
.item:nth-child(3n) {
background: #dc3545;
}
/* flex-direction demos */
.dir-row {
flex-direction: row;
}
.dir-row-reverse {
flex-direction: row-reverse;
}
.dir-column {
flex-direction: column;
width: auto;
min-width: 200px;
min-height: 200px;
}
.dir-column-reverse {
flex-direction: column-reverse;
width: auto;
min-width: 200px;
min-height: 200px;
}
/* flex-wrap demos */
.wrap-nowrap {
flex-wrap: nowrap;
}
.wrap-wrap {
flex-wrap: wrap;
}
.wrap-wrap-reverse {
flex-wrap: wrap-reverse;
}
/* Wider container for wrap demos */
.container-wide {
width: 350px;
}
.container-wide .item {
width: 100px;
}
/* Side-by-side comparison */
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.comparison-item {
text-align: center;
}
.comparison-item h4 {
margin-bottom: 5px;
color: #007bff;
}
/* Axis visualization */
.axis-demo {
position: relative;
padding: 25px 15px 15px 15px;
}
.axis-demo .main-axis-label {
position: absolute;
top: 2px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
font-weight: bold;
color: #dc3545;
white-space: nowrap;
}
.axis-demo .cross-axis-label {
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%);
font-size: 0.75rem;
font-weight: bold;
color: #6c5ce7;
writing-mode: vertical-rl;
}
</style>
</head>
<body>
<h1>flex-direction and flex-wrap Properties</h1>
<!-- ====== 1. FLEX-DIRECTION: ROW ====== -->
<section>
<h2>1. flex-direction: row (default)</h2>
<p>Main axis is horizontal, items flow from left to right.</p>
<div class="container dir-row axis-demo">
<span class="main-axis-label">← Main Axis →</span>
<span class="cross-axis-label">↕ Cross</span>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<div class="code-block">
.container {
display: flex;
flex-direction: row; /* Default */
}
</div>
</section>
<!-- ====== 2. FLEX-DIRECTION: ROW-REVERSE ====== -->
<section>
<h2>2. flex-direction: row-reverse</h2>
<p>Main axis is horizontal, but items flow from right to left.</p>
<div class="container dir-row-reverse axis-demo">
<span class="main-axis-label">← Main Axis →</span>
<span class="cross-axis-label">↕ Cross</span>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<div class="code-block">
.container {
display: flex;
flex-direction: row-reverse;
}
</div>
</section>
<!-- ====== 3. FLEX-DIRECTION: COLUMN ====== -->
<section>
<h2>3. flex-direction: column</h2>
<p>Main axis is vertical, items flow from top to bottom.</p>
<div class="container dir-column axis-demo">
<span class="main-axis-label">↓ Main Axis ↓</span>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<div class="code-block">
.container {
display: flex;
flex-direction: column;
}
</div>
</section>
<!-- ====== 4. FLEX-DIRECTION: COLUMN-REVERSE ====== -->
<section>
<h2>4. flex-direction: column-reverse</h2>
<p>Main axis is vertical, but items flow from bottom to top.</p>
<div class="container dir-column-reverse axis-demo">
<span class="main-axis-label">↑ Main Axis ↑</span>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<div class="code-block">
.container {
display: flex;
flex-direction: column-reverse;
}
</div>
</section>
<!-- ====== 5. FLEX-WRAP: NOWRAP ====== -->
<section>
<h2>5. flex-wrap: nowrap (default)</h2>
<p>All items are forced onto a single line. If they don't fit, they overflow or shrink.</p>
<div class="container container-wide wrap-nowrap">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
<p class="note">Items overflow the container because they don't wrap.</p>
<div class="code-block">
.container {
display: flex;
flex-wrap: nowrap; /* Default */
}
</div>
</section>
<!-- ====== 6. FLEX-WRAP: WRAP ====== -->
<section>
<h2>6. flex-wrap: wrap</h2>
<p>Items wrap to multiple lines when they exceed the container's size.</p>
<div class="container container-wide wrap-wrap">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
<p class="note">Items wrap onto new lines when there's not enough space.</p>
<div class="code-block">
.container {
display: flex;
flex-wrap: wrap;
}
</div>
</section>
<!-- ====== 7. FLEX-WRAP: WRAP-REVERSE ====== -->
<section>
<h2>7. flex-wrap: wrap-reverse</h2>
<p>Items wrap, but the lines are stacked in reverse order (bottom to top).</p>
<div class="container container-wide wrap-wrap-reverse">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
<p class="note">The first line is at the bottom, the second line is at the top.</p>
<div class="code-block">
.container {
display: flex;
flex-wrap: wrap-reverse;
}
</div>
</section>
<!-- ====== 8. SIDE-BY-SIDE COMPARISON ====== -->
<section>
<h2>8. Side-by-Side Comparison</h2>
<h3>flex-direction</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>row</h4>
<div class="container" style="flex-direction: row;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
</div>
<div class="comparison-item">
<h4>row-reverse</h4>
<div class="container" style="flex-direction: row-reverse;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
</div>
<div class="comparison-item">
<h4>column</h4>
<div class="container" style="flex-direction: column; width: auto; min-width: 150px;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
</div>
<div class="comparison-item">
<h4>column-reverse</h4>
<div class="container" style="flex-direction: column-reverse; width: auto; min-width: 150px;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
</div>
</div>
<h3 style="margin-top: 30px;">flex-wrap</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>nowrap</h4>
<div class="container container-wide" style="flex-wrap: nowrap; width: 100%;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
</div>
<div class="comparison-item">
<h4>wrap</h4>
<div class="container container-wide" style="flex-wrap: wrap; width: 100%;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
</div>
<div class="comparison-item">
<h4>wrap-reverse</h4>
<div class="container container-wide" style="flex-wrap: wrap-reverse; width: 100%;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
</div>
</div>
</section>
<!-- ====== 9. REFERENCE TABLES ====== -->
<section>
<h2>9. Reference Tables</h2>
<h3>flex-direction Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Main Axis</th>
<th>Direction</th>
<th>Example</th>
</tr>
<tr>
<td><code>row</code></td>
<td>Horizontal</td>
<td>Left → Right (default)</td>
<td><code>[1] [2] [3]</code></td>
</tr>
<tr>
<td><code>row-reverse</code></td>
<td>Horizontal</td>
<td>Right → Left</td>
<td><code>[3] [2] [1]</code></td>
</tr>
<tr>
<td><code>column</code></td>
<td>Vertical</td>
<td>Top → Bottom</td>
<td><code>[1]</code><br><code>[2]</code><br><code>[3]</code></td>
</tr>
<tr>
<td><code>column-reverse</code></td>
<td>Vertical</td>
<td>Bottom → Top</td>
<td><code>[3]</code><br><code>[2]</code><br><code>[1]</code></td>
</tr>
</table>
<h3>flex-wrap Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Visual</th>
</tr>
<tr>
<td><code>nowrap</code></td>
<td>All items on one line (default)</td>
<td><code>[1][2][3][4][5]</code></td>
</tr>
<tr>
<td><code>wrap</code></td>
<td>Items wrap to multiple lines</td>
<td><code>[1][2][3]</code><br><code>[4][5]</code></td>
</tr>
<tr>
<td><code>wrap-reverse</code></td>
<td>Items wrap in reverse order (bottom to top)</td>
<td><code>[4][5]</code><br><code>[1][2][3]</code></td>
</tr>
</table>
<h3>Shorthand: flex-flow</h3>
<table class="reference-table">
<tr>
<th>Shorthand</th>
<th>Equivalent</th>
<th>Example</th>
</tr>
<tr>
<td><code>flex-flow</code></td>
<td><code>flex-direction</code> + <code>flex-wrap</code></td>
<td><code>flex-flow: row wrap;</code></td>
</tr>
</table>
</section>
<!-- ====== 10. BEST PRACTICES ====== -->
<section>
<h2>10. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>flex-direction: row</code> for horizontal layouts</li>
<li>Use <code>flex-direction: column</code> for vertical layouts</li>
<li>Use <code>flex-wrap: wrap</code> for responsive layouts</li>
<li>Use <code>flex-flow</code> shorthand to combine both properties</li>
<li>Remember that changing <code>flex-direction</code> swaps the main and cross axes</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>flex-wrap: wrap-reverse</code> without understanding the visual order change</li>
<li>Don't forget that <code>justify-content</code> and <code>align-items</code> swap axes when you change <code>flex-direction</code></li>
<li>Don't use <code>nowrap</code> when items might overflow on small screens</li>
<li>Don't confuse <code>row-reverse</code> (visual order) with <code>order</code> (DOM order)</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Values | Description |
|---|---|---|
flex-direction | row, row-reverse, column, column-reverse | Sets the main axis direction |
flex-wrap | nowrap, wrap, wrap-reverse | Controls wrapping behavior |
flex-flow | <flex-direction> <flex-wrap> | Shorthand for both |
flex-direction Values
| Value | Main Axis | Direction |
|---|---|---|
row | Horizontal | Left → Right (default) |
row-reverse | Horizontal | Right → Left |
column | Vertical | Top → Bottom |
column-reverse | Vertical | Bottom → Top |
flex-wrap Values
| Value | Description |
|---|---|
nowrap | All items on one line (default) |
wrap | Items wrap to multiple lines |
wrap-reverse | Items wrap in reverse order (bottom to top) |
flex-flow Shorthand
/* Instead of: */
flex-direction: row;
flex-wrap: wrap;
/* Use: */
flex-flow: row wrap;
Best Practices
✅ Do This:
/* Responsive flex layout */
.container {
display: flex;
flex-flow: row wrap; /* Shorthand */
gap: 20px;
}
/* Vertical layout */
.sidebar {
display: flex;
flex-direction: column;
gap: 10px;
}
❌ Don’t Do This:
/* Don't use wrap-reverse without understanding it */
.container {
flex-wrap: wrap-reverse; /* Lines stack from bottom to top */
}
/* Don't forget axis swap when changing direction */
.container {
flex-direction: column;
justify-content: center; /* Now controls vertical alignment! */
align-items: center; /* Now controls horizontal alignment! */
}
Pro Tip: flex-direction and flex-wrap are the two most fundamental Flexbox properties. Use flex-flow: row wrap as a shorthand for responsive layouts. Remember that changing flex-direction swaps the main and cross axes — which means justify-content and align-items swap their effects too! This is a common source of confusion for beginners.
26. flex-grow and flex-shrink Properties
These two properties control how flex items grow to fill available space and shrink when there isn’t enough space. They’re essential for creating flexible, responsive layouts.
Overview of Properties
| Property | Description | Default | Values |
|---|---|---|---|
flex-grow | How much an item can grow relative to siblings | 0 | Number (e.g., 1, 2, 3) |
flex-shrink | How much an item can shrink relative to siblings | 1 | Number (e.g., 0, 1, 2) |
1. flex-grow
The flex-grow property specifies the grow factor — how much an item can grow relative to its siblings when there’s available space.
.item1, .item3 {
flex-grow: 1; /* Each takes 1 share */
}
.item2 {
flex-grow: 2; /* Takes 2 shares */
}
How It Works
The available space is divided among items based on their grow factors:
Total grow factors = 1 + 2 + 1 = 4
Item 1: 1/4 of available space (25%)
Item 2: 2/4 of available space (50%)
Item 3: 1/4 of available space (25%)
Values
| Value | Description | Example |
|---|---|---|
0 | Item does not grow (default) | flex-grow: 0; |
1 | Item grows to fill available space | flex-grow: 1; |
2 | Item grows twice as much as flex-grow: 1 | flex-grow: 2; |
3+ | Higher values grow more | flex-grow: 3; |
Important: flex-grow only applies when there is extra space in the container. If items already fill the container, flex-grow has no effect.
2. flex-shrink
The flex-shrink property specifies the shrink factor — how much an item can shrink relative to its siblings when there isn’t enough space.
.item4, .item5 {
flex-shrink: 1; /* Each shrinks by 1 share */
}
.item6 {
flex-shrink: 2; /* Shrinks by 2 shares */
}
How It Works
When the total size of items exceeds the container, items shrink based on their shrink factors:
Total shrink factors = 1 + 1 + 2 = 4
Item 4: 1/4 of the overflow is removed (25% shrink)
Item 5: 1/4 of the overflow is removed (25% shrink)
Item 6: 2/4 of the overflow is removed (50% shrink)
Values
| Value | Description | Example |
|---|---|---|
0 | Item does not shrink | flex-shrink: 0; |
1 | Item shrinks proportionally (default) | flex-shrink: 1; |
2 | Item shrinks twice as much as flex-shrink: 1 | flex-shrink: 2; |
3+ | Higher values shrink more | flex-shrink: 3; |
Important: flex-shrink only applies when items overflow the container. If there’s enough space, flex-shrink has no effect.
flex-grow vs flex-shrink
| Aspect | flex-grow | flex-shrink |
|---|---|---|
| When it applies | When there’s extra space | When there’s not enough space |
| Effect | Items get bigger | Items get smaller |
| Default | 0 (no growth) | 1 (can shrink) |
| Higher value | Grows more | Shrinks more |
Value 0 | Won’t grow | Won’t shrink |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>flex-grow and flex-shrink Properties</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== FLEX CONTAINERS ====== */
.container {
display: flex;
border: 2px solid #333;
margin: 15px 0;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
gap: 10px;
}
.item {
height: 100px;
border: 3px solid #6c5ce7;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 1.2rem;
}
.item1 { background: #007bff; }
.item2 { background: #28a745; }
.item3 { background: #dc3545; }
.item4 { background: #ffc107; color: #333; }
.item5 { background: #17a2b8; }
.item6 { background: #e17055; }
/* Grow demos */
.grow-demo-1 .item1,
.grow-demo-1 .item3 {
flex-grow: 1;
}
.grow-demo-1 .item2 {
flex-grow: 2;
}
.grow-demo-2 .item1 {
flex-grow: 0;
}
.grow-demo-2 .item2 {
flex-grow: 1;
}
.grow-demo-2 .item3 {
flex-grow: 1;
}
.grow-demo-3 .item1 {
flex-grow: 3;
}
.grow-demo-3 .item2 {
flex-grow: 1;
}
.grow-demo-3 .item3 {
flex-grow: 1;
}
/* Shrink demos */
.shrink-container {
width: 500px;
}
.shrink-demo-1 .item4,
.shrink-demo-1 .item5 {
flex-shrink: 1;
width: 200px;
}
.shrink-demo-1 .item6 {
flex-shrink: 2;
width: 200px;
}
.shrink-demo-2 .item4 {
flex-shrink: 0;
width: 200px;
}
.shrink-demo-2 .item5 {
flex-shrink: 1;
width: 200px;
}
.shrink-demo-2 .item6 {
flex-shrink: 1;
width: 200px;
}
/* Side-by-side comparison */
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.comparison-item {
text-align: center;
}
.comparison-item h4 {
margin-bottom: 5px;
color: #007bff;
}
.comparison-item .container {
margin: 5px 0;
}
/* Visual explanation */
.visual-explanation {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
margin: 20px 0;
}
.visual-box {
text-align: center;
}
.visual-box .bar {
height: 30px;
background: #007bff;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.8rem;
font-weight: bold;
margin: 5px 0;
}
.visual-box .bar.grow-1 {
background: #007bff;
width: 60px;
}
.visual-box .bar.grow-2 {
background: #28a745;
width: 120px;
}
.visual-box .bar.grow-3 {
background: #dc3545;
width: 180px;
}
</style>
</head>
<body>
<h1>flex-grow and flex-shrink Properties</h1>
<!-- ====== 1. FLEX-GROW ====== -->
<section>
<h2>1. flex-grow</h2>
<p>Specifies how much an item can <strong>grow</strong> relative to its siblings when there's available space.</p>
<h3>flex-grow: 1 | 2 | 1</h3>
<div class="container grow-demo-1">
<div class="item item1">1 (grow: 1)</div>
<div class="item item2">2 (grow: 2)</div>
<div class="item item3">3 (grow: 1)</div>
</div>
<p class="note">Total grow factors = 4. Item 1 and 3 get 25% each, Item 2 gets 50%.</p>
<h3>flex-grow: 0 | 1 | 1</h3>
<div class="container grow-demo-2">
<div class="item item1">1 (grow: 0)</div>
<div class="item item2">2 (grow: 1)</div>
<div class="item item3">3 (grow: 1)</div>
</div>
<p class="note">Item 1 doesn't grow. Items 2 and 3 share the available space equally.</p>
<h3>flex-grow: 3 | 1 | 1</h3>
<div class="container grow-demo-3">
<div class="item item1">1 (grow: 3)</div>
<div class="item item2">2 (grow: 1)</div>
<div class="item item3">3 (grow: 1)</div>
</div>
<p class="note">Total grow factors = 5. Item 1 gets 60%, Items 2 and 3 get 20% each.</p>
<div class="code-block">
/* Item 1 and 3 grow equally, Item 2 grows twice as much */
.item1, .item3 { flex-grow: 1; }
.item2 { flex-grow: 2; }
/* Item 1 doesn't grow, Items 2 and 3 grow equally */
.item1 { flex-grow: 0; }
.item2, .item3 { flex-grow: 1; }
/* Item 1 grows 3x, Items 2 and 3 grow 1x each */
.item1 { flex-grow: 3; }
.item2, .item3 { flex-grow: 1; }
</div>
</section>
<!-- ====== 2. FLEX-SHRINK ====== -->
<section>
<h2>2. flex-shrink</h2>
<p>Specifies how much an item can <strong>shrink</strong> relative to its siblings when there isn't enough space.</p>
<h3>flex-shrink: 1 | 1 | 2</h3>
<div class="container shrink-container shrink-demo-1">
<div class="item item4">4 (shrink: 1)</div>
<div class="item item5">5 (shrink: 1)</div>
<div class="item item6">6 (shrink: 2)</div>
</div>
<p class="note">Container is 500px wide. Items are 200px each (600px total). Item 6 shrinks twice as much.</p>
<h3>flex-shrink: 0 | 1 | 1</h3>
<div class="container shrink-container shrink-demo-2">
<div class="item item4">4 (shrink: 0)</div>
<div class="item item5">5 (shrink: 1)</div>
<div class="item item6">6 (shrink: 1)</div>
</div>
<p class="note">Item 4 doesn't shrink. Items 5 and 6 share the overflow reduction equally.</p>
<div class="code-block">
/* Items 4 and 5 shrink equally, Item 6 shrinks twice as much */
.item4, .item5 { flex-shrink: 1; width: 200px; }
.item6 { flex-shrink: 2; width: 200px; }
/* Item 4 doesn't shrink, Items 5 and 6 shrink equally */
.item4 { flex-shrink: 0; width: 200px; }
.item5, .item6 { flex-shrink: 1; width: 200px; }
</div>
</section>
<!-- ====== 3. SIDE-BY-SIDE COMPARISON ====== -->
<section>
<h2>3. Side-by-Side Comparison</h2>
<h3>flex-grow</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>grow: 1 | 1 | 1</h4>
<div class="container" style="gap: 5px;">
<div class="item item1" style="flex-grow: 1; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item2" style="flex-grow: 1; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item3" style="flex-grow: 1; height: 60px; font-size: 0.8rem;">1</div>
</div>
<p class="note">Equal growth</p>
</div>
<div class="comparison-item">
<h4>grow: 1 | 2 | 1</h4>
<div class="container" style="gap: 5px;">
<div class="item item1" style="flex-grow: 1; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item2" style="flex-grow: 2; height: 60px; font-size: 0.8rem;">2</div>
<div class="item item3" style="flex-grow: 1; height: 60px; font-size: 0.8rem;">1</div>
</div>
<p class="note">Middle grows more</p>
</div>
<div class="comparison-item">
<h4>grow: 0 | 1 | 1</h4>
<div class="container" style="gap: 5px;">
<div class="item item1" style="flex-grow: 0; height: 60px; font-size: 0.8rem;">0</div>
<div class="item item2" style="flex-grow: 1; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item3" style="flex-grow: 1; height: 60px; font-size: 0.8rem;">1</div>
</div>
<p class="note">First doesn't grow</p>
</div>
</div>
<h3 style="margin-top: 30px;">flex-shrink</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>shrink: 1 | 1 | 1</h4>
<div class="container" style="width: 100%; gap: 5px;">
<div class="item item4" style="flex-shrink: 1; width: 200px; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item5" style="flex-shrink: 1; width: 200px; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item6" style="flex-shrink: 1; width: 200px; height: 60px; font-size: 0.8rem;">1</div>
</div>
<p class="note">Equal shrink</p>
</div>
<div class="comparison-item">
<h4>shrink: 1 | 1 | 2</h4>
<div class="container" style="width: 100%; gap: 5px;">
<div class="item item4" style="flex-shrink: 1; width: 200px; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item5" style="flex-shrink: 1; width: 200px; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item6" style="flex-shrink: 2; width: 200px; height: 60px; font-size: 0.8rem;">2</div>
</div>
<p class="note">Last shrinks more</p>
</div>
<div class="comparison-item">
<h4>shrink: 0 | 1 | 1</h4>
<div class="container" style="width: 100%; gap: 5px;">
<div class="item item4" style="flex-shrink: 0; width: 200px; height: 60px; font-size: 0.8rem;">0</div>
<div class="item item5" style="flex-shrink: 1; width: 200px; height: 60px; font-size: 0.8rem;">1</div>
<div class="item item6" style="flex-shrink: 1; width: 200px; height: 60px; font-size: 0.8rem;">1</div>
</div>
<p class="note">First doesn't shrink</p>
</div>
</div>
</section>
<!-- ====== 4. VISUAL EXPLANATION ====== -->
<section>
<h2>4. Visual Explanation</h2>
<h3>How flex-grow works</h3>
<div class="visual-explanation">
<div class="visual-box">
<div class="bar grow-1">grow: 1</div>
<p class="note">1 share</p>
</div>
<div class="visual-box">
<div class="bar grow-2">grow: 2</div>
<p class="note">2 shares</p>
</div>
<div class="visual-box">
<div class="bar grow-3">grow: 3</div>
<p class="note">3 shares</p>
</div>
</div>
<p class="note" style="text-align: center;">
Total = 6 shares. The available space is divided proportionally: 1/6, 2/6, 3/6.
</p>
<div class="code-block">
/* Available space is divided based on grow factors */
.item1 { flex-grow: 1; } /* Gets 1/6 of space */
.item2 { flex-grow: 2; } /* Gets 2/6 of space */
.item3 { flex-grow: 3; } /* Gets 3/6 of space */
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>flex-grow vs flex-shrink</h3>
<table class="reference-table">
<tr>
<th>Aspect</th>
<th>flex-grow</th>
<th>flex-shrink</th>
</tr>
<tr>
<td><strong>When it applies</strong></td>
<td>When there's extra space</td>
<td>When there's not enough space</td>
</tr>
<tr>
<td><strong>Effect</strong></td>
<td>Items get bigger</td>
<td>Items get smaller</td>
</tr>
<tr>
<td><strong>Default value</strong></td>
<td><code>0</code></td>
<td><code>1</code></td>
</tr>
<tr>
<td><strong>Value 0</strong></td>
<td>Won't grow</td>
<td>Won't shrink</td>
</tr>
<tr>
<td><strong>Higher value</strong></td>
<td>Grows more</td>
<td>Shrinks more</td>
</tr>
</table>
<h3>Common Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>flex-grow</th>
<th>flex-shrink</th>
</tr>
<tr>
<td><code>0</code></td>
<td>No growth</td>
<td>No shrinking</td>
</tr>
<tr>
<td><code>1</code></td>
<td>Normal growth</td>
<td>Normal shrinking (default)</td>
</tr>
<tr>
<td><code>2</code></td>
<td>Grows 2x more</td>
<td>Shrinks 2x more</td>
</tr>
<tr>
<td><code>3+</code></td>
<td>Grows 3x+ more</td>
<td>Shrinks 3x+ more</td>
</tr>
</table>
<h3>flex Shorthand</h3>
<table class="reference-table">
<tr>
<th>Shorthand</th>
<th>Equivalent</th>
<th>Description</th>
</tr>
<tr>
<td><code>flex: 1;</code></td>
<td><code>flex-grow: 1; flex-shrink: 1; flex-basis: 0;</code></td>
<td>Grow equally, shrink equally</td>
</tr>
<tr>
<td><code>flex: auto;</code></td>
<td><code>flex-grow: 1; flex-shrink: 1; flex-basis: auto;</code></td>
<td>Grow and shrink based on content</td>
</tr>
<tr>
<td><code>flex: none;</code></td>
<td><code>flex-grow: 0; flex-shrink: 0; flex-basis: auto;</code></td>
<td>Fixed size, no grow/shrink</td>
</tr>
<tr>
<td><code>flex: 2 1 200px;</code></td>
<td><code>flex-grow: 2; flex-shrink: 1; flex-basis: 200px;</code></td>
<td>Grows 2x, shrinks 1x, base 200px</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>flex-grow: 1</code> to make items fill available space equally</li>
<li>Use <code>flex-shrink: 0</code> to prevent items from shrinking (e.g., icons, buttons)</li>
<li>Use different grow factors to create proportional layouts</li>
<li>Use the <code>flex</code> shorthand for concise code</li>
<li>Test with different container sizes to ensure proper behavior</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>flex-grow</code> when items should have fixed widths</li>
<li>Don't forget that <code>flex-shrink: 0</code> can cause overflow on small screens</li>
<li>Don't confuse <code>flex-grow</code> (extra space) with <code>flex-shrink</code> (overflow)</li>
<li>Don't use large grow factors without understanding the proportional distribution</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Default | Values |
|---|---|---|---|
flex-grow | How much an item grows | 0 | Number |
flex-shrink | How much an item shrinks | 1 | Number |
flex-grow vs flex-shrink
| Aspect | flex-grow | flex-shrink |
|---|---|---|
| When it applies | Extra space | Not enough space |
| Effect | Items get bigger | Items get smaller |
| Default | 0 (no growth) | 1 (can shrink) |
| Value 0 | Won’t grow | Won’t shrink |
| Higher value | Grows more | Shrinks more |
flex Shorthand
| Shorthand | Equivalent | Description |
|---|---|---|
flex: 1; | flex-grow: 1; flex-shrink: 1; flex-basis: 0; | Grow equally |
flex: auto; | flex-grow: 1; flex-shrink: 1; flex-basis: auto; | Based on content |
flex: none; | flex-grow: 0; flex-shrink: 0; flex-basis: auto; | Fixed size |
flex: 2 1 200px; | flex-grow: 2; flex-shrink: 1; flex-basis: 200px; | Custom |
Best Practices
✅ Do This:
/* Equal-width items */
.item {
flex: 1; /* grow: 1, shrink: 1, basis: 0 */
}
/* Proportional widths */
.item1 { flex-grow: 1; }
.item2 { flex-grow: 2; } /* Twice as wide */
.item3 { flex-grow: 1; }
/* Prevent shrinking */
.icon {
flex-shrink: 0; /* Won't shrink */
}
/* Fixed-size item */
.sidebar {
flex: none; /* Won't grow or shrink */
width: 250px;
}
❌ Don’t Do This:
/* Don't use flex-grow without understanding distribution */
.item { flex-grow: 1; } /* Each gets equal share of extra space */
/* Don't prevent shrinking without considering small screens */
.item { flex-shrink: 0; } /* May overflow on mobile */
/* Don't confuse grow and shrink */
.item {
flex-grow: 0; /* Won't grow — applies when extra space */
flex-shrink: 0; /* Won't shrink — applies when overflow */
}
Pro Tip: Think of flex-grow as “how much of the extra space do I get?” and flex-shrink as “how much of the overflow do I absorb?” Use flex: 1 for equal-width items, flex: none for fixed items, and different flex-grow values for proportional layouts. The flex shorthand is almost always cleaner than writing the three longhand properties separately!
27. flex-basis, flex, and flex-flow Properties
These three properties give you precise control over initial sizing, space distribution, and direction/wrapping in Flexbox. They’re essential for building sophisticated, responsive layouts.
Overview of Properties
| Property | Description | Type |
|---|---|---|
flex-basis | Sets the initial main size of a flex item | Longhand |
flex | Shorthand for flex-grow, flex-shrink, flex-basis | Shorthand |
flex-flow | Shorthand for flex-direction and flex-wrap | Shorthand |
1. flex-basis
The flex-basis property specifies the initial main size of a flex item before any free space is distributed.
.item1 {
flex-basis: 100px; /* Starts at 100px before growing/shrinking */
}
Values
| Value | Description | Example |
|---|---|---|
auto | Based on content or width/height (default) | flex-basis: auto; |
length | Fixed size (px, em, rem) | flex-basis: 100px; |
percentage | Percentage of container | flex-basis: 20%; |
content | Based on content size | flex-basis: content; |
max-content | Maximum content size | flex-basis: max-content; |
min-content | Minimum content size | flex-basis: min-content; |
fit-content | Fits content within limits | flex-basis: fit-content(200px); |
Examples
/* Fixed size */
.item1 {
flex-basis: 100px; /* Starts at 100px */
}
/* Percentage of container */
.item3 {
flex-basis: 20%; /* Starts at 20% of container width */
}
/* Relative to font size */
.item4 {
flex-basis: 5em; /* Starts at 5em */
}
/* Content-based */
.item {
flex-basis: content; /* Size based on content */
}
Important: flex-basis is applied before flex-grow and flex-shrink come into play. It’s the starting point for space distribution.
2. flex (Shorthand)
The flex property is a shorthand for flex-grow, flex-shrink, and flex-basis.
.item {
flex: 2 1 100px; /* grow: 2, shrink: 1, basis: 100px */
}
Syntax
flex: <flex-grow> <flex-shrink> <flex-basis>;
flex: <flex-grow> <flex-basis>; /* shrink defaults to 1 */
flex: <flex-grow>; /* shrink: 1, basis: 0 */
Common Values
| Value | Equivalent | Description |
|---|---|---|
flex: none; | 0 0 auto | Fixed size, no grow/shrink |
flex: auto; | 1 1 auto | Based on content, can grow/shrink |
flex: 1; | 1 1 0% | Equal distribution, ignores content |
flex: 2; | 2 1 0% | Grows 2x more than flex: 1 |
flex: 1 1 10em; | 1 1 10em | Grow 1x, shrink 1x, base 10em |
flex: 2 1 100px; | 2 1 100px | Grow 2x, shrink 1x, base 100px |
flex: 1 2 auto; | 1 2 auto | Grow 1x, shrink 2x, base auto |
Special Values
| Shorthand | Equivalent | Description |
|---|---|---|
flex: none | 0 0 auto | No flexibility at all |
flex: auto | 1 1 auto | Fully flexible based on content |
flex: initial | 0 1 auto | Default flex behavior |
Two-Value Syntax
If you provide two values and both are integers, the second is flex-shrink:
flex: 2 2; /* grow: 2, shrink: 2, basis: 0% */
If one is an integer and the other is a length, the integer is flex-grow and the length is flex-basis:
flex: 1 30px; /* grow: 1, shrink: 1, basis: 30px */
3. flex-flow (Shorthand)
The flex-flow property is a shorthand for flex-direction and flex-wrap.
.container {
flex-flow: row wrap; /* direction: row, wrap: wrap */
}
Values
| Part | Values |
|---|---|
flex-direction | row, row-reverse, column, column-reverse |
flex-wrap | nowrap, wrap, wrap-reverse |
Examples
/* Row direction with wrapping */
.container {
flex-flow: row wrap;
}
/* Column direction without wrapping */
.container {
flex-flow: column nowrap;
}
/* Row reverse with wrapping */
.container {
flex-flow: row-reverse wrap;
}
/* Only direction (wrap defaults to nowrap) */
.container {
flex-flow: column;
}
/* Only wrap (direction defaults to row) */
.container {
flex-flow: wrap;
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>flex-basis, flex, and flex-flow Properties</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== FLEX CONTAINERS ====== */
.container {
display: flex;
border: 2px solid #333;
width: 500px;
height: 100px;
padding: 5px;
margin: 15px 0;
background: #e9ecef;
border-radius: 8px;
gap: 5px;
}
.container-small {
width: 300px;
}
.container-tall {
height: 350px;
}
.item {
border: 2px solid #dc3545;
height: 50px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.8rem;
padding: 5px;
background: #007bff;
}
.item:nth-child(even) {
background: #28a745;
}
.item:nth-child(3n) {
background: #6c5ce7;
}
/* flex-basis demos */
.basis-100px {
flex-basis: 100px;
}
.basis-20p {
flex-basis: 20%;
}
.basis-5em {
flex-basis: 5em;
}
.basis-auto {
flex-basis: auto;
}
/* flex shorthand demos */
.flex-1 {
flex: 1;
}
.flex-2 {
flex: 2;
}
.flex-none {
flex: none;
}
.flex-auto {
flex: auto;
}
.flex-2-1-100 {
flex: 2 1 100px;
}
.flex-1-2-auto {
flex: 1 2 auto;
}
.flex-1-1-5em {
flex: 1 1 5em;
}
/* flex-flow demos */
.flow-row-wrap {
flex-flow: row wrap;
}
.flow-column-nowrap {
flex-flow: column nowrap;
}
.flow-row-reverse-wrap {
flex-flow: row-reverse wrap;
}
.item-flow {
border: 2px solid #dc3545;
flex: 0 0 100px;
height: 60px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.8rem;
background: #007bff;
}
.item-flow:nth-child(even) {
background: #28a745;
}
/* Side-by-side comparison */
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.comparison-item {
text-align: center;
}
.comparison-item h4 {
margin-bottom: 5px;
color: #007bff;
}
/* Visual explanation */
.visual-explanation {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
margin: 20px 0;
}
.visual-box {
text-align: center;
}
.visual-box .bar {
height: 30px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.8rem;
font-weight: bold;
margin: 5px 0;
}
.visual-box .bar.basis-100 {
background: #007bff;
width: 100px;
}
.visual-box .bar.basis-50 {
background: #28a745;
width: 50px;
}
.visual-box .bar.basis-150 {
background: #dc3545;
width: 150px;
}
</style>
</head>
<body>
<h1>flex-basis, flex, and flex-flow Properties</h1>
<!-- ====== 1. FLEX-BASIS ====== -->
<section>
<h2>1. flex-basis</h2>
<p>Specifies the <strong>initial main size</strong> of a flex item before any space is distributed.</p>
<div class="container">
<div class="item basis-100px">basis: 100px</div>
<div class="item basis-20p">basis: 20%</div>
<div class="item basis-5em">basis: 5em</div>
</div>
<p class="note">Each item starts at its <code>flex-basis</code> value. The remaining space is distributed based on <code>flex-grow</code>.</p>
<div class="code-block">
.item1 { flex-basis: 100px; } /* Starts at 100px */
.item2 { flex-basis: 20%; } /* Starts at 20% of container */
.item3 { flex-basis: 5em; } /* Starts at 5em */
.item4 { flex-basis: auto; } /* Based on content (default) */
</div>
</section>
<!-- ====== 2. FLEX SHORTHAND ====== -->
<section>
<h2>2. flex (Shorthand)</h2>
<p>Combines <code>flex-grow</code>, <code>flex-shrink</code>, and <code>flex-basis</code>.</p>
<h3>flex: 2 1 100px | 1 2 auto | 1 1 5em</h3>
<div class="container">
<div class="item flex-2-1-100">flex: 2 1 100px</div>
<div class="item flex-1-2-auto">flex: 1 2 auto</div>
<div class="item flex-1-1-5em">flex: 1 1 5em</div>
</div>
<h3>flex: 1 | 2 | none | auto</h3>
<div class="container">
<div class="item flex-1">flex: 1</div>
<div class="item flex-2">flex: 2</div>
<div class="item flex-none">flex: none</div>
<div class="item flex-auto">flex: auto</div>
</div>
<div class="code-block">
/* Longhand version */
.item {
flex-grow: 2;
flex-shrink: 1;
flex-basis: 100px;
}
/* Shorthand version */
.item {
flex: 2 1 100px;
}
/* Common shorthand values */
.item { flex: 1; } /* grow: 1, shrink: 1, basis: 0% */
.item { flex: 2; } /* grow: 2, shrink: 1, basis: 0% */
.item { flex: none; } /* grow: 0, shrink: 0, basis: auto */
.item { flex: auto; } /* grow: 1, shrink: 1, basis: auto */
.item { flex: 1 30px; } /* grow: 1, shrink: 1, basis: 30px */
</div>
</section>
<!-- ====== 3. FLEX-FLOW ====== -->
<section>
<h2>3. flex-flow (Shorthand)</h2>
<p>Combines <code>flex-direction</code> and <code>flex-wrap</code>.</p>
<h3>flex-flow: row wrap</h3>
<div class="container container-small container-tall flow-row-wrap">
<div class="item-flow">1</div>
<div class="item-flow">2</div>
<div class="item-flow">3</div>
<div class="item-flow">4</div>
</div>
<h3>flex-flow: column nowrap</h3>
<div class="container container-small container-tall flow-column-nowrap" style="width: auto; height: 300px;">
<div class="item-flow" style="flex: 0 0 50px;">1</div>
<div class="item-flow" style="flex: 0 0 50px;">2</div>
<div class="item-flow" style="flex: 0 0 50px;">3</div>
<div class="item-flow" style="flex: 0 0 50px;">4</div>
</div>
<h3>flex-flow: row-reverse wrap</h3>
<div class="container container-small container-tall flow-row-reverse-wrap">
<div class="item-flow">1</div>
<div class="item-flow">2</div>
<div class="item-flow">3</div>
<div class="item-flow">4</div>
</div>
<div class="code-block">
/* Row direction with wrapping */
.container { flex-flow: row wrap; }
/* Column direction without wrapping */
.container { flex-flow: column nowrap; }
/* Row reverse with wrapping */
.container { flex-flow: row-reverse wrap; }
/* Only direction (wrap defaults to nowrap) */
.container { flex-flow: column; }
/* Only wrap (direction defaults to row) */
.container { flex-flow: wrap; }
</div>
</section>
<!-- ====== 4. SIDE-BY-SIDE COMPARISON ====== -->
<section>
<h2>4. Side-by-Side Comparison</h2>
<h3>flex-basis vs flex-grow</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>flex-basis: 100px | 100px | 100px</h4>
<div class="container" style="width: 100%;">
<div class="item basis-100px">100px</div>
<div class="item basis-100px">100px</div>
<div class="item basis-100px">100px</div>
</div>
<p class="note">All start at 100px, no growth</p>
</div>
<div class="comparison-item">
<h4>flex: 1 | 1 | 1</h4>
<div class="container" style="width: 100%;">
<div class="item flex-1">flex: 1</div>
<div class="item flex-1">flex: 1</div>
<div class="item flex-1">flex: 1</div>
</div>
<p class="note">All grow equally to fill space</p>
</div>
<div class="comparison-item">
<h4>flex: 1 | 2 | 1</h4>
<div class="container" style="width: 100%;">
<div class="item flex-1">flex: 1</div>
<div class="item flex-2">flex: 2</div>
<div class="item flex-1">flex: 1</div>
</div>
<p class="note">Middle grows twice as much</p>
</div>
</div>
</section>
<!-- ====== 5. VISUAL EXPLANATION ====== -->
<section>
<h2>5. Visual Explanation: flex-basis</h2>
<p>Think of <code>flex-basis</code> as the starting point before any space is distributed.</p>
<div class="visual-explanation">
<div class="visual-box">
<div class="bar basis-50">50px</div>
<p class="note">flex-basis: 50px</p>
</div>
<div class="visual-box">
<div class="bar basis-100">100px</div>
<p class="note">flex-basis: 100px</p>
</div>
<div class="visual-box">
<div class="bar basis-150">150px</div>
<p class="note">flex-basis: 150px</p>
</div>
</div>
<div class="code-block">
/* flex-basis sets the starting size */
.item1 { flex-basis: 50px; }
.item2 { flex-basis: 100px; }
.item3 { flex-basis: 150px; }
/* Then flex-grow distributes remaining space */
.item { flex-grow: 1; }
</div>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>flex-basis Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Based on content or width/height (default)</td>
<td><code>flex-basis: auto;</code></td>
</tr>
<tr>
<td><code>length</code></td>
<td>Fixed size (px, em, rem)</td>
<td><code>flex-basis: 100px;</code></td>
</tr>
<tr>
<td><code>percentage</code></td>
<td>Percentage of container</td>
<td><code>flex-basis: 20%;</code></td>
</tr>
<tr>
<td><code>content</code></td>
<td>Based on content size</td>
<td><code>flex-basis: content;</code></td>
</tr>
<tr>
<td><code>max-content</code></td>
<td>Maximum content size</td>
<td><code>flex-basis: max-content;</code></td>
</tr>
<tr>
<td><code>min-content</code></td>
<td>Minimum content size</td>
<td><code>flex-basis: min-content;</code></td>
</tr>
<tr>
<td><code>fit-content</code></td>
<td>Fits content within limits</td>
<td><code>flex-basis: fit-content(200px);</code></td>
</tr>
</table>
<h3>flex Shorthand Values</h3>
<table class="reference-table">
<tr>
<th>Shorthand</th>
<th>Equivalent</th>
<th>Description</th>
</tr>
<tr>
<td><code>flex: none;</code></td>
<td><code>0 0 auto</code></td>
<td>Fixed size, no grow/shrink</td>
</tr>
<tr>
<td><code>flex: auto;</code></td>
<td><code>1 1 auto</code></td>
<td>Based on content, can grow/shrink</td>
</tr>
<tr>
<td><code>flex: 1;</code></td>
<td><code>1 1 0%</code></td>
<td>Equal distribution, ignores content</td>
</tr>
<tr>
<td><code>flex: 2;</code></td>
<td><code>2 1 0%</code></td>
<td>Grows 2x more than flex: 1</td>
</tr>
<tr>
<td><code>flex: 1 1 10em;</code></td>
<td><code>1 1 10em</code></td>
<td>Grow 1x, shrink 1x, base 10em</td>
</tr>
<tr>
<td><code>flex: 2 1 100px;</code></td>
<td><code>2 1 100px</code></td>
<td>Grow 2x, shrink 1x, base 100px</td>
</tr>
<tr>
<td><code>flex: 1 2 auto;</code></td>
<td><code>1 2 auto</code></td>
<td>Grow 1x, shrink 2x, base auto</td>
</tr>
</table>
<h3>flex-flow Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>row wrap</code></td>
<td>Row direction with wrapping</td>
<td><code>flex-flow: row wrap;</code></td>
</tr>
<tr>
<td><code>column nowrap</code></td>
<td>Column direction without wrapping</td>
<td><code>flex-flow: column nowrap;</code></td>
</tr>
<tr>
<td><code>row-reverse wrap</code></td>
<td>Row reverse with wrapping</td>
<td><code>flex-flow: row-reverse wrap;</code></td>
</tr>
<tr>
<td><code>column</code></td>
<td>Column direction (wrap defaults to nowrap)</td>
<td><code>flex-flow: column;</code></td>
</tr>
<tr>
<td><code>wrap</code></td>
<td>Wrapping (direction defaults to row)</td>
<td><code>flex-flow: wrap;</code></td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>flex: 1</code> for equal-width items that fill available space</li>
<li>Use <code>flex: none</code> for fixed-size items that shouldn't grow or shrink</li>
<li>Use <code>flex-basis</code> to set a starting size before distribution</li>
<li>Use <code>flex-flow</code> to combine direction and wrapping</li>
<li>Use the shorthand properties for cleaner, more readable code</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't confuse <code>flex-basis</code> with <code>width</code> — they behave differently</li>
<li>Don't use <code>flex: 1</code> when you want items to be based on their content (use <code>flex: auto</code>)</li>
<li>Don't forget that <code>flex-basis: auto</code> looks at <code>width</code>/<code>height</code> first</li>
<li>Don't write all three longhand properties when <code>flex</code> shorthand works</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Example |
|---|---|---|
flex-basis | Initial main size | flex-basis: 100px; |
flex | Shorthand for grow, shrink, basis | flex: 2 1 100px; |
flex-flow | Shorthand for direction and wrap | flex-flow: row wrap; |
flex-basis Values
| Value | Description |
|---|---|
auto | Based on content or width/height (default) |
length | Fixed size (px, em, rem) |
percentage | Percentage of container |
content | Based on content size |
max-content | Maximum content size |
min-content | Minimum content size |
fit-content(limit) | Fits content within limits |
flex Shorthand Values
| Shorthand | Equivalent | Description |
|---|---|---|
flex: none; | 0 0 auto | Fixed size, no grow/shrink |
flex: auto; | 1 1 auto | Based on content |
flex: 1; | 1 1 0% | Equal distribution |
flex: 2; | 2 1 0% | Grows 2x more |
flex: 1 1 10em; | 1 1 10em | Grow 1x, shrink 1x, base 10em |
flex: 2 1 100px; | 2 1 100px | Grow 2x, shrink 1x, base 100px |
flex-flow Values
| Value | Description |
|---|---|
row wrap | Row direction with wrapping |
column nowrap | Column direction without wrapping |
row-reverse wrap | Row reverse with wrapping |
column | Column direction (wrap defaults to nowrap) |
wrap | Wrapping (direction defaults to row) |
Best Practices
✅ Do This:
/* Equal-width items */
.item {
flex: 1; /* grow: 1, shrink: 1, basis: 0% */
}
/* Fixed-size item */
.sidebar {
flex: none; /* grow: 0, shrink: 0, basis: auto */
}
/* Content-based item */
.content {
flex: auto; /* grow: 1, shrink: 1, basis: auto */
}
/* Custom sizing */
.item {
flex: 2 1 200px; /* grow: 2, shrink: 1, basis: 200px */
}
/* Direction + wrap */
.container {
flex-flow: row wrap;
}
❌ Don’t Do This:
/* Don't confuse flex-basis with width */
.item {
flex-basis: 100px; /* Initial size before distribution */
width: 100px; /* Ignored in flex context (mostly) */
}
/* Don't use flex: 1 when you want content-based sizing */
.item {
flex: 1; /* Ignores content, uses 0% basis */
/* Use flex: auto for content-based sizing */
}
/* Don't write longhand when shorthand works */
.item {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
/* Just write: flex: 1; */
}
Pro Tip: flex-basis is the starting point for a flex item before any space is distributed. Think of it as “how big should this item be before we start sharing the extra space?” Use flex: 1 for equal-width items that ignore content size, and flex: auto for items that respect their content size. The flex shorthand is almost always cleaner than writing the three longhand properties separately. And flex-flow is a convenient way to set both direction and wrapping in one line!
28. justify-content and align-items Properties
These two properties are the alignment powerhouses of Flexbox. They control how items are distributed and aligned within a flex container along the main and cross axes.
Overview of Properties
| Property | Axis | Description |
|---|---|---|
justify-content | Main axis | Distributes extra space between/around items |
align-items | Cross axis | Aligns items within the container |
1. justify-content
The justify-content property specifies how extra space is distributed between and around items along the main axis.
.container {
display: flex;
justify-content: space-between;
}
Values
| Value | Description | Visual |
|---|---|---|
flex-start | Items packed at the start (default) | [1][2][3]__________ |
flex-end | Items packed at the end | __________[1][2][3] |
center | Items centered | _____[1][2][3]_____ |
space-between | First at start, last at end, equal space between | [1]___[2]___[3] |
space-around | Equal space around each item | _[1]__[2]__[3]_ |
space-evenly | Equal space everywhere | __[1]__[2]__[3]__ |
stretch | Items stretch to fill space | [1][2][3] (stretched) |
Visual Comparison
flex-start: [1][2][3]__________
flex-end: __________[1][2][3]
center: _____[1][2][3]_____
space-between: [1]___[2]___[3]
space-around: _[1]__[2]__[3]_
space-evenly: __[1]__[2]__[3]__
Key Difference:
space-between— no space at the edgesspace-around— half space at the edgesspace-evenly— equal space everywhere
2. align-items
The align-items property specifies how items are aligned along the cross axis.
.container {
display: flex;
align-items: center;
}
Values
| Value | Description | Visual |
|---|---|---|
stretch | Items stretch to fill the container height (default) | Items fill height |
flex-start | Items aligned to the top | [1][2][3] |
flex-end | Items aligned to the bottom | [1][2][3] |
center | Items centered vertically | [1][2][3] |
baseline | Items aligned to their text baseline | Text baselines align |
start | Logical start of cross axis | Same as flex-start (for LTR) |
end | Logical end of cross axis | Same as flex-end (for LTR) |
Visual Comparison
stretch: [1][2][3] (tall items)
flex-start: [1][2][3]
flex-end:
[1][2][3]
center:
[1][2][3]
baseline: [1][2][3] (text baselines align)
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>justify-content and align-items Properties</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== FLEX CONTAINERS ====== */
.container {
display: flex;
border: 2px dashed #dc3545;
margin: 10px 0;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
min-height: 80px;
}
.container-tall {
min-height: 150px;
}
.item {
background: #007bff;
color: white;
padding: 15px 20px;
margin: 5px;
border: 2px dashed #0056b3;
border-radius: 6px;
font-weight: bold;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.item:nth-child(even) {
background: #28a745;
border-color: #1e7e34;
}
.item:nth-child(3n) {
background: #6c5ce7;
border-color: #4a2a9e;
}
/* justify-content demos */
.jc-start {
justify-content: flex-start;
}
.jc-end {
justify-content: flex-end;
}
.jc-center {
justify-content: center;
}
.jc-between {
justify-content: space-between;
}
.jc-around {
justify-content: space-around;
}
.jc-evenly {
justify-content: space-evenly;
}
/* align-items demos */
.ai-start {
align-items: flex-start;
}
.ai-end {
align-items: flex-end;
}
.ai-center {
align-items: center;
}
.ai-baseline {
align-items: baseline;
}
.ai-stretch {
align-items: stretch;
}
.ai-stretch .item {
padding: 10px;
}
/* Baseline demo items */
.baseline-item-1 {
font-size: 14px;
padding: 5px 15px;
}
.baseline-item-2 {
font-size: 28px;
padding: 5px 15px;
}
.baseline-item-3 {
font-size: 20px;
padding: 5px 15px;
}
.baseline-item-4 {
font-size: 16px;
padding: 5px 15px;
}
/* Side-by-side comparison */
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.comparison-item {
text-align: center;
}
.comparison-item h4 {
margin-bottom: 5px;
color: #007bff;
}
.comparison-item .container {
margin: 5px 0;
}
/* Visual explanation */
.visual-explanation {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
margin: 20px 0;
}
.visual-row {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
max-width: 600px;
}
.visual-label {
font-weight: bold;
color: #007bff;
min-width: 130px;
text-align: right;
font-size: 0.85rem;
}
.visual-bar {
flex: 1;
height: 40px;
background: #e9ecef;
border-radius: 6px;
position: relative;
display: flex;
align-items: center;
padding: 0 5px;
border: 1px solid #ddd;
}
.visual-bar .dot {
width: 30px;
height: 30px;
background: #007bff;
border-radius: 4px;
margin: 0 3px;
}
.visual-bar .dot:nth-child(even) {
background: #28a745;
}
.visual-bar .dot:nth-child(3n) {
background: #6c5ce7;
}
</style>
</head>
<body>
<h1>justify-content and align-items Properties</h1>
<!-- ====== 1. JUSTIFY-CONTENT ====== -->
<section>
<h2>1. justify-content</h2>
<p>Distributes extra space between and around items along the <strong>main axis</strong>.</p>
<h3>flex-start (default)</h3>
<div class="container jc-start">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>flex-end</h3>
<div class="container jc-end">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>center</h3>
<div class="container jc-center">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>space-between</h3>
<div class="container jc-between">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<p class="note">First item at start, last item at end, equal space between.</p>
<h3>space-around</h3>
<div class="container jc-around">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<p class="note">Equal space around each item. Half space at the edges.</p>
<h3>space-evenly</h3>
<div class="container jc-evenly">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<p class="note">Equal space everywhere — between items and at the edges.</p>
<div class="code-block">
.container { justify-content: flex-start; } /* Default — start */
.container { justify-content: flex-end; } /* End */
.container { justify-content: center; } /* Center */
.container { justify-content: space-between; } /* Space between */
.container { justify-content: space-around; } /* Space around */
.container { justify-content: space-evenly; } /* Equal space */
</div>
</section>
<!-- ====== 2. ALIGN-ITEMS ====== -->
<section>
<h2>2. align-items</h2>
<p>Aligns items along the <strong>cross axis</strong>.</p>
<h3>flex-start</h3>
<div class="container container-tall ai-start">
<div class="item">1</div>
<div class="item" style="padding: 30px 20px;">2 (taller)</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>flex-end</h3>
<div class="container container-tall ai-end">
<div class="item">1</div>
<div class="item" style="padding: 30px 20px;">2 (taller)</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>center</h3>
<div class="container container-tall ai-center">
<div class="item">1</div>
<div class="item" style="padding: 30px 20px;">2 (taller)</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<h3>baseline</h3>
<div class="container container-tall ai-baseline">
<div class="item baseline-item-1">Small</div>
<div class="item baseline-item-2">LARGE</div>
<div class="item baseline-item-3">Medium</div>
<div class="item baseline-item-4">Normal</div>
</div>
<p class="note">Items align to their text baselines, regardless of font size.</p>
<h3>stretch (default)</h3>
<div class="container container-tall ai-stretch">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<p class="note">Items stretch to fill the container's height.</p>
<div class="code-block">
.container { align-items: flex-start; } /* Top */
.container { align-items: flex-end; } /* Bottom */
.container { align-items: center; } /* Center */
.container { align-items: baseline; } /* Text baseline */
.container { align-items: stretch; } /* Fill height (default) */
</div>
</section>
<!-- ====== 3. COMBINING BOTH ====== -->
<section>
<h2>3. Combining justify-content and align-items</h2>
<p>Use both properties together for perfect alignment.</p>
<h3>Perfect Centering: justify-content: center + align-items: center</h3>
<div class="container container-tall" style="justify-content: center; align-items: center;">
<div class="item" style="padding: 30px 40px;">Centered</div>
</div>
<h3>Space Between + Center</h3>
<div class="container container-tall" style="justify-content: space-between; align-items: center;">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<h3>Space Evenly + Flex-End</h3>
<div class="container container-tall" style="justify-content: space-evenly; align-items: flex-end;">
<div class="item">1</div>
<div class="item" style="padding: 25px 20px;">2</div>
<div class="item">3</div>
</div>
<div class="code-block">
/* Perfect centering */
.container {
display: flex;
justify-content: center; /* Main axis */
align-items: center; /* Cross axis */
}
/* Space between + center */
.container {
justify-content: space-between;
align-items: center;
}
/* Space evenly + flex-end */
.container {
justify-content: space-evenly;
align-items: flex-end;
}
</div>
</section>
<!-- ====== 4. SIDE-BY-SIDE COMPARISON ====== -->
<section>
<h2>4. Side-by-Side Comparison</h2>
<h3>justify-content</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>flex-start</h4>
<div class="container jc-start" style="min-height: 60px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 5px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
<div class="comparison-item">
<h4>center</h4>
<div class="container jc-center" style="min-height: 60px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 5px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
<div class="comparison-item">
<h4>space-between</h4>
<div class="container jc-between" style="min-height: 60px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 5px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
<div class="comparison-item">
<h4>space-around</h4>
<div class="container jc-around" style="min-height: 60px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 5px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
<div class="comparison-item">
<h4>space-evenly</h4>
<div class="container jc-evenly" style="min-height: 60px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 5px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
</div>
<h3 style="margin-top: 30px;">align-items</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>flex-start</h4>
<div class="container ai-start" style="min-height: 100px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 20px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
<div class="comparison-item">
<h4>center</h4>
<div class="container ai-center" style="min-height: 100px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 20px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
<div class="comparison-item">
<h4>flex-end</h4>
<div class="container ai-end" style="min-height: 100px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 20px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
<div class="comparison-item">
<h4>stretch</h4>
<div class="container ai-stretch" style="min-height: 100px;">
<div class="item" style="padding: 5px 10px;">1</div>
<div class="item" style="padding: 5px 10px;">2</div>
<div class="item" style="padding: 5px 10px;">3</div>
</div>
</div>
</div>
</section>
<!-- ====== 5. VISUAL EXPLANATION ====== -->
<section>
<h2>5. Visual Explanation</h2>
<p>See how different <code>justify-content</code> values distribute space.</p>
<div class="visual-explanation">
<div class="visual-row">
<div class="visual-label">flex-start:</div>
<div class="visual-bar" style="justify-content: flex-start;">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">flex-end:</div>
<div class="visual-bar" style="justify-content: flex-end;">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">center:</div>
<div class="visual-bar" style="justify-content: center;">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">space-between:</div>
<div class="visual-bar" style="justify-content: space-between;">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">space-around:</div>
<div class="visual-bar" style="justify-content: space-around;">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">space-evenly:</div>
<div class="visual-bar" style="justify-content: space-evenly;">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
</div>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>justify-content Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Space at Edges</th>
</tr>
<tr>
<td><code>flex-start</code></td>
<td>Items packed at start (default)</td>
<td>No</td>
</tr>
<tr>
<td><code>flex-end</code></td>
<td>Items packed at end</td>
<td>No</td>
</tr>
<tr>
<td><code>center</code></td>
<td>Items centered</td>
<td>No</td>
</tr>
<tr>
<td><code>space-between</code></td>
<td>Equal space between items</td>
<td>No</td>
</tr>
<tr>
<td><code>space-around</code></td>
<td>Equal space around items</td>
<td>Half space</td>
</tr>
<tr>
<td><code>space-evenly</code></td>
<td>Equal space everywhere</td>
<td>Full space</td>
</tr>
<tr>
<td><code>stretch</code></td>
<td>Items stretch to fill space</td>
<td>—</td>
</tr>
</table>
<h3>align-items Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>stretch</code></td>
<td>Items stretch to fill container (default)</td>
</tr>
<tr>
<td><code>flex-start</code> / <code>start</code></td>
<td>Items aligned to the start</td>
</tr>
<tr>
<td><code>flex-end</code> / <code>end</code></td>
<td>Items aligned to the end</td>
</tr>
<tr>
<td><code>center</code></td>
<td>Items centered</td>
</tr>
<tr>
<td><code>baseline</code></td>
<td>Items aligned to text baseline</td>
</tr>
</table>
<h3>Common Combinations</h3>
<table class="reference-table">
<tr>
<th>Combination</th>
<th>Effect</th>
</tr>
<tr>
<td><code>justify-content: center;<br>align-items: center;</code></td>
<td>Perfect centering</td>
</tr>
<tr>
<td><code>justify-content: space-between;<br>align-items: center;</code></td>
<td>Items spread out, vertically centered</td>
</tr>
<tr>
<td><code>justify-content: center;<br>align-items: flex-end;</code></td>
<td>Horizontally centered, aligned to bottom</td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>justify-content: center; align-items: center;</code> for perfect centering</li>
<li>Use <code>space-between</code> for navigation bars and toolbars</li>
<li>Use <code>space-evenly</code> for evenly distributed items</li>
<li>Use <code>align-items: baseline</code> for text of different sizes</li>
<li>Remember that <code>justify-content</code> works on the main axis and <code>align-items</code> on the cross axis</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't confuse <code>justify-content</code> (main axis) with <code>align-items</code> (cross axis)</li>
<li>Don't forget that the axes swap when you change <code>flex-direction</code></li>
<li>Don't use <code>space-between</code> when you need space at the edges</li>
<li>Don't use <code>align-items</code> on a single item (use <code>align-self</code> instead)</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Axis | Values |
|---|---|---|
justify-content | Main axis | flex-start, flex-end, center, space-between, space-around, space-evenly |
align-items | Cross axis | stretch, flex-start, flex-end, center, baseline |
Space Distribution Comparison
| Value | Space Between Items | Space at Edges |
|---|---|---|
flex-start | None | All at end |
flex-end | None | All at start |
center | None | Equal at both ends |
space-between | Equal | None |
space-around | Equal | Half space |
space-evenly | Equal | Full space |
Best Practices
✅ Do This:
/* Perfect centering */
.container {
display: flex;
justify-content: center;
align-items: center;
}
/* Navigation bar */
.nav {
display: flex;
justify-content: space-between;
align-items: center;
}
/* Evenly distributed items */
.toolbar {
display: flex;
justify-content: space-evenly;
}
/* Baseline alignment for mixed font sizes */
.baseline-demo {
display: flex;
align-items: baseline;
}
❌ Don’t Do This:
/* Don't confuse the axes */
.container {
justify-content: center; /* Main axis */
align-items: center; /* Cross axis */
}
/* Don't forget axes swap with flex-direction */
.container {
flex-direction: column;
justify-content: center; /* Now controls VERTICAL alignment */
align-items: center; /* Now controls HORIZONTAL alignment */
}
/* Don't use align-items for single item */
.item {
align-items: center; /* Invalid — use align-self */
}
Pro Tip: The combination justify-content: center; align-items: center; is the classic “perfect centering” trick that works in both Flexbox and Grid. Remember: justify-content works on the main axis (controlled by flex-direction), while align-items works on the cross axis. When you change flex-direction to column, these two properties swap their visual effects — justify-content now controls vertical alignment, and align-items controls horizontal alignment!
29. align-content and align-self Properties
These two properties complete the Flexbox alignment toolkit. align-content controls how multiple lines are distributed along the cross axis, while align-self lets you override the container’s align-items for individual items.
Overview of Properties
| Property | Applies To | Description |
|---|---|---|
align-content | Container | Aligns multiple lines along the cross axis |
align-self | Item | Overrides align-items for a single item |
1. align-content
The align-content property controls the alignment of flex lines along the cross axis. It only works when:
- The container has
flex-wrap: wrap(orwrap-reverse) - There are multiple lines of items
- There is extra space in the container
.container {
display: flex;
flex-wrap: wrap;
height: 400px;
align-content: center;
}
Values
| Value | Description | Visual |
|---|---|---|
stretch | Lines stretch to fill container (default) | Lines fill height |
flex-start | Lines packed at start | [1][2][3][4] |
flex-end | Lines packed at end | [1][2][3][4] |
center | Lines centered | [1][2][3][4] |
space-between | First at start, last at end | [1][2][3][4] |
space-around | Equal space around each line | [1][2][3][4] |
space-evenly | Equal space everywhere | [1][2][3][4] |
Important: align-content has no effect if:
flex-wrap: nowrap(single line)- There’s only one line of items
- There’s no extra space in the container
2. align-self
The align-self property allows an individual flex item to override the container’s align-items value.
.item2 {
align-self: flex-start; /* This item aligns differently */
}
Values
Same as align-items:
| Value | Description |
|---|---|
auto | Inherits from parent’s align-items (default) |
stretch | Stretches to fill container |
flex-start | Aligns to the start |
flex-end | Aligns to the end |
center | Centers the item |
baseline | Aligns to text baseline |
align-content vs align-items
| Aspect | align-content | align-items |
|---|---|---|
| Applies to | Multiple lines | Items within a line |
| When it works | flex-wrap: wrap + multiple lines | Always (single or multi-line) |
| Axis | Cross axis | Cross axis |
| Controls | Distribution of lines | Alignment of items |
| Number of lines | Only multi-line | Single or multi-line |
Visual difference:
align-items: center align-content: center
┌─────────────────┐ ┌─────────────────┐
│ │ │ │
│ [1] [2] [3] │ │ [1] [2] [3] │
│ │ │ [4] [5] [6] │
│ │ │ │
└─────────────────┘ └─────────────────┘
Items centered Lines centered
within each line as a group
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>align-content and align-self Properties</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== FLEX CONTAINERS ====== */
.container {
display: flex;
flex-wrap: wrap;
height: 300px;
border: 2px solid #333;
margin: 10px 0;
padding: 5px;
background: #e9ecef;
border-radius: 8px;
gap: 5px;
}
.item {
background: #007bff;
color: white;
padding: 15px;
margin: 3px;
text-align: center;
width: 200px;
border-radius: 6px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
}
.item:nth-child(even) {
background: #28a745;
}
.item:nth-child(3n) {
background: #6c5ce7;
}
/* align-content demos */
.ac-stretch {
align-content: stretch;
}
.ac-start {
align-content: flex-start;
}
.ac-end {
align-content: flex-end;
}
.ac-center {
align-content: center;
}
.ac-between {
align-content: space-between;
}
.ac-around {
align-content: space-around;
}
.ac-evenly {
align-content: space-evenly;
}
/* align-self demo */
.container-self {
display: flex;
height: 300px;
border: 2px solid #333;
margin: 10px 0;
padding: 5px;
background: #e9ecef;
border-radius: 8px;
gap: 5px;
}
.container-self .item {
width: auto;
flex: 1;
min-width: 80px;
}
.container-self .item1 {
align-self: stretch;
background: #007bff;
}
.container-self .item2 {
align-self: flex-start;
background: #28a745;
}
.container-self .item3 {
align-self: flex-end;
background: #dc3545;
}
.container-self .item4 {
align-self: center;
background: #ffc107;
color: #333;
}
.container-self .item5 {
align-self: baseline;
background: #17a2b8;
}
/* Comparison grid */
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.comparison-item {
text-align: center;
}
.comparison-item h4 {
margin-bottom: 5px;
color: #007bff;
}
.comparison-item .container {
margin: 5px 0;
height: 200px;
}
.comparison-item .item {
width: auto;
flex: 1;
min-width: 60px;
font-size: 0.8rem;
padding: 10px 5px;
}
/* Visual explanation */
.visual-explanation {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
margin: 20px 0;
}
.visual-row {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
max-width: 700px;
}
.visual-label {
font-weight: bold;
color: #007bff;
min-width: 160px;
text-align: right;
font-size: 0.85rem;
}
.visual-bar {
flex: 1;
height: 120px;
background: #e9ecef;
border-radius: 6px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10px;
border: 1px solid #ddd;
gap: 5px;
}
.visual-bar .line {
display: flex;
gap: 5px;
padding: 5px 10px;
background: #007bff;
border-radius: 4px;
color: white;
font-size: 0.7rem;
font-weight: bold;
}
.visual-bar .line:nth-child(even) {
background: #28a745;
}
</style>
</head>
<body>
<h1>align-content and align-self Properties</h1>
<!-- ====== 1. ALIGN-CONTENT ====== -->
<section>
<h2>1. align-content</h2>
<p>Aligns <strong>multiple lines</strong> along the cross axis. Only works with <code>flex-wrap: wrap</code> and multiple lines.</p>
<h3>align-content: stretch (default)</h3>
<div class="container ac-stretch">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<p class="note">Lines stretch to fill the container's height.</p>
<h3>align-content: flex-start</h3>
<div class="container ac-start">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<h3>align-content: flex-end</h3>
<div class="container ac-end">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<h3>align-content: center</h3>
<div class="container ac-center">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<h3>align-content: space-between</h3>
<div class="container ac-between">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<h3>align-content: space-around</h3>
<div class="container ac-around">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<h3>align-content: space-evenly</h3>
<div class="container ac-evenly">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
</div>
<div class="code-block">
.container {
display: flex;
flex-wrap: wrap;
height: 300px;
align-content: stretch; /* Default — fills height */
align-content: flex-start; /* Packed at start */
align-content: flex-end; /* Packed at end */
align-content: center; /* Centered */
align-content: space-between;/* First at start, last at end */
align-content: space-around; /* Equal space around lines */
align-content: space-evenly; /* Equal space everywhere */
}
</div>
</section>
<!-- ====== 2. ALIGN-SELF ====== -->
<section>
<h2>2. align-self</h2>
<p>Overrides <code>align-items</code> for an <strong>individual item</strong>.</p>
<div class="container-self">
<div class="item item1">align-self: stretch</div>
<div class="item item2">align-self: flex-start</div>
<div class="item item3">align-self: flex-end</div>
<div class="item item4">align-self: center</div>
<div class="item item5">align-self: baseline</div>
</div>
<p class="note">Each item has a different <code>align-self</code> value, overriding the container's default alignment.</p>
<div class="code-block">
.item1 { align-self: stretch; } /* Stretches to fill */
.item2 { align-self: flex-start; } /* Top */
.item3 { align-self: flex-end; } /* Bottom */
.item4 { align-self: center; } /* Center */
.item5 { align-self: baseline; } /* Text baseline */
</div>
</section>
<!-- ====== 3. SIDE-BY-SIDE COMPARISON ====== -->
<section>
<h2>3. Side-by-Side Comparison</h2>
<h3>align-content (multi-line distribution)</h3>
<div class="comparison-grid">
<div class="comparison-item">
<h4>stretch (default)</h4>
<div class="container ac-stretch">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</div>
<div class="comparison-item">
<h4>flex-start</h4>
<div class="container ac-start">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</div>
<div class="comparison-item">
<h4>center</h4>
<div class="container ac-center">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</div>
<div class="comparison-item">
<h4>space-between</h4>
<div class="container ac-between">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</div>
<div class="comparison-item">
<h4>space-around</h4>
<div class="container ac-around">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</div>
<div class="comparison-item">
<h4>space-evenly</h4>
<div class="container ac-evenly">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</div>
</div>
</section>
<!-- ====== 4. VISUAL EXPLANATION ====== -->
<section>
<h2>4. Visual Explanation</h2>
<p>See how <code>align-content</code> distributes multiple lines.</p>
<div class="visual-explanation">
<div class="visual-row">
<div class="visual-label">flex-start:</div>
<div class="visual-bar" style="justify-content: flex-start;">
<div class="line">Line 1</div>
<div class="line">Line 2</div>
<div class="line">Line 3</div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">center:</div>
<div class="visual-bar" style="justify-content: center;">
<div class="line">Line 1</div>
<div class="line">Line 2</div>
<div class="line">Line 3</div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">flex-end:</div>
<div class="visual-bar" style="justify-content: flex-end;">
<div class="line">Line 1</div>
<div class="line">Line 2</div>
<div class="line">Line 3</div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">space-between:</div>
<div class="visual-bar" style="justify-content: space-between;">
<div class="line">Line 1</div>
<div class="line">Line 2</div>
<div class="line">Line 3</div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">space-around:</div>
<div class="visual-bar" style="justify-content: space-around;">
<div class="line">Line 1</div>
<div class="line">Line 2</div>
<div class="line">Line 3</div>
</div>
</div>
<div class="visual-row">
<div class="visual-label">space-evenly:</div>
<div class="visual-bar" style="justify-content: space-evenly;">
<div class="line">Line 1</div>
<div class="line">Line 2</div>
<div class="line">Line 3</div>
</div>
</div>
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>align-content Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Space at Edges</th>
</tr>
<tr>
<td><code>stretch</code></td>
<td>Lines stretch to fill container (default)</td>
<td>—</td>
</tr>
<tr>
<td><code>flex-start</code></td>
<td>Lines packed at start</td>
<td>No</td>
</tr>
<tr>
<td><code>flex-end</code></td>
<td>Lines packed at end</td>
<td>No</td>
</tr>
<tr>
<td><code>center</code></td>
<td>Lines centered</td>
<td>Equal at both ends</td>
</tr>
<tr>
<td><code>space-between</code></td>
<td>Equal space between lines</td>
<td>No</td>
</tr>
<tr>
<td><code>space-around</code></td>
<td>Equal space around lines</td>
<td>Half space</td>
</tr>
<tr>
<td><code>space-evenly</code></td>
<td>Equal space everywhere</td>
<td>Full space</td>
</tr>
</table>
<h3>align-self Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Inherits from parent's align-items (default)</td>
</tr>
<tr>
<td><code>stretch</code></td>
<td>Stretches to fill container</td>
</tr>
<tr>
<td><code>flex-start</code></td>
<td>Aligns to the start</td>
</tr>
<tr>
<td><code>flex-end</code></td>
<td>Aligns to the end</td>
</tr>
<tr>
<td><code>center</code></td>
<td>Centers the item</td>
</tr>
<tr>
<td><code>baseline</code></td>
<td>Aligns to text baseline</td>
</tr>
</table>
<h3>align-content vs align-items</h3>
<table class="reference-table">
<tr>
<th>Aspect</th>
<th>align-content</th>
<th>align-items</th>
</tr>
<tr>
<td><strong>Applies to</strong></td>
<td>Multiple lines</td>
<td>Items within a line</td>
</tr>
<tr>
<td><strong>When it works</strong></td>
<td><code>flex-wrap: wrap</code> + multiple lines</td>
<td>Always</td>
</tr>
<tr>
<td><strong>Controls</strong></td>
<td>Distribution of lines</td>
<td>Alignment of items</td>
</tr>
<tr>
<td><strong>Number of lines</strong></td>
<td>Multi-line only</td>
<td>Single or multi-line</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>align-content</code> only with <code>flex-wrap: wrap</code></li>
<li>Use <code>align-self</code> to override alignment for specific items</li>
<li>Use <code>align-content: center</code> to center multiple lines vertically</li>
<li>Use <code>align-self: flex-start</code> for items that need to stick to the top</li>
<li>Remember that <code>align-content</code> has no effect with a single line</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>align-content</code> without <code>flex-wrap: wrap</code></li>
<li>Don't confuse <code>align-content</code> (lines) with <code>align-items</code> (items)</li>
<li>Don't forget that <code>align-self</code> overrides <code>align-items</code> for one item</li>
<li>Don't use <code>align-content</code> on single-line containers</li>
</ul>
</div>
</section>
</body>
</html>
Quick Reference
| Property | Applies To | Description |
|---|---|---|
align-content | Container | Aligns multiple lines along the cross axis |
align-self | Item | Overrides align-items for one item |
align-content Values
| Value | Description |
|---|---|
stretch | Lines stretch to fill container (default) |
flex-start | Lines packed at start |
flex-end | Lines packed at end |
center | Lines centered |
space-between | First at start, last at end |
space-around | Equal space around lines |
space-evenly | Equal space everywhere |
align-content vs align-items
| Aspect | align-content | align-items |
|---|---|---|
| Applies to | Multiple lines | Items within a line |
| When it works | flex-wrap: wrap + multiple lines | Always |
| Controls | Distribution of lines | Alignment of items |
| Single line | No effect | Works |
Best Practices
✅ Do This:
/* Center multiple lines vertically */
.container {
display: flex;
flex-wrap: wrap;
height: 400px;
align-content: center; /* Centers lines as a group */
}
/* Override alignment for one item */
.special-item {
align-self: flex-start; /* This item aligns to the top */
}
/* Distribute lines evenly */
.container {
display: flex;
flex-wrap: wrap;
align-content: space-between;
}
❌ Don’t Do This:
/* Don't use align-content without wrap */
.container {
display: flex;
flex-wrap: nowrap; /* align-content has NO effect */
align-content: center;
}
/* Don't confuse align-content and align-items */
.container {
align-content: center; /* Centers LINES, not items */
align-items: center; /* Centers ITEMS within lines */
}
Pro Tip: align-content is often confused with align-items. The key difference: align-items aligns items within each line, while align-content aligns the lines themselves as a group. Remember: align-content only works when you have multiple lines (i.e., flex-wrap: wrap and enough items to wrap). And align-self is your escape hatch — it lets a single item break away from the container’s align-items rule and align itself independently!
30. Media Queries Introduction
Media queries are the foundation of responsive web design. They allow you to apply different styles based on the user’s device characteristics — screen size, orientation, resolution, and more.
What are Media Queries?
A media query is a CSS technique that applies styles conditionally based on the characteristics of the device or viewport. They’re defined using the @media rule.
@media screen and (min-width: 780px) {
div {
display: flex;
}
}
What happens:
- The styles inside the block are applied only when the condition is true
- The condition is: screen device AND viewport width ≥ 780px
- On smaller screens, the styles are ignored
Media Types
| Type | Description |
|---|---|
all | All devices (default) |
print | Print preview mode and printed pages |
screen | Screens (desktops, laptops, tablets, phones) |
/* All devices */
@media all {
body { margin: 0; }
}
/* Print only */
@media print {
div {
background-color: white;
color: black;
}
}
/* Screen only */
@media screen {
body { background: #f8f9fa; }
}
Logical Operators
| Operator | Description | Example |
|---|---|---|
and | Combines multiple conditions (all must be true) | screen and (min-width: 780px) |
not | Negates a condition | not (hover: hover) |
only | Prevents older browsers from applying styles | only screen and (min-width: 780px) |
, (or) | Either condition can be true | screen, print |
/* AND — both conditions must be true */
@media screen and (min-width: 780px) {
div { display: flex; }
}
/* NOT — condition must be false */
@media not (hover: hover) {
.no-hover { display: none; }
}
/* ONLY — for older browsers */
@media only screen and (min-width: 780px) {
div { display: flex; }
}
/* OR — either condition can be true */
@media screen, print {
body { font-family: Arial, sans-serif; }
}
Common Media Features (Descriptors)
| Feature | Description | Example |
|---|---|---|
width | Viewport width | (min-width: 768px) |
height | Viewport height | (min-height: 600px) |
aspect-ratio | Aspect ratio of viewport | (aspect-ratio: 16/9) |
orientation | Portrait or landscape | (orientation: landscape) |
resolution | Pixel density | (min-resolution: 2dppx) |
hover | Hover capability | (hover: hover) |
pointer | Pointer accuracy | (pointer: fine) |
prefers-color-scheme | Light or dark mode | (prefers-color-scheme: dark) |
prefers-reduced-motion | Reduced motion preference | (prefers-reduced-motion: reduce) |
/* Width-based */
@media (min-width: 768px) and (max-width: 1024px) {
body { font-size: 16px; }
}
/* Orientation-based */
@media (orientation: landscape) {
body { background-color: lightblue; }
}
/* Hover capability */
@media not (hover: hover) {
.no-hover { display: none; }
}
/* Dark mode preference */
@media (prefers-color-scheme: dark) {
body { background: #1a1a1a; color: #fff; }
}
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
* { animation: none; transition: none; }
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Media Queries Introduction</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: background 0.3s, color 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== RESPONSIVE DEMO ====== */
.responsive-demo {
display: block;
padding: 20px;
border: 2px dashed #007bff;
border-radius: 8px;
margin: 15px 0;
background: #e9ecef;
text-align: center;
}
.responsive-demo .icon {
font-size: 3rem;
display: block;
margin-bottom: 10px;
}
.responsive-demo .label {
font-weight: bold;
color: #007bff;
}
/* Default: Mobile (small screens) */
.responsive-demo {
background: #ff6b6b;
color: white;
}
.responsive-demo .label::before {
content: "📱 Mobile";
}
/* Tablet: 768px and up */
@media (min-width: 768px) {
.responsive-demo {
background: #ffc107;
color: #333;
}
.responsive-demo .label::before {
content: "📟 Tablet";
}
}
/* Desktop: 1024px and up */
@media (min-width: 1024px) {
.responsive-demo {
background: #28a745;
color: white;
}
.responsive-demo .label::before {
content: "💻 Desktop";
}
}
/* Large Desktop: 1400px and up */
@media (min-width: 1400px) {
.responsive-demo {
background: #6c5ce7;
color: white;
}
.responsive-demo .label::before {
content: "🖥️ Large Desktop";
}
}
/* ====== ORIENTATION DEMO ====== */
.orientation-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
background: #17a2b8;
color: white;
margin: 15px 0;
}
.orientation-demo::before {
content: "🔄 Portrait Mode";
}
@media (orientation: landscape) {
.orientation-demo {
background: #fd7e14;
}
.orientation-demo::before {
content: "🔄 Landscape Mode";
}
}
/* ====== DARK MODE ====== */
@media (prefers-color-scheme: dark) {
body {
background: #1a1a1a;
color: #f8f9fa;
}
section {
background: #2d2d2d;
color: #f8f9fa;
}
h1 {
color: #4dabf7;
border-bottom-color: #4dabf7;
}
h2 {
color: #69db7c;
border-left-color: #69db7c;
}
h3 {
color: #f8f9fa;
}
.reference-table th {
background: #4dabf7;
}
.reference-table tr:nth-child(even) {
background: #3d3d3d;
}
.reference-table td {
border-color: #555;
}
.responsive-demo {
background: #364fc7;
color: white;
}
.code-block {
background: #0d0d0d;
}
.note {
color: #adb5bd;
}
}
/* ====== HOVER CAPABILITY ====== */
.hover-demo {
padding: 15px;
background: #007bff;
color: white;
border-radius: 8px;
text-align: center;
font-weight: bold;
margin: 15px 0;
}
@media not (hover: hover) {
.hover-demo {
background: #dc3545;
}
.hover-demo::before {
content: "📱 Touch device — hover not available";
}
}
@media (hover: hover) {
.hover-demo:hover {
background: #28a745;
transform: scale(1.02);
transition: all 0.3s;
}
}
/* ====== REDUCED MOTION ====== */
.motion-demo {
padding: 20px;
background: #6c5ce7;
color: white;
border-radius: 8px;
text-align: center;
font-weight: bold;
animation: pulse 2s infinite;
margin: 15px 0;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@media (prefers-reduced-motion: reduce) {
.motion-demo {
animation: none;
}
.motion-demo::after {
content: " (animation disabled)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== PRINT STYLES ====== */
@media print {
body {
background: white;
color: black;
font-size: 12pt;
}
section {
box-shadow: none;
border: 1px solid #ccc;
page-break-inside: avoid;
}
.code-block {
background: #f4f4f4;
color: #333;
border: 1px solid #ddd;
}
.no-print {
display: none;
}
}
/* ====== BREAKPOINT VISUALIZATION ====== */
.breakpoint-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 10px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.9rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
}
.breakpoint-indicator::before {
content: "📱 < 768px";
}
@media (min-width: 768px) {
.breakpoint-indicator {
background: #ffc107;
color: #333;
}
.breakpoint-indicator::before {
content: "📟 768px - 1023px";
}
}
@media (min-width: 1024px) {
.breakpoint-indicator {
background: #28a745;
color: white;
}
.breakpoint-indicator::before {
content: "💻 1024px - 1399px";
}
}
@media (min-width: 1400px) {
.breakpoint-indicator {
background: #6c5ce7;
color: white;
}
.breakpoint-indicator::before {
content: "🖥️ ≥ 1400px";
}
}
</style>
</head>
<body>
<h1>Media Queries Introduction</h1>
<!-- ====== 1. RESPONSIVE DEMO ====== -->
<section>
<h2>1. Responsive Breakpoints</h2>
<p>Resize your browser to see the breakpoint change!</p>
<div class="responsive-demo">
<span class="icon">📱</span>
<span class="label"></span>
<p>Current breakpoint: <span class="highlight" id="current-breakpoint">Resize to see</span></p>
</div>
<div class="code-block">
/* Mobile first — default styles */
.responsive-demo {
background: #ff6b6b;
}
/* Tablet: 768px and up */
@media (min-width: 768px) {
.responsive-demo {
background: #ffc107;
}
}
/* Desktop: 1024px and up */
@media (min-width: 1024px) {
.responsive-demo {
background: #28a745;
}
}
/* Large Desktop: 1400px and up */
@media (min-width: 1400px) {
.responsive-demo {
background: #6c5ce7;
}
}
</div>
</section>
<!-- ====== 2. ORIENTATION ====== -->
<section>
<h2>2. Orientation</h2>
<p>Rotate your device or resize your browser to change orientation.</p>
<div class="orientation-demo"></div>
<div class="code-block">
/* Portrait (default) */
.orientation-demo::before {
content: "Portrait Mode";
}
/* Landscape */
@media (orientation: landscape) {
.orientation-demo {
background: #fd7e14;
}
.orientation-demo::before {
content: "Landscape Mode";
}
}
</div>
</section>
<!-- ====== 3. DARK MODE ====== -->
<section>
<h2>3. Dark Mode (prefers-color-scheme)</h2>
<p>This page automatically adapts to your system's dark mode preference.</p>
<div class="code-block">
@media (prefers-color-scheme: dark) {
body {
background: #1a1a1a;
color: #f8f9fa;
}
section {
background: #2d2d2d;
}
}
</div>
</section>
<!-- ====== 4. HOVER CAPABILITY ====== -->
<section>
<h2>4. Hover Capability</h2>
<p>This element changes style on hover (desktop) or shows a message (touch devices).</p>
<div class="hover-demo">
<span class="no-hover-text">Hover over me (if you can!)</span>
</div>
<div class="code-block">
/* For devices with hover capability */
@media (hover: hover) {
.hover-demo:hover {
background: #28a745;
}
}
/* For touch devices without hover */
@media not (hover: hover) {
.hover-demo {
background: #dc3545;
}
}
</div>
</section>
<!-- ====== 5. REDUCED MOTION ====== -->
<section>
<h2>5. Reduced Motion</h2>
<p>This element respects your system's reduced motion preference.</p>
<div class="motion-demo">
⚡ Animated Element
</div>
<div class="code-block">
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.motion-demo {
animation: pulse 2s infinite;
}
@media (prefers-reduced-motion: reduce) {
.motion-demo {
animation: none;
}
}
</div>
</section>
<!-- ====== 6. PRINT STYLES ====== -->
<section>
<h2>6. Print Styles</h2>
<p>Print this page to see the print-specific styles in action.</p>
<div class="code-block no-print">
@media print {
body {
background: white;
color: black;
}
.no-print {
display: none;
}
}
</div>
<p class="note">This entire section is styled differently when printed.</p>
</section>
<!-- ====== 7. REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>Media Types</h3>
<table class="reference-table">
<tr>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td><code>all</code></td>
<td>All devices (default)</td>
</tr>
<tr>
<td><code>print</code></td>
<td>Print preview mode and printed pages</td>
</tr>
<tr>
<td><code>screen</code></td>
<td>Screens (desktops, laptops, tablets, phones)</td>
</tr>
</table>
<h3>Logical Operators</h3>
<table class="reference-table">
<tr>
<th>Operator</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>and</code></td>
<td>Both conditions must be true</td>
<td><code>screen and (min-width: 780px)</code></td>
</tr>
<tr>
<td><code>not</code></td>
<td>Negates a condition</td>
<td><code>not (hover: hover)</code></td>
</tr>
<tr>
<td><code>only</code></td>
<td>For older browsers</td>
<td><code>only screen and (min-width: 780px)</code></td>
</tr>
<tr>
<td><code>,</code></td>
<td>Either condition can be true</td>
<td><code>screen, print</code></td>
</tr>
</table>
<h3>Common Media Features</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>width</code></td>
<td>Viewport width</td>
<td><code>(min-width: 768px)</code></td>
</tr>
<tr>
<td><code>height</code></td>
<td>Viewport height</td>
<td><code>(min-height: 600px)</code></td>
</tr>
<tr>
<td><code>orientation</code></td>
<td>Portrait or landscape</td>
<td><code>(orientation: landscape)</code></td>
</tr>
<tr>
<td><code>aspect-ratio</code></td>
<td>Aspect ratio of viewport</td>
<td><code>(aspect-ratio: 16/9)</code></td>
</tr>
<tr>
<td><code>resolution</code></td>
<td>Pixel density</td>
<td><code>(min-resolution: 2dppx)</code></td>
</tr>
<tr>
<td><code>hover</code></td>
<td>Hover capability</td>
<td><code>(hover: hover)</code></td>
</tr>
<tr>
<td><code>pointer</code></td>
<td>Pointer accuracy</td>
<td><code>(pointer: fine)</code></td>
</tr>
<tr>
<td><code>prefers-color-scheme</code></td>
<td>Light or dark mode</td>
<td><code>(prefers-color-scheme: dark)</code></td>
</tr>
<tr>
<td><code>prefers-reduced-motion</code></td>
<td>Reduced motion preference</td>
<td><code>(prefers-reduced-motion: reduce)</code></td>
</tr>
</table>
<h3>Common Breakpoints</h3>
<table class="reference-table">
<tr>
<th>Device</th>
<th>Breakpoint</th>
<th>Example</th>
</tr>
<tr>
<td>Mobile</td>
<td>< 768px</td>
<td><code>@media (max-width: 767px)</code></td>
</tr>
<tr>
<td>Tablet</td>
<td>768px – 1023px</td>
<td><code>@media (min-width: 768px) and (max-width: 1023px)</code></td>
</tr>
<tr>
<td>Desktop</td>
<td>1024px – 1399px</td>
<td><code>@media (min-width: 1024px)</code></td>
</tr>
<tr>
<td>Large Desktop</td>
<td>≥ 1400px</td>
<td><code>@media (min-width: 1400px)</code></td>
</tr>
</table>
</section>
<!-- ====== 8. BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li><strong>Mobile First</strong> — Start with the smallest screen and build up</li>
<li>Use <strong>relative units</strong> (em, rem, %) instead of fixed pixels</li>
<li>Aim for a <strong>breakpoint every 50–70 pixels</strong> or so</li>
<li><strong>Test thoroughly</strong> on various devices and screen sizes</li>
<li>Use <code>prefers-color-scheme</code> for dark mode support</li>
<li>Use <code>prefers-reduced-motion</code> for accessibility</li>
<li>Use <code>@media print</code> to optimize for printing</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't <strong>overuse media queries</strong> — too many can slow down your site</li>
<li>Don't use <strong>fixed units</strong> (px) for everything</li>
<li>Don't ignore <strong>accessibility</strong> features like reduced motion</li>
<li>Don't forget to include the <strong>viewport meta tag</strong></li>
<li>Don't design only for one device — test everywhere</li>
</ul>
</div>
</section>
<!-- ====== BREAKPOINT INDICATOR ====== -->
<div class="breakpoint-indicator"></div>
<!-- ====== SCRIPT FOR BREAKPOINT DISPLAY ====== -->
<script>
function updateBreakpoint() {
const width = window.innerWidth;
const el = document.getElementById('current-breakpoint');
if (width < 768) {
el.textContent = '📱 Mobile (< 768px) — ' + width + 'px';
} else if (width < 1024) {
el.textContent = '📟 Tablet (768–1023px) — ' + width + 'px';
} else if (width < 1400) {
el.textContent = '💻 Desktop (1024–1399px) — ' + width + 'px';
} else {
el.textContent = '🖥️ Large Desktop (≥ 1400px) — ' + width + 'px';
}
}
window.addEventListener('resize', updateBreakpoint);
window.addEventListener('load', updateBreakpoint);
updateBreakpoint();
</script>
</body>
</html>
Quick Reference
| Concept | Description | Example |
|---|---|---|
@media | Media query rule | @media screen and (min-width: 768px) |
all | All devices | @media all |
print | Print styles | @media print |
screen | Screen devices | @media screen |
and | Both conditions | screen and (min-width: 768px) |
not | Negation | not (hover: hover) |
only | Legacy browser support | only screen and (min-width: 768px) |
, | OR condition | screen, print |
Common Media Features
| Feature | Description | Example |
|---|---|---|
width | Viewport width | (min-width: 768px) |
height | Viewport height | (min-height: 600px) |
orientation | Portrait/landscape | (orientation: landscape) |
aspect-ratio | Viewport aspect ratio | (aspect-ratio: 16/9) |
resolution | Pixel density | (min-resolution: 2dppx) |
hover | Hover capability | (hover: hover) |
prefers-color-scheme | Dark/light mode | (prefers-color-scheme: dark) |
prefers-reduced-motion | Reduced motion | (prefers-reduced-motion: reduce) |
Best Practices
✅ Do This:
/* Mobile First approach */
/* Base styles for mobile */
.container {
display: block;
padding: 10px;
}
/* Tablet and up */
@media (min-width: 768px) {
.container {
display: flex;
padding: 20px;
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
}
}
❌ Don’t Do This:
/* Don't use too many breakpoints */
@media (min-width: 320px) { }
@media (min-width: 375px) { }
@media (min-width: 414px) { }
@media (min-width: 480px) { }
@media (min-width: 640px) { }
/* Too many — aim for 3-5 major breakpoints */
/* Don't forget the viewport meta tag */
/* <meta name="viewport" content="width=device-width, initial-scale=1.0"> */
Pro Tip: Mobile First is the recommended approach — start with styles for the smallest screens and add complexity as the screen gets larger. Use min-width for breakpoints. Don’t forget the viewport meta tag — without it, media queries won’t work correctly on mobile devices! And always respect user preferences like prefers-color-scheme and prefers-reduced-motion for a more accessible experience.
31. any-hover and any-pointer Media Features
These two media features let you detect the input capabilities of a user’s device — whether they have a mouse that can hover, a touch screen, or both. This is crucial for creating interfaces that work well across all device types.
Overview of Features
| Feature | Description | Values |
|---|---|---|
any-hover | Detects if any pointing device can hover | none, hover |
any-pointer | Detects the accuracy of any pointing device | none, coarse, fine |
1. any-hover
The any-hover media feature detects whether any available pointing device can hover over elements.
@media (any-hover: none) {
.container {
background-color: lightcoral;
}
}
Values
| Value | Description |
|---|---|
none | No pointing device can hover |
hover | At least one pointing device can hover |
Key Points:
- Desktop browsers with a mouse →
any-hover: hover - Mobile browsers with touch input →
any-hover: none - Devices with both mouse and touch →
any-hover: hover(because the mouse can hover)
2. any-pointer
The any-pointer media feature detects the accuracy of any available pointing device.
@media (any-pointer: none) {
.container {
background-color: lightcoral;
}
}
Values
| Value | Description |
|---|---|
none | No pointing device available |
coarse | Pointing device with limited accuracy (e.g., touch, stylus) |
fine | Pointing device with fine accuracy (e.g., mouse, trackpad) |
Key Points:
- Desktop with mouse →
any-pointer: fine - Mobile with touch →
any-pointer: coarse - Devices with both →
any-pointer: fine(because at least one device is fine)
any-hover vs hover
| Feature | Description |
|---|---|
hover | Detects the primary pointing device’s hover capability |
any-hover | Detects if any pointing device can hover |
Example:
- A laptop with a touchscreen and a mouse:
hover: hover(primary device is mouse)any-hover: hover(at least one device can hover)
- A tablet with a Bluetooth mouse:
hover: none(primary device is touch)any-hover: hover(mouse can hover)
any-pointer vs pointer
| Feature | Description |
|---|---|
pointer | Detects the primary pointing device’s accuracy |
any-pointer | Detects the best accuracy among all pointing devices |
Example:
- A laptop with a touchscreen and a mouse:
pointer: fine(primary device is mouse)any-pointer: fine(at least one device is fine)
- A tablet with a Bluetooth mouse:
pointer: coarse(primary device is touch)any-pointer: fine(mouse is fine)
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>any-hover and any-pointer Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO CONTAINER ====== */
.container {
width: 200px;
height: 200px;
background-color: lightblue;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 18px;
border-radius: 12px;
margin: 20px auto;
cursor: pointer;
transition: all 0.3s;
text-align: center;
padding: 20px;
}
.container:hover {
background-color: lightgreen;
transform: scale(1.05);
}
.container .status {
font-size: 0.8rem;
margin-top: 10px;
font-weight: normal;
}
/* ====== DETECTION BADGES ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
font-weight: bold;
background: #e9ecef;
border: 2px solid #ddd;
}
.detection-card .feature-name {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .feature-value {
font-size: 1.1rem;
color: #007bff;
}
/* ====== HOVER CAPABILITY ====== */
.hover-capable {
background: #d4edda;
border-color: #28a745;
color: #155724;
}
.hover-capable .feature-value {
color: #28a745;
}
.hover-not-capable {
background: #f8d7da;
border-color: #dc3545;
color: #721c24;
}
.hover-not-capable .feature-value {
color: #dc3545;
}
/* ====== POINTER CAPABILITY ====== */
.pointer-fine {
background: #cce5ff;
border-color: #007bff;
color: #004085;
}
.pointer-fine .feature-value {
color: #007bff;
}
.pointer-coarse {
background: #fff3cd;
border-color: #ffc107;
color: #856404;
}
.pointer-coarse .feature-value {
color: #856404;
}
.pointer-none {
background: #f8d7da;
border-color: #dc3545;
color: #721c24;
}
.pointer-none .feature-value {
color: #dc3545;
}
/* ====== MEDIA QUERY DEMOS ====== */
@media (any-hover: none) {
.container {
background-color: lightcoral;
}
.container:hover {
background-color: lightcoral;
transform: none;
}
.container .status::after {
content: " (no hover available)";
font-style: italic;
}
}
@media (any-hover: hover) {
.container .status::after {
content: " (hover available!)";
font-style: italic;
}
}
@media (any-pointer: none) {
.pointer-demo {
background-color: #dc3545;
color: white;
}
.pointer-demo::after {
content: " — No pointing device detected";
}
}
@media (any-pointer: coarse) {
.pointer-demo {
background-color: #ffc107;
color: #333;
}
.pointer-demo::after {
content: " — Coarse pointer (touch)";
}
}
@media (any-pointer: fine) {
.pointer-demo {
background-color: #28a745;
color: white;
}
.pointer-demo::after {
content: " — Fine pointer (mouse)";
}
}
/* ====== INTERACTIVE DEMO ====== */
.interactive-demo {
display: flex;
flex-wrap: wrap;
gap: 15px;
justify-content: center;
margin: 20px 0;
}
.interactive-box {
width: 150px;
height: 100px;
background: #007bff;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
}
.interactive-box:hover {
background: #28a745;
transform: scale(1.05);
}
/* ====== BEST PRACTICE DEMO ====== */
.best-practice-card {
background: white;
border: 2px solid #ddd;
border-radius: 12px;
padding: 20px;
max-width: 400px;
margin: 20px auto;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.best-practice-card h3 {
margin-top: 0;
color: #007bff;
}
.best-practice-card .btn {
display: inline-block;
padding: 12px 30px;
background: #007bff;
color: white;
border-radius: 8px;
text-decoration: none;
font-weight: bold;
margin-top: 10px;
transition: all 0.3s;
}
.best-practice-card .btn:hover {
background: #28a745;
transform: scale(1.02);
}
/* Hide hover-only elements on touch devices */
@media (any-hover: none) {
.hover-only {
display: none;
}
}
@media (any-hover: hover) {
.hover-only {
display: inline;
}
}
</style>
</head>
<body>
<h1>any-hover and any-pointer Media Features</h1>
<!-- ====== 1. ANY-HOVER DEMO ====== -->
<section>
<h2>1. any-hover</h2>
<p>Detects whether <strong>any</strong> pointing device can hover over elements.</p>
<div class="container">
<strong>Hover over me!</strong>
<span class="status">Testing any-hover</span>
</div>
<p class="note">On desktop, the box turns green on hover. On touch devices with no hover, it stays coral.</p>
<div class="code-block">
/* Default state */
.container {
background-color: lightblue;
}
.container:hover {
background-color: lightgreen;
}
/* No hover capability */
@media (any-hover: none) {
.container {
background-color: lightcoral;
}
.container:hover {
background-color: lightcoral; /* Disable hover effect */
transform: none;
}
}
</div>
</section>
<!-- ====== 2. ANY-POINTER DEMO ====== -->
<section>
<h2>2. any-pointer</h2>
<p>Detects the <strong>accuracy</strong> of any available pointing device.</p>
<div class="pointer-demo" style="padding: 20px; border-radius: 8px; text-align: center; font-weight: bold; margin: 15px 0;">
Pointer Detection
</div>
<div class="code-block">
/* No pointing device */
@media (any-pointer: none) {
.pointer-demo {
background-color: #dc3545;
color: white;
}
}
/* Coarse pointer (touch) */
@media (any-pointer: coarse) {
.pointer-demo {
background-color: #ffc107;
color: #333;
}
}
/* Fine pointer (mouse) */
@media (any-pointer: fine) {
.pointer-demo {
background-color: #28a745;
color: white;
}
}
</div>
</section>
<!-- ====== 3. DETECTION SUMMARY ====== -->
<section>
<h2>3. Your Device's Capabilities</h2>
<p>Below is a live detection of your device's input capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="any-hover-card">
<span class="feature-name">any-hover</span>
<span class="feature-value" id="any-hover-value">Detecting...</span>
</div>
<div class="detection-card" id="any-pointer-card">
<span class="feature-name">any-pointer</span>
<span class="feature-value" id="any-pointer-value">Detecting...</span>
</div>
<div class="detection-card" id="hover-card">
<span class="feature-name">hover (primary)</span>
<span class="feature-value" id="hover-value">Detecting...</span>
</div>
<div class="detection-card" id="pointer-card">
<span class="feature-name">pointer (primary)</span>
<span class="feature-value" id="pointer-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect any-hover */
@media (any-hover: hover) { /* At least one device can hover */ }
@media (any-hover: none) { /* No device can hover */ }
/* Detect any-pointer */
@media (any-pointer: fine) { /* At least one fine pointer */ }
@media (any-pointer: coarse) { /* At least one coarse pointer */ }
@media (any-pointer: none) { /* No pointer available */ }
</div>
</section>
<!-- ====== 4. PRACTICAL USE CASE ====== -->
<section>
<h2>4. Practical Use Case: Hover-Only Elements</h2>
<p>Some UI elements only make sense with a mouse. Here's how to handle them.</p>
<div class="best-practice-card">
<h3>📱 Touch-Friendly Card</h3>
<p>
This card shows a "hover to see more" message
<span class="hover-only"> — hover over the button to see the effect!</span>
</p>
<a href="#" class="btn">Learn More</a>
</div>
<p class="note">On touch devices, hover-only elements are hidden or replaced with tap-friendly alternatives.</p>
<div class="code-block">
/* Hide hover-only elements on touch devices */
@media (any-hover: none) {
.hover-only {
display: none;
}
}
@media (any-hover: hover) {
.hover-only {
display: inline;
}
}
</div>
</section>
<!-- ====== 5. SIDE-BY-SIDE COMPARISON ====== -->
<section>
<h2>5. Side-by-Side Comparison</h2>
<h3>any-hover vs hover</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Use Case</th>
</tr>
<tr>
<td><code>hover</code></td>
<td>Primary pointing device can hover</td>
<td>Optimizing for the main input method</td>
</tr>
<tr>
<td><code>any-hover</code></td>
<td>At least one pointing device can hover</td>
<td>Detecting if hover effects are possible at all</td>
</tr>
</table>
<h3>any-pointer vs pointer</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Use Case</th>
</tr>
<tr>
<td><code>pointer</code></td>
<td>Primary pointing device accuracy</td>
<td>Optimizing for the main input method</td>
</tr>
<tr>
<td><code>any-pointer</code></td>
<td>Best accuracy among all pointing devices</td>
<td>Detecting if precise input is possible</td>
</tr>
</table>
<h3>Detection Scenarios</h3>
<table class="reference-table">
<tr>
<th>Device</th>
<th>hover</th>
<th>any-hover</th>
<th>pointer</th>
<th>any-pointer</th>
</tr>
<tr>
<td>Desktop with mouse</td>
<td><code>hover</code></td>
<td><code>hover</code></td>
<td><code>fine</code></td>
<td><code>fine</code></td>
</tr>
<tr>
<td>Mobile phone (touch)</td>
<td><code>none</code></td>
<td><code>none</code></td>
<td><code>coarse</code></td>
<td><code>coarse</code></td>
</tr>
<tr>
<td>Tablet with stylus</td>
<td><code>none</code></td>
<td><code>hover</code></td>
<td><code>coarse</code></td>
<td><code>fine</code></td>
</tr>
<tr>
<td>Laptop with touchscreen</td>
<td><code>hover</code></td>
<td><code>hover</code></td>
<td><code>fine</code></td>
<td><code>fine</code></td>
</tr>
<tr>
<td>Tablet with Bluetooth mouse</td>
<td><code>none</code></td>
<td><code>hover</code></td>
<td><code>coarse</code></td>
<td><code>fine</code></td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>any-hover</code> to detect if hover effects are possible at all</li>
<li>Use <code>any-pointer</code> to detect if precise input is available</li>
<li>Provide tap-friendly alternatives for touch devices</li>
<li>Hide hover-only UI elements on touch devices</li>
<li>Test on both desktop and mobile devices</li>
<li>Use <code>hover: hover</code> for primary input optimization</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely solely on hover for critical functionality</li>
<li>Don't confuse <code>hover</code> with <code>any-hover</code></li>
<li>Don't forget that touch devices have no hover capability</li>
<li>Don't hide important content on touch devices</li>
<li>Don't assume all desktops have a mouse (some use touch)</li>
</ul>
</div>
</section>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
// Detect any-hover
function updateAnyHover() {
const card = document.getElementById('any-hover-card');
const value = document.getElementById('any-hover-value');
if (window.matchMedia('(any-hover: hover)').matches) {
value.textContent = 'hover ✅';
card.className = 'detection-card hover-capable';
} else {
value.textContent = 'none ❌';
card.className = 'detection-card hover-not-capable';
}
}
// Detect any-pointer
function updateAnyPointer() {
const card = document.getElementById('any-pointer-card');
const value = document.getElementById('any-pointer-value');
if (window.matchMedia('(any-pointer: fine)').matches) {
value.textContent = 'fine 🖱️';
card.className = 'detection-card pointer-fine';
} else if (window.matchMedia('(any-pointer: coarse)').matches) {
value.textContent = 'coarse 👆';
card.className = 'detection-card pointer-coarse';
} else {
value.textContent = 'none ❌';
card.className = 'detection-card pointer-none';
}
}
// Detect hover (primary)
function updateHover() {
const card = document.getElementById('hover-card');
const value = document.getElementById('hover-value');
if (window.matchMedia('(hover: hover)').matches) {
value.textContent = 'hover ✅';
card.className = 'detection-card hover-capable';
} else {
value.textContent = 'none ❌';
card.className = 'detection-card hover-not-capable';
}
}
// Detect pointer (primary)
function updatePointer() {
const card = document.getElementById('pointer-card');
const value = document.getElementById('pointer-value');
if (window.matchMedia('(pointer: fine)').matches) {
value.textContent = 'fine 🖱️';
card.className = 'detection-card pointer-fine';
} else if (window.matchMedia('(pointer: coarse)').matches) {
value.textContent = 'coarse 👆';
card.className = 'detection-card pointer-coarse';
} else {
value.textContent = 'none ❌';
card.className = 'detection-card pointer-none';
}
}
// Update all on load and resize
function updateAll() {
updateAnyHover();
updateAnyPointer();
updateHover();
updatePointer();
}
window.addEventListener('load', updateAll);
window.addEventListener('resize', updateAll);
updateAll();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
any-hover | Any pointing device can hover | none, hover |
any-pointer | Accuracy of any pointing device | none, coarse, fine |
any-hover vs hover
| Feature | Description |
|---|---|
hover | Primary pointing device’s hover capability |
any-hover | Any pointing device’s hover capability |
any-pointer vs pointer
| Feature | Description |
|---|---|
pointer | Primary pointing device’s accuracy |
any-pointer | Best accuracy among all pointing devices |
Detection Scenarios
| Device | hover | any-hover | pointer | any-pointer |
|---|---|---|---|---|
| Desktop with mouse | hover | hover | fine | fine |
| Mobile phone (touch) | none | none | coarse | coarse |
| Tablet with stylus | none | hover | coarse | fine |
| Laptop with touchscreen | hover | hover | fine | fine |
| Tablet with Bluetooth mouse | none | hover | coarse | fine |
Best Practices
✅ Do This:
/* Hide hover-only elements on touch devices */
@media (any-hover: none) {
.hover-only {
display: none;
}
}
/* Provide tap-friendly alternatives */
@media (any-hover: hover) {
.tooltip:hover .tooltip-text {
display: block;
}
}
/* Detect fine pointers for precision UI */
@media (any-pointer: fine) {
.color-picker {
display: block;
}
}
@media (any-pointer: coarse) {
.color-picker {
display: none; /* Hide precision UI on touch */
}
}
❌ Don’t Do This:
/* Don't rely on hover for critical functionality */
.delete-btn {
display: none; /* Only visible on hover — bad for touch! */
}
.item:hover .delete-btn {
display: block;
}
/* Don't confuse hover with any-hover */
@media (hover: none) {
/* Only checks primary device — may miss secondary devices */
}
/* Don't assume all desktops have a mouse */
@media (min-width: 1024px) {
.hover-effect { /* May fail on touchscreen laptops */ }
}
Pro Tip: Use any-hover and any-pointer to create input-aware interfaces. The key insight is that hover/pointer check the primary input device, while any-hover/any-pointer check all available devices. For example, a tablet with a Bluetooth mouse has hover: none (primary is touch) but any-hover: hover (mouse can hover). Use any-hover to detect if hover effects are possible at all, and any-pointer to detect if precise input is available. Always provide tap-friendly alternatives for touch devices!
32. aspect-ratio and color Media Features
These two media features let you adapt styles based on the shape of the viewport and the color capabilities of the output device — useful for responsive design and print optimization.
Overview of Features
| Feature | Description | Values |
|---|---|---|
aspect-ratio | Ratio of viewport width to height | 16/9, 4/3, 1/1 |
color | Bits per color component (RGB) | Integer (e.g., 8, 32) |
1. aspect-ratio
The aspect-ratio media feature applies styles based on the ratio of the viewport’s width to its height.
@media (aspect-ratio: 16/9) {
.box {
width: 500px;
height: auto;
background-color: #ffcccc;
}
}
Syntax
aspect-ratio: <width> / <height>
Variations
| Feature | Description | Example |
|---|---|---|
aspect-ratio | Exact ratio | @media (aspect-ratio: 16/9) |
min-aspect-ratio | Minimum ratio | @media (min-aspect-ratio: 4/3) |
max-aspect-ratio | Maximum ratio | @media (max-aspect-ratio: 3/2) |
Common Aspect Ratios
| Ratio | Description | Typical Use |
|---|---|---|
16/9 | Widescreen | Modern monitors, TVs |
4/3 | Traditional | Older monitors, tablets |
21/9 | Ultra-wide | Cinematic displays |
1/1 | Square | Mobile portrait |
9/16 | Vertical | Mobile landscape |
Key Points:
- The viewport’s aspect ratio changes when the user rotates their device or resizes their browser
- Useful for adapting layouts to portrait vs. landscape orientations
- You can use
min-aspect-ratioandmax-aspect-ratiofor range-based queries
2. color
The color media feature applies styles based on the number of bits per color component (red, green, blue) of the output device.
@media (color) {
p {
color: blue;
}
}
@media (min-color: 32) {
p {
color: red;
}
}
Syntax
color: <integer>
color: < 12
color: >= 24
Variations
| Feature | Description | Example |
|---|---|---|
color | Device has color | @media (color) |
min-color | Minimum bits per component | @media (min-color: 8) |
max-color | Maximum bits per component | @media (max-color: 32) |
Common Color Depths
| Bits per Component | Total Colors | Description |
|---|---|---|
0 | Monochrome | No color (e.g., e-ink) |
8 | 16.7 million | Standard color (24-bit total) |
10 | 1.07 billion | HDR displays |
12 | 68.7 billion | Professional displays |
16 | — | High-end displays |
Key Points:
color: 0means the device is monochrome (no color)colorwithout a value applies if the device has any color capabilitymin-color: 8means at least 8 bits per component (24-bit color)- The value is per component (red, green, or blue), not total bits
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>aspect-ratio and color Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 20px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f8f9fa;
line-height: 1.6;
min-height: 100vh;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== ASPECT RATIO DEMO ====== */
.container {
width: 100%;
min-height: 50vh;
display: flex;
justify-content: center;
align-items: center;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
padding: 20px;
transition: background 0.3s;
}
.box {
width: 300px;
height: 200px;
background-color: #dc3545;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
text-align: center;
padding: 20px;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.box .ratio-label {
font-size: 0.8rem;
opacity: 0.8;
margin-top: 5px;
}
/* Aspect ratio: 16/9 */
@media (aspect-ratio: 16/9) {
.box {
width: 500px;
height: auto;
background-color: #ffcccc;
color: #333;
}
.box .ratio-label::after {
content: " (16:9 detected!)";
font-weight: bold;
}
}
/* Aspect ratio: 4/3 */
@media (aspect-ratio: 4/3) {
.box {
width: 400px;
height: 300px;
background-color: #ffc107;
color: #333;
}
.box .ratio-label::after {
content: " (4:3 detected!)";
font-weight: bold;
}
}
/* Aspect ratio: 1/1 (square) */
@media (aspect-ratio: 1/1) {
.box {
width: 300px;
height: 300px;
background-color: #6c5ce7;
border-radius: 50%;
}
.box .ratio-label::after {
content: " (1:1 square detected!)";
font-weight: bold;
}
}
/* Portrait orientation */
@media (max-aspect-ratio: 1/1) {
.container {
background: #d4edda;
}
}
/* Landscape orientation */
@media (min-aspect-ratio: 1/1) {
.container {
background: #cce5ff;
}
}
/* ====== COLOR DEMO ====== */
.color-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
background: #e9ecef;
margin: 15px 0;
border: 2px solid #ddd;
}
/* Monochrome device */
@media (color: 0) {
.color-demo {
background: #000;
color: #fff;
border-color: #fff;
}
.color-demo::after {
content: " (monochrome device detected)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Any color device */
@media (color) {
.color-demo {
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
border-color: #007bff;
}
.color-demo::after {
content: " (color device detected)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* High color depth */
@media (min-color: 32) {
.color-demo {
background: linear-gradient(135deg, #ff6b6b, #ffc107, #28a745);
border-color: #ffc107;
}
.color-demo::after {
content: " (high color depth ≥ 32 bits)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== COLOR DETECTION GRID ====== */
.color-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.color-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
}
.color-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.color-card .value {
font-size: 1.2rem;
font-weight: bold;
color: #007bff;
}
/* Color depth indicators */
.color-card.depth-0 {
background: #000;
color: #fff;
border-color: #333;
}
.color-card.depth-0 .value { color: #fff; }
.color-card.depth-8 {
background: #cce5ff;
border-color: #007bff;
}
.color-card.depth-8 .value { color: #007bff; }
.color-card.depth-16 {
background: #d4edda;
border-color: #28a745;
}
.color-card.depth-16 .value { color: #28a745; }
.color-card.depth-32 {
background: #fff3cd;
border-color: #ffc107;
}
.color-card.depth-32 .value { color: #856404; }
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
}
.live-indicator::before {
content: "📐 Aspect Ratio: ";
}
/* ====== PRACTICAL EXAMPLE ====== */
.video-container {
position: relative;
width: 100%;
max-width: 800px;
margin: 20px auto;
background: #000;
border-radius: 8px;
overflow: hidden;
}
.video-container .video-placeholder {
aspect-ratio: 16/9;
background: linear-gradient(135deg, #1e1e1e, #2d2d2d);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
}
.video-container .video-placeholder .play-icon {
font-size: 3rem;
margin-bottom: 10px;
}
@media (max-aspect-ratio: 4/3) {
.video-container .video-placeholder {
aspect-ratio: 4/3;
background: linear-gradient(135deg, #2d1b4e, #1e1e1e);
}
.video-container .video-placeholder::after {
content: " (4:3 mode)";
font-weight: normal;
font-size: 0.85rem;
}
}
</style>
</head>
<body>
<h1>aspect-ratio and color Media Features</h1>
<!-- ====== 1. ASPECT-RATIO DEMO ====== -->
<section>
<h2>1. aspect-ratio</h2>
<p>Applies styles based on the <strong>viewport's width-to-height ratio</strong>.</p>
<p>Resize your browser or rotate your device to see the changes!</p>
<div class="container">
<div class="box">
<span>Aspect Ratio Demo</span>
<span class="ratio-label">Default (no ratio matched)</span>
</div>
</div>
<div class="code-block">
/* Default state */
.box {
width: 300px;
height: 200px;
background-color: #dc3545;
}
/* 16:9 widescreen */
@media (aspect-ratio: 16/9) {
.box {
width: 500px;
height: auto;
background-color: #ffcccc;
}
}
/* 4:3 traditional */
@media (aspect-ratio: 4/3) {
.box {
width: 400px;
height: 300px;
background-color: #ffc107;
}
}
/* 1:1 square */
@media (aspect-ratio: 1/1) {
.box {
width: 300px;
height: 300px;
background-color: #6c5ce7;
border-radius: 50%;
}
}
/* Portrait orientation */
@media (max-aspect-ratio: 1/1) {
.container { background: #d4edda; }
}
/* Landscape orientation */
@media (min-aspect-ratio: 1/1) {
.container { background: #cce5ff; }
}
</div>
</section>
<!-- ====== 2. COLOR DEMO ====== -->
<section>
<h2>2. color</h2>
<p>Applies styles based on the <strong>number of bits per color component</strong>.</p>
<div class="color-demo">
Color Device Detection
</div>
<div class="code-block">
/* Monochrome device (no color) */
@media (color: 0) {
.color-demo {
background: #000;
color: #fff;
}
}
/* Any color device */
@media (color) {
.color-demo {
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
}
}
/* High color depth (≥ 32 bits) */
@media (min-color: 32) {
.color-demo {
background: linear-gradient(135deg, #ff6b6b, #ffc107, #28a745);
}
}
</div>
</section>
<!-- ====== 3. COLOR DEPTH DETECTION ====== -->
<section>
<h2>3. Color Depth Detection</h2>
<p>Your device's color capabilities are detected below.</p>
<div class="color-grid">
<div class="color-card" id="color-monochrome">
<span class="label">color: 0</span>
<span class="value">Monochrome</span>
</div>
<div class="color-card" id="color-any">
<span class="label">color (any)</span>
<span class="value">Color</span>
</div>
<div class="color-card" id="color-8">
<span class="label">min-color: 8</span>
<span class="value">Standard</span>
</div>
<div class="color-card" id="color-16">
<span class="label">min-color: 16</span>
<span class="value">High</span>
</div>
<div class="color-card" id="color-32">
<span class="label">min-color: 32</span>
<span class="value">Ultra</span>
</div>
</div>
<div class="code-block">
/* Detect monochrome */
@media (color: 0) { /* No color */ }
/* Detect any color */
@media (color) { /* Has color */ }
/* Detect color depth */
@media (min-color: 8) { /* At least 8 bits per component */ }
@media (min-color: 16) { /* At least 16 bits per component */ }
@media (min-color: 32) { /* At least 32 bits per component */ }
</div>
</section>
<!-- ====== 4. PRACTICAL EXAMPLE ====== -->
<section>
<h2>4. Practical Example: Responsive Video Container</h2>
<p>A video container that adapts to different aspect ratios.</p>
<div class="video-container">
<div class="video-placeholder">
<span class="play-icon">▶️</span>
<span>Video Player</span>
</div>
</div>
<div class="code-block">
/* 16:9 aspect ratio (widescreen) */
.video-placeholder {
aspect-ratio: 16/9;
}
/* 4:3 aspect ratio (traditional) */
@media (max-aspect-ratio: 4/3) {
.video-placeholder {
aspect-ratio: 4/3;
}
}
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>aspect-ratio Values</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>aspect-ratio</code></td>
<td>Exact aspect ratio</td>
<td><code>@media (aspect-ratio: 16/9)</code></td>
</tr>
<tr>
<td><code>min-aspect-ratio</code></td>
<td>Minimum aspect ratio</td>
<td><code>@media (min-aspect-ratio: 4/3)</code></td>
</tr>
<tr>
<td><code>max-aspect-ratio</code></td>
<td>Maximum aspect ratio</td>
<td><code>@media (max-aspect-ratio: 3/2)</code></td>
</tr>
</table>
<h3>Common Aspect Ratios</h3>
<table class="reference-table">
<tr>
<th>Ratio</th>
<th>Description</th>
<th>Typical Use</th>
</tr>
<tr>
<td><code>16/9</code></td>
<td>Widescreen</td>
<td>Modern monitors, TVs, video</td>
</tr>
<tr>
<td><code>4/3</code></td>
<td>Traditional</td>
<td>Older monitors, tablets</td>
</tr>
<tr>
<td><code>21/9</code></td>
<td>Ultra-wide</td>
<td>Cinematic displays</td>
</tr>
<tr>
<td><code>1/1</code></td>
<td>Square</td>
<td>Mobile portrait, avatars</td>
</tr>
<tr>
<td><code>9/16</code></td>
<td>Vertical</td>
<td>Mobile landscape, stories</td>
</tr>
</table>
<h3>color Values</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>color</code></td>
<td>Device has color</td>
<td><code>@media (color)</code></td>
</tr>
<tr>
<td><code>min-color</code></td>
<td>Minimum bits per component</td>
<td><code>@media (min-color: 8)</code></td>
</tr>
<tr>
<td><code>max-color</code></td>
<td>Maximum bits per component</td>
<td><code>@media (max-color: 32)</code></td>
</tr>
<tr>
<td><code>color: 0</code></td>
<td>Monochrome device</td>
<td><code>@media (color: 0)</code></td>
</tr>
</table>
<h3>Common Color Depths</h3>
<table class="reference-table">
<tr>
<th>Bits per Component</th>
<th>Total Colors</th>
<th>Description</th>
</tr>
<tr>
<td><code>0</code></td>
<td>Monochrome</td>
<td>No color (e-ink, braille)</td>
</tr>
<tr>
<td><code>8</code></td>
<td>16.7 million</td>
<td>Standard color (24-bit total)</td>
</tr>
<tr>
<td><code>10</code></td>
<td>1.07 billion</td>
<td>HDR displays</td>
</tr>
<tr>
<td><code>12</code></td>
<td>68.7 billion</td>
<td>Professional displays</td>
</tr>
<tr>
<td><code>16</code></td>
<td>—</td>
<td>High-end displays</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>aspect-ratio</code> to adapt layouts to different screen shapes</li>
<li>Use <code>max-aspect-ratio</code> and <code>min-aspect-ratio</code> for range queries</li>
<li>Use <code>color</code> to detect monochrome devices and provide fallbacks</li>
<li>Use <code>min-color</code> to detect high color depth displays</li>
<li>Test on different devices and orientations</li>
<li>Combine with <code>orientation</code> for comprehensive device detection</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely solely on aspect-ratio for responsive design</li>
<li>Don't forget that aspect-ratio changes with orientation</li>
<li>Don't assume all devices have color (e-ink readers exist)</li>
<li>Don't use <code>color</code> for critical functionality — use it for enhancements</li>
<li>Don't confuse bits per component with total bits</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">Detecting...</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Aspect ratio indicator
const indicator = document.getElementById('live-indicator');
const w = window.innerWidth;
const h = window.innerHeight;
const ratio = (w / h).toFixed(2);
let ratioName = '';
const exactRatio = w / h;
if (Math.abs(exactRatio - 16/9) < 0.05) ratioName = '16:9';
else if (Math.abs(exactRatio - 4/3) < 0.05) ratioName = '4:3';
else if (Math.abs(exactRatio - 1) < 0.05) ratioName = '1:1';
else if (exactRatio > 1) ratioName = 'Landscape';
else ratioName = 'Portrait';
indicator.textContent = '📐 ' + ratioName + ' (' + w + '×' + h + ' = ' + ratio + ')';
// Color depth detection
const colorCards = {
monochrome: document.getElementById('color-monochrome'),
any: document.getElementById('color-any'),
c8: document.getElementById('color-8'),
c16: document.getElementById('color-16'),
c32: document.getElementById('color-32')
};
// Reset classes
Object.values(colorCards).forEach(card => {
if (card) card.className = 'color-card';
});
// Check color: 0 (monochrome)
if (window.matchMedia('(color: 0)').matches) {
colorCards.monochrome.className = 'color-card depth-0';
colorCards.monochrome.querySelector('.value').textContent = 'Detected ✅';
} else {
colorCards.monochrome.querySelector('.value').textContent = 'Not monochrome';
}
// Check any color
if (window.matchMedia('(color)').matches) {
colorCards.any.className = 'color-card depth-8';
colorCards.any.querySelector('.value').textContent = 'Detected ✅';
}
// Check min-color: 8
if (window.matchMedia('(min-color: 8)').matches) {
colorCards.c8.className = 'color-card depth-8';
colorCards.c8.querySelector('.value').textContent = '≥ 8 bits ✅';
}
// Check min-color: 16
if (window.matchMedia('(min-color: 16)').matches) {
colorCards.c16.className = 'color-card depth-16';
colorCards.c16.querySelector('.value').textContent = '≥ 16 bits ✅';
}
// Check min-color: 32
if (window.matchMedia('(min-color: 32)').matches) {
colorCards.c32.className = 'color-card depth-32';
colorCards.c32.querySelector('.value').textContent = '≥ 32 bits ✅';
}
}
window.addEventListener('resize', updateIndicators);
window.addEventListener('load', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
aspect-ratio | Viewport width-to-height ratio | 16/9, 4/3, 1/1 |
color | Bits per color component | Integer (0, 8, 16, 32) |
aspect-ratio Variations
| Feature | Description | Example |
|---|---|---|
aspect-ratio | Exact ratio | @media (aspect-ratio: 16/9) |
min-aspect-ratio | Minimum ratio | @media (min-aspect-ratio: 4/3) |
max-aspect-ratio | Maximum ratio | @media (max-aspect-ratio: 3/2) |
Common Aspect Ratios
| Ratio | Description | Typical Use |
|---|---|---|
16/9 | Widescreen | Modern monitors, TVs |
4/3 | Traditional | Older monitors, tablets |
21/9 | Ultra-wide | Cinematic displays |
1/1 | Square | Mobile portrait |
9/16 | Vertical | Mobile landscape |
color Variations
| Feature | Description | Example |
|---|---|---|
color | Device has color | @media (color) |
min-color | Minimum bits per component | @media (min-color: 8) |
max-color | Maximum bits per component | @media (max-color: 32) |
color: 0 | Monochrome device | @media (color: 0) |
Common Color Depths
| Bits per Component | Total Colors | Description |
|---|---|---|
0 | Monochrome | No color (e-ink) |
8 | 16.7 million | Standard color |
10 | 1.07 billion | HDR displays |
12 | 68.7 billion | Professional displays |
16 | — | High-end displays |
Best Practices
✅ Do This:
/* Adapt layout to different aspect ratios */
@media (aspect-ratio: 16/9) {
.video-container {
aspect-ratio: 16/9;
}
}
@media (max-aspect-ratio: 4/3) {
.video-container {
aspect-ratio: 4/3;
}
}
/* Provide fallbacks for monochrome devices */
@media (color: 0) {
.chart {
/* Use patterns instead of colors */
background-image: repeating-linear-gradient(...);
}
}
/* Enhance for high color depth */
@media (min-color: 32) {
.hero {
background: linear-gradient(135deg, #ff6b6b, #ffc107, #28a745);
}
}
❌ Don’t Do This:
/* Don't rely solely on aspect-ratio for responsiveness */
@media (aspect-ratio: 16/9) {
/* This won't catch all widescreen devices */
}
/* Don't assume all devices have color */
.chart {
color: #ff0000; /* May not be visible on monochrome */
}
/* Don't forget that aspect-ratio changes with orientation */
/* A 16:9 landscape device becomes 9:16 in portrait */
Pro Tip: aspect-ratio is great for adapting to device orientation and screen shape. Use it to switch between portrait and landscape layouts or to adjust video player dimensions. color is useful for accessibility — providing fallbacks for monochrome e-ink devices. Remember that aspect-ratio changes when the user rotates their device, so you might want to use both aspect-ratio and orientation together for precise control!
33. color-gamut and color-index Media Features
These two advanced media features let you adapt styles based on the color capabilities of the display — from the range of colors it can reproduce to the size of its color lookup table.
Overview of Features
| Feature | Description | Values |
|---|---|---|
color-gamut | Approximate color gamut of the display | srgb, p3, rec2020 |
color-index | Number of entries in the color lookup table | Integer (e.g., 1, 8, 256) |
1. color-gamut
The color-gamut media feature queries the approximate range of colors (gamut) that the output device can display.
@media (color-gamut: p3) {
.display-p3 {
background-color: oklch(69% 0.27 140);
}
}
Values
| Value | Description |
|---|---|
srgb | Standard sRGB color space (most common) |
p3 | DCI-P3 or Adobe RGB (1988) — wider than sRGB |
rec2020 | Rec. 2020 — widest color space |
display-p3 | Alias for p3 |
Color Space Comparison
┌─────────────────────────────────────────────────────┐
│ rec2020 │
│ ┌─────────────────────────────────────────────┐ │
│ │ p3 │ │
│ │ ┌───────────────────────────────────────┐ │ │
│ │ │ srgb │ │ │
│ │ │ (Standard — most devices) │ │ │
│ │ └───────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Key Points:
srgb— Standard for most monitors, phones, and the webp3— Wider gamut found on modern Apple devices and high-end displaysrec2020— Widest gamut, used in HDR and professional video- A device that supports
p3also supportssrgb(it’s a superset) - A device that supports
rec2020also supportsp3andsrgb
Important: color-gamut tells you what the display can reproduce, not what it’s currently set to. You still need to use wide-gamut color values (like oklch(), color(display-p3 ...)) for the wider gamut to be visible.
2. color-index
The color-index media feature queries the number of entries in the output device’s color lookup table (CLUT). This is primarily relevant for devices with indexed color, like older displays or certain embedded systems.
@media (min-color-index: 1) {
body {
background-color: lightgray;
}
}
Syntax
color-index: <integer>
color-index: >= 8
min-color-index: 1
max-color-index: 8
Variations
| Feature | Description | Example |
|---|---|---|
color-index | Exact number of entries | @media (color-index: 256) |
min-color-index | Minimum entries | @media (min-color-index: 1) |
max-color-index | Maximum entries | @media (max-color-index: 8) |
Key Points:
color-index: 0means the device does not use a color lookup table- Most modern devices use direct color (not indexed), so
color-index: 0 - Indexed color was common in older systems (e.g., 256-color VGA)
min-color-indexis equivalent tocolor-index >= valuemax-color-indexis equivalent tocolor-index <= value
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>color-gamut and color-index Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
padding: 20px;
background: #f8f9fa;
max-width: 1200px;
margin: 0 auto;
transition: background 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: background 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== COLOR GAMUT DEMO ====== */
.color-box {
width: 200px;
height: 200px;
display: inline-block;
margin: 10px;
border-radius: 12px;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(0,0,0,0.15);
position: relative;
overflow: hidden;
}
.color-box::after {
content: attr(data-label);
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.6);
color: white;
padding: 8px;
text-align: center;
font-size: 0.85rem;
font-weight: bold;
backdrop-filter: blur(4px);
}
/* Default: srgb */
.color-box {
background-color: rgb(85, 85, 85);
}
/* sRGB gamut */
@media (color-gamut: srgb) {
.srgb {
background-color: rgb(85, 85, 85);
}
}
/* P3 gamut */
@media (color-gamut: p3) {
.display-p3 {
background-color: oklch(69% 0.27 140);
}
}
/* Rec. 2020 gamut */
@media (color-gamut: rec2020) {
.rec2020 {
background-color: rgb(15, 15, 15);
}
}
/* ====== COLOR INDEX DEMO ====== */
@media (min-color-index: 1) {
body {
background-color: lightgray;
}
.content {
max-width: 80%;
margin: auto;
padding: 20px;
border: 1px solid #ccc;
box-shadow: 2px 2px 10px rgba(0,0,0,0.1);
}
}
@media (max-color-index: 8) {
body {
background-color: lightblue;
}
.content {
max-width: 60%;
margin: auto;
padding: 10px;
border: 1px solid #aaa;
box-shadow: none;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
/* Gamut cards */
.gamut-card.srgb {
background: #cce5ff;
border-color: #007bff;
}
.gamut-card.srgb .value { color: #007bff; }
.gamut-card.p3 {
background: #d4edda;
border-color: #28a745;
}
.gamut-card.p3 .value { color: #28a745; }
.gamut-card.rec2020 {
background: #fff3cd;
border-color: #ffc107;
}
.gamut-card.rec2020 .value { color: #856404; }
/* Color index cards */
.index-card {
background: #e9ecef;
border-color: #6c757d;
}
.index-card .value { color: #6c757d; }
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
}
/* ====== PRACTICAL EXAMPLE ====== */
.hero-image {
width: 100%;
height: 300px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
font-weight: bold;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
margin: 20px 0;
transition: all 0.3s;
/* Default: sRGB */
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
/* Enhanced for P3 displays */
@media (color-gamut: p3) {
.hero-image {
background: linear-gradient(135deg, oklch(69% 0.27 240), oklch(69% 0.27 300));
}
}
/* Enhanced for Rec. 2020 displays */
@media (color-gamut: rec2020) {
.hero-image {
background: linear-gradient(135deg, oklch(69% 0.27 30), oklch(69% 0.27 120), oklch(69% 0.27 240));
}
}
/* ====== CONTENT BOX ====== */
.content {
background: white;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
transition: all 0.3s;
}
</style>
</head>
<body>
<h1>color-gamut and color-index Media Features</h1>
<!-- ====== 1. COLOR-GAMUT DEMO ====== -->
<section>
<h2>1. color-gamut</h2>
<p>Queries the <strong>approximate color gamut</strong> of the display.</p>
<div style="text-align: center;">
<div class="color-box srgb" data-label="sRGB (Standard)"></div>
<div class="color-box display-p3" data-label="Display P3 (Wide)"></div>
<div class="color-box rec2020" data-label="Rec. 2020 (Widest)"></div>
</div>
<p class="note" style="text-align: center;">Each box uses a color from its respective gamut. On a standard sRGB display, they'll all look similar. On a wide-gamut display, the P3 and Rec. 2020 colors will appear more vibrant.</p>
<div class="code-block">
/* Default: sRGB */
.color-box {
background-color: rgb(85, 85, 85);
}
/* sRGB gamut */
@media (color-gamut: srgb) {
.srgb {
background-color: rgb(85, 85, 85);
}
}
/* P3 gamut (DCI-P3 / Adobe RGB) */
@media (color-gamut: p3) {
.display-p3 {
background-color: oklch(69% 0.27 140);
}
}
/* Rec. 2020 gamut (widest) */
@media (color-gamut: rec2020) {
.rec2020 {
background-color: rgb(15, 15, 15);
}
}
</div>
</section>
<!-- ====== 2. COLOR-INDEX DEMO ====== -->
<section>
<h2>2. color-index</h2>
<p>Queries the <strong>number of entries</strong> in the output device's color lookup table.</p>
<div class="content">
<h3>Welcome to Our Website!</h3>
<p>This is a simple example of using the <code>color-index</code> media feature in CSS.</p>
<p>The styles on this page adapt based on your device's color index capability.</p>
</div>
<div class="code-block">
/* At least 1 color index */
@media (min-color-index: 1) {
body {
background-color: lightgray;
}
.content {
max-width: 80%;
padding: 20px;
border: 1px solid #ccc;
box-shadow: 2px 2px 10px rgba(0,0,0,0.1);
}
}
/* At most 8 color indices */
@media (max-color-index: 8) {
body {
background-color: lightblue;
}
.content {
max-width: 60%;
padding: 10px;
border: 1px solid #aaa;
box-shadow: none;
}
}
</div>
</section>
<!-- ====== 3. DETECTION SUMMARY ====== -->
<section>
<h2>3. Your Display's Capabilities</h2>
<p>Below is a live detection of your display's color capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="gamut-srgb">
<span class="label">color-gamut: srgb</span>
<span class="value" id="gamut-srgb-value">Detecting...</span>
</div>
<div class="detection-card" id="gamut-p3">
<span class="label">color-gamut: p3</span>
<span class="value" id="gamut-p3-value">Detecting...</span>
</div>
<div class="detection-card" id="gamut-rec2020">
<span class="label">color-gamut: rec2020</span>
<span class="value" id="gamut-rec2020-value">Detecting...</span>
</div>
<div class="detection-card" id="index-any">
<span class="label">color-index</span>
<span class="value" id="index-any-value">Detecting...</span>
</div>
<div class="detection-card" id="index-min">
<span class="label">min-color-index: 1</span>
<span class="value" id="index-min-value">Detecting...</span>
</div>
<div class="detection-card" id="index-max">
<span class="label">max-color-index: 8</span>
<span class="value" id="index-max-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect sRGB */
@media (color-gamut: srgb) { /* Standard color gamut */ }
/* Detect P3 */
@media (color-gamut: p3) { /* Wide color gamut */ }
/* Detect Rec. 2020 */
@media (color-gamut: rec2020) { /* Widest color gamut */ }
/* Detect color index */
@media (min-color-index: 1) { /* At least 1 color index */ }
@media (max-color-index: 8) { /* At most 8 color indices */ }
</div>
</section>
<!-- ====== 4. PRACTICAL EXAMPLE ====== -->
<section>
<h2>4. Practical Example: Enhanced Hero Image</h2>
<p>This hero image uses wider-gamut colors on displays that support them.</p>
<div class="hero-image">
🎨 Enhanced for Wide-Gamut Displays
</div>
<div class="code-block">
/* Default: sRGB gradient */
.hero-image {
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
/* Enhanced for P3 displays */
@media (color-gamut: p3) {
.hero-image {
background: linear-gradient(135deg, oklch(69% 0.27 240), oklch(69% 0.27 300));
}
}
/* Enhanced for Rec. 2020 displays */
@media (color-gamut: rec2020) {
.hero-image {
background: linear-gradient(135deg, oklch(69% 0.27 30), oklch(69% 0.27 120), oklch(69% 0.27 240));
}
}
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>color-gamut Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Typical Devices</th>
</tr>
<tr>
<td><code>srgb</code></td>
<td>Standard sRGB color space</td>
<td>Most monitors, phones, laptops</td>
</tr>
<tr>
<td><code>p3</code></td>
<td>DCI-P3 or Adobe RGB (1988)</td>
<td>Modern Apple devices, high-end displays</td>
</tr>
<tr>
<td><code>rec2020</code></td>
<td>Rec. 2020 color space</td>
<td>HDR displays, professional video</td>
</tr>
<tr>
<td><code>display-p3</code></td>
<td>Alias for <code>p3</code></td>
<td>Same as <code>p3</code></td>
</tr>
</table>
<h3>color-index Values</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>color-index</code></td>
<td>Exact number of entries</td>
<td><code>@media (color-index: 256)</code></td>
</tr>
<tr>
<td><code>min-color-index</code></td>
<td>Minimum entries</td>
<td><code>@media (min-color-index: 1)</code></td>
</tr>
<tr>
<td><code>max-color-index</code></td>
<td>Maximum entries</td>
<td><code>@media (max-color-index: 8)</code></td>
</tr>
</table>
<h3>Color Space Comparison</h3>
<table class="reference-table">
<tr>
<th>Color Space</th>
<th>Coverage</th>
<th>Description</th>
</tr>
<tr>
<td><code>sRGB</code></td>
<td>~35% of visible colors</td>
<td>Standard for web and most devices</td>
</tr>
<tr>
<td><code>DCI-P3</code></td>
<td>~45% of visible colors</td>
<td>Wider than sRGB, used in cinema and modern displays</td>
</tr>
<tr>
<td><code>Rec. 2020</code></td>
<td>~75% of visible colors</td>
<td>Widest standard, used in HDR and 8K video</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>color-gamut</code> to provide enhanced colors on capable displays</li>
<li>Always provide an sRGB fallback for devices that don't support wider gamuts</li>
<li>Use modern color functions like <code>oklch()</code> and <code>color(display-p3 ...)</code></li>
<li>Use <code>color-index</code> only for legacy or specialized devices</li>
<li>Test on both standard and wide-gamut displays</li>
<li>Remember that wider gamut ≠ better design — use it purposefully</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't assume all displays support wide gamuts</li>
<li>Don't use <code>color-index</code> for modern devices (most use direct color)</li>
<li>Don't forget to provide sRGB fallbacks</li>
<li>Don't use wide-gamut colors without testing on standard displays</li>
<li>Don't rely on color-gamut for critical functionality</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">Detecting...</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Gamut detection
const srgbCard = document.getElementById('gamut-srgb');
const p3Card = document.getElementById('gamut-p3');
const rec2020Card = document.getElementById('gamut-rec2020');
const indicator = document.getElementById('live-indicator');
let gamutName = 'Unknown';
if (window.matchMedia('(color-gamut: rec2020)').matches) {
srgbCard.className = 'detection-card gamut-card rec2020';
srgbCard.querySelector('.value').textContent = 'Supported ✅';
p3Card.className = 'detection-card gamut-card rec2020';
p3Card.querySelector('.value').textContent = 'Supported ✅';
rec2020Card.className = 'detection-card gamut-card rec2020';
rec2020Card.querySelector('.value').textContent = 'Supported ✅';
gamutName = 'Rec. 2020';
} else if (window.matchMedia('(color-gamut: p3)').matches) {
srgbCard.className = 'detection-card gamut-card p3';
srgbCard.querySelector('.value').textContent = 'Supported ✅';
p3Card.className = 'detection-card gamut-card p3';
p3Card.querySelector('.value').textContent = 'Supported ✅';
rec2020Card.querySelector('.value').textContent = 'Not supported ❌';
gamutName = 'Display P3';
} else if (window.matchMedia('(color-gamut: srgb)').matches) {
srgbCard.className = 'detection-card gamut-card srgb';
srgbCard.querySelector('.value').textContent = 'Supported ✅';
p3Card.querySelector('.value').textContent = 'Not supported ❌';
rec2020Card.querySelector('.value').textContent = 'Not supported ❌';
gamutName = 'sRGB';
} else {
srgbCard.querySelector('.value').textContent = 'Not supported ❌';
p3Card.querySelector('.value').textContent = 'Not supported ❌';
rec2020Card.querySelector('.value').textContent = 'Not supported ❌';
}
// Color index detection
const indexAnyCard = document.getElementById('index-any');
const indexMinCard = document.getElementById('index-min');
const indexMaxCard = document.getElementById('index-max');
if (window.matchMedia('(color-index: 0)').matches) {
indexAnyCard.querySelector('.value').textContent = 'Direct color (0)';
} else if (window.matchMedia('(min-color-index: 1)').matches) {
indexAnyCard.querySelector('.value').textContent = 'Indexed color ✅';
} else {
indexAnyCard.querySelector('.value').textContent = 'Unknown';
}
if (window.matchMedia('(min-color-index: 1)').matches) {
indexMinCard.className = 'detection-card index-card';
indexMinCard.querySelector('.value').textContent = '≥ 1 ✅';
} else {
indexMinCard.querySelector('.value').textContent = 'Not detected';
}
if (window.matchMedia('(max-color-index: 8)').matches) {
indexMaxCard.className = 'detection-card index-card';
indexMaxCard.querySelector('.value').textContent = '≤ 8 ✅';
} else {
indexMaxCard.querySelector('.value').textContent = 'Not detected';
}
// Update live indicator
indicator.textContent = '🎨 ' + gamutName;
indicator.style.background = gamutName === 'Rec. 2020' ? '#6c5ce7' :
gamutName === 'Display P3' ? '#28a745' :
gamutName === 'sRGB' ? '#007bff' : '#6c757d';
}
window.addEventListener('load', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
color-gamut | Approximate color gamut of display | srgb, p3, rec2020 |
color-index | Color lookup table entries | Integer |
color-gamut Values
| Value | Description | Typical Devices |
|---|---|---|
srgb | Standard sRGB | Most monitors, phones |
p3 | DCI-P3 or Adobe RGB | Modern Apple devices, high-end displays |
rec2020 | Rec. 2020 | HDR displays, professional video |
display-p3 | Alias for p3 | Same as p3 |
color-index Variations
| Feature | Description | Example |
|---|---|---|
color-index | Exact entries | @media (color-index: 256) |
min-color-index | Minimum entries | @media (min-color-index: 1) |
max-color-index | Maximum entries | @media (max-color-index: 8) |
Color Space Comparison
| Color Space | Coverage | Description |
|---|---|---|
sRGB | ~35% of visible colors | Standard for web |
DCI-P3 | ~45% of visible colors | Wider, used in cinema |
Rec. 2020 | ~75% of visible colors | Widest, used in HDR |
Best Practices
✅ Do This:
/* Provide sRGB fallback first */
.hero {
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
/* Enhance for P3 displays */
@media (color-gamut: p3) {
.hero {
background: linear-gradient(135deg, oklch(69% 0.27 240), oklch(69% 0.27 300));
}
}
/* Further enhance for Rec. 2020 displays */
@media (color-gamut: rec2020) {
.hero {
background: linear-gradient(135deg, oklch(69% 0.27 30), oklch(69% 0.27 120), oklch(69% 0.27 240));
}
}
/* Handle indexed color devices */
@media (max-color-index: 8) {
.image {
/* Reduce color complexity */
}
}
❌ Don’t Do This:
/* Don't use wide-gamut colors without fallbacks */
.hero {
background: oklch(69% 0.27 240); /* May look wrong on sRGB */
}
/* Don't assume all devices support wide gamuts */
@media (color-gamut: p3) {
/* This is fine, but always provide sRGB fallback */
}
/* Don't use color-index for modern devices */
@media (color-index: 256) {
/* Most modern devices use direct color, not indexed */
}
Pro Tip: color-gamut is your key to vibrant, modern web design. Always provide an sRGB fallback first, then enhance for P3 and Rec. 2020 displays using modern color functions like oklch() or color(display-p3 ...). color-index is mostly relevant for legacy or specialized devices — modern devices use direct color rather than indexed color. Remember: wider gamut is about quality, not quantity — use it to make your colors more vivid and lifelike, not to change your design completely!
34. display-mode and dynamic-range Media Features
These two media features let you adapt styles based on how the app is displayed and the visual capabilities of the screen — from standalone PWA mode to HDR displays.
Overview of Features
| Feature | Description | Values |
|---|---|---|
display-mode | How the document is displayed | browser, standalone, minimal-ui, fullscreen, picture-in-picture |
dynamic-range | Brightness, contrast, and color depth | standard, high |
1. display-mode
The display-mode media feature queries how the document is being displayed — whether it’s in a browser tab, installed as an app, in fullscreen, etc.
@media (display-mode: standalone) {
body {
background-color: #f0f8ff;
color: #036;
}
}
Values
| Value | Description |
|---|---|
browser | In a normal browser tab or window |
standalone | Installed as a standalone app (PWA) |
minimal-ui | Like standalone, but with minimal browser UI |
fullscreen | Uses the full screen (no browser chrome) |
picture-in-picture | In a picture-in-picture window |
window-controls-overlay | PWA with window controls overlay (desktop) |
How It’s Set
The display mode is controlled by:
- The manifest file’s
displayproperty (for PWAs) - The Fullscreen API (
requestFullscreen()) - The Picture-in-Picture API
Manifest example:
{
"display": "standalone",
"name": "My PWA",
"short_name": "PWA"
}
JavaScript example:
// Enter fullscreen
document.documentElement.requestFullscreen();
// Check current display mode
if (window.matchMedia('(display-mode: standalone)').matches) {
console.log('Running as standalone app');
}
2. dynamic-range
The dynamic-range media feature queries the combination of brightness, contrast, and color depth capabilities of the display.
@media (dynamic-range: high) {
body {
background-color: #f5f5f5;
color: #000;
}
}
Values
| Value | Description |
|---|---|
standard | Standard dynamic range (SDR) — typical displays |
high | High dynamic range (HDR) — supports HDR content |
HDR requirements:
- Color depth greater than 24-bit (usually 30-bit or 36-bit)
- High contrast ratio
- High peak brightness (typically 1000+ nits)
Key Points:
- A device that matches
highalso matchesstandard(HDR is a superset) - A device that matches
standardmay or may not supporthigh - Use
highto enhance visuals on capable displays - Always provide a
standardfallback
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>display-mode and dynamic-range Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: background 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DISPLAY MODE DEMOS ====== */
@media (display-mode: browser) {
.display-browser {
background: #cce5ff;
border-color: #007bff;
}
.display-browser .value {
color: #007bff;
}
}
@media (display-mode: standalone) {
body {
background-color: #f0f8ff;
color: #036;
}
a {
color: #00f;
}
.display-standalone {
background: #d4edda;
border-color: #28a745;
}
.display-standalone .value {
color: #28a745;
}
}
@media (display-mode: minimal-ui) {
.display-minimal-ui {
background: #fff3cd;
border-color: #ffc107;
}
.display-minimal-ui .value {
color: #856404;
}
}
@media (display-mode: fullscreen) {
body {
background-color: #fff;
color: #000;
}
a {
color: #03f;
}
.display-fullscreen {
background: #f8d7da;
border-color: #dc3545;
}
.display-fullscreen .value {
color: #dc3545;
}
}
@media (display-mode: picture-in-picture) {
.display-pip {
background: #e2d9f3;
border-color: #6c5ce7;
}
.display-pip .value {
color: #6c5ce7;
}
}
/* ====== DYNAMIC RANGE DEMOS ====== */
@media (dynamic-range: standard) {
body {
background-color: #e5e5e5;
color: #333;
}
p {
font-size: 1em;
}
.dynamic-standard {
background: #e9ecef;
border-color: #6c757d;
}
.dynamic-standard .value {
color: #6c757d;
}
}
@media (dynamic-range: high) {
body {
background-color: #f5f5f5;
color: #000;
}
p {
font-size: 1.2em;
}
.dynamic-high {
background: #fff3cd;
border-color: #ffc107;
}
.dynamic-high .value {
color: #856404;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
/* ====== BUTTONS ====== */
.btn-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 15px 0;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
color: white;
}
.btn-primary {
background: #007bff;
}
.btn-primary:hover {
background: #0056b3;
transform: translateY(-2px);
}
.btn-success {
background: #28a745;
}
.btn-success:hover {
background: #1e7e34;
transform: translateY(-2px);
}
.btn-danger {
background: #dc3545;
}
.btn-danger:hover {
background: #a71d2a;
transform: translateY(-2px);
}
.btn-warning {
background: #ffc107;
color: #333;
}
.btn-warning:hover {
background: #d39e00;
transform: translateY(-2px);
}
/* ====== HDR VISUAL DEMO ====== */
.hdr-demo {
border-radius: 12px;
overflow: hidden;
margin: 20px 0;
position: relative;
}
.hdr-demo .image-container {
width: 100%;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
font-weight: bold;
color: white;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
transition: all 0.3s;
/* Standard dynamic range */
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
/* High dynamic range enhancement */
@media (dynamic-range: high) {
.hdr-demo .image-container {
background: linear-gradient(135deg,
oklch(69% 0.27 240),
oklch(69% 0.27 300),
oklch(69% 0.27 30));
box-shadow: 0 0 40px rgba(108, 92, 231, 0.5);
}
.hdr-demo .image-container::after {
content: " (HDR Enhanced ✨)";
font-size: 1rem;
font-weight: normal;
}
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
}
/* ====== PWA MANIFEST NOTE ====== */
.manifest-note {
background: #e2d9f3;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #6c5ce7;
margin: 15px 0;
}
.manifest-note code {
background: rgba(0,0,0,0.08);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
}
</style>
</head>
<body>
<h1>display-mode and dynamic-range Media Features</h1>
<!-- ====== 1. DISPLAY-MODE DEMO ====== -->
<section>
<h2>1. display-mode</h2>
<p>Queries how the document is being <strong>displayed</strong> — browser tab, standalone app, fullscreen, etc.</p>
<div class="btn-group">
<button class="btn btn-primary" onclick="enterFullscreen()">🖥️ Enter Fullscreen</button>
<button class="btn btn-danger" onclick="exitFullscreen()">🚪 Exit Fullscreen</button>
<button class="btn btn-success" onclick="showDisplayMode()">📱 Detect Display Mode</button>
<button class="btn btn-warning" onclick="requestPiP()">📺 Picture-in-Picture</button>
</div>
<div id="display-mode-result" class="detection-card" style="margin: 15px 0;">
<span class="label">Current display mode:</span>
<span class="value" id="current-display-mode">Click "Detect Display Mode"</span>
</div>
<div class="code-block">
/* Browser tab or window */
@media (display-mode: browser) {
body { background-color: #f8f9fa; }
}
/* Installed as a standalone PWA */
@media (display-mode: standalone) {
body { background-color: #f0f8ff; }
}
/* Minimal UI (browser controls hidden) */
@media (display-mode: minimal-ui) {
body { background-color: #fff3cd; }
}
/* Fullscreen (no browser chrome) */
@media (display-mode: fullscreen) {
body { background-color: #fff; }
}
/* Picture-in-Picture window */
@media (display-mode: picture-in-picture) {
body { background-color: #e2d9f3; }
}
</div>
<div class="manifest-note">
<strong>📋 How display-mode is set:</strong>
<ul>
<li>In a <strong>manifest.json</strong> file: <code>"display": "standalone"</code></li>
<li>With the <strong>Fullscreen API</strong>: <code>document.documentElement.requestFullscreen()</code></li>
<li>With the <strong>Picture-in-Picture API</strong>: <code>video.requestPictureInPicture()</code></li>
</ul>
</div>
</section>
<!-- ====== 2. DYNAMIC-RANGE DEMO ====== -->
<section>
<h2>2. dynamic-range</h2>
<p>Queries the <strong>brightness, contrast, and color depth</strong> capabilities of the display.</p>
<div class="hdr-demo">
<div class="image-container">
🎨 Dynamic Range Demo
</div>
</div>
<div id="dynamic-range-result" class="detection-card" style="margin: 15px 0;">
<span class="label">Current dynamic range:</span>
<span class="value" id="current-dynamic-range">Detecting...</span>
</div>
<div class="code-block">
/* Standard Dynamic Range (SDR) — most displays */
@media (dynamic-range: standard) {
body {
background-color: #e5e5e5;
color: #333;
}
p {
font-size: 1em;
}
}
/* High Dynamic Range (HDR) — HDR displays */
@media (dynamic-range: high) {
body {
background-color: #f5f5f5;
color: #000;
}
p {
font-size: 1.2em;
}
}
</div>
<p class="note"><strong>Note:</strong> HDR requires color depth > 24-bit, high contrast, and high peak brightness (1000+ nits). A device that matches <code>high</code> also matches <code>standard</code>.</p>
</section>
<!-- ====== 3. DETECTION SUMMARY ====== -->
<section>
<h2>3. Your Device's Capabilities</h2>
<p>Below is a live detection of your display's capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="dm-browser">
<span class="label">display-mode: browser</span>
<span class="value" id="dm-browser-value">Detecting...</span>
</div>
<div class="detection-card" id="dm-standalone">
<span class="label">display-mode: standalone</span>
<span class="value" id="dm-standalone-value">Detecting...</span>
</div>
<div class="detection-card" id="dm-minimal-ui">
<span class="label">display-mode: minimal-ui</span>
<span class="value" id="dm-minimal-ui-value">Detecting...</span>
</div>
<div class="detection-card" id="dm-fullscreen">
<span class="label">display-mode: fullscreen</span>
<span class="value" id="dm-fullscreen-value">Detecting...</span>
</div>
<div class="detection-card" id="dm-pip">
<span class="label">display-mode: picture-in-picture</span>
<span class="value" id="dm-pip-value">Detecting...</span>
</div>
<div class="detection-card" id="dr-standard">
<span class="label">dynamic-range: standard</span>
<span class="value" id="dr-standard-value">Detecting...</span>
</div>
<div class="detection-card" id="dr-high">
<span class="label">dynamic-range: high</span>
<span class="value" id="dr-high-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect display-mode */
@media (display-mode: browser) { /* Normal browser tab */ }
@media (display-mode: standalone) { /* Installed PWA */ }
@media (display-mode: fullscreen) { /* Fullscreen */ }
@media (display-mode: picture-in-picture) { /* PiP */ }
/* Detect dynamic-range */
@media (dynamic-range: standard) { /* SDR display */ }
@media (dynamic-range: high) { /* HDR display */ }
</div>
</section>
<!-- ====== 4. PRACTICAL EXAMPLE ====== -->
<section>
<h2>4. Practical Example: PWA-Aware Styling</h2>
<p>Styles that adapt when your site is installed as a standalone app.</p>
<div class="detection-card" id="pwa-status" style="background: #e9ecef; border-color: #6c757d;">
<span class="label">PWA Status</span>
<span class="value" id="pwa-status-value">Checking...</span>
</div>
<div class="code-block">
/* Hide browser-specific UI in standalone mode */
@media (display-mode: standalone) {
.browser-only {
display: none;
}
.standalone-only {
display: block;
}
}
@media (display-mode: browser) {
.standalone-only {
display: none;
}
}
</div>
<p class="browser-only" style="display: block; padding: 15px; background: #cce5ff; border-radius: 8px; margin-top: 15px;">
🌐 You're viewing this in a <strong>browser tab</strong>. Install this site as a PWA to see standalone styles!
</p>
<p class="standalone-only" style="display: none; padding: 15px; background: #d4edda; border-radius: 8px; margin-top: 15px;">
📱 You're viewing this as a <strong>standalone app</strong>! Enjoy the app-like experience.
</p>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>display-mode Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>How to Set</th>
</tr>
<tr>
<td><code>browser</code></td>
<td>Normal browser tab or window</td>
<td>Default</td>
</tr>
<tr>
<td><code>standalone</code></td>
<td>Installed as a standalone app (PWA)</td>
<td>Manifest: <code>"display": "standalone"</code></td>
</tr>
<tr>
<td><code>minimal-ui</code></td>
<td>Standalone with minimal browser UI</td>
<td>Manifest: <code>"display": "minimal-ui"</code></td>
</tr>
<tr>
<td><code>fullscreen</code></td>
<td>Full screen (no browser chrome)</td>
<td>Fullscreen API</td>
</tr>
<tr>
<td><code>picture-in-picture</code></td>
<td>Picture-in-Picture window</td>
<td>PiP API</td>
</tr>
<tr>
<td><code>window-controls-overlay</code></td>
<td>PWA with window controls overlay</td>
<td>Manifest: <code>"display_override"</code></td>
</tr>
</table>
<h3>dynamic-range Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Requirements</th>
</tr>
<tr>
<td><code>standard</code></td>
<td>Standard Dynamic Range (SDR)</td>
<td>Typical displays (8-bit color)</td>
</tr>
<tr>
<td><code>high</code></td>
<td>High Dynamic Range (HDR)</td>
<td>Color depth > 24-bit, high contrast, high peak brightness</td>
</tr>
</table>
<h3>HDR Requirements</h3>
<table class="reference-table">
<tr>
<th>Requirement</th>
<th>Minimum</th>
<th>Typical HDR</th>
</tr>
<tr>
<td>Color depth</td>
<td>> 24-bit</td>
<td>30-bit or 36-bit</td>
</tr>
<tr>
<td>Peak brightness</td>
<td>—</td>
<td>1000+ nits</td>
</tr>
<tr>
<td>Contrast ratio</td>
<td>—</td>
<td>High (10000:1+)</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>display-mode</code> to adapt your PWA's UI in standalone mode</li>
<li>Hide browser-specific UI (like "Add to Home Screen" prompts) in standalone mode</li>
<li>Use <code>dynamic-range</code> to enhance visuals on HDR displays</li>
<li>Always provide a <code>standard</code> fallback for SDR displays</li>
<li>Test your PWA in both browser and standalone modes</li>
<li>Use the Fullscreen API to test <code>fullscreen</code> mode</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't assume all users are in <code>browser</code> mode</li>
<li>Don't forget to handle the <code>standalone</code> case for PWAs</li>
<li>Don't use HDR colors without a <code>standard</code> fallback</li>
<li>Don't rely on <code>display-mode</code> for critical functionality</li>
<li>Don't confuse <code>dynamic-range</code> with <code>color-gamut</code> (they're different!)</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">Detecting...</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
// Display mode detection
const displayModes = [
{ id: 'dm-browser', value: 'browser' },
{ id: 'dm-standalone', value: 'standalone' },
{ id: 'dm-minimal-ui', value: 'minimal-ui' },
{ id: 'dm-fullscreen', value: 'fullscreen' },
{ id: 'dm-pip', value: 'picture-in-picture' }
];
function updateDisplayModes() {
displayModes.forEach(mode => {
const card = document.getElementById(mode.id);
const valueEl = card.querySelector('.value');
const query = `(display-mode: ${mode.value})`;
if (window.matchMedia(query).matches) {
valueEl.textContent = 'Active ✅';
card.style.background = '#d4edda';
card.style.borderColor = '#28a745';
valueEl.style.color = '#28a745';
} else {
valueEl.textContent = 'Inactive';
card.style.background = '#e9ecef';
card.style.borderColor = '#6c757d';
valueEl.style.color = '#6c757d';
}
});
}
// Dynamic range detection
function updateDynamicRange() {
const standardCard = document.getElementById('dr-standard');
const highCard = document.getElementById('dr-high');
const indicator = document.getElementById('live-indicator');
const isStandard = window.matchMedia('(dynamic-range: standard)').matches;
const isHigh = window.matchMedia('(dynamic-range: high)').matches;
if (isStandard) {
standardCard.querySelector('.value').textContent = 'Supported ✅';
standardCard.style.background = '#cce5ff';
standardCard.style.borderColor = '#007bff';
standardCard.querySelector('.value').style.color = '#007bff';
}
if (isHigh) {
highCard.querySelector('.value').textContent = 'Supported ✅';
highCard.style.background = '#fff3cd';
highCard.style.borderColor = '#ffc107';
highCard.querySelector('.value').style.color = '#856404';
indicator.textContent = '✨ HDR Display';
indicator.style.background = '#ffc107';
indicator.style.color = '#333';
} else {
highCard.querySelector('.value').textContent = 'Not supported';
highCard.style.background = '#e9ecef';
highCard.style.borderColor = '#6c757d';
highCard.querySelector('.value').style.color = '#6c757d';
indicator.textContent = '🎨 SDR Display';
indicator.style.background = '#007bff';
indicator.style.color = 'white';
}
}
// Current display mode detection
function showDisplayMode() {
const modes = ['browser', 'standalone', 'minimal-ui', 'fullscreen', 'picture-in-picture'];
let current = 'unknown';
for (const mode of modes) {
if (window.matchMedia(`(display-mode: ${mode})`).matches) {
current = mode;
break;
}
}
document.getElementById('current-display-mode').textContent = current;
}
// Fullscreen API
function enterFullscreen() {
document.documentElement.requestFullscreen().catch(err => {
alert('Fullscreen not supported or denied: ' + err.message);
});
}
function exitFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
}
}
// Picture-in-Picture (needs a video element)
function requestPiP() {
alert('Picture-in-Picture requires a video element. Open a video in PiP mode to see this display mode!');
}
// PWA status
function updatePWAStatus() {
const el = document.getElementById('pwa-status-value');
if (window.matchMedia('(display-mode: standalone)').matches) {
el.textContent = '📱 Standalone (Installed)';
el.style.color = '#28a745';
} else {
el.textContent = '🌐 Browser Tab';
el.style.color = '#007bff';
}
}
// Update all
function updateAll() {
updateDisplayModes();
updateDynamicRange();
updatePWAStatus();
showDisplayMode();
}
window.addEventListener('load', updateAll);
window.addEventListener('resize', updateAll);
// Listen for fullscreen changes
document.addEventListener('fullscreenchange', updateAll);
// Listen for PiP changes
document.addEventListener('enterpictureinpicture', updateAll);
document.addEventListener('leavepictureinpicture', updateAll);
updateAll();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
display-mode | How the document is displayed | browser, standalone, minimal-ui, fullscreen, picture-in-picture |
dynamic-range | Brightness, contrast, color depth | standard, high |
display-mode Values
| Value | Description | How to Set |
|---|---|---|
browser | Normal browser tab | Default |
standalone | Installed PWA | Manifest: "display": "standalone" |
minimal-ui | Standalone with minimal UI | Manifest: "display": "minimal-ui" |
fullscreen | Full screen | Fullscreen API |
picture-in-picture | PiP window | PiP API |
dynamic-range Values
| Value | Description | Requirements |
|---|---|---|
standard | Standard Dynamic Range (SDR) | Typical displays (8-bit) |
high | High Dynamic Range (HDR) | Color depth > 24-bit, high contrast, high brightness |
Best Practices
✅ Do This:
/* Adapt PWA UI in standalone mode */
@media (display-mode: standalone) {
.browser-only {
display: none;
}
.standalone-only {
display: block;
}
}
/* Enhance visuals on HDR displays */
@media (dynamic-range: high) {
.hero {
background: linear-gradient(135deg, oklch(69% 0.27 240), oklch(69% 0.27 300));
}
}
/* Standard fallback */
.hero {
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
❌ Don’t Do This:
/* Don't assume all users are in browser mode */
.browser-only {
display: block; /* May show in standalone too */
}
/* Don't use HDR without fallback */
.hero {
background: oklch(69% 0.27 240); /* May look wrong on SDR */
}
/* Don't confuse dynamic-range with color-gamut */
@media (dynamic-range: high) {
/* HDR is about brightness/contrast, not just color */
}
Pro Tip: display-mode is essential for Progressive Web Apps (PWAs). Use it to hide browser-specific UI (like “Add to Home Screen” prompts) when your app is installed as a standalone app. dynamic-range is your key to HDR-enhanced visuals — but remember, HDR is about brightness and contrast, not just color. Always provide a standard fallback first, then enhance for high dynamic range displays. The two features work great together: one for app-like experiences, the other for visually stunning displays!
35. forced-colors and grid Media Features
These two media features let you adapt styles for accessibility (high-contrast mode) and legacy devices (grid-based screens).
Overview of Features
| Feature | Description | Values |
|---|---|---|
forced-colors | Detects forced colors mode (e.g., Windows High Contrast) | none, active |
grid | Detects if the device uses a grid-based screen | 0, 1 |
1. forced-colors
The forced-colors media feature detects when the user agent has forced colors mode enabled — such as Windows High Contrast mode or similar accessibility features.
@media (forced-colors: active) {
button {
border: 1px solid ButtonBorder;
}
}
Values
| Value | Description |
|---|---|
none | Forced colors mode is not active (default) |
active | Forced colors mode is active |
What Happens in Forced Colors Mode
When forced colors mode is active, the browser overrides many CSS properties:
| Property | Forced To |
|---|---|
box-shadow | none |
text-shadow | none |
background-image | none |
color-scheme | light dark |
scrollbar-color | auto |
color | System color |
background-color | System color |
border-color | System color |
outline-color | System color |
text-decoration-color | System color |
column-rule-color | System color |
System Colors
You can use system colors that adapt to the user’s forced colors theme:
| System Color | Description |
|---|---|
Canvas | Background of the document |
CanvasText | Text color |
LinkText | Link color |
ButtonFace | Button background |
ButtonText | Button text |
ButtonBorder | Button border |
Highlight | Selected item background |
HighlightText | Selected item text |
GrayText | Disabled text |
Field | Input field background |
FieldText | Input field text |
/* Use system colors in forced colors mode */
@media (forced-colors: active) {
button {
border: 1px solid ButtonBorder;
background: ButtonFace;
color: ButtonText;
}
}
2. grid
The grid media feature checks whether the output device uses a grid-based screen (like old text-only terminals) or a bitmap-based screen (like modern computers and smartphones).
@media (grid: 0) {
.text {
color: red;
}
}
@media (grid: 1) {
.text {
color: black;
}
}
Values
| Value | Description |
|---|---|
0 | Bitmap-based screen (modern devices) |
1 | Grid-based screen (text-only terminals) |
Grid vs Bitmap
| Screen Type | Description | Examples |
|---|---|---|
| Bitmap | Pixel-based, can display any image | Computers, smartphones, tablets |
| Grid | Character-based, fixed grid of characters | Text-only terminals, old phones, braille displays |
Key Points:
- Modern devices almost always match
grid: 0 - Grid-based devices are rare (legacy or specialized)
- Use
grid: 1to optimize for text-only terminals - Most developers will never need this feature — it’s for extreme edge cases
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>forced-colors and grid Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: background 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== BUTTON STYLES ====== */
button {
padding: 12px 24px;
color: white;
background-color: #007bff;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1em;
font-weight: bold;
transition: all 0.3s;
margin: 5px;
}
button:hover {
background-color: #0056b3;
transform: translateY(-2px);
}
button.btn-danger {
background-color: #dc3545;
}
button.btn-danger:hover {
background-color: #a71d2a;
}
button.btn-success {
background-color: #28a745;
}
button.btn-success:hover {
background-color: #1e7e34;
}
button.btn-warning {
background-color: #ffc107;
color: #333;
}
button.btn-warning:hover {
background-color: #d39e00;
}
/* ====== FORCED COLORS DEMO ====== */
.forced-colors-demo {
padding: 20px;
border-radius: 8px;
background: #e9ecef;
margin: 15px 0;
border: 2px solid #ddd;
transition: all 0.3s;
}
.forced-colors-demo .demo-box {
padding: 15px;
background: #007bff;
color: white;
border-radius: 8px;
margin: 10px 0;
text-align: center;
font-weight: bold;
transition: all 0.3s;
}
/* Forced colors mode styles */
@media (forced-colors: active) {
body {
background: Canvas;
color: CanvasText;
}
section {
background: Canvas;
border: 1px solid CanvasText;
}
h1, h2, h3 {
color: CanvasText;
}
h1 {
border-bottom-color: CanvasText;
}
h2 {
border-left-color: CanvasText;
}
.forced-colors-demo {
background: Canvas;
border: 1px solid CanvasText;
}
.forced-colors-demo .demo-box {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonBorder;
}
button {
border: 1px solid ButtonBorder;
background: ButtonFace;
color: ButtonText;
}
button:hover {
background: Highlight;
color: HighlightText;
}
.code-block {
background: Canvas;
color: CanvasText;
border: 1px solid CanvasText;
}
.reference-table th {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonBorder;
}
.reference-table td {
border: 1px solid CanvasText;
}
.reference-table tr:nth-child(even) {
background: Canvas;
}
a {
color: LinkText;
}
.highlight {
background: Highlight;
color: HighlightText;
}
}
/* ====== GRID DEMO ====== */
.grid-demo {
padding: 20px;
border-radius: 8px;
background: #e9ecef;
margin: 15px 0;
border: 2px solid #ddd;
transition: all 0.3s;
}
.grid-demo .header {
font-size: 1.2rem;
font-weight: bold;
margin-bottom: 10px;
transition: color 0.3s;
}
.grid-demo .text {
transition: color 0.3s;
}
/* Bitmap-based screen (modern devices) */
@media (grid: 0) {
.grid-demo .header {
color: #6c757d;
}
.grid-demo .text {
color: #dc3545;
}
}
/* Grid-based screen (text-only terminals) */
@media (grid: 1) {
.grid-demo .header {
color: #6c757d;
}
.grid-demo .text {
color: #000;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
/* ====== SYSTEM COLORS DEMO ====== */
.system-colors-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
margin: 15px 0;
}
.system-color-box {
padding: 15px;
border-radius: 8px;
text-align: center;
font-weight: bold;
border: 2px solid #ddd;
transition: all 0.3s;
}
/* Default colors */
.sys-canvas { background: #ffffff; color: #000000; }
.sys-canvastext { background: #f0f0f0; color: #000000; }
.sys-linktext { background: #e0e0ff; color: #0000ff; }
.sys-buttonface { background: #e9ecef; color: #333; }
.sys-buttontext { background: #007bff; color: #ffffff; }
.sys-buttonborder { background: #e9ecef; color: #333; border: 3px solid #007bff; }
.sys-highlight { background: #ffc107; color: #333; }
.sys-highlighttext { background: #fff3cd; color: #856404; }
.sys-graytext { background: #e9ecef; color: #999; }
.sys-field { background: #ffffff; color: #333; border: 2px solid #ccc; }
.sys-fieldtext { background: #ffffff; color: #333; }
/* Forced colors mode: use system colors */
@media (forced-colors: active) {
.sys-canvas { background: Canvas; color: CanvasText; }
.sys-canvastext { background: Canvas; color: CanvasText; }
.sys-linktext { background: Canvas; color: LinkText; }
.sys-buttonface { background: ButtonFace; color: ButtonText; }
.sys-buttontext { background: ButtonFace; color: ButtonText; }
.sys-buttonborder { background: ButtonFace; color: ButtonText; border-color: ButtonBorder; }
.sys-highlight { background: Highlight; color: HighlightText; }
.sys-highlighttext { background: Highlight; color: HighlightText; }
.sys-graytext { background: Canvas; color: GrayText; }
.sys-field { background: Field; color: FieldText; }
.sys-fieldtext { background: Field; color: FieldText; }
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
}
@media (forced-colors: active) {
.live-indicator {
background: Highlight;
color: HighlightText;
border: 1px solid ButtonBorder;
box-shadow: none;
}
}
/* ====== BEST PRACTICE DEMO ====== */
.best-practice-card {
background: white;
border: 2px solid #ddd;
border-radius: 12px;
padding: 20px;
max-width: 400px;
margin: 20px auto;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transition: all 0.3s;
}
.best-practice-card h3 {
margin-top: 0;
color: #007bff;
}
.best-practice-card .btn {
display: inline-block;
padding: 12px 30px;
background: #007bff;
color: white;
border-radius: 8px;
text-decoration: none;
font-weight: bold;
margin-top: 10px;
transition: all 0.3s;
}
.best-practice-card .btn:hover {
background: #28a745;
transform: scale(1.02);
}
@media (forced-colors: active) {
.best-practice-card {
background: Canvas;
border: 1px solid CanvasText;
box-shadow: none;
}
.best-practice-card h3 {
color: CanvasText;
}
.best-practice-card .btn {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonBorder;
}
.best-practice-card .btn:hover {
background: Highlight;
color: HighlightText;
}
}
</style>
</head>
<body>
<h1>forced-colors and grid Media Features</h1>
<!-- ====== 1. FORCED-COLORS DEMO ====== -->
<section>
<h2>1. forced-colors</h2>
<p>Detects when the user has <strong>forced colors mode</strong> enabled (e.g., Windows High Contrast).</p>
<div class="forced-colors-demo">
<h3>Forced Colors Demo</h3>
<p>This box adapts to forced colors mode:</p>
<div class="demo-box">
I change colors in forced colors mode!
</div>
<div style="margin-top: 15px;">
<button>Primary Button</button>
<button class="btn-danger">Danger Button</button>
<button class="btn-success">Success Button</button>
<button class="btn-warning">Warning Button</button>
</div>
</div>
<p class="note">
<strong>Try it:</strong> Enable Windows High Contrast mode or a similar accessibility feature to see these styles change.
</p>
<div class="code-block">
/* Detect forced colors mode */
@media (forced-colors: active) {
button {
border: 1px solid ButtonBorder;
background: ButtonFace;
color: ButtonText;
}
button:hover {
background: Highlight;
color: HighlightText;
}
}
</div>
</section>
<!-- ====== 2. SYSTEM COLORS DEMO ====== -->
<section>
<h2>2. System Colors</h2>
<p>System colors adapt to the user's forced colors theme.</p>
<div class="system-colors-grid">
<div class="system-color-box sys-canvas">Canvas</div>
<div class="system-color-box sys-canvastext">CanvasText</div>
<div class="system-color-box sys-linktext">LinkText</div>
<div class="system-color-box sys-buttonface">ButtonFace</div>
<div class="system-color-box sys-buttontext">ButtonText</div>
<div class="system-color-box sys-buttonborder">ButtonBorder</div>
<div class="system-color-box sys-highlight">Highlight</div>
<div class="system-color-box sys-highlighttext">HighlightText</div>
<div class="system-color-box sys-graytext">GrayText</div>
<div class="system-color-box sys-field">Field</div>
<div class="system-color-box sys-fieldtext">FieldText</div>
</div>
<div class="code-block">
/* System colors adapt automatically */
.button {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonBorder;
}
.selected {
background: Highlight;
color: HighlightText;
}
.disabled {
color: GrayText;
}
input {
background: Field;
color: FieldText;
}
</div>
</section>
<!-- ====== 3. GRID DEMO ====== -->
<section>
<h2>3. grid</h2>
<p>Detects whether the device uses a <strong>grid-based screen</strong> (text-only terminal) or <strong>bitmap-based screen</strong> (modern device).</p>
<div class="grid-demo">
<div class="header">Header (always gray)</div>
<div class="text">
This text is <span class="highlight">red</span> on bitmap screens and <span class="highlight">black</span> on grid-based screens.
</div>
</div>
<div id="grid-detection" class="detection-card" style="margin: 15px 0;">
<span class="label">Current screen type:</span>
<span class="value" id="grid-value">Detecting...</span>
</div>
<div class="code-block">
/* Bitmap-based screen (modern devices) */
@media (grid: 0) {
.text {
color: red;
}
}
/* Grid-based screen (text-only terminals) */
@media (grid: 1) {
.text {
color: black;
}
}
</div>
<p class="note"><strong>Note:</strong> Modern devices almost always match <code>grid: 0</code>. Grid-based devices are rare (legacy terminals, braille displays).</p>
</section>
<!-- ====== 4. DETECTION SUMMARY ====== -->
<section>
<h2>4. Your Device's Capabilities</h2>
<p>Below is a live detection of your device's capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="fc-active">
<span class="label">forced-colors: active</span>
<span class="value" id="fc-active-value">Detecting...</span>
</div>
<div class="detection-card" id="fc-none">
<span class="label">forced-colors: none</span>
<span class="value" id="fc-none-value">Detecting...</span>
</div>
<div class="detection-card" id="grid-0">
<span class="label">grid: 0 (bitmap)</span>
<span class="value" id="grid-0-value">Detecting...</span>
</div>
<div class="detection-card" id="grid-1">
<span class="label">grid: 1 (grid-based)</span>
<span class="value" id="grid-1-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect forced colors */
@media (forced-colors: active) { /* Forced colors ON */ }
@media (forced-colors: none) { /* Forced colors OFF */ }
/* Detect screen type */
@media (grid: 0) { /* Bitmap screen */ }
@media (grid: 1) { /* Grid-based screen */ }
</div>
</section>
<!-- ====== 5. PRACTICAL EXAMPLE ====== -->
<section>
<h2>5. Practical Example: Accessible Card</h2>
<p>This card is fully accessible in forced colors mode.</p>
<div class="best-practice-card">
<h3>📱 Accessible Card</h3>
<p>This card uses system colors and proper borders to remain usable in forced colors mode.</p>
<a href="#" class="btn">Learn More</a>
</div>
<div class="code-block">
/* Default styles */
.card {
background: white;
border: 2px solid #ddd;
}
/* Forced colors mode */
@media (forced-colors: active) {
.card {
background: Canvas;
border: 1px solid CanvasText;
}
.card .btn {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonBorder;
}
}
</div>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>forced-colors Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Forced colors mode is not active (default)</td>
</tr>
<tr>
<td><code>active</code></td>
<td>Forced colors mode is active</td>
</tr>
</table>
<h3>Properties Forced in Forced Colors Mode</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Forced To</th>
</tr>
<tr>
<td><code>box-shadow</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>text-shadow</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>background-image</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>color-scheme</code></td>
<td><code>light dark</code></td>
</tr>
<tr>
<td><code>scrollbar-color</code></td>
<td><code>auto</code></td>
</tr>
</table>
<h3>Common System Colors</h3>
<table class="reference-table">
<tr>
<th>System Color</th>
<th>Description</th>
</tr>
<tr>
<td><code>Canvas</code></td>
<td>Background of the document</td>
</tr>
<tr>
<td><code>CanvasText</code></td>
<td>Text color</td>
</tr>
<tr>
<td><code>LinkText</code></td>
<td>Link color</td>
</tr>
<tr>
<td><code>ButtonFace</code></td>
<td>Button background</td>
</tr>
<tr>
<td><code>ButtonText</code></td>
<td>Button text</td>
</tr>
<tr>
<td><code>ButtonBorder</code></td>
<td>Button border</td>
</tr>
<tr>
<td><code>Highlight</code></td>
<td>Selected item background</td>
</tr>
<tr>
<td><code>HighlightText</code></td>
<td>Selected item text</td>
</tr>
<tr>
<td><code>GrayText</code></td>
<td>Disabled text</td>
</tr>
<tr>
<td><code>Field</code></td>
<td>Input field background</td>
</tr>
<tr>
<td><code>FieldText</code></td>
<td>Input field text</td>
</tr>
</table>
<h3>grid Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Examples</th>
</tr>
<tr>
<td><code>0</code></td>
<td>Bitmap-based screen (modern)</td>
<td>Computers, smartphones, tablets</td>
</tr>
<tr>
<td><code>1</code></td>
<td>Grid-based screen (character-based)</td>
<td>Text-only terminals, braille displays</td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>forced-colors</code> to ensure your site is usable in high-contrast mode</li>
<li>Use <strong>system colors</strong> (ButtonFace, CanvasText, etc.) for forced colors mode</li>
<li>Add borders to elements that rely on background color for boundaries</li>
<li>Test your site with Windows High Contrast mode enabled</li>
<li>Use <code>grid</code> only if you need to support text-only terminals</li>
<li>Provide meaningful content even without images or shadows</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely solely on background colors to convey meaning</li>
<li>Don't use box-shadows as the only visual separator in forced colors mode</li>
<li>Don't forget to test with accessibility features enabled</li>
<li>Don't use <code>grid: 1</code> styles for modern devices (they won't match)</li>
<li>Don't ignore forced colors mode — it affects millions of users</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">Detecting...</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
const indicator = document.getElementById('live-indicator');
// Forced colors detection
const fcActive = document.getElementById('fc-active');
const fcNone = document.getElementById('fc-none');
const isForcedColors = window.matchMedia('(forced-colors: active)').matches;
if (isForcedColors) {
fcActive.querySelector('.value').textContent = 'Active ✅';
fcActive.style.background = '#fff3cd';
fcActive.style.borderColor = '#ffc107';
fcActive.querySelector('.value').style.color = '#856404';
fcNone.querySelector('.value').textContent = 'Inactive';
fcNone.style.background = '#e9ecef';
fcNone.style.borderColor = '#6c757d';
fcNone.querySelector('.value').style.color = '#6c757d';
indicator.textContent = '♿ Forced Colors Active';
indicator.style.background = '#ffc107';
indicator.style.color = '#333';
} else {
fcActive.querySelector('.value').textContent = 'Not active';
fcActive.style.background = '#e9ecef';
fcActive.style.borderColor = '#6c757d';
fcActive.querySelector('.value').style.color = '#6c757d';
fcNone.querySelector('.value').textContent = 'Active (default) ✅';
fcNone.style.background = '#d4edda';
fcNone.style.borderColor = '#28a745';
fcNone.querySelector('.value').style.color = '#28a745';
indicator.textContent = '🎨 Normal Colors';
indicator.style.background = '#007bff';
indicator.style.color = 'white';
}
// Grid detection
const grid0 = document.getElementById('grid-0');
const grid1 = document.getElementById('grid-1');
const gridValue = document.getElementById('grid-value');
if (window.matchMedia('(grid: 0)').matches) {
grid0.querySelector('.value').textContent = 'Bitmap ✅';
grid0.style.background = '#cce5ff';
grid0.style.borderColor = '#007bff';
grid0.querySelector('.value').style.color = '#007bff';
grid1.querySelector('.value').textContent = 'Not grid-based';
grid1.style.background = '#e9ecef';
grid1.style.borderColor = '#6c757d';
grid1.querySelector('.value').style.color = '#6c757d';
if (gridValue) gridValue.textContent = '🖥️ Bitmap-based screen (modern device)';
} else if (window.matchMedia('(grid: 1)').matches) {
grid1.querySelector('.value').textContent = 'Grid-based ✅';
grid1.style.background = '#fff3cd';
grid1.style.borderColor = '#ffc107';
grid1.querySelector('.value').style.color = '#856404';
grid0.querySelector('.value').textContent = 'Not bitmap';
grid0.style.background = '#e9ecef';
grid0.style.borderColor = '#6c757d';
grid0.querySelector('.value').style.color = '#6c757d';
if (gridValue) gridValue.textContent = '📟 Grid-based screen (text-only terminal)';
}
}
window.addEventListener('load', updateIndicators);
// Listen for forced colors changes
window.matchMedia('(forced-colors: active)').addEventListener('change', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
forced-colors | Detects forced colors mode | none, active |
grid | Detects grid-based screens | 0, 1 |
forced-colors Values
| Value | Description |
|---|---|
none | Forced colors mode is not active (default) |
active | Forced colors mode is active |
Properties Forced in Forced Colors Mode
| Property | Forced To |
|---|---|
box-shadow | none |
text-shadow | none |
background-image | none |
color-scheme | light dark |
scrollbar-color | auto |
Common System Colors
| System Color | Description |
|---|---|
Canvas | Background of the document |
CanvasText | Text color |
LinkText | Link color |
ButtonFace | Button background |
ButtonText | Button text |
ButtonBorder | Button border |
Highlight | Selected item background |
HighlightText | Selected item text |
GrayText | Disabled text |
Field | Input field background |
FieldText | Input field text |
grid Values
| Value | Description | Examples |
|---|---|---|
0 | Bitmap-based screen (modern) | Computers, smartphones, tablets |
1 | Grid-based screen (character-based) | Text-only terminals, braille displays |
Best Practices
✅ Do This:
/* Support forced colors mode */
@media (forced-colors: active) {
button {
border: 1px solid ButtonBorder;
background: ButtonFace;
color: ButtonText;
}
button:hover {
background: Highlight;
color: HighlightText;
}
}
/* Use system colors for accessibility */
.card {
background: Canvas;
color: CanvasText;
border: 1px solid CanvasText;
}
/* Optimize for grid-based devices */
@media (grid: 1) {
.text {
color: black;
}
}
❌ Don’t Do This:
/* Don't rely on shadows for boundaries */
.card {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
/* Shadow disappears in forced colors mode! */
}
/* Don't rely on background colors alone */
.error {
background: #ff0000; /* May not be visible in high contrast */
}
/* Don't ignore forced colors mode */
/* Test with Windows High Contrast mode! */
Pro Tip: forced-colors is essential for accessibility. Millions of users rely on high-contrast modes due to visual impairments. Always test your site with Windows High Contrast mode or similar features. Use system colors (ButtonFace, CanvasText, etc.) and add borders to elements that rely on background colors for boundaries. grid is a legacy feature — modern devices almost always match grid: 0, but it’s good to know for extreme
36. height and width Media Features
These two media features are the cornerstones of responsive design. They let you apply styles based on the viewport’s dimensions, enabling layouts that adapt to any screen size.
Overview of Features
| Feature | Description | Values |
|---|---|---|
height | Viewport height | Exact, min-height, max-height |
width | Viewport width | Exact, min-width, max-width |
1. height
The height media feature applies styles based on the height of the viewport (or the dimensions of an embedded document).
@media (height: 1024px) {
.container {
background-color: pink;
}
}
Variations
| Feature | Description | Example |
|---|---|---|
height | Exact height | @media (height: 1024px) |
min-height | Minimum height | @media (min-height: 1024px) |
max-height | Maximum height | @media (max-height: 1200px) |
Syntax
height: <length>
height: >= 1024px
height: < 1200px
height: 1024px
Common Use Cases
| Use Case | Example |
|---|---|
| Mobile landscape | @media (max-height: 500px) |
| Tall screens | @media (min-height: 900px) |
| Short screens | @media (max-height: 600px) |
| Full-screen sections | @media (min-height: 100vh) |
2. width
The width media feature applies styles based on the width of the viewport.
@media (min-width: 780px) {
.container {
background-color: red;
}
}
Variations
| Feature | Description | Example |
|---|---|---|
width | Exact width | @media (width: 780px) |
min-width | Minimum width | @media (min-width: 780px) |
max-width | Maximum width | @media (max-width: 1200px) |
Syntax
width: <length>
width: >= 780px
width: < 1200px
width: 780px
Common Breakpoints
| Breakpoint | Description |
|---|---|
320px | Small phones |
480px | Larger phones |
768px | Tablets |
1024px | Small laptops |
1200px | Desktops |
1440px | Large desktops |
1920px | Full HD displays |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>height and width Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== WIDTH DEMO ====== */
.width-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
margin: 15px 0;
transition: all 0.3s;
background: #e9ecef;
border: 2px solid #ddd;
}
/* Mobile: < 480px */
@media (max-width: 479px) {
.width-demo {
background: #ff6b6b;
color: white;
border-color: #c92a2a;
}
.width-demo::after {
content: " 📱 Mobile (< 480px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Large phones: 480px - 767px */
@media (min-width: 480px) and (max-width: 767px) {
.width-demo {
background: #ffc107;
color: #333;
border-color: #d39e00;
}
.width-demo::after {
content: " 📱 Large Phone (480-767px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Tablets: 768px - 1023px */
@media (min-width: 768px) and (max-width: 1023px) {
.width-demo {
background: #28a745;
color: white;
border-color: #1e7e34;
}
.width-demo::after {
content: " 📟 Tablet (768-1023px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Desktops: 1024px - 1439px */
@media (min-width: 1024px) and (max-width: 1439px) {
.width-demo {
background: #007bff;
color: white;
border-color: #0056b3;
}
.width-demo::after {
content: " 💻 Desktop (1024-1439px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Large Desktops: ≥ 1440px */
@media (min-width: 1440px) {
.width-demo {
background: #6c5ce7;
color: white;
border-color: #4a2a9e;
}
.width-demo::after {
content: " 🖥️ Large Desktop (≥ 1440px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== HEIGHT DEMO ====== */
.height-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
margin: 15px 0;
transition: all 0.3s;
background: #e9ecef;
border: 2px solid #ddd;
min-height: 150px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* Short viewport: < 600px */
@media (max-height: 599px) {
.height-demo {
background: #dc3545;
color: white;
border-color: #a71d2a;
}
.height-demo::after {
content: " 📏 Short Viewport (< 600px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Medium viewport: 600px - 899px */
@media (min-height: 600px) and (max-height: 899px) {
.height-demo {
background: #ffc107;
color: #333;
border-color: #d39e00;
}
.height-demo::after {
content: " 📏 Medium Viewport (600-899px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Tall viewport: ≥ 900px */
@media (min-height: 900px) {
.height-demo {
background: #28a745;
color: white;
border-color: #1e7e34;
}
.height-demo::after {
content: " 📏 Tall Viewport (≥ 900px)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== RESPONSIVE GRID DEMO ====== */
.responsive-grid {
display: grid;
gap: 15px;
margin: 20px 0;
}
/* Mobile: 1 column */
.responsive-grid {
grid-template-columns: 1fr;
}
/* Tablet: 2 columns */
@media (min-width: 600px) {
.responsive-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop: 3 columns */
@media (min-width: 900px) {
.responsive-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Large Desktop: 4 columns */
@media (min-width: 1200px) {
.responsive-grid {
grid-template-columns: repeat(4, 1fr);
}
}
.responsive-card {
background: white;
border: 2px solid #ddd;
border-radius: 8px;
padding: 20px;
text-align: center;
transition: all 0.3s;
}
.responsive-card:hover {
border-color: #007bff;
transform: translateY(-3px);
box-shadow: 0 4px 15px rgba(0,123,255,0.15);
}
.responsive-card .icon {
font-size: 2.5rem;
display: block;
margin-bottom: 10px;
}
.responsive-card .title {
font-weight: bold;
color: #007bff;
margin-bottom: 5px;
}
.responsive-card .desc {
font-size: 0.85rem;
color: #6c757d;
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .dimension {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== VIEWPORT VISUALIZATION ====== */
.viewport-viz {
position: relative;
background: #e9ecef;
border: 2px dashed #007bff;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
min-height: 200px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.viewport-viz .dimension-label {
position: absolute;
font-weight: bold;
color: #007bff;
font-size: 0.85rem;
}
.viewport-viz .width-label {
top: -12px;
left: 50%;
transform: translateX(-50%);
background: white;
padding: 2px 10px;
border-radius: 4px;
border: 1px solid #007bff;
}
.viewport-viz .height-label {
right: -12px;
top: 50%;
transform: translateY(-50%) rotate(90deg);
background: white;
padding: 2px 10px;
border-radius: 4px;
border: 1px solid #007bff;
white-space: nowrap;
}
.viewport-viz .content {
font-size: 1.1rem;
font-weight: bold;
color: #333;
}
/* ====== PRACTICAL: RESPONSIVE NAVBAR ====== */
.navbar {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 15px;
background: #007bff;
border-radius: 8px;
margin: 15px 0;
transition: all 0.3s;
}
.navbar a {
color: white;
text-decoration: none;
padding: 8px 16px;
border-radius: 6px;
transition: background 0.3s;
font-weight: bold;
}
.navbar a:hover {
background: rgba(255,255,255,0.2);
}
/* Mobile: vertical stack */
@media (max-width: 599px) {
.navbar {
flex-direction: column;
background: #dc3545;
}
.navbar a {
text-align: center;
}
}
/* Desktop: horizontal row */
@media (min-width: 600px) {
.navbar {
flex-direction: row;
justify-content: center;
}
}
/* ====== BEST PRACTICE: MOBILE FIRST ====== */
.mobile-first-demo {
padding: 20px;
border-radius: 8px;
background: #ff6b6b;
color: white;
text-align: center;
font-weight: bold;
margin: 15px 0;
transition: all 0.3s;
}
/* Mobile first — base styles */
.mobile-first-demo::after {
content: " 📱 Mobile First (base styles)";
font-weight: normal;
font-size: 0.85rem;
display: block;
}
/* Tablet and up */
@media (min-width: 768px) {
.mobile-first-demo {
background: #28a745;
}
.mobile-first-demo::after {
content: " 📟 Tablet and up (min-width: 768px)";
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.mobile-first-demo {
background: #007bff;
}
.mobile-first-demo::after {
content: " 💻 Desktop and up (min-width: 1024px)";
}
}
/* Large desktop and up */
@media (min-width: 1440px) {
.mobile-first-demo {
background: #6c5ce7;
}
.mobile-first-demo::after {
content: " 🖥️ Large Desktop and up (min-width: 1440px)";
}
}
</style>
</head>
<body>
<h1>height and width Media Features</h1>
<!-- ====== 1. WIDTH DEMO ====== -->
<section>
<h2>1. width</h2>
<p>Applies styles based on the <strong>viewport width</strong>. Resize your browser to see the changes!</p>
<div class="width-demo">
Current Width
</div>
<div class="viewport-viz">
<span class="dimension-label width-label" id="width-label">Width: detecting...</span>
<span class="dimension-label height-label" id="height-label">Height: detecting...</span>
<div class="content">
Resize your browser window
</div>
</div>
<div class="code-block">
/* Mobile: < 480px */
@media (max-width: 479px) {
.width-demo { background: #ff6b6b; }
}
/* Large phones: 480px - 767px */
@media (min-width: 480px) and (max-width: 767px) {
.width-demo { background: #ffc107; }
}
/* Tablets: 768px - 1023px */
@media (min-width: 768px) and (max-width: 1023px) {
.width-demo { background: #28a745; }
}
/* Desktops: 1024px - 1439px */
@media (min-width: 1024px) and (max-width: 1439px) {
.width-demo { background: #007bff; }
}
/* Large Desktops: ≥ 1440px */
@media (min-width: 1440px) {
.width-demo { background: #6c5ce7; }
}
</div>
</section>
<!-- ====== 2. HEIGHT DEMO ====== -->
<section>
<h2>2. height</h2>
<p>Applies styles based on the <strong>viewport height</strong>. Resize your browser vertically to see the changes!</p>
<div class="height-demo">
Current Height
</div>
<div class="code-block">
/* Short viewport: < 600px */
@media (max-height: 599px) {
.height-demo { background: #dc3545; }
}
/* Medium viewport: 600px - 899px */
@media (min-height: 600px) and (max-height: 899px) {
.height-demo { background: #ffc107; }
}
/* Tall viewport: ≥ 900px */
@media (min-height: 900px) {
.height-demo { background: #28a745; }
}
</div>
</section>
<!-- ====== 3. RESPONSIVE GRID ====== -->
<section>
<h2>3. Practical Example: Responsive Grid</h2>
<p>This grid adapts from 1 to 4 columns based on the viewport width.</p>
<div class="responsive-grid">
<div class="responsive-card">
<span class="icon">🎨</span>
<div class="title">Design</div>
<div class="desc">Beautiful interfaces</div>
</div>
<div class="responsive-card">
<span class="icon">💻</span>
<div class="title">Development</div>
<div class="desc">Clean code</div>
</div>
<div class="responsive-card">
<span class="icon">📱</span>
<div class="title">Responsive</div>
<div class="desc">Works everywhere</div>
</div>
<div class="responsive-card">
<span class="icon">⚡</span>
<div class="title">Performance</div>
<div class="desc">Fast loading</div>
</div>
</div>
<div class="code-block">
/* Mobile: 1 column */
.responsive-grid {
grid-template-columns: 1fr;
}
/* Tablet: 2 columns */
@media (min-width: 600px) {
.responsive-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop: 3 columns */
@media (min-width: 900px) {
.responsive-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Large Desktop: 4 columns */
@media (min-width: 1200px) {
.responsive-grid {
grid-template-columns: repeat(4, 1fr);
}
}
</div>
</section>
<!-- ====== 4. RESPONSIVE NAVBAR ====== -->
<section>
<h2>4. Practical Example: Responsive Navbar</h2>
<p>The navbar changes from vertical (mobile) to horizontal (desktop).</p>
<div class="navbar">
<a href="#">🏠 Home</a>
<a href="#">📄 About</a>
<a href="#">⚙️ Services</a>
<a href="#">📞 Contact</a>
</div>
<div class="code-block">
/* Mobile: vertical stack */
@media (max-width: 599px) {
.navbar {
flex-direction: column;
}
}
/* Desktop: horizontal row */
@media (min-width: 600px) {
.navbar {
flex-direction: row;
justify-content: center;
}
}
</div>
</section>
<!-- ====== 5. MOBILE FIRST DEMO ====== -->
<section>
<h2>5. Mobile First Approach</h2>
<p>Start with mobile styles and add complexity as the screen gets larger.</p>
<div class="mobile-first-demo">
Mobile First Demo
</div>
<div class="code-block">
/* Mobile first — base styles */
.demo {
background: #ff6b6b;
}
/* Tablet and up */
@media (min-width: 768px) {
.demo {
background: #28a745;
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.demo {
background: #007bff;
}
}
/* Large desktop and up */
@media (min-width: 1440px) {
.demo {
background: #6c5ce7;
}
}
</div>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>width and height Variations</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>width</code></td>
<td>Exact viewport width</td>
<td><code>@media (width: 780px)</code></td>
</tr>
<tr>
<td><code>min-width</code></td>
<td>Minimum viewport width</td>
<td><code>@media (min-width: 780px)</code></td>
</tr>
<tr>
<td><code>max-width</code></td>
<td>Maximum viewport width</td>
<td><code>@media (max-width: 1200px)</code></td>
</tr>
<tr>
<td><code>height</code></td>
<td>Exact viewport height</td>
<td><code>@media (height: 1024px)</code></td>
</tr>
<tr>
<td><code>min-height</code></td>
<td>Minimum viewport height</td>
<td><code>@media (min-height: 1024px)</code></td>
</tr>
<tr>
<td><code>max-height</code></td>
<td>Maximum viewport height</td>
<td><code>@media (max-height: 1200px)</code></td>
</tr>
</table>
<h3>Common Breakpoints</h3>
<table class="reference-table">
<tr>
<th>Breakpoint</th>
<th>Device</th>
<th>Example</th>
</tr>
<tr>
<td><code>320px</code></td>
<td>Small phones</td>
<td><code>@media (min-width: 320px)</code></td>
</tr>
<tr>
<td><code>480px</code></td>
<td>Larger phones</td>
<td><code>@media (min-width: 480px)</code></td>
</tr>
<tr>
<td><code>768px</code></td>
<td>Tablets</td>
<td><code>@media (min-width: 768px)</code></td>
</tr>
<tr>
<td><code>1024px</code></td>
<td>Small laptops</td>
<td><code>@media (min-width: 1024px)</code></td>
</tr>
<tr>
<td><code>1200px</code></td>
<td>Desktops</td>
<td><code>@media (min-width: 1200px)</code></td>
</tr>
<tr>
<td><code>1440px</code></td>
<td>Large desktops</td>
<td><code>@media (min-width: 1440px)</code></td>
</tr>
<tr>
<td><code>1920px</code></td>
<td>Full HD displays</td>
<td><code>@media (min-width: 1920px)</code></td>
</tr>
</table>
<h3>Common Height Breakpoints</h3>
<table class="reference-table">
<tr>
<th>Breakpoint</th>
<th>Device</th>
<th>Example</th>
</tr>
<tr>
<td><code>500px</code></td>
<td>Mobile landscape</td>
<td><code>@media (max-height: 500px)</code></td>
</tr>
<tr>
<td><code>600px</code></td>
<td>Short viewport</td>
<td><code>@media (max-height: 600px)</code></td>
</tr>
<tr>
<td><code>900px</code></td>
<td>Tall viewport</td>
<td><code>@media (min-height: 900px)</code></td>
</tr>
<tr>
<td><code>1080px</code></td>
<td>Full HD height</td>
<td><code>@media (min-height: 1080px)</code></td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li><strong>Mobile First</strong> — Start with smallest screen and build up</li>
<li>Use <code>min-width</code> for breakpoints (mobile-first approach)</li>
<li>Use <code>max-width</code> for desktop-first approach</li>
<li>Use relative units (<code>em</code>, <code>rem</code>, <code>%</code>) for breakpoints</li>
<li>Test on real devices, not just browser resizing</li>
<li>Combine width and height for comprehensive responsiveness</li>
<li>Use <code>min-height: 100vh</code> for full-screen sections</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use too many breakpoints — aim for 3–5 major ones</li>
<li>Don't use exact <code>width</code> values (they're rarely useful)</li>
<li>Don't forget about mobile landscape orientation</li>
<li>Don't use fixed pixel values for everything</li>
<li>Don't assume all screens are the same size</li>
<li>Don't forget to test with the viewport meta tag</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-width">Width: —</span>
<span class="dimension" id="indicator-height">Height: —</span>
</div>
<!-- ====== SCRIPT FOR LIVE DIMENSIONS ====== -->
<script>
function updateDimensions() {
const w = window.innerWidth;
const h = window.innerHeight;
// Update live indicator
document.getElementById('indicator-width').textContent = 'Width: ' + w + 'px';
document.getElementById('indicator-height').textContent = 'Height: ' + h + 'px';
// Update viewport visualization labels
const widthLabel = document.getElementById('width-label');
const heightLabel = document.getElementById('height-label');
if (widthLabel) widthLabel.textContent = 'Width: ' + w + 'px';
if (heightLabel) heightLabel.textContent = 'Height: ' + h + 'px';
// Update breakpoint indicator color
const indicator = document.getElementById('live-indicator');
if (w < 480) {
indicator.style.background = '#ff6b6b';
} else if (w < 768) {
indicator.style.background = '#ffc107';
indicator.style.color = '#333';
} else if (w < 1024) {
indicator.style.background = '#28a745';
indicator.style.color = 'white';
} else if (w < 1440) {
indicator.style.background = '#007bff';
indicator.style.color = 'white';
} else {
indicator.style.background = '#6c5ce7';
indicator.style.color = 'white';
}
}
window.addEventListener('resize', updateDimensions);
window.addEventListener('load', updateDimensions);
updateDimensions();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Example |
|---|---|---|
width | Exact viewport width | @media (width: 780px) |
min-width | Minimum viewport width | @media (min-width: 780px) |
max-width | Maximum viewport width | @media (max-width: 1200px) |
height | Exact viewport height | @media (height: 1024px) |
min-height | Minimum viewport height | @media (min-height: 1024px) |
max-height | Maximum viewport height | @media (max-height: 1200px) |
Common Breakpoints
| Breakpoint | Device |
|---|---|
320px | Small phones |
480px | Larger phones |
768px | Tablets |
1024px | Small laptops |
1200px | Desktops |
1440px | Large desktops |
1920px | Full HD displays |
Common Height Breakpoints
| Breakpoint | Device |
|---|---|
500px | Mobile landscape |
600px | Short viewport |
900px | Tall viewport |
1080px | Full HD height |
Best Practices
✅ Do This:
/* Mobile First approach */
.container {
/* Base styles for mobile */
display: block;
}
/* Tablet and up */
@media (min-width: 768px) {
.container {
display: flex;
}
}
/* Desktop and up */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
}
}
/* Handle mobile landscape */
@media (max-height: 500px) {
.hero {
min-height: 100vh;
}
}
❌ Don’t Do This:
/* Don't use exact width values */
@media (width: 780px) {
/* Rarely useful — only matches exactly 780px */
}
/* Don't use too many breakpoints */
@media (min-width: 320px) { }
@media (min-width: 375px) { }
@media (min-width: 414px) { }
@media (min-width: 480px) { }
@media (min-width: 640px) { }
@media (min-width: 768px) { }
/* Too many — aim for 3-5 major breakpoints */
/* Don't forget the viewport meta tag */
/* <meta name="viewport" content="width=device-width, initial-scale=1.0"> */
Pro Tip: width and height are the most fundamental media features for responsive design. Use the Mobile First approach — start with styles for the smallest screens and add complexity as the screen gets larger. Use min-width for breakpoints (not max-width). Don’t forget the viewport meta tag — without it, mobile devices won’t respect your media queries! And remember: test on real devices, not just by resizing your browser window!
37. hover and inverted-colors Media Features
These two media features help you create input-aware and accessibility-friendly interfaces — one detects hover capability, the other detects inverted color schemes.
Overview of Features
| Feature | Description | Values |
|---|---|---|
hover | Detects if the primary pointing device can hover | none, hover |
inverted-colors | Detects if the system uses inverted colors | none, inverted |
1. hover
The hover media feature applies styles when the primary pointing device can hover over elements.
@media (hover: hover) {
button:hover {
background-color: red;
}
}
Values
| Value | Description |
|---|---|
none | Primary input cannot hover (e.g., touch screen) |
hover | Primary input can hover (e.g., mouse, trackpad) |
Key Points:
hoverchecks the primary pointing deviceany-hoverchecks any available pointing device- Use
hoverto ensure hover effects only apply when they make sense - On touch devices,
:hovercan be triggered accidentally — use@media (hover: hover)to prevent this
hover vs any-hover
| Feature | Description |
|---|---|
hover | Primary pointing device’s hover capability |
any-hover | Any pointing device’s hover capability |
Example:
- Tablet with Bluetooth mouse:
hover: none(primary is touch)any-hover: hover(mouse can hover)
2. inverted-colors
The inverted-colors media feature applies styles when the system’s display uses an inverted color scheme — where dark colors become bright and vice versa.
@media (inverted-colors: inverted) {
body {
background-color: black;
color: white;
}
}
Values
| Value | Description |
|---|---|
none | Colors are displayed normally |
inverted | Display uses inverted color scheme |
Key Points:
- Inverted colors are used for accessibility (poor visibility conditions)
- Common on iOS (Accessibility > Display & Text Size > Classic Invert)
- Images and media may be double-inverted — use
filter: invert(1)to counter - Helps make content readable when inverted colors are enabled
Handling Images in Inverted Mode
@media (inverted-colors: inverted) {
/* Re-invert images so they look normal */
img, video {
filter: invert(1);
}
}
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hover and inverted-colors Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: background 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== BUTTON STYLES ====== */
button {
padding: 12px 24px;
font-size: 16px;
color: white;
background-color: #007bff;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s;
margin: 5px;
}
/* Hover effect only for devices that can hover */
@media (hover: hover) {
button:hover {
background-color: #dc3545;
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(220, 53, 69, 0.4);
}
}
/* For touch devices — use :active instead */
@media (hover: none) {
button:active {
background-color: #dc3545;
transform: scale(0.98);
}
button::after {
content: " (tap me)";
font-weight: normal;
font-size: 0.8rem;
opacity: 0.8;
}
}
/* ====== CARD STYLES ====== */
.card {
background: white;
border: 2px solid #ddd;
border-radius: 12px;
padding: 20px;
margin: 15px 0;
transition: all 0.3s;
cursor: pointer;
}
@media (hover: hover) {
.card:hover {
border-color: #007bff;
box-shadow: 0 4px 20px rgba(0, 123, 255, 0.15);
transform: translateY(-3px);
}
}
@media (hover: none) {
.card:active {
border-color: #007bff;
background: #f0f7ff;
}
}
/* ====== TOOLTIP STYLES ====== */
.tooltip-container {
position: relative;
display: inline-block;
margin: 10px;
}
.tooltip-text {
visibility: hidden;
opacity: 0;
background: #333;
color: white;
padding: 8px 12px;
border-radius: 6px;
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
font-size: 0.85rem;
transition: all 0.3s;
margin-bottom: 8px;
}
.tooltip-text::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #333;
}
@media (hover: hover) {
.tooltip-container:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
}
/* ====== INVERTED COLORS DEMO ====== */
.invert-demo {
padding: 20px;
border-radius: 8px;
background: #e9ecef;
margin: 15px 0;
border: 2px solid #ddd;
transition: all 0.3s;
}
.invert-demo .box {
padding: 15px;
background: #007bff;
color: white;
border-radius: 8px;
margin: 10px 0;
text-align: center;
font-weight: bold;
transition: all 0.3s;
}
/* Inverted colors mode */
@media (inverted-colors: inverted) {
body {
background: #000;
color: #fff;
}
section {
background: #1a1a1a;
border: 1px solid #333;
}
h1, h2, h3 {
color: #4dabf7;
}
h1 {
border-bottom-color: #4dabf7;
}
h2 {
border-left-color: #69db7c;
}
.invert-demo {
background: #2d2d2d;
border-color: #444;
}
.invert-demo .box {
background: #4dabf7;
color: #000;
}
.code-block {
background: #0d0d0d;
border: 1px solid #333;
}
.reference-table th {
background: #4dabf7;
color: #000;
}
.reference-table td {
border-color: #444;
}
.reference-table tr:nth-child(even) {
background: #1a1a1a;
}
button {
background: #4dabf7;
color: #000;
}
@media (hover: hover) {
button:hover {
background: #ff6b6b;
color: #000;
}
}
.card {
background: #1a1a1a;
border-color: #444;
}
/* Re-invert images so they look normal */
img, video {
filter: invert(1);
}
.tooltip-text {
background: #fff;
color: #000;
}
.tooltip-text::after {
border-top-color: #fff;
}
.note {
color: #adb5bd;
}
.highlight {
background: #4dabf7;
color: #000;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== PRACTICAL: NAVIGATION ====== */
.nav-menu {
display: flex;
flex-wrap: wrap;
gap: 10px;
list-style: none;
padding: 0;
margin: 15px 0;
}
.nav-menu li a {
display: block;
padding: 10px 20px;
background: #007bff;
color: white;
text-decoration: none;
border-radius: 8px;
transition: all 0.3s;
font-weight: bold;
}
@media (hover: hover) {
.nav-menu li a:hover {
background: #28a745;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
}
}
@media (hover: none) {
.nav-menu li a:active {
background: #28a745;
transform: scale(0.97);
}
}
/* ====== INVERTED COLORS BUTTON ====== */
.invert-toggle {
background: #6c5ce7;
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
margin: 10px 5px;
}
@media (hover: hover) {
.invert-toggle:hover {
background: #4a2a9e;
transform: translateY(-2px);
}
}
/* ====== PRACTICAL: INVERTED IMAGE HANDLING ====== */
.image-demo {
display: flex;
flex-wrap: wrap;
gap: 20px;
align-items: center;
margin: 15px 0;
}
.image-demo .img-box {
width: 200px;
height: 150px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 1.2rem;
text-align: center;
padding: 20px;
}
@media (inverted-colors: inverted) {
.image-demo .img-box {
filter: invert(1);
}
}
</style>
</head>
<body>
<h1>hover and inverted-colors Media Features</h1>
<!-- ====== 1. HOVER DEMO ====== -->
<section>
<h2>1. hover</h2>
<p>Applies styles when the <strong>primary pointing device</strong> can hover over elements.</p>
<h3>Button Hover</h3>
<button>Hover or Tap Me!</button>
<p class="note">On desktop with a mouse, the button turns red on hover. On touch devices, it responds to tap (active state).</p>
<h3>Card Hover</h3>
<div class="card">
<strong>Interactive Card</strong>
<p>This card lifts up on hover (desktop) or changes on tap (mobile).</p>
</div>
<h3>Tooltip Hover</h3>
<div class="tooltip-container">
<button>Hover for tooltip</button>
<span class="tooltip-text">This is a tooltip!</span>
</div>
<p class="note">Tooltips only appear on devices that can hover.</p>
<div class="code-block">
/* Hover effect only for devices that can hover */
@media (hover: hover) {
button:hover {
background-color: #dc3545;
transform: translateY(-2px);
}
}
/* Touch-friendly alternative */
@media (hover: none) {
button:active {
background-color: #dc3545;
transform: scale(0.98);
}
}
</div>
</section>
<!-- ====== 2. INVERTED-COLORS DEMO ====== -->
<section>
<h2>2. inverted-colors</h2>
<p>Applies styles when the system uses an <strong>inverted color scheme</strong>.</p>
<div class="invert-demo">
<h3>Inverted Colors Demo</h3>
<p>This box adapts to inverted colors mode:</p>
<div class="box">
I adapt to inverted colors!
</div>
<p class="note">Enable inverted colors in your OS accessibility settings to see the changes.</p>
</div>
<h3>Image Handling in Inverted Mode</h3>
<div class="image-demo">
<div class="img-box">🖼️ Image</div>
<p>Images are re-inverted with <code>filter: invert(1)</code> so they look normal in inverted mode.</p>
</div>
<div class="code-block">
/* Inverted colors mode */
@media (inverted-colors: inverted) {
body {
background: #000;
color: #fff;
}
section {
background: #1a1a1a;
}
/* Re-invert images so they look normal */
img, video {
filter: invert(1);
}
}
</div>
</section>
<!-- ====== 3. DETECTION SUMMARY ====== -->
<section>
<h2>3. Your Device's Capabilities</h2>
<p>Below is a live detection of your device's capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="hover-hover">
<span class="label">hover: hover</span>
<span class="value" id="hover-hover-value">Detecting...</span>
</div>
<div class="detection-card" id="hover-none">
<span class="label">hover: none</span>
<span class="value" id="hover-none-value">Detecting...</span>
</div>
<div class="detection-card" id="any-hover-hover">
<span class="label">any-hover: hover</span>
<span class="value" id="any-hover-hover-value">Detecting...</span>
</div>
<div class="detection-card" id="invert-none">
<span class="label">inverted-colors: none</span>
<span class="value" id="invert-none-value">Detecting...</span>
</div>
<div class="detection-card" id="invert-inverted">
<span class="label">inverted-colors: inverted</span>
<span class="value" id="invert-inverted-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect hover capability */
@media (hover: hover) { /* Primary device can hover */ }
@media (hover: none) { /* Primary device cannot hover */ }
/* Detect any hover capability */
@media (any-hover: hover) { /* Any device can hover */ }
/* Detect inverted colors */
@media (inverted-colors: none) { /* Normal colors */ }
@media (inverted-colors: inverted) { /* Inverted colors */ }
</div>
</section>
<!-- ====== 4. PRACTICAL EXAMPLE: NAVIGATION ====== -->
<section>
<h2>4. Practical Example: Hover-Aware Navigation</h2>
<p>A navigation menu that works well on both desktop and touch devices.</p>
<ul class="nav-menu">
<li><a href="#">🏠 Home</a></li>
<li><a href="#">📄 About</a></li>
<li><a href="#">⚙️ Services</a></li>
<li><a href="#">📞 Contact</a></li>
</ul>
<div class="code-block">
/* Desktop: hover effect */
@media (hover: hover) {
.nav-menu a:hover {
background: #28a745;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
}
}
/* Touch: active effect */
@media (hover: none) {
.nav-menu a:active {
background: #28a745;
transform: scale(0.97);
}
}
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>hover Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Typical Devices</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Primary input cannot hover</td>
<td>Touch screens, phones, tablets</td>
</tr>
<tr>
<td><code>hover</code></td>
<td>Primary input can hover</td>
<td>Desktop with mouse, laptop with trackpad</td>
</tr>
</table>
<h3>hover vs any-hover</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
</tr>
<tr>
<td><code>hover</code></td>
<td>Primary pointing device's hover capability</td>
</tr>
<tr>
<td><code>any-hover</code></td>
<td>Any pointing device's hover capability</td>
</tr>
</table>
<h3>inverted-colors Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Colors are displayed normally (default)</td>
</tr>
<tr>
<td><code>inverted</code></td>
<td>Display uses an inverted color scheme</td>
</tr>
</table>
<h3>Detection Scenarios</h3>
<table class="reference-table">
<tr>
<th>Device</th>
<th>hover</th>
<th>any-hover</th>
</tr>
<tr>
<td>Desktop with mouse</td>
<td><code>hover</code></td>
<td><code>hover</code></td>
</tr>
<tr>
<td>Mobile phone (touch)</td>
<td><code>none</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td>Tablet with stylus</td>
<td><code>none</code></td>
<td><code>hover</code></td>
</tr>
<tr>
<td>Laptop with touchscreen</td>
<td><code>hover</code></td>
<td><code>hover</code></td>
</tr>
<tr>
<td>Tablet with Bluetooth mouse</td>
<td><code>none</code></td>
<td><code>hover</code></td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>@media (hover: hover)</code> to apply hover effects only when they work</li>
<li>Provide <code>:active</code> alternatives for touch devices</li>
<li>Use <code>@media (inverted-colors: inverted)</code> to ensure readability in inverted mode</li>
<li>Re-invert images with <code>filter: invert(1)</code> in inverted mode</li>
<li>Test with actual touch devices and screen readers</li>
<li>Combine <code>hover</code> with <code>any-hover</code> for comprehensive detection</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely solely on hover for critical functionality</li>
<li>Don't forget that touch devices trigger <code>:hover</code> accidentally</li>
<li>Don't ignore inverted colors — it affects users with visual impairments</li>
<li>Don't assume all desktops have a mouse (touchscreen laptops exist)</li>
<li>Don't use hover effects without touch alternatives</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-text">Detecting...</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Hover detection
const hoverHover = document.getElementById('hover-hover');
const hoverNone = document.getElementById('hover-none');
const anyHoverHover = document.getElementById('any-hover-hover');
if (window.matchMedia('(hover: hover)').matches) {
hoverHover.querySelector('.value').textContent = 'Supported ✅';
hoverHover.style.background = '#d4edda';
hoverHover.style.borderColor = '#28a745';
hoverHover.querySelector('.value').style.color = '#28a745';
hoverNone.querySelector('.value').textContent = 'Not applicable';
hoverNone.style.background = '#e9ecef';
hoverNone.style.borderColor = '#6c757d';
hoverNone.querySelector('.value').style.color = '#6c757d';
} else {
hoverNone.querySelector('.value').textContent = 'Active ✅';
hoverNone.style.background = '#fff3cd';
hoverNone.style.borderColor = '#ffc107';
hoverNone.querySelector('.value').style.color = '#856404';
hoverHover.querySelector('.value').textContent = 'Not supported';
hoverHover.style.background = '#e9ecef';
hoverHover.style.borderColor = '#6c757d';
hoverHover.querySelector('.value').style.color = '#6c757d';
}
if (window.matchMedia('(any-hover: hover)').matches) {
anyHoverHover.querySelector('.value').textContent = 'Supported ✅';
anyHoverHover.style.background = '#d4edda';
anyHoverHover.style.borderColor = '#28a745';
anyHoverHover.querySelector('.value').style.color = '#28a745';
} else {
anyHoverHover.querySelector('.value').textContent = 'Not supported';
anyHoverHover.style.background = '#e9ecef';
anyHoverHover.style.borderColor = '#6c757d';
anyHoverHover.querySelector('.value').style.color = '#6c757d';
}
// Inverted colors detection
const invertNone = document.getElementById('invert-none');
const invertInverted = document.getElementById('invert-inverted');
if (window.matchMedia('(inverted-colors: inverted)').matches) {
invertInverted.querySelector('.value').textContent = 'Active ✅';
invertInverted.style.background = '#fff3cd';
invertInverted.style.borderColor = '#ffc107';
invertInverted.querySelector('.value').style.color = '#856404';
invertNone.querySelector('.value').textContent = 'Not active';
invertNone.style.background = '#e9ecef';
invertNone.style.borderColor = '#6c757d';
invertNone.querySelector('.value').style.color = '#6c757d';
} else {
invertNone.querySelector('.value').textContent = 'Active (default) ✅';
invertNone.style.background = '#d4edda';
invertNone.style.borderColor = '#28a745';
invertNone.querySelector('.value').style.color = '#28a745';
invertInverted.querySelector('.value').textContent = 'Not active';
invertInverted.style.background = '#e9ecef';
invertInverted.style.borderColor = '#6c757d';
invertInverted.querySelector('.value').style.color = '#6c757d';
}
// Live indicator
const indicator = document.getElementById('indicator-text');
const hasHover = window.matchMedia('(hover: hover)').matches;
const isInverted = window.matchMedia('(inverted-colors: inverted)').matches;
let text = '';
if (hasHover) text += '🖱️ Hover: Yes';
else text += '👆 Hover: No (Touch)';
if (isInverted) text += ' | 🔄 Inverted: Yes';
else text += ' | 🎨 Inverted: No';
indicator.textContent = text;
// Update live indicator color
const liveIndicator = document.getElementById('live-indicator');
if (isInverted) {
liveIndicator.style.background = '#ffc107';
liveIndicator.style.color = '#333';
} else if (hasHover) {
liveIndicator.style.background = '#007bff';
liveIndicator.style.color = 'white';
} else {
liveIndicator.style.background = '#28a745';
liveIndicator.style.color = 'white';
}
}
window.addEventListener('load', updateIndicators);
// Listen for changes
window.matchMedia('(hover: hover)').addEventListener('change', updateIndicators);
window.matchMedia('(inverted-colors: inverted)').addEventListener('change', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
hover | Primary device can hover | none, hover |
inverted-colors | System uses inverted colors | none, inverted |
hover Values
| Value | Description | Typical Devices |
|---|---|---|
none | Cannot hover | Touch screens, phones, tablets |
hover | Can hover | Desktop with mouse, laptop with trackpad |
hover vs any-hover
| Feature | Description |
|---|---|
hover | Primary pointing device’s hover capability |
any-hover | Any pointing device’s hover capability |
inverted-colors Values
| Value | Description |
|---|---|
none | Colors are displayed normally (default) |
inverted | Display uses an inverted color scheme |
Best Practices
✅ Do This:
/* Only apply hover effects when hover is possible */
@media (hover: hover) {
button:hover {
background-color: #dc3545;
transform: translateY(-2px);
}
}
/* Provide touch-friendly alternatives */
@media (hover: none) {
button:active {
background-color: #dc3545;
transform: scale(0.98);
}
}
/* Handle inverted colors mode */
@media (inverted-colors: inverted) {
body {
background: #000;
color: #fff;
}
/* Re-invert images */
img, video {
filter: invert(1);
}
}
❌ Don’t Do This:
/* Don't apply hover effects unconditionally */
button:hover {
background: red;
/* On touch devices, this triggers on tap and sticks! */
}
/* Don't forget touch alternatives */
.menu-item:hover .submenu {
display: block;
/* Won't work on touch devices! */
}
/* Don't ignore inverted colors */
body {
background: white;
color: black;
/* May be unreadable if user has inverted colors on */
}
Pro Tip: hover is essential for input-aware design. Always wrap hover effects in @media (hover: hover) — otherwise, touch devices will trigger hover styles on tap, and they’ll “stick” until you tap elsewhere. Provide :active alternatives for touch devices. inverted-colors is crucial for accessibility — users with visual impairments may enable inverted colors. Re-invert images with filter: invert(1) so they look normal, and ensure your text remains readable in inverted mode!
38. monochrome and orientation Media Features
These two media features let you adapt styles for monochrome displays and screen orientation — one for specialized devices, the other for everyday responsive design.
Overview of Features
| Feature | Description | Values |
|---|---|---|
monochrome | Bits per pixel for monochrome devices | Integer (0, 1, 8) |
orientation | Portrait or landscape orientation | portrait, landscape |
1. monochrome
The monochrome media feature tests the number of bits per pixel (bpp) on monochrome (non-color) devices.
@media (monochrome) {
body {
background-color: #f0f0f0;
color: #000;
}
}
Variations
| Feature | Description | Example |
|---|---|---|
monochrome | Any monochrome device | @media (monochrome) |
monochrome: 0 | Non-monochrome device | @media (monochrome: 0) |
min-monochrome: 1 | At least 1 bpp | @media (min-monochrome: 1) |
max-monochrome: 8 | At most 8 bpp | @media (max-monochrome: 8) |
Common Values
| Bits per Pixel | Description | Device Example |
|---|---|---|
0 | Not monochrome | Modern color displays |
1 | 2 colors (black/white) | Simple e-ink readers |
2 | 4 shades | Early e-ink |
4 | 16 shades | Grayscale displays |
8 | 256 shades | Advanced e-ink, medical displays |
Key Points:
monochrome: 0means the device is not monochrome (has color)monochromewithout a value matches any monochrome device (bpp > 0)min-monochrome: 1is equivalent tomonochrome(any monochrome device)- Most modern devices match
monochrome: 0 - Useful for e-ink readers, braille displays, and specialized hardware
2. orientation
The orientation media feature applies styles based on whether the viewport is in portrait or landscape orientation.
@media screen and (orientation: portrait) {
body {
background-color: #f0f0f0;
}
}
@media screen and (orientation: landscape) {
body {
background-color: #ccc;
}
}
Values
| Value | Description | Condition |
|---|---|---|
portrait | Height ≥ Width | Vertical orientation |
landscape | Width > Height | Horizontal orientation |
Visual Representation
Portrait: Landscape:
┌─────────┐ ┌───────────────────┐
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└─────────┘ └───────────────────┘
Height ≥ Width Width > Height
Key Points:
orientationis about the viewport shape, not the physical device- It changes when the user rotates their device
- It changes when the user resizes their browser window
- A square viewport is considered
portrait(height ≥ width) - Common use: adjusting layouts for mobile landscape vs. portrait
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>monochrome and orientation Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: all 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== MONOCHROME DEMO ====== */
.monochrome-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
margin: 15px 0;
transition: all 0.3s;
background: #e9ecef;
border: 2px solid #ddd;
}
/* Any monochrome device */
@media (monochrome) {
.monochrome-demo {
background: #000;
color: #fff;
border-color: #fff;
}
.monochrome-demo::after {
content: " (monochrome device detected)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Non-monochrome device */
@media (monochrome: 0) {
.monochrome-demo {
background: #e9ecef;
color: #333;
border-color: #ddd;
}
.monochrome-demo::after {
content: " (color device — monochrome: 0)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== ORIENTATION DEMO ====== */
.orientation-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
margin: 15px 0;
transition: all 0.3s;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 150px;
}
/* Portrait orientation */
@media screen and (orientation: portrait) {
.orientation-demo {
background: #ff6b6b;
color: white;
border: 2px solid #c92a2a;
}
.orientation-demo .icon {
font-size: 3rem;
display: block;
}
.orientation-demo .icon::before {
content: "📱";
}
.orientation-demo .label::after {
content: " Portrait";
font-weight: bold;
}
}
/* Landscape orientation */
@media screen and (orientation: landscape) {
.orientation-demo {
background: #28a745;
color: white;
border: 2px solid #1e7e34;
}
.orientation-demo .icon {
font-size: 3rem;
display: block;
}
.orientation-demo .icon::before {
content: "🖥️";
}
.orientation-demo .label::after {
content: " Landscape";
font-weight: bold;
}
}
/* ====== RESPONSIVE LAYOUT DEMO ====== */
.layout-demo {
display: flex;
gap: 15px;
margin: 15px 0;
transition: all 0.3s;
flex-wrap: wrap;
}
.layout-demo .box {
background: #007bff;
color: white;
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
flex: 1;
min-width: 100px;
transition: all 0.3s;
}
/* Portrait: stack vertically */
@media screen and (orientation: portrait) {
.layout-demo {
flex-direction: column;
}
.layout-demo .box {
background: #ff6b6b;
}
}
/* Landscape: horizontal row */
@media screen and (orientation: landscape) {
.layout-demo {
flex-direction: row;
}
.layout-demo .box {
background: #28a745;
}
}
/* ====== MOBILE LANDSCAPE WARNING ====== */
.landscape-warning {
display: none;
padding: 15px;
background: #fff3cd;
border: 2px solid #ffc107;
border-radius: 8px;
text-align: center;
margin: 15px 0;
font-weight: bold;
color: #856404;
}
@media screen and (orientation: landscape) and (max-height: 500px) {
.landscape-warning {
display: block;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== PRACTICAL: RESPONSIVE CARD ====== */
.profile-card {
display: flex;
gap: 20px;
padding: 20px;
background: white;
border: 2px solid #ddd;
border-radius: 12px;
margin: 15px 0;
transition: all 0.3s;
align-items: center;
}
.profile-card .avatar {
width: 80px;
height: 80px;
background: #007bff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2rem;
font-weight: bold;
flex-shrink: 0;
}
.profile-card .info {
flex: 1;
}
.profile-card .info h4 {
margin: 0 0 5px 0;
color: #007bff;
}
.profile-card .info p {
margin: 0;
color: #6c757d;
font-size: 0.9rem;
}
/* Portrait: stack vertically */
@media screen and (orientation: portrait) {
.profile-card {
flex-direction: column;
text-align: center;
}
}
/* Landscape: horizontal row */
@media screen and (orientation: landscape) {
.profile-card {
flex-direction: row;
text-align: left;
}
}
/* ====== MONOCHROME STYLES ====== */
@media (monochrome) {
body {
background: #fff;
color: #000;
}
section {
background: #fff;
border: 1px solid #000;
box-shadow: none;
}
h1, h2, h3 {
color: #000;
}
h1 {
border-bottom-color: #000;
}
h2 {
border-left-color: #000;
}
.code-block {
background: #f0f0f0;
color: #000;
border: 1px solid #ccc;
}
.reference-table th {
background: #000;
color: #fff;
}
.reference-table td {
border-color: #999;
}
.detection-card {
border-color: #000;
background: #fff;
}
.profile-card {
border-color: #000;
}
.profile-card .avatar {
background: #000;
}
.highlight {
background: #000;
color: #fff;
}
}
</style>
</head>
<body>
<h1>monochrome and orientation Media Features</h1>
<!-- ====== 1. MONOCHROME DEMO ====== -->
<section>
<h2>1. monochrome</h2>
<p>Tests the <strong>number of bits per pixel</strong> on monochrome devices.</p>
<div class="monochrome-demo">
Monochrome Detection
</div>
<div class="code-block">
/* Any monochrome device */
@media (monochrome) {
.demo {
background: #000;
color: #fff;
}
}
/* Non-monochrome device */
@media (monochrome: 0) {
.demo {
background: #e9ecef;
color: #333;
}
}
/* At least 1 bit per pixel */
@media (min-monochrome: 1) {
/* Monochrome device with ≥ 1 bpp */
}
/* At most 8 bits per pixel */
@media (max-monochrome: 8) {
/* Monochrome device with ≤ 8 bpp */
}
</div>
</section>
<!-- ====== 2. ORIENTATION DEMO ====== -->
<section>
<h2>2. orientation</h2>
<p>Applies styles based on <strong>portrait</strong> or <strong>landscape</strong> orientation. Rotate your device or resize your browser!</p>
<div class="orientation-demo">
<span class="icon"></span>
<span class="label">Current Orientation:</span>
</div>
<h3>Responsive Layout</h3>
<div class="layout-demo">
<div class="box">📦 Box 1</div>
<div class="box">📦 Box 2</div>
<div class="box">📦 Box 3</div>
</div>
<div class="landscape-warning">
⚠️ You're in mobile landscape mode — content may be cramped. Rotate to portrait for a better experience.
</div>
<div class="code-block">
/* Portrait orientation (height ≥ width) */
@media screen and (orientation: portrait) {
.layout-demo {
flex-direction: column;
}
}
/* Landscape orientation (width > height) */
@media screen and (orientation: landscape) {
.layout-demo {
flex-direction: row;
}
}
/* Mobile landscape warning */
@media screen and (orientation: landscape) and (max-height: 500px) {
.landscape-warning {
display: block;
}
}
</div>
</section>
<!-- ====== 3. PRACTICAL EXAMPLE ====== -->
<section>
<h2>3. Practical Example: Responsive Profile Card</h2>
<p>This card adapts its layout based on orientation.</p>
<div class="profile-card">
<div class="avatar">👤</div>
<div class="info">
<h4>John Doe</h4>
<p>Full-Stack Developer</p>
<p>Passionate about creating responsive, accessible web experiences.</p>
</div>
</div>
<div class="code-block">
/* Portrait: stack vertically */
@media screen and (orientation: portrait) {
.profile-card {
flex-direction: column;
text-align: center;
}
}
/* Landscape: horizontal row */
@media screen and (orientation: landscape) {
.profile-card {
flex-direction: row;
text-align: left;
}
}
</div>
</section>
<!-- ====== 4. DETECTION SUMMARY ====== -->
<section>
<h2>4. Your Device's Capabilities</h2>
<p>Below is a live detection of your device's capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="mono-any">
<span class="label">monochrome</span>
<span class="value" id="mono-any-value">Detecting...</span>
</div>
<div class="detection-card" id="mono-0">
<span class="label">monochrome: 0</span>
<span class="value" id="mono-0-value">Detecting...</span>
</div>
<div class="detection-card" id="mono-min-1">
<span class="label">min-monochrome: 1</span>
<span class="value" id="mono-min-1-value">Detecting...</span>
</div>
<div class="detection-card" id="orient-portrait">
<span class="label">orientation: portrait</span>
<span class="value" id="orient-portrait-value">Detecting...</span>
</div>
<div class="detection-card" id="orient-landscape">
<span class="label">orientation: landscape</span>
<span class="value" id="orient-landscape-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect monochrome */
@media (monochrome) { /* Any monochrome device */ }
@media (monochrome: 0) { /* Non-monochrome device */ }
@media (min-monochrome: 1) { /* At least 1 bpp */ }
/* Detect orientation */
@media (orientation: portrait) { /* Height ≥ Width */ }
@media (orientation: landscape) { /* Width > Height */ }
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>monochrome Values</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>monochrome</code></td>
<td>Any monochrome device (bpp > 0)</td>
<td><code>@media (monochrome)</code></td>
</tr>
<tr>
<td><code>monochrome: 0</code></td>
<td>Non-monochrome device</td>
<td><code>@media (monochrome: 0)</code></td>
</tr>
<tr>
<td><code>min-monochrome: 1</code></td>
<td>At least 1 bit per pixel</td>
<td><code>@media (min-monochrome: 1)</code></td>
</tr>
<tr>
<td><code>max-monochrome: 8</code></td>
<td>At most 8 bits per pixel</td>
<td><code>@media (max-monochrome: 8)</code></td>
</tr>
</table>
<h3>Bits per Pixel Values</h3>
<table class="reference-table">
<tr>
<th>Bits per Pixel</th>
<th>Description</th>
<th>Device Example</th>
</tr>
<tr>
<td><code>0</code></td>
<td>Not monochrome</td>
<td>Modern color displays</td>
</tr>
<tr>
<td><code>1</code></td>
<td>2 colors (black/white)</td>
<td>Simple e-ink readers</td>
</tr>
<tr>
<td><code>2</code></td>
<td>4 shades</td>
<td>Early e-ink</td>
</tr>
<tr>
<td><code>4</code></td>
<td>16 shades</td>
<td>Grayscale displays</td>
</tr>
<tr>
<td><code>8</code></td>
<td>256 shades</td>
<td>Advanced e-ink, medical displays</td>
</tr>
</table>
<h3>orientation Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Condition</th>
<th>Visual</th>
</tr>
<tr>
<td><code>portrait</code></td>
<td>Height ≥ Width</td>
<td>Vertical rectangle</td>
</tr>
<tr>
<td><code>landscape</code></td>
<td>Width > Height</td>
<td>Horizontal rectangle</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>orientation</code> to adapt layouts for portrait vs. landscape</li>
<li>Use <code>monochrome</code> to provide grayscale fallbacks for specialized devices</li>
<li>Combine <code>orientation</code> with <code>max-height</code> for mobile landscape warnings</li>
<li>Test on real devices in both orientations</li>
<li>Use <code>monochrome: 0</code> for modern color devices</li>
<li>Provide meaningful content even on monochrome devices</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely on orientation alone for responsive design</li>
<li>Don't assume all users have color displays</li>
<li>Don't forget about mobile landscape (short height)</li>
<li>Don't use monochrome for critical functionality — use it for enhancements</li>
<li>Don't confuse viewport orientation with device orientation</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-orientation">Orientation: —</span>
<span class="detail" id="indicator-monochrome">Monochrome: —</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Monochrome detection
const monoAny = document.getElementById('mono-any');
const mono0 = document.getElementById('mono-0');
const monoMin1 = document.getElementById('mono-min-1');
const isMonochrome = window.matchMedia('(monochrome)').matches;
const isMonochrome0 = window.matchMedia('(monochrome: 0)').matches;
const isMinMono1 = window.matchMedia('(min-monochrome: 1)').matches;
if (isMonochrome) {
monoAny.querySelector('.value').textContent = 'Monochrome ✅';
monoAny.style.background = '#e9ecef';
monoAny.style.borderColor = '#333';
monoAny.querySelector('.value').style.color = '#333';
} else {
monoAny.querySelector('.value').textContent = 'Not monochrome';
monoAny.style.background = '#e9ecef';
monoAny.style.borderColor = '#6c757d';
monoAny.querySelector('.value').style.color = '#6c757d';
}
if (isMonochrome0) {
mono0.querySelector('.value').textContent = 'Color device ✅';
mono0.style.background = '#d4edda';
mono0.style.borderColor = '#28a745';
mono0.querySelector('.value').style.color = '#28a745';
} else {
mono0.querySelector('.value').textContent = 'Monochrome';
mono0.style.background = '#e9ecef';
mono0.style.borderColor = '#6c757d';
mono0.querySelector('.value').style.color = '#6c757d';
}
if (isMinMono1) {
monoMin1.querySelector('.value').textContent = '≥ 1 bpp ✅';
monoMin1.style.background = '#e9ecef';
monoMin1.style.borderColor = '#333';
monoMin1.querySelector('.value').style.color = '#333';
} else {
monoMin1.querySelector('.value').textContent = 'Not detected';
monoMin1.style.background = '#e9ecef';
monoMin1.style.borderColor = '#6c757d';
monoMin1.querySelector('.value').style.color = '#6c757d';
}
// Orientation detection
const orientPortrait = document.getElementById('orient-portrait');
const orientLandscape = document.getElementById('orient-landscape');
const indicator = document.getElementById('live-indicator');
const indicatorOrientation = document.getElementById('indicator-orientation');
const indicatorMonochrome = document.getElementById('indicator-monochrome');
const isPortrait = window.matchMedia('(orientation: portrait)').matches;
const isLandscape = window.matchMedia('(orientation: landscape)').matches;
if (isPortrait) {
orientPortrait.querySelector('.value').textContent = 'Active ✅';
orientPortrait.style.background = '#ff6b6b';
orientPortrait.style.borderColor = '#c92a2a';
orientPortrait.querySelector('.value').style.color = 'white';
orientLandscape.querySelector('.value').textContent = 'Inactive';
orientLandscape.style.background = '#e9ecef';
orientLandscape.style.borderColor = '#6c757d';
orientLandscape.querySelector('.value').style.color = '#6c757d';
indicatorOrientation.textContent = '📱 Portrait';
indicator.style.background = '#ff6b6b';
indicator.style.color = 'white';
} else if (isLandscape) {
orientLandscape.querySelector('.value').textContent = 'Active ✅';
orientLandscape.style.background = '#28a745';
orientLandscape.style.borderColor = '#1e7e34';
orientLandscape.querySelector('.value').style.color = 'white';
orientPortrait.querySelector('.value').textContent = 'Inactive';
orientPortrait.style.background = '#e9ecef';
orientPortrait.style.borderColor = '#6c757d';
orientPortrait.querySelector('.value').style.color = '#6c757d';
indicatorOrientation.textContent = '🖥️ Landscape';
indicator.style.background = '#28a745';
indicator.style.color = 'white';
}
// Update monochrome indicator
if (isMonochrome) {
indicatorMonochrome.textContent = 'Monochrome: Yes';
} else {
indicatorMonochrome.textContent = 'Monochrome: No (Color)';
}
}
window.addEventListener('resize', updateIndicators);
window.addEventListener('load', updateIndicators);
window.addEventListener('orientationchange', updateIndicators);
// Listen for changes
window.matchMedia('(orientation: portrait)').addEventListener('change', updateIndicators);
window.matchMedia('(monochrome)').addEventListener('change', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
monochrome | Bits per pixel on monochrome devices | Integer (0, 1, 8) |
orientation | Portrait or landscape | portrait, landscape |
monochrome Values
| Feature | Description | Example |
|---|---|---|
monochrome | Any monochrome device | @media (monochrome) |
monochrome: 0 | Non-monochrome device | @media (monochrome: 0) |
min-monochrome: 1 | At least 1 bpp | @media (min-monochrome: 1) |
max-monochrome: 8 | At most 8 bpp | @media (max-monochrome: 8) |
Bits per Pixel
| Bits per Pixel | Description | Device Example |
|---|---|---|
0 | Not monochrome | Modern color displays |
1 | 2 colors | Simple e-ink readers |
2 | 4 shades | Early e-ink |
4 | 16 shades | Grayscale displays |
8 | 256 shades | Advanced e-ink, medical displays |
orientation Values
| Value | Condition | Visual |
|---|---|---|
portrait | Height ≥ Width | Vertical rectangle |
landscape | Width > Height | Horizontal rectangle |
Best Practices
✅ Do This:
/* Adapt layout for orientation */
@media screen and (orientation: portrait) {
.container {
flex-direction: column;
}
}
@media screen and (orientation: landscape) {
.container {
flex-direction: row;
}
}
/* Mobile landscape warning */
@media screen and (orientation: landscape) and (max-height: 500px) {
.warning {
display: block;
}
}
/* Monochrome fallback */
@media (monochrome) {
.image {
filter: grayscale(100%);
}
}
❌ Don’t Do This:
/* Don't rely on orientation alone */
@media (orientation: portrait) {
/* Use width/height for more precise control */
}
/* Don't assume all users have color displays */
.chart {
color: #ff0000; /* May not be visible on monochrome */
}
/* Don't forget mobile landscape */
/* A phone in landscape has a very short viewport height */
Pro Tip: orientation is essential for responsive design — mobile users frequently rotate their devices, and layouts should adapt accordingly. Use it to switch between vertical and horizontal layouts, and combine it with max-height to detect mobile landscape mode (where the viewport is very short). monochrome is for specialized devices — e-ink readers, braille displays, and medical monitors. Most modern devices match monochrome: 0 (color). Always provide meaningful content regardless of color capability!
39. overflow-block and overflow-inline Media Features
These two media features describe how a device handles content overflow — whether it scrolls, paginates, or simply clips. They’re especially relevant for paged media (like print) and e-readers.
Overview of Features
| Feature | Description | Axis |
|---|---|---|
overflow-block | Overflow handling along the block axis | Vertical (in horizontal writing modes) |
overflow-inline | Overflow handling along the inline axis | Horizontal (in horizontal writing modes) |
Understanding Block and Inline Axes
Before diving in, it’s important to understand the two axes:
| Axis | Description | Default Direction |
|---|---|---|
| Block axis | The direction in which block-level elements are laid out | Vertical (top → bottom) |
| Inline axis | The direction in which inline content flows | Horizontal (left → right) |
Note: These axes swap in vertical writing modes (e.g., writing-mode: vertical-rl).
Block Axis (vertical)
↑
│ Inline Axis (horizontal)
│ ────────────────────────→
│
1. overflow-block
The overflow-block media feature checks how the device handles overflow along the block axis.
@media (overflow-block: scroll) {
div {
color: blue;
}
}
Values
| Value | Description |
|---|---|
none | Content that overflows is not displayed (clipped) |
scroll | Content that overflows can be scrolled |
optional-paged | Scrolling is available, but pages can also be triggered |
paged | Content is broken into discrete pages |
Real-World Examples
| Device / Medium | overflow-block Value |
|---|---|
| Computer screen | scroll |
| Smartphone | scroll |
| Printed page | paged |
| E-reader | paged or optional-paged |
| Text-only terminal | scroll or none |
| Projection | none |
2. overflow-inline
The overflow-inline media feature checks how the device handles overflow along the inline axis.
@media (overflow-inline: scroll) {
div {
color: blue;
}
}
Values
| Value | Description |
|---|---|
none | Overflowing content is not displayed (clipped) |
scroll | Overflowing content can be scrolled to |
Real-World Examples
| Device / Medium | overflow-inline Value |
|---|---|
| Computer screen | scroll |
| Smartphone | scroll (horizontal swipe) |
| Printed page | none (content is clipped or wraps) |
| Text-only terminal | scroll or none |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>overflow-block and overflow-inline Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: all 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== OVERFLOW-BLOCK DEMO ====== */
.overflow-block-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
margin: 15px 0;
transition: all 0.3s;
background: #e9ecef;
border: 2px solid #ddd;
}
/* None — clipped */
@media (overflow-block: none) {
.overflow-block-demo {
background: #dc3545;
color: white;
border-color: #a71d2a;
}
.overflow-block-demo::after {
content: " (none — content clipped)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Scroll — scrollable */
@media (overflow-block: scroll) {
.overflow-block-demo {
background: #007bff;
color: white;
border-color: #0056b3;
}
.overflow-block-demo::after {
content: " (scroll — content scrollable)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Optional-paged */
@media (overflow-block: optional-paged) {
.overflow-block-demo {
background: #ffc107;
color: #333;
border-color: #d39e00;
}
.overflow-block-demo::after {
content: " (optional-paged — can paginate)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Paged */
@media (overflow-block: paged) {
.overflow-block-demo {
background: #28a745;
color: white;
border-color: #1e7e34;
}
.overflow-block-demo::after {
content: " (paged — discrete pages)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== OVERFLOW-INLINE DEMO ====== */
.overflow-inline-demo {
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
margin: 15px 0;
transition: all 0.3s;
background: #e9ecef;
border: 2px solid #ddd;
}
@media (overflow-inline: none) {
.overflow-inline-demo {
background: #dc3545;
color: white;
border-color: #a71d2a;
}
.overflow-inline-demo::after {
content: " (none — inline overflow clipped)";
font-weight: normal;
font-size: 0.85rem;
}
}
@media (overflow-inline: scroll) {
.overflow-inline-demo {
background: #007bff;
color: white;
border-color: #0056b3;
}
.overflow-inline-demo::after {
content: " (scroll — inline overflow scrollable)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== AXIS VISUALIZATION ====== */
.axis-viz {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
margin: 20px 0;
}
.axis-row {
display: flex;
align-items: center;
gap: 10px;
}
.axis-label {
font-weight: bold;
color: #007bff;
min-width: 120px;
text-align: right;
}
.axis-arrow {
font-size: 1.5rem;
color: #dc3545;
}
.axis-description {
color: #6c757d;
font-size: 0.9rem;
}
/* ====== PRACTICAL: PAGED MEDIA ====== */
.article {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: white;
border: 2px solid #ddd;
border-radius: 8px;
}
.article h3 {
color: #007bff;
margin-top: 0;
}
.article p {
text-align: justify;
line-height: 1.8;
}
/* Print / paged media styles */
@media (overflow-block: paged) {
.article {
border: none;
padding: 0;
background: transparent;
}
.article h3 {
color: #000;
page-break-after: avoid;
}
.article p {
orphans: 3;
widows: 3;
}
.no-print {
display: none;
}
}
/* Scrollable media styles */
@media (overflow-block: scroll) {
.article {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
}
/* ====== CONTENT OVERFLOW DEMO ====== */
.overflow-container {
width: 100%;
max-width: 600px;
margin: 15px auto;
border: 2px dashed #007bff;
border-radius: 8px;
overflow: auto;
padding: 15px;
background: #f8f9fa;
}
.overflow-container .long-content {
white-space: nowrap;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
color: #333;
}
/* Inline overflow behavior */
@media (overflow-inline: scroll) {
.overflow-container {
overflow-x: auto;
}
}
@media (overflow-inline: none) {
.overflow-container {
overflow-x: hidden;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== PRINT STYLES ====== */
@media print {
body {
background: white;
color: black;
padding: 0;
}
section {
box-shadow: none;
border: 1px solid #ccc;
page-break-inside: avoid;
}
.code-block {
background: #f4f4f4;
color: #333;
border: 1px solid #ddd;
}
.no-print {
display: none;
}
.live-indicator {
display: none;
}
}
</style>
</head>
<body>
<h1>overflow-block and overflow-inline Media Features</h1>
<!-- ====== 1. OVERFLOW-BLOCK DEMO ====== -->
<section>
<h2>1. overflow-block</h2>
<p>Checks how the device handles overflow along the <strong>block axis</strong> (vertical in horizontal writing modes).</p>
<div class="overflow-block-demo">
Block Axis Overflow
</div>
<div class="axis-viz">
<div class="axis-row">
<span class="axis-label">Block Axis:</span>
<span class="axis-arrow">↕</span>
<span class="axis-description">Vertical (top → bottom)</span>
</div>
</div>
<div class="code-block">
/* Content is clipped */
@media (overflow-block: none) {
div { color: red; }
}
/* Content can be scrolled */
@media (overflow-block: scroll) {
div { color: blue; }
}
/* Can paginate or scroll */
@media (overflow-block: optional-paged) {
div { color: orange; }
}
/* Content is broken into pages */
@media (overflow-block: paged) {
div { color: green; }
}
</div>
<p class="note"><strong>Try it:</strong> Print this page to see <code>overflow-block: paged</code> in action!</p>
</section>
<!-- ====== 2. OVERFLOW-INLINE DEMO ====== -->
<section>
<h2>2. overflow-inline</h2>
<p>Checks how the device handles overflow along the <strong>inline axis</strong> (horizontal in horizontal writing modes).</p>
<div class="overflow-inline-demo">
Inline Axis Overflow
</div>
<div class="axis-viz">
<div class="axis-row">
<span class="axis-label">Inline Axis:</span>
<span class="axis-arrow">↔</span>
<span class="axis-description">Horizontal (left → right)</span>
</div>
</div>
<div class="code-block">
/* Inline overflow is clipped */
@media (overflow-inline: none) {
div { color: blue; }
}
/* Inline overflow can be scrolled */
@media (overflow-inline: scroll) {
div { color: blue; }
}
</div>
</section>
<!-- ====== 3. CONTENT OVERFLOW DEMO ====== -->
<section>
<h2>3. Content Overflow Demo</h2>
<p>This container demonstrates inline overflow behavior.</p>
<div class="overflow-container">
<div class="long-content">
This is a very long line of text that will overflow the container if it's too wide. On devices with <code>overflow-inline: scroll</code>, you can scroll horizontally to see the rest. On devices with <code>overflow-inline: none</code>, the text is clipped. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</div>
<div class="code-block">
.overflow-container {
overflow: auto; /* Handles both axes */
}
/* Inline overflow behavior */
@media (overflow-inline: scroll) {
.overflow-container {
overflow-x: auto; /* Horizontal scroll */
}
}
@media (overflow-inline: none) {
.overflow-container {
overflow-x: hidden; /* Clip */
}
}
</div>
</section>
<!-- ====== 4. PRACTICAL: PAGED MEDIA ====== -->
<section class="no-print">
<h2>4. Practical Example: Paged Media Styles</h2>
<p>This article uses <code>overflow-block</code> to adapt to paged media.</p>
</section>
<div class="article">
<h3>📄 Sample Article</h3>
<p>
This article demonstrates how to adapt styles for different overflow behaviors.
On a screen (with <code>overflow-block: scroll</code>), it has a shadow and border.
When printed (with <code>overflow-block: paged</code>), it becomes clean and borderless,
with proper page-break controls.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</p>
<p class="no-print" style="color: #6c757d; font-style: italic;">
(This paragraph is hidden when printed.)
</p>
</div>
<div class="code-block no-print">
/* Paged media styles */
@media (overflow-block: paged) {
.article {
border: none;
padding: 0;
background: transparent;
}
.article h3 {
page-break-after: avoid;
}
.article p {
orphans: 3;
widows: 3;
}
.no-print {
display: none;
}
}
/* Scrollable media styles */
@media (overflow-block: scroll) {
.article {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
}
</div>
<!-- ====== 5. DETECTION SUMMARY ====== -->
<section>
<h2>5. Your Device's Overflow Handling</h2>
<p>Below is a live detection of your device's overflow capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="ob-none">
<span class="label">overflow-block: none</span>
<span class="value" id="ob-none-value">Detecting...</span>
</div>
<div class="detection-card" id="ob-scroll">
<span class="label">overflow-block: scroll</span>
<span class="value" id="ob-scroll-value">Detecting...</span>
</div>
<div class="detection-card" id="ob-paged">
<span class="label">overflow-block: paged</span>
<span class="value" id="ob-paged-value">Detecting...</span>
</div>
<div class="detection-card" id="ob-optional-paged">
<span class="label">overflow-block: optional-paged</span>
<span class="value" id="ob-optional-paged-value">Detecting...</span>
</div>
<div class="detection-card" id="oi-none">
<span class="label">overflow-inline: none</span>
<span class="value" id="oi-none-value">Detecting...</span>
</div>
<div class="detection-card" id="oi-scroll">
<span class="label">overflow-inline: scroll</span>
<span class="value" id="oi-scroll-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect overflow-block */
@media (overflow-block: none) { /* Clipped */ }
@media (overflow-block: scroll) { /* Scrollable */ }
@media (overflow-block: optional-paged) { /* Scroll or paginate */ }
@media (overflow-block: paged) { /* Paginated */ }
/* Detect overflow-inline */
@media (overflow-inline: none) { /* Clipped */ }
@media (overflow-inline: scroll) { /* Scrollable */ }
</div>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>overflow-block Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Device Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Overflowing content is clipped</td>
<td>Projection, some terminals</td>
</tr>
<tr>
<td><code>scroll</code></td>
<td>Content can be scrolled</td>
<td>Computer screens, phones</td>
</tr>
<tr>
<td><code>optional-paged</code></td>
<td>Can scroll or paginate</td>
<td>Some e-readers</td>
</tr>
<tr>
<td><code>paged</code></td>
<td>Content is broken into pages</td>
<td>Print, e-readers</td>
</tr>
</table>
<h3>overflow-inline Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Device Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Overflowing content is clipped</td>
<td>Print, some terminals</td>
</tr>
<tr>
<td><code>scroll</code></td>
<td>Content can be scrolled horizontally</td>
<td>Computer screens, phones</td>
</tr>
</table>
<h3>Block vs Inline Axis</h3>
<table class="reference-table">
<tr>
<th>Axis</th>
<th>Description</th>
<th>Default Direction</th>
</tr>
<tr>
<td><strong>Block axis</strong></td>
<td>Direction of block-level layout</td>
<td>Vertical (top → bottom)</td>
</tr>
<tr>
<td><strong>Inline axis</strong></td>
<td>Direction of inline content flow</td>
<td>Horizontal (left → right)</td>
</tr>
</table>
<h3>Device Detection Summary</h3>
<table class="reference-table">
<tr>
<th>Device</th>
<th>overflow-block</th>
<th>overflow-inline</th>
</tr>
<tr>
<td>Computer screen</td>
<td><code>scroll</code></td>
<td><code>scroll</code></td>
</tr>
<tr>
<td>Smartphone</td>
<td><code>scroll</code></td>
<td><code>scroll</code></td>
</tr>
<tr>
<td>Printed page</td>
<td><code>paged</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td>E-reader</td>
<td><code>paged</code></td>
<td><code>none</code></td>
</tr>
<tr>
<td>Text terminal</td>
<td><code>scroll</code></td>
<td><code>scroll</code></td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>overflow-block</code> to adapt styles for paged media (print, e-readers)</li>
<li>Use <code>overflow-inline</code> to handle horizontal overflow on different devices</li>
<li>Provide print-friendly styles with <code>overflow-block: paged</code></li>
<li>Use <code>orphans</code> and <code>widows</code> for paged media</li>
<li>Test your print styles with the browser's print preview</li>
<li>Hide non-essential elements (like navigation) in paged media</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely on <code>overflow-block</code> for critical functionality — it's for enhancements</li>
<li>Don't forget that axes swap in vertical writing modes</li>
<li>Don't assume all devices support the same overflow behavior</li>
<li>Don't use <code>overflow-inline: none</code> for essential content</li>
<li>Don't ignore paged media — many users print web pages</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator no-print" id="live-indicator">
<span id="indicator-block">Block: —</span>
<span class="detail" id="indicator-inline">Inline: —</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// overflow-block detection
const obNone = document.getElementById('ob-none');
const obScroll = document.getElementById('ob-scroll');
const obPaged = document.getElementById('ob-paged');
const obOptionalPaged = document.getElementById('ob-optional-paged');
const isObNone = window.matchMedia('(overflow-block: none)').matches;
const isObScroll = window.matchMedia('(overflow-block: scroll)').matches;
const isObPaged = window.matchMedia('(overflow-block: paged)').matches;
const isObOptionalPaged = window.matchMedia('(overflow-block: optional-paged)').matches;
function setCard(card, isActive, activeText, inactiveText) {
const value = card.querySelector('.value');
if (isActive) {
value.textContent = activeText;
card.style.background = '#d4edda';
card.style.borderColor = '#28a745';
value.style.color = '#28a745';
} else {
value.textContent = inactiveText;
card.style.background = '#e9ecef';
card.style.borderColor = '#6c757d';
value.style.color = '#6c757d';
}
}
setCard(obNone, isObNone, 'Active ✅', 'Not active');
setCard(obScroll, isObScroll, 'Active ✅', 'Not active');
setCard(obPaged, isObPaged, 'Active ✅', 'Not active');
setCard(obOptionalPaged, isObOptionalPaged, 'Active ✅', 'Not active');
// overflow-inline detection
const oiNone = document.getElementById('oi-none');
const oiScroll = document.getElementById('oi-scroll');
const isOiNone = window.matchMedia('(overflow-inline: none)').matches;
const isOiScroll = window.matchMedia('(overflow-inline: scroll)').matches;
setCard(oiNone, isOiNone, 'Active ✅', 'Not active');
setCard(oiScroll, isOiScroll, 'Active ✅', 'Not active');
// Live indicator
const indicatorBlock = document.getElementById('indicator-block');
const indicatorInline = document.getElementById('indicator-inline');
const indicator = document.getElementById('live-indicator');
let blockValue = 'unknown';
if (isObScroll) blockValue = 'scroll';
else if (isObPaged) blockValue = 'paged';
else if (isObOptionalPaged) blockValue = 'optional-paged';
else if (isObNone) blockValue = 'none';
let inlineValue = 'unknown';
if (isOiScroll) inlineValue = 'scroll';
else if (isOiNone) inlineValue = 'none';
indicatorBlock.textContent = '📄 Block: ' + blockValue;
indicatorInline.textContent = '↔️ Inline: ' + inlineValue;
// Color the indicator
if (blockValue === 'paged' || blockValue === 'optional-paged') {
indicator.style.background = '#28a745';
indicator.style.color = 'white';
} else if (blockValue === 'scroll') {
indicator.style.background = '#007bff';
indicator.style.color = 'white';
} else {
indicator.style.background = '#dc3545';
indicator.style.color = 'white';
}
}
window.addEventListener('load', updateIndicators);
// Listen for print changes (when print preview is opened)
window.addEventListener('beforeprint', function() {
// Print media queries become active
setTimeout(updateIndicators, 100);
});
window.addEventListener('afterprint', function() {
setTimeout(updateIndicators, 100);
});
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Axis |
|---|---|---|
overflow-block | Overflow handling on block axis | Vertical (default) |
overflow-inline | Overflow handling on inline axis | Horizontal (default) |
overflow-block Values
| Value | Description | Device Example |
|---|---|---|
none | Content is clipped | Projection, some terminals |
scroll | Content can be scrolled | Computer screens, phones |
optional-paged | Can scroll or paginate | Some e-readers |
paged | Content is broken into pages | Print, e-readers |
overflow-inline Values
| Value | Description | Device Example |
|---|---|---|
none | Content is clipped | Print, some terminals |
scroll | Content can be scrolled horizontally | Computer screens, phones |
Block vs Inline Axis
| Axis | Description | Default Direction |
|---|---|---|
| Block axis | Direction of block-level layout | Vertical (top → bottom) |
| Inline axis | Direction of inline content flow | Horizontal (left → right) |
Best Practices
✅ Do This:
/* Print-friendly styles */
@media (overflow-block: paged) {
.article {
border: none;
padding: 0;
}
.article p {
orphans: 3;
widows: 3;
}
.no-print {
display: none;
}
}
/* Screen-friendly styles */
@media (overflow-block: scroll) {
.article {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
}
/* Handle inline overflow */
@media (overflow-inline: scroll) {
.container {
overflow-x: auto;
}
}
❌ Don’t Do This:
/* Don't rely on overflow-block for critical functionality */
@media (overflow-block: paged) {
/* This is fine for enhancements, but don't hide essential content */
}
/* Don't forget that axes swap in vertical writing modes */
@media (overflow-block: scroll) {
/* In vertical-rl, this now refers to horizontal scrolling */
}
/* Don't ignore print styles */
/* Many users still print web pages! */
Pro Tip: overflow-block and overflow-inline are most useful for paged media (print, e-readers). Use overflow-block: paged to provide clean, print-friendly styles — remove shadows, borders, and non-essential elements, and use orphans/widows to control page breaks. Remember that the block and inline axes swap in vertical writing modes (like writing-mode: vertical-rl), so these features adapt automatically to the writing direction!
40. pointer and resolution Media Features
These two media features let you adapt styles based on the accuracy of the pointing device and the pixel density of the display — essential for touch-friendly interfaces and high-DPI screens.
Overview of Features
| Feature | Description | Values |
|---|---|---|
pointer | Accuracy of the primary pointing device | none, coarse, fine |
resolution | Pixel density of the output device | dpi, dpcm, dppx |
1. pointer
The pointer media feature queries the accuracy of the primary pointing device — such as a mouse, touchpad, or touchscreen.
@media (pointer: fine) {
.container {
padding: 20px;
border: 1px solid #add8e6;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
}
Values
| Value | Description | Device Example |
|---|---|---|
none | No pointing device available | Keyboard-only device, TV remote |
coarse | Pointing device with limited accuracy | Finger on touchscreen, stylus |
fine | Pointing device with fine accuracy | Mouse, trackpad, precision stylus |
Real-World Examples
| Device | pointer Value |
|---|---|
| Desktop with mouse | fine |
| Laptop with trackpad | fine |
| Smartphone (touch) | coarse |
| Tablet (touch) | coarse |
| Smart TV (remote) | none or coarse |
| Gaming console (controller) | none or coarse |
pointer vs any-pointer
| Feature | Description |
|---|---|
pointer | Primary pointing device’s accuracy |
any-pointer | Best accuracy among all pointing devices |
Example:
- Laptop with touchscreen and mouse:
pointer: fine(primary is mouse)any-pointer: fine(at least one fine pointer)
- Tablet with Bluetooth mouse:
pointer: coarse(primary is touch)any-pointer: fine(mouse is fine)
2. resolution
The resolution media feature queries the pixel density of the output device.
@media (resolution: 150dpi) {
.container {
padding: 10px;
border: 1px solid red;
}
}
Values
| Unit | Description | Example |
|---|---|---|
dpi | Dots per inch | 150dpi |
dpcm | Dots per centimeter | 60dpcm |
dppx | Dots per pixel unit (1dppx = 96dpi) | 2dppx |
Variations
| Feature | Description | Example |
|---|---|---|
resolution | Exact resolution | @media (resolution: 150dpi) |
min-resolution | Minimum resolution | @media (min-resolution: 2dppx) |
max-resolution | Maximum resolution | @media (max-resolution: 72dpi) |
Common Resolutions
| Device | Resolution | dppx Equivalent |
|---|---|---|
| Older monitors | 72–96 dpi | 0.75–1 dppx |
| Standard laptops | 96–120 dpi | 1–1.25 dppx |
| Retina displays | 192–220 dpi | 2–2.3 dppx |
| High-end smartphones | 400–500 dpi | 4–5 dppx |
| 4K monitors | 150–200 dpi | 1.5–2 dppx |
Unit Conversions
1 dppx = 96 dpi
1 dpi = 1/96 dppx
1 dpcm = 2.54 dpi
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pointer and resolution Media Features</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
transition: all 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== POINTER DEMO ====== */
.container {
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
margin: 15px 0;
transition: all 0.3s;
text-align: center;
font-weight: bold;
font-size: 1.1rem;
background: #f8f9fa;
}
/* Fine pointer (mouse) */
@media (pointer: fine) {
.container {
padding: 20px;
border: 2px solid #add8e6;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
background: #e3f2fd;
}
.container::after {
content: " 🖱️ Fine pointer (mouse)";
font-weight: normal;
font-size: 0.85rem;
color: #007bff;
}
}
/* Coarse pointer (touch) */
@media (pointer: coarse) {
.container {
padding: 10px;
border: 2px solid #98fb98;
box-shadow: none;
background: #e8f5e9;
}
.container::after {
content: " 👆 Coarse pointer (touch)";
font-weight: normal;
font-size: 0.85rem;
color: #28a745;
}
}
/* No pointer */
@media (pointer: none) {
.container {
padding: 15px;
border: 2px dashed #dc3545;
background: #f8d7da;
}
.container::after {
content: " ⌨️ No pointing device";
font-weight: normal;
font-size: 0.85rem;
color: #dc3545;
}
}
/* ====== TOUCH-FRIENDLY BUTTONS ====== */
.btn-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 15px 0;
}
.btn {
padding: 12px 24px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
}
/* Larger buttons for touch */
@media (pointer: coarse) {
.btn {
padding: 16px 32px;
font-size: 1.1em;
min-height: 48px;
min-width: 120px;
}
}
/* Smaller buttons for mouse */
@media (pointer: fine) {
.btn {
padding: 10px 20px;
font-size: 0.95em;
}
.btn:hover {
background: #0056b3;
transform: translateY(-2px);
}
}
/* ====== RESOLUTION DEMO ====== */
.resolution-demo {
padding: 20px;
border-radius: 8px;
margin: 15px 0;
text-align: center;
font-weight: bold;
font-size: 1.1rem;
transition: all 0.3s;
background: #e9ecef;
border: 2px solid #ddd;
}
/* Low resolution (≤ 72dpi) */
@media (max-resolution: 72dpi) {
.resolution-demo {
background: #ff6b6b;
color: white;
border-color: #c92a2a;
}
.resolution-demo::after {
content: " (low resolution ≤ 72dpi)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Medium resolution (72–150dpi) */
@media (min-resolution: 72dpi) and (max-resolution: 192dpi) {
.resolution-demo {
background: #ffc107;
color: #333;
border-color: #d39e00;
}
.resolution-demo::after {
content: " (medium resolution 72–192dpi)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* High resolution (≥ 2dppx / 192dpi) */
@media (min-resolution: 2dppx) {
.resolution-demo {
background: #28a745;
color: white;
border-color: #1e7e34;
}
.resolution-demo::after {
content: " (high resolution ≥ 2dppx)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== HDPI IMAGE DEMO ====== */
.hdpi-demo {
display: flex;
flex-wrap: wrap;
gap: 20px;
align-items: center;
margin: 15px 0;
}
.hdpi-demo .image-box {
width: 200px;
height: 150px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 1rem;
text-align: center;
padding: 20px;
/* Standard resolution image */
background: linear-gradient(135deg, #007bff, #6c5ce7);
transition: all 0.3s;
}
/* Sharper image for high-DPI displays */
@media (min-resolution: 2dppx) {
.hdpi-demo .image-box {
background: linear-gradient(135deg, #007bff, #6c5ce7);
box-shadow: 0 0 0 2px #28a745, 0 4px 20px rgba(40, 167, 69, 0.3);
}
.hdpi-demo .image-box::after {
content: " (HDPI enhanced)";
font-weight: normal;
font-size: 0.8rem;
}
}
/* ====== BORDER WIDTH ADJUSTMENT ====== */
.border-demo {
padding: 20px;
margin: 15px 0;
border-radius: 8px;
background: #f8f9fa;
text-align: center;
transition: all 0.3s;
/* Default border */
border: 1px solid #007bff;
}
/* Thinner borders on high-DPI (1px looks thicker) */
@media (min-resolution: 2dppx) {
.border-demo {
border-width: 0.5px;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: #666;
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: #007bff;
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: #007bff;
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px rgba(0,123,255,0.4);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== PRACTICAL: TOUCH-FRIENDLY FORM ====== */
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
font-weight: bold;
margin-bottom: 5px;
color: #333;
}
.form-group input,
.form-group select {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 1em;
transition: all 0.3s;
}
.form-group input:focus,
.form-group select:focus {
border-color: #007bff;
outline: none;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.15);
}
/* Larger touch targets for coarse pointers */
@media (pointer: coarse) {
.form-group input,
.form-group select {
padding: 16px;
font-size: 1.1em;
min-height: 48px;
}
.form-group label {
font-size: 1.05em;
}
}
</style>
</head>
<body>
<h1>pointer and resolution Media Features</h1>
<!-- ====== 1. POINTER DEMO ====== -->
<section>
<h2>1. pointer</h2>
<p>Queries the <strong>accuracy of the primary pointing device</strong>.</p>
<div class="container">
Pointer Detection
</div>
<p class="note">On desktop with a mouse: fine. On mobile with touch: coarse. On keyboard-only: none.</p>
<div class="code-block">
/* Fine pointer (mouse, trackpad) */
@media (pointer: fine) {
.container {
padding: 20px;
border: 2px solid #add8e6;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
}
/* Coarse pointer (touch) */
@media (pointer: coarse) {
.container {
padding: 10px;
border: 2px solid #98fb98;
box-shadow: none;
}
}
/* No pointer */
@media (pointer: none) {
.container {
border: 2px dashed #dc3545;
}
}
</div>
</section>
<!-- ====== 2. TOUCH-FRIENDLY BUTTONS ====== -->
<section>
<h2>2. Touch-Friendly Buttons</h2>
<p>Buttons adapt their size based on pointer accuracy.</p>
<div class="btn-group">
<button class="btn">Primary</button>
<button class="btn">Secondary</button>
<button class="btn">Tertiary</button>
</div>
<p class="note">On touch devices, buttons have larger touch targets (48px minimum).</p>
<div class="code-block">
/* Smaller buttons for mouse */
@media (pointer: fine) {
.btn {
padding: 10px 20px;
}
.btn:hover {
transform: translateY(-2px);
}
}
/* Larger buttons for touch */
@media (pointer: coarse) {
.btn {
padding: 16px 32px;
min-height: 48px;
min-width: 120px;
}
}
</div>
</section>
<!-- ====== 3. RESOLUTION DEMO ====== -->
<section>
<h2>3. resolution</h2>
<p>Queries the <strong>pixel density</strong> of the output device.</p>
<div class="resolution-demo">
Resolution Detection
</div>
<div class="code-block">
/* Low resolution */
@media (max-resolution: 72dpi) {
.demo { background: #ff6b6b; }
}
/* Medium resolution */
@media (min-resolution: 72dpi) and (max-resolution: 192dpi) {
.demo { background: #ffc107; }
}
/* High resolution */
@media (min-resolution: 2dppx) {
.demo { background: #28a745; }
}
</div>
</section>
<!-- ====== 4. HDPI IMAGE ENHANCEMENT ====== -->
<section>
<h2>4. HDPI Image Enhancement</h2>
<p>Images can be enhanced on high-resolution displays.</p>
<div class="hdpi-demo">
<div class="image-box">🖼️ HDPI Image</div>
<p>On high-DPI displays (≥ 2dppx), this image gets a sharper border and enhanced shadow.</p>
</div>
<div class="code-block">
/* Sharper image for high-DPI displays */
@media (min-resolution: 2dppx) {
.image-box {
box-shadow: 0 0 0 2px #28a745, 0 4px 20px rgba(40, 167, 69, 0.3);
}
}
</div>
</section>
<!-- ====== 5. BORDER WIDTH ADJUSTMENT ====== -->
<section>
<h2>5. Border Width Adjustment</h2>
<p>Thin borders look thicker on high-DPI displays. This border adjusts accordingly.</p>
<div class="border-demo">
This border is 1px on standard displays and 0.5px on high-DPI displays.
</div>
<div class="code-block">
/* Thinner borders on high-DPI */
@media (min-resolution: 2dppx) {
.border-demo {
border-width: 0.5px;
}
}
</div>
</section>
<!-- ====== 6. PRACTICAL: TOUCH-FRIENDLY FORM ====== -->
<section>
<h2>6. Practical Example: Touch-Friendly Form</h2>
<p>Form inputs adapt their size based on pointer accuracy.</p>
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" placeholder="Enter your name">
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" placeholder="your@email.com">
</div>
<div class="form-group">
<label for="country">Country:</label>
<select id="country">
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
</select>
</div>
<div class="code-block">
/* Larger touch targets for coarse pointers */
@media (pointer: coarse) {
.form-group input,
.form-group select {
padding: 16px;
font-size: 1.1em;
min-height: 48px;
}
}
</div>
</section>
<!-- ====== 7. DETECTION SUMMARY ====== -->
<section>
<h2>7. Your Device's Capabilities</h2>
<p>Below is a live detection of your device's capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="pointer-fine">
<span class="label">pointer: fine</span>
<span class="value" id="pointer-fine-value">Detecting...</span>
</div>
<div class="detection-card" id="pointer-coarse">
<span class="label">pointer: coarse</span>
<span class="value" id="pointer-coarse-value">Detecting...</span>
</div>
<div class="detection-card" id="pointer-none">
<span class="label">pointer: none</span>
<span class="value" id="pointer-none-value">Detecting...</span>
</div>
<div class="detection-card" id="any-pointer-fine">
<span class="label">any-pointer: fine</span>
<span class="value" id="any-pointer-fine-value">Detecting...</span>
</div>
<div class="detection-card" id="res-low">
<span class="label">max-resolution: 72dpi</span>
<span class="value" id="res-low-value">Detecting...</span>
</div>
<div class="detection-card" id="res-high">
<span class="label">min-resolution: 2dppx</span>
<span class="value" id="res-high-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect pointer accuracy */
@media (pointer: fine) { /* Mouse, trackpad */ }
@media (pointer: coarse) { /* Touch */ }
@media (pointer: none) { /* No pointer */ }
/* Detect resolution */
@media (max-resolution: 72dpi) { /* Low resolution */ }
@media (min-resolution: 2dppx) { /* High resolution */ }
</div>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>pointer Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Device Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>No pointing device</td>
<td>Keyboard-only, TV remote</td>
</tr>
<tr>
<td><code>coarse</code></td>
<td>Limited accuracy</td>
<td>Finger on touchscreen, stylus</td>
</tr>
<tr>
<td><code>fine</code></td>
<td>Fine accuracy</td>
<td>Mouse, trackpad, precision stylus</td>
</tr>
</table>
<h3>pointer vs any-pointer</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Description</th>
</tr>
<tr>
<td><code>pointer</code></td>
<td>Primary pointing device's accuracy</td>
</tr>
<tr>
<td><code>any-pointer</code></td>
<td>Best accuracy among all pointing devices</td>
</tr>
</table>
<h3>resolution Units</h3>
<table class="reference-table">
<tr>
<th>Unit</th>
<th>Description</th>
<th>Example</th>
</tr>
<tr>
<td><code>dpi</code></td>
<td>Dots per inch</td>
<td><code>150dpi</code></td>
</tr>
<tr>
<td><code>dpcm</code></td>
<td>Dots per centimeter</td>
<td><code>60dpcm</code></td>
</tr>
<tr>
<td><code>dppx</code></td>
<td>Dots per pixel unit (1dppx = 96dpi)</td>
<td><code>2dppx</code></td>
</tr>
</table>
<h3>Common Resolutions</h3>
<table class="reference-table">
<tr>
<th>Device</th>
<th>Resolution</th>
<th>dppx</th>
</tr>
<tr>
<td>Older monitors</td>
<td>72–96 dpi</td>
<td>0.75–1</td>
</tr>
<tr>
<td>Standard laptops</td>
<td>96–120 dpi</td>
<td>1–1.25</td>
</tr>
<tr>
<td>Retina displays</td>
<td>192–220 dpi</td>
<td>2–2.3</td>
</tr>
<tr>
<td>High-end smartphones</td>
<td>400–500 dpi</td>
<td>4–5</td>
</tr>
<tr>
<td>4K monitors</td>
<td>150–200 dpi</td>
<td>1.5–2</td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>pointer: coarse</code> to create larger touch targets (48px minimum)</li>
<li>Use <code>pointer: fine</code> for hover effects and smaller UI elements</li>
<li>Use <code>resolution</code> to serve higher-quality images on high-DPI displays</li>
<li>Adjust border widths for high-DPI displays (0.5px looks like 1px)</li>
<li>Test on both touch and mouse devices</li>
<li>Use <code>min-resolution: 2dppx</code> for Retina/HDPI optimizations</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely solely on pointer type — some devices have both</li>
<li>Don't use tiny touch targets on touch devices</li>
<li>Don't serve large images to low-resolution displays</li>
<li>Don't assume all high-DPI displays are mobile</li>
<li>Don't forget that <code>pointer</code> checks the primary device only</li>
<li>Don't use <code>resolution</code> for critical functionality</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-pointer">Pointer: —</span>
<span class="detail" id="indicator-resolution">Resolution: —</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Pointer detection
const pointerFine = document.getElementById('pointer-fine');
const pointerCoarse = document.getElementById('pointer-coarse');
const pointerNone = document.getElementById('pointer-none');
const anyPointerFine = document.getElementById('any-pointer-fine');
const isFine = window.matchMedia('(pointer: fine)').matches;
const isCoarse = window.matchMedia('(pointer: coarse)').matches;
const isNone = window.matchMedia('(pointer: none)').matches;
const isAnyFine = window.matchMedia('(any-pointer: fine)').matches;
function setCard(card, isActive, activeText, inactiveText) {
const value = card.querySelector('.value');
if (isActive) {
value.textContent = activeText;
card.style.background = '#d4edda';
card.style.borderColor = '#28a745';
value.style.color = '#28a745';
} else {
value.textContent = inactiveText;
card.style.background = '#e9ecef';
card.style.borderColor = '#6c757d';
value.style.color = '#6c757d';
}
}
setCard(pointerFine, isFine, 'Active ✅', 'Not active');
setCard(pointerCoarse, isCoarse, 'Active ✅', 'Not active');
setCard(pointerNone, isNone, 'Active ✅', 'Not active');
setCard(anyPointerFine, isAnyFine, 'Active ✅', 'Not active');
// Resolution detection
const resLow = document.getElementById('res-low');
const resHigh = document.getElementById('res-high');
const isLowRes = window.matchMedia('(max-resolution: 72dpi)').matches;
const isHighRes = window.matchMedia('(min-resolution: 2dppx)').matches;
setCard(resLow, isLowRes, 'Low Res ✅', 'Not low res');
setCard(resHigh, isHighRes, 'High DPI ✅', 'Not high DPI');
// Live indicator
const indicatorPointer = document.getElementById('indicator-pointer');
const indicatorResolution = document.getElementById('indicator-resolution');
const indicator = document.getElementById('live-indicator');
let pointerText = '';
if (isFine) pointerText = '🖱️ Fine (Mouse)';
else if (isCoarse) pointerText = '👆 Coarse (Touch)';
else if (isNone) pointerText = '⌨️ None';
else pointerText = '❓ Unknown';
indicatorPointer.textContent = 'Pointer: ' + pointerText;
// Resolution display
const dpr = window.devicePixelRatio || 1;
const dpi = Math.round(dpr * 96);
let resText = dpr + 'x (' + dpi + 'dpi)';
if (isHighRes) resText += ' — HDPI';
indicatorResolution.textContent = 'Resolution: ' + resText;
// Color the indicator
if (isFine) {
indicator.style.background = '#007bff';
indicator.style.color = 'white';
} else if (isCoarse) {
indicator.style.background = '#28a745';
indicator.style.color = 'white';
} else {
indicator.style.background = '#dc3545';
indicator.style.color = 'white';
}
if (isHighRes) {
indicator.style.boxShadow = '0 4px 15px rgba(40, 167, 69, 0.5)';
}
}
window.addEventListener('load', updateIndicators);
window.addEventListener('resize', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
pointer | Primary pointing device accuracy | none, coarse, fine |
resolution | Pixel density | dpi, dpcm, dppx |
pointer Values
| Value | Description | Device Example |
|---|---|---|
none | No pointing device | Keyboard-only, TV remote |
coarse | Limited accuracy | Finger on touchscreen |
fine | Fine accuracy | Mouse, trackpad |
resolution Units
| Unit | Description | Example |
|---|---|---|
dpi | Dots per inch | 150dpi |
dpcm | Dots per centimeter | 60dpcm |
dppx | Dots per pixel unit (1dppx = 96dpi) | 2dppx |
Common Resolutions
| Device | Resolution | dppx |
|---|---|---|
| Older monitors | 72–96 dpi | 0.75–1 |
| Standard laptops | 96–120 dpi | 1–1.25 |
| Retina displays | 192–220 dpi | 2–2.3 |
| High-end smartphones | 400–500 dpi | 4–5 |
| 4K monitors | 150–200 dpi | 1.5–2 |
Best Practices
✅ Do This:
/* Larger touch targets for touch devices */
@media (pointer: coarse) {
.btn {
padding: 16px 32px;
min-height: 48px;
min-width: 120px;
}
}
/* Hover effects only for mouse */
@media (pointer: fine) {
.btn:hover {
transform: translateY(-2px);
}
}
/* Sharper images for HDPI */
@media (min-resolution: 2dppx) {
.image {
background-image: url('image@2x.png');
}
}
/* Thinner borders on HDPI */
@media (min-resolution: 2dppx) {
.border {
border-width: 0.5px;
}
}
❌ Don’t Do This:
/* Don't use tiny touch targets */
@media (pointer: coarse) {
.btn {
padding: 4px 8px; /* Too small for fingers! */
}
}
/* Don't assume all high-DPI displays are mobile */
@media (min-resolution: 2dppx) {
/* This could be a 4K desktop monitor */
}
/* Don't rely on pointer type alone */
@media (pointer: fine) {
/* Some devices have both mouse and touch */
}
Pro Tip: pointer is essential for touch-friendly design. Use pointer: coarse to create larger touch targets (48px minimum) and pointer: fine for hover effects and precision interactions. resolution is key for high-DPI optimization — serve sharper images on Retina displays, and adjust border widths (0.5px looks like 1px on 2x displays). Remember: pointer checks the primary device, while any-pointer checks all devices. A laptop with both mouse and touchscreen has pointer: fine (mouse is primary) but any-pointer: fine too!
41. prefers-color-scheme and prefers-contrast Media Features
These two media features are essential for accessibility and personalization. They let you respect the user’s preferred color scheme (light/dark) and contrast level.
Overview of Features
| Feature | Description | Values |
|---|---|---|
prefers-color-scheme | User’s preferred color scheme | light, dark |
prefers-contrast | User’s preferred contrast level | no-preference, more, less, custom |
1. prefers-color-scheme
The prefers-color-scheme media feature detects whether the user prefers a light or dark color theme.
@media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: white;
}
}
Values
| Value | Description |
|---|---|
light | User prefers a light theme (or has no preference) |
dark | User prefers a dark theme |
How It Works
- The user sets their preference in their operating system settings
- Windows: Settings → Personalization → Colors → Choose your mode
- macOS: System Preferences → General → Appearance
- iOS: Settings → Display & Brightness → Appearance
- Android: Settings → Display → Dark theme
Key Points:
lightis the default if no preference is expressed- Always provide a light theme as the base, then enhance for dark
- Use the same content and structure — only change colors
- Test both themes for contrast and readability
2. prefers-contrast
The prefers-contrast media feature detects whether the user prefers more or less contrast.
@media (prefers-contrast: more) {
body {
background-color: #f0f0f0;
color: #1a1a1a;
}
}
Values
| Value | Description |
|---|---|
no-preference | No specific preference (default) |
more | User prefers higher contrast |
less | User prefers lower contrast |
custom | User uses a specific set of colors/contrast |
How It Works
- The user sets their preference in their operating system accessibility settings
- Windows: Settings → Ease of Access → Display → High contrast
- macOS: System Preferences → Accessibility → Display → Increase contrast
- iOS: Settings → Accessibility → Display & Text Size → Increase Contrast
Key Points:
more— increase contrast for better legibilityless— reduce contrast (for users sensitive to bright colors)custom— user has chosen a specific color scheme (often withforced-colors)- Always ensure WCAG AAA contrast (7:1) for
more
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>prefers-color-scheme and prefers-contrast</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
/* ====== BASE (LIGHT THEME) ====== */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
--text-primary: #212529;
--text-secondary: #6c757d;
--accent: #007bff;
--accent-hover: #0056b3;
--border: #dee2e6;
--shadow: rgba(0, 0, 0, 0.1);
--code-bg: #1e1e1e;
--code-text: #d4d4d4;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: var(--bg-secondary);
color: var(--text-primary);
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: var(--accent);
border-bottom: 3px solid var(--accent);
padding-bottom: 10px;
}
h2 {
color: var(--accent);
border-left: 4px solid var(--accent);
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: var(--text-primary);
margin-top: 20px;
}
section {
background: var(--bg-primary);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px var(--shadow);
margin: 20px 0;
transition: all 0.3s;
}
.code-block {
background: var(--code-bg);
color: var(--code-text);
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid var(--border);
text-align: left;
}
.reference-table th {
background: var(--accent);
color: white;
}
.reference-table tr:nth-child(even) {
background: var(--bg-tertiary);
}
.note {
font-size: 0.9rem;
color: var(--text-secondary);
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DARK THEME ====== */
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #1a1a1a;
--bg-secondary: #0d0d0d;
--bg-tertiary: #2d2d2d;
--text-primary: #f8f9fa;
--text-secondary: #adb5bd;
--accent: #4dabf7;
--accent-hover: #339af0;
--border: #444;
--shadow: rgba(0, 0, 0, 0.5);
--code-bg: #0d0d0d;
--code-text: #d4d4d4;
}
.highlight {
background: #4dabf7;
color: #000;
}
}
/* ====== HIGH CONTRAST ====== */
@media (prefers-contrast: more) {
:root {
--bg-primary: #ffffff;
--bg-secondary: #ffffff;
--bg-tertiary: #f0f0f0;
--text-primary: #000000;
--text-secondary: #1a1a1a;
--accent: #0000cc;
--accent-hover: #000099;
--border: #000000;
--shadow: rgba(0, 0, 0, 0.3);
}
body {
border: 2px solid #000;
padding: 25px;
}
section {
border: 2px solid #000;
box-shadow: none;
}
h1 {
border-bottom-width: 4px;
}
h2 {
border-left-width: 6px;
}
.code-block {
background: #000;
color: #fff;
border: 2px solid #000;
}
.reference-table th {
background: #000;
color: #fff;
}
.reference-table td {
border-color: #000;
}
.note {
color: #1a1a1a;
}
.highlight {
background: #000;
color: #fff;
border: 1px solid #000;
}
}
@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
:root {
--bg-primary: #000000;
--bg-secondary: #000000;
--bg-tertiary: #1a1a1a;
--text-primary: #ffffff;
--text-secondary: #f0f0f0;
--accent: #66b3ff;
--accent-hover: #99ccff;
--border: #ffffff;
}
body {
border-color: #fff;
}
section {
border-color: #fff;
}
.code-block {
background: #000;
color: #fff;
border-color: #fff;
}
.reference-table th {
background: #fff;
color: #000;
}
.reference-table td {
border-color: #fff;
}
.highlight {
background: #fff;
color: #000;
}
}
/* ====== LOW CONTRAST ====== */
@media (prefers-contrast: less) {
:root {
--bg-primary: #2d2d2d;
--bg-secondary: #1a1a1a;
--bg-tertiary: #3d3d3d;
--text-primary: #d0d0d0;
--text-secondary: #999;
--accent: #6c9bd2;
--accent-hover: #8aadd4;
--border: #444;
--shadow: rgba(0, 0, 0, 0.3);
}
.highlight {
background: #6c9bd2;
color: #1a1a1a;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid var(--border);
background: var(--bg-tertiary);
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: var(--text-secondary);
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: var(--accent);
}
.detection-card.active {
border-color: var(--accent);
background: var(--bg-primary);
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--accent);
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px var(--shadow);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== PRACTICAL: THEME TOGGLE DEMO ====== */
.theme-demo {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin: 15px 0;
}
.theme-card {
flex: 1;
min-width: 250px;
padding: 20px;
border-radius: 12px;
border: 2px solid var(--border);
background: var(--bg-primary);
transition: all 0.3s;
}
.theme-card h4 {
margin-top: 0;
color: var(--accent);
}
.theme-card .preview {
padding: 15px;
border-radius: 8px;
background: var(--bg-tertiary);
margin: 10px 0;
text-align: center;
font-weight: bold;
border: 1px solid var(--border);
}
/* Light preview */
.light-preview {
background: #ffffff;
color: #212529;
}
/* Dark preview */
.dark-preview {
background: #1a1a1a;
color: #f8f9fa;
}
/* ====== CONTRAST VISUALIZATION ====== */
.contrast-demo {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin: 15px 0;
}
.contrast-box {
flex: 1;
min-width: 200px;
padding: 20px;
border-radius: 8px;
text-align: center;
font-weight: bold;
border: 2px solid var(--border);
}
.contrast-normal {
background: #ffffff;
color: #6c757d;
}
.contrast-more {
background: #ffffff;
color: #000000;
border-color: #000;
}
.contrast-less {
background: #2d2d2d;
color: #999;
}
/* ====== ACCESSIBILITY TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid var(--accent);
background: var(--bg-tertiary);
}
.tip-box.success {
border-left-color: #28a745;
}
.tip-box.warning {
border-left-color: #ffc107;
}
.tip-box.danger {
border-left-color: #dc3545;
}
</style>
</head>
<body>
<h1>prefers-color-scheme and prefers-contrast</h1>
<!-- ====== 1. COLOR SCHEME DEMO ====== -->
<section>
<h2>1. prefers-color-scheme</h2>
<p>Detects the user's preferred <strong>color scheme</strong> (light or dark).</p>
<div class="theme-demo">
<div class="theme-card">
<h4>☀️ Light Theme</h4>
<div class="preview light-preview">
This is a light theme preview
</div>
<p class="note">Applied when user prefers light mode (or has no preference).</p>
</div>
<div class="theme-card">
<h4>🌙 Dark Theme</h4>
<div class="preview dark-preview">
This is a dark theme preview
</div>
<p class="note">Applied when user prefers dark mode.</p>
</div>
</div>
<div class="code-block">
/* Light theme (default) */
:root {
--bg-primary: #ffffff;
--text-primary: #212529;
--accent: #007bff;
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #1a1a1a;
--text-primary: #f8f9fa;
--accent: #4dabf7;
}
}
</div>
<p class="note"><strong>Try it:</strong> Change your OS theme to dark mode and watch this page adapt!</p>
</section>
<!-- ====== 2. CONTRAST DEMO ====== -->
<section>
<h2>2. prefers-contrast</h2>
<p>Detects the user's preferred <strong>contrast level</strong>.</p>
<div class="contrast-demo">
<div class="contrast-box contrast-normal">
Normal Contrast<br>
<small>#6c757d on #ffffff</small>
</div>
<div class="contrast-box contrast-more">
More Contrast<br>
<small>#000000 on #ffffff</small>
</div>
<div class="contrast-box contrast-less">
Less Contrast<br>
<small>#999 on #2d2d2d</small>
</div>
</div>
<div class="code-block">
/* High contrast preference */
@media (prefers-contrast: more) {
:root {
--text-primary: #000000;
--bg-primary: #ffffff;
--accent: #0000cc;
}
section {
border: 2px solid #000;
box-shadow: none;
}
}
/* Low contrast preference */
@media (prefers-contrast: less) {
:root {
--bg-primary: #2d2d2d;
--text-primary: #d0d0d0;
--accent: #6c9bd2;
}
}
</div>
</section>
<!-- ====== 3. COMBINED THEME + CONTRAST ====== -->
<section>
<h2>3. Combined Color Scheme + Contrast</h2>
<p>Both features can work together for a fully personalized experience.</p>
<div class="code-block">
/* Dark + High Contrast */
@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
:root {
--bg-primary: #000000;
--text-primary: #ffffff;
--accent: #66b3ff;
--border: #ffffff;
}
}
/* Dark + Low Contrast */
@media (prefers-contrast: less) and (prefers-color-scheme: dark) {
:root {
--bg-primary: #2d2d2d;
--text-primary: #d0d0d0;
}
}
</div>
</section>
<!-- ====== 4. DETECTION SUMMARY ====== -->
<section>
<h2>4. Your Preferences</h2>
<p>Below is a live detection of your preferences.</p>
<div class="detection-grid">
<div class="detection-card" id="scheme-light">
<span class="label">prefers-color-scheme: light</span>
<span class="value" id="scheme-light-value">Detecting...</span>
</div>
<div class="detection-card" id="scheme-dark">
<span class="label">prefers-color-scheme: dark</span>
<span class="value" id="scheme-dark-value">Detecting...</span>
</div>
<div class="detection-card" id="contrast-no-pref">
<span class="label">prefers-contrast: no-preference</span>
<span class="value" id="contrast-no-pref-value">Detecting...</span>
</div>
<div class="detection-card" id="contrast-more">
<span class="label">prefers-contrast: more</span>
<span class="value" id="contrast-more-value">Detecting...</span>
</div>
<div class="detection-card" id="contrast-less">
<span class="label">prefers-contrast: less</span>
<span class="value" id="contrast-less-value">Detecting...</span>
</div>
<div class="detection-card" id="contrast-custom">
<span class="label">prefers-contrast: custom</span>
<span class="value" id="contrast-custom-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect color scheme */
@media (prefers-color-scheme: light) { /* Light preference */ }
@media (prefers-color-scheme: dark) { /* Dark preference */ }
/* Detect contrast preference */
@media (prefers-contrast: no-preference) { /* Default */ }
@media (prefers-contrast: more) { /* High contrast */ }
@media (prefers-contrast: less) { /* Low contrast */ }
@media (prefers-contrast: custom) { /* Custom */ }
</div>
</section>
<!-- ====== 5. ACCESSIBILITY TIPS ====== -->
<section>
<h2>5. Accessibility Tips</h2>
<div class="tip-box success">
<strong>✅ Do This:</strong>
<ul style="margin: 5px 0;">
<li>Start with light theme as the base</li>
<li>Use CSS custom properties (variables) for easy theming</li>
<li>Test both light and dark themes for contrast</li>
<li>Respect the user's system preferences</li>
<li>Ensure WCAG AA (4.5:1) or AAA (7:1) contrast</li>
</ul>
</div>
<div class="tip-box warning">
<strong>⚠️ Be Careful:</strong>
<ul style="margin: 5px 0;">
<li>Don't use pure black (#000) on pure white (#fff) — it's harsh</li>
<li>Don't use pure white (#fff) on pure black (#000) — it causes halation</li>
<li>Don't forget to test with actual users</li>
<li>Don't override user preferences without good reason</li>
</ul>
</div>
<div class="tip-box danger">
<strong>❌ Don't Do This:</strong>
<ul style="margin: 5px 0;">
<li>Don't ignore user preferences — it's an accessibility issue</li>
<li>Don't use low-contrast text (#999 on #fff)</li>
<li>Don't rely solely on color to convey information</li>
<li>Don't forget about images — they need dark mode variants too</li>
</ul>
</div>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>prefers-color-scheme Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>light</code></td>
<td>User prefers a light theme (or has no preference)</td>
</tr>
<tr>
<td><code>dark</code></td>
<td>User prefers a dark theme</td>
</tr>
</table>
<h3>prefers-contrast Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>no-preference</code></td>
<td>No specific preference (default)</td>
</tr>
<tr>
<td><code>more</code></td>
<td>User prefers higher contrast</td>
</tr>
<tr>
<td><code>less</code></td>
<td>User prefers lower contrast</td>
</tr>
<tr>
<td><code>custom</code></td>
<td>User uses a specific set of colors/contrast</td>
</tr>
</table>
<h3>WCAG Contrast Requirements</h3>
<table class="reference-table">
<tr>
<th>Level</th>
<th>Normal Text</th>
<th>Large Text</th>
</tr>
<tr>
<td><strong>AA</strong></td>
<td>4.5:1</td>
<td>3:1</td>
</tr>
<tr>
<td><strong>AAA</strong></td>
<td>7:1</td>
<td>4.5:1</td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use CSS custom properties for theming</li>
<li>Start with light theme, then add dark theme overrides</li>
<li>Test both themes for readability and contrast</li>
<li>Respect <code>prefers-contrast: more</code> for accessibility</li>
<li>Use <code>prefers-contrast: less</code> for users sensitive to bright colors</li>
<li>Combine both features for a fully personalized experience</li>
<li>Test with real users who have accessibility needs</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't ignore user preferences — it's an accessibility issue</li>
<li>Don't use pure black on pure white (or vice versa)</li>
<li>Don't forget to test with actual users</li>
<li>Don't use color alone to convey information</li>
<li>Don't force a theme on users without an option to change</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-scheme">Scheme: —</span>
<span class="detail" id="indicator-contrast">Contrast: —</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Color scheme detection
const schemeLight = document.getElementById('scheme-light');
const schemeDark = document.getElementById('scheme-dark');
const isLight = window.matchMedia('(prefers-color-scheme: light)').matches;
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
function setCard(card, isActive, activeText, inactiveText) {
const value = card.querySelector('.value');
if (isActive) {
value.textContent = activeText;
card.classList.add('active');
} else {
value.textContent = inactiveText;
card.classList.remove('active');
}
}
setCard(schemeLight, isLight, 'Preferred ✅', 'Not preferred');
setCard(schemeDark, isDark, 'Preferred ✅', 'Not preferred');
// Contrast detection
const contrastNoPref = document.getElementById('contrast-no-pref');
const contrastMore = document.getElementById('contrast-more');
const contrastLess = document.getElementById('contrast-less');
const contrastCustom = document.getElementById('contrast-custom');
const isNoPref = window.matchMedia('(prefers-contrast: no-preference)').matches;
const isMore = window.matchMedia('(prefers-contrast: more)').matches;
const isLess = window.matchMedia('(prefers-contrast: less)').matches;
const isCustom = window.matchMedia('(prefers-contrast: custom)').matches;
setCard(contrastNoPref, isNoPref, 'Active ✅', 'Not active');
setCard(contrastMore, isMore, 'Active ✅', 'Not active');
setCard(contrastLess, isLess, 'Active ✅', 'Not active');
setCard(contrastCustom, isCustom, 'Active ✅', 'Not active');
// Live indicator
const indicatorScheme = document.getElementById('indicator-scheme');
const indicatorContrast = document.getElementById('indicator-contrast');
const indicator = document.getElementById('live-indicator');
let schemeText = isDark ? '🌙 Dark' : '☀️ Light';
indicatorScheme.textContent = 'Scheme: ' + schemeText;
let contrastText = 'Default';
if (isMore) contrastText = 'More';
else if (isLess) contrastText = 'Less';
else if (isCustom) contrastText = 'Custom';
indicatorContrast.textContent = 'Contrast: ' + contrastText;
// Update indicator color based on contrast
if (isMore) {
indicator.style.background = '#000';
indicator.style.color = '#fff';
indicator.style.border = '2px solid #fff';
} else if (isLess) {
indicator.style.background = '#6c9bd2';
indicator.style.color = '#1a1a1a';
indicator.style.border = 'none';
} else {
indicator.style.background = isDark ? '#4dabf7' : '#007bff';
indicator.style.color = 'white';
indicator.style.border = 'none';
}
}
window.addEventListener('load', updateIndicators);
// Listen for changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateIndicators);
window.matchMedia('(prefers-contrast: more)').addEventListener('change', updateIndicators);
window.matchMedia('(prefers-contrast: less)').addEventListener('change', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
prefers-color-scheme | User’s preferred color scheme | light, dark |
prefers-contrast | User’s preferred contrast level | no-preference, more, less, custom |
prefers-color-scheme Values
| Value | Description |
|---|---|
light | User prefers a light theme (or has no preference) |
dark | User prefers a dark theme |
prefers-contrast Values
| Value | Description |
|---|---|
no-preference | No specific preference (default) |
more | User prefers higher contrast |
less | User prefers lower contrast |
custom | User uses a specific set of colors/contrast |
WCAG Contrast Requirements
| Level | Normal Text | Large Text |
|---|---|---|
| AA | 4.5:1 | 3:1 |
| AAA | 7:1 | 4.5:1 |
Best Practices
✅ Do This:
/* Base light theme */
:root {
--bg: #ffffff;
--text: #212529;
--accent: #007bff;
}
/* Dark theme overrides */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #f8f9fa;
--accent: #4dabf7;
}
}
/* High contrast */
@media (prefers-contrast: more) {
:root {
--bg: #ffffff;
--text: #000000;
--accent: #0000cc;
}
section {
border: 2px solid #000;
box-shadow: none;
}
}
/* Combined dark + high contrast */
@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
:root {
--bg: #000000;
--text: #ffffff;
--accent: #66b3ff;
}
}
❌ Don’t Do This:
/* Don't use pure black on pure white */
body {
background: #fff;
color: #000; /* Harsh — use #212529 instead */
}
/* Don't ignore user preferences */
body {
background: #fff;
color: #333;
/* No dark mode support — accessibility issue */
}
/* Don't use low contrast */
.note {
color: #ccc; /* On white — fails WCAG */
background: #fff;
}
/* Don't rely solely on color */
.error {
color: red; /* Add icons or text too */
}
Pro Tip: prefers-color-scheme and prefers-contrast are essential for accessibility. Always start with a light theme as the base, then enhance for dark mode. Use CSS custom properties for easy theming. Respect prefers-contrast: more by increasing contrast and adding borders. Remember: pure black on pure white is harsh — use off-black (#212529) and off-white (#f8f9fa) for better readability. Test with real users who have accessibility needs, and always ensure your text meets WCAG AA (4.5:1) or AAA (7:1) contrast ratios!
42. prefers-reduced-data and prefers-reduced-motion Media Features
These two media features prioritize user comfort and performance. One respects users who want less data usage, the other respects users who are sensitive to motion.
Overview of Features
| Feature | Description | Values |
|---|---|---|
prefers-reduced-data | User wants lighter data usage | no-preference, reduce |
prefers-reduced-motion | User wants less animation/motion | no-preference, reduce |
1. prefers-reduced-data
The prefers-reduced-data media feature detects if the user has requested a lightweight alternative to save data.
@media (prefers-reduced-data: reduce) {
.hero {
background-image: none;
}
}
Values
| Value | Description |
|---|---|
no-preference | User has not expressed a preference (default) |
reduce | User prefers lighter data usage |
When to Use
- Avoid large background images on reduced data
- Skip autoplay videos and heavy media
- Use simpler fonts (system fonts instead of web fonts)
- Reduce image quality or use placeholders
- Defer non-essential resources
⚠️ Important Note
prefers-reduced-data is an experimental technology with limited browser support. Always provide a working fallback.
How users enable it:
- Chrome (Android): Settings → Privacy → Lite mode
- Safari: Low Data Mode in iOS settings
- Not yet widely supported in desktop browsers
2. prefers-reduced-motion
The prefers-reduced-motion media feature detects if the user has requested reduced motion — essential for users with vestibular disorders, motion sensitivity, or epilepsy.
@media (prefers-reduced-motion: reduce) {
.container h1,
.container p {
animation: none !important;
transition: none !important;
}
}
Values
| Value | Description |
|---|---|
no-preference | User has not expressed a preference (default) |
reduce | User prefers less motion |
When to Use
- Disable animations (fade, slide, bounce)
- Remove transitions (hover, focus)
- Stop auto-playing carousels and videos
- Avoid parallax scrolling
- Reduce or remove loading spinners
How Users Enable It
- Windows: Settings → Ease of Access → Display → Show animations
- macOS: System Preferences → Accessibility → Display → Reduce motion
- iOS: Settings → Accessibility → Motion → Reduce Motion
- Android: Settings → Accessibility → Remove animations
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>prefers-reduced-data and prefers-reduced-motion</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
:root {
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
--text-primary: #212529;
--text-secondary: #6c757d;
--accent: #007bff;
--border: #dee2e6;
--shadow: rgba(0, 0, 0, 0.1);
}
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #1a1a1a;
--bg-secondary: #0d0d0d;
--bg-tertiary: #2d2d2d;
--text-primary: #f8f9fa;
--text-secondary: #adb5bd;
--accent: #4dabf7;
--border: #444;
--shadow: rgba(0, 0, 0, 0.5);
}
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: var(--bg-secondary);
color: var(--text-primary);
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: var(--accent);
border-bottom: 3px solid var(--accent);
padding-bottom: 10px;
}
h2 {
color: var(--accent);
border-left: 4px solid var(--accent);
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: var(--text-primary);
margin-top: 20px;
}
section {
background: var(--bg-primary);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px var(--shadow);
margin: 20px 0;
transition: all 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid var(--border);
text-align: left;
}
.reference-table th {
background: var(--accent);
color: white;
}
.reference-table tr:nth-child(even) {
background: var(--bg-tertiary);
}
.note {
font-size: 0.9rem;
color: var(--text-secondary);
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== MOTION DEMOS ====== */
.container h1,
.container p {
animation: fadeIn 2s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.4);
}
50% {
transform: scale(1.05);
box-shadow: 0 0 0 15px rgba(0, 123, 255, 0);
}
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes slide {
0% { transform: translateX(0); }
50% { transform: translateX(100px); }
100% { transform: translateX(0); }
}
.motion-demo {
padding: 20px;
border-radius: 8px;
background: var(--bg-tertiary);
border: 2px solid var(--border);
text-align: center;
margin: 15px 0;
min-height: 120px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 15px;
}
.animated-box {
width: 80px;
height: 80px;
background: var(--accent);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2rem;
}
.animated-box.pulse {
animation: pulse 2s infinite;
}
.animated-box.spin {
animation: spin 3s linear infinite;
}
.animated-box.slide {
animation: slide 2s ease-in-out infinite;
}
/* Transitions */
.transition-demo {
padding: 20px;
border-radius: 8px;
background: var(--bg-tertiary);
border: 2px solid var(--border);
margin: 15px 0;
cursor: pointer;
transition: all 0.4s ease;
text-align: center;
font-weight: bold;
}
.transition-demo:hover {
background: var(--accent);
color: white;
transform: scale(1.02);
box-shadow: 0 4px 20px var(--shadow);
}
/* ====== REDUCED MOTION ====== */
@media (prefers-reduced-motion: reduce) {
.container h1,
.container p {
animation: none !important;
transition: none !important;
}
.animated-box {
animation: none !important;
}
.animated-box::after {
content: " (motion reduced)";
font-size: 0.7rem;
font-weight: normal;
display: block;
}
.transition-demo {
transition: none !important;
}
.transition-demo:hover {
transform: none;
}
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
@media (prefers-reduced-motion: no-preference) {
.container h1,
.container p {
animation: fadeIn 2s ease-in;
}
}
/* ====== REDUCED DATA ====== */
.hero-section {
padding: 40px;
border-radius: 12px;
text-align: center;
margin: 15px 0;
color: white;
font-weight: bold;
font-size: 1.5rem;
/* Heavy background image */
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="400" height="200"><defs><linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" style="stop-color:%23007bff"/><stop offset="100%" style="stop-color:%236c5ce7"/></linearGradient></defs><rect width="400" height="200" fill="url(%23g)"/><circle cx="100" cy="100" r="40" fill="rgba(255,255,255,0.1)"/><circle cx="300" cy="50" r="60" fill="rgba(255,255,255,0.1)"/></svg>');
background-size: cover;
background-position: center;
transition: all 0.3s;
}
@media (prefers-reduced-data: reduce) {
.hero-section {
/* Remove heavy background image */
background-image: none;
background: var(--accent);
font-size: 1.2rem;
padding: 30px;
}
.hero-section::after {
content: " (data saver mode)";
font-size: 0.8rem;
font-weight: normal;
display: block;
margin-top: 5px;
}
/* Hide non-essential images */
.decorative-image {
display: none;
}
/* Use system fonts */
body {
font-family: system-ui, -apple-system, sans-serif;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid var(--border);
background: var(--bg-tertiary);
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: var(--text-secondary);
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: var(--accent);
}
.detection-card.active {
border-color: var(--accent);
background: var(--bg-primary);
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--accent);
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px var(--shadow);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid var(--accent);
background: var(--bg-tertiary);
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
/* ====== PRACTICAL: ACCESSIBLE ANIMATION ====== */
.loading-spinner {
width: 50px;
height: 50px;
border: 5px solid var(--bg-tertiary);
border-top: 5px solid var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@media (prefers-reduced-motion: reduce) {
.loading-spinner {
animation: none;
border-top-color: var(--accent);
/* Show a static alternative */
}
.loading-spinner::after {
content: "Loading...";
display: block;
text-align: center;
font-size: 0.8rem;
margin-top: 10px;
color: var(--text-secondary);
}
}
/* ====== PRACTICAL: DATA SAVER IMAGES ====== */
.image-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin: 15px 0;
}
.image-gallery .gallery-item {
height: 150px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 1.5rem;
background: linear-gradient(135deg, var(--accent), #6c5ce7);
transition: all 0.3s;
}
/* High-quality images (default) */
.image-gallery .gallery-item::after {
content: "🖼️ HD";
font-size: 0.8rem;
position: absolute;
}
@media (prefers-reduced-data: reduce) {
.image-gallery .gallery-item {
/* Use simple placeholder */
background: var(--bg-tertiary);
border: 2px dashed var(--border);
color: var(--text-secondary);
font-size: 1rem;
}
.image-gallery .gallery-item::after {
content: "📷 (data saver)";
font-size: 0.8rem;
}
}
</style>
</head>
<body>
<h1>prefers-reduced-data and prefers-reduced-motion</h1>
<!-- ====== 1. REDUCED MOTION DEMO ====== -->
<section class="container">
<h2>1. prefers-reduced-motion</h2>
<p>Detects if the user has requested <strong>reduced motion</strong> for accessibility.</p>
<h3>Animated Elements</h3>
<div class="motion-demo">
<div class="animated-box pulse">💓</div>
<div class="animated-box spin">🔄</div>
<div class="animated-box slide">➡️</div>
<p class="note">These animations are disabled when <code>prefers-reduced-motion: reduce</code> is active.</p>
</div>
<h3>Transition Demo</h3>
<div class="transition-demo">
Hover over me to see the transition
</div>
<p class="note">Transitions are also disabled for users who prefer reduced motion.</p>
<div class="code-block">
/* Disable animations and transitions */
@media (prefers-reduced-motion: reduce) {
.container h1,
.container p {
animation: none !important;
transition: none !important;
}
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Enable animations only when no preference */
@media (prefers-reduced-motion: no-preference) {
.container h1,
.container p {
animation: fadeIn 2s ease-in;
}
}
</div>
</section>
<!-- ====== 2. REDUCED DATA DEMO ====== -->
<section>
<h2>2. prefers-reduced-data</h2>
<p>Detects if the user prefers <strong>lighter data usage</strong>.</p>
<div class="hero-section">
🎨 Hero Section with Background Image
</div>
<p class="note">On reduced data, the heavy background image is replaced with a solid color.</p>
<h3>Image Gallery</h3>
<div class="image-gallery">
<div class="gallery-item"></div>
<div class="gallery-item"></div>
<div class="gallery-item"></div>
<div class="gallery-item"></div>
</div>
<p class="note">On reduced data, high-quality images become simple placeholders.</p>
<div class="code-block">
/* Default: heavy background image */
.hero-section {
background-image: url('large-image.jpg');
background-size: cover;
}
/* Reduced data: remove heavy images */
@media (prefers-reduced-data: reduce) {
.hero-section {
background-image: none;
background: var(--accent);
}
.decorative-image {
display: none;
}
body {
font-family: system-ui, sans-serif;
}
}
</div>
<div class="tip-box warning">
<strong>⚠️ Experimental Feature:</strong>
<code>prefers-reduced-data</code> has limited browser support. Always provide a working fallback.
</div>
</section>
<!-- ====== 3. DETECTION SUMMARY ====== -->
<section>
<h2>3. Your Preferences</h2>
<p>Below is a live detection of your preferences.</p>
<div class="detection-grid">
<div class="detection-card" id="motion-no-pref">
<span class="label">prefers-reduced-motion: no-preference</span>
<span class="value" id="motion-no-pref-value">Detecting...</span>
</div>
<div class="detection-card" id="motion-reduce">
<span class="label">prefers-reduced-motion: reduce</span>
<span class="value" id="motion-reduce-value">Detecting...</span>
</div>
<div class="detection-card" id="data-no-pref">
<span class="label">prefers-reduced-data: no-preference</span>
<span class="value" id="data-no-pref-value">Detecting...</span>
</div>
<div class="detection-card" id="data-reduce">
<span class="label">prefers-reduced-data: reduce</span>
<span class="value" id="data-reduce-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect motion preference */
@media (prefers-reduced-motion: no-preference) { /* Animations OK */ }
@media (prefers-reduced-motion: reduce) { /* Reduce motion */ }
/* Detect data preference */
@media (prefers-reduced-data: no-preference) { /* Normal data */ }
@media (prefers-reduced-data: reduce) { /* Lightweight content */ }
</div>
</section>
<!-- ====== 4. PRACTICAL: ACCESSIBLE SPINNER ====== -->
<section>
<h2>4. Practical Example: Accessible Loading Spinner</h2>
<p>A spinner that respects reduced motion preferences.</p>
<div class="loading-spinner"></div>
<div class="code-block">
/* Default: spinning animation */
.loading-spinner {
animation: spin 1s linear infinite;
}
/* Reduced motion: static alternative */
@media (prefers-reduced-motion: reduce) {
.loading-spinner {
animation: none;
}
.loading-spinner::after {
content: "Loading...";
}
}
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>prefers-reduced-motion Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>no-preference</code></td>
<td>No preference expressed (default)</td>
</tr>
<tr>
<td><code>reduce</code></td>
<td>User prefers reduced motion</td>
</tr>
</table>
<h3>prefers-reduced-data Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>no-preference</code></td>
<td>No preference expressed (default)</td>
</tr>
<tr>
<td><code>reduce</code></td>
<td>User prefers lighter data usage</td>
</tr>
</table>
<h3>When to Reduce Motion</h3>
<table class="reference-table">
<tr>
<th>Element</th>
<th>Default</th>
<th>Reduced Motion</th>
</tr>
<tr>
<td>Fade animations</td>
<td>Enabled</td>
<td>Disabled</td>
</tr>
<tr>
<td>Slide transitions</td>
<td>Enabled</td>
<td>Disabled</td>
</tr>
<tr>
<td>Parallax scrolling</td>
<td>Enabled</td>
<td>Disabled</td>
</tr>
<tr>
<td>Auto-playing carousels</td>
<td>Enabled</td>
<td>Paused/disabled</td>
</tr>
<tr>
<td>Loading spinners</td>
<td>Animated</td>
<td>Static</td>
</tr>
</table>
<h3>When to Reduce Data</h3>
<table class="reference-table">
<tr>
<th>Element</th>
<th>Default</th>
<th>Reduced Data</th>
</tr>
<tr>
<td>Background images</td>
<td>High quality</td>
<td>None or simplified</td>
</tr>
<tr>
<td>Web fonts</td>
<td>Custom fonts</td>
<td>System fonts</td>
</tr>
<tr>
<td>Auto-play videos</td>
<td>Enabled</td>
<td>Disabled</td>
</tr>
<tr>
<td>Decorative images</td>
<td>Visible</td>
<td>Hidden</td>
</tr>
<tr>
<td>High-res images</td>
<td>Full quality</td>
<td>Placeholders</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Respect <code>prefers-reduced-motion: reduce</code> for all animations</li>
<li>Provide static alternatives for animated content</li>
<li>Use <code>prefers-reduced-data: reduce</code> to skip heavy media</li>
<li>Test with reduced motion enabled in your OS</li>
<li>Provide working fallbacks (since <code>prefers-reduced-data</code> is experimental)</li>
<li>Consider the user's comfort over visual flair</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't ignore reduced motion — it's an accessibility requirement</li>
<li>Don't use animations that can't be disabled</li>
<li>Don't rely solely on <code>prefers-reduced-data</code> (limited support)</li>
<li>Don't force heavy media on users with limited data</li>
<li>Don't forget to test with actual users who have motion sensitivity</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-motion">Motion: —</span>
<span class="detail" id="indicator-data">Data: —</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Motion detection
const motionNoPref = document.getElementById('motion-no-pref');
const motionReduce = document.getElementById('motion-reduce');
const isMotionNoPref = window.matchMedia('(prefers-reduced-motion: no-preference)').matches;
const isMotionReduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function setCard(card, isActive, activeText, inactiveText) {
const value = card.querySelector('.value');
if (isActive) {
value.textContent = activeText;
card.classList.add('active');
} else {
value.textContent = inactiveText;
card.classList.remove('active');
}
}
setCard(motionNoPref, isMotionNoPref, 'Active ✅', 'Not active');
setCard(motionReduce, isMotionReduce, 'Active ✅', 'Not active');
// Data detection
const dataNoPref = document.getElementById('data-no-pref');
const dataReduce = document.getElementById('data-reduce');
const isDataNoPref = window.matchMedia('(prefers-reduced-data: no-preference)').matches;
const isDataReduce = window.matchMedia('(prefers-reduced-data: reduce)').matches;
setCard(dataNoPref, isDataNoPref, 'Active ✅', 'Not active');
setCard(dataReduce, isDataReduce, 'Active ✅', 'Not active');
// Live indicator
const indicatorMotion = document.getElementById('indicator-motion');
const indicatorData = document.getElementById('indicator-data');
const indicator = document.getElementById('live-indicator');
indicatorMotion.textContent = isMotionReduce ? 'Motion: Reduced 🚫' : 'Motion: Full ✨';
indicatorData.textContent = isDataReduce ? 'Data: Reduced 📉' : 'Data: Normal 📶';
// Color the indicator
if (isMotionReduce || isDataReduce) {
indicator.style.background = '#28a745';
indicator.style.color = 'white';
} else {
indicator.style.background = '#007bff';
indicator.style.color = 'white';
}
}
window.addEventListener('load', updateIndicators);
// Listen for changes
window.matchMedia('(prefers-reduced-motion: reduce)').addEventListener('change', updateIndicators);
window.matchMedia('(prefers-reduced-data: reduce)').addEventListener('change', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
prefers-reduced-motion | User wants less animation | no-preference, reduce |
prefers-reduced-data | User wants lighter data usage | no-preference, reduce |
prefers-reduced-motion Values
| Value | Description |
|---|---|
no-preference | No preference expressed (default) |
reduce | User prefers reduced motion |
prefers-reduced-data Values
| Value | Description |
|---|---|
no-preference | No preference expressed (default) |
reduce | User prefers lighter data usage |
When to Reduce Motion
| Element | Default | Reduced Motion |
|---|---|---|
| Fade animations | Enabled | Disabled |
| Slide transitions | Enabled | Disabled |
| Parallax scrolling | Enabled | Disabled |
| Auto-playing carousels | Enabled | Paused/disabled |
| Loading spinners | Animated | Static |
When to Reduce Data
| Element | Default | Reduced Data |
|---|---|---|
| Background images | High quality | None or simplified |
| Web fonts | Custom fonts | System fonts |
| Auto-play videos | Enabled | Disabled |
| Decorative images | Visible | Hidden |
| High-res images | Full quality | Placeholders |
Best Practices
✅ Do This:
/* Disable animations for reduced motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Enable animations only when no preference */
@media (prefers-reduced-motion: no-preference) {
.fade-in {
animation: fadeIn 2s ease-in;
}
}
/* Reduce data usage */
@media (prefers-reduced-data: reduce) {
.hero {
background-image: none;
background: #007bff;
}
.decorative-image {
display: none;
}
}
/* Provide static alternatives */
@media (prefers-reduced-motion: reduce) {
.spinner {
animation: none;
}
.spinner::after {
content: "Loading...";
}
}
❌ Don’t Do This:
/* Don't ignore reduced motion */
.animation {
animation: bounce 2s infinite;
/* No reduced motion support — accessibility issue! */
}
/* Don't rely solely on prefers-reduced-data */
@media (prefers-reduced-data: reduce) {
/* Limited browser support — always provide fallbacks */
}
/* Don't force heavy media on data-saver users */
.hero {
background-image: url('huge-image.jpg');
/* No alternative for reduced data */
}
Pro Tip: prefers-reduced-motion is essential for accessibility. Users with vestibular disorders, motion sensitivity, or epilepsy can be physically harmed by excessive animation. Always provide static alternatives — a loading spinner can become “Loading…” text, and a fade-in can simply appear. prefers-reduced-data is still experimental with limited support, so use it as an enhancement, not a requirement. Always provide working fallbacks, and test with actual users who have these preferences enabled!
43. prefers-reduced-transparency and scan Media Features
These two media features address accessibility (transparency preferences) and legacy display technology (interlaced vs progressive scanning).
Overview of Features
| Feature | Description | Values |
|---|---|---|
prefers-reduced-transparency | User wants less transparency | no-preference, reduce |
scan | Monitor’s scanning process | interlaced, progressive |
1. prefers-reduced-transparency
The prefers-reduced-transparency media feature detects if the user has requested reduced transparency — important for users who have difficulty reading text over translucent backgrounds.
@media (prefers-reduced-transparency: reduce) {
body {
background-color: #ffffff;
}
}
Values
| Value | Description |
|---|---|
no-preference | User has not expressed a preference (default) |
reduce | User prefers reduced transparency |
When to Use
- Remove backdrop blur effects (
backdrop-filter: blur()) - Replace translucent backgrounds with solid colors
- Remove opacity from overlays and modals
- Simplify glassmorphism effects
- Increase contrast for better readability
How Users Enable It
- iOS: Settings → Accessibility → Motion → Reduce Transparency
- macOS: System Preferences → Accessibility → Display → Reduce transparency
- Windows: Settings → Personalization → Colors → Transparency effects (off)
⚠️ Important Note
prefers-reduced-transparency is a relatively new feature with limited browser support. Always provide a working fallback.
2. scan
The scan media feature detects the scanning process of the display — how the image is drawn on screen.
@media (scan: interlaced) {
body {
background-color: #f0f;
color: #0ff;
}
}
Values
| Value | Description |
|---|---|
interlaced | Image drawn in alternating lines (old CRT monitors) |
progressive | Each frame is complete (modern displays) |
Interlaced vs Progressive
Interlaced: Progressive:
Frame 1: Frame 1:
┌─────────────┐ ┌─────────────┐
│ ███████████ │ │ ███████████ │
│ │ │ ███████████ │
│ ███████████ │ │ ███████████ │
│ │ │ ███████████ │
│ ███████████ │ │ ███████████ │
└─────────────┘ └─────────────┘
Odd lines first, All lines at once
then even lines
Key Points
| Aspect | Interlaced | Progressive |
|---|---|---|
| Technology | Old CRT monitors | Modern LCD, OLED |
| Rendering | Alternating lines | Complete frames |
| Motion | Can flicker with fast movement | Smooth |
| Thin lines | May flicker (< 1px) | No flicker |
| Current use | Rare (legacy) | Universal |
Design Considerations for Interlaced
- Avoid thin lines (less than 1px) — they can flicker
- Avoid fast movement — causes visible flicker
- Use wider strokes for text and graphics
- Reduce animation speed
- Most modern developers will never encounter interlaced displays
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>prefers-reduced-transparency and scan</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
:root {
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
--text-primary: #212529;
--text-secondary: #6c757d;
--accent: #007bff;
--border: #dee2e6;
--shadow: rgba(0, 0, 0, 0.1);
}
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #1a1a1a;
--bg-secondary: #0d0d0d;
--bg-tertiary: #2d2d2d;
--text-primary: #f8f9fa;
--text-secondary: #adb5bd;
--accent: #4dabf7;
--border: #444;
--shadow: rgba(0, 0, 0, 0.5);
}
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: var(--bg-secondary);
color: var(--text-primary);
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: var(--accent);
border-bottom: 3px solid var(--accent);
padding-bottom: 10px;
}
h2 {
color: var(--accent);
border-left: 4px solid var(--accent);
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: var(--text-primary);
margin-top: 20px;
}
section {
background: var(--bg-primary);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px var(--shadow);
margin: 20px 0;
transition: all 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid var(--border);
text-align: left;
}
.reference-table th {
background: var(--accent);
color: white;
}
.reference-table tr:nth-child(even) {
background: var(--bg-tertiary);
}
.note {
font-size: 0.9rem;
color: var(--text-secondary);
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== TRANSPARENCY DEMO ====== */
.transparency-demo {
position: relative;
padding: 40px 20px;
border-radius: 12px;
overflow: hidden;
margin: 15px 0;
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
/* Background image for transparency effect */
background-image: linear-gradient(135deg, #007bff, #6c5ce7, #dc3545);
background-size: cover;
}
.glass-card {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 16px;
padding: 30px;
text-align: center;
color: white;
font-weight: bold;
max-width: 400px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
transition: all 0.3s;
}
.glass-card h3 {
margin-top: 0;
color: white;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
.glass-card p {
margin-bottom: 0;
font-size: 0.95rem;
opacity: 0.95;
}
/* Reduced transparency: solid background */
@media (prefers-reduced-transparency: reduce) {
.glass-card {
background: var(--bg-primary);
backdrop-filter: none;
-webkit-backdrop-filter: none;
border: 2px solid var(--border);
color: var(--text-primary);
box-shadow: 0 4px 15px var(--shadow);
}
.glass-card h3 {
color: var(--text-primary);
text-shadow: none;
}
.glass-card p {
color: var(--text-secondary);
}
.glass-card::after {
content: " (reduced transparency)";
display: block;
font-size: 0.75rem;
margin-top: 10px;
color: var(--text-secondary);
font-weight: normal;
}
}
/* ====== TRANSPARENCY COMPARISON ====== */
.transparency-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 15px 0;
}
.transparency-box {
padding: 25px;
border-radius: 12px;
text-align: center;
font-weight: bold;
color: white;
min-height: 150px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
/* Colorful background behind */
background-image: linear-gradient(135deg, #ff6b6b, #ffc107, #28a745, #007bff, #6c5ce7);
background-size: cover;
}
.transparency-box .inner {
padding: 15px 25px;
border-radius: 8px;
width: 100%;
}
/* Transparent versions */
.transparent-high {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.transparent-medium {
background: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.5);
color: #333;
}
.transparent-low {
background: rgba(255, 255, 255, 0.9);
color: #333;
border: 1px solid rgba(0, 0, 0, 0.1);
}
/* Reduced transparency: all solid */
@media (prefers-reduced-transparency: reduce) {
.transparent-high,
.transparent-medium,
.transparent-low {
background: var(--bg-primary);
backdrop-filter: none;
-webkit-backdrop-filter: none;
border: 2px solid var(--border);
color: var(--text-primary);
}
}
/* ====== SCAN DEMO ====== */
.scan-demo {
padding: 25px;
border-radius: 8px;
margin: 15px 0;
text-align: center;
font-weight: bold;
font-size: 1.1rem;
transition: all 0.3s;
background: var(--bg-tertiary);
border: 2px solid var(--border);
}
/* Progressive scan (modern displays) */
@media (scan: progressive) {
.scan-demo {
background: #cce5ff;
color: #004085;
border-color: #007bff;
}
.scan-demo::after {
content: " (progressive scan — modern display)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Interlaced scan (old CRT monitors) */
@media (scan: interlaced) {
.scan-demo {
background: #f0f;
color: #0ff;
border-color: #f0f;
/* Avoid thin lines and fast movement */
font-weight: bold;
}
.scan-demo::after {
content: " (interlaced scan — legacy display)";
font-weight: normal;
font-size: 0.85rem;
}
/* Wider strokes for interlaced */
.scan-demo {
border-width: 3px;
}
}
/* ====== INTERLACED DESIGN CONSIDERATIONS ====== */
.line-demo {
display: flex;
flex-direction: column;
gap: 10px;
margin: 15px 0;
}
.line-demo .thin-line {
height: 1px;
background: var(--text-primary);
width: 100%;
}
.line-demo .thick-line {
height: 4px;
background: var(--accent);
width: 100%;
}
.line-demo .text-sample {
font-size: 16px;
font-weight: 300;
letter-spacing: 0.5px;
}
.line-demo .bold-sample {
font-size: 16px;
font-weight: 700;
letter-spacing: 0.5px;
}
/* Interlaced: avoid thin lines and light fonts */
@media (scan: interlaced) {
.line-demo .thin-line {
height: 3px;
}
.line-demo .text-sample {
font-weight: 500;
font-size: 18px;
}
.line-demo .bold-sample {
font-weight: 900;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid var(--border);
background: var(--bg-tertiary);
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: var(--text-secondary);
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: var(--accent);
}
.detection-card.active {
border-color: var(--accent);
background: var(--bg-primary);
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--accent);
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px var(--shadow);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid var(--accent);
background: var(--bg-tertiary);
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
/* ====== PRACTICAL: ACCESSIBLE MODAL ====== */
.modal-demo {
position: relative;
padding: 40px 20px;
border-radius: 12px;
background-image: linear-gradient(135deg, #007bff, #6c5ce7);
min-height: 250px;
display: flex;
align-items: center;
justify-content: center;
margin: 15px 0;
}
.modal-content {
background: rgba(255, 255, 255, 0.95);
border-radius: 12px;
padding: 30px;
max-width: 400px;
text-align: center;
color: #333;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
transition: all 0.3s;
}
.modal-content h3 {
margin-top: 0;
color: #007bff;
}
/* Reduced transparency: solid modal */
@media (prefers-reduced-transparency: reduce) {
.modal-content {
background: var(--bg-primary);
color: var(--text-primary);
border: 2px solid var(--border);
box-shadow: 0 4px 15px var(--shadow);
}
.modal-content h3 {
color: var(--accent);
}
}
</style>
</head>
<body>
<h1>prefers-reduced-transparency and scan</h1>
<!-- ====== 1. TRANSPARENCY DEMO ====== -->
<section>
<h2>1. prefers-reduced-transparency</h2>
<p>Detects if the user prefers <strong>reduced transparency</strong> — important for users who have difficulty reading text over translucent backgrounds.</p>
<h3>Glassmorphism Card</h3>
<div class="transparency-demo">
<div class="glass-card">
<h3>✨ Glass Card</h3>
<p>This card uses transparency and backdrop blur. On reduced transparency, it becomes solid with a border.</p>
</div>
</div>
<h3>Transparency Levels Comparison</h3>
<div class="transparency-grid">
<div class="transparency-box">
<div class="inner transparent-high">High Transparency (0.2)</div>
</div>
<div class="transparency-box">
<div class="inner transparent-medium">Medium Transparency (0.5)</div>
</div>
<div class="transparency-box">
<div class="inner transparent-low">Low Transparency (0.9)</div>
</div>
</div>
<div class="code-block">
/* Default: glassmorphism with transparency */
.glass-card {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
/* Reduced transparency: solid background */
@media (prefers-reduced-transparency: reduce) {
.glass-card {
background: var(--bg-primary);
backdrop-filter: none;
border: 2px solid var(--border);
}
}
</div>
<div class="tip-box warning">
<strong>⚠️ Limited Support:</strong>
<code>prefers-reduced-transparency</code> is a relatively new feature with limited browser support. Always provide a working fallback.
</div>
</section>
<!-- ====== 2. SCAN DEMO ====== -->
<section>
<h2>2. scan</h2>
<p>Detects the <strong>scanning process</strong> of the display — interlaced (old CRT) or progressive (modern).</p>
<div class="scan-demo">
Scan Detection
</div>
<h3>Interlaced Design Considerations</h3>
<div class="line-demo">
<div class="thin-line"></div>
<div class="text-sample">Light text sample — thin strokes may flicker on interlaced</div>
<div class="thick-line"></div>
<div class="bold-sample">Bold text sample — thicker strokes are more stable</div>
</div>
<p class="note">On interlaced displays, thin lines and light fonts can flicker. Use thicker strokes and bolder fonts.</p>
<div class="code-block">
/* Progressive scan (modern displays) */
@media (scan: progressive) {
.demo {
/* Normal styles — thin lines and light fonts are fine */
}
}
/* Interlaced scan (old CRT monitors) */
@media (scan: interlaced) {
.demo {
/* Use thicker lines and bolder fonts */
border-width: 3px;
}
.thin-line {
height: 3px; /* Avoid flicker */
}
.light-text {
font-weight: 500; /* Bolder than normal */
}
}
</div>
<div class="tip-box success">
<strong>✅ Good News:</strong>
Interlaced displays are extremely rare today. Most developers will never encounter them. The <code>scan</code> feature is primarily for legacy support.
</div>
</section>
<!-- ====== 3. DETECTION SUMMARY ====== -->
<section>
<h2>3. Your Display's Capabilities</h2>
<p>Below is a live detection of your display's capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="transparency-no-pref">
<span class="label">prefers-reduced-transparency: no-preference</span>
<span class="value" id="transparency-no-pref-value">Detecting...</span>
</div>
<div class="detection-card" id="transparency-reduce">
<span class="label">prefers-reduced-transparency: reduce</span>
<span class="value" id="transparency-reduce-value">Detecting...</span>
</div>
<div class="detection-card" id="scan-interlaced">
<span class="label">scan: interlaced</span>
<span class="value" id="scan-interlaced-value">Detecting...</span>
</div>
<div class="detection-card" id="scan-progressive">
<span class="label">scan: progressive</span>
<span class="value" id="scan-progressive-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect reduced transparency preference */
@media (prefers-reduced-transparency: no-preference) { /* Normal */ }
@media (prefers-reduced-transparency: reduce) { /* Reduced */ }
/* Detect scan type */
@media (scan: interlaced) { /* CRT monitor */ }
@media (scan: progressive) { /* Modern display */ }
</div>
</section>
<!-- ====== 4. PRACTICAL: ACCESSIBLE MODAL ====== -->
<section>
<h2>4. Practical Example: Accessible Modal</h2>
<p>A modal that adapts to transparency preferences.</p>
<div class="modal-demo">
<div class="modal-content">
<h3>📋 Modal Dialog</h3>
<p>This modal uses a semi-transparent background. On reduced transparency, it becomes fully opaque with a solid border.</p>
</div>
</div>
<div class="code-block">
/* Default: semi-transparent modal */
.modal-content {
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
/* Reduced transparency: solid modal */
@media (prefers-reduced-transparency: reduce) {
.modal-content {
background: var(--bg-primary);
border: 2px solid var(--border);
box-shadow: 0 4px 15px var(--shadow);
}
}
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>prefers-reduced-transparency Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>no-preference</code></td>
<td>User has not expressed a preference (default)</td>
</tr>
<tr>
<td><code>reduce</code></td>
<td>User prefers reduced transparency</td>
</tr>
</table>
<h3>scan Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Display Type</th>
</tr>
<tr>
<td><code>interlaced</code></td>
<td>Image drawn in alternating lines</td>
<td>Old CRT monitors</td>
</tr>
<tr>
<td><code>progressive</code></td>
<td>Each frame is complete</td>
<td>Modern LCD, OLED</td>
</tr>
</table>
<h3>Interlaced vs Progressive</h3>
<table class="reference-table">
<tr>
<th>Aspect</th>
<th>Interlaced</th>
<th>Progressive</th>
</tr>
<tr>
<td><strong>Technology</strong></td>
<td>Old CRT monitors</td>
<td>Modern displays</td>
</tr>
<tr>
<td><strong>Rendering</strong></td>
<td>Alternating lines</td>
<td>Complete frames</td>
</tr>
<tr>
<td><strong>Thin lines</strong></td>
<td>May flicker (< 1px)</td>
<td>No flicker</td>
</tr>
<tr>
<td><strong>Fast motion</strong></td>
<td>Can cause visible flicker</td>
<td>Smooth</td>
</tr>
<tr>
<td><strong>Current use</strong></td>
<td>Rare (legacy)</td>
<td>Universal</td>
</tr>
</table>
<h3>When to Reduce Transparency</h3>
<table class="reference-table">
<tr>
<th>Element</th>
<th>Default</th>
<th>Reduced Transparency</th>
</tr>
<tr>
<td>Glassmorphism cards</td>
<td>Translucent + blur</td>
<td>Solid + border</td>
</tr>
<tr>
<td>Modals/overlays</td>
<td>Semi-transparent</td>
<td>Fully opaque</td>
</tr>
<tr>
<td>Navigation bars</td>
<td>Translucent</td>
<td>Solid</td>
</tr>
<tr>
<td>Dropdown menus</td>
<td>Transparent</td>
<td>Solid with border</td>
</tr>
<tr>
<td>Tooltips</td>
<td>Translucent</td>
<td>Solid</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Respect <code>prefers-reduced-transparency: reduce</code> for accessibility</li>
<li>Replace translucent backgrounds with solid colors</li>
<li>Remove <code>backdrop-filter: blur()</code> on reduced transparency</li>
<li>Add borders to maintain visual hierarchy without transparency</li>
<li>Test with reduced transparency enabled in your OS</li>
<li>Use <code>scan</code> only for legacy CRT support (rarely needed)</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely solely on transparency for visual design</li>
<li>Don't ignore reduced transparency — it's an accessibility issue</li>
<li>Don't use <code>backdrop-filter</code> without a fallback</li>
<li>Don't use thin lines (less than 1px) for critical UI on interlaced displays</li>
<li>Don't assume all users can comfortably read text over translucent backgrounds</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-transparency">Transparency: —</span>
<span class="detail" id="indicator-scan">Scan: —</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
// Transparency detection
const transparencyNoPref = document.getElementById('transparency-no-pref');
const transparencyReduce = document.getElementById('transparency-reduce');
const isNoPref = window.matchMedia('(prefers-reduced-transparency: no-preference)').matches;
const isReduce = window.matchMedia('(prefers-reduced-transparency: reduce)').matches;
function setCard(card, isActive, activeText, inactiveText) {
const value = card.querySelector('.value');
if (isActive) {
value.textContent = activeText;
card.classList.add('active');
} else {
value.textContent = inactiveText;
card.classList.remove('active');
}
}
setCard(transparencyNoPref, isNoPref, 'Active ✅', 'Not active');
setCard(transparencyReduce, isReduce, 'Active ✅', 'Not active');
// Scan detection
const scanInterlaced = document.getElementById('scan-interlaced');
const scanProgressive = document.getElementById('scan-progressive');
const isInterlaced = window.matchMedia('(scan: interlaced)').matches;
const isProgressive = window.matchMedia('(scan: progressive)').matches;
setCard(scanInterlaced, isInterlaced, 'Active ✅', 'Not active');
setCard(scanProgressive, isProgressive, 'Active ✅', 'Not active');
// Live indicator
const indicatorTransparency = document.getElementById('indicator-transparency');
const indicatorScan = document.getElementById('indicator-scan');
const indicator = document.getElementById('live-indicator');
indicatorTransparency.textContent = isReduce ? 'Transparency: Reduced 🚫' : 'Transparency: Normal ✨';
let scanText = 'Unknown';
if (isProgressive) scanText = 'Progressive ✅';
else if (isInterlaced) scanText = 'Interlaced ⚠️';
indicatorScan.textContent = 'Scan: ' + scanText;
// Color the indicator
if (isReduce || isInterlaced) {
indicator.style.background = '#28a745';
indicator.style.color = 'white';
} else {
indicator.style.background = '#007bff';
indicator.style.color = 'white';
}
}
window.addEventListener('load', updateIndicators);
// Listen for changes
window.matchMedia('(prefers-reduced-transparency: reduce)').addEventListener('change', updateIndicators);
window.matchMedia('(scan: interlaced)').addEventListener('change', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
prefers-reduced-transparency | User wants less transparency | no-preference, reduce |
scan | Monitor’s scanning process | interlaced, progressive |
prefers-reduced-transparency Values
| Value | Description |
|---|---|
no-preference | No preference expressed (default) |
reduce | User prefers reduced transparency |
scan Values
| Value | Description | Display Type |
|---|---|---|
interlaced | Alternating lines | Old CRT monitors |
progressive | Complete frames | Modern displays |
Interlaced vs Progressive
| Aspect | Interlaced | Progressive |
|---|---|---|
| Technology | Old CRT | Modern LCD/OLED |
| Thin lines | May flicker (< 1px) | No flicker |
| Fast motion | Can flicker | Smooth |
| Current use | Rare | Universal |
Best Practices
✅ Do This:
/* Reduce transparency for accessibility */
@media (prefers-reduced-transparency: reduce) {
.glass-card {
background: #ffffff;
backdrop-filter: none;
border: 2px solid #ddd;
}
}
/* Handle interlaced displays (rare) */
@media (scan: interlaced) {
.thin-line {
height: 3px; /* Avoid flicker */
}
.light-text {
font-weight: 500;
}
}
❌ Don’t Do This:
/* Don't rely on transparency for design */
.glass {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
/* No fallback for reduced transparency */
}
/* Don't use thin lines on interlaced displays */
@media (scan: interlaced) {
.border {
border-width: 0.5px; /* Will flicker! */
}
}
Pro Tip: prefers-reduced-transparency is essential for accessibility. Users with visual impairments may struggle to read text over translucent backgrounds. Always provide a solid fallback — replace backdrop-filter: blur() with a solid background and add a border to maintain visual hierarchy. Note that this feature has limited browser support, so test carefully. scan is a legacy feature — interlaced CRT monitors are extremely rare today, so most developers will never need it. If you do encounter it, use thicker lines (3px+) and bolder fonts to prevent flicker!
44. scripting, update, and video-dynamic-range Media Features
These three media features let you adapt styles based on JavaScript availability, display refresh capability, and HDR video support.
Overview of Features
| Feature | Description | Values |
|---|---|---|
scripting | Detects JavaScript availability | none, initial-only, enabled |
update | How often the display can update | none, slow, fast |
video-dynamic-range | Dynamic range of the video plane | standard, high |
1. scripting
The scripting media feature detects whether JavaScript is available in the browser.
@media (scripting: enabled) {
.enabled {
color: red;
font-weight: bold;
}
}
Values
| Value | Description |
|---|---|
none | Scripting is not available (JavaScript disabled) |
initial-only | Scripting available only during initial page load |
enabled | Scripting is fully supported (default) |
When to Use
- Provide fallbacks for users with JavaScript disabled
- Show/hide JavaScript-dependent UI elements
- Style progressive enhancement layers
- Display noscript alternatives with CSS instead of
<noscript>
How Users Disable JavaScript
- Chrome: Settings → Privacy → Site Settings → JavaScript → Blocked
- Firefox: about:config →
javascript.enabled→ false - Safari: Develop → Disable JavaScript
- Brave: Shields → Scripts blocked
Key Points:
enabledis the default in modern browsersnonemeans no JavaScript at all — plan for progressive enhancementinitial-onlyis rare (some email clients, print preview)- Use this to style fallback content instead of relying on
<noscript>
2. update
The update media feature checks how frequently the device can update rendered content — essentially, its refresh capability.
@media (update: fast) {
p {
animation: slide 1s infinite alternate;
}
}
Values
| Value | Description | Device Example |
|---|---|---|
none | Content cannot be updated after rendering | Print, e-ink (static) |
slow | Updates are possible but not fast enough for smooth animation | E-ink readers, some low-power devices |
fast | Content can be updated quickly for smooth animation | Modern screens (60Hz+) |
When to Use
| Update Value | Recommendation |
|---|---|
none | Disable all animations and transitions |
slow | Use slow, infrequent animations only |
fast | Enable smooth animations and transitions |
Key Points:
fastis the default on modern displaysslowdevices (e-ink) can’t handle smooth animationnonemeans the content is static after rendering- Use this to conditionally enable animations based on device capability
Practical Example
/* No animation on static devices */
@media (update: none) {
p {
animation: none;
}
}
/* Slow animation on e-ink */
@media (update: slow) {
p {
animation: slide 5s infinite alternate;
}
}
/* Fast animation on modern screens */
@media (update: fast) {
p {
animation: slide 1s infinite alternate;
}
}
3. video-dynamic-range
The video-dynamic-range media feature queries the dynamic range of the video plane — specifically for HDR video content.
@media (video-dynamic-range: high) {
h1 {
color: blue;
}
}
Values
| Value | Description |
|---|---|
standard | Standard Dynamic Range (SDR) — most displays |
high | High Dynamic Range (HDR) — supports HDR video |
How It Differs from dynamic-range
| Feature | Target |
|---|---|
dynamic-range | The whole output device (display) |
video-dynamic-range | The video plane specifically |
Key Points:
video-dynamic-rangeis for video content (HDR video playback)dynamic-rangeis for the display itself- A device may support HDR video but not have a high-dynamic-range display
- Limited browser support — always provide a
standardfallback
HDR Requirements
| Requirement | Minimum |
|---|---|
| Color depth | > 24-bit |
| Peak brightness | High (1000+ nits) |
| Contrast ratio | High |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>scripting, update, and video-dynamic-range</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
:root {
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
--text-primary: #212529;
--text-secondary: #6c757d;
--accent: #007bff;
--border: #dee2e6;
--shadow: rgba(0, 0, 0, 0.1);
}
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #1a1a1a;
--bg-secondary: #0d0d0d;
--bg-tertiary: #2d2d2d;
--text-primary: #f8f9fa;
--text-secondary: #adb5bd;
--accent: #4dabf7;
--border: #444;
--shadow: rgba(0, 0, 0, 0.5);
}
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: var(--bg-secondary);
color: var(--text-primary);
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
transition: background 0.3s, color 0.3s;
}
h1 {
color: var(--accent);
border-bottom: 3px solid var(--accent);
padding-bottom: 10px;
}
h2 {
color: var(--accent);
border-left: 4px solid var(--accent);
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: var(--text-primary);
margin-top: 20px;
}
section {
background: var(--bg-primary);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px var(--shadow);
margin: 20px 0;
transition: all 0.3s;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid var(--border);
text-align: left;
}
.reference-table th {
background: var(--accent);
color: white;
}
.reference-table tr:nth-child(even) {
background: var(--bg-tertiary);
}
.note {
font-size: 0.9rem;
color: var(--text-secondary);
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== SCRIPTING DEMO ====== */
.scripting-demo {
padding: 20px;
border-radius: 8px;
margin: 15px 0;
border: 2px solid var(--border);
background: var(--bg-tertiary);
text-align: center;
font-weight: bold;
font-size: 1.1rem;
transition: all 0.3s;
}
/* No scripting */
@media (scripting: none) {
.scripting-demo {
background: #dc3545;
color: white;
border-color: #a71d2a;
}
.scripting-demo::after {
content: " (scripting disabled)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Initial-only scripting */
@media (scripting: initial-only) {
.scripting-demo {
background: #ffc107;
color: #333;
border-color: #d39e00;
}
.scripting-demo::after {
content: " (scripting available only on initial load)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Full scripting */
@media (scripting: enabled) {
.scripting-demo {
background: #28a745;
color: white;
border-color: #1e7e34;
}
.scripting-demo::after {
content: " (scripting fully enabled)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== UPDATE DEMO ====== */
.update-demo {
padding: 20px;
border-radius: 8px;
margin: 15px 0;
border: 2px solid var(--border);
background: var(--bg-tertiary);
text-align: center;
font-weight: bold;
font-size: 1.1rem;
transition: all 0.3s;
overflow: hidden;
position: relative;
min-height: 80px;
display: flex;
align-items: center;
justify-content: center;
}
@keyframes slide {
from {
transform: translateX(-100%);
}
to {
transform: translateX(100%);
}
}
.update-demo .animated-content {
display: inline-block;
white-space: nowrap;
}
/* No update capability */
@media (update: none) {
.update-demo {
background: #dc3545;
color: white;
border-color: #a71d2a;
}
.update-demo .animated-content {
animation: none;
}
.update-demo::after {
content: " (no update capability — animation disabled)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Slow update */
@media (update: slow) {
.update-demo {
background: #ffc107;
color: #333;
border-color: #d39e00;
}
.update-demo .animated-content {
animation: slide 5s infinite alternate;
}
.update-demo::after {
content: " (slow update — slow animation)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* Fast update */
@media (update: fast) {
.update-demo {
background: #28a745;
color: white;
border-color: #1e7e34;
}
.update-demo .animated-content {
animation: slide 1s infinite alternate;
}
.update-demo::after {
content: " (fast update — smooth animation)";
font-weight: normal;
font-size: 0.85rem;
}
}
/* ====== VIDEO-DYNAMIC-RANGE DEMO ====== */
.hdr-demo {
border-radius: 12px;
overflow: hidden;
margin: 15px 0;
}
.hdr-demo .video-container {
width: 100%;
height: 250px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
font-weight: bold;
color: white;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
transition: all 0.3s;
/* Standard dynamic range */
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
/* High dynamic range video */
@media (video-dynamic-range: high) {
.hdr-demo .video-container {
background: linear-gradient(135deg,
oklch(69% 0.27 240),
oklch(69% 0.27 300),
oklch(69% 0.27 30));
box-shadow: 0 0 40px rgba(108, 92, 231, 0.5);
}
.hdr-demo .video-container::after {
content: " (HDR video enhanced ✨)";
font-size: 1rem;
font-weight: normal;
display: block;
margin-top: 5px;
}
}
/* ====== DETECTION CARDS ====== */
.detection-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.detection-card {
padding: 15px;
border-radius: 8px;
text-align: center;
border: 2px solid var(--border);
background: var(--bg-tertiary);
transition: all 0.3s;
}
.detection-card .label {
font-size: 0.85rem;
color: var(--text-secondary);
display: block;
margin-bottom: 5px;
}
.detection-card .value {
font-size: 1.1rem;
font-weight: bold;
color: var(--accent);
}
.detection-card.active {
border-color: var(--accent);
background: var(--bg-primary);
}
/* ====== LIVE INDICATOR ====== */
.live-indicator {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--accent);
color: white;
padding: 12px 20px;
border-radius: 50px;
font-weight: bold;
font-size: 0.85rem;
box-shadow: 0 4px 15px var(--shadow);
z-index: 1000;
transition: all 0.3s;
display: flex;
flex-direction: column;
gap: 3px;
}
.live-indicator .detail {
font-size: 0.75rem;
opacity: 0.9;
}
/* ====== NOSCRIPT FALLBACK ====== */
.noscript-message {
display: none;
padding: 15px;
background: #f8d7da;
border: 2px solid #dc3545;
border-radius: 8px;
color: #721c24;
margin: 15px 0;
font-weight: bold;
}
@media (scripting: none) {
.noscript-message {
display: block;
}
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid var(--accent);
background: var(--bg-tertiary);
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>scripting, update, and video-dynamic-range</h1>
<!-- ====== NOSCRIPT MESSAGE ====== -->
<div class="noscript-message">
⚠️ JavaScript is disabled in your browser. Some features may not work correctly.
</div>
<!-- ====== 1. SCRIPTING DEMO ====== -->
<section>
<h2>1. scripting</h2>
<p>Detects whether <strong>JavaScript is available</strong> in the browser.</p>
<div class="scripting-demo">
Scripting Detection
</div>
<div class="code-block">
/* No scripting */
@media (scripting: none) {
.demo {
background: #dc3545;
}
}
/* Initial-only scripting */
@media (scripting: initial-only) {
.demo {
background: #ffc107;
}
}
/* Full scripting */
@media (scripting: enabled) {
.demo {
background: #28a745;
}
}
</div>
<p class="note">Use this to style fallback content for users with JavaScript disabled.</p>
</section>
<!-- ====== 2. UPDATE DEMO ====== -->
<section>
<h2>2. update</h2>
<p>Detects how <strong>frequently the display can update</strong> content.</p>
<div class="update-demo">
<span class="animated-content">🚀 Animation Speed Demo</span>
</div>
<div class="code-block">
/* No update capability — disable animation */
@media (update: none) {
.demo .animated-content {
animation: none;
}
}
/* Slow update — slow animation */
@media (update: slow) {
.demo .animated-content {
animation: slide 5s infinite alternate;
}
}
/* Fast update — smooth animation */
@media (update: fast) {
.demo .animated-content {
animation: slide 1s infinite alternate;
}
}
</div>
<p class="note">E-ink displays have <code>update: slow</code> — animations should be infrequent. Print has <code>update: none</code> — no animation at all.</p>
</section>
<!-- ====== 3. VIDEO-DYNAMIC-RANGE DEMO ====== -->
<section>
<h2>3. video-dynamic-range</h2>
<p>Detects the <strong>dynamic range of the video plane</strong> — specifically for HDR video.</p>
<div class="hdr-demo">
<div class="video-container">
🎬 HDR Video Demo
</div>
</div>
<div class="code-block">
/* Standard dynamic range video */
@media (video-dynamic-range: standard) {
.video {
/* SDR styles */
}
}
/* High dynamic range video */
@media (video-dynamic-range: high) {
.video {
background: linear-gradient(135deg,
oklch(69% 0.27 240),
oklch(69% 0.27 300));
}
}
</div>
<div class="tip-box warning">
<strong>⚠️ Limited Availability:</strong>
<code>video-dynamic-range</code> has limited browser support. Always provide a <code>standard</code> fallback.
</div>
</section>
<!-- ====== 4. DETECTION SUMMARY ====== -->
<section>
<h2>4. Your Device's Capabilities</h2>
<p>Below is a live detection of your device's capabilities.</p>
<div class="detection-grid">
<div class="detection-card" id="scripting-none">
<span class="label">scripting: none</span>
<span class="value" id="scripting-none-value">Detecting...</span>
</div>
<div class="detection-card" id="scripting-initial">
<span class="label">scripting: initial-only</span>
<span class="value" id="scripting-initial-value">Detecting...</span>
</div>
<div class="detection-card" id="scripting-enabled">
<span class="label">scripting: enabled</span>
<span class="value" id="scripting-enabled-value">Detecting...</span>
</div>
<div class="detection-card" id="update-none">
<span class="label">update: none</span>
<span class="value" id="update-none-value">Detecting...</span>
</div>
<div class="detection-card" id="update-slow">
<span class="label">update: slow</span>
<span class="value" id="update-slow-value">Detecting...</span>
</div>
<div class="detection-card" id="update-fast">
<span class="label">update: fast</span>
<span class="value" id="update-fast-value">Detecting...</span>
</div>
<div class="detection-card" id="vdr-standard">
<span class="label">video-dynamic-range: standard</span>
<span class="value" id="vdr-standard-value">Detecting...</span>
</div>
<div class="detection-card" id="vdr-high">
<span class="label">video-dynamic-range: high</span>
<span class="value" id="vdr-high-value">Detecting...</span>
</div>
</div>
<div class="code-block">
/* Detect scripting */
@media (scripting: none) { /* No JavaScript */ }
@media (scripting: initial-only) { /* Initial load only */ }
@media (scripting: enabled) { /* Full JavaScript */ }
/* Detect update capability */
@media (update: none) { /* No update — static */ }
@media (update: slow) { /* Slow update — e-ink */ }
@media (update: fast) { /* Fast update — modern */ }
/* Detect video dynamic range */
@media (video-dynamic-range: standard) { /* SDR video */ }
@media (video-dynamic-range: high) { /* HDR video */ }
</div>
</section>
<!-- ====== 5. REFERENCE TABLES ====== -->
<section>
<h2>5. Reference Tables</h2>
<h3>scripting Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Device Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Scripting not available</td>
<td>JavaScript disabled, some email clients</td>
</tr>
<tr>
<td><code>initial-only</code></td>
<td>Scripting only during initial load</td>
<td>Print preview, some email clients</td>
</tr>
<tr>
<td><code>enabled</code></td>
<td>Scripting fully supported (default)</td>
<td>Modern browsers</td>
</tr>
</table>
<h3>update Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Device Example</th>
</tr>
<tr>
<td><code>none</code></td>
<td>Content cannot be updated after rendering</td>
<td>Print, static e-ink</td>
</tr>
<tr>
<td><code>slow</code></td>
<td>Updates possible but not smooth</td>
<td>E-ink readers, low-power devices</td>
</tr>
<tr>
<td><code>fast</code></td>
<td>Content updates quickly (default)</td>
<td>Modern screens (60Hz+)</td>
</tr>
</table>
<h3>video-dynamic-range Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
<th>Requirements</th>
</tr>
<tr>
<td><code>standard</code></td>
<td>Standard Dynamic Range (SDR)</td>
<td>Typical displays</td>
</tr>
<tr>
<td><code>high</code></td>
<td>High Dynamic Range (HDR)</td>
<td>Color depth > 24-bit, high brightness, high contrast</td>
</tr>
</table>
<h3>dynamic-range vs video-dynamic-range</h3>
<table class="reference-table">
<tr>
<th>Feature</th>
<th>Target</th>
</tr>
<tr>
<td><code>dynamic-range</code></td>
<td>The whole output device (display)</td>
</tr>
<tr>
<td><code>video-dynamic-range</code></td>
<td>The video plane specifically</td>
</tr>
</table>
</section>
<!-- ====== 6. BEST PRACTICES ====== -->
<section>
<h2>6. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>scripting</code> to style fallbacks for users with JavaScript disabled</li>
<li>Use <code>update</code> to conditionally enable animations based on device capability</li>
<li>Use <code>video-dynamic-range</code> to enhance HDR video playback</li>
<li>Always provide a working fallback for each feature</li>
<li>Test with JavaScript disabled in your browser</li>
<li>Test on e-ink devices or simulate slow updates</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't rely solely on <code>scripting</code> for critical functionality</li>
<li>Don't use fast animations on <code>update: slow</code> devices</li>
<li>Don't use <code>video-dynamic-range</code> without a <code>standard</code> fallback</li>
<li>Don't confuse <code>dynamic-range</code> with <code>video-dynamic-range</code></li>
<li>Don't forget that <code>update: none</code> means no animation at all</li>
</ul>
</div>
</section>
<!-- ====== LIVE INDICATOR ====== -->
<div class="live-indicator" id="live-indicator">
<span id="indicator-scripting">Scripting: —</span>
<span class="detail" id="indicator-update">Update: —</span>
<span class="detail" id="indicator-vdr">Video DR: —</span>
</div>
<!-- ====== SCRIPT FOR DETECTION ====== -->
<script>
function updateIndicators() {
function setCard(card, isActive, activeText, inactiveText) {
const value = card.querySelector('.value');
if (isActive) {
value.textContent = activeText;
card.classList.add('active');
} else {
value.textContent = inactiveText;
card.classList.remove('active');
}
}
// Scripting detection
const scriptingNone = document.getElementById('scripting-none');
const scriptingInitial = document.getElementById('scripting-initial');
const scriptingEnabled = document.getElementById('scripting-enabled');
const isScriptingNone = window.matchMedia('(scripting: none)').matches;
const isScriptingInitial = window.matchMedia('(scripting: initial-only)').matches;
const isScriptingEnabled = window.matchMedia('(scripting: enabled)').matches;
setCard(scriptingNone, isScriptingNone, 'Active ✅', 'Not active');
setCard(scriptingInitial, isScriptingInitial, 'Active ✅', 'Not active');
setCard(scriptingEnabled, isScriptingEnabled, 'Active ✅', 'Not active');
// Update detection
const updateNone = document.getElementById('update-none');
const updateSlow = document.getElementById('update-slow');
const updateFast = document.getElementById('update-fast');
const isUpdateNone = window.matchMedia('(update: none)').matches;
const isUpdateSlow = window.matchMedia('(update: slow)').matches;
const isUpdateFast = window.matchMedia('(update: fast)').matches;
setCard(updateNone, isUpdateNone, 'Active ✅', 'Not active');
setCard(updateSlow, isUpdateSlow, 'Active ✅', 'Not active');
setCard(updateFast, isUpdateFast, 'Active ✅', 'Not active');
// Video dynamic range detection
const vdrStandard = document.getElementById('vdr-standard');
const vdrHigh = document.getElementById('vdr-high');
const isVdrStandard = window.matchMedia('(video-dynamic-range: standard)').matches;
const isVdrHigh = window.matchMedia('(video-dynamic-range: high)').matches;
setCard(vdrStandard, isVdrStandard, 'Active ✅', 'Not active');
setCard(vdrHigh, isVdrHigh, 'Active ✅', 'Not active');
// Live indicator
const indicatorScripting = document.getElementById('indicator-scripting');
const indicatorUpdate = document.getElementById('indicator-update');
const indicatorVdr = document.getElementById('indicator-vdr');
const indicator = document.getElementById('live-indicator');
let scriptingText = 'Unknown';
if (isScriptingEnabled) scriptingText = 'Enabled ✅';
else if (isScriptingInitial) scriptingText = 'Initial only ⚠️';
else if (isScriptingNone) scriptingText = 'None ❌';
indicatorScripting.textContent = 'JS: ' + scriptingText;
let updateText = 'Unknown';
if (isUpdateFast) updateText = 'Fast ⚡';
else if (isUpdateSlow) updateText = 'Slow 🐌';
else if (isUpdateNone) updateText = 'None 🚫';
indicatorUpdate.textContent = 'Update: ' + updateText;
let vdrText = 'Unknown';
if (isVdrHigh) vdrText = 'HDR ✨';
else if (isVdrStandard) vdrText = 'SDR';
indicatorVdr.textContent = 'Video DR: ' + vdrText;
// Color indicator
if (isUpdateFast && isScriptingEnabled) {
indicator.style.background = '#28a745';
indicator.style.color = 'white';
} else if (isUpdateSlow || isScriptingNone) {
indicator.style.background = '#ffc107';
indicator.style.color = '#333';
} else {
indicator.style.background = '#007bff';
indicator.style.color = 'white';
}
}
window.addEventListener('load', updateIndicators);
// Listen for changes
window.matchMedia('(scripting: enabled)').addEventListener('change', updateIndicators);
window.matchMedia('(update: fast)').addEventListener('change', updateIndicators);
window.matchMedia('(video-dynamic-range: high)').addEventListener('change', updateIndicators);
updateIndicators();
</script>
</body>
</html>
Quick Reference
| Feature | Description | Values |
|---|---|---|
scripting | JavaScript availability | none, initial-only, enabled |
update | Display update capability | none, slow, fast |
video-dynamic-range | Dynamic range of video plane | standard, high |
scripting Values
| Value | Description | Device Example |
|---|---|---|
none | No scripting | JS disabled, some email clients |
initial-only | Scripting only on initial load | Print preview, some email clients |
enabled | Full scripting (default) | Modern browsers |
update Values
| Value | Description | Device Example |
|---|---|---|
none | Content is static after rendering | Print, static e-ink |
slow | Updates possible but not smooth | E-ink readers |
fast | Smooth updates (default) | Modern screens (60Hz+) |
video-dynamic-range Values
| Value | Description | Requirements |
|---|---|---|
standard | Standard Dynamic Range (SDR) | Typical displays |
high | High Dynamic Range (HDR) | Color depth > 24-bit, high brightness, high contrast |
Best Practices
✅ Do This:
/* Style fallbacks for no JavaScript */
@media (scripting: none) {
.js-only {
display: none;
}
.noscript-fallback {
display: block;
}
}
/* Conditionally enable animations */
@media (update: none) {
.animated {
animation: none;
}
}
@media (update: slow) {
.animated {
animation: slide 5s infinite alternate;
}
}
@media (update: fast) {
.animated {
animation: slide 1s infinite alternate;
}
}
/* Enhance HDR video */
@media (video-dynamic-range: high) {
.video {
background: linear-gradient(135deg, oklch(69% 0.27 240), oklch(69% 0.27 300));
}
}
❌ Don’t Do This:
/* Don't rely on JavaScript for critical functionality */
@media (scripting: enabled) {
.content {
display: block;
/* Content is hidden without JS! */
}
}
/* Don't use fast animations on slow-update devices */
@media (update: slow) {
.animated {
animation: slide 0.1s infinite; /* Will flicker terribly! */
}
}
/* Don't confuse dynamic-range with video-dynamic-range */
@media (video-dynamic-range: high) {
/* This is for video, not the whole display */
}
Pro Tip: scripting is essential for progressive enhancement — always provide a working fallback for users with JavaScript disabled. update is great for device-adaptive animations — e-ink readers can’t handle smooth animation, so use slow, infrequent transitions. video-dynamic-range is a specialized feature for HDR video content — it has limited browser support, so always provide a standard fallback. Remember: dynamic-range is for the display, while video-dynamic-range is for the video plane specifically!
45. Transform Property Introduction
The transform property is one of the most powerful tools in CSS. It lets you rotate, scale, skew, and translate elements — all without affecting the layout of other elements.
What is the transform Property?
The transform property applies 2D or 3D transformations to an element. Unlike properties like margin or position, transforms don’t affect the document flow — the element’s original space remains reserved.
Key Benefits
| Benefit | Description |
|---|---|
| No layout impact | Other elements stay in place |
| Hardware accelerated | Smooth performance |
| Combinable | Multiple transforms in one declaration |
| Animatable | Works with transition and animation |
| 2D and 3D | Supports both flat and spatial transformations |
Basic Syntax
.box {
transform: rotate(45deg);
}
You can combine multiple transform functions:
.box {
transform: rotate(45deg) scale(1.5) translate(20px, 10px);
}
Important: The order matters! Transforms are applied right to left (last function first).
Transform Functions Overview
| Category | Functions |
|---|---|
| Rotation | rotate(), rotate3d(), rotateX(), rotateY(), rotateZ() |
| Scaling | scale(), scale3d(), scaleX(), scaleY(), scaleZ() |
| Skewing | skew(), skewX(), skewY() |
| Translation | translate(), translate3d(), translateX(), translateY(), translateZ() |
| Matrix | matrix(), matrix3d() |
| Perspective | perspective() |
1. rotate()
Rotates an element around its origin (center by default).
.rotate {
transform: rotate(45deg);
}
- Positive values rotate clockwise
- Negative values rotate counter-clockwise
- Use
transform-originto change the rotation point
2. scale()
Scales an element — makes it larger or smaller.
.scale {
transform: scale(2, 1); /* 2x width, 1x height */
}
| Syntax | Effect |
|---|---|
scale(2) | 2x in both directions |
scale(2, 1) | 2x width, 1x height |
scale(0.5) | Half size |
scaleX(2) | 2x width only |
scaleY(0.5) | Half height only |
3. skew()
Skews an element — slants it along the X or Y axis.
.skew {
transform: skew(30deg, -20deg);
}
| Syntax | Effect |
|---|---|
skew(30deg) | Skew 30° along X axis |
skew(30deg, -20deg) | Skew X 30°, Y -20° |
skewX(30deg) | Skew X only |
skewY(20deg) | Skew Y only |
4. translate()
Moves an element from its original position.
.translate {
transform: translate(50px, 25px);
}
| Syntax | Effect |
|---|---|
translate(50px) | Move 50px right |
translate(50px, 25px) | Move 50px right, 25px down |
translateX(50px) | Move horizontally |
translateY(25px) | Move vertically |
translate(-50%, -50%) | Center an absolutely positioned element |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Transform Property Introduction</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== TRANSFORM DEMOS ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 250px;
}
.box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.9rem;
border-radius: 8px;
transition: transform 0.5s ease;
cursor: pointer;
text-align: center;
padding: 10px;
}
.box:hover {
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.4);
}
/* Individual transforms */
.rotate {
transform: rotate(45deg);
}
.rotate:hover {
transform: rotate(225deg);
}
.scale {
transform: scale(1.5, 1);
}
.scale:hover {
transform: scale(2, 2);
}
.skew {
transform: skew(20deg, -10deg);
}
.skew:hover {
transform: skew(0deg, 0deg);
}
.translate {
transform: translate(50px, 25px);
}
.translate:hover {
transform: translate(0, 0);
}
/* Combined transforms */
.combined-1 {
transform: rotate(45deg) scale(1.2);
}
.combined-2 {
transform: scale(1.5) rotate(-20deg) translate(10px, 10px);
}
.combined-3 {
transform: translate(20px, -20px) skew(10deg, 5deg) scale(0.9);
}
/* Origin demo */
.origin-center {
transform-origin: center center;
transform: rotate(45deg);
}
.origin-top-left {
transform-origin: top left;
transform: rotate(45deg);
}
.origin-bottom-right {
transform-origin: bottom right;
transform: rotate(45deg);
}
/* 3D transform demo */
.rotate-x {
transform: rotateX(60deg);
}
.rotate-y {
transform: rotateY(60deg);
}
.rotate-z {
transform: rotateZ(45deg);
}
/* 3D card flip */
.flip-container {
perspective: 1000px;
width: 200px;
height: 200px;
margin: 20px auto;
}
.flip-card {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.8s;
cursor: pointer;
}
.flip-container:hover .flip-card {
transform: rotateY(180deg);
}
.flip-front,
.flip-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.2rem;
color: white;
}
.flip-front {
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
.flip-back {
background: linear-gradient(135deg, #28a745, #20c997);
transform: rotateY(180deg);
}
/* Practical: button hover */
.btn-transform {
padding: 15px 30px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
margin: 5px;
}
.btn-transform:hover {
transform: translateY(-3px) scale(1.02);
box-shadow: 0 8px 20px rgba(0, 123, 255, 0.3);
}
.btn-transform:active {
transform: translateY(0) scale(0.98);
}
/* Practical: card hover */
.card-transform {
background: white;
border: 2px solid #ddd;
border-radius: 12px;
padding: 25px;
max-width: 300px;
margin: 15px auto;
transition: all 0.3s;
cursor: pointer;
text-align: center;
}
.card-transform:hover {
transform: translateY(-5px) scale(1.02);
border-color: #007bff;
box-shadow: 0 10px 30px rgba(0, 123, 255, 0.15);
}
/* Image gallery transforms */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin: 15px 0;
}
.gallery-item {
height: 150px;
border-radius: 8px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2rem;
transition: all 0.4s;
cursor: pointer;
overflow: hidden;
}
.gallery-item:hover {
transform: scale(1.1) rotate(3deg);
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.3);
z-index: 10;
}
/* Rotate animation */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spin {
animation: spin 3s linear infinite;
}
/* ====== DETECTION CARDS ====== */
.reference-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin: 20px 0;
}
.reference-card {
padding: 15px;
border-radius: 8px;
border: 2px solid #ddd;
background: #f8f9fa;
transition: all 0.3s;
}
.reference-card h4 {
margin-top: 0;
color: #007bff;
}
.reference-card code {
background: #e9ecef;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
}
</style>
</head>
<body>
<h1>Transform Property Introduction</h1>
<!-- ====== 1. BASIC TRANSFORMS ====== -->
<section>
<h2>1. Basic Transform Functions</h2>
<p>The <code>transform</code> property applies 2D or 3D transformations without affecting layout.</p>
<div class="demo-area">
<div class="box rotate">Rotate<br>45°</div>
<div class="box scale">Scale<br>2x, 1x</div>
<div class="box skew">Skew<br>20°, -10°</div>
<div class="box translate">Translate<br>50px, 25px</div>
</div>
<p class="note">Hover over each box to see the transform change!</p>
<div class="code-block">
/* Rotate 45 degrees */
.rotate {
transform: rotate(45deg);
}
/* Scale 2x width, 1x height */
.scale {
transform: scale(2, 1);
}
/* Skew 20° X, -10° Y */
.skew {
transform: skew(20deg, -10deg);
}
/* Move 50px right, 25px down */
.translate {
transform: translate(50px, 25px);
}
</div>
</section>
<!-- ====== 2. COMBINED TRANSFORMS ====== -->
<section>
<h2>2. Combining Multiple Transforms</h2>
<p>Multiple transform functions can be combined in a single declaration.</p>
<div class="demo-area">
<div class="box combined-1">Rotate + Scale</div>
<div class="box combined-2">Scale + Rotate + Translate</div>
<div class="box combined-3">Translate + Skew + Scale</div>
</div>
<div class="code-block">
/* Rotate then scale */
.combined-1 {
transform: rotate(45deg) scale(1.2);
}
/* Scale, rotate, then translate */
.combined-2 {
transform: scale(1.5) rotate(-20deg) translate(10px, 10px);
}
/* Translate, skew, then scale */
.combined-3 {
transform: translate(20px, -20px) skew(10deg, 5deg) scale(0.9);
}
</div>
<p class="note"><strong>Important:</strong> Transforms are applied <strong>right to left</strong> (last function first).</p>
</section>
<!-- ====== 3. TRANSFORM ORIGIN ====== -->
<section>
<h2>3. transform-origin</h2>
<p>Changes the point around which transforms are applied.</p>
<div class="demo-area">
<div class="box origin-center">center center<br>(default)</div>
<div class="box origin-top-left">top left</div>
<div class="box origin-bottom-right">bottom right</div>
</div>
<div class="code-block">
/* Default origin (center) */
.origin-center {
transform-origin: center center;
transform: rotate(45deg);
}
/* Rotate around top-left corner */
.origin-top-left {
transform-origin: top left;
transform: rotate(45deg);
}
/* Rotate around bottom-right corner */
.origin-bottom-right {
transform-origin: bottom right;
transform: rotate(45deg);
}
</div>
</section>
<!-- ====== 4. 3D TRANSFORMS ====== -->
<section>
<h2>4. 3D Transforms</h2>
<p>Transform functions like <code>rotateX()</code>, <code>rotateY()</code>, and <code>rotateZ()</code> work in 3D space.</p>
<div class="demo-area">
<div class="box rotate-x">rotateX<br>60°</div>
<div class="box rotate-y">rotateY<br>60°</div>
<div class="box rotate-z">rotateZ<br>45°</div>
</div>
<div class="code-block">
/* Rotate around X axis */
.rotate-x {
transform: rotateX(60deg);
}
/* Rotate around Y axis */
.rotate-y {
transform: rotateY(60deg);
}
/* Rotate around Z axis */
.rotate-z {
transform: rotateZ(45deg);
}
</div>
</section>
<!-- ====== 5. 3D CARD FLIP ====== -->
<section>
<h2>5. Practical Example: 3D Card Flip</h2>
<p>Hover over the card to see a 3D flip animation.</p>
<div class="flip-container">
<div class="flip-card">
<div class="flip-front">Front</div>
<div class="flip-back">Back</div>
</div>
</div>
<div class="code-block">
.flip-container {
perspective: 1000px;
}
.flip-card {
transform-style: preserve-3d;
transition: transform 0.8s;
}
.flip-container:hover .flip-card {
transform: rotateY(180deg);
}
.flip-back {
transform: rotateY(180deg);
backface-visibility: hidden;
}
</div>
</section>
<!-- ====== 6. PRACTICAL: BUTTON AND CARD HOVER ====== -->
<section>
<h2>6. Practical Examples</h2>
<h3>Button Hover</h3>
<div style="text-align: center; margin: 15px 0;">
<button class="btn-transform">Hover Me</button>
<button class="btn-transform">Click Me</button>
</div>
<h3>Card Hover</h3>
<div class="card-transform">
<h4 style="margin-top: 0; color: #007bff;">Interactive Card</h4>
<p>Hover over this card to see the transform effect.</p>
</div>
<h3>Image Gallery</h3>
<div class="gallery">
<div class="gallery-item">🖼️</div>
<div class="gallery-item">🎨</div>
<div class="gallery-item">📷</div>
<div class="gallery-item">🎭</div>
</div>
<div class="code-block">
/* Button hover */
.btn:hover {
transform: translateY(-3px) scale(1.02);
box-shadow: 0 8px 20px rgba(0, 123, 255, 0.3);
}
/* Card hover */
.card:hover {
transform: translateY(-5px) scale(1.02);
box-shadow: 0 10px 30px rgba(0, 123, 255, 0.15);
}
/* Gallery item hover */
.gallery-item:hover {
transform: scale(1.1) rotate(3deg);
z-index: 10;
}
</div>
</section>
<!-- ====== 7. ANIMATED TRANSFORMS ====== -->
<section>
<h2>7. Animated Transforms</h2>
<p>Transforms can be animated with <code>transition</code> or <code>animation</code>.</p>
<div class="demo-area">
<div class="box spin">Spinning</div>
</div>
<div class="code-block">
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spin {
animation: spin 3s linear infinite;
}
</div>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>Transform Functions</h3>
<table class="reference-table">
<tr>
<th>Category</th>
<th>Functions</th>
<th>Description</th>
</tr>
<tr>
<td><strong>Rotation</strong></td>
<td><code>rotate()</code>, <code>rotate3d()</code>, <code>rotateX()</code>, <code>rotateY()</code>, <code>rotateZ()</code></td>
<td>Rotate around an axis</td>
</tr>
<tr>
<td><strong>Scaling</strong></td>
<td><code>scale()</code>, <code>scale3d()</code>, <code>scaleX()</code>, <code>scaleY()</code>, <code>scaleZ()</code></td>
<td>Resize the element</td>
</tr>
<tr>
<td><strong>Skewing</strong></td>
<td><code>skew()</code>, <code>skewX()</code>, <code>skewY()</code></td>
<td>Slant the element</td>
</tr>
<tr>
<td><strong>Translation</strong></td>
<td><code>translate()</code>, <code>translate3d()</code>, <code>translateX()</code>, <code>translateY()</code>, <code>translateZ()</code></td>
<td>Move the element</td>
</tr>
<tr>
<td><strong>Matrix</strong></td>
<td><code>matrix()</code>, <code>matrix3d()</code></td>
<td>Complex transformations</td>
</tr>
<tr>
<td><strong>Perspective</strong></td>
<td><code>perspective()</code></td>
<td>3D perspective effect</td>
</tr>
</table>
<h3>Common Transform Values</h3>
<table class="reference-table">
<tr>
<th>Function</th>
<th>Example</th>
<th>Effect</th>
</tr>
<tr>
<td><code>rotate()</code></td>
<td><code>rotate(45deg)</code></td>
<td>Rotate 45° clockwise</td>
</tr>
<tr>
<td><code>scale()</code></td>
<td><code>scale(2, 1)</code></td>
<td>2x width, 1x height</td>
</tr>
<tr>
<td><code>skew()</code></td>
<td><code>skew(30deg, -20deg)</code></td>
<td>Skew 30° X, -20° Y</td>
</tr>
<tr>
<td><code>translate()</code></td>
<td><code>translate(50px, 25px)</code></td>
<td>Move 50px right, 25px down</td>
</tr>
</table>
<h3>Related Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
</tr>
<tr>
<td><code>transform-origin</code></td>
<td>Sets the origin point for transforms</td>
</tr>
<tr>
<td><code>transform-style</code></td>
<td>Preserves 3D positioning (<code>preserve-3d</code>)</td>
</tr>
<tr>
<td><code>perspective</code></td>
<td>Sets the 3D perspective for children</td>
</tr>
<tr>
<td><code>backface-visibility</code></td>
<td>Hides the back face of a 3D element</td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>transform</code> for animations instead of <code>top</code>/<code>left</code> (better performance)</li>
<li>Combine multiple transforms in one declaration</li>
<li>Use <code>transform-origin</code> to control the pivot point</li>
<li>Add <code>transition</code> for smooth animations</li>
<li>Use <code>translate(-50%, -50%)</code> to center elements</li>
<li>Use <code>will-change: transform</code> for frequently animated elements</li>
<li>Test transforms on different devices and browsers</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't overuse transforms — they can cause motion sickness</li>
<li>Don't forget <code>prefers-reduced-motion</code> for accessibility</li>
<li>Don't animate transforms that cause layout shifts</li>
<li>Don't use transforms on elements with fixed positioning without testing</li>
<li>Don't forget that transforms create a new stacking context</li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.animated {
transition: none;
animation: none;
transform: none;
}
}
/* Performance: hint the browser */
.will-animate {
will-change: transform;
}
</div>
</section>
</body>
</html>
Quick Reference
| Function | Example | Effect |
|---|---|---|
rotate() | rotate(45deg) | Rotate 45° clockwise |
scale() | scale(2, 1) | 2x width, 1x height |
skew() | skew(30deg, -20deg) | Skew 30° X, -20° Y |
translate() | translate(50px, 25px) | Move 50px right, 25px down |
Transform Categories
| Category | Functions |
|---|---|
| Rotation | rotate(), rotate3d(), rotateX(), rotateY(), rotateZ() |
| Scaling | scale(), scale3d(), scaleX(), scaleY(), scaleZ() |
| Skewing | skew(), skewX(), skewY() |
| Translation | translate(), translate3d(), translateX(), translateY(), translateZ() |
| Matrix | matrix(), matrix3d() |
| Perspective | perspective() |
Related Properties
| Property | Description |
|---|---|
transform-origin | Sets the origin point for transforms |
transform-style | Preserves 3D positioning (preserve-3d) |
perspective | Sets 3D perspective for children |
backface-visibility | Hides the back face of a 3D element |
Best Practices
✅ Do This:
/* Combine transforms */
.box {
transform: rotate(45deg) scale(1.5) translate(20px, 10px);
}
/* Use transform for animations (better performance) */
.box:hover {
transform: translateY(-5px);
transition: transform 0.3s;
}
/* Center with translate */
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.animated {
transform: none;
transition: none;
}
}
❌ Don’t Do This:
/* Don't animate top/left (worse performance) */
.box:hover {
top: -5px;
transition: top 0.3s;
}
/* Don't forget transform-origin */
.box {
transform: rotate(45deg);
/* Rotates around center by default */
}
/* Don't overuse transforms */
.everything {
transform: rotate(5deg) scale(1.1) skew(2deg);
}
Pro Tip: The transform property is one of the most powerful tools in CSS. Use it for animations instead of top/left — transforms are hardware-accelerated and don’t cause layout recalculations. Combine multiple transforms in one declaration (e.g., transform: rotate(45deg) scale(1.5)), and remember that the order matters — transforms are applied right to left. Use transform-origin to change the pivot point, and always respect prefers-reduced-motion for accessibility!
46. transform matrix() and matrix3d()
The matrix() and matrix3d() functions are the most powerful — and most complex — transform functions in CSS. They let you apply exact mathematical transformations using matrices.
Overview
| Function | Dimensions | Matrix Size | Values |
|---|---|---|---|
matrix() | 2D | 3×3 (6 values) | a, b, c, d, e, f |
matrix3d() | 3D | 4×4 (16 values) | a through p |
1. matrix() — 2D Transformations
The matrix() function applies a 2D transformation using a 3×3 matrix (represented by 6 values).
.box {
transform: matrix(a, b, c, d, e, f);
}
Matrix Structure
| a c e |
| b d f |
| 0 0 1 |
| Value | Controls | Description |
|---|---|---|
a | Scale X | Horizontal scaling |
b | Skew Y | Vertical skew |
c | Skew X | Horizontal skew |
d | Scale Y | Vertical scaling |
e | Translate X | Horizontal movement |
f | Translate Y | Vertical movement |
2D Matrix Examples
Translation
.translated {
transform: matrix(1, 0, 0, 1, 100, 100);
}
a=1, d=1→ No scalingb=0, c=0→ No skewinge=100, f=100→ Move 100px right and down
Equivalent to: translate(100px, 100px)
Rotation (45°)
.rotated {
transform: matrix(0.707, 0.707, -0.707, 0.707, 0, 0);
}
0.707= cos(45°) ≈ sin(45°)a=cos(θ),b=sin(θ),c=-sin(θ),d=cos(θ)
Equivalent to: rotate(45deg)
Scaling and Skewing
.transformed {
transform: matrix(1, 0.3, -0.3, 1, 0, 0);
}
a=1, d=1→ No scalingb=0.3→ Skew Y by ~16.7°c=-0.3→ Skew X by ~-16.7°
Equivalent to: skew(16.7deg, -16.7deg)
2. matrix3d() — 3D Transformations
The matrix3d() function applies a 3D transformation using a 4×4 matrix (16 values).
.box {
transform: matrix3d(
a, b, c, d,
e, f, g, h,
i, j, k, l,
m, n, o, p
);
}
Matrix Structure
| a e i m |
| b f j n |
| c g k o |
| d h l p |
| Group | Values | Controls |
|---|---|---|
| Linear transformation | a, b, c, e, f, g, i, j, k | Scale, rotate, skew |
| Translation | m, n, o | X, Y, Z movement |
| Perspective | d, h, l | Perspective effects |
| Homogeneous | p | Usually 1 |
3D Matrix Examples
3D Rotation (45° around Y axis)
.rotated3d {
transform: matrix3d(
cos(45deg), 0, sin(45deg), 0,
0, 1, 0, 0,
-sin(45deg), 0, cos(45deg), 0,
0, 0, 0, 1
);
}
- Rotates around the Y axis
cos(45°)andsin(45°)create the rotation
Equivalent to: rotateY(45deg)
3D Scaling and Translation
.transformed3d {
transform: matrix3d(
2, 0, 0, 0,
0, 2, 0, 0,
0, 0, 1, 0,
100, 100, 0, 1
);
}
a=2, f=2→ Scale 2x in X and Ym=100, n=100→ Move 100px right and down
Equivalent to: translate(100px, 100px) scale(2, 2)
3D Skewing
.skewed {
transform: matrix3d(
1, 0.3, 0, 0,
-0.3, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
b=0.3→ Skew Ye=-0.3→ Skew X
Equivalent to: skew(16.7deg, -16.7deg)
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transform matrix() and matrix3d()</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 300px;
perspective: 800px;
}
.box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #28a745, #20c997);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.8rem;
border-radius: 8px;
transition: transform 0.5s ease-in-out;
cursor: pointer;
text-align: center;
padding: 5px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.box:hover {
box-shadow: 0 8px 25px rgba(40, 167, 69, 0.4);
}
/* 2D Matrix transforms */
.translated {
transform: matrix(1, 0, 0, 1, 100, 100);
}
.translated:hover {
transform: matrix(1, 0, 0, 1, 150, 50);
}
.rotated {
transform: matrix(0.707, 0.707, -0.707, 0.707, 0, 0);
}
.rotated:hover {
transform: matrix(0.707, 0.707, -0.707, 0.707, 50, 50);
}
.transformed {
transform: matrix(1, 0.3, -0.3, 1, 0, 0);
}
.transformed:hover {
transform: matrix(1.2, 0.5, -0.5, 1.2, 20, 20);
}
/* 3D Matrix transforms */
.rotated3d {
transform: matrix3d(
cos(45deg), 0, sin(45deg), 0,
0, 1, 0, 0,
-sin(45deg), 0, cos(45deg), 0,
0, 0, 0, 1
);
}
.rotated3d:hover {
transform: matrix3d(
cos(90deg), 0, sin(90deg), 0,
0, 1, 0, 0,
-sin(90deg), 0, cos(90deg), 0,
0, 0, 0, 1
);
}
.transformed3d {
transform: matrix3d(
2, 0, 0, 0,
0, 2, 0, 0,
0, 0, 1, 0,
100, 100, 0, 1
);
}
.transformed3d:hover {
transform: matrix3d(
2.5, 0, 0, 0,
0, 2.5, 0, 0,
0, 0, 1, 0,
50, 50, 0, 1
);
}
.skewed {
transform: matrix3d(
1, 0.3, 0, 0,
-0.3, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
.skewed:hover {
transform: matrix3d(
1, 0.5, 0, 0,
-0.5, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
/* ====== COMPARISON GRID ====== */
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin: 15px 0;
}
.comparison-card {
background: white;
border: 2px solid #ddd;
border-radius: 8px;
padding: 20px;
text-align: center;
transition: all 0.3s;
}
.comparison-card:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.15);
}
.comparison-card h4 {
margin-top: 0;
color: #007bff;
}
.comparison-card .matrix-display {
background: #1e1e1e;
color: #d4d4d4;
padding: 10px;
border-radius: 6px;
font-family: 'Courier New', monospace;
font-size: 0.75rem;
margin: 10px 0;
white-space: pre;
text-align: left;
overflow-x: auto;
}
/* ====== MATRIX VISUALIZATION ====== */
.matrix-viz {
display: flex;
gap: 20px;
flex-wrap: wrap;
justify-content: center;
margin: 20px 0;
}
.matrix-box {
display: grid;
grid-template-columns: repeat(3, 50px);
gap: 5px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 2px solid #ddd;
}
.matrix-box .cell {
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
background: #007bff;
color: white;
border-radius: 4px;
font-weight: bold;
font-size: 0.8rem;
}
.matrix-box .cell.highlight {
background: #ffc107;
color: #333;
}
.matrix-box .cell.scale {
background: #28a745;
}
.matrix-box .cell.skew {
background: #dc3545;
}
.matrix-box .cell.translate {
background: #6c5ce7;
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>transform matrix() and matrix3d()</h1>
<!-- ====== 1. MATRIX() DEMO ====== -->
<section>
<h2>1. matrix() — 2D Transformations</h2>
<p>The <code>matrix()</code> function applies a 2D transformation using 6 values.</p>
<div class="demo-area">
<div class="box translated">matrix<br>Translate</div>
<div class="box rotated">matrix<br>Rotate</div>
<div class="box transformed">matrix<br>Skew + Scale</div>
</div>
<p class="note">Hover over each box to see the transform change!</p>
<div class="code-block">
/* Translation: move 100px right and down */
.translated {
transform: matrix(1, 0, 0, 1, 100, 100);
}
/* Rotation: 45 degrees */
.rotated {
transform: matrix(0.707, 0.707, -0.707, 0.707, 0, 0);
}
/* Skew + Scale */
.transformed {
transform: matrix(1, 0.3, -0.3, 1, 0, 0);
}
</div>
</section>
<!-- ====== 2. MATRIX3D() DEMO ====== -->
<section>
<h2>2. matrix3d() — 3D Transformations</h2>
<p>The <code>matrix3d()</code> function applies a 3D transformation using 16 values.</p>
<div class="demo-area">
<div class="box rotated3d">matrix3d<br>Rotate Y</div>
<div class="box transformed3d">matrix3d<br>Scale + Translate</div>
<div class="box skewed">matrix3d<br>Skew</div>
</div>
<div class="code-block">
/* 3D Rotation around Y axis */
.rotated3d {
transform: matrix3d(
cos(45deg), 0, sin(45deg), 0,
0, 1, 0, 0,
-sin(45deg), 0, cos(45deg), 0,
0, 0, 0, 1
);
}
/* 3D Scale + Translate */
.transformed3d {
transform: matrix3d(
2, 0, 0, 0,
0, 2, 0, 0,
0, 0, 1, 0,
100, 100, 0, 1
);
}
/* 3D Skew */
.skewed {
transform: matrix3d(
1, 0.3, 0, 0,
-0.3, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
</div>
</section>
<!-- ====== 3. MATRIX VISUALIZATION ====== -->
<section>
<h2>3. Matrix Structure Visualization</h2>
<h3>2D Matrix (3×3)</h3>
<div class="matrix-viz">
<div class="matrix-box">
<div class="cell scale">a</div>
<div class="cell skew">c</div>
<div class="cell translate">e</div>
<div class="cell skew">b</div>
<div class="cell scale">d</div>
<div class="cell translate">f</div>
<div class="cell">0</div>
<div class="cell">0</div>
<div class="cell highlight">1</div>
</div>
<div style="max-width: 300px;">
<p><strong>a, d</strong> — Scale X and Y</p>
<p><strong>b, c</strong> — Skew Y and X</p>
<p><strong>e, f</strong> — Translate X and Y</p>
</div>
</div>
<div class="code-block">
/* 2D matrix structure */
transform: matrix(a, b, c, d, e, f);
/* Represented as:
| a c e |
| b d f |
| 0 0 1 |
*/
</div>
</section>
<!-- ====== 4. COMPARISON ====== -->
<section>
<h2>4. matrix() vs matrix3d()</h2>
<div class="comparison-grid">
<div class="comparison-card">
<h4>matrix()</h4>
<div class="matrix-display">| a c e |
| b d f |
| 0 0 1 |</div>
<p><strong>6 values</strong></p>
<p>2D transformations</p>
<p>Scale, rotate, skew, translate</p>
</div>
<div class="comparison-card">
<h4>matrix3d()</h4>
<div class="matrix-display">| a e i m |
| b f j n |
| c g k o |
| d h l p |</div>
<p><strong>16 values</strong></p>
<p>3D transformations</p>
<p>Adds Z-axis and perspective</p>
</div>
</div>
</section>
<!-- ====== 5. FUNCTION EQUIVALENTS ====== -->
<section>
<h2>5. Function Equivalents</h2>
<p>Many transform functions are shortcuts for matrix values.</p>
<table class="reference-table">
<tr>
<th>Function</th>
<th>Matrix Equivalent</th>
</tr>
<tr>
<td><code>translate(100px, 50px)</code></td>
<td><code>matrix(1, 0, 0, 1, 100, 50)</code></td>
</tr>
<tr>
<td><code>scale(2, 2)</code></td>
<td><code>matrix(2, 0, 0, 2, 0, 0)</code></td>
</tr>
<tr>
<td><code>rotate(45deg)</code></td>
<td><code>matrix(0.707, 0.707, -0.707, 0.707, 0, 0)</code></td>
</tr>
<tr>
<td><code>skew(30deg, 0)</code></td>
<td><code>matrix(1, 0, 0.577, 1, 0, 0)</code></td>
</tr>
<tr>
<td><code>rotateY(45deg)</code></td>
<td><code>matrix3d(cos(45deg), 0, sin(45deg), 0, 0, 1, 0, 0, -sin(45deg), 0, cos(45deg), 0, 0, 0, 0, 1)</code></td>
</tr>
</table>
<div class="code-block">
/* These are equivalent */
transform: translate(100px, 50px);
transform: matrix(1, 0, 0, 1, 100, 50);
/* These are equivalent */
transform: scale(2, 2);
transform: matrix(2, 0, 0, 2, 0, 0);
/* These are equivalent */
transform: rotate(45deg);
transform: matrix(0.707, 0.707, -0.707, 0.707, 0, 0);
</div>
</section>
<!-- ====== 6. REFERENCE TABLES ====== -->
<section>
<h2>6. Reference Tables</h2>
<h3>matrix() Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Controls</th>
<th>Description</th>
</tr>
<tr>
<td><code>a</code></td>
<td>Scale X</td>
<td>Horizontal scaling</td>
</tr>
<tr>
<td><code>b</code></td>
<td>Skew Y</td>
<td>Vertical skew</td>
</tr>
<tr>
<td><code>c</code></td>
<td>Skew X</td>
<td>Horizontal skew</td>
</tr>
<tr>
<td><code>d</code></td>
<td>Scale Y</td>
<td>Vertical scaling</td>
</tr>
<tr>
<td><code>e</code></td>
<td>Translate X</td>
<td>Horizontal movement</td>
</tr>
<tr>
<td><code>f</code></td>
<td>Translate Y</td>
<td>Vertical movement</td>
</tr>
</table>
<h3>matrix3d() Value Groups</h3>
<table class="reference-table">
<tr>
<th>Group</th>
<th>Values</th>
<th>Controls</th>
</tr>
<tr>
<td><strong>Linear</strong></td>
<td><code>a, b, c, e, f, g, i, j, k</code></td>
<td>Scale, rotate, skew</td>
</tr>
<tr>
<td><strong>Translation</strong></td>
<td><code>m, n, o</code></td>
<td>X, Y, Z movement</td>
</tr>
<tr>
<td><strong>Perspective</strong></td>
<td><code>d, h, l</code></td>
<td>Perspective effects</td>
</tr>
<tr>
<td><strong>Homogeneous</strong></td>
<td><code>p</code></td>
<td>Usually <code>1</code></td>
</tr>
</table>
</section>
<!-- ====== 7. BEST PRACTICES ====== -->
<section>
<h2>7. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>matrix()</code> for complex 2D transformations</li>
<li>Use <code>matrix3d()</code> for 3D transformations and perspective</li>
<li>Use transform functions (<code>rotate()</code>, <code>scale()</code>) for simplicity</li>
<li>Use matrix when you need precise control over all values</li>
<li>Remember that `a, d` control scale and `e, f` control translation</li>
<li>Test matrix transformations in different browsers</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>matrix()</code> when <code>translate()</code> or <code>scale()</code> is clearer</li>
<li>Don't manually calculate matrices if you can use simpler functions</li>
<li>Don't forget that <code>matrix3d()</code> requires 16 values</li>
<li>Don't mix up the order of matrix values</li>
<li>Don't use matrix transformations without understanding them</li>
</ul>
</div>
<div class="tip-box warning">
<strong>⚠️ When to Use Matrix:</strong>
Use <code>matrix()</code> and <code>matrix3d()</code> only when you need to apply a
transformation that can't be achieved with simpler functions, or when you're
generating transformations programmatically (e.g., in JavaScript).
</div>
</section>
</body>
</html>
Quick Reference
| Function | Values | Matrix Size |
|---|---|---|
matrix() | a, b, c, d, e, f | 3×3 (2D) |
matrix3d() | 16 values (a through p) | 4×4 (3D) |
matrix() Values
| Value | Controls | Description |
|---|---|---|
a | Scale X | Horizontal scaling |
b | Skew Y | Vertical skew |
c | Skew X | Horizontal skew |
d | Scale Y | Vertical scaling |
e | Translate X | Horizontal movement |
f | Translate Y | Vertical movement |
matrix3d() Value Groups
| Group | Values | Controls |
|---|---|---|
| Linear | a, b, c, e, f, g, i, j, k | Scale, rotate, skew |
| Translation | m, n, o | X, Y, Z movement |
| Perspective | d, h, l | Perspective effects |
| Homogeneous | p | Usually 1 |
Function Equivalents
| Function | Matrix Equivalent |
|---|---|
translate(100px, 50px) | matrix(1, 0, 0, 1, 100, 50) |
scale(2, 2) | matrix(2, 0, 0, 2, 0, 0) |
rotate(45deg) | matrix(0.707, 0.707, -0.707, 0.707, 0, 0) |
Best Practices
✅ Do This:
/* Use simpler functions when possible */
.box {
transform: rotate(45deg) scale(1.5) translate(20px, 10px);
}
/* Use matrix() for complex 2D transforms */
.box {
transform: matrix(1, 0.3, -0.3, 1, 100, 50);
}
/* Use matrix3d() for 3D transforms */
.box {
transform: matrix3d(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
50, 50, 0, 1
);
}
❌ Don’t Do This:
/* Don't use matrix when a simple function works */
.box {
transform: matrix(1, 0, 0, 1, 100, 50);
/* Use translate(100px, 50px) instead */
}
/* Don't forget the 16 values in matrix3d */
.box {
transform: matrix3d(1, 0, 0, 0); /* Invalid! */
}
/* Don't mix up matrix value order */
.box {
transform: matrix(0, 1, 1, 0, 100, 50); /* Wrong order! */
}
Pro Tip: matrix() and matrix3d() are the low-level API of CSS transforms. Most of the time, you should use the simpler functions (rotate(), scale(), translate(), skew()) — they’re easier to read and maintain. Use matrix() when you need precise control over all transformation values, or when you’re generating transforms programmatically with JavaScript. Remember: matrix() uses 6 values (a, b, c, d, e, f), while matrix3d() uses 16 values for full 3D transformations. The order matters — always double-check your matrix values!
47. transform perspective, rotate, and rotate3d
These three properties unlock the third dimension in CSS. With perspective, rotate3d, and transform-style, you can create stunning 3D effects like cubes, cards, and spatial interfaces.
Overview of Features
| Property/Function | Description | Values |
|---|---|---|
perspective | Distance from viewer to z=0 plane | Length (e.g., 600px) |
rotate() | 2D rotation around a fixed point | Angle (e.g., 45deg) |
rotate3d() | 3D rotation around a custom axis | x, y, z, angle |
1. perspective
The perspective property gives the appearance that elements are viewed from a particular distance, creating a 3D effect.
.scene {
perspective: 600px;
}
How It Works
Viewer
👁️
|
| perspective: 600px
|
┌────┴────┐
│ Scene │
└─────────┘
- Smaller values (e.g.,
200px) → Stronger perspective effect - Larger values (e.g.,
2000px) → Subtler perspective effect - Default →
none(no perspective, flat)
Values
| Value | Effect |
|---|---|
none | No perspective (default, flat) |
200px | Very strong perspective (close viewer) |
600px | Moderate perspective (common) |
1000px | Subtle perspective (far viewer) |
2000px+ | Very subtle perspective |
Key Points:
- Apply
perspectiveto the parent of the 3D elements - Use
transform-style: preserve-3don the 3D container so children are positioned in 3D space - Perspective affects all children of the element it’s applied to
2. transform-style
The transform-style property ensures child elements are treated as 3D objects within their parent’s 3D space.
.cube {
transform-style: preserve-3d;
}
Values
| Value | Description |
|---|---|
flat | Children are flattened onto the parent’s plane (default) |
preserve-3d | Children maintain their 3D positioning |
Key Points:
preserve-3dis required for 3D cubes, cards, and scenes- Without it, child 3D transforms are flattened
- Apply to the container that holds the 3D faces
3. rotate()
The rotate() function rotates an element around a fixed point (its center by default) in 2D space.
.rotate:hover {
transform: rotate(45deg);
}
Values
| Value | Effect |
|---|---|
rotate(45deg) | 45° clockwise |
rotate(-45deg) | 45° counter-clockwise |
rotate(0.5turn) | Half turn (180°) |
rotate(1rad) | ~57.3° |
Key Points:
- Positive values rotate clockwise
- Negative values rotate counter-clockwise
- Rotation happens in the 2D plane (no 3D effect)
- Change the pivot with
transform-origin
4. rotate3d()
The rotate3d() function applies a 3D rotation around a custom axis defined by x, y, z coordinates.
.box {
transform: rotate3d(1, 1, 1, 90deg);
}
Syntax
rotate3d(x, y, z, angle)
| Parameter | Description |
|---|---|
x | X-axis component (0–1) |
y | Y-axis component (0–1) |
z | Z-axis component (0–1) |
angle | Rotation angle (deg, rad, turn) |
Common Axes
| Axis | Function | Effect |
|---|---|---|
| X | rotate3d(1, 0, 0, angle) | Rotate around horizontal axis |
| Y | rotate3d(0, 1, 0, angle) | Rotate around vertical axis |
| Z | rotate3d(0, 0, 1, angle) | Rotate around depth axis (like rotate()) |
| Diagonal | rotate3d(1, 1, 1, angle) | Rotate around diagonal axis |
Key Points:
- The axis is a vector (x, y, z) — not necessarily normalized
rotate3d(1, 1, 1, 90deg)rotates along a diagonal line from one corner to the opposite- Combine with
perspectivefor a true 3D effect - Use
transform-style: preserve-3don the parent container
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>perspective, rotate, and rotate3d</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== PERSPECTIVE DEMO ====== */
.perspective-grid {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
margin: 20px 0;
}
.perspective-demo {
text-align: center;
}
.perspective-demo .scene {
width: 150px;
height: 150px;
margin: 0 auto 10px;
background: #e9ecef;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.perspective-demo .card {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 8px;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
transition: transform 0.5s;
}
.perspective-demo .card.rotate-y {
transform: rotateY(45deg);
}
.perspective-demo .label {
font-weight: bold;
color: #333;
font-size: 0.85rem;
}
.perspective-200 { perspective: 200px; }
.perspective-600 { perspective: 600px; }
.perspective-1200 { perspective: 1200px; }
/* ====== ROTATE DEMO ====== */
.rotate-demo {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
align-items: center;
padding: 30px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.rotate-box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #28a745, #20c997);
border-radius: 8px;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.8rem;
transition: transform 0.5s;
cursor: pointer;
text-align: center;
padding: 5px;
}
.rotate-box:hover {
transform: rotate(45deg);
}
.rotate-3d-box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #dc3545, #fd7e14);
border-radius: 8px;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.8rem;
transition: transform 0.5s;
cursor: pointer;
text-align: center;
padding: 5px;
}
.rotate-3d-box:hover {
transform: rotate3d(1, 1, 1, 90deg);
}
/* ====== 3D CUBE ====== */
.cube-scene {
width: 200px;
height: 200px;
perspective: 600px;
margin: 50px auto;
background: transparent;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation: rotateCube 10s infinite linear;
}
@keyframes rotateCube {
from { transform: rotateX(0) rotateY(0); }
to { transform: rotateX(360deg) rotateY(360deg); }
}
.cube .face {
position: absolute;
width: 200px;
height: 200px;
border: 2px solid rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 1.2rem;
text-shadow: 0 2px 5px rgba(0,0,0,0.5);
border-radius: 8px;
}
.cube .front {
background: rgba(255, 0, 0, 0.7);
transform: translateZ(100px);
}
.cube .back {
background: rgba(0, 255, 0, 0.7);
transform: rotateY(180deg) translateZ(100px);
}
.cube .right {
background: rgba(0, 0, 255, 0.7);
transform: rotateY(90deg) translateZ(100px);
}
.cube .left {
background: rgba(255, 255, 0, 0.7);
transform: rotateY(-90deg) translateZ(100px);
}
.cube .top {
background: rgba(255, 0, 255, 0.7);
transform: rotateX(90deg) translateZ(100px);
}
.cube .bottom {
background: rgba(0, 255, 255, 0.7);
transform: rotateX(-90deg) translateZ(100px);
}
/* ====== PRACTICAL: 3D CARD ====== */
.card-scene {
width: 250px;
height: 350px;
perspective: 1000px;
margin: 20px auto;
}
.card-3d {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.8s;
cursor: pointer;
}
.card-scene:hover .card-3d {
transform: rotateY(180deg);
}
.card-face {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
text-align: center;
color: white;
font-weight: bold;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.card-front {
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
.card-front .icon {
font-size: 3rem;
margin-bottom: 15px;
}
.card-back {
background: linear-gradient(135deg, #28a745, #20c997);
transform: rotateY(180deg);
}
.card-back h4 {
margin-top: 0;
}
.card-back p {
font-size: 0.9rem;
font-weight: normal;
opacity: 0.95;
}
/* ====== PRACTICAL: 3D BUTTON ====== */
.btn-3d {
padding: 15px 35px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1.1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
transform-style: preserve-3d;
perspective: 500px;
position: relative;
margin: 10px;
}
.btn-3d:hover {
transform: rotateX(15deg) rotateY(-15deg) scale(1.05);
box-shadow: 0 10px 25px rgba(0, 123, 255, 0.4);
}
/* ====== AXIS VISUALIZATION ====== */
.axis-demo {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
margin: 20px 0;
}
.axis-box {
width: 120px;
height: 120px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 8px;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.75rem;
text-align: center;
padding: 5px;
transition: transform 0.5s;
cursor: pointer;
}
.axis-box:hover {
transform: rotateX(45deg);
}
.axis-box.rotate-y:hover {
transform: rotateY(45deg);
}
.axis-box.rotate-z:hover {
transform: rotateZ(45deg);
}
.axis-box.rotate-3d:hover {
transform: rotate3d(1, 1, 1, 45deg);
}
.axis-demo .scene-wrapper {
perspective: 600px;
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>transform perspective, rotate, and rotate3d</h1>
<!-- ====== 1. PERSPECTIVE DEMO ====== -->
<section>
<h2>1. perspective</h2>
<p>Creates the illusion of depth by defining the distance from the viewer to the z=0 plane.</p>
<div class="perspective-grid">
<div class="perspective-demo">
<div class="scene perspective-200">
<div class="card rotate-y">200px</div>
</div>
<div class="label">Strong perspective</div>
</div>
<div class="perspective-demo">
<div class="scene perspective-600">
<div class="card rotate-y">600px</div>
</div>
<div class="label">Moderate perspective</div>
</div>
<div class="perspective-demo">
<div class="scene perspective-1200">
<div class="card rotate-y">1200px</div>
</div>
<div class="label">Subtle perspective</div>
</div>
</div>
<p class="note">Smaller <code>perspective</code> values create a stronger 3D effect (viewer is closer).</p>
<div class="code-block">
/* Apply perspective to the parent */
.scene {
perspective: 600px; /* Distance from viewer */
}
/* Child elements get 3D positioning */
.card {
transform: rotateY(45deg);
}
</div>
</section>
<!-- ====== 2. ROTATE AND ROTATE3D ====== -->
<section>
<h2>2. rotate() and rotate3d()</h2>
<p>Hover over each box to see the rotation effect.</p>
<h3>rotate() — 2D Rotation</h3>
<div class="rotate-demo">
<div class="rotate-box">rotate(45deg)</div>
</div>
<h3>rotate3d() — 3D Rotation</h3>
<div class="rotate-demo">
<div class="rotate-3d-box">rotate3d(1,1,1,90deg)</div>
</div>
<div class="code-block">
/* 2D rotation — around a fixed point */
.box:hover {
transform: rotate(45deg);
}
/* 3D rotation — around a diagonal axis */
.box:hover {
transform: rotate3d(1, 1, 1, 90deg);
}
</div>
</section>
<!-- ====== 3. AXIS VISUALIZATION ====== -->
<section>
<h2>3. Rotation Axes</h2>
<p>Hover over each box to see rotation around different axes.</p>
<div class="axis-demo">
<div class="scene-wrapper">
<div class="axis-box">rotateX(45deg)<br>(horizontal axis)</div>
</div>
<div class="scene-wrapper">
<div class="axis-box rotate-y">rotateY(45deg)<br>(vertical axis)</div>
</div>
<div class="scene-wrapper">
<div class="axis-box rotate-z">rotateZ(45deg)<br>(depth axis)</div>
</div>
<div class="scene-wrapper">
<div class="axis-box rotate-3d">rotate3d(1,1,1,45deg)<br>(diagonal axis)</div>
</div>
</div>
<div class="code-block">
/* Rotate around X axis (horizontal) */
.box:hover {
transform: rotateX(45deg);
}
/* Rotate around Y axis (vertical) */
.box:hover {
transform: rotateY(45deg);
}
/* Rotate around Z axis (depth) — same as rotate() */
.box:hover {
transform: rotateZ(45deg);
}
/* Rotate around a custom diagonal axis */
.box:hover {
transform: rotate3d(1, 1, 1, 45deg);
}
</div>
</section>
<!-- ====== 4. 3D CUBE ====== -->
<section>
<h2>4. 3D Cube</h2>
<p>A complete 3D cube using <code>perspective</code>, <code>transform-style: preserve-3d</code>, and <code>rotate3d</code>.</p>
<div class="cube-scene">
<div class="cube">
<div class="face front">Front</div>
<div class="face back">Back</div>
<div class="face right">Right</div>
<div class="face left">Left</div>
<div class="face top">Top</div>
<div class="face bottom">Bottom</div>
</div>
</div>
<div class="code-block">
/* Scene with perspective */
.scene {
perspective: 600px;
}
/* Cube container preserves 3D */
.cube {
transform-style: preserve-3d;
animation: rotateCube 10s infinite linear;
}
@keyframes rotateCube {
from { transform: rotateX(0) rotateY(0); }
to { transform: rotateX(360deg) rotateY(360deg); }
}
/* Each face positioned in 3D space */
.front {
transform: translateZ(100px);
}
.back {
transform: rotateY(180deg) translateZ(100px);
}
.right {
transform: rotateY(90deg) translateZ(100px);
}
.left {
transform: rotateY(-90deg) translateZ(100px);
}
.top {
transform: rotateX(90deg) translateZ(100px);
}
.bottom {
transform: rotateX(-90deg) translateZ(100px);
}
</div>
</section>
<!-- ====== 5. PRACTICAL: 3D CARD FLIP ====== -->
<section>
<h2>5. Practical Example: 3D Card Flip</h2>
<p>Hover over the card to flip it in 3D space.</p>
<div class="card-scene">
<div class="card-3d">
<div class="card-face card-front">
<div class="icon">🃏</div>
<h3 style="margin: 0;">3D Card</h3>
<p style="font-weight: normal; font-size: 0.9rem;">Hover to flip</p>
</div>
<div class="card-face card-back">
<h4>✨ Back Side</h4>
<p>This card uses <code>rotateY(180deg)</code> and <code>backface-visibility: hidden</code> to create a 3D flip effect.</p>
</div>
</div>
</div>
<div class="code-block">
.card-scene {
perspective: 1000px;
}
.card-3d {
transform-style: preserve-3d;
transition: transform 0.8s;
}
.card-scene:hover .card-3d {
transform: rotateY(180deg);
}
.card-face {
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg);
}
</div>
</section>
<!-- ====== 6. PRACTICAL: 3D BUTTON ====== -->
<section>
<h2>6. Practical Example: 3D Button</h2>
<p>Hover over the button to see a 3D tilt effect.</p>
<div style="text-align: center;">
<button class="btn-3d">Hover Me</button>
</div>
<div class="code-block">
.btn-3d {
transform-style: preserve-3d;
perspective: 500px;
transition: all 0.3s;
}
.btn-3d:hover {
transform: rotateX(15deg) rotateY(-15deg) scale(1.05);
box-shadow: 0 10px 25px rgba(0, 123, 255, 0.4);
}
</div>
</section>
<!-- ====== 7. REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>perspective Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
<th>Use Case</th>
</tr>
<tr>
<td><code>none</code></td>
<td>No perspective (flat, default)</td>
<td>2D layouts</td>
</tr>
<tr>
<td><code>200px</code></td>
<td>Very strong perspective</td>
<td>Dramatic 3D effects</td>
</tr>
<tr>
<td><code>600px</code></td>
<td>Moderate perspective</td>
<td>Cards, cubes</td>
</tr>
<tr>
<td><code>1000px</code></td>
<td>Subtle perspective</td>
<td>Realistic 3D scenes</td>
</tr>
<tr>
<td><code>2000px+</code></td>
<td>Very subtle perspective</td>
<td>Large 3D scenes</td>
</tr>
</table>
<h3>rotate() Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
</tr>
<tr>
<td><code>rotate(45deg)</code></td>
<td>45° clockwise</td>
</tr>
<tr>
<td><code>rotate(-45deg)</code></td>
<td>45° counter-clockwise</td>
</tr>
<tr>
<td><code>rotate(0.5turn)</code></td>
<td>Half turn (180°)</td>
</tr>
</table>
<h3>rotate3d() Axes</h3>
<table class="reference-table">
<tr>
<th>Axis</th>
<th>Function</th>
<th>Effect</th>
</tr>
<tr>
<td>X</td>
<td><code>rotate3d(1, 0, 0, angle)</code></td>
<td>Rotate around horizontal axis</td>
</tr>
<tr>
<td>Y</td>
<td><code>rotate3d(0, 1, 0, angle)</code></td>
<td>Rotate around vertical axis</td>
</tr>
<tr>
<td>Z</td>
<td><code>rotate3d(0, 0, 1, angle)</code></td>
<td>Rotate around depth axis (like rotate())</td>
</tr>
<tr>
<td>Diagonal</td>
<td><code>rotate3d(1, 1, 1, angle)</code></td>
<td>Rotate around diagonal axis</td>
</tr>
</table>
<h3>Related Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
</tr>
<tr>
<td><code>perspective</code></td>
<td>Distance from viewer (on parent)</td>
</tr>
<tr>
<td><code>perspective-origin</code></td>
<td>Vanishing point (default: center)</td>
</tr>
<tr>
<td><code>transform-style</code></td>
<td><code>flat</code> or <code>preserve-3d</code></td>
</tr>
<tr>
<td><code>backface-visibility</code></td>
<td>Hide back face of 3D element</td>
</tr>
</table>
</section>
<!-- ====== 8. BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Apply <code>perspective</code> to the <strong>parent</strong> of 3D elements</li>
<li>Use <code>transform-style: preserve-3d</code> for 3D scenes</li>
<li>Use <code>backface-visibility: hidden</code> for card flips</li>
<li>Use <code>perspective: 600px–1000px</code> for realistic 3D effects</li>
<li>Test 3D transforms on different devices</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't apply <code>perspective</code> to the element being transformed (apply to parent)</li>
<li>Don't forget <code>transform-style: preserve-3d</code> for 3D children</li>
<li>Don't use extreme perspective values (too small or too large)</li>
<li>Don't overuse 3D effects — they can cause motion sickness</li>
<li>Don't forget to test with reduced motion preferences</li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.cube {
animation: none;
}
.card-3d {
transition: none;
}
.card-scene:hover .card-3d {
transform: none;
}
}
</div>
</section>
</body>
</html>
Quick Reference
| Property/Function | Description | Values |
|---|---|---|
perspective | Distance from viewer | none, 600px |
rotate() | 2D rotation | Angle (45deg) |
rotate3d() | 3D rotation around custom axis | x, y, z, angle |
transform-style | 3D positioning of children | flat, preserve-3d |
perspective Values
| Value | Effect |
|---|---|
none | No perspective (flat, default) |
200px | Very strong perspective |
600px | Moderate perspective |
1000px | Subtle perspective |
2000px+ | Very subtle perspective |
rotate3d() Axes
| Axis | Function | Effect |
|---|---|---|
| X | rotate3d(1, 0, 0, angle) | Horizontal axis |
| Y | rotate3d(0, 1, 0, angle) | Vertical axis |
| Z | rotate3d(0, 0, 1, angle) | Depth axis |
| Diagonal | rotate3d(1, 1, 1, angle) | Diagonal axis |
Related Properties
| Property | Description |
|---|---|
perspective | Distance from viewer (on parent) |
perspective-origin | Vanishing point (default: center) |
transform-style | flat or preserve-3d |
backface-visibility | Hide back face of 3D element |
Best Practices
✅ Do This:
/* Apply perspective to parent */
.scene {
perspective: 600px;
}
/* Preserve 3D for children */
.cube {
transform-style: preserve-3d;
}
/* Card flip */
.card-scene:hover .card-3d {
transform: rotateY(180deg);
}
.card-back {
transform: rotateY(180deg);
backface-visibility: hidden;
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.cube {
animation: none;
}
}
❌ Don’t Do This:
/* Don't apply perspective to the transformed element */
.box {
perspective: 600px; /* Wrong — apply to parent */
transform: rotateY(45deg);
}
/* Don't forget transform-style */
.cube {
/* Missing transform-style: preserve-3d */
}
.cube .face {
transform: translateZ(100px); /* Won't work correctly */
}
/* Don't overuse 3D effects */
.everything {
transform: rotate3d(1, 1, 1, 45deg);
/* Can cause motion sickness */
}
Pro Tip: The key to 3D transforms is understanding the three key properties: perspective (on the parent), transform-style: preserve-3d (on the container), and the transform functions (rotate3d, translateZ, etc.) on the children. Remember: perspective goes on the parent, not the element being transformed. Use rotate3d(1, 1, 1, 90deg) for a diagonal rotation that looks impressive. And always respect prefers-reduced-motion — 3D effects can be disorienting for users with vestibular disorders!
48. transform rotateX, rotateY, and rotateZ
These three functions are the individual axis rotations in CSS 3D transforms. Each one rotates an element around a specific axis: X (horizontal), Y (vertical), or Z (depth).
Overview of Functions
| Function | Axis | Equivalent | Visual Effect |
|---|---|---|---|
rotateX() | Horizontal | rotate3d(1, 0, 0, angle) | Tilts forward/backward |
rotateY() | Vertical | rotate3d(0, 1, 0, angle) | Turns left/right |
rotateZ() | Depth | rotate3d(0, 0, 1, angle) | Spins in place (2D) |
Understanding the Axes
Y (vertical)
↑
│
│
│
└──────────→ X (horizontal)
/
/
↙
Z (depth, toward viewer)
| Axis | Direction | Rotation Effect |
|---|---|---|
| X | Horizontal (left–right) | Tilts top toward/away from viewer |
| Y | Vertical (up–down) | Turns left/right (like a door) |
| Z | Depth (toward viewer) | Spins in the 2D plane |
1. rotateX()
Rotates an element around the X-axis (horizontal). The top and bottom move toward or away from the viewer.
.rotateX {
transform: rotateX(45deg);
}
| Angle | Effect |
|---|---|
rotateX(45deg) | Top tilts away, bottom tilts toward viewer |
rotateX(-45deg) | Top tilts toward, bottom tilts away |
rotateX(90deg) | Element becomes a flat line (edge-on) |
rotateX(180deg) | Element is flipped upside down |
Equivalent: rotate3d(1, 0, 0, 45deg)
Use cases:
- Flipping cards vertically
- Creating a “flipboard” effect
- 3D menus that open upward/downward
2. rotateY()
Rotates an element around the Y-axis (vertical). The left and right sides move toward or away from the viewer.
.rotateY {
transform: rotateY(45deg);
}
| Angle | Effect |
|---|---|
rotateY(45deg) | Right side goes away, left comes forward |
rotateY(-45deg) | Left side goes away, right comes forward |
rotateY(90deg) | Element becomes a flat line (edge-on) |
rotateY(180deg) | Element is flipped (mirror image) |
Equivalent: rotate3d(0, 1, 0, 45deg)
Use cases:
- Card flip animations (horizontal)
- Carousels
- Book page turns
- 3D product previews
3. rotateZ()
Rotates an element around the Z-axis (depth). This is the same as the 2D rotate() function.
.rotateZ {
transform: rotateZ(45deg);
}
| Angle | Effect |
|---|---|
rotateZ(45deg) | 45° clockwise (in the 2D plane) |
rotateZ(-45deg) | 45° counter-clockwise |
rotateZ(180deg) | Upside down (2D) |
Equivalent: rotate3d(0, 0, 1, 45deg) = rotate(45deg)
Use cases:
- Spinning icons
- Rotating badges
- 2D rotations in 3D contexts
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rotateX, rotateY, and rotateZ</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 50px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 350px;
perspective: 800px;
}
.box {
width: 150px;
height: 150px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.9rem;
border-radius: 12px;
transition: transform 1s;
cursor: pointer;
text-align: center;
padding: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
transform-style: preserve-3d;
}
.box:hover {
box-shadow: 0 8px 30px rgba(0, 123, 255, 0.4);
}
/* Individual rotations */
.rotateX {
transform: rotateX(45deg);
}
.rotateX:hover {
transform: rotateX(90deg);
}
.rotateY {
transform: rotateY(45deg);
}
.rotateY:hover {
transform: rotateY(90deg);
}
.rotateZ {
transform: rotateZ(45deg);
}
.rotateZ:hover {
transform: rotateZ(90deg);
}
/* Angle comparisons */
.rotate-x-30 { transform: rotateX(30deg); }
.rotate-x-60 { transform: rotateX(60deg); }
.rotate-x-90 { transform: rotateX(90deg); }
.rotate-x-neg { transform: rotateX(-45deg); }
.rotate-y-30 { transform: rotateY(30deg); }
.rotate-y-60 { transform: rotateY(60deg); }
.rotate-y-90 { transform: rotateY(90deg); }
.rotate-y-neg { transform: rotateY(-45deg); }
.rotate-z-30 { transform: rotateZ(30deg); }
.rotate-z-90 { transform: rotateZ(90deg); }
.rotate-z-180 { transform: rotateZ(180deg); }
.rotate-z-neg { transform: rotateZ(-45deg); }
/* ====== AXIS VISUALIZATION ====== */
.axis-viz {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
margin: 20px 0;
}
.axis-card {
text-align: center;
padding: 20px;
background: white;
border: 2px solid #ddd;
border-radius: 12px;
width: 200px;
transition: all 0.3s;
}
.axis-card:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.15);
}
.axis-card .axis-icon {
font-size: 3rem;
margin-bottom: 10px;
}
.axis-card h4 {
margin: 5px 0;
color: #007bff;
}
.axis-card p {
font-size: 0.85rem;
color: #6c757d;
margin: 5px 0;
}
.axis-card code {
background: #e9ecef;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.8rem;
color: #dc3545;
}
/* ====== PRACTICAL: CARD FLIP VERTICAL ====== */
.flip-container {
perspective: 1000px;
width: 250px;
height: 300px;
margin: 20px auto;
}
.flip-card {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.8s;
cursor: pointer;
}
.flip-container:hover .flip-card {
transform: rotateX(180deg);
}
.flip-front,
.flip-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
text-align: center;
color: white;
font-weight: bold;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.flip-front {
background: linear-gradient(135deg, #007bff, #6c5ce7);
}
.flip-front .icon {
font-size: 3rem;
margin-bottom: 15px;
}
.flip-back {
background: linear-gradient(135deg, #28a745, #20c997);
transform: rotateX(180deg);
}
.flip-back h4 {
margin-top: 0;
}
.flip-back p {
font-size: 0.9rem;
font-weight: normal;
opacity: 0.95;
}
/* ====== PRACTICAL: 3D CUBE ====== */
.cube-scene {
width: 180px;
height: 180px;
perspective: 600px;
margin: 30px auto;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation: spinCube 8s infinite linear;
}
@keyframes spinCube {
from { transform: rotateX(0) rotateY(0); }
to { transform: rotateX(360deg) rotateY(360deg); }
}
.cube .face {
position: absolute;
width: 180px;
height: 180px;
border: 2px solid rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 1rem;
text-shadow: 0 2px 5px rgba(0,0,0,0.5);
border-radius: 8px;
}
.cube .front {
background: rgba(255, 0, 0, 0.8);
transform: translateZ(90px);
}
.cube .back {
background: rgba(0, 255, 0, 0.8);
transform: rotateY(180deg) translateZ(90px);
}
.cube .right {
background: rgba(0, 0, 255, 0.8);
transform: rotateY(90deg) translateZ(90px);
}
.cube .left {
background: rgba(255, 255, 0, 0.8);
transform: rotateY(-90deg) translateZ(90px);
}
.cube .top {
background: rgba(255, 0, 255, 0.8);
transform: rotateX(90deg) translateZ(90px);
}
.cube .bottom {
background: rgba(0, 255, 255, 0.8);
transform: rotateX(-90deg) translateZ(90px);
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>rotateX, rotateY, and rotateZ</h1>
<!-- ====== 1. BASIC ROTATIONS ====== -->
<section>
<h2>1. Basic Axis Rotations</h2>
<p>Hover over each box to see the rotation increase.</p>
<div class="demo-area">
<div class="box rotateX">rotateX(45deg)</div>
<div class="box rotateY">rotateY(45deg)</div>
<div class="box rotateZ">rotateZ(45deg)</div>
</div>
<p class="note"><strong>rotateX</strong> tilts forward/backward. <strong>rotateY</strong> turns left/right. <strong>rotateZ</strong> spins in place.</p>
<div class="code-block">
/* Rotate around X-axis (horizontal) */
.rotateX {
transform: rotateX(45deg);
}
/* Rotate around Y-axis (vertical) */
.rotateY {
transform: rotateY(45deg);
}
/* Rotate around Z-axis (depth) */
.rotateZ {
transform: rotateZ(45deg);
}
</div>
</section>
<!-- ====== 2. AXIS VISUALIZATION ====== -->
<section>
<h2>2. Understanding the Axes</h2>
<div class="axis-viz">
<div class="axis-card">
<div class="axis-icon">↔️</div>
<h4>X-Axis</h4>
<p>Horizontal (left–right)</p>
<code>rotateX(45deg)</code>
<p class="note">Tilts top toward/away from viewer</p>
</div>
<div class="axis-card">
<div class="axis-icon">↕️</div>
<h4>Y-Axis</h4>
<p>Vertical (up–down)</p>
<code>rotateY(45deg)</code>
<p class="note">Turns left/right like a door</p>
</div>
<div class="axis-card">
<div class="axis-icon">🔄</div>
<h4>Z-Axis</h4>
<p>Depth (toward viewer)</p>
<code>rotateZ(45deg)</code>
<p class="note">Spins in the 2D plane (like rotate())</p>
</div>
</div>
<div class="code-block">
/* These are equivalent */
rotateX(45deg) = rotate3d(1, 0, 0, 45deg)
rotateY(45deg) = rotate3d(0, 1, 0, 45deg)
rotateZ(45deg) = rotate3d(0, 0, 1, 45deg)
/* rotateZ() is the same as 2D rotate() */
rotateZ(45deg) = rotate(45deg)
</div>
</section>
<!-- ====== 3. ROTATEX ANGLES ====== -->
<section>
<h2>3. rotateX() Angle Comparison</h2>
<div class="demo-area">
<div class="box rotate-x-30">30°</div>
<div class="box rotate-x-60">60°</div>
<div class="box rotate-x-90">90°</div>
<div class="box rotate-x-neg">-45°</div>
</div>
<div class="code-block">
.rotate-x-30 { transform: rotateX(30deg); }
.rotate-x-60 { transform: rotateX(60deg); }
.rotate-x-90 { transform: rotateX(90deg); } /* Edge-on */
.rotate-x-neg { transform: rotateX(-45deg); } /* Opposite direction */
</div>
<p class="note">At <code>rotateX(90deg)</code>, the element becomes a flat line (edge-on to the viewer).</p>
</section>
<!-- ====== 4. ROTATEY ANGLES ====== -->
<section>
<h2>4. rotateY() Angle Comparison</h2>
<div class="demo-area">
<div class="box rotate-y-30">30°</div>
<div class="box rotate-y-60">60°</div>
<div class="box rotate-y-90">90°</div>
<div class="box rotate-y-neg">-45°</div>
</div>
<div class="code-block">
.rotate-y-30 { transform: rotateY(30deg); }
.rotate-y-60 { transform: rotateY(60deg); }
.rotate-y-90 { transform: rotateY(90deg); } /* Edge-on */
.rotate-y-neg { transform: rotateY(-45deg); } /* Opposite direction */
</div>
<p class="note">At <code>rotateY(90deg)</code>, the element becomes a flat line (edge-on to the viewer).</p>
</section>
<!-- ====== 5. ROTATEZ ANGLES ====== -->
<section>
<h2>5. rotateZ() Angle Comparison</h2>
<div class="demo-area">
<div class="box rotate-z-30">30°</div>
<div class="box rotate-z-90">90°</div>
<div class="box rotate-z-180">180°</div>
<div class="box rotate-z-neg">-45°</div>
</div>
<div class="code-block">
.rotate-z-30 { transform: rotateZ(30deg); }
.rotate-z-90 { transform: rotateZ(90deg); }
.rotate-z-180 { transform: rotateZ(180deg); } /* Upside down */
.rotate-z-neg { transform: rotateZ(-45deg); }
</div>
<p class="note"><code>rotateZ()</code> is a 2D rotation — it doesn't create depth.</p>
</section>
<!-- ====== 6. PRACTICAL: VERTICAL CARD FLIP ====== -->
<section>
<h2>6. Practical Example: Vertical Card Flip</h2>
<p>Hover over the card to flip it vertically using <code>rotateX()</code>.</p>
<div class="flip-container">
<div class="flip-card">
<div class="flip-front">
<div class="icon">🎴</div>
<h3 style="margin: 0;">Vertical Flip</h3>
<p style="font-weight: normal; font-size: 0.9rem;">Hover to flip</p>
</div>
<div class="flip-back">
<h4>✨ Back Side</h4>
<p>This card uses <code>rotateX(180deg)</code> to flip vertically.</p>
</div>
</div>
</div>
<div class="code-block">
.flip-container {
perspective: 1000px;
}
.flip-card {
transform-style: preserve-3d;
transition: transform 0.8s;
}
.flip-container:hover .flip-card {
transform: rotateX(180deg); /* Vertical flip */
}
.flip-back {
transform: rotateX(180deg);
backface-visibility: hidden;
}
</div>
</section>
<!-- ====== 7. PRACTICAL: 3D CUBE ====== -->
<section>
<h2>7. Practical Example: 3D Cube</h2>
<p>A rotating cube using <code>rotateX</code> and <code>rotateY</code> together.</p>
<div class="cube-scene">
<div class="cube">
<div class="face front">Front</div>
<div class="face back">Back</div>
<div class="face right">Right</div>
<div class="face left">Left</div>
<div class="face top">Top</div>
<div class="face bottom">Bottom</div>
</div>
</div>
<div class="code-block">
.cube {
transform-style: preserve-3d;
animation: spinCube 8s infinite linear;
}
@keyframes spinCube {
from { transform: rotateX(0) rotateY(0); }
to { transform: rotateX(360deg) rotateY(360deg); }
}
/* Each face positioned in 3D space */
.front { transform: translateZ(90px); }
.back { transform: rotateY(180deg) translateZ(90px); }
.right { transform: rotateY(90deg) translateZ(90px); }
.left { transform: rotateY(-90deg) translateZ(90px); }
.top { transform: rotateX(90deg) translateZ(90px); }
.bottom { transform: rotateX(-90deg) translateZ(90px); }
</div>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>Axis Rotation Functions</h3>
<table class="reference-table">
<tr>
<th>Function</th>
<th>Axis</th>
<th>Equivalent</th>
<th>Effect</th>
</tr>
<tr>
<td><code>rotateX()</code></td>
<td>Horizontal</td>
<td><code>rotate3d(1, 0, 0, angle)</code></td>
<td>Tilts forward/backward</td>
</tr>
<tr>
<td><code>rotateY()</code></td>
<td>Vertical</td>
<td><code>rotate3d(0, 1, 0, angle)</code></td>
<td>Turns left/right</td>
</tr>
<tr>
<td><code>rotateZ()</code></td>
<td>Depth</td>
<td><code>rotate3d(0, 0, 1, angle)</code></td>
<td>Spins in 2D plane</td>
</tr>
</table>
<h3>Angle Effects</h3>
<table class="reference-table">
<tr>
<th>Angle</th>
<th>Effect</th>
</tr>
<tr>
<td><code>0deg</code></td>
<td>No rotation</td>
</tr>
<tr>
<td><code>45deg</code></td>
<td>Moderate tilt/turn</td>
</tr>
<tr>
<td><code>90deg</code></td>
<td>Edge-on (flat line)</td>
</tr>
<tr>
<td><code>180deg</code></td>
<td>Flipped (upside down / mirror)</td>
</tr>
<tr>
<td><code>360deg</code></td>
<td>Full rotation (same as 0°)</td>
</tr>
<tr>
<td><code>-45deg</code></td>
<td>Opposite direction</td>
</tr>
</table>
<h3>Related Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
</tr>
<tr>
<td><code>perspective</code></td>
<td>Distance from viewer (on parent)</td>
</tr>
<tr>
<td><code>transform-style</code></td>
<td><code>preserve-3d</code> for 3D children</td>
</tr>
<tr>
<td><code>backface-visibility</code></td>
<td>Hide back face of 3D element</td>
</tr>
<tr>
<td><code>perspective-origin</code></td>
<td>Vanishing point (default: center)</td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>rotateX()</code> for vertical flips (cards, panels)</li>
<li>Use <code>rotateY()</code> for horizontal flips (carousels, book pages)</li>
<li>Use <code>rotateZ()</code> for 2D spinning (icons, badges)</li>
<li>Apply <code>perspective</code> to the parent for 3D depth</li>
<li>Use <code>transform-style: preserve-3d</code> on the 3D container</li>
<li>Use <code>backface-visibility: hidden</code> for card flips</li>
<li>Test on different devices and browsers</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't apply <code>perspective</code> to the element being rotated (apply to parent)</li>
<li>Don't forget <code>transform-style: preserve-3d</code> for 3D children</li>
<li>Don't overuse 3D rotations — they can cause motion sickness</li>
<li>Don't use <code>rotateX(90deg)</code> or <code>rotateY(90deg)</code> for visible content (it becomes edge-on)</li>
<li>Don't forget to respect <code>prefers-reduced-motion</code></li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box {
transition: none;
transform: none;
}
.cube {
animation: none;
}
}
</div>
</section>
</body>
</html>
Quick Reference
| Function | Axis | Equivalent | Effect |
|---|---|---|---|
rotateX() | Horizontal | rotate3d(1, 0, 0, angle) | Tilts forward/backward |
rotateY() | Vertical | rotate3d(0, 1, 0, angle) | Turns left/right |
rotateZ() | Depth | rotate3d(0, 0, 1, angle) | Spins in 2D plane |
Angle Effects
| Angle | Effect |
|---|---|
0deg | No rotation |
45deg | Moderate tilt/turn |
90deg | Edge-on (flat line) |
180deg | Flipped (upside down / mirror) |
360deg | Full rotation (same as 0°) |
-45deg | Opposite direction |
Use Cases
| Function | Best For |
|---|---|
rotateX() | Vertical card flips, flipboards, 3D menus |
rotateY() | Horizontal card flips, carousels, book pages |
rotateZ() | Spinning icons, rotating badges, 2D rotations |
Best Practices
✅ Do This:
/* Vertical card flip */
.card-scene:hover .card {
transform: rotateX(180deg);
}
/* Horizontal card flip */
.card-scene:hover .card {
transform: rotateY(180deg);
}
/* 2D spin */
.icon:hover {
transform: rotateZ(360deg);
}
/* 3D cube with both axes */
.cube {
animation: spin 8s infinite;
transform-style: preserve-3d;
}
@keyframes spin {
from { transform: rotateX(0) rotateY(0); }
to { transform: rotateX(360deg) rotateY(360deg); }
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.card-scene:hover .card {
transform: none;
}
}
❌ Don’t Do This:
/* Don't apply perspective to the rotating element */
.box {
perspective: 600px; /* Wrong — apply to parent */
transform: rotateX(45deg);
}
/* Don't forget transform-style for 3D children */
.cube {
/* Missing transform-style: preserve-3d */
}
/* Don't use 90deg for visible content */
.box {
transform: rotateX(90deg); /* Becomes a flat line! */
}
Pro Tip: Think of the axes like this: rotateX is like a book cover opening (tilting top away from you), rotateY is like a door swinging (turning left/right), and rotateZ is like a clock hand spinning (2D rotation). Use rotateX(180deg) for vertical card flips, rotateY(180deg) for horizontal flips, and rotateZ() for simple spinning icons. Remember: at 90 degrees, the element becomes edge-on and appears as a flat line — so avoid using 90° for content that needs to remain visible!
49. transform scale() and scale3d()
The scale() and scale3d() functions let you resize elements — making them larger or smaller — without affecting the layout of surrounding elements.
Overview of Functions
| Function | Dimensions | Values | Description |
|---|---|---|---|
scale() | 2D | s or s1, s2 | Uniform or separate X/Y scaling |
scale3d() | 3D | s1, s2, s3 | X, Y, and Z scaling |
1. scale() — 2D Scaling
The scale() function resizes an element in 2D space — horizontally and vertically.
.box {
transform: scale(1.5);
}
Syntax
scale(s) /* Uniform scaling */
scale(s1, s2) /* Separate X and Y scaling */
| Syntax | Effect |
|---|---|
scale(1.5) | 1.5x in both directions |
scale(1.5, 2) | 1.5x horizontal, 2x vertical |
scale(0.5) | Half size (both directions) |
scale(2) | 2x size (both directions) |
scale(1) | Original size (no change) |
Values
| Value | Effect |
|---|---|
1 | Original size |
> 1 | Enlarges (e.g., 1.5 = 150%) |
< 1 | Shrinks (e.g., 0.5 = 50%) |
0 | Invisible (zero size) |
| Negative | Flips/mirrors the element |
Key Points:
- Scaling maintains the element’s aspect ratio when using a single value
- Scaling doesn’t affect layout — other elements stay in place
- Scale is applied from the transform-origin (center by default)
- Use
transform-originto change the scaling pivot
2. scale3d() — 3D Scaling
The scale3d() function resizes an element in 3D space — along the X, Y, and Z axes.
.box {
transform: scale3d(1.5, 1.5, 2);
}
Syntax
scale3d(s1, s2, s3)
| Value | Axis | Description |
|---|---|---|
s1 | X | Horizontal scaling |
s2 | Y | Vertical scaling |
s3 | Z | Depth scaling |
Key Points:
s3(Z scaling) requiresperspectiveon the parent to be visiblescale3d(1, 1, 1)= no scalingscale3d(2, 2, 2)= 2x in all dimensionsscale3d(1, 1, 2)= normal X/Y, but 2x depth- Use with
transform-style: preserve-3dfor children to maintain 3D positioning
scale() vs scale3d()
| Aspect | scale() | scale3d() |
|---|---|---|
| Dimensions | 2D (X, Y) | 3D (X, Y, Z) |
| Values | 1 or 2 | 3 (required) |
| Z-axis | Not supported | Supported |
| Perspective needed | No | Yes (for Z) |
| Use case | Simple resizing | 3D effects |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transform scale() and scale3d()</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 50px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 250px;
}
.demo-area.perspective {
perspective: 800px;
}
.box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.8rem;
border-radius: 8px;
transition: transform 0.5s;
cursor: pointer;
text-align: center;
padding: 5px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.box:hover {
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.4);
}
/* Scale demos */
.scale-1-5 {
transform: scale(1.5);
}
.scale-1-5:hover {
transform: scale(2);
}
.scale-1-5-2 {
transform: scale(1.5, 2);
}
.scale-1-5-2:hover {
transform: scale(2, 1);
}
.scale-half {
transform: scale(0.5);
}
.scale-half:hover {
transform: scale(0.8);
}
/* Scale3d demos */
.scale3d-2-2-2 {
transform: scale3d(2, 2, 2);
}
.scale3d-2-2-2:hover {
transform: scale3d(1.5, 1.5, 3);
}
.scale3d-1-1-2 {
transform: scale3d(1, 1, 2);
}
.scale3d-1-1-2:hover {
transform: scale3d(1.5, 1.5, 3);
}
/* ====== SCALE COMPARISON ====== */
.scale-comparison {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin: 20px 0;
}
.scale-item {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 2px solid #ddd;
transition: all 0.3s;
}
.scale-item:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.15);
}
.scale-item .box-small {
width: 60px;
height: 60px;
margin: 0 auto 10px;
background: linear-gradient(135deg, #28a745, #20c997);
border-radius: 8px;
transition: transform 0.3s;
}
.scale-item:hover .box-small {
transform: scale(1.3);
}
.scale-item .label {
font-weight: bold;
font-size: 0.85rem;
color: #007bff;
}
/* ====== PRACTICAL: BUTTON SCALE ====== */
.btn-scale {
padding: 15px 30px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
margin: 5px;
}
.btn-scale:hover {
transform: scale(1.05);
background: #0056b3;
box-shadow: 0 8px 20px rgba(0, 123, 255, 0.3);
}
.btn-scale:active {
transform: scale(0.98);
}
/* ====== PRACTICAL: IMAGE GALLERY ====== */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin: 15px 0;
}
.gallery-item {
height: 150px;
border-radius: 8px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2rem;
transition: all 0.4s;
cursor: pointer;
overflow: hidden;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.gallery-item:hover {
transform: scale(1.1);
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.3);
z-index: 10;
}
/* ====== PRACTICAL: 3D CUBE SCALE ====== */
.cube-scene {
width: 150px;
height: 150px;
perspective: 800px;
margin: 30px auto;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation: pulseCube 3s infinite alternate;
}
@keyframes pulseCube {
from { transform: scale3d(0.8, 0.8, 0.8) rotateX(0) rotateY(0); }
to { transform: scale3d(1.2, 1.2, 1.2) rotateX(15deg) rotateY(15deg); }
}
.cube .face {
position: absolute;
width: 150px;
height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 0.9rem;
text-shadow: 0 2px 5px rgba(0,0,0,0.5);
border-radius: 8px;
}
.cube .front {
background: rgba(255, 0, 0, 0.8);
transform: translateZ(75px);
}
.cube .back {
background: rgba(0, 255, 0, 0.8);
transform: rotateY(180deg) translateZ(75px);
}
.cube .right {
background: rgba(0, 0, 255, 0.8);
transform: rotateY(90deg) translateZ(75px);
}
.cube .left {
background: rgba(255, 255, 0, 0.8);
transform: rotateY(-90deg) translateZ(75px);
}
.cube .top {
background: rgba(255, 0, 255, 0.8);
transform: rotateX(90deg) translateZ(75px);
}
.cube .bottom {
background: rgba(0, 255, 255, 0.8);
transform: rotateX(-90deg) translateZ(75px);
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>transform scale() and scale3d()</h1>
<!-- ====== 1. SCALE() DEMO ====== -->
<section>
<h2>1. scale() — 2D Scaling</h2>
<p>Resizes an element horizontally and vertically. Hover over each box to see the scale change!</p>
<div class="demo-area">
<div class="box">Original<br>(scale: 1)</div>
<div class="box scale-1-5">scale(1.5)<br>Uniform</div>
<div class="box scale-1-5-2">scale(1.5, 2)<br>X=1.5, Y=2</div>
<div class="box scale-half">scale(0.5)<br>Half size</div>
</div>
<div class="code-block">
/* Uniform scaling — both directions */
.scale-1-5 {
transform: scale(1.5);
}
/* Separate X and Y scaling */
.scale-1-5-2 {
transform: scale(1.5, 2); /* 1.5x width, 2x height */
}
/* Shrink to half size */
.scale-half {
transform: scale(0.5);
}
</div>
</section>
<!-- ====== 2. SCALE3D() DEMO ====== -->
<section>
<h2>2. scale3d() — 3D Scaling</h2>
<p>Resizes an element in 3D space (X, Y, and Z axes). Requires <code>perspective</code> on the parent.</p>
<div class="demo-area perspective">
<div class="box scale3d-2-2-2">scale3d(2, 2, 2)<br>All axes</div>
<div class="box scale3d-1-1-2">scale3d(1, 1, 2)<br>Depth only</div>
</div>
<div class="code-block">
/* Scale all three axes */
.scale3d-2-2-2 {
transform: scale3d(2, 2, 2); /* 2x in X, Y, Z */
}
/* Scale only the Z (depth) axis */
.scale3d-1-1-2 {
transform: scale3d(1, 1, 2); /* Normal X/Y, 2x depth */
}
</div>
<p class="note">The Z-axis scale (depth) is only visible when <code>perspective</code> is applied to the parent.</p>
</section>
<!-- ====== 3. SCALE COMPARISON ====== -->
<section>
<h2>3. Scale Value Comparison</h2>
<p>Hover over each card to see the scale effect.</p>
<div class="scale-comparison">
<div class="scale-item">
<div class="box-small" style="transform: scale(0.5);"></div>
<div class="label">scale(0.5)</div>
<p class="note">Half size</p>
</div>
<div class="scale-item">
<div class="box-small" style="transform: scale(1);"></div>
<div class="label">scale(1)</div>
<p class="note">Original</p>
</div>
<div class="scale-item">
<div class="box-small" style="transform: scale(1.5);"></div>
<div class="label">scale(1.5)</div>
<p class="note">1.5x size</p>
</div>
<div class="scale-item">
<div class="box-small" style="transform: scale(2);"></div>
<div class="label">scale(2)</div>
<p class="note">2x size</p>
</div>
<div class="scale-item">
<div class="box-small" style="transform: scale(2, 0.5);"></div>
<div class="label">scale(2, 0.5)</div>
<p class="note">Wide and short</p>
</div>
</div>
<div class="code-block">
transform: scale(0.5); /* Half size */
transform: scale(1); /* Original size */
transform: scale(1.5); /* 1.5x size */
transform: scale(2); /* 2x size */
transform: scale(2, 0.5); /* 2x width, 0.5x height */
</div>
</section>
<!-- ====== 4. PRACTICAL: BUTTON SCALE ====== -->
<section>
<h2>4. Practical Example: Button Scale</h2>
<p>Hover and click the buttons to see the scale effect.</p>
<div style="text-align: center; margin: 15px 0;">
<button class="btn-scale">Hover Me</button>
<button class="btn-scale">Click Me</button>
<button class="btn-scale">Press Me</button>
</div>
<div class="code-block">
.btn-scale {
transition: all 0.3s;
}
.btn-scale:hover {
transform: scale(1.05); /* Slightly larger on hover */
}
.btn-scale:active {
transform: scale(0.98); /* Slightly smaller on click */
}
</div>
</section>
<!-- ====== 5. PRACTICAL: IMAGE GALLERY ====== -->
<section>
<h2>5. Practical Example: Image Gallery</h2>
<p>Hover over each item to see the scale effect.</p>
<div class="gallery">
<div class="gallery-item">🖼️</div>
<div class="gallery-item">🎨</div>
<div class="gallery-item">📷</div>
<div class="gallery-item">🎭</div>
</div>
<div class="code-block">
.gallery-item {
transition: all 0.4s;
}
.gallery-item:hover {
transform: scale(1.1);
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.3);
z-index: 10;
}
</div>
</section>
<!-- ====== 6. PRACTICAL: 3D CUBE SCALE ====== -->
<section>
<h2>6. Practical Example: 3D Cube with scale3d()</h2>
<p>A cube that pulses using <code>scale3d()</code>.</p>
<div class="cube-scene">
<div class="cube">
<div class="face front">Front</div>
<div class="face back">Back</div>
<div class="face right">Right</div>
<div class="face left">Left</div>
<div class="face top">Top</div>
<div class="face bottom">Bottom</div>
</div>
</div>
<div class="code-block">
.cube {
transform-style: preserve-3d;
animation: pulseCube 3s infinite alternate;
}
@keyframes pulseCube {
from { transform: scale3d(0.8, 0.8, 0.8) rotateX(0) rotateY(0); }
to { transform: scale3d(1.2, 1.2, 1.2) rotateX(15deg) rotateY(15deg); }
}
</div>
</section>
<!-- ====== 7. REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>scale() Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
<th>Example</th>
</tr>
<tr>
<td><code>scale(1)</code></td>
<td>Original size</td>
<td><code>scale(1)</code></td>
</tr>
<tr>
<td><code>scale(1.5)</code></td>
<td>1.5x size (both axes)</td>
<td><code>scale(1.5)</code></td>
</tr>
<tr>
<td><code>scale(2)</code></td>
<td>2x size (both axes)</td>
<td><code>scale(2)</code></td>
</tr>
<tr>
<td><code>scale(0.5)</code></td>
<td>Half size</td>
<td><code>scale(0.5)</code></td>
</tr>
<tr>
<td><code>scale(2, 1)</code></td>
<td>2x width, 1x height</td>
<td><code>scale(2, 1)</code></td>
</tr>
<tr>
<td><code>scale(1, 2)</code></td>
<td>1x width, 2x height</td>
<td><code>scale(1, 2)</code></td>
</tr>
</table>
<h3>scale3d() Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
</tr>
<tr>
<td><code>scale3d(1, 1, 1)</code></td>
<td>No scaling</td>
</tr>
<tr>
<td><code>scale3d(2, 2, 2)</code></td>
<td>2x in all dimensions</td>
</tr>
<tr>
<td><code>scale3d(1, 1, 2)</code></td>
<td>Normal X/Y, 2x depth</td>
</tr>
<tr>
<td><code>scale3d(2, 1, 1)</code></td>
<td>2x width only</td>
</tr>
<tr>
<td><code>scale3d(1, 2, 1)</code></td>
<td>2x height only</td>
</tr>
</table>
<h3>scale() vs scale3d()</h3>
<table class="reference-table">
<tr>
<th>Aspect</th>
<th>scale()</th>
<th>scale3d()</th>
</tr>
<tr>
<td><strong>Dimensions</strong></td>
<td>2D (X, Y)</td>
<td>3D (X, Y, Z)</td>
</tr>
<tr>
<td><strong>Values</strong></td>
<td>1 or 2</td>
<td>3 (required)</td>
</tr>
<tr>
<td><strong>Z-axis</strong></td>
<td>Not supported</td>
<td>Supported</td>
</tr>
<tr>
<td><strong>Perspective needed</strong></td>
<td>No</td>
<td>Yes (for Z)</td>
</tr>
<tr>
<td><strong>Use case</strong></td>
<td>Simple resizing</td>
<td>3D effects</td>
</tr>
</table>
</section>
<!-- ====== 8. BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>scale(1.05)</code> for subtle hover effects on buttons</li>
<li>Use <code>scale(0.98)</code> for active/pressed states</li>
<li>Use <code>scale(1.1)</code> for image gallery hover effects</li>
<li>Use <code>scale3d()</code> for 3D scaling with perspective</li>
<li>Combine scale with other transforms (rotate, translate)</li>
<li>Use <code>transform-origin</code> to control the scaling pivot</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't scale elements too much (can cause blurriness or overflow)</li>
<li>Don't use <code>scale3d()</code> without <code>perspective</code> on the parent</li>
<li>Don't scale text dramatically (can affect readability)</li>
<li>Don't use negative scale values without understanding the mirror effect</li>
<li>Don't forget that scaling doesn't affect layout</li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.btn-scale:hover,
.gallery-item:hover {
transform: none;
}
}
/* Control scaling pivot */
.box {
transform-origin: top left;
transform: scale(1.5);
}
</div>
</section>
</body>
</html>
Quick Reference
| Function | Values | Effect |
|---|---|---|
scale(s) | 1 value | Uniform scaling |
scale(s1, s2) | 2 values | X and Y scaling |
scale3d(s1, s2, s3) | 3 values | X, Y, and Z scaling |
scale() Values
| Value | Effect |
|---|---|
scale(1) | Original size |
scale(1.5) | 1.5x size (both axes) |
scale(2) | 2x size (both axes) |
scale(0.5) | Half size |
scale(2, 1) | 2x width, 1x height |
scale(1, 2) | 1x width, 2x height |
scale3d() Values
| Value | Effect |
|---|---|
scale3d(1, 1, 1) | No scaling |
scale3d(2, 2, 2) | 2x in all dimensions |
scale3d(1, 1, 2) | Normal X/Y, 2x depth |
scale3d(2, 1, 1) | 2x width only |
scale3d(1, 2, 1) | 2x height only |
scale() vs scale3d()
| Aspect | scale() | scale3d() |
|---|---|---|
| Dimensions | 2D (X, Y) | 3D (X, Y, Z) |
| Values | 1 or 2 | 3 (required) |
| Z-axis | Not supported | Supported |
| Perspective needed | No | Yes (for Z) |
| Use case | Simple resizing | 3D effects |
Best Practices
✅ Do This:
/* Subtle hover effect */
.btn:hover {
transform: scale(1.05);
}
/* Pressed state */
.btn:active {
transform: scale(0.98);
}
/* Image gallery hover */
.gallery-item:hover {
transform: scale(1.1);
}
/* 3D scaling with perspective */
.scene {
perspective: 800px;
}
.box {
transform: scale3d(1.5, 1.5, 2);
}
/* Control scaling pivot */
.box {
transform-origin: top left;
transform: scale(1.5);
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.btn:hover {
transform: none;
}
}
❌ Don’t Do This:
/* Don't scale too much */
.box:hover {
transform: scale(5); /* Can cause overflow and blurriness */
}
/* Don't use scale3d without perspective */
.box {
transform: scale3d(2, 2, 2); /* Z scaling won't be visible */
/* Add perspective on the parent */
}
/* Don't scale text dramatically */
h1:hover {
transform: scale(3); /* Text becomes blurry and hard to read */
}
Pro Tip: scale() is one of the most versatile transforms. Use subtle scaling (1.02–1.1) for hover effects on buttons, cards, and images — it adds a satisfying “pop” without being distracting. Use scale(0.98) for active/pressed states to simulate a button being pushed down. For 3D effects, use scale3d() with perspective on the parent. Remember: scaling doesn’t affect layout — other elements stay in place, so you don’t need to worry about breaking your page structure. And always respect prefers-reduced-motion for users who are sensitive to motion!
50. transform scaleX, scaleY, and scaleZ
These three functions are the individual axis scaling functions in CSS transforms. Each one scales an element along a specific axis: X (horizontal), Y (vertical), or Z (depth).
Overview of Functions
| Function | Axis | Equivalent | Effect |
|---|---|---|---|
scaleX() | Horizontal | scale3d(s, 1, 1) | Stretches width |
scaleY() | Vertical | scale3d(1, s, 1) | Stretches height |
scaleZ() | Depth | scale3d(1, 1, s) | Stretches depth |
Understanding the Axes
Y (vertical)
↑
│
│
│
└──────────→ X (horizontal)
/
/
↙
Z (depth, toward viewer)
| Axis | Direction | Scaling Effect |
|---|---|---|
| X | Horizontal (left–right) | Width increases/decreases |
| Y | Vertical (up–down) | Height increases/decreases |
| Z | Depth (toward viewer) | Depth increases/decreases |
1. scaleX()
Scales an element horizontally (along the X-axis). Only the width changes.
.scaleX {
transform: scaleX(2);
}
| Value | Effect |
|---|---|
scaleX(1) | Original width |
scaleX(2) | 2x width |
scaleX(0.5) | Half width |
scaleX(-1) | Flipped horizontally (mirror) |
scaleX(0) | Zero width (invisible) |
Equivalent: scale3d(2, 1, 1) or scale(2, 1)
Use cases:
- Stretching text or images horizontally
- Creating a “wide” effect
- Animating width changes (without affecting layout)
- Flipping elements horizontally
2. scaleY()
Scales an element vertically (along the Y-axis). Only the height changes.
.scaleY {
transform: scaleY(2);
}
| Value | Effect |
|---|---|
scaleY(1) | Original height |
scaleY(2) | 2x height |
scaleY(0.5) | Half height |
scaleY(-1) | Flipped vertically (mirror) |
scaleY(0) | Zero height (invisible) |
Equivalent: scale3d(1, 2, 1) or scale(1, 2)
Use cases:
- Stretching text or images vertically
- Creating a “tall” effect
- Animating height changes (without affecting layout)
- Flipping elements vertically
3. scaleZ()
Scales an element in depth (along the Z-axis). Requires perspective on the parent to be visible.
.scaleZ {
transform: scaleZ(2);
}
| Value | Effect |
|---|---|
scaleZ(1) | Original depth |
scaleZ(2) | 2x depth |
scaleZ(0.5) | Half depth |
scaleZ(0) | Zero depth |
Equivalent: scale3d(1, 1, 2)
Important:
scaleZ()only affects 3D-positioned children (those withtranslateZ)- The element itself doesn’t appear to change size
- Requires
perspectiveon the parent andtransform-style: preserve-3don the container - Best used with 3D scenes (cubes, cards, etc.)
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transform scaleX, scaleY, and scaleZ</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 50px;
justify-content: center;
align-items: center;
padding: 50px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 250px;
}
.demo-area.perspective {
perspective: 600px;
}
.box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.9rem;
border-radius: 8px;
transition: transform 0.5s;
cursor: pointer;
text-align: center;
padding: 5px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.box:hover {
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.4);
}
/* Individual scale functions */
.scaleX {
transform: scaleX(2);
}
.scaleX:hover {
transform: scaleX(0.5);
}
.scaleY {
transform: scaleY(2);
}
.scaleY:hover {
transform: scaleY(0.5);
}
.scaleZ {
transform: scaleZ(2);
}
.scaleZ:hover {
transform: scaleZ(0.5);
}
/* Comparison grid */
.scale-comparison {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 20px;
margin: 20px 0;
}
.scale-item {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 2px solid #ddd;
transition: all 0.3s;
}
.scale-item:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.15);
}
.scale-item .demo-box {
width: 60px;
height: 60px;
margin: 0 auto 10px;
background: linear-gradient(135deg, #28a745, #20c997);
border-radius: 8px;
transition: transform 0.3s;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.7rem;
font-weight: bold;
}
.scale-item .label {
font-weight: bold;
font-size: 0.85rem;
color: #007bff;
}
/* ====== SCALEX DEMO ====== */
.scaleX-demo {
display: flex;
flex-direction: column;
gap: 15px;
align-items: center;
margin: 15px 0;
}
.scaleX-row {
display: flex;
gap: 15px;
align-items: center;
flex-wrap: wrap;
justify-content: center;
}
.scaleX-box {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.8rem;
transition: transform 0.3s;
}
.scaleX-1 { transform: scaleX(1); }
.scaleX-2 { transform: scaleX(2); }
.scaleX-0-5 { transform: scaleX(0.5); }
.scaleX-neg { transform: scaleX(-1); }
/* ====== SCALEY DEMO ====== */
.scaleY-demo {
display: flex;
gap: 20px;
align-items: flex-end;
justify-content: center;
margin: 15px 0;
flex-wrap: wrap;
}
.scaleY-box {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #28a745, #20c997);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.8rem;
transition: transform 0.3s;
transform-origin: bottom;
}
.scaleY-1 { transform: scaleY(1); }
.scaleY-2 { transform: scaleY(2); }
.scaleY-0-5 { transform: scaleY(0.5); }
.scaleY-neg { transform: scaleY(-1); }
/* ====== PRACTICAL: BAR CHART ====== */
.bar-chart {
display: flex;
gap: 20px;
align-items: flex-end;
justify-content: center;
height: 200px;
margin: 20px 0;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
border: 2px solid #ddd;
}
.bar {
width: 60px;
background: linear-gradient(180deg, #007bff, #6c5ce7);
border-radius: 8px 8px 0 0;
transform-origin: bottom;
transition: transform 0.5s;
display: flex;
align-items: flex-start;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.8rem;
padding-top: 5px;
}
.bar:hover {
transform: scaleY(1.1);
}
/* ====== PRACTICAL: HORIZONTAL PROGRESS ====== */
.progress-container {
width: 100%;
max-width: 500px;
height: 30px;
background: #e9ecef;
border-radius: 15px;
overflow: hidden;
margin: 15px auto;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #007bff, #6c5ce7);
border-radius: 15px;
transform-origin: left;
transition: transform 1s;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 10px;
color: white;
font-weight: bold;
font-size: 0.8rem;
}
.progress-bar:hover {
transform: scaleX(1.05);
}
/* ====== 3D CUBE WITH SCALEZ ====== */
.cube-scene {
width: 150px;
height: 150px;
perspective: 800px;
margin: 30px auto;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation: pulseCubeZ 3s infinite alternate;
}
@keyframes pulseCubeZ {
from { transform: rotateX(-15deg) rotateY(-15deg) scaleZ(1); }
to { transform: rotateX(-15deg) rotateY(-15deg) scaleZ(2); }
}
.cube .face {
position: absolute;
width: 150px;
height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 0.9rem;
text-shadow: 0 2px 5px rgba(0,0,0,0.5);
border-radius: 8px;
}
.cube .front {
background: rgba(255, 0, 0, 0.8);
transform: translateZ(75px);
}
.cube .back {
background: rgba(0, 255, 0, 0.8);
transform: rotateY(180deg) translateZ(75px);
}
.cube .right {
background: rgba(0, 0, 255, 0.8);
transform: rotateY(90deg) translateZ(75px);
}
.cube .left {
background: rgba(255, 255, 0, 0.8);
transform: rotateY(-90deg) translateZ(75px);
}
.cube .top {
background: rgba(255, 0, 255, 0.8);
transform: rotateX(90deg) translateZ(75px);
}
.cube .bottom {
background: rgba(0, 255, 255, 0.8);
transform: rotateX(-90deg) translateZ(75px);
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>transform scaleX, scaleY, and scaleZ</h1>
<!-- ====== 1. BASIC SCALE FUNCTIONS ====== -->
<section>
<h2>1. Basic Axis Scaling</h2>
<p>Hover over each box to see the scale change!</p>
<div class="demo-area">
<div class="box scaleX">scaleX(2)</div>
<div class="box scaleY">scaleY(2)</div>
<div class="box scaleZ">scaleZ(2)</div>
</div>
<p class="note"><strong>scaleX</strong> stretches horizontally. <strong>scaleY</strong> stretches vertically. <strong>scaleZ</strong> stretches depth (needs perspective).</p>
<div class="code-block">
/* Scale horizontally (X-axis) */
.scaleX {
transform: scaleX(2); /* 2x width */
}
/* Scale vertically (Y-axis) */
.scaleY {
transform: scaleY(2); /* 2x height */
}
/* Scale in depth (Z-axis) */
.scaleZ {
transform: scaleZ(2); /* 2x depth */
}
</div>
</section>
<!-- ====== 2. AXIS VISUALIZATION ====== -->
<section>
<h2>2. Understanding the Axes</h2>
<div class="scale-comparison">
<div class="scale-item">
<div class="demo-box" style="transform: scaleX(1.5);">X</div>
<div class="label">scaleX()</div>
<p class="note">Stretches width</p>
</div>
<div class="scale-item">
<div class="demo-box" style="transform: scaleY(1.5);">Y</div>
<div class="label">scaleY()</div>
<p class="note">Stretches height</p>
</div>
<div class="scale-item">
<div class="demo-box" style="transform: scaleZ(1.5);">Z</div>
<div class="label">scaleZ()</div>
<p class="note">Stretches depth</p>
</div>
</div>
<div class="code-block">
/* These are equivalent */
scaleX(2) = scale3d(2, 1, 1) = scale(2, 1)
scaleY(2) = scale3d(1, 2, 1) = scale(1, 2)
scaleZ(2) = scale3d(1, 1, 2)
</div>
</section>
<!-- ====== 3. SCALEX COMPARISON ====== -->
<section>
<h2>3. scaleX() Comparison</h2>
<div class="scaleX-demo">
<div class="scaleX-row">
<div class="scaleX-box scaleX-1">scaleX(1)</div>
<div class="scaleX-box scaleX-2">scaleX(2)</div>
<div class="scaleX-box scaleX-0-5">scaleX(0.5)</div>
<div class="scaleX-box scaleX-neg">scaleX(-1)</div>
</div>
</div>
<div class="code-block">
transform: scaleX(1); /* Original width */
transform: scaleX(2); /* 2x width */
transform: scaleX(0.5); /* Half width */
transform: scaleX(-1); /* Flipped horizontally */
</div>
</section>
<!-- ====== 4. SCALEY COMPARISON ====== -->
<section>
<h2>4. scaleY() Comparison</h2>
<div class="scaleY-demo">
<div class="scaleY-box scaleY-1">scaleY(1)</div>
<div class="scaleY-box scaleY-2">scaleY(2)</div>
<div class="scaleY-box scaleY-0-5">scaleY(0.5)</div>
<div class="scaleY-box scaleY-neg">scaleY(-1)</div>
</div>
<div class="code-block">
transform: scaleY(1); /* Original height */
transform: scaleY(2); /* 2x height */
transform: scaleY(0.5); /* Half height */
transform: scaleY(-1); /* Flipped vertically */
</div>
</section>
<!-- ====== 5. PRACTICAL: BAR CHART ====== -->
<section>
<h2>5. Practical Example: Bar Chart</h2>
<p>Bars grow using <code>scaleY()</code> — hover over each bar to see the effect.</p>
<div class="bar-chart">
<div class="bar" style="height: 40%;">40%</div>
<div class="bar" style="height: 65%;">65%</div>
<div class="bar" style="height: 85%;">85%</div>
<div class="bar" style="height: 55%;">55%</div>
<div class="bar" style="height: 75%;">75%</div>
</div>
<div class="code-block">
.bar {
transform-origin: bottom;
transition: transform 0.5s;
}
.bar:hover {
transform: scaleY(1.1); /* Grows from the bottom */
}
</div>
<p class="note">Use <code>transform-origin: bottom</code> so bars grow upward from their base.</p>
</section>
<!-- ====== 6. PRACTICAL: PROGRESS BAR ====== -->
<section>
<h2>6. Practical Example: Progress Bar</h2>
<p>The progress bar uses <code>scaleX()</code> to fill from the left.</p>
<div class="progress-container">
<div class="progress-bar" style="width: 75%;">75%</div>
</div>
<div class="code-block">
.progress-bar {
transform-origin: left;
transition: transform 1s;
}
.progress-bar:hover {
transform: scaleX(1.05); /* Slight growth on hover */
}
</div>
<p class="note">Use <code>transform-origin: left</code> so the bar grows from the left edge.</p>
</section>
<!-- ====== 7. PRACTICAL: 3D CUBE WITH SCALEZ ====== -->
<section>
<h2>7. Practical Example: 3D Cube with scaleZ()</h2>
<p>A cube that pulses in depth using <code>scaleZ()</code>.</p>
<div class="cube-scene">
<div class="cube">
<div class="face front">Front</div>
<div class="face back">Back</div>
<div class="face right">Right</div>
<div class="face left">Left</div>
<div class="face top">Top</div>
<div class="face bottom">Bottom</div>
</div>
</div>
<div class="code-block">
.cube {
transform-style: preserve-3d;
animation: pulseCubeZ 3s infinite alternate;
}
@keyframes pulseCubeZ {
from { transform: rotateX(-15deg) rotateY(-15deg) scaleZ(1); }
to { transform: rotateX(-15deg) rotateY(-15deg) scaleZ(2); }
}
</div>
<p class="note"><code>scaleZ()</code> changes the depth of the cube — the front and back faces move apart.</p>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>scaleX() Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
</tr>
<tr>
<td><code>scaleX(1)</code></td>
<td>Original width</td>
</tr>
<tr>
<td><code>scaleX(2)</code></td>
<td>2x width</td>
</tr>
<tr>
<td><code>scaleX(0.5)</code></td>
<td>Half width</td>
</tr>
<tr>
<td><code>scaleX(-1)</code></td>
<td>Flipped horizontally</td>
</tr>
</table>
<h3>scaleY() Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
</tr>
<tr>
<td><code>scaleY(1)</code></td>
<td>Original height</td>
</tr>
<tr>
<td><code>scaleY(2)</code></td>
<td>2x height</td>
</tr>
<tr>
<td><code>scaleY(0.5)</code></td>
<td>Half height</td>
</tr>
<tr>
<td><code>scaleY(-1)</code></td>
<td>Flipped vertically</td>
</tr>
</table>
<h3>scaleZ() Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
</tr>
<tr>
<td><code>scaleZ(1)</code></td>
<td>Original depth</td>
</tr>
<tr>
<td><code>scaleZ(2)</code></td>
<td>2x depth</td>
</tr>
<tr>
<td><code>scaleZ(0.5)</code></td>
<td>Half depth</td>
</tr>
</table>
<h3>Function Equivalents</h3>
<table class="reference-table">
<tr>
<th>Function</th>
<th>Equivalent</th>
</tr>
<tr>
<td><code>scaleX(2)</code></td>
<td><code>scale3d(2, 1, 1)</code> or <code>scale(2, 1)</code></td>
</tr>
<tr>
<td><code>scaleY(2)</code></td>
<td><code>scale3d(1, 2, 1)</code> or <code>scale(1, 2)</code></td>
</tr>
<tr>
<td><code>scaleZ(2)</code></td>
<td><code>scale3d(1, 1, 2)</code></td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>scaleX()</code> for horizontal stretching (progress bars, dividers)</li>
<li>Use <code>scaleY()</code> for vertical stretching (bar charts, accordions)</li>
<li>Use <code>scaleZ()</code> for 3D depth effects (cubes, 3D scenes)</li>
<li>Use <code>transform-origin</code> to control the scaling pivot</li>
<li>Combine with other transforms for complex effects</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>scaleZ()</code> without <code>perspective</code> on the parent</li>
<li>Don't scale elements too much (causes blurriness or overflow)</li>
<li>Don't use <code>scaleX(-1)</code> or <code>scaleY(-1)</code> without understanding the mirror effect</li>
<li>Don't forget that scaling doesn't affect layout</li>
<li>Don't scale text dramatically (affects readability)</li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.bar:hover,
.progress-bar:hover {
transform: none;
}
}
/* Control scaling pivot */
.bar {
transform-origin: bottom; /* Grow from bottom */
}
.progress-bar {
transform-origin: left; /* Fill from left */
}
</div>
</section>
</body>
</html>
Quick Reference
| Function | Axis | Equivalent | Effect |
|---|---|---|---|
scaleX() | Horizontal | scale3d(s, 1, 1) | Stretches width |
scaleY() | Vertical | scale3d(1, s, 1) | Stretches height |
scaleZ() | Depth | scale3d(1, 1, s) | Stretches depth |
scaleX() Values
| Value | Effect |
|---|---|
scaleX(1) | Original width |
scaleX(2) | 2x width |
scaleX(0.5) | Half width |
scaleX(-1) | Flipped horizontally |
scaleY() Values
| Value | Effect |
|---|---|
scaleY(1) | Original height |
scaleY(2) | 2x height |
scaleY(0.5) | Half height |
scaleY(-1) | Flipped vertically |
scaleZ() Values
| Value | Effect |
|---|---|
scaleZ(1) | Original depth |
scaleZ(2) | 2x depth |
scaleZ(0.5) | Half depth |
Best Practices
✅ Do This:
/* Progress bar fills from left */
.progress-bar {
transform-origin: left;
transform: scaleX(0.75);
}
/* Bar chart grows from bottom */
.bar {
transform-origin: bottom;
transform: scaleY(0.8);
}
/* 3D cube pulses in depth */
.cube {
transform-style: preserve-3d;
transform: scaleZ(2);
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.bar:hover {
transform: none;
}
}
❌ Don’t Do This:
/* Don't use scaleZ without perspective */
.box {
transform: scaleZ(2); /* Won't be visible */
/* Add perspective on the parent */
}
/* Don't scale too much */
.box:hover {
transform: scaleX(5); /* Can cause overflow */
}
/* Don't forget transform-origin for bars */
.bar {
transform: scaleY(2); /* Grows from center by default */
/* Use transform-origin: bottom for upward growth */
}
Pro Tip: scaleX() and scaleY() are perfect for data visualization — use scaleY() with transform-origin: bottom for bar charts that grow upward, and scaleX() with transform-origin: left for progress bars that fill from the left. scaleZ() is your gateway to 3D depth effects — it changes how far apart the front and back faces of a 3D object are. Remember: scaleZ() requires perspective on the parent and transform-style: preserve-3d on the container to be visible. And always respect prefers-reduced-motion for users who are sensitive to animation!
51. transform skew(), skewX(), and skewY()
These transform functions slant or distort elements along the X or Y axis, creating a parallelogram effect. They’re useful for dynamic banners, stylized cards, and geometric layouts.
Overview of Functions
| Function | Axis | Effect | Equivalent |
|---|---|---|---|
skew() | X and/or Y | Slants both axes | skewX() + skewY() |
skewX() | Horizontal | Slants left/right | skew(angle, 0) |
skewY() | Vertical | Slants up/down | skew(0, angle) |
Understanding Skew
Original: skewX(20deg): skewY(20deg):
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ │ │ /│ │ │
│ Element │ │ Element / │ │ Element │
│ │ │ / │ │ │
│ │ │ / │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
Top shifted right Left shifted down
| Skew Type | Effect | Visual |
|---|---|---|
| skewX() | Horizontal slant | Top moves right, bottom stays |
| skewY() | Vertical slant | Left moves down, right stays |
| skew() | Both | Parallelogram shape |
1. skew()
The skew() function applies a 2D skew along both axes.
.skewed-element {
transform: skew(30deg);
}
Syntax
skew(angle) /* Skew X only */
skew(xAngle, yAngle) /* Skew X and Y */
| Syntax | Effect |
|---|---|
skew(30deg) | Skew X by 30° |
skew(30deg, 20deg) | Skew X by 30°, Y by 20° |
skew(0, 20deg) | Skew Y only |
Angle Units
| Unit | Example | Description |
|---|---|---|
deg | 30deg | Degrees |
grad | 100grad | Gradians (400 = full circle) |
rad | 0.5rad | Radians (2π = full circle) |
turn | 0.25turn | Turns (1 = full circle) |
2. skewX()
The skewX() function skews an element horizontally along the X-axis.
.skewX-box {
transform: skewX(30deg);
}
| Value | Effect |
|---|---|
skewX(0deg) | No skew |
skewX(30deg) | Top shifts right by 30° |
skewX(-30deg) | Top shifts left by 30° |
skewX(90deg) | Extreme distortion |
Equivalent: skew(30deg) or skew(30deg, 0)
Use cases:
- Slanted banners and ribbons
- Dynamic card layouts
- Stylized buttons
- Perspective-like effects
3. skewY()
The skewY() function skews an element vertically along the Y-axis.
.skewY-box {
transform: skewY(20deg);
}
| Value | Effect |
|---|---|
skewY(0deg) | No skew |
skewY(20deg) | Left side shifts down by 20° |
skewY(-20deg) | Left side shifts up by 20° |
skewY(90deg) | Extreme distortion |
Equivalent: skew(0, 20deg)
Use cases:
- Diagonal section dividers
- Slanted headers
- Stylized quotes
- Vertical rhythm effects
4. Combining skewX() and skewY()
You can combine both skews for a parallelogram effect.
.combined-skew-box {
transform: skewX(30deg) skewY(20deg);
}
This creates a shape that’s slanted both horizontally and vertically.
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transform skew(), skewX(), and skewY()</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 50px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 250px;
}
.box {
width: 180px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.9rem;
border-radius: 8px;
transition: transform 0.5s;
cursor: pointer;
text-align: center;
padding: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.box:hover {
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.4);
}
/* Individual skews */
.skewX-box {
background: linear-gradient(135deg, #4CAF50, #2e7d32);
transform: skewX(30deg);
}
.skewX-box:hover {
transform: skewX(-30deg);
}
.skewY-box {
background: linear-gradient(135deg, #2196F3, #0d47a1);
transform: skewY(20deg);
}
.skewY-box:hover {
transform: skewY(-20deg);
}
.combined-skew-box {
background: linear-gradient(135deg, #FF5722, #bf360c);
transform: skewX(30deg) skewY(20deg);
}
.combined-skew-box:hover {
transform: skewX(-30deg) skewY(-20deg);
}
/* Skew demo for text */
.skewed-text {
width: 200px;
height: 100px;
background: linear-gradient(135deg, #6c5ce7, #4a2a9e);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
border-radius: 8px;
transform: skew(15deg);
transition: transform 0.3s;
}
.skewed-text:hover {
transform: skew(-15deg);
}
/* Angle comparison */
.angle-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin: 20px 0;
}
.angle-item {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 2px solid #ddd;
transition: all 0.3s;
}
.angle-item:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.15);
}
.angle-item .demo-box {
width: 80px;
height: 60px;
margin: 0 auto 10px;
background: linear-gradient(135deg, #28a745, #20c997);
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.7rem;
font-weight: bold;
transition: transform 0.3s;
}
.angle-item .label {
font-weight: bold;
font-size: 0.85rem;
color: #007bff;
}
/* Angle unit demo */
.unit-demo {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
margin: 15px 0;
}
.unit-box {
width: 120px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.75rem;
border-radius: 8px;
background: linear-gradient(135deg, #dc3545, #a71d2a);
transition: transform 0.3s;
}
/* ====== PRACTICAL: SLANTED BANNER ====== */
.banner-container {
position: relative;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
overflow: hidden;
margin: 15px 0;
}
.banner {
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
padding: 30px 50px;
transform: skewX(-10deg);
text-align: center;
font-size: 1.5rem;
font-weight: bold;
box-shadow: 0 10px 30px rgba(0, 123, 255, 0.3);
margin: 0 -20px;
}
.banner span {
display: inline-block;
transform: skewX(10deg); /* Counter-skew the text */
}
/* ====== PRACTICAL: SLANTED CARD ====== */
.card-container {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
margin: 20px 0;
}
.slant-card {
width: 220px;
height: 280px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
text-align: center;
padding: 20px;
transform: skewY(-5deg);
transition: all 0.3s;
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.2);
cursor: pointer;
}
.slant-card:hover {
transform: skewY(0deg) scale(1.05);
box-shadow: 0 15px 40px rgba(0, 123, 255, 0.3);
}
.slant-card .icon {
font-size: 2.5rem;
margin-bottom: 15px;
}
.slant-card h4 {
margin: 0 0 10px 0;
}
.slant-card p {
font-size: 0.85rem;
opacity: 0.95;
margin: 0;
}
/* ====== PRACTICAL: 3D PERSPECTIVE SKEW ====== */
.perspective-scene {
perspective: 800px;
margin: 20px auto;
width: 300px;
}
.perspective-skew {
width: 100%;
padding: 30px;
background: linear-gradient(135deg, #28a745, #20c997);
color: white;
text-align: center;
font-weight: bold;
font-size: 1.2rem;
border-radius: 12px;
transform: rotateY(20deg) skewY(5deg);
transition: transform 0.5s;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
cursor: pointer;
}
.perspective-skew:hover {
transform: rotateY(0deg) skewY(0deg);
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>transform skew(), skewX(), and skewY()</h1>
<!-- ====== 1. BASIC SKEW FUNCTIONS ====== -->
<section>
<h2>1. Basic Skew Functions</h2>
<p>Hover over each box to see the skew change!</p>
<div class="demo-area">
<div class="box skewX-box">skewX(30deg)</div>
<div class="box skewY-box">skewY(20deg)</div>
<div class="box combined-skew-box">skewX(30deg)<br>skewY(20deg)</div>
</div>
<p class="note"><strong>skewX</strong> slants horizontally. <strong>skewY</strong> slants vertically. Combining both creates a parallelogram.</p>
<div class="code-block">
/* Skew horizontally (X-axis) */
.skewX-box {
transform: skewX(30deg);
}
/* Skew vertically (Y-axis) */
.skewY-box {
transform: skewY(20deg);
}
/* Combine both skews */
.combined-skew-box {
transform: skewX(30deg) skewY(20deg);
}
</div>
</section>
<!-- ====== 2. ANGLE COMPARISON ====== -->
<section>
<h2>2. Skew Angle Comparison</h2>
<div class="angle-grid">
<div class="angle-item">
<div class="demo-box" style="transform: skewX(0deg);">0°</div>
<div class="label">skewX(0deg)</div>
<p class="note">No skew</p>
</div>
<div class="angle-item">
<div class="demo-box" style="transform: skewX(15deg);">15°</div>
<div class="label">skewX(15deg)</div>
<p class="note">Subtle slant</p>
</div>
<div class="angle-item">
<div class="demo-box" style="transform: skewX(30deg);">30°</div>
<div class="label">skewX(30deg)</div>
<p class="note">Moderate slant</p>
</div>
<div class="angle-item">
<div class="demo-box" style="transform: skewX(45deg);">45°</div>
<div class="label">skewX(45deg)</div>
<p class="note">Strong slant</p>
</div>
<div class="angle-item">
<div class="demo-box" style="transform: skewX(-30deg);">-30°</div>
<div class="label">skewX(-30deg)</div>
<p class="note">Opposite direction</p>
</div>
</div>
<div class="code-block">
transform: skewX(0deg); /* No skew */
transform: skewX(15deg); /* Subtle slant */
transform: skewX(30deg); /* Moderate slant */
transform: skewX(45deg); /* Strong slant */
transform: skewX(-30deg); /* Opposite direction */
</div>
</section>
<!-- ====== 3. ANGLE UNITS ====== -->
<section>
<h2>3. Angle Units</h2>
<p>Skew angles can be specified in different units.</p>
<div class="unit-demo">
<div class="unit-box" style="transform: skew(30deg);">30deg</div>
<div class="unit-box" style="transform: skew(0.05turn);">0.05turn</div>
<div class="unit-box" style="transform: skew(10grad);">10grad</div>
<div class="unit-box" style="transform: skew(0.5rad);">0.5rad</div>
</div>
<div class="code-block">
/* Degrees */
transform: skew(30deg);
/* Turns (0.05 turn = 18°) */
transform: skew(0.05turn);
/* Gradians (10grad = 9°) */
transform: skew(10grad);
/* Radians (0.5rad ≈ 28.6°) */
transform: skew(0.5rad);
</div>
<table class="reference-table">
<tr>
<th>Unit</th>
<th>Full Circle</th>
<th>Example</th>
</tr>
<tr>
<td><code>deg</code></td>
<td>360°</td>
<td><code>30deg</code></td>
</tr>
<tr>
<td><code>grad</code></td>
<td>400</td>
<td><code>100grad</code></td>
</tr>
<tr>
<td><code>rad</code></td>
<td>2π</td>
<td><code>0.5rad</code></td>
</tr>
<tr>
<td><code>turn</code></td>
<td>1</td>
<td><code>0.25turn</code></td>
</tr>
</table>
</section>
<!-- ====== 4. PRACTICAL: SLANTED BANNER ====== -->
<section>
<h2>4. Practical Example: Slanted Banner</h2>
<p>A banner with a slanted design — the text is counter-skewed to stay upright.</p>
<div class="banner-container">
<div class="banner">
<span>🎉 Special Offer! 🎉</span>
</div>
</div>
<div class="code-block">
.banner {
transform: skewX(-10deg); /* Slant the banner */
}
.banner span {
transform: skewX(10deg); /* Counter-skew the text */
display: inline-block;
}
</div>
<p class="note">Use a counter-skew on the text to keep it upright while the background is slanted.</p>
</section>
<!-- ====== 5. PRACTICAL: SLANTED CARDS ====== -->
<section>
<h2>5. Practical Example: Slanted Cards</h2>
<p>Hover over each card to straighten it.</p>
<div class="card-container">
<div class="slant-card">
<div class="icon">🚀</div>
<h4>Fast</h4>
<p>Lightning-fast performance</p>
</div>
<div class="slant-card">
<div class="icon">🎨</div>
<h4>Beautiful</h4>
<p>Stunning design system</p>
</div>
<div class="slant-card">
<div class="icon">🔒</div>
<h4>Secure</h4>
<p>Enterprise-grade security</p>
</div>
</div>
<div class="code-block">
.slant-card {
transform: skewY(-5deg);
transition: all 0.3s;
}
.slant-card:hover {
transform: skewY(0deg) scale(1.05);
}
</div>
</section>
<!-- ====== 6. PRACTICAL: 3D PERSPECTIVE SKEW ====== -->
<section>
<h2>6. Practical Example: 3D Perspective Skew</h2>
<p>Combining skew with rotateY for a 3D perspective effect.</p>
<div class="perspective-scene">
<div class="perspective-skew">
🎯 3D Perspective Skew
</div>
</div>
<div class="code-block">
.perspective-scene {
perspective: 800px;
}
.perspective-skew {
transform: rotateY(20deg) skewY(5deg);
transition: transform 0.5s;
}
.perspective-skew:hover {
transform: rotateY(0deg) skewY(0deg);
}
</div>
</section>
<!-- ====== 7. REFERENCE TABLES ====== -->
<section>
<h2>7. Reference Tables</h2>
<h3>Skew Functions</h3>
<table class="reference-table">
<tr>
<th>Function</th>
<th>Axis</th>
<th>Effect</th>
<th>Equivalent</th>
</tr>
<tr>
<td><code>skew()</code></td>
<td>X and/or Y</td>
<td>Slants both axes</td>
<td>—</td>
</tr>
<tr>
<td><code>skewX()</code></td>
<td>Horizontal</td>
<td>Slants left/right</td>
<td><code>skew(angle, 0)</code></td>
</tr>
<tr>
<td><code>skewY()</code></td>
<td>Vertical</td>
<td>Slants up/down</td>
<td><code>skew(0, angle)</code></td>
</tr>
</table>
<h3>Angle Units</h3>
<table class="reference-table">
<tr>
<th>Unit</th>
<th>Full Circle</th>
<th>Example</th>
</tr>
<tr>
<td><code>deg</code></td>
<td>360°</td>
<td><code>30deg</code></td>
</tr>
<tr>
<td><code>grad</code></td>
<td>400</td>
<td><code>100grad</code></td>
</tr>
<tr>
<td><code>rad</code></td>
<td>2π</td>
<td><code>0.5rad</code></td>
</tr>
<tr>
<td><code>turn</code></td>
<td>1</td>
<td><code>0.25turn</code></td>
</tr>
</table>
<h3>Common Skew Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Effect</th>
<th>Use Case</th>
</tr>
<tr>
<td><code>skewX(10deg)</code></td>
<td>Subtle slant</td>
<td>Buttons, tags</td>
</tr>
<tr>
<td><code>skewX(20deg)</code></td>
<td>Moderate slant</td>
<td>Banners, ribbons</td>
</tr>
<tr>
<td><code>skewX(30deg)</code></td>
<td>Strong slant</td>
<td>Dynamic layouts</td>
</tr>
<tr>
<td><code>skewY(5deg)</code></td>
<td>Subtle vertical</td>
<td>Card layouts</td>
</tr>
<tr>
<td><code>skewY(10deg)</code></td>
<td>Moderate vertical</td>
<td>Section dividers</td>
</tr>
</table>
</section>
<!-- ====== 8. BEST PRACTICES ====== -->
<section>
<h2>8. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>skewX()</code> for slanted banners and ribbons</li>
<li>Use <code>skewY()</code> for diagonal section dividers</li>
<li>Use counter-skew on text to keep it upright</li>
<li>Combine with other transforms for dynamic effects</li>
<li>Use subtle angles (5°–20°) for a refined look</li>
<li>Use <code>transform-origin</code> to control the pivot point</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't skew text without counter-skewing it (makes it hard to read)</li>
<li>Don't use extreme angles (90° makes elements invisible)</li>
<li>Don't overuse skew — it can look chaotic</li>
<li>Don't forget that skew affects the element's bounding box</li>
<li>Don't use skew for critical content that must remain readable</li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.slant-card:hover {
transform: none;
}
}
/* Counter-skew text for readability */
.banner {
transform: skewX(-10deg);
}
.banner span {
display: inline-block;
transform: skewX(10deg);
}
</div>
</section>
</body>
</html>
Quick Reference
| Function | Axis | Effect | Equivalent |
|---|---|---|---|
skew() | X and/or Y | Slants both axes | — |
skewX() | Horizontal | Slants left/right | skew(angle, 0) |
skewY() | Vertical | Slants up/down | skew(0, angle) |
Angle Units
| Unit | Full Circle | Example |
|---|---|---|
deg | 360° | 30deg |
grad | 400 | 100grad |
rad | 2π | 0.5rad |
turn | 1 | 0.25turn |
Common Skew Values
| Value | Effect | Use Case |
|---|---|---|
skewX(10deg) | Subtle slant | Buttons, tags |
skewX(20deg) | Moderate slant | Banners, ribbons |
skewX(30deg) | Strong slant | Dynamic layouts |
skewY(5deg) | Subtle vertical | Card layouts |
skewY(10deg) | Moderate vertical | Section dividers |
Best Practices
✅ Do This:
/* Slanted banner with counter-skewed text */
.banner {
transform: skewX(-10deg);
}
.banner span {
display: inline-block;
transform: skewX(10deg);
}
/* Subtle slanted cards */
.card {
transform: skewY(-3deg);
transition: transform 0.3s;
}
.card:hover {
transform: skewY(0deg);
}
/* Combined skew for parallelogram */
.shape {
transform: skewX(20deg) skewY(10deg);
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.card:hover {
transform: none;
}
}
❌ Don’t Do This:
/* Don't skew text without counter-skewing */
.text {
transform: skewX(30deg); /* Hard to read! */
}
/* Don't use extreme angles */
.box {
transform: skewX(90deg); /* Becomes a line! */
}
/* Don't overuse skew */
.everything {
transform: skewX(20deg) skewY(15deg); /* Chaotic */
}
Pro Tip: skewX() and skewY() are perfect for dynamic, modern designs — slanted banners, ribbons, cards, and section dividers. The key trick is counter-skewing text: if you skew a container by -10deg, skew the text inside by 10deg to keep it upright and readable. Use subtle angles (5°–20°) for a refined look — extreme angles (45°+) can make content unreadable. Remember: skew affects the element’s bounding box, so the element may overflow its container — plan accordingly! And always respect prefers-reduced-motion for users who are sensitive to motion!
52. transform translate() and translate3d()
The translate() and translate3d() functions move elements from their original position — horizontally, vertically, or in 3D space. Unlike position or margin, translation doesn’t affect the layout of other elements.
Overview of Functions
| Function | Dimensions | Values | Description |
|---|---|---|---|
translate() | 2D | x or x, y | Moves along X and Y |
translate3d() | 3D | x, y, z | Moves along X, Y, and Z |
translateX() | 1D | x | Moves horizontally |
translateY() | 1D | y | Moves vertically |
translateZ() | 1D | z | Moves in depth |
Understanding Translation
Original: translate(50px, 30px):
┌─────────┐
│ Box │ ┌─────────┐
│ │ │ Box │
└─────────┘ └─────────┘
← 50px →
↓ 30px
| Translation | Direction |
|---|---|
| Positive X | Right |
| Negative X | Left |
| Positive Y | Down |
| Negative Y | Up |
| Positive Z | Toward viewer |
| Negative Z | Away from viewer |
1. translate() — 2D Translation
Moves an element along the X and Y axes.
.translate-single {
transform: translate(150px);
}
.translate {
transform: translate(50px, 50px);
}
Syntax
translate(x) /* X only */
translate(x, y) /* X and Y */
translate(x, y) /* Both required if two values */
| Syntax | Effect |
|---|---|
translate(150px) | Move 150px right |
translate(50px, 50px) | Move 50px right, 50px down |
translate(-30px, -20px) | Move 30px left, 20px up |
translate(50%) | Move 50% of element’s width |
translate(50%, 50%) | Move 50% of width and height |
Values
| Unit | Example | Description |
|---|---|---|
px | translate(50px) | Pixels |
% | translate(50%) | Percentage of element size |
em | translate(2em) | Relative to font size |
rem | translate(2rem) | Relative to root font size |
Key Points:
- Percentages are relative to the element’s own size, not the parent
translate(50%, 50%)moves the element by 50% of its own width/height- Can mix units:
translate(50px, 25%) - Translation doesn’t affect layout — other elements stay in place
2. translate3d() — 3D Translation
Moves an element along the X, Y, and Z axes.
.translate-3d {
transform: translate3d(50px, 50px, 100px);
}
Syntax
translate3d(x, y, z)
| Value | Axis | Description |
|---|---|---|
x | X | Horizontal movement |
y | Y | Vertical movement |
z | Z | Depth movement (toward/away from viewer) |
Key Points:
translateZ()requiresperspectiveon the parent to be visible- Positive Z moves toward the viewer; negative Z moves away
- Use with
transform-style: preserve-3dfor 3D children
3. translateX(), translateY(), translateZ()
Individual axis translation functions.
.translate-x {
transform: translateX(50px);
}
.translate-y {
transform: translateY(30px);
}
.translate-z {
transform: translateZ(100px);
}
| Function | Axis | Equivalent |
|---|---|---|
translateX(50px) | Horizontal | translate(50px, 0) |
translateY(30px) | Vertical | translate(0, 30px) |
translateZ(100px) | Depth | translate3d(0, 0, 100px) |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transform translate() and translate3d()</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
align-items: center;
padding: 50px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 250px;
}
.demo-area.perspective {
perspective: 800px;
}
.box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.8rem;
border-radius: 8px;
transition: transform 0.5s;
cursor: pointer;
text-align: center;
padding: 5px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.box:hover {
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.4);
}
/* Translate demos */
.translate-single {
transform: translate(150px);
}
.translate-single:hover {
transform: translate(0);
}
.translate {
transform: translate(50px, 50px);
}
.translate:hover {
transform: translate(0, 0);
}
.translate-neg {
transform: translate(-30px, -20px);
}
.translate-neg:hover {
transform: translate(0, 0);
}
.translate-percent {
transform: translate(50%, 50%);
}
.translate-percent:hover {
transform: translate(0, 0);
}
/* Translate3d demos */
.translate-3d {
transform: translate3d(50px, 50px, 100px);
}
.translate-3d:hover {
transform: translate3d(0, 0, 0);
}
.translate-z {
transform: translateZ(100px);
}
.translate-z:hover {
transform: translateZ(0);
}
/* ====== TRANSLATE COMPARISON ====== */
.translate-comparison {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin: 20px 0;
}
.translate-item {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 2px solid #ddd;
transition: all 0.3s;
}
.translate-item:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.15);
}
.translate-item .demo-box {
width: 60px;
height: 60px;
margin: 0 auto 10px;
background: linear-gradient(135deg, #28a745, #20c997);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.7rem;
font-weight: bold;
transition: transform 0.3s;
}
.translate-item .label {
font-weight: bold;
font-size: 0.85rem;
color: #007bff;
}
/* ====== AXIS VISUALIZATION ====== */
.axis-viz {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
margin: 20px 0;
}
.axis-card {
text-align: center;
padding: 20px;
background: white;
border: 2px solid #ddd;
border-radius: 12px;
width: 180px;
transition: all 0.3s;
}
.axis-card:hover {
border-color: #007bff;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.15);
}
.axis-card .axis-icon {
font-size: 2.5rem;
margin-bottom: 10px;
}
.axis-card h4 {
margin: 5px 0;
color: #007bff;
}
.axis-card p {
font-size: 0.85rem;
color: #6c757d;
margin: 5px 0;
}
.axis-card code {
background: #e9ecef;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.8rem;
color: #dc3545;
}
/* ====== PRACTICAL: CENTERING ====== */
.center-container {
position: relative;
height: 250px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
}
.centered-box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 150px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.3);
}
/* ====== PRACTICAL: BUTTON LIFT ====== */
.btn-lift {
padding: 15px 30px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
margin: 5px;
}
.btn-lift:hover {
transform: translateY(-4px);
background: #0056b3;
box-shadow: 0 10px 25px rgba(0, 123, 255, 0.3);
}
.btn-lift:active {
transform: translateY(-1px);
}
/* ====== PRACTICAL: TOOLTIP ====== */
.tooltip-container {
position: relative;
display: inline-block;
margin: 40px 20px;
}
.tooltip-trigger {
padding: 10px 20px;
background: #6c5ce7;
color: white;
border: none;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
}
.tooltip-text {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(-10px);
background: #333;
color: white;
padding: 8px 15px;
border-radius: 6px;
font-size: 0.85rem;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: all 0.3s;
}
.tooltip-text::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #333;
}
.tooltip-container:hover .tooltip-text {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(-5px);
}
/* ====== PRACTICAL: 3D CUBE ====== */
.cube-scene {
width: 150px;
height: 150px;
perspective: 800px;
margin: 30px auto;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation: rotateCube 8s infinite linear;
}
@keyframes rotateCube {
from { transform: rotateX(0) rotateY(0); }
to { transform: rotateX(360deg) rotateY(360deg); }
}
.cube .face {
position: absolute;
width: 150px;
height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
font-size: 0.9rem;
text-shadow: 0 2px 5px rgba(0,0,0,0.5);
border-radius: 8px;
}
.cube .front {
background: rgba(255, 0, 0, 0.8);
transform: translateZ(75px);
}
.cube .back {
background: rgba(0, 255, 0, 0.8);
transform: rotateY(180deg) translateZ(75px);
}
.cube .right {
background: rgba(0, 0, 255, 0.8);
transform: rotateY(90deg) translateZ(75px);
}
.cube .left {
background: rgba(255, 255, 0, 0.8);
transform: rotateY(-90deg) translateZ(75px);
}
.cube .top {
background: rgba(255, 0, 255, 0.8);
transform: rotateX(90deg) translateZ(75px);
}
.cube .bottom {
background: rgba(0, 255, 255, 0.8);
transform: rotateX(-90deg) translateZ(75px);
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>transform translate() and translate3d()</h1>
<!-- ====== 1. TRANSLATE() DEMO ====== -->
<section>
<h2>1. translate() — 2D Translation</h2>
<p>Hover over each box to see it return to its original position.</p>
<div class="demo-area">
<div class="box">Original</div>
<div class="box translate-single">translate(150px)</div>
<div class="box translate">translate(50px, 50px)</div>
<div class="box translate-neg">translate(-30px, -20px)</div>
<div class="box translate-percent">translate(50%, 50%)</div>
</div>
<div class="code-block">
/* Move 150px right */
.translate-single {
transform: translate(150px);
}
/* Move 50px right and down */
.translate {
transform: translate(50px, 50px);
}
/* Move 30px left and 20px up */
.translate-neg {
transform: translate(-30px, -20px);
}
/* Move 50% of its own width and height */
.translate-percent {
transform: translate(50%, 50%);
}
</div>
</section>
<!-- ====== 2. TRANSLATE3D() DEMO ====== -->
<section>
<h2>2. translate3d() — 3D Translation</h2>
<p>Moves elements in 3D space. Requires <code>perspective</code> on the parent.</p>
<div class="demo-area perspective">
<div class="box">Original</div>
<div class="box translate-3d">translate3d<br>(50, 50, 100)</div>
<div class="box translate-z">translateZ(100px)</div>
</div>
<div class="code-block">
/* Move in 3D space */
.translate-3d {
transform: translate3d(50px, 50px, 100px);
}
/* Move only in depth (toward viewer) */
.translate-z {
transform: translateZ(100px);
}
</div>
<p class="note">Positive Z moves the element <strong>toward</strong> the viewer; negative Z moves it <strong>away</strong>.</p>
</section>
<!-- ====== 3. AXIS VISUALIZATION ====== -->
<section>
<h2>3. Understanding the Axes</h2>
<div class="axis-viz">
<div class="axis-card">
<div class="axis-icon">↔️</div>
<h4>translateX()</h4>
<p>Horizontal movement</p>
<code>translateX(50px)</code>
<p class="note">Positive = right<br>Negative = left</p>
</div>
<div class="axis-card">
<div class="axis-icon">↕️</div>
<h4>translateY()</h4>
<p>Vertical movement</p>
<code>translateY(30px)</code>
<p class="note">Positive = down<br>Negative = up</p>
</div>
<div class="axis-card">
<div class="axis-icon">🔍</div>
<h4>translateZ()</h4>
<p>Depth movement</p>
<code>translateZ(100px)</code>
<p class="note">Positive = toward viewer<br>Negative = away</p>
</div>
</div>
<div class="code-block">
/* These are equivalent */
translateX(50px) = translate(50px, 0) = translate3d(50px, 0, 0)
translateY(30px) = translate(0, 30px) = translate3d(0, 30px, 0)
translateZ(100px) = translate3d(0, 0, 100px)
</div>
</section>
<!-- ====== 4. TRANSLATE COMPARISON ====== -->
<section>
<h2>4. Translation Comparison</h2>
<div class="translate-comparison">
<div class="translate-item">
<div class="demo-box" style="transform: translate(0);">0</div>
<div class="label">translate(0)</div>
<p class="note">Original position</p>
</div>
<div class="translate-item">
<div class="demo-box" style="transform: translate(20px);">20px</div>
<div class="label">translate(20px)</div>
<p class="note">Move right</p>
</div>
<div class="translate-item">
<div class="demo-box" style="transform: translate(0, 20px);">Y</div>
<div class="label">translateY(20px)</div>
<p class="note">Move down</p>
</div>
<div class="translate-item">
<div class="demo-box" style="transform: translate(20px, 20px);">XY</div>
<div class="label">translate(20px, 20px)</div>
<p class="note">Move diagonally</p>
</div>
<div class="translate-item">
<div class="demo-box" style="transform: translate(-20px, -20px);">-XY</div>
<div class="label">translate(-20px, -20px)</div>
<p class="note">Move up-left</p>
</div>
</div>
<div class="code-block">
transform: translate(0); /* No movement */
transform: translate(20px); /* Move right */
transform: translate(0, 20px); /* Move down */
transform: translate(20px, 20px); /* Move diagonally */
transform: translate(-20px, -20px); /* Move up-left */
</div>
</section>
<!-- ====== 5. PRACTICAL: PERFECT CENTERING ====== -->
<section>
<h2>5. Practical Example: Perfect Centering</h2>
<p>The classic trick: <code>top: 50%; left: 50%; transform: translate(-50%, -50%);</code></p>
<div class="center-container">
<div class="centered-box">
Perfectly Centered
</div>
</div>
<div class="code-block">
.centered-box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* Move back by half its own width and height */
}
</div>
<p class="note">This works because percentages in <code>translate()</code> are relative to the <strong>element's own size</strong>.</p>
</section>
<!-- ====== 6. PRACTICAL: BUTTON LIFT ====== -->
<section>
<h2>6. Practical Example: Button Lift</h2>
<p>Hover and click the buttons to see the translate effect.</p>
<div style="text-align: center; margin: 15px 0;">
<button class="btn-lift">Hover Me</button>
<button class="btn-lift">Click Me</button>
<button class="btn-lift">Press Me</button>
</div>
<div class="code-block">
.btn-lift {
transition: all 0.3s;
}
.btn-lift:hover {
transform: translateY(-4px); /* Lift up */
}
.btn-lift:active {
transform: translateY(-1px); /* Press down slightly */
}
</div>
</section>
<!-- ====== 7. PRACTICAL: TOOLTIP ====== -->
<section>
<h2>7. Practical Example: Tooltip</h2>
<p>Hover over the button to see a tooltip positioned with <code>translate()</code>.</p>
<div style="text-align: center;">
<div class="tooltip-container">
<button class="tooltip-trigger">Hover Me</button>
<div class="tooltip-text">I'm a tooltip!</div>
</div>
</div>
<div class="code-block">
.tooltip-text {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%); /* Center horizontally */
}
.tooltip-container:hover .tooltip-text {
transform: translateX(-50%) translateY(-5px); /* Animate up */
}
</div>
</section>
<!-- ====== 8. PRACTICAL: 3D CUBE ====== -->
<section>
<h2>8. Practical Example: 3D Cube with translateZ()</h2>
<p>A 3D cube built with <code>translateZ()</code> to position each face.</p>
<div class="cube-scene">
<div class="cube">
<div class="face front">Front</div>
<div class="face back">Back</div>
<div class="face right">Right</div>
<div class="face left">Left</div>
<div class="face top">Top</div>
<div class="face bottom">Bottom</div>
</div>
</div>
<div class="code-block">
.cube {
transform-style: preserve-3d;
}
/* Each face positioned with translateZ */
.front {
transform: translateZ(75px);
}
.back {
transform: rotateY(180deg) translateZ(75px);
}
.right {
transform: rotateY(90deg) translateZ(75px);
}
.left {
transform: rotateY(-90deg) translateZ(75px);
}
.top {
transform: rotateX(90deg) translateZ(75px);
}
.bottom {
transform: rotateX(-90deg) translateZ(75px);
}
</div>
</section>
<!-- ====== 9. REFERENCE TABLES ====== -->
<section>
<h2>9. Reference Tables</h2>
<h3>Translate Functions</h3>
<table class="reference-table">
<tr>
<th>Function</th>
<th>Axes</th>
<th>Example</th>
</tr>
<tr>
<td><code>translate()</code></td>
<td>X, Y</td>
<td><code>translate(50px, 30px)</code></td>
</tr>
<tr>
<td><code>translate3d()</code></td>
<td>X, Y, Z</td>
<td><code>translate3d(50px, 30px, 100px)</code></td>
</tr>
<tr>
<td><code>translateX()</code></td>
<td>X</td>
<td><code>translateX(50px)</code></td>
</tr>
<tr>
<td><code>translateY()</code></td>
<td>Y</td>
<td><code>translateY(30px)</code></td>
</tr>
<tr>
<td><code>translateZ()</code></td>
<td>Z</td>
<td><code>translateZ(100px)</code></td>
</tr>
</table>
<h3>Values</h3>
<table class="reference-table">
<tr>
<th>Unit</th>
<th>Relative To</th>
<th>Example</th>
</tr>
<tr>
<td><code>px</code></td>
<td>Absolute pixels</td>
<td><code>translate(50px)</code></td>
</tr>
<tr>
<td><code>%</code></td>
<td>Element's own size</td>
<td><code>translate(50%)</code></td>
</tr>
<tr>
<td><code>em</code></td>
<td>Font size</td>
<td><code>translate(2em)</code></td>
</tr>
<tr>
<td><code>rem</code></td>
<td>Root font size</td>
<td><code>translate(2rem)</code></td>
</tr>
</table>
<h3>Direction Reference</h3>
<table class="reference-table">
<tr>
<th>Direction</th>
<th>X</th>
<th>Y</th>
<th>Z</th>
</tr>
<tr>
<td>Right</td>
<td>Positive</td>
<td>—</td>
<td>—</td>
</tr>
<tr>
<td>Left</td>
<td>Negative</td>
<td>—</td>
<td>—</td>
</tr>
<tr>
<td>Down</td>
<td>—</td>
<td>Positive</td>
<td>—</td>
</tr>
<tr>
<td>Up</td>
<td>—</td>
<td>Negative</td>
<td>—</td>
</tr>
<tr>
<td>Toward viewer</td>
<td>—</td>
<td>—</td>
<td>Positive</td>
</tr>
<tr>
<td>Away from viewer</td>
<td>—</td>
<td>—</td>
<td>Negative</td>
</tr>
</table>
</section>
<!-- ====== 10. BEST PRACTICES ====== -->
<section>
<h2>10. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>translate()</code> for animations instead of <code>top</code>/<code>left</code> (better performance)</li>
<li>Use <code>translate(-50%, -50%)</code> for perfect centering</li>
<li>Use <code>translateY()</code> for button lift effects</li>
<li>Use <code>translateX()</code> for sliding menus and carousels</li>
<li>Use <code>translateZ()</code> with perspective for 3D effects</li>
<li>Combine with other transforms for complex animations</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't animate <code>top</code>/<code>left</code> when <code>translate()</code> works better</li>
<li>Don't use <code>translateZ()</code> without <code>perspective</code> on the parent</li>
<li>Don't forget that percentages in <code>translate()</code> are relative to the element's own size</li>
<li>Don't use <code>translate()</code> for layout — use it for effects</li>
<li>Don't translate elements so far they go off-screen</li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.btn-lift:hover {
transform: none;
}
}
/* Prefer translate over top/left for animations */
.box {
transition: transform 0.3s;
}
.box:hover {
transform: translateY(-5px); /* Better performance */
}
</div>
</section>
</body>
</html>
Quick Reference
| Function | Axes | Example |
|---|---|---|
translate() | X, Y | translate(50px, 30px) |
translate3d() | X, Y, Z | translate3d(50px, 30px, 100px) |
translateX() | X | translateX(50px) |
translateY() | Y | translateY(30px) |
translateZ() | Z | translateZ(100px) |
Values
| Unit | Relative To | Example |
|---|---|---|
px | Absolute pixels | translate(50px) |
% | Element’s own size | translate(50%) |
em | Font size | translate(2em) |
rem | Root font size | translate(2rem) |
Direction Reference
| Direction | X | Y | Z |
|---|---|---|---|
| Right | Positive | — | — |
| Left | Negative | — | — |
| Down | — | Positive | — |
| Up | — | Negative | — |
| Toward viewer | — | — | Positive |
| Away from viewer | — | — | Negative |
Best Practices
✅ Do This:
/* Perfect centering */
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Button lift */
.btn:hover {
transform: translateY(-4px);
}
/* Prefer translate over top/left for animations */
.box {
transition: transform 0.3s;
}
.box:hover {
transform: translateX(10px);
}
/* 3D depth */
.scene {
perspective: 800px;
}
.box {
transform: translateZ(100px);
}
❌ Don’t Do This:
/* Don't animate top/left (worse performance) */
.box:hover {
top: -5px;
transition: top 0.3s;
}
/* Don't use translateZ without perspective */
.box {
transform: translateZ(100px); /* Won't be visible */
}
/* Don't translate elements off-screen */
.box {
transform: translate(5000px, 0); /* Lost forever */
}
Pro Tip: translate() is one of the most useful transforms — use it for animations instead of top/left for better performance (it’s hardware-accelerated). The classic translate(-50%, -50%) trick is essential for perfect centering. Use translateY(-4px) for button lift effects and translateX() for sliding menus. Remember: percentages in translate() are relative to the element’s own size, not the parent — that’s what makes the centering trick work! And always respect prefers-reduced-motion for accessibility!
53. Transitions
CSS transitions let you smoothly animate changes to CSS properties over a specified duration. They add polish, interactivity, and visual appeal — turning abrupt state changes into fluid motion.
What is a Transition?
A transition defines how a CSS property changes from one state to another over time. Instead of instantly jumping from red to blue, the color gradually shifts over the specified duration.
Key Benefits
| Benefit | Description |
|---|---|
| Smooth animations | Gradual changes instead of instant jumps |
| Interactivity | Responds to hover, focus, and active states |
| Performance | Hardware-accelerated for transform and opacity |
| Simplicity | Pure CSS — no JavaScript required |
| Customizable | Control duration, timing, and delay |
The Four Transition Properties
The transition shorthand combines four individual properties:
| Property | Description | Default |
|---|---|---|
transition-property | Which property to animate | all |
transition-duration | How long the transition takes | 0s |
transition-timing-function | Speed curve of the transition | ease |
transition-delay | Wait before starting | 0s |
1. transition-property
Specifies which CSS property should transition.
.box {
transition-property: background-color, transform;
}
| Value | Description |
|---|---|
none | No property transitions |
all | All animatable properties transition |
property-name | Specific property (e.g., background-color) |
| Multiple | Comma-separated list |
Best practice: Specify individual properties rather than all for better performance.
2. transition-duration
Specifies how long the transition takes.
.box {
transition-duration: 0.5s;
}
| Value | Description |
|---|---|
0s | No transition (instant) |
0.5s | Half a second |
1s | One second |
500ms | 500 milliseconds (same as 0.5s) |
Note: If duration is 0s, the transition is instant — no animation occurs.
3. transition-timing-function
Specifies the speed curve of the transition.
.box {
transition-timing-function: ease-in-out;
}
| Value | Description |
|---|---|
ease | Slow start, fast middle, slow end (default) |
linear | Constant speed |
ease-in | Slow start, fast end |
ease-out | Fast start, slow end |
ease-in-out | Slow start, fast middle, slow end |
cubic-bezier(n,n,n,n) | Custom curve |
steps(n) | Stepped animation |
4. transition-delay
Specifies how long to wait before starting the transition.
.box {
transition-delay: 0.2s;
}
| Value | Description |
|---|---|
0s | Start immediately (default) |
0.2s | Wait 200ms before starting |
1s | Wait 1 second |
The transition Shorthand
/* Syntax */
transition: <property> <duration> <timing-function> <delay>;
/* Examples */
transition: background-color 0.5s ease; /* property, duration, timing */
transition: transform 0.3s ease-in-out 0.1s; /* all four values */
transition: background-color 0.5s, transform 0.3s; /* multiple properties */
transition: all 0.3s ease; /* all properties */
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Transitions</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 200px;
}
.box {
width: 120px;
height: 120px;
background: #007bff;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.85rem;
border-radius: 12px;
cursor: pointer;
text-align: center;
padding: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
/* ====== BASIC TRANSITION ====== */
.transition-box {
width: 150px;
height: 150px;
background-color: #dc3545;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
border-radius: 12px;
transition: background-color 0.5s ease, transform 0.3s ease;
cursor: pointer;
text-align: center;
padding: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.transition-box:hover {
background-color: #007bff;
transform: scale(1.2);
}
/* ====== TIMING FUNCTIONS ====== */
.timing-ease {
transition: transform 1s ease;
}
.timing-ease:hover {
transform: translateX(100px);
}
.timing-linear {
transition: transform 1s linear;
}
.timing-linear:hover {
transform: translateX(100px);
}
.timing-ease-in {
transition: transform 1s ease-in;
}
.timing-ease-in:hover {
transform: translateX(100px);
}
.timing-ease-out {
transition: transform 1s ease-out;
}
.timing-ease-out:hover {
transform: translateX(100px);
}
.timing-ease-in-out {
transition: transform 1s ease-in-out;
}
.timing-ease-in-out:hover {
transform: translateX(100px);
}
.timing-custom {
transition: transform 1s cubic-bezier(0, 1, 1, 0);
}
.timing-custom:hover {
transform: translateX(100px);
}
/* ====== DURATION COMPARISON ====== */
.duration-fast {
transition: transform 0.2s ease;
}
.duration-fast:hover {
transform: scale(1.3);
}
.duration-medium {
transition: transform 0.5s ease;
}
.duration-medium:hover {
transform: scale(1.3);
}
.duration-slow {
transition: transform 1.5s ease;
}
.duration-slow:hover {
transform: scale(1.3);
}
/* ====== DELAY COMPARISON ====== */
.delay-none {
transition: transform 0.5s ease 0s;
}
.delay-none:hover {
transform: translateY(-30px);
}
.delay-short {
transition: transform 0.5s ease 0.2s;
}
.delay-short:hover {
transform: translateY(-30px);
}
.delay-long {
transition: transform 0.5s ease 0.5s;
}
.delay-long:hover {
transform: translateY(-30px);
}
/* ====== MULTIPLE PROPERTIES ====== */
.multi-transition {
transition:
background-color 0.5s ease,
transform 0.3s ease,
border-radius 0.4s ease,
box-shadow 0.5s ease;
}
.multi-transition:hover {
background-color: #28a745;
transform: rotate(10deg) scale(1.1);
border-radius: 50%;
box-shadow: 0 10px 30px rgba(40, 167, 69, 0.4);
}
/* ====== PRACTICAL: BUTTON ====== */
.btn-transition {
padding: 15px 35px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
margin: 5px;
}
.btn-transition:hover {
background: #28a745;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(40, 167, 69, 0.3);
}
.btn-transition:active {
transform: translateY(-1px);
box-shadow: 0 4px 10px rgba(40, 167, 69, 0.2);
}
/* ====== PRACTICAL: CARD ====== */
.card-transition {
background: white;
border: 2px solid #ddd;
border-radius: 12px;
padding: 25px;
max-width: 300px;
margin: 15px auto;
transition: all 0.3s ease;
cursor: pointer;
text-align: center;
}
.card-transition:hover {
transform: translateY(-8px);
border-color: #007bff;
box-shadow: 0 15px 35px rgba(0, 123, 255, 0.15);
}
.card-transition h4 {
margin-top: 0;
color: #007bff;
transition: color 0.3s ease;
}
.card-transition:hover h4 {
color: #28a745;
}
/* ====== PRACTICAL: TOGGLE SWITCH ====== */
.toggle-switch {
position: relative;
width: 70px;
height: 36px;
background: #ccc;
border-radius: 18px;
cursor: pointer;
transition: background 0.3s ease;
margin: 20px auto;
}
.toggle-switch::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 30px;
height: 30px;
background: white;
border-radius: 50%;
transition: transform 0.3s ease, background 0.3s ease;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.toggle-switch:hover {
background: #28a745;
}
.toggle-switch:hover::after {
transform: translateX(34px);
background: #fff;
}
/* ====== PRACTICAL: FADE-IN ====== */
.fade-container {
display: flex;
gap: 20px;
justify-content: center;
flex-wrap: wrap;
margin: 15px 0;
}
.fade-box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.8rem;
opacity: 0.3;
transition: opacity 0.5s ease, transform 0.5s ease;
cursor: pointer;
}
.fade-box:hover {
opacity: 1;
transform: translateY(-10px);
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>CSS Transitions</h1>
<!-- ====== 1. BASIC TRANSITION ====== -->
<section>
<h2>1. Basic Transition</h2>
<p>Hover over the box to see the smooth transition.</p>
<div class="demo-area">
<div class="transition-box">Hover Over Me!</div>
</div>
<div class="code-block">
.transition-box {
transition: background-color 0.5s ease, transform 0.3s ease;
}
.transition-box:hover {
background-color: #007bff;
transform: scale(1.2);
}
</div>
</section>
<!-- ====== 2. TIMING FUNCTIONS ====== -->
<section>
<h2>2. Timing Functions</h2>
<p>Each box uses a different timing function. Hover to compare the motion.</p>
<div class="demo-area" style="flex-direction: column; gap: 15px;">
<div class="box timing-ease" style="width: 200px; height: 60px;">ease (default)</div>
<div class="box timing-linear" style="width: 200px; height: 60px; background: #28a745;">linear</div>
<div class="box timing-ease-in" style="width: 200px; height: 60px; background: #dc3545;">ease-in</div>
<div class="box timing-ease-out" style="width: 200px; height: 60px; background: #ffc107; color: #333;">ease-out</div>
<div class="box timing-ease-in-out" style="width: 200px; height: 60px; background: #6c5ce7;">ease-in-out</div>
<div class="box timing-custom" style="width: 200px; height: 60px; background: #17a2b8;">cubic-bezier(0,1,1,0)</div>
</div>
<div class="code-block">
.timing-ease { transition: transform 1s ease; }
.timing-linear { transition: transform 1s linear; }
.timing-ease-in { transition: transform 1s ease-in; }
.timing-ease-out { transition: transform 1s ease-out; }
.timing-ease-in-out { transition: transform 1s ease-in-out; }
.timing-custom { transition: transform 1s cubic-bezier(0, 1, 1, 0); }
</div>
</section>
<!-- ====== 3. DURATION COMPARISON ====== -->
<section>
<h2>3. Duration Comparison</h2>
<p>Each box uses a different duration. Hover to compare the speed.</p>
<div class="demo-area">
<div class="box duration-fast" style="background: #28a745;">0.2s</div>
<div class="box duration-medium" style="background: #ffc107; color: #333;">0.5s</div>
<div class="box duration-slow" style="background: #dc3545;">1.5s</div>
</div>
<div class="code-block">
.duration-fast { transition: transform 0.2s ease; }
.duration-medium { transition: transform 0.5s ease; }
.duration-slow { transition: transform 1.5s ease; }
</div>
</section>
<!-- ====== 4. DELAY COMPARISON ====== -->
<section>
<h2>4. Delay Comparison</h2>
<p>Each box has a different delay before the transition starts.</p>
<div class="demo-area">
<div class="box delay-none" style="background: #007bff;">0s delay</div>
<div class="box delay-short" style="background: #28a745;">0.2s delay</div>
<div class="box delay-long" style="background: #dc3545;">0.5s delay</div>
</div>
<div class="code-block">
.delay-none { transition: transform 0.5s ease 0s; }
.delay-short { transition: transform 0.5s ease 0.2s; }
.delay-long { transition: transform 0.5s ease 0.5s; }
</div>
</section>
<!-- ====== 5. MULTIPLE PROPERTIES ====== -->
<section>
<h2>5. Multiple Properties</h2>
<p>This box transitions multiple properties at once — background, transform, border-radius, and shadow.</p>
<div class="demo-area">
<div class="box multi-transition">Hover Me</div>
</div>
<div class="code-block">
.multi-transition {
transition:
background-color 0.5s ease,
transform 0.3s ease,
border-radius 0.4s ease,
box-shadow 0.5s ease;
}
.multi-transition:hover {
background-color: #28a745;
transform: rotate(10deg) scale(1.1);
border-radius: 50%;
box-shadow: 0 10px 30px rgba(40, 167, 69, 0.4);
}
</div>
</section>
<!-- ====== 6. PRACTICAL: BUTTON ====== -->
<section>
<h2>6. Practical Example: Button</h2>
<p>Hover and click the buttons to see the transition effects.</p>
<div style="text-align: center; margin: 15px 0;">
<button class="btn-transition">Hover Me</button>
<button class="btn-transition">Click Me</button>
<button class="btn-transition">Press Me</button>
</div>
<div class="code-block">
.btn-transition {
transition: all 0.3s ease;
}
.btn-transition:hover {
background: #28a745;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(40, 167, 69, 0.3);
}
.btn-transition:active {
transform: translateY(-1px);
box-shadow: 0 4px 10px rgba(40, 167, 69, 0.2);
}
</div>
</section>
<!-- ====== 7. PRACTICAL: CARD ====== -->
<section>
<h2>7. Practical Example: Card</h2>
<p>Hover over the card to see the lift and shadow transition.</p>
<div class="card-transition">
<h4>Interactive Card</h4>
<p>Hover over this card to see the transition effect. The border color, shadow, and position all transition smoothly.</p>
</div>
<div class="code-block">
.card-transition {
transition: all 0.3s ease;
}
.card-transition:hover {
transform: translateY(-8px);
border-color: #007bff;
box-shadow: 0 15px 35px rgba(0, 123, 255, 0.15);
}
.card-transition h4 {
transition: color 0.3s ease;
}
.card-transition:hover h4 {
color: #28a745;
}
</div>
</section>
<!-- ====== 8. PRACTICAL: TOGGLE SWITCH ====== -->
<section>
<h2>8. Practical Example: Toggle Switch</h2>
<p>Hover over the switch to see the transition effect.</p>
<div style="text-align: center;">
<div class="toggle-switch"></div>
</div>
<div class="code-block">
.toggle-switch {
transition: background 0.3s ease;
}
.toggle-switch::after {
transition: transform 0.3s ease, background 0.3s ease;
}
.toggle-switch:hover {
background: #28a745;
}
.toggle-switch:hover::after {
transform: translateX(34px);
}
</div>
</section>
<!-- ====== 9. PRACTICAL: FADE-IN ====== -->
<section>
<h2>9. Practical Example: Fade-In</h2>
<p>Hover over each box to see it fade in and lift up.</p>
<div class="fade-container">
<div class="fade-box">Fade 1</div>
<div class="fade-box">Fade 2</div>
<div class="fade-box">Fade 3</div>
<div class="fade-box">Fade 4</div>
</div>
<div class="code-block">
.fade-box {
opacity: 0.3;
transition: opacity 0.5s ease, transform 0.5s ease;
}
.fade-box:hover {
opacity: 1;
transform: translateY(-10px);
}
</div>
</section>
<!-- ====== 10. REFERENCE TABLES ====== -->
<section>
<h2>10. Reference Tables</h2>
<h3>Transition Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Default</th>
</tr>
<tr>
<td><code>transition-property</code></td>
<td>Which property to animate</td>
<td><code>all</code></td>
</tr>
<tr>
<td><code>transition-duration</code></td>
<td>How long the transition takes</td>
<td><code>0s</code></td>
</tr>
<tr>
<td><code>transition-timing-function</code></td>
<td>Speed curve</td>
<td><code>ease</code></td>
</tr>
<tr>
<td><code>transition-delay</code></td>
<td>Wait before starting</td>
<td><code>0s</code></td>
</tr>
</table>
<h3>Timing Functions</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>ease</code></td>
<td>Slow start, fast middle, slow end (default)</td>
</tr>
<tr>
<td><code>linear</code></td>
<td>Constant speed</td>
</tr>
<tr>
<td><code>ease-in</code></td>
<td>Slow start, fast end</td>
</tr>
<tr>
<td><code>ease-out</code></td>
<td>Fast start, slow end</td>
</tr>
<tr>
<td><code>ease-in-out</code></td>
<td>Slow start, fast middle, slow end</td>
</tr>
<tr>
<td><code>cubic-bezier(n,n,n,n)</code></td>
<td>Custom speed curve</td>
</tr>
<tr>
<td><code>steps(n)</code></td>
<td>Stepped animation</td>
</tr>
</table>
<h3>Common Transition Examples</h3>
<table class="reference-table">
<tr>
<th>Example</th>
<th>Description</th>
</tr>
<tr>
<td><code>transition: all 0.3s ease;</code></td>
<td>All properties, 0.3s, ease</td>
</tr>
<tr>
<td><code>transition: background-color 0.5s;</code></td>
<td>Background only, 0.5s</td>
</tr>
<tr>
<td><code>transition: transform 0.3s ease-in-out 0.1s;</code></td>
<td>Transform, 0.3s, custom timing, 0.1s delay</td>
</tr>
<tr>
<td><code>transition: background-color 0.5s, transform 0.3s;</code></td>
<td>Multiple properties with different durations</td>
</tr>
</table>
<h3>Animatable Properties (Common)</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Notes</th>
</tr>
<tr>
<td><code>opacity</code></td>
<td>Hardware-accelerated, very smooth</td>
</tr>
<tr>
<td><code>transform</code></td>
<td>Hardware-accelerated, very smooth</td>
</tr>
<tr>
<td><code>background-color</code></td>
<td>Commonly used, works well</td>
</tr>
<tr>
<td><code>color</code></td>
<td>Works well for text</td>
</tr>
<tr>
<td><code>box-shadow</code></td>
<td>Can be expensive on large elements</td>
</tr>
<tr>
<td><code>border-radius</code></td>
<td>Works well</td>
</tr>
<tr>
<td><code>width</code>/<code>height</code></td>
<td>Can cause layout recalculation</td>
</tr>
</table>
</section>
<!-- ====== 11. BEST PRACTICES ====== -->
<section>
<h2>11. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>transform</code> and <code>opacity</code> for the smoothest animations (hardware-accelerated)</li>
<li>Specify individual properties instead of <code>all</code> for better performance</li>
<li>Use short durations (0.2s–0.5s) for UI feedback</li>
<li>Use <code>ease</code> or <code>ease-in-out</code> for natural motion</li>
<li>Add transitions to hover, focus, and active states</li>
<li>Use <code>transition-delay</code> for staggered animations</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't animate <code>width</code>, <code>height</code>, <code>top</code>, or <code>left</code> (causes layout recalculation)</li>
<li>Don't use long durations (> 1s) for UI feedback</li>
<li>Don't forget to add <code>transition</code> to the base state (not just hover)</li>
<li>Don't animate too many properties at once</li>
<li>Don't use <code>transition: all</code> for everything</li>
<li>Don't forget about <code>prefers-reduced-motion</code></li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.transition-box,
.btn-transition,
.card-transition {
transition: none;
}
}
/* Better performance: use transform instead of top/left */
.box {
transition: transform 0.3s ease;
}
.box:hover {
transform: translateY(-5px); /* Better than top: -5px */
}
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Default |
|---|---|---|
transition-property | Which property to animate | all |
transition-duration | How long the transition takes | 0s |
transition-timing-function | Speed curve | ease |
transition-delay | Wait before starting | 0s |
Timing Functions
| Value | Description |
|---|---|
ease | Slow start, fast middle, slow end (default) |
linear | Constant speed |
ease-in | Slow start, fast end |
ease-out | Fast start, slow end |
ease-in-out | Slow start, fast middle, slow end |
cubic-bezier(n,n,n,n) | Custom speed curve |
Common Transition Examples
| Example | Description |
|---|---|
transition: all 0.3s ease; | All properties, 0.3s, ease |
transition: background-color 0.5s; | Background only, 0.5s |
transition: transform 0.3s ease-in-out 0.1s; | Transform, 0.3s, custom timing, 0.1s delay |
transition: background-color 0.5s, transform 0.3s; | Multiple properties |
Best Practices
✅ Do This:
/* Smooth hover effect */
.box {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.box:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}
/* Button feedback */
.btn {
transition: all 0.2s ease;
}
.btn:hover {
background: #0056b3;
}
.btn:active {
transform: scale(0.98);
}
/* Staggered animation */
.item:nth-child(1) { transition-delay: 0s; }
.item:nth-child(2) { transition-delay: 0.1s; }
.item:nth-child(3) { transition-delay: 0.2s; }
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box {
transition: none;
}
}
❌ Don’t Do This:
/* Don't animate layout properties */
.box {
transition: width 0.3s, height 0.3s; /* Causes layout recalculation */
}
/* Don't forget the base transition */
.box:hover {
transition: all 0.3s; /* Too late — transition won't work on mouse-out */
}
/* Don't use excessively long durations */
.box {
transition: all 3s; /* Too slow for UI */
}
/* Don't use transition: all for everything */
.box {
transition: all 0.5s; /* Can cause unexpected animations */
}
Pro Tip: Transitions are the key to polished, interactive interfaces. Use transform and opacity for the smoothest animations (they’re hardware-accelerated). Keep durations short — 0.2s–0.5s for UI feedback. Always apply the transition to the base state, not just the hover state, so it works on both mouse-in and mouse-out. Use prefers-reduced-motion to respect users who are sensitive to animation. And remember: less is more — a few well-placed transitions are better than animating everything!
54. transition-behavior, transition-delay, transition-duration, and transition-property
These four longhand properties give you precise control over how transitions behave — which properties animate, how long they take, when they start, and how discrete changes are handled.
Overview of Properties
| Property | Description | Default |
|---|---|---|
transition-property | Which properties to animate | all |
transition-duration | How long the transition takes | 0s |
transition-delay | Wait before starting | 0s |
transition-behavior | How discrete properties animate | normal |
transition-timing-function | Speed curve | ease |
1. transition-property
Specifies which CSS properties should transition.
.box {
transition-property: background-color, transform;
}
Values
| Value | Description |
|---|---|
none | No properties transition |
all | All animatable properties transition (default) |
property-name | Specific property (e.g., background-color) |
| Multiple | Comma-separated list |
Best practice: Specify individual properties rather than all for better performance and predictability.
2. transition-duration
Specifies how long the transition takes.
.box {
transition-duration: 1s, 2s;
}
Values
| Value | Description |
|---|---|
0s / 0ms | No transition (instant) |
0.5s | Half a second |
1s | One second |
500ms | 500 milliseconds |
| Multiple | Comma-separated list for multiple properties |
Key Point: If you specify multiple durations, they map to the properties in transition-property in order.
transition-property: background-color, transform;
transition-duration: 1s, 2s;
/* background-color: 1s, transform: 2s */
3. transition-delay
Specifies how long to wait before starting the transition.
.box {
transition-delay: 0.5s;
}
Values
| Value | Description |
|---|---|
0s | Start immediately (default) |
0.2s | Wait 200ms |
0.5s | Wait half a second |
1s | Wait one second |
| Negative | Starts partway through the transition |
| Multiple | Comma-separated list |
Negative delay: A negative value starts the transition already partway through.
transition-delay: -0.5s; /* Starts halfway through a 1s transition */
4. transition-behavior
Specifies how discrete properties (like display, visibility) are animated.
.box {
transition-behavior: allow-discrete;
}
Values
| Value | Description |
|---|---|
normal | Discrete properties don’t animate (default) |
allow-discrete | Discrete properties can animate |
What Are Discrete Properties?
Discrete properties don’t have intermediate values — they jump from one state to another:
| Property | Values |
|---|---|
display | none, block, flex, etc. |
visibility | visible, hidden |
position | static, relative, absolute |
float | left, right, none |
Without allow-discrete: These properties change instantly — no animation.
With allow-discrete: They can be animated when combined with other properties (like opacity and transform).
5. transition-timing-function
Specifies the speed curve of the transition.
.box {
transition-timing-function: ease-in;
}
Values
| Value | Description |
|---|---|
ease | Slow start, fast middle, slow end (default) |
ease-in | Slow start, fast end |
ease-out | Fast start, slow end |
ease-in-out | Slow start, fast middle, slow end |
linear | Constant speed |
step-start | Jump to end immediately |
step-end | Stay at start until the end |
cubic-bezier(n,n,n,n) | Custom curve |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transition-behavior, delay, duration, and property</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 200px;
}
/* ====== TRANSITION-PROPERTY DEMO ====== */
.property-demo {
width: 120px;
height: 120px;
background: #3498db;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.8rem;
border-radius: 12px;
cursor: pointer;
text-align: center;
padding: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.property-bg-only {
transition-property: background-color;
transition-duration: 0.5s;
transition-timing-function: ease;
}
.property-bg-only:hover {
background: #e74c3c;
transform: scale(1.2) rotate(10deg);
}
.property-transform-only {
transition-property: transform;
transition-duration: 0.5s;
transition-timing-function: ease;
}
.property-transform-only:hover {
background: #e74c3c;
transform: scale(1.2) rotate(10deg);
}
.property-both {
transition-property: background-color, transform;
transition-duration: 0.5s;
transition-timing-function: ease;
}
.property-both:hover {
background: #e74c3c;
transform: scale(1.2) rotate(10deg);
}
/* ====== DURATION DEMO ====== */
.duration-box {
width: 100px;
height: 100px;
background: #007bff;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
border-radius: 12px;
cursor: pointer;
transition-property: transform;
transition-timing-function: ease;
}
.duration-0-5:hover {
transition-duration: 0.5s;
transform: scale(1.3);
}
.duration-1:hover {
transition-duration: 1s;
transform: scale(1.3);
}
.duration-2:hover {
transition-duration: 2s;
transform: scale(1.3);
}
/* ====== DELAY DEMO ====== */
.delay-box {
width: 100px;
height: 100px;
background: #28a745;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
border-radius: 12px;
cursor: pointer;
transition-property: transform;
transition-duration: 0.5s;
transition-timing-function: ease;
}
.delay-0:hover {
transition-delay: 0s;
transform: translateY(-40px);
}
.delay-0-5:hover {
transition-delay: 0.5s;
transform: translateY(-40px);
}
.delay-1:hover {
transition-delay: 1s;
transform: translateY(-40px);
}
/* ====== TIMING FUNCTION DEMO ====== */
.timing-box {
width: 100px;
height: 100px;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.75rem;
border-radius: 12px;
cursor: pointer;
transition-property: transform;
transition-duration: 1s;
}
.timing-ease {
background: #007bff;
transition-timing-function: ease;
}
.timing-ease:hover {
transform: translateX(80px);
}
.timing-linear {
background: #28a745;
transition-timing-function: linear;
}
.timing-linear:hover {
transform: translateX(80px);
}
.timing-ease-in {
background: #dc3545;
transition-timing-function: ease-in;
}
.timing-ease-in:hover {
transform: translateX(80px);
}
.timing-ease-out {
background: #ffc107;
color: #333;
transition-timing-function: ease-out;
}
.timing-ease-out:hover {
transform: translateX(80px);
}
.timing-ease-in-out {
background: #6c5ce7;
transition-timing-function: ease-in-out;
}
.timing-ease-in-out:hover {
transform: translateX(80px);
}
/* ====== TRANSITION-BEHAVIOR DEMO ====== */
.behavior-container {
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
}
.behavior-card {
width: 200px;
padding: 20px;
background: #007bff;
color: white;
border-radius: 12px;
text-align: center;
font-weight: bold;
cursor: pointer;
position: relative;
overflow: hidden;
}
.behavior-card .hidden-content {
display: none;
margin-top: 10px;
font-size: 0.85rem;
font-weight: normal;
opacity: 0;
transition: opacity 0.5s ease;
}
/* Without allow-discrete */
.behavior-normal {
transition-property: background-color, display;
transition-duration: 0.5s;
transition-behavior: normal;
}
.behavior-normal:hover {
background: #28a745;
}
.behavior-normal:hover .hidden-content {
display: block;
opacity: 1;
}
/* With allow-discrete */
.behavior-discrete {
transition-property: background-color, display, opacity;
transition-duration: 0.5s;
transition-behavior: allow-discrete;
}
.behavior-discrete:hover {
background: #28a745;
}
.behavior-discrete:hover .hidden-content {
display: block;
opacity: 1;
}
/* ====== PRACTICAL: DROPDOWN ====== */
.dropdown-container {
position: relative;
display: inline-block;
margin: 20px;
}
.dropdown-trigger {
padding: 12px 24px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
min-width: 200px;
background: white;
border: 2px solid #ddd;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
padding: 10px 0;
margin-top: 5px;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition-property: opacity, visibility, transform;
transition-duration: 0.3s;
transition-timing-function: ease;
transition-behavior: allow-discrete;
}
.dropdown-container:hover .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.dropdown-menu a {
display: block;
padding: 10px 20px;
color: #333;
text-decoration: none;
transition: background 0.2s ease;
}
.dropdown-menu a:hover {
background: #e9ecef;
color: #007bff;
}
/* ====== PRACTICAL: MODAL ====== */
.modal-trigger {
padding: 12px 24px;
background: #6c5ce7;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
margin: 20px;
}
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition-property: opacity, visibility;
transition-duration: 0.3s;
transition-behavior: allow-discrete;
z-index: 1000;
}
.modal-content {
background: white;
padding: 30px;
border-radius: 16px;
max-width: 400px;
text-align: center;
transform: scale(0.9);
transition: transform 0.3s ease;
}
.modal-overlay:target {
opacity: 1;
visibility: visible;
}
.modal-overlay:target .modal-content {
transform: scale(1);
}
.modal-content h3 {
margin-top: 0;
color: #6c5ce7;
}
.modal-close {
display: inline-block;
padding: 10px 20px;
background: #dc3545;
color: white;
text-decoration: none;
border-radius: 8px;
font-weight: bold;
margin-top: 15px;
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>transition-behavior, delay, duration, and property</h1>
<!-- ====== 1. TRANSITION-PROPERTY ====== -->
<section>
<h2>1. transition-property</h2>
<p>Hover over each box to see which properties transition.</p>
<div class="demo-area">
<div class="property-demo property-bg-only">
background-color<br>only
</div>
<div class="property-demo property-transform-only">
transform<br>only
</div>
<div class="property-demo property-both">
background + transform
</div>
</div>
<div class="code-block">
/* Only background-color transitions */
.property-bg-only {
transition-property: background-color;
transition-duration: 0.5s;
}
/* Only transform transitions */
.property-transform-only {
transition-property: transform;
transition-duration: 0.5s;
}
/* Both properties transition */
.property-both {
transition-property: background-color, transform;
transition-duration: 0.5s;
}
</div>
<p class="note">Use <code>transition-property</code> to control exactly which properties animate.</p>
</section>
<!-- ====== 2. TRANSITION-DURATION ====== -->
<section>
<h2>2. transition-duration</h2>
<p>Hover over each box to compare the speed.</p>
<div class="demo-area">
<div class="duration-box duration-0-5">0.5s</div>
<div class="duration-box duration-1" style="background: #28a745;">1s</div>
<div class="duration-box duration-2" style="background: #dc3545;">2s</div>
</div>
<div class="code-block">
.duration-0-5:hover {
transition-duration: 0.5s;
transform: scale(1.3);
}
.duration-1:hover {
transition-duration: 1s;
transform: scale(1.3);
}
.duration-2:hover {
transition-duration: 2s;
transform: scale(1.3);
}
</div>
<p class="note">Multiple values can be set for multiple properties: <code>transition-duration: 1s, 2s;</code></p>
</section>
<!-- ====== 3. TRANSITION-DELAY ====== -->
<section>
<h2>3. transition-delay</h2>
<p>Hover over each box to see the delayed transition.</p>
<div class="demo-area">
<div class="delay-box delay-0">0s</div>
<div class="delay-box delay-0-5" style="background: #ffc107; color: #333;">0.5s</div>
<div class="delay-box delay-1" style="background: #dc3545;">1s</div>
</div>
<div class="code-block">
.delay-0:hover {
transition-delay: 0s;
transform: translateY(-40px);
}
.delay-0-5:hover {
transition-delay: 0.5s;
transform: translateY(-40px);
}
.delay-1:hover {
transition-delay: 1s;
transform: translateY(-40px);
}
</div>
<p class="note">Negative delays start the transition partway through: <code>transition-delay: -0.5s;</code></p>
</section>
<!-- ====== 4. TRANSITION-TIMING-FUNCTION ====== -->
<section>
<h2>4. transition-timing-function</h2>
<p>Hover over each box to compare the timing curves.</p>
<div class="demo-area" style="flex-direction: column; gap: 15px; align-items: flex-start;">
<div class="timing-box timing-ease">ease</div>
<div class="timing-box timing-linear">linear</div>
<div class="timing-box timing-ease-in">ease-in</div>
<div class="timing-box timing-ease-out">ease-out</div>
<div class="timing-box timing-ease-in-out">ease-in-out</div>
</div>
<div class="code-block">
.timing-ease { transition-timing-function: ease; }
.timing-linear { transition-timing-function: linear; }
.timing-ease-in { transition-timing-function: ease-in; }
.timing-ease-out { transition-timing-function: ease-out; }
.timing-ease-in-out { transition-timing-function: ease-in-out; }
</div>
</section>
<!-- ====== 5. TRANSITION-BEHAVIOR ====== -->
<section>
<h2>5. transition-behavior</h2>
<p>Controls how <strong>discrete properties</strong> (like <code>display</code>) animate.</p>
<div class="demo-area" style="flex-direction: column;">
<div class="behavior-container">
<div class="behavior-card behavior-normal">
<div>behavior: normal</div>
<div class="hidden-content">This content appears instantly (no transition on display)</div>
</div>
<div class="behavior-card behavior-discrete">
<div>behavior: allow-discrete</div>
<div class="hidden-content">This content can transition with opacity</div>
</div>
</div>
</div>
<div class="code-block">
/* Without allow-discrete — display changes instantly */
.behavior-normal {
transition-property: background-color, display;
transition-duration: 0.5s;
transition-behavior: normal;
}
/* With allow-discrete — display can animate */
.behavior-discrete {
transition-property: background-color, display, opacity;
transition-duration: 0.5s;
transition-behavior: allow-discrete;
}
</div>
<p class="note"><code>allow-discrete</code> is essential for animating <code>display</code>, <code>visibility</code>, and other discrete properties.</p>
</section>
<!-- ====== 6. PRACTICAL: DROPDOWN MENU ====== -->
<section>
<h2>6. Practical Example: Dropdown Menu</h2>
<p>Hover over the button to see the dropdown with <code>allow-discrete</code>.</p>
<div style="text-align: center;">
<div class="dropdown-container">
<button class="dropdown-trigger">Hover Me ▼</button>
<div class="dropdown-menu">
<a href="#">🏠 Home</a>
<a href="#">📄 About</a>
<a href="#">⚙️ Services</a>
<a href="#">📞 Contact</a>
</div>
</div>
</div>
<div class="code-block">
.dropdown-menu {
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition-property: opacity, visibility, transform;
transition-duration: 0.3s;
transition-behavior: allow-discrete;
}
.dropdown-container:hover .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
</div>
<p class="note">Without <code>allow-discrete</code>, the menu would appear/disappear instantly instead of fading in.</p>
</section>
<!-- ====== 7. PRACTICAL: MODAL ====== -->
<section>
<h2>7. Practical Example: Modal</h2>
<p>Click the button to open a modal with a smooth fade-in.</p>
<div style="text-align: center;">
<a href="#modal" class="modal-trigger">Open Modal</a>
</div>
<div id="modal" class="modal-overlay">
<div class="modal-content">
<h3>🎉 Modal Title</h3>
<p>This modal fades in using <code>transition-behavior: allow-discrete</code> with <code>visibility</code> and <code>opacity</code>.</p>
<a href="#" class="modal-close">Close</a>
</div>
</div>
<div class="code-block">
.modal-overlay {
opacity: 0;
visibility: hidden;
transition-property: opacity, visibility;
transition-duration: 0.3s;
transition-behavior: allow-discrete;
}
.modal-overlay:target {
opacity: 1;
visibility: visible;
}
</div>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>Transition Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Default</th>
</tr>
<tr>
<td><code>transition-property</code></td>
<td>Which properties to animate</td>
<td><code>all</code></td>
</tr>
<tr>
<td><code>transition-duration</code></td>
<td>How long the transition takes</td>
<td><code>0s</code></td>
</tr>
<tr>
<td><code>transition-delay</code></td>
<td>Wait before starting</td>
<td><code>0s</code></td>
</tr>
<tr>
<td><code>transition-behavior</code></td>
<td>How discrete properties animate</td>
<td><code>normal</code></td>
</tr>
<tr>
<td><code>transition-timing-function</code></td>
<td>Speed curve</td>
<td><code>ease</code></td>
</tr>
</table>
<h3>transition-property Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>none</code></td>
<td>No properties transition</td>
</tr>
<tr>
<td><code>all</code></td>
<td>All animatable properties transition</td>
</tr>
<tr>
<td><code>property-name</code></td>
<td>Specific property (e.g., <code>background-color</code>)</td>
</tr>
<tr>
<td>Multiple</td>
<td>Comma-separated list</td>
</tr>
</table>
<h3>transition-behavior Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>Discrete properties don't animate (default)</td>
</tr>
<tr>
<td><code>allow-discrete</code></td>
<td>Discrete properties can animate</td>
</tr>
</table>
<h3>Discrete Properties (Common)</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Values</th>
</tr>
<tr>
<td><code>display</code></td>
<td><code>none</code>, <code>block</code>, <code>flex</code></td>
</tr>
<tr>
<td><code>visibility</code></td>
<td><code>visible</code>, <code>hidden</code></td>
</tr>
<tr>
<td><code>position</code></td>
<td><code>static</code>, <code>relative</code>, <code>absolute</code></td>
</tr>
<tr>
<td><code>float</code></td>
<td><code>left</code>, <code>right</code>, <code>none</code></td>
</tr>
</table>
<h3>Timing Functions</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>ease</code></td>
<td>Slow start, fast middle, slow end</td>
</tr>
<tr>
<td><code>linear</code></td>
<td>Constant speed</td>
</tr>
<tr>
<td><code>ease-in</code></td>
<td>Slow start, fast end</td>
</tr>
<tr>
<td><code>ease-out</code></td>
<td>Fast start, slow end</td>
</tr>
<tr>
<td><code>ease-in-out</code></td>
<td>Slow start, fast middle, slow end</td>
</tr>
<tr>
<td><code>step-start</code></td>
<td>Jump to end immediately</td>
</tr>
<tr>
<td><code>step-end</code></td>
<td>Stay at start until the end</td>
</tr>
<tr>
<td><code>cubic-bezier(n,n,n,n)</code></td>
<td>Custom curve</td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>transition-property</code> to specify exact properties (not <code>all</code>)</li>
<li>Use <code>transition-behavior: allow-discrete</code> for <code>display</code>/<code>visibility</code></li>
<li>Use <code>transition-delay</code> for staggered animations</li>
<li>Keep durations short (0.2s–0.5s) for UI feedback</li>
<li>Use <code>ease-out</code> for entering elements and <code>ease-in</code> for exiting</li>
<li>Test with <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't use <code>transition-property: all</code> for everything</li>
<li>Don't forget <code>allow-discrete</code> when animating <code>display</code></li>
<li>Don't use long delays for critical UI feedback</li>
<li>Don't animate layout properties (<code>width</code>, <code>height</code>)</li>
<li>Don't forget that negative delays start partway through</li>
<li>Don't use transitions without respecting <code>prefers-reduced-motion</code></li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box,
.dropdown-menu,
.modal-overlay {
transition: none;
}
}
/* Best practice: specify exact properties */
.box {
transition-property: background-color, transform;
transition-duration: 0.3s, 0.5s;
transition-timing-function: ease, ease-out;
}
/* Enable discrete animations */
.dropdown {
transition-property: opacity, visibility;
transition-duration: 0.3s;
transition-behavior: allow-discrete;
}
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Default |
|---|---|---|
transition-property | Which properties to animate | all |
transition-duration | How long the transition takes | 0s |
transition-delay | Wait before starting | 0s |
transition-behavior | How discrete properties animate | normal |
transition-timing-function | Speed curve | ease |
transition-property Values
| Value | Description |
|---|---|
none | No properties transition |
all | All animatable properties transition |
property-name | Specific property |
| Multiple | Comma-separated list |
transition-behavior Values
| Value | Description |
|---|---|
normal | Discrete properties don’t animate (default) |
allow-discrete | Discrete properties can animate |
Discrete Properties (Common)
| Property | Values |
|---|---|
display | none, block, flex |
visibility | visible, hidden |
position | static, relative, absolute |
float | left, right, none |
Best Practices
✅ Do This:
/* Specify exact properties */
.box {
transition-property: background-color, transform;
transition-duration: 0.3s, 0.5s;
transition-timing-function: ease, ease-out;
}
/* Animate display with allow-discrete */
.dropdown {
transition-property: opacity, visibility, transform;
transition-duration: 0.3s;
transition-behavior: allow-discrete;
}
/* Staggered animation with delay */
.item:nth-child(1) { transition-delay: 0s; }
.item:nth-child(2) { transition-delay: 0.1s; }
.item:nth-child(3) { transition-delay: 0.2s; }
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box {
transition: none;
}
}
❌ Don’t Do This:
/* Don't use transition-property: all */
.box {
transition-property: all; /* Unpredictable and less performant */
}
/* Don't forget allow-discrete */
.modal {
transition-property: visibility, opacity; /* visibility won't animate */
}
/* Don't use long delays for UI */
.btn:hover {
transition-delay: 2s; /* Users will think it's broken */
}
/* Don't animate layout properties */
.box {
transition-property: width, height; /* Causes layout recalculation */
}
Pro Tip: The transition-behavior: allow-discrete property is a game-changer for UI animations. Before it, you couldn’t animate display or visibility — modals, dropdowns, and tooltips would just pop in and out. Now you can create smooth fade-in/fade-out animations for these elements. Use it with opacity and visibility for dropdowns and modals. Remember: transition-property should be specific (not all) for better performance and predictability, and always respect prefers-reduced-motion for users who are sensitive to animation!
55. The animation Property
The animation property is one of the most powerful tools in CSS. It lets you create complex, multi-step animations — with full control over timing, repetition, direction, and more.
What is an Animation?
An animation in CSS is defined by two parts:
@keyframes— defines the stages of the animationanimationproperty — applies the animation to an element
Unlike transitions (which animate between two states), animations can have many stages and run automatically without user interaction.
The animation Shorthand
The animation property is a shorthand for 8 individual animation properties:
| Property | Description | Default |
|---|---|---|
animation-name | Name of the @keyframes rule | none |
animation-duration | How long one cycle takes | 0s |
animation-timing-function | Speed curve | ease |
animation-delay | Wait before starting | 0s |
animation-iteration-count | How many times to repeat | 1 |
animation-direction | Direction of each cycle | normal |
animation-fill-mode | Styles before/after animation | none |
animation-play-state | Running or paused | running |
Syntax
animation: name duration timing-function delay iteration-count direction fill-mode play-state;
Only name and duration are required. All other values are optional.
The @keyframes Rule
The @keyframes rule defines the stages of an animation.
@keyframes exampleAnimation {
0% { background-color: red; transform: scale(1); }
50% { background-color: blue; transform: scale(1.5); }
100% { background-color: green; transform: scale(1); }
}
Syntax Options
Using from and to:
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
Using percentages:
@keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-50px); }
100% { transform: translateY(0); }
}
| Keyword | Equivalent |
|---|---|
from | 0% |
to | 100% |
Individual Animation Properties
1. animation-name
The name of the @keyframes rule to use.
animation-name: exampleAnimation;
2. animation-duration
How long one cycle of the animation takes.
animation-duration: 4s;
3. animation-timing-function
The speed curve of the animation.
animation-timing-function: ease-in-out;
| Value | Description |
|---|---|
ease | Slow start, fast middle, slow end (default) |
linear | Constant speed |
ease-in | Slow start, fast end |
ease-out | Fast start, slow end |
ease-in-out | Slow start, fast middle, slow end |
cubic-bezier(n,n,n,n) | Custom curve |
steps(n) | Stepped animation |
4. animation-delay
How long to wait before starting the animation.
animation-delay: 2s;
5. animation-iteration-count
How many times the animation repeats.
animation-iteration-count: infinite; /* or a number */
| Value | Description |
|---|---|
1 | Plays once (default) |
3 | Plays 3 times |
infinite | Repeats forever |
6. animation-direction
The direction of each cycle.
animation-direction: alternate;
| Value | Description |
|---|---|
normal | Forward each cycle (default) |
reverse | Backward each cycle |
alternate | Forward, then backward |
alternate-reverse | Backward, then forward |
7. animation-fill-mode
What styles apply before and after the animation.
animation-fill-mode: forwards;
| Value | Description |
|---|---|
none | No styles applied outside the animation (default) |
forwards | Keep the last keyframe’s styles |
backwards | Apply the first keyframe’s styles during delay |
both | Apply both forwards and backwards |
8. animation-play-state
Whether the animation is running or paused.
animation-play-state: paused;
| Value | Description |
|---|---|
running | Animation is playing (default) |
paused | Animation is paused |
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The animation Property</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 200px;
overflow: hidden;
}
/* ====== BASIC ANIMATION ====== */
.animation-example {
width: 100px;
height: 100px;
background-color: #dc3545;
border-radius: 12px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
animation-name: exampleAnimation;
animation-duration: 4s;
animation-timing-function: ease-in-out;
animation-delay: 2s;
animation-iteration-count: infinite;
}
@keyframes exampleAnimation {
0% { background-color: #dc3545; transform: scale(1); }
50% { background-color: #007bff; transform: scale(1.5); }
100% { background-color: #28a745; transform: scale(1); }
}
/* ====== SHORTHAND ====== */
.animation-shorthand {
width: 100px;
height: 100px;
background: #6c5ce7;
border-radius: 12px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
animation: bounce 2s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-40px); }
}
/* ====== FADE IN ====== */
.fade-in {
width: 100px;
height: 100px;
background: #28a745;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
animation: fadeIn 2s ease forwards;
opacity: 0;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* ====== SLIDE IN ====== */
.slide-in {
width: 100px;
height: 100px;
background: #ffc107;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: #333;
font-weight: bold;
animation: slideIn 1.5s ease-out forwards;
transform: translateX(-200px);
}
@keyframes slideIn {
from { transform: translateX(-200px); }
to { transform: translateX(0); }
}
/* ====== PULSE ====== */
.pulse {
width: 100px;
height: 100px;
background: #dc3545;
border-radius: 50%;
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7); }
70% { transform: scale(1.1); box-shadow: 0 0 0 20px rgba(220, 53, 69, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); }
}
/* ====== ROTATE ====== */
.rotate {
width: 100px;
height: 100px;
background: #17a2b8;
border-radius: 12px;
animation: rotate 3s linear infinite;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ====== SPINNER ====== */
.spinner {
width: 60px;
height: 60px;
border: 6px solid #e9ecef;
border-top: 6px solid #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* ====== MULTI-STEP ====== */
.multi-step {
width: 100px;
height: 100px;
background: #007bff;
border-radius: 12px;
animation: multiStep 4s ease-in-out infinite;
}
@keyframes multiStep {
0% { background: #007bff; transform: translate(0, 0) rotate(0deg); }
25% { background: #28a745; transform: translate(100px, 0) rotate(90deg); }
50% { background: #ffc107; transform: translate(100px, 100px) rotate(180deg); }
75% { background: #dc3545; transform: translate(0, 100px) rotate(270deg); }
100% { background: #007bff; transform: translate(0, 0) rotate(360deg); }
}
/* ====== DIRECTION COMPARISON ====== */
.direction-box {
width: 80px;
height: 80px;
background: #6c5ce7;
border-radius: 12px;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
.direction-normal {
animation-name: slideAcross;
animation-direction: normal;
}
.direction-reverse {
animation-name: slideAcross;
animation-direction: reverse;
}
.direction-alternate {
animation-name: slideAcross;
animation-direction: alternate;
}
.direction-alternate-reverse {
animation-name: slideAcross;
animation-direction: alternate-reverse;
}
@keyframes slideAcross {
from { transform: translateX(0); background: #6c5ce7; }
to { transform: translateX(150px); background: #28a745; }
}
/* ====== FILL MODE COMPARISON ====== */
.fill-box {
width: 80px;
height: 80px;
background: #007bff;
border-radius: 12px;
animation-duration: 2s;
animation-delay: 1s;
animation-timing-function: ease;
animation-fill-mode: none;
animation-name: fillDemo;
opacity: 0;
}
.fill-none {
animation-fill-mode: none;
}
.fill-forwards {
animation-fill-mode: forwards;
}
.fill-backwards {
animation-fill-mode: backwards;
}
.fill-both {
animation-fill-mode: both;
}
@keyframes fillDemo {
from { opacity: 0; transform: translateX(-50px); background: #dc3545; }
to { opacity: 1; transform: translateX(0); background: #28a745; }
}
/* ====== PLAY STATE ====== */
.play-pause-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.play-pause-box {
width: 100px;
height: 100px;
background: #ffc107;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: #333;
font-weight: bold;
animation: pulse 1.5s ease-in-out infinite;
}
.play-pause-box:hover {
animation-play-state: paused;
}
/* ====== PRACTICAL: LOADING SKELETON ====== */
.skeleton-container {
max-width: 400px;
margin: 15px auto;
padding: 20px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.skeleton-line {
height: 16px;
background: linear-gradient(90deg, #e9ecef 25%, #f8f9fa 50%, #e9ecef 75%);
background-size: 200% 100%;
border-radius: 8px;
margin-bottom: 12px;
animation: shimmer 1.5s infinite;
}
.skeleton-line.short {
width: 60%;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* ====== PRACTICAL: BUTTON LOADING ====== */
.btn-loading {
padding: 15px 35px;
background: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 10px;
margin: 10px;
}
.btn-loading .dot {
width: 8px;
height: 8px;
background: white;
border-radius: 50%;
animation: dotPulse 1.4s ease-in-out infinite;
}
.btn-loading .dot:nth-child(2) {
animation-delay: 0.2s;
}
.btn-loading .dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes dotPulse {
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
/* ====== PRACTICAL: FLOATING ELEMENTS ====== */
.float-container {
display: flex;
gap: 30px;
justify-content: center;
margin: 20px 0;
}
.float-item {
width: 80px;
height: 80px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
animation: float 3s ease-in-out infinite;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.float-item:nth-child(1) { background: #007bff; animation-delay: 0s; }
.float-item:nth-child(2) { background: #28a745; animation-delay: 0.3s; }
.float-item:nth-child(3) { background: #ffc107; animation-delay: 0.6s; }
.float-item:nth-child(4) { background: #dc3545; animation-delay: 0.9s; }
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>The animation Property</h1>
<!-- ====== 1. BASIC ANIMATION ====== -->
<section>
<h2>1. Basic Animation</h2>
<p>This box animates through three colors and scales using all individual properties.</p>
<div class="demo-area">
<div class="animation-example"></div>
</div>
<div class="code-block">
.animation-example {
animation-name: exampleAnimation;
animation-duration: 4s;
animation-timing-function: ease-in-out;
animation-delay: 2s;
animation-iteration-count: infinite;
}
@keyframes exampleAnimation {
0% { background-color: red; transform: scale(1); }
50% { background-color: blue; transform: scale(1.5); }
100% { background-color: green; transform: scale(1); }
}
</div>
</section>
<!-- ====== 2. SHORTHAND ====== -->
<section>
<h2>2. animation Shorthand</h2>
<p>The shorthand combines all animation properties into one declaration.</p>
<div class="demo-area">
<div class="animation-shorthand"></div>
</div>
<div class="code-block">
/* Shorthand: name duration timing-function delay iteration-count direction fill-mode play-state */
.animation-shorthand {
animation: bounce 2s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-40px); }
}
</div>
</section>
<!-- ====== 3. COMMON ANIMATIONS ====== -->
<section>
<h2>3. Common Animation Patterns</h2>
<h3>Fade In</h3>
<div class="demo-area">
<div class="fade-in">Fade In</div>
</div>
<div class="code-block">
.fade-in {
animation: fadeIn 2s ease forwards;
opacity: 0;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</div>
<h3>Slide In</h3>
<div class="demo-area">
<div class="slide-in">Slide In</div>
</div>
<div class="code-block">
.slide-in {
animation: slideIn 1.5s ease-out forwards;
transform: translateX(-200px);
}
@keyframes slideIn {
from { transform: translateX(-200px); }
to { transform: translateX(0); }
}
</div>
<h3>Pulse</h3>
<div class="demo-area">
<div class="pulse"></div>
</div>
<div class="code-block">
.pulse {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7); }
70% { transform: scale(1.1); box-shadow: 0 0 0 20px rgba(220, 53, 69, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); }
}
</div>
<h3>Rotate</h3>
<div class="demo-area">
<div class="rotate"></div>
</div>
<div class="code-block">
.rotate {
animation: rotate 3s linear infinite;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
</div>
<h3>Spinner</h3>
<div class="demo-area">
<div class="spinner"></div>
</div>
<div class="code-block">
.spinner {
border: 6px solid #e9ecef;
border-top: 6px solid #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</div>
</section>
<!-- ====== 4. MULTI-STEP ANIMATION ====== -->
<section>
<h2>4. Multi-Step Animation</h2>
<p>This box travels in a square pattern using multiple keyframes.</p>
<div class="demo-area" style="min-height: 300px;">
<div class="multi-step"></div>
</div>
<div class="code-block">
.multi-step {
animation: multiStep 4s ease-in-out infinite;
}
@keyframes multiStep {
0% { background: #007bff; transform: translate(0, 0) rotate(0deg); }
25% { background: #28a745; transform: translate(100px, 0) rotate(90deg); }
50% { background: #ffc107; transform: translate(100px, 100px) rotate(180deg); }
75% { background: #dc3545; transform: translate(0, 100px) rotate(270deg); }
100% { background: #007bff; transform: translate(0, 0) rotate(360deg); }
}
</div>
</section>
<!-- ====== 5. ANIMATION DIRECTION ====== -->
<section>
<h2>5. animation-direction</h2>
<p>Compare the four direction values.</p>
<div class="demo-area" style="flex-direction: column; gap: 20px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">normal:</span>
<div class="direction-box direction-normal"></div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">reverse:</span>
<div class="direction-box direction-reverse"></div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">alternate:</span>
<div class="direction-box direction-alternate"></div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">alternate-reverse:</span>
<div class="direction-box direction-alternate-reverse"></div>
</div>
</div>
<div class="code-block">
.direction-normal { animation-direction: normal; }
.direction-reverse { animation-direction: reverse; }
.direction-alternate { animation-direction: alternate; }
.direction-alternate-reverse { animation-direction: alternate-reverse; }
@keyframes slideAcross {
from { transform: translateX(0); background: #6c5ce7; }
to { transform: translateX(150px); background: #28a745; }
}
</div>
</section>
<!-- ====== 6. ANIMATION FILL MODE ====== -->
<section>
<h2>6. animation-fill-mode</h2>
<p>Compare how styles apply before and after the animation.</p>
<div class="demo-area" style="flex-direction: column; gap: 20px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">none:</span>
<div class="fill-box fill-none"></div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">forwards:</span>
<div class="fill-box fill-forwards"></div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">backwards:</span>
<div class="fill-box fill-backwards"></div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">both:</span>
<div class="fill-box fill-both"></div>
</div>
</div>
<div class="code-block">
.fill-none { animation-fill-mode: none; }
.fill-forwards { animation-fill-mode: forwards; }
.fill-backwards { animation-fill-mode: backwards; }
.fill-both { animation-fill-mode: both; }
@keyframes fillDemo {
from { opacity: 0; transform: translateX(-50px); background: #dc3545; }
to { opacity: 1; transform: translateX(0); background: #28a745; }
}
</div>
<p class="note"><strong>forwards</strong> keeps the end state, <strong>backwards</strong> applies the start state during delay, and <strong>both</strong> does both.</p>
</section>
<!-- ====== 7. ANIMATION PLAY STATE ====== -->
<section>
<h2>7. animation-play-state</h2>
<p>Hover over the box to pause the animation.</p>
<div class="demo-area">
<div class="play-pause-container">
<div class="play-pause-box">Hover to Pause</div>
</div>
</div>
<div class="code-block">
.play-pause-box {
animation: pulse 1.5s ease-in-out infinite;
}
.play-pause-box:hover {
animation-play-state: paused;
}
</div>
</section>
<!-- ====== 8. PRACTICAL: LOADING SKELETON ====== -->
<section>
<h2>8. Practical Example: Loading Skeleton</h2>
<p>A shimmer effect commonly used in loading placeholders.</p>
<div class="skeleton-container">
<div class="skeleton-line"></div>
<div class="skeleton-line"></div>
<div class="skeleton-line short"></div>
</div>
<div class="code-block">
.skeleton-line {
background: linear-gradient(90deg, #e9ecef 25%, #f8f9fa 50%, #e9ecef 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
</div>
</section>
<!-- ====== 9. PRACTICAL: BUTTON LOADING ====== -->
<section>
<h2>9. Practical Example: Button Loading Dots</h2>
<p>Animated dots for a loading button.</p>
<div style="text-align: center;">
<button class="btn-loading">
Loading
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</button>
</div>
<div class="code-block">
.btn-loading .dot {
width: 8px;
height: 8px;
background: white;
border-radius: 50%;
animation: dotPulse 1.4s ease-in-out infinite;
}
.btn-loading .dot:nth-child(2) { animation-delay: 0.2s; }
.btn-loading .dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes dotPulse {
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
</div>
</section>
<!-- ====== 10. PRACTICAL: FLOATING ELEMENTS ====== -->
<section>
<h2>10. Practical Example: Floating Elements</h2>
<p>Staggered floating animation with different delays.</p>
<div class="float-container">
<div class="float-item">🚀</div>
<div class="float-item">⭐</div>
<div class="float-item">🎯</div>
<div class="float-item">💎</div>
</div>
<div class="code-block">
.float-item {
animation: float 3s ease-in-out infinite;
}
.float-item:nth-child(1) { animation-delay: 0s; }
.float-item:nth-child(2) { animation-delay: 0.3s; }
.float-item:nth-child(3) { animation-delay: 0.6s; }
.float-item:nth-child(4) { animation-delay: 0.9s; }
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
</div>
</section>
<!-- ====== 11. REFERENCE TABLES ====== -->
<section>
<h2>11. Reference Tables</h2>
<h3>Animation Properties</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Default</th>
</tr>
<tr>
<td><code>animation-name</code></td>
<td>Name of the @keyframes rule</td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>animation-duration</code></td>
<td>How long one cycle takes</td>
<td><code>0s</code></td>
</tr>
<tr>
<td><code>animation-timing-function</code></td>
<td>Speed curve</td>
<td><code>ease</code></td>
</tr>
<tr>
<td><code>animation-delay</code></td>
<td>Wait before starting</td>
<td><code>0s</code></td>
</tr>
<tr>
<td><code>animation-iteration-count</code></td>
<td>How many times to repeat</td>
<td><code>1</code></td>
</tr>
<tr>
<td><code>animation-direction</code></td>
<td>Direction of each cycle</td>
<td><code>normal</code></td>
</tr>
<tr>
<td><code>animation-fill-mode</code></td>
<td>Styles before/after animation</td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>animation-play-state</code></td>
<td>Running or paused</td>
<td><code>running</code></td>
</tr>
</table>
<h3>animation-direction Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>Forward each cycle (default)</td>
</tr>
<tr>
<td><code>reverse</code></td>
<td>Backward each cycle</td>
</tr>
<tr>
<td><code>alternate</code></td>
<td>Forward, then backward</td>
</tr>
<tr>
<td><code>alternate-reverse</code></td>
<td>Backward, then forward</td>
</tr>
</table>
<h3>animation-fill-mode Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>none</code></td>
<td>No styles applied outside the animation (default)</td>
</tr>
<tr>
<td><code>forwards</code></td>
<td>Keep the last keyframe's styles</td>
</tr>
<tr>
<td><code>backwards</code></td>
<td>Apply the first keyframe's styles during delay</td>
</tr>
<tr>
<td><code>both</code></td>
<td>Apply both forwards and backwards</td>
</tr>
</table>
<h3>@keyframes Syntax</h3>
<table class="reference-table">
<tr>
<th>Syntax</th>
<th>Description</th>
</tr>
<tr>
<td><code>from { }</code></td>
<td>Equivalent to <code>0%</code></td>
</tr>
<tr>
<td><code>to { }</code></td>
<td>Equivalent to <code>100%</code></td>
</tr>
<tr>
<td><code>50% { }</code></td>
<td>Halfway through the animation</td>
</tr>
</table>
</section>
<!-- ====== 12. BEST PRACTICES ====== -->
<section>
<h2>12. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>transform</code> and <code>opacity</code> for smooth, hardware-accelerated animations</li>
<li>Use the shorthand <code>animation</code> for cleaner code</li>
<li>Use <code>animation-fill-mode: forwards</code> to keep the end state</li>
<li>Use <code>animation-delay</code> for staggered effects</li>
<li>Use <code>infinite</code> for continuous animations (loading spinners)</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
<li>Keep animations short and purposeful</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't animate layout properties (<code>width</code>, <code>height</code>, <code>top</code>, <code>left</code>)</li>
<li>Don't use <code>infinite</code> animations that distract from content</li>
<li>Don't forget to specify <code>animation-duration</code> (default is 0s — no animation!)</li>
<li>Don't use animations for critical functionality that must work without them</li>
<li>Don't overuse animations — a few well-placed ones are better than many</li>
<li>Don't forget about <code>prefers-reduced-motion</code></li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.animation-example,
.bounce,
.pulse,
.rotate,
.spinner,
.float-item {
animation: none;
}
}
/* Best practice: use transform and opacity */
.smooth {
animation: slideIn 1s ease forwards;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Default |
|---|---|---|
animation-name | Name of the @keyframes rule | none |
animation-duration | How long one cycle takes | 0s |
animation-timing-function | Speed curve | ease |
animation-delay | Wait before starting | 0s |
animation-iteration-count | How many times to repeat | 1 |
animation-direction | Direction of each cycle | normal |
animation-fill-mode | Styles before/after animation | none |
animation-play-state | Running or paused | running |
animation-direction Values
| Value | Description |
|---|---|
normal | Forward each cycle (default) |
reverse | Backward each cycle |
alternate | Forward, then backward |
alternate-reverse | Backward, then forward |
animation-fill-mode Values
| Value | Description |
|---|---|
none | No styles applied outside the animation (default) |
forwards | Keep the last keyframe’s styles |
backwards | Apply the first keyframe’s styles during delay |
both | Apply both forwards and backwards |
@keyframes Syntax
| Syntax | Description |
|---|---|
from { } | Equivalent to 0% |
to { } | Equivalent to 100% |
50% { } | Halfway through the animation |
Best Practices
✅ Do This:
/* Simple bounce animation */
.bounce {
animation: bounce 1s ease infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
/* Fade in and stay visible */
.fade-in {
animation: fadeIn 1s ease forwards;
opacity: 0;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Staggered animation */
.item:nth-child(1) { animation-delay: 0s; }
.item:nth-child(2) { animation-delay: 0.1s; }
.item:nth-child(3) { animation-delay: 0.2s; }
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.bounce, .fade-in {
animation: none;
}
}
❌ Don’t Do This:
/* Don't forget animation-duration */
.box {
animation-name: bounce; /* Duration is 0s by default — no animation! */
}
/* Don't animate layout properties */
.box {
animation: moveBox 1s infinite;
}
@keyframes moveBox {
from { left: 0; }
to { left: 100px; } /* Causes layout recalculation */
}
/* Don't overuse infinite animations */
.everything {
animation: spin 1s infinite; /* Distracting */
}
Pro Tip: CSS animations are perfect for attention-grabbing effects (loading spinners, notifications), entrance animations (fade in, slide in), and continuous ambient effects (floating, pulsing). Use transform and opacity for the smoothest animations — they’re hardware-accelerated. Remember: animation-duration is required (default 0s means no animation). Use animation-fill-mode: forwards to keep the end state. And always respect prefers-reduced-motion — some users are sensitive to motion and may experience discomfort from animations!
56. Animation Properties — Part 1
This lesson covers six essential animation properties: animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, and animation-direction.
Overview of Properties
| Property | Description | Default |
|---|---|---|
animation-name | Name of the @keyframes rule | none |
animation-duration | How long one cycle takes | 0s |
animation-timing-function | Speed curve | ease |
animation-delay | Wait before starting | 0s |
animation-iteration-count | How many times to repeat | 1 |
animation-direction | Direction of each cycle | normal |
1. animation-name
Specifies the name of the @keyframes rule that defines the animation.
.box {
animation-name: slideIn;
}
| Value | Description |
|---|---|
none | No animation (default) |
custom-name | Name of a @keyframes rule |
| Multiple | Comma-separated list |
Multiple animations:
.box {
animation-name: slideIn, fadeIn;
animation-duration: 2s, 1s;
}
2. animation-duration
Specifies the length of time for one animation cycle.
.box {
animation-duration: 2s;
}
| Value | Description |
|---|---|
0s | No animation (default) |
2s | 2 seconds |
500ms | 500 milliseconds |
auto | Fills the entire timeline (for time-based animations) |
Important: If duration is 0s, the animation doesn’t play — even if animation-name is set.
3. animation-timing-function
Controls how the animation progresses through its duration — the speed curve.
.box {
animation-timing-function: ease-in-out;
}
| Value | Description |
|---|---|
ease | Slow start, fast middle, slow end (default) |
linear | Constant speed |
ease-in | Slow start, fast end |
ease-out | Fast start, slow end |
ease-in-out | Slow start, fast middle, slow end |
cubic-bezier(n,n,n,n) | Custom curve |
steps(n) | Stepped animation |
4. animation-delay
Specifies the wait time before the animation starts.
.box {
animation-delay: 1s;
}
| Value | Description |
|---|---|
0s | Start immediately (default) |
1s | Wait 1 second |
500ms | Wait 500 milliseconds |
| Negative | Starts partway through the animation |
Negative Delay
A negative delay starts the animation already in progress:
animation-delay: -2s; /* Starts 2 seconds into the animation */
Visual comparison:
animation-delay: 0s; → [start]──────[end]
animation-delay: 1s; → ────[start]──────[end]
animation-delay: -2s; → ──[starts 2s in]──[end]
5. animation-iteration-count
Specifies how many times the animation repeats.
.box {
animation-iteration-count: 3;
}
| Value | Description |
|---|---|
1 | Plays once (default) |
3 | Plays 3 times |
infinite | Repeats forever |
0.5 | Plays half the animation |
2.5 | Plays 2.5 cycles |
Non-integer values: 0.5 plays half the animation, 2.5 plays two and a half cycles.
6. animation-direction
Specifies whether the animation plays forward, backward, or alternates.
.box {
animation-direction: alternate-reverse;
}
| Value | Description | Cycle 1 | Cycle 2 | Cycle 3 |
|---|---|---|---|---|
normal | Forward each cycle | → | → | → |
reverse | Backward each cycle | ← | ← | ← |
alternate | Forward, then backward | → | ← | → |
alternate-reverse | Backward, then forward | ← | → | ← |
Visual comparison:
normal: [1→2→3] [1→2→3] [1→2→3]
reverse: [3→2→1] [3→2→1] [3→2→1]
alternate: [1→2→3] [3→2→1] [1→2→3]
alternate-reverse: [3→2→1] [1→2→3] [3→2→1]
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Animation Properties — Part 1</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 200px;
overflow: hidden;
}
/* ====== MAIN DEMO BOX ====== */
.box {
width: 100px;
height: 100px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 12px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
animation-name: slideIn;
animation-duration: 2s;
animation-delay: 1s;
animation-iteration-count: 3;
animation-timing-function: ease-in-out;
animation-direction: alternate-reverse;
}
@keyframes slideIn {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
/* ====== TIMING FUNCTIONS ====== */
.timing-box {
width: 80px;
height: 80px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.7rem;
animation-name: moveRight;
animation-duration: 2s;
animation-iteration-count: infinite;
}
.timing-ease {
background: #007bff;
animation-timing-function: ease;
}
.timing-linear {
background: #28a745;
animation-timing-function: linear;
}
.timing-ease-in {
background: #dc3545;
animation-timing-function: ease-in;
}
.timing-ease-out {
background: #ffc107;
color: #333;
animation-timing-function: ease-out;
}
.timing-ease-in-out {
background: #6c5ce7;
animation-timing-function: ease-in-out;
}
@keyframes moveRight {
from { transform: translateX(0); }
to { transform: translateX(150px); }
}
/* ====== DELAY COMPARISON ====== */
.delay-box {
width: 80px;
height: 80px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.7rem;
animation-name: fadeSlide;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-iteration-count: 1;
opacity: 0;
}
.delay-0 {
background: #007bff;
animation-delay: 0s;
}
.delay-1 {
background: #28a745;
animation-delay: 1s;
}
.delay-neg {
background: #dc3545;
animation-delay: -0.5s;
}
@keyframes fadeSlide {
from { opacity: 0; transform: translateX(-50px); }
to { opacity: 1; transform: translateX(0); }
}
/* ====== ITERATION COUNT ====== */
.iteration-box {
width: 80px;
height: 80px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.7rem;
animation-name: pulseScale;
animation-duration: 1s;
animation-timing-function: ease-in-out;
}
.iter-1 {
background: #007bff;
animation-iteration-count: 1;
}
.iter-3 {
background: #28a745;
animation-iteration-count: 3;
}
.iter-half {
background: #ffc107;
color: #333;
animation-iteration-count: 0.5;
}
.iter-infinite {
background: #dc3545;
animation-iteration-count: infinite;
}
@keyframes pulseScale {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.3); }
}
/* ====== DIRECTION COMPARISON ====== */
.direction-box {
width: 80px;
height: 80px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.7rem;
animation-name: slideAcross;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
.dir-normal {
background: #007bff;
animation-direction: normal;
}
.dir-reverse {
background: #28a745;
animation-direction: reverse;
}
.dir-alternate {
background: #dc3545;
animation-direction: alternate;
}
.dir-alternate-reverse {
background: #6c5ce7;
animation-direction: alternate-reverse;
}
@keyframes slideAcross {
from { transform: translateX(0); }
to { transform: translateX(150px); }
}
/* ====== PRACTICAL: SLIDE IN BANNER ====== */
.banner-container {
padding: 20px;
background: #e9ecef;
border-radius: 8px;
overflow: hidden;
margin: 15px 0;
}
.banner {
background: linear-gradient(135deg, #007bff, #6c5ce7);
color: white;
padding: 20px 30px;
border-radius: 8px;
font-weight: bold;
font-size: 1.2rem;
text-align: center;
animation-name: bannerSlide;
animation-duration: 1s;
animation-delay: 0.5s;
animation-fill-mode: backwards;
animation-timing-function: ease-out;
}
@keyframes bannerSlide {
from {
opacity: 0;
transform: translateY(-50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ====== PRACTICAL: STAGGERED LIST ====== */
.staggered-list {
list-style: none;
padding: 0;
margin: 15px 0;
}
.staggered-list li {
padding: 12px 20px;
background: white;
border-radius: 8px;
margin-bottom: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
animation-name: listItemIn;
animation-duration: 0.5s;
animation-fill-mode: both;
animation-timing-function: ease-out;
opacity: 0;
transform: translateX(-30px);
}
.staggered-list li:nth-child(1) { animation-delay: 0.1s; }
.staggered-list li:nth-child(2) { animation-delay: 0.2s; }
.staggered-list li:nth-child(3) { animation-delay: 0.3s; }
.staggered-list li:nth-child(4) { animation-delay: 0.4s; }
.staggered-list li:nth-child(5) { animation-delay: 0.5s; }
@keyframes listItemIn {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* ====== PRACTICAL: PULSING NOTIFICATION ====== */
.notification-container {
position: relative;
display: inline-block;
margin: 20px;
}
.notification-badge {
position: absolute;
top: -8px;
right: -8px;
background: #dc3545;
color: white;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: bold;
animation-name: pulseRing;
animation-duration: 1.5s;
animation-iteration-count: infinite;
animation-timing-function: ease-out;
}
@keyframes pulseRing {
0% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
}
70% {
box-shadow: 0 0 0 12px rgba(220, 53, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
}
}
.notification-icon {
font-size: 2.5rem;
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>Animation Properties — Part 1</h1>
<!-- ====== 1. MAIN DEMO ====== -->
<section>
<h2>1. Complete Animation with All Properties</h2>
<p>This box uses <code>animation-name</code>, <code>animation-duration</code>, <code>animation-delay</code>, <code>animation-iteration-count</code>, <code>animation-timing-function</code>, and <code>animation-direction</code>.</p>
<div class="demo-area">
<div class="box"></div>
</div>
<div class="code-block">
@keyframes slideIn {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
.box {
animation-name: slideIn;
animation-duration: 2s;
animation-delay: 1s;
animation-iteration-count: 3;
animation-timing-function: ease-in-out;
animation-direction: alternate-reverse;
}
</div>
<p class="note">The box waits 1 second, then slides in and out 3 times, alternating direction.</p>
</section>
<!-- ====== 2. ANIMATION-NAME ====== -->
<section>
<h2>2. animation-name</h2>
<p>Specifies which <code>@keyframes</code> rule to use. You can specify multiple animations separated by commas.</p>
<div class="code-block">
/* Single animation */
.box {
animation-name: slideIn;
}
/* Multiple animations */
.box {
animation-name: slideIn, fadeIn;
animation-duration: 2s, 1s;
}
</div>
</section>
<!-- ====== 3. ANIMATION-DURATION ====== -->
<section>
<h2>3. animation-duration</h2>
<p>Specifies how long one cycle takes. <code>0s</code> means no animation.</p>
<div class="code-block">
.box { animation-duration: 2s; } /* 2 seconds */
.box { animation-duration: 500ms; } /* 500 milliseconds */
.box { animation-duration: 0s; } /* No animation */
</div>
<p class="note"><strong>Important:</strong> If <code>animation-duration</code> is <code>0s</code> (the default), the animation won't play — even if <code>animation-name</code> is set.</p>
</section>
<!-- ====== 4. ANIMATION-TIMING-FUNCTION ====== -->
<section>
<h2>4. animation-timing-function</h2>
<p>Each box moves across the screen with a different timing function.</p>
<div class="demo-area" style="flex-direction: column; gap: 15px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 110px;">ease:</span>
<div class="timing-box timing-ease">ease</div>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 110px;">linear:</span>
<div class="timing-box timing-linear">linear</div>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 110px;">ease-in:</span>
<div class="timing-box timing-ease-in">ease-in</div>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 110px;">ease-out:</span>
<div class="timing-box timing-ease-out">ease-out</div>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 110px;">ease-in-out:</span>
<div class="timing-box timing-ease-in-out">ease-in-out</div>
</div>
</div>
<div class="code-block">
@keyframes moveRight {
from { transform: translateX(0); }
to { transform: translateX(150px); }
}
.timing-ease { animation-timing-function: ease; }
.timing-linear { animation-timing-function: linear; }
.timing-ease-in { animation-timing-function: ease-in; }
.timing-ease-out { animation-timing-function: ease-out; }
.timing-ease-in-out { animation-timing-function: ease-in-out; }
</div>
</section>
<!-- ====== 5. ANIMATION-DELAY ====== -->
<section>
<h2>5. animation-delay</h2>
<p>Compare positive and negative delays. Refresh the page to see the difference.</p>
<div class="demo-area">
<div style="text-align: center;">
<div class="delay-box delay-0">0s</div>
<p class="note">delay: 0s</p>
</div>
<div style="text-align: center;">
<div class="delay-box delay-1">1s</div>
<p class="note">delay: 1s</p>
</div>
<div style="text-align: center;">
<div class="delay-box delay-neg">-0.5s</div>
<p class="note">delay: -0.5s</p>
</div>
</div>
<div class="code-block">
.delay-0 { animation-delay: 0s; }
.delay-1 { animation-delay: 1s; }
.delay-neg { animation-delay: -0.5s; }
@keyframes fadeSlide {
from { opacity: 0; transform: translateX(-50px); }
to { opacity: 1; transform: translateX(0); }
}
</div>
<p class="note">A <strong>negative delay</strong> starts the animation already partway through. With <code>-0.5s</code> on a <code>1s</code> animation, it starts halfway (at 50% opacity and position).</p>
</section>
<!-- ====== 6. ANIMATION-ITERATION-COUNT ====== -->
<section>
<h2>6. animation-iteration-count</h2>
<p>Compare different iteration counts. Watch closely — they all use the same animation.</p>
<div class="demo-area">
<div style="text-align: center;">
<div class="iteration-box iter-1">1×</div>
<p class="note">count: 1</p>
</div>
<div style="text-align: center;">
<div class="iteration-box iter-3">3×</div>
<p class="note">count: 3</p>
</div>
<div style="text-align: center;">
<div class="iteration-box iter-half">0.5×</div>
<p class="note">count: 0.5</p>
</div>
<div style="text-align: center;">
<div class="iteration-box iter-infinite">∞</div>
<p class="note">count: infinite</p>
</div>
</div>
<div class="code-block">
.iter-1 { animation-iteration-count: 1; }
.iter-3 { animation-iteration-count: 3; }
.iter-half { animation-iteration-count: 0.5; }
.iter-infinite { animation-iteration-count: infinite; }
@keyframes pulseScale {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.3); }
}
</div>
</section>
<!-- ====== 7. ANIMATION-DIRECTION ====== -->
<section>
<h2>7. animation-direction</h2>
<p>Compare the four direction values. Watch how each box moves.</p>
<div class="demo-area" style="flex-direction: column; gap: 15px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 160px;">normal:</span>
<div class="direction-box dir-normal">normal</div>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 160px;">reverse:</span>
<div class="direction-box dir-reverse">reverse</div>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 160px;">alternate:</span>
<div class="direction-box dir-alternate">alternate</div>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="font-weight: bold; min-width: 160px;">alternate-reverse:</span>
<div class="direction-box dir-alternate-reverse">alt-rev</div>
</div>
</div>
<div class="code-block">
@keyframes slideAcross {
from { transform: translateX(0); }
to { transform: translateX(150px); }
}
.dir-normal { animation-direction: normal; }
.dir-reverse { animation-direction: reverse; }
.dir-alternate { animation-direction: alternate; }
.dir-alternate-reverse { animation-direction: alternate-reverse; }
</div>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Cycle 1</th>
<th>Cycle 2</th>
<th>Cycle 3</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>→</td>
<td>→</td>
<td>→</td>
</tr>
<tr>
<td><code>reverse</code></td>
<td>←</td>
<td>←</td>
<td>←</td>
</tr>
<tr>
<td><code>alternate</code></td>
<td>→</td>
<td>←</td>
<td>→</td>
</tr>
<tr>
<td><code>alternate-reverse</code></td>
<td>←</td>
<td>→</td>
<td>←</td>
</tr>
</table>
</section>
<!-- ====== 8. PRACTICAL: SLIDE-IN BANNER ====== -->
<section>
<h2>8. Practical Example: Slide-In Banner</h2>
<p>Refresh the page to see the banner slide in from the top.</p>
<div class="banner-container">
<div class="banner">
🎉 Welcome! This banner slides in from the top.
</div>
</div>
<div class="code-block">
.banner {
animation-name: bannerSlide;
animation-duration: 1s;
animation-delay: 0.5s;
animation-fill-mode: backwards;
animation-timing-function: ease-out;
}
@keyframes bannerSlide {
from { opacity: 0; transform: translateY(-50px); }
to { opacity: 1; transform: translateY(0); }
}
</div>
<p class="note">The <code>animation-fill-mode: backwards</code> applies the <code>from</code> styles during the delay, so the banner is invisible before it starts.</p>
</section>
<!-- ====== 9. PRACTICAL: STAGGERED LIST ====== -->
<section>
<h2>9. Practical Example: Staggered List</h2>
<p>Refresh the page to see the list items appear one by one.</p>
<ul class="staggered-list">
<li>✨ First item slides in</li>
<li>🚀 Second item follows</li>
<li>🎯 Third item appears</li>
<li>💎 Fourth item arrives</li>
<li>🔥 Fifth item completes the list</li>
</ul>
<div class="code-block">
.staggered-list li {
animation-name: listItemIn;
animation-duration: 0.5s;
animation-fill-mode: both;
animation-timing-function: ease-out;
}
.staggered-list li:nth-child(1) { animation-delay: 0.1s; }
.staggered-list li:nth-child(2) { animation-delay: 0.2s; }
.staggered-list li:nth-child(3) { animation-delay: 0.3s; }
.staggered-list li:nth-child(4) { animation-delay: 0.4s; }
.staggered-list li:nth-child(5) { animation-delay: 0.5s; }
@keyframes listItemIn {
from { opacity: 0; transform: translateX(-30px); }
to { opacity: 1; transform: translateX(0); }
}
</div>
</section>
<!-- ====== 10. PRACTICAL: PULSING NOTIFICATION ====== -->
<section>
<h2>10. Practical Example: Pulsing Notification</h2>
<p>A notification badge that pulses to draw attention.</p>
<div style="text-align: center;">
<div class="notification-container">
<span class="notification-icon">🔔</span>
<span class="notification-badge">3</span>
</div>
</div>
<div class="code-block">
.notification-badge {
animation-name: pulseRing;
animation-duration: 1.5s;
animation-iteration-count: infinite;
animation-timing-function: ease-out;
}
@keyframes pulseRing {
0% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7); }
70% { box-shadow: 0 0 0 12px rgba(220, 53, 69, 0); }
100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); }
}
</div>
</section>
<!-- ====== 11. REFERENCE TABLES ====== -->
<section>
<h2>11. Reference Tables</h2>
<h3>Animation Properties — Part 1</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Default</th>
</tr>
<tr>
<td><code>animation-name</code></td>
<td>Name of the @keyframes rule</td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>animation-duration</code></td>
<td>How long one cycle takes</td>
<td><code>0s</code></td>
</tr>
<tr>
<td><code>animation-timing-function</code></td>
<td>Speed curve</td>
<td><code>ease</code></td>
</tr>
<tr>
<td><code>animation-delay</code></td>
<td>Wait before starting</td>
<td><code>0s</code></td>
</tr>
<tr>
<td><code>animation-iteration-count</code></td>
<td>How many times to repeat</td>
<td><code>1</code></td>
</tr>
<tr>
<td><code>animation-direction</code></td>
<td>Direction of each cycle</td>
<td><code>normal</code></td>
</tr>
</table>
<h3>animation-timing-function Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>ease</code></td>
<td>Slow start, fast middle, slow end (default)</td>
</tr>
<tr>
<td><code>linear</code></td>
<td>Constant speed</td>
</tr>
<tr>
<td><code>ease-in</code></td>
<td>Slow start, fast end</td>
</tr>
<tr>
<td><code>ease-out</code></td>
<td>Fast start, slow end</td>
</tr>
<tr>
<td><code>ease-in-out</code></td>
<td>Slow start, fast middle, slow end</td>
</tr>
<tr>
<td><code>cubic-bezier(n,n,n,n)</code></td>
<td>Custom curve</td>
</tr>
<tr>
<td><code>steps(n)</code></td>
<td>Stepped animation</td>
</tr>
</table>
<h3>animation-iteration-count Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>1</code></td>
<td>Plays once (default)</td>
</tr>
<tr>
<td><code>3</code></td>
<td>Plays 3 times</td>
</tr>
<tr>
<td><code>infinite</code></td>
<td>Repeats forever</td>
</tr>
<tr>
<td><code>0.5</code></td>
<td>Plays half the animation</td>
</tr>
<tr>
<td><code>2.5</code></td>
<td>Plays 2.5 cycles</td>
</tr>
</table>
<h3>animation-direction Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>normal</code></td>
<td>Forward each cycle (default)</td>
</tr>
<tr>
<td><code>reverse</code></td>
<td>Backward each cycle</td>
</tr>
<tr>
<td><code>alternate</code></td>
<td>Forward, then backward</td>
</tr>
<tr>
<td><code>alternate-reverse</code></td>
<td>Backward, then forward</td>
</tr>
</table>
</section>
<!-- ====== 12. BEST PRACTICES ====== -->
<section>
<h2>12. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Always specify <code>animation-duration</code> — the default is <code>0s</code></li>
<li>Use <code>animation-delay</code> for staggered effects</li>
<li>Use <code>animation-iteration-count: infinite</code> for continuous animations</li>
<li>Use <code>animation-direction: alternate</code> for back-and-forth motion</li>
<li>Use negative delays to start animations partway through</li>
<li>Use <code>transform</code> and <code>opacity</code> for smooth performance</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't forget <code>animation-duration</code> — nothing will happen</li>
<li>Don't use <code>infinite</code> for animations that should stop</li>
<li>Don't confuse <code>animation-delay</code> with <code>animation-duration</code></li>
<li>Don't use negative delays without understanding the effect</li>
<li>Don't animate layout properties like <code>width</code> or <code>height</code></li>
<li>Don't forget about <code>prefers-reduced-motion</code></li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box,
.banner,
.staggered-list li,
.notification-badge {
animation: none;
}
}
/* Staggered animation */
.item:nth-child(1) { animation-delay: 0.1s; }
.item:nth-child(2) { animation-delay: 0.2s; }
.item:nth-child(3) { animation-delay: 0.3s; }
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Default |
|---|---|---|
animation-name | Name of the @keyframes rule | none |
animation-duration | How long one cycle takes | 0s |
animation-timing-function | Speed curve | ease |
animation-delay | Wait before starting | 0s |
animation-iteration-count | How many times to repeat | 1 |
animation-direction | Direction of each cycle | normal |
animation-timing-function Values
| Value | Description |
|---|---|
ease | Slow start, fast middle, slow end (default) |
linear | Constant speed |
ease-in | Slow start, fast end |
ease-out | Fast start, slow end |
ease-in-out | Slow start, fast middle, slow end |
cubic-bezier(n,n,n,n) | Custom curve |
steps(n) | Stepped animation |
animation-iteration-count Values
| Value | Description |
|---|---|
1 | Plays once (default) |
3 | Plays 3 times |
infinite | Repeats forever |
0.5 | Plays half the animation |
2.5 | Plays 2.5 cycles |
animation-direction Values
| Value | Description |
|---|---|
normal | Forward each cycle (default) |
reverse | Backward each cycle |
alternate | Forward, then backward |
alternate-reverse | Backward, then forward |
Best Practices
✅ Do This:
/* Complete animation setup */
.box {
animation-name: slideIn;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 0.5s;
animation-iteration-count: 3;
animation-direction: alternate;
}
/* Staggered animation with delay */
.item:nth-child(1) { animation-delay: 0.1s; }
.item:nth-child(2) { animation-delay: 0.2s; }
.item:nth-child(3) { animation-delay: 0.3s; }
/* Continuous animation */
.spinner {
animation: spin 1s linear infinite;
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box {
animation: none;
}
}
❌ Don’t Do This:
/* Don't forget animation-duration */
.box {
animation-name: slideIn; /* Duration is 0s — nothing happens! */
}
/* Don't confuse delay with duration */
.box {
animation-delay: 2s; /* Waits 2s before starting */
animation-duration: 1s; /* Then takes 1s to complete */
}
/* Don't use infinite for one-time animations */
.alert {
animation: pulse 1s infinite; /* Will pulse forever! */
}
/* Don't forget reduced motion */
@media (prefers-reduced-motion: reduce) {
/* Missing — animations still play */
}
Pro Tip: The six properties in this lesson are the core of CSS animations. Remember: animation-duration is required (default 0s means no animation). Use animation-delay for staggered effects — applying incremental delays to list items creates a beautiful cascade. animation-iteration-count: infinite is perfect for continuous animations like spinners and pulses. animation-direction: alternate creates natural back-and-forth motion. And always respect prefers-reduced-motion — some users experience discomfort from animations!
57. Animation Properties — Part 2
This lesson covers the remaining animation properties: animation-fill-mode, animation-play-state, animation-timeline, and animation-composition.
Overview of Properties
| Property | Description | Default |
|---|---|---|
animation-fill-mode | Styles before/after animation | none |
animation-play-state | Running or paused | running |
animation-timeline | Timeline that controls the animation | auto |
animation-composition | How multiple animations combine | replace |
1. animation-fill-mode
Defines the styles applied to an element before and after the animation plays.
.box {
animation-fill-mode: forwards;
}
Values
| Value | Description |
|---|---|
none | No styles applied outside the animation (default) |
forwards | Keep the last keyframe’s styles after the animation ends |
backwards | Apply the first keyframe’s styles during the delay |
both | Apply both forwards and backwards |
Visual Explanation
Without fill-mode:
[before] [animation] [after → reverts to original]
With forwards:
[before] [animation] [after → keeps end state]
With backwards:
[before → shows start state] [animation] [after]
With both:
[before → shows start state] [animation] [after → keeps end state]
Key Points:
forwards— the element stays at the animation’s end statebackwards— the element shows the start state during the delayboth— combines both effects- Without
fill-mode, the element snaps back to its original state after the animation
2. animation-play-state
Controls whether an animation is running or paused.
.box {
animation-play-state: paused;
}
Values
| Value | Description |
|---|---|
running | Animation is playing (default) |
paused | Animation is paused |
Key Points:
- Useful for pausing animations on hover
- The animation resumes from where it was paused
- Commonly used with
:hoverfor interactive control
Example:
.animation:hover {
animation-play-state: paused;
}
3. animation-timeline
Allows you to specify a timeline that controls the animation instead of the default document timeline.
.box {
animation-timeline: scroll();
}
Values
| Value | Description |
|---|---|
auto | Default document timeline (default) |
none | No timeline — animation doesn’t play |
scroll() | Binds animation to scroll position |
view() | Binds animation to element visibility in viewport |
scroll() Timeline
.box {
animation-timeline: scroll();
}
The animation progresses in sync with scrolling — as the user scrolls, the animation advances. This creates scroll-driven animations that feel connected to the user’s interaction.
Variations:
animation-timeline: scroll(); /* Root scroller */
animation-timeline: scroll(root); /* Root scroller */
animation-timeline: scroll(nearest); /* Nearest scrollable ancestor */
animation-timeline: scroll(self); /* The element itself */
view() Timeline
.box {
animation-timeline: view();
}
The animation progresses based on the element’s visibility within the viewport — as the element enters, crosses, and exits the viewport, the animation plays.
Key Points:
animation-timelineis a modern feature with growing browser support- Scroll-driven animations are performant — they run on the compositor thread
- Always provide a fallback for browsers that don’t support it
4. animation-composition
Controls how multiple animations that target the same properties combine their effects.
.box {
animation-composition: add;
}
Values
| Value | Description |
|---|---|
replace | New animations overwrite previous ones (default) |
add | New animations add to existing effects |
accumulate | Similar to add, but with additional rules for keyframes and timing |
How It Works
replace (default): The last animation wins.
.box {
animation-name: moveRight, fadeOut;
animation-composition: replace;
/* fadeOut's opacity replaces any opacity from moveRight */
}
add: Effects are combined.
.box {
animation-name: moveRight, moveUp;
animation-composition: add;
/* The box moves both right AND up */
}
accumulate: Similar to add, but keyframe values accumulate.
.box {
animation-name: scale1, scale2;
animation-composition: accumulate;
/* scale1 and scale2 combine their scale values */
}
Key Points:
replaceis the default — later animations override earlier onesaddcombines transform effects (e.g., translate + rotate)accumulateis useful for stacking animations with the same property
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Animation Properties — Part 2</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
padding: 20px;
background: #f8f9fa;
color: #333;
max-width: 1200px;
margin: 0 auto;
line-height: 1.6;
}
h1 {
color: #007bff;
border-bottom: 3px solid #007bff;
padding-bottom: 10px;
}
h2 {
color: #28a745;
border-left: 4px solid #28a745;
padding-left: 15px;
margin-top: 30px;
}
h3 {
color: #333;
margin-top: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
margin: 10px 0;
}
.reference-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.reference-table th,
.reference-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.reference-table th {
background: #007bff;
color: white;
}
.reference-table tr:nth-child(even) {
background: #f8f9fa;
}
.note {
font-size: 0.9rem;
color: #6c757d;
margin-top: 5px;
}
.highlight {
background: #ffc107;
color: #333;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* ====== DEMO AREA ====== */
.demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: #e9ecef;
border-radius: 8px;
margin: 15px 0;
min-height: 200px;
overflow: hidden;
}
/* ====== BASIC ANIMATION ====== */
.box {
width: 100px;
height: 100px;
background: #007bff;
border-radius: 12px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
animation-name: exampleAnimation;
animation-duration: 2s;
animation-fill-mode: forwards;
}
@keyframes exampleAnimation {
from {
transform: translateX(0);
background-color: #007bff;
}
to {
transform: translateX(200px);
background-color: #dc3545;
}
}
/* ====== FILL MODE COMPARISON ====== */
.fill-box {
width: 80px;
height: 80px;
background: #007bff;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.7rem;
animation-name: fillDemo;
animation-duration: 1s;
animation-delay: 0.5s;
animation-timing-function: ease;
}
.fill-none {
animation-fill-mode: none;
}
.fill-forwards {
animation-fill-mode: forwards;
}
.fill-backwards {
animation-fill-mode: backwards;
}
.fill-both {
animation-fill-mode: both;
}
@keyframes fillDemo {
from {
opacity: 0;
transform: translateX(-50px);
background: #dc3545;
}
to {
opacity: 1;
transform: translateX(0);
background: #28a745;
}
}
/* ====== PLAY STATE ====== */
.play-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.play-box {
width: 100px;
height: 100px;
background: #6c5ce7;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
animation: pulseAnim 1.5s ease-in-out infinite;
}
.play-box:hover {
animation-play-state: paused;
}
@keyframes pulseAnim {
0%, 100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(108, 92, 231, 0.7);
}
50% {
transform: scale(1.15);
box-shadow: 0 0 0 15px rgba(108, 92, 231, 0);
}
}
/* ====== COMPOSITION ====== */
.composition-box {
width: 80px;
height: 80px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 0.6rem;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
.comp-replace {
background: #007bff;
animation-name: moveRight, rotateBox;
animation-composition: replace;
}
.comp-add {
background: #28a745;
animation-name: moveRight, moveUp;
animation-composition: add;
}
.comp-accumulate {
background: #ffc107;
color: #333;
animation-name: scaleUp, scaleDown;
animation-composition: accumulate;
}
@keyframes moveRight {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
@keyframes moveUp {
from { transform: translateY(0); }
to { transform: translateY(-50px); }
}
@keyframes rotateBox {
from { transform: rotate(0deg); }
to { transform: rotate(180deg); }
}
@keyframes scaleUp {
from { transform: scale(1); }
to { transform: scale(1.5); }
}
@keyframes scaleDown {
from { transform: scale(1); }
to { transform: scale(0.7); }
}
/* ====== SCROLL TIMELINE ====== */
.scroll-container {
height: 300px;
overflow-y: scroll;
background: #f8f9fa;
border: 2px solid #ddd;
border-radius: 8px;
margin: 15px 0;
position: relative;
}
.scroll-content {
height: 1200px;
padding: 20px;
}
.scroll-animated-box {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 12px;
position: sticky;
top: 20px;
animation-name: scrollAnimation;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
}
@keyframes scrollAnimation {
from {
transform: scale(0.5) rotate(0deg);
background: #007bff;
opacity: 0.5;
}
to {
transform: scale(1) rotate(360deg);
background: #28a745;
opacity: 1;
}
}
.scroll-instruction {
position: sticky;
top: 0;
background: rgba(255, 255, 255, 0.9);
padding: 10px;
border-radius: 8px;
text-align: center;
font-weight: bold;
color: #007bff;
z-index: 10;
}
/* ====== PRACTICAL: HOVER-PAUSE CARD ====== */
.hover-pause-container {
display: flex;
gap: 30px;
justify-content: center;
flex-wrap: wrap;
margin: 20px 0;
}
.hover-pause-card {
width: 180px;
height: 180px;
background: linear-gradient(135deg, #007bff, #6c5ce7);
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
text-align: center;
padding: 20px;
cursor: pointer;
animation: floatCard 3s ease-in-out infinite;
box-shadow: 0 8px 25px rgba(0, 123, 255, 0.2);
}
.hover-pause-card:hover {
animation-play-state: paused;
}
.hover-pause-card .icon {
font-size: 2.5rem;
margin-bottom: 10px;
}
.hover-pause-card h4 {
margin: 0;
font-size: 1rem;
}
.hover-pause-card p {
margin: 5px 0 0 0;
font-size: 0.8rem;
opacity: 0.9;
}
@keyframes floatCard {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
/* ====== PRACTICAL: SCROLL PROGRESS ====== */
.progress-scroll-container {
height: 250px;
overflow-y: scroll;
background: #f8f9fa;
border: 2px solid #ddd;
border-radius: 8px;
margin: 15px 0;
position: relative;
}
.progress-scroll-content {
height: 1000px;
padding: 20px;
}
.progress-indicator {
position: sticky;
top: 0;
height: 8px;
background: #e9ecef;
border-radius: 4px;
z-index: 10;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #007bff, #6c5ce7);
border-radius: 4px;
animation-name: progressGrow;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
transform-origin: left;
}
@keyframes progressGrow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* ====== TIPS ====== */
.tip-box {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
background: #f8f9fa;
}
.tip-box.success { border-left-color: #28a745; }
.tip-box.warning { border-left-color: #ffc107; }
.tip-box.danger { border-left-color: #dc3545; }
</style>
</head>
<body>
<h1>Animation Properties — Part 2</h1>
<!-- ====== 1. BASIC ANIMATION WITH FILL-MODE ====== -->
<section>
<h2>1. animation-fill-mode: forwards</h2>
<p>The box stays at its final position and color after the animation ends.</p>
<div class="demo-area">
<div class="box"></div>
</div>
<div class="code-block">
.box {
animation-name: exampleAnimation;
animation-duration: 2s;
animation-fill-mode: forwards;
}
@keyframes exampleAnimation {
from { transform: translateX(0); background-color: blue; }
to { transform: translateX(200px); background-color: red; }
}
</div>
<p class="note">Without <code>fill-mode: forwards</code>, the box would snap back to its original position and color.</p>
</section>
<!-- ====== 2. FILL MODE COMPARISON ====== -->
<section>
<h2>2. animation-fill-mode Comparison</h2>
<p>Refresh the page to see the four fill-mode values in action.</p>
<div class="demo-area" style="flex-direction: column; gap: 20px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">none:</span>
<div class="fill-box fill-none">none</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">forwards:</span>
<div class="fill-box fill-forwards">forwards</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">backwards:</span>
<div class="fill-box fill-backwards">backwards</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 150px;">both:</span>
<div class="fill-box fill-both">both</div>
</div>
</div>
<div class="code-block">
.fill-none { animation-fill-mode: none; }
.fill-forwards { animation-fill-mode: forwards; }
.fill-backwards { animation-fill-mode: backwards; }
.fill-both { animation-fill-mode: both; }
@keyframes fillDemo {
from { opacity: 0; transform: translateX(-50px); background: #dc3545; }
to { opacity: 1; transform: translateX(0); background: #28a745; }
}
</div>
<p class="note"><strong>none</strong> reverts after the animation. <strong>forwards</strong> keeps the end state. <strong>backwards</strong> applies the start state during the delay. <strong>both</strong> does both.</p>
</section>
<!-- ====== 3. ANIMATION-PLAY-STATE ====== -->
<section>
<h2>3. animation-play-state</h2>
<p>Hover over the box to pause the animation.</p>
<div class="demo-area">
<div class="play-container">
<div class="play-box">Hover to Pause</div>
</div>
</div>
<div class="code-block">
.play-box {
animation: pulseAnim 1.5s ease-in-out infinite;
}
.play-box:hover {
animation-play-state: paused;
}
</div>
</section>
<!-- ====== 4. ANIMATION-COMPOSITION ====== -->
<section>
<h2>4. animation-composition</h2>
<p>Compare how multiple animations combine with different composition values.</p>
<div class="demo-area" style="flex-direction: column; gap: 25px; align-items: flex-start;">
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">replace (default):</span>
<div class="composition-box comp-replace">replace</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">add:</span>
<div class="composition-box comp-add">add</div>
</div>
<div style="display: flex; align-items: center; gap: 20px;">
<span style="font-weight: bold; min-width: 180px;">accumulate:</span>
<div class="composition-box comp-accumulate">accumulate</div>
</div>
</div>
<div class="code-block">
/* replace: later animation overwrites earlier */
.comp-replace {
animation-name: moveRight, rotateBox;
animation-composition: replace;
}
/* add: effects combine (move right AND up) */
.comp-add {
animation-name: moveRight, moveUp;
animation-composition: add;
}
/* accumulate: scale values add together */
.comp-accumulate {
animation-name: scaleUp, scaleDown;
animation-composition: accumulate;
}
</div>
<p class="note">With <code>add</code>, the box moves both right and up. With <code>replace</code>, only the second animation's transform applies.</p>
</section>
<!-- ====== 5. ANIMATION-TIMELINE (SCROLL) ====== -->
<section>
<h2>5. animation-timeline: scroll()</h2>
<p>Scroll inside the box to see the animation progress with the scroll position.</p>
<div class="scroll-container">
<div class="scroll-content">
<div class="scroll-instruction">⬇️ Scroll down to animate ⬇️</div>
<div class="scroll-animated-box"></div>
<p style="margin-top: 20px; color: #6c757d;">
The box grows, rotates, and changes color as you scroll.
Keep scrolling to see the full animation.
</p>
<p style="margin-top: 200px; color: #6c757d;">
The animation is driven by the scroll position — not by time.
</p>
<p style="margin-top: 200px; color: #6c757d;">
Near the bottom — the animation is almost complete.
</p>
<p style="margin-top: 200px; color: #6c757d;">
🎉 You've reached the end!
</p>
</div>
</div>
<div class="code-block">
.scroll-animated-box {
animation-name: scrollAnimation;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
}
@keyframes scrollAnimation {
from {
transform: scale(0.5) rotate(0deg);
background: #007bff;
opacity: 0.5;
}
to {
transform: scale(1) rotate(360deg);
background: #28a745;
opacity: 1;
}
}
</div>
<div class="tip-box warning">
<strong>⚠️ Browser Support:</strong>
<code>animation-timeline</code> is a modern feature. Check <a href="https://caniuse.com/css-scroll-timeline" target="_blank">caniuse.com</a> for current browser support.
</div>
</section>
<!-- ====== 6. PRACTICAL: HOVER-PAUSE CARDS ====== -->
<section>
<h2>6. Practical Example: Hover-Pause Cards</h2>
<p>Hover over a card to pause its floating animation.</p>
<div class="hover-pause-container">
<div class="hover-pause-card">
<div class="icon">🚀</div>
<h4>Fast</h4>
<p>Hover to pause</p>
</div>
<div class="hover-pause-card">
<div class="icon">🎨</div>
<h4>Beautiful</h4>
<p>Hover to pause</p>
</div>
<div class="hover-pause-card">
<div class="icon">🔒</div>
<h4>Secure</h4>
<p>Hover to pause</p>
</div>
</div>
<div class="code-block">
.hover-pause-card {
animation: floatCard 3s ease-in-out infinite;
}
.hover-pause-card:hover {
animation-play-state: paused;
}
@keyframes floatCard {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
</div>
</section>
<!-- ====== 7. PRACTICAL: SCROLL PROGRESS ====== -->
<section>
<h2>7. Practical Example: Scroll Progress Bar</h2>
<p>Scroll inside the box to see the progress bar fill based on scroll position.</p>
<div class="progress-scroll-container">
<div class="progress-scroll-content">
<div class="progress-indicator">
<div class="progress-fill"></div>
</div>
<p style="margin-top: 20px; color: #6c757d;">
The progress bar at the top fills as you scroll.
</p>
<p style="margin-top: 300px; color: #6c757d;">
Keep scrolling to see the progress grow.
</p>
<p style="margin-top: 300px; color: #6c757d;">
Almost there...
</p>
<p style="margin-top: 300px; color: #6c757d;">
🎉 Complete!
</p>
</div>
</div>
<div class="code-block">
.progress-fill {
animation-name: progressGrow;
animation-duration: 1s;
animation-timeline: scroll(nearest);
animation-fill-mode: both;
transform-origin: left;
}
@keyframes progressGrow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
</div>
</section>
<!-- ====== 8. REFERENCE TABLES ====== -->
<section>
<h2>8. Reference Tables</h2>
<h3>Animation Properties — Part 2</h3>
<table class="reference-table">
<tr>
<th>Property</th>
<th>Description</th>
<th>Default</th>
</tr>
<tr>
<td><code>animation-fill-mode</code></td>
<td>Styles before/after animation</td>
<td><code>none</code></td>
</tr>
<tr>
<td><code>animation-play-state</code></td>
<td>Running or paused</td>
<td><code>running</code></td>
</tr>
<tr>
<td><code>animation-timeline</code></td>
<td>Timeline that controls the animation</td>
<td><code>auto</code></td>
</tr>
<tr>
<td><code>animation-composition</code></td>
<td>How multiple animations combine</td>
<td><code>replace</code></td>
</tr>
</table>
<h3>animation-fill-mode Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>none</code></td>
<td>No styles applied outside the animation (default)</td>
</tr>
<tr>
<td><code>forwards</code></td>
<td>Keep the last keyframe's styles</td>
</tr>
<tr>
<td><code>backwards</code></td>
<td>Apply the first keyframe's styles during delay</td>
</tr>
<tr>
<td><code>both</code></td>
<td>Apply both forwards and backwards</td>
</tr>
</table>
<h3>animation-play-state Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>running</code></td>
<td>Animation is playing (default)</td>
</tr>
<tr>
<td><code>paused</code></td>
<td>Animation is paused</td>
</tr>
</table>
<h3>animation-timeline Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>auto</code></td>
<td>Default document timeline (default)</td>
</tr>
<tr>
<td><code>none</code></td>
<td>No timeline — animation doesn't play</td>
</tr>
<tr>
<td><code>scroll()</code></td>
<td>Binds animation to scroll position</td>
</tr>
<tr>
<td><code>view()</code></td>
<td>Binds animation to element visibility in viewport</td>
</tr>
</table>
<h3>animation-composition Values</h3>
<table class="reference-table">
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>replace</code></td>
<td>New animations overwrite previous ones (default)</td>
</tr>
<tr>
<td><code>add</code></td>
<td>New animations add to existing effects</td>
</tr>
<tr>
<td><code>accumulate</code></td>
<td>Similar to add, with additional rules for keyframes</td>
</tr>
</table>
</section>
<!-- ====== 9. BEST PRACTICES ====== -->
<section>
<h2>9. Best Practices</h2>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
<h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
<ul>
<li>Use <code>animation-fill-mode: forwards</code> to keep the end state</li>
<li>Use <code>animation-fill-mode: backwards</code> to apply start styles during delay</li>
<li>Use <code>animation-play-state: paused</code> on hover for interactive control</li>
<li>Use <code>animation-timeline: scroll()</code> for scroll-driven animations</li>
<li>Use <code>animation-composition: add</code> to combine transform effects</li>
<li>Always provide fallbacks for modern features</li>
<li>Respect <code>prefers-reduced-motion</code> for accessibility</li>
</ul>
</div>
<div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
<h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
<ul>
<li>Don't forget <code>animation-fill-mode</code> — elements snap back without it</li>
<li>Don't use <code>animation-timeline</code> without a fallback for unsupported browsers</li>
<li>Don't confuse <code>animation-composition: add</code> with <code>replace</code></li>
<li>Don't pause animations that are essential for feedback</li>
<li>Don't forget that scroll-driven animations need a scrollable container</li>
<li>Don't forget about <code>prefers-reduced-motion</code></li>
</ul>
</div>
<div class="code-block">
/* Accessibility: respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box,
.fill-box,
.play-box,
.composition-box,
.scroll-animated-box,
.hover-pause-card {
animation: none;
}
}
/* Scroll-driven animation with fallback */
.scroll-animated-box {
animation-name: scrollAnimation;
animation-duration: 1s;
animation-fill-mode: both;
}
@supports (animation-timeline: scroll()) {
.scroll-animated-box {
animation-timeline: scroll(nearest);
}
}
</div>
</section>
</body>
</html>
Quick Reference
| Property | Description | Default |
|---|---|---|
animation-fill-mode | Styles before/after animation | none |
animation-play-state | Running or paused | running |
animation-timeline | Timeline that controls the animation | auto |
animation-composition | How multiple animations combine | replace |
animation-fill-mode Values
| Value | Description |
|---|---|
none | No styles applied outside the animation (default) |
forwards | Keep the last keyframe’s styles |
backwards | Apply the first keyframe’s styles during delay |
both | Apply both forwards and backwards |
animation-play-state Values
| Value | Description |
|---|---|
running | Animation is playing (default) |
paused | Animation is paused |
animation-timeline Values
| Value | Description |
|---|---|
auto | Default document timeline (default) |
none | No timeline — animation doesn’t play |
scroll() | Binds animation to scroll position |
view() | Binds animation to element visibility |
animation-composition Values
| Value | Description |
|---|---|
replace | New animations overwrite previous ones (default) |
add | New animations add to existing effects |
accumulate | Similar to add, with additional rules for keyframes |
Best Practices
✅ Do This:
/* Keep the end state */
.box {
animation-fill-mode: forwards;
}
/* Pause on hover */
.box:hover {
animation-play-state: paused;
}
/* Scroll-driven animation with fallback */
@supports (animation-timeline: scroll()) {
.box {
animation-timeline: scroll();
}
}
/* Combine transforms */
.box {
animation-name: moveRight, rotate;
animation-composition: add;
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.box {
animation: none;
}
}
❌ Don’t Do This:
/* Don't forget fill-mode */
.box {
animation: slideIn 1s;
/* Snaps back after animation */
}
/* Don't use animation-timeline without fallback */
.box {
animation-timeline: scroll();
/* Won't work in unsupported browsers */
}
/* Don't use replace when you want combined effects */
.box {
animation-name: moveRight, moveUp;
animation-composition: replace;
/* Only moveUp applies — moveRight is overwritten */
}
Pro Tip: animation-fill-mode: forwards is essential for entrance animations — without it, elements snap back to their original state. animation-play-state: paused on hover gives users control over animations, which is great for accessibility. animation-timeline: scroll() enables scroll-driven animations — a modern, performant way to create engaging scroll experiences (always provide a fallback). animation-composition: add lets you combine multiple transforms (e.g., move right AND rotate). And always respect prefers-reduced-motion — some users experience discomfort from animations!
58. CSS Cheatsheet — Complete Reference
1. Selectors
Basic Selectors
| Selector | Syntax | Description |
|---|---|---|
| Element | p { } | Targets all elements by tag name |
| Class | .highlight { } | Targets elements with a specific class |
| ID | #main-title { } | Targets one unique element |
| Universal | * { } | Targets all elements |
| Attribute | [ type="text" ] { } | Targets elements with a specific attribute |
p { color: blue; }
.highlight { background-color: yellow; }
#main-title { font-size: 36px; }
* { margin: 0; padding: 0; }
[type="text"] { border: 1px solid black; }
Attribute Selectors
| Syntax | Description | Example |
|---|---|---|
[ attr ] | Has attribute | [ disabled ] { } |
[ attr="value" ] | Exact match | [ type="text" ] { } |
[ attr^="value" ] | Starts with | [ class^="btn-" ] { } |
[ attr$="value" ] | Ends with | [ href$=".pdf" ] { } |
[ attr*="value" ] | Contains | [ class*="icon" ] { } |
[ attr~="value" ] | Word match | [ class~="active" ] { } |
Combinators
| Combinator | Syntax | Description |
|---|---|---|
| Descendant | div p { } | All <p> inside <div> |
| Child | ul > li { } | Direct children only |
| Adjacent Sibling | h1 + p { } | <p> immediately after <h1> |
| General Sibling | h2 ~ p { } | All <p> after <h2> |
div p { color: red; }
ul > li { font-weight: bold; }
h1 + p { margin-top: 20px; }
h2 ~ p { font-style: italic; }
Pseudo-Classes
| Pseudo-Class | Description |
|---|---|
:hover | Mouse over element |
:focus | Element has focus |
:active | Element being clicked |
:visited | Visited link |
:first-child | First child of parent |
:last-child | Last child of parent |
:nth-child(n) | Nth child |
:not(selector) | Negation |
a:hover { color: red; }
input:focus { border-color: blue; }
li:first-child { font-weight: bold; }
li:nth-child(odd) { background: #f0f0f0; }
Pseudo-Elements
| Pseudo-Element | Description |
|---|---|
::before | Insert before content |
::after | Insert after content |
::first-line | First line of text |
::first-letter | First letter |
::placeholder | Input placeholder |
.quote::before { content: "“"; }
.quote::after { content: "”"; }
p::first-letter { font-size: 2em; }
input::placeholder { color: gray; }
2. Box Model
The Four Layers
┌─────────────────────────────────────┐
│ MARGIN │
│ ┌───────────────────────────────┐ │
│ │ BORDER │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ PADDING │ │ │
│ │ │ ┌───────────────────┐ │ │ │
│ │ │ │ CONTENT │ │ │ │
│ │ │ └───────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
Properties
/* Margin — space OUTSIDE the border */
margin: 10px; /* All sides */
margin: 10px 20px; /* Vertical | Horizontal */
margin: 10px 20px 30px; /* Top | Horizontal | Bottom */
margin: 10px 20px 30px 40px; /* Top | Right | Bottom | Left */
margin: 0 auto; /* Center block element */
/* Padding — space INSIDE the border */
padding: 10px;
padding: 10px 20px;
padding: 10px 20px 30px;
padding: 10px 20px 30px 40px;
/* Border */
border: 1px solid black;
border-width: 2px;
border-style: dashed; /* solid, dashed, dotted, double, groove, ridge, inset, outset */
border-color: red;
border-radius: 8px; /* Rounded corners */
/* Width & Height */
width: 300px;
height: 200px;
max-width: 100%;
min-height: 100vh;
/* Box Sizing (best practice) */
*, *::before, *::after {
box-sizing: border-box;
}
3. Typography
/* Font Family */
font-family: 'Segoe UI', Arial, sans-serif;
/* Font Size */
font-size: 16px;
font-size: 1rem; /* Relative to root */
font-size: 1.2em; /* Relative to parent */
font-size: clamp(1rem, 2vw, 2rem); /* Responsive */
/* Font Weight */
font-weight: normal; /* 400 */
font-weight: bold; /* 700 */
font-weight: 300; /* Light */
/* Font Style */
font-style: italic;
font-style: normal;
/* Text Alignment */
text-align: left; /* Default */
text-align: center;
text-align: right;
text-align: justify;
/* Text Decoration */
text-decoration: none;
text-decoration: underline;
text-decoration: line-through;
/* Text Transform */
text-transform: uppercase;
text-transform: lowercase;
text-transform: capitalize;
/* Line Height */
line-height: 1.6; /* Unitless — recommended */
line-height: 24px;
/* Letter Spacing */
letter-spacing: 2px;
/* Word Spacing */
word-spacing: 4px;
/* Text Shadow */
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
/* Truncate Text */
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
4. Colors
| Format | Example | Description |
|---|---|---|
| Named | red | Predefined color names |
| Hex | #FF0000 | 6-digit hex |
| Hex (short) | #F00 | 3-digit hex |
| RGB | rgb(255, 0, 0) | Red, Green, Blue (0–255) |
| RGBA | rgba(255, 0, 0, 0.5) | RGB + opacity |
| HSL | hsl(0, 100%, 50%) | Hue, Saturation, Lightness |
| HSLA | hsla(0, 100%, 50%, 0.5) | HSL + opacity |
| Modern | oklch(69% 0.27 240) | Perceptually uniform |
/* Color Properties */
color: #333;
background-color: rgba(255, 0, 0, 0.5);
border-color: hsl(0, 100%, 75%);
caret-color: red;
/* Opacity */
opacity: 0.7;
/* Gradients */
background: linear-gradient(to right, red, blue);
background: radial-gradient(circle, red, blue);
background: conic-gradient(red, blue, green, red);
background: repeating-linear-gradient(45deg, red 10%, blue 20%);
5. Background
/* Background Color */
background-color: #f0f0f0;
/* Background Image */
background-image: url('image.jpg');
background-image: linear-gradient(to right, red, blue);
/* Background Repeat */
background-repeat: no-repeat; /* repeat, repeat-x, repeat-y, space, round */
/* Background Position */
background-position: center; /* top, bottom, left, right, center, % */
background-position: center top;
/* Background Size */
background-size: cover; /* cover, contain, auto, 100% */
background-size: contain;
/* Background Attachment */
background-attachment: fixed; /* scroll, fixed, local */
/* Background Clip */
background-clip: padding-box; /* border-box, padding-box, content-box, text */
/* Background Origin */
background-origin: border-box;
/* Shorthand */
background: url('image.jpg') center / cover no-repeat fixed #f8f9fa;
6. Display
| Value | Description |
|---|---|
block | Full width, new line |
inline | Sits next to other elements |
inline-block | Inline but respects width/height |
flex | Flex container |
grid | Grid container |
none | Hidden (removed from flow) |
display: block;
display: inline;
display: inline-block;
display: flex;
display: grid;
display: none;
7. Positioning
| Value | Description |
|---|---|
static | Normal flow (default) |
relative | Relative to normal position |
absolute | Relative to nearest positioned ancestor |
fixed | Relative to viewport |
sticky | Relative until threshold, then fixed |
/* Relative */
position: relative;
top: 20px;
left: 30px;
/* Absolute */
position: absolute;
top: 50px;
right: 100px;
/* Fixed */
position: fixed;
bottom: 20px;
left: 30px;
/* Sticky */
position: sticky;
top: 0;
/* Z-index */
z-index: 10;
/* Center with absolute + translate */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
8. Flexbox
Container Properties
.container {
display: flex;
/* Direction */
flex-direction: row; /* Default */
flex-direction: column;
flex-direction: row-reverse;
flex-direction: column-reverse;
/* Wrapping */
flex-wrap: nowrap; /* Default */
flex-wrap: wrap;
flex-wrap: wrap-reverse;
/* Shorthand */
flex-flow: row wrap;
/* Main Axis Alignment */
justify-content: flex-start; /* Default */
justify-content: flex-end;
justify-content: center;
justify-content: space-between;
justify-content: space-around;
justify-content: space-evenly;
/* Cross Axis Alignment */
align-items: stretch; /* Default */
align-items: flex-start;
align-items: flex-end;
align-items: center;
align-items: baseline;
/* Multi-line Alignment */
align-content: flex-start;
align-content: center;
align-content: space-between;
/* Gap */
gap: 20px;
gap: 20px 10px; /* row-gap column-gap */
}
Item Properties
.item {
/* Growth */
flex-grow: 0; /* Default */
flex-grow: 1;
/* Shrinking */
flex-shrink: 1; /* Default */
flex-shrink: 0;
/* Base Size */
flex-basis: auto; /* Default */
flex-basis: 200px;
flex-basis: 50%;
/* Shorthand */
flex: 1; /* grow: 1, shrink: 1, basis: 0% */
flex: auto; /* grow: 1, shrink: 1, basis: auto */
flex: none; /* grow: 0, shrink: 0, basis: auto */
flex: 2 1 200px; /* grow: 2, shrink: 1, basis: 200px */
/* Individual Alignment */
align-self: center;
/* Order */
order: 2; /* Default is 0 */
}
9. Grid
Container Properties
.container {
display: grid;
/* Columns */
grid-template-columns: 1fr 2fr 1fr;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-template-columns: minmax(100px, 200px) auto;
/* Rows */
grid-template-rows: 100px auto 200px;
grid-template-rows: repeat(3, 1fr);
/* Named Areas */
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
/* Shorthand */
grid-template:
"header header" auto
"sidebar main" 200px / 1fr 3fr;
/* Gaps */
gap: 20px;
row-gap: 20px;
column-gap: 10px;
/* Alignment */
justify-items: start; /* start, center, end, stretch */
align-items: start; /* start, center, end, stretch */
justify-content: center; /* whole grid */
align-content: center;
/* Auto Placement */
grid-auto-flow: row; /* row, column, row dense */
grid-auto-columns: 200px;
grid-auto-rows: 100px;
}
Item Properties
.item {
/* Named Area */
grid-area: header;
/* Numeric Placement */
grid-area: 1 / 1 / 3 / 3; /* row-start / col-start / row-end / col-end */
/* Individual */
grid-row: 1 / 3;
grid-column: 2 / 4;
grid-row-start: 1;
grid-row-end: 3;
grid-column-start: 2;
grid-column-end: 4;
/* Span */
grid-column: 1 / span 2;
/* Self Alignment */
justify-self: center;
align-self: center;
}
10. Transitions
/* Individual Properties */
transition-property: background-color, transform;
transition-duration: 0.5s;
transition-timing-function: ease-in-out;
transition-delay: 0.2s;
transition-behavior: allow-discrete; /* For display/visibility */
/* Shorthand */
transition: background-color 0.5s ease;
transition: transform 0.3s ease-in-out 0.1s;
transition: all 0.3s ease;
/* Multiple Properties */
transition:
background-color 0.5s ease,
transform 0.3s ease,
box-shadow 0.5s ease;
Timing Functions
| Value | Description |
|---|---|
ease | Slow start, fast middle, slow end (default) |
linear | Constant speed |
ease-in | Slow start, fast end |
ease-out | Fast start, slow end |
ease-in-out | Slow start, fast middle, slow end |
cubic-bezier(n,n,n,n) | Custom curve |
steps(n) | Stepped animation |
11. Animations
Applying Animations
.box {
/* Individual Properties */
animation-name: slideIn;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 0.5s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;
animation-play-state: running;
animation-timeline: scroll();
animation-composition: replace;
/* Shorthand */
animation: slideIn 2s ease-in-out 0.5s infinite alternate forwards;
}
@keyframes
/* Using from/to */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Using percentages */
@keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-50px); }
100% { transform: translateY(0); }
}
/* Multi-step */
@keyframes multiStep {
0% { background: red; transform: translate(0, 0); }
25% { background: blue; transform: translate(100px, 0); }
50% { background: green; transform: translate(100px, 100px); }
75% { background: yellow; transform: translate(0, 100px); }
100% { background: red; transform: translate(0, 0); }
}
Animation Properties Reference
| Property | Values | Default |
|---|---|---|
animation-name | @keyframes name | none |
animation-duration | s, ms | 0s |
animation-timing-function | ease, linear, etc. | ease |
animation-delay | s, ms | 0s |
animation-iteration-count | number, infinite | 1 |
animation-direction | normal, reverse, alternate, alternate-reverse | normal |
animation-fill-mode | none, forwards, backwards, both | none |
animation-play-state | running, paused | running |
animation-timeline | auto, none, scroll(), view() | auto |
animation-composition | replace, add, accumulate | replace |
12. Transforms
/* 2D Transforms */
transform: translate(50px, 25px);
transform: translateX(50px);
transform: translateY(25px);
transform: scale(1.5);
transform: scale(1.5, 2);
transform: scaleX(2);
transform: scaleY(0.5);
transform: rotate(45deg);
transform: skew(30deg, -20deg);
transform: skewX(30deg);
transform: skewY(20deg);
/* 3D Transforms */
transform: translate3d(50px, 25px, 100px);
transform: translateZ(100px);
transform: scale3d(1.5, 1.5, 2);
transform: scaleZ(2);
transform: rotate3d(1, 1, 1, 90deg);
transform: rotateX(45deg);
transform: rotateY(45deg);
transform: rotateZ(45deg);
/* Matrix */
transform: matrix(1, 0, 0, 1, 100, 100);
transform: matrix3d(...); /* 16 values */
/* Combined */
transform: rotate(45deg) scale(1.5) translate(20px, 10px);
/* Related Properties */
transform-origin: center center; /* top left, bottom right, % */
transform-style: preserve-3d;
perspective: 800px;
backface-visibility: hidden;
13. Media Queries
Common Breakpoints
| Breakpoint | Device |
|---|---|
320px | Small phones |
480px | Larger phones |
768px | Tablets |
1024px | Small laptops |
1200px | Desktops |
1440px | Large desktops |
Syntax
/* Width-based */
@media (min-width: 768px) { }
@media (max-width: 1024px) { }
@media (min-width: 768px) and (max-width: 1024px) { }
/* Orientation */
@media (orientation: portrait) { }
@media (orientation: landscape) { }
/* Dark mode */
@media (prefers-color-scheme: dark) { }
@media (prefers-color-scheme: light) { }
/* Reduced motion */
@media (prefers-reduced-motion: reduce) { }
@media (prefers-reduced-motion: no-preference) { }
/* Contrast */
@media (prefers-contrast: more) { }
@media (prefers-contrast: less) { }
/* Hover capability */
@media (hover: hover) { }
@media (hover: none) { }
/* Pointer accuracy */
@media (pointer: fine) { }
@media (pointer: coarse) { }
@media (pointer: none) { }
/* Reduced data */
@media (prefers-reduced-data: reduce) { }
/* Reduced transparency */
@media (prefers-reduced-transparency: reduce) { }
/* Forced colors (high contrast mode) */
@media (forced-colors: active) { }
/* Print */
@media print { }
/* Range syntax (modern) */
@media (width >= 768px) { }
@media (768px <= width <= 1024px) { }
Common Media Features
| Feature | Description |
|---|---|
width / min-width / max-width | Viewport width |
height / min-height / max-height | Viewport height |
orientation | portrait / landscape |
aspect-ratio | Viewport ratio |
resolution | Pixel density (dpi, dppx) |
hover / any-hover | Hover capability |
pointer / any-pointer | Pointer accuracy |
prefers-color-scheme | Dark/light mode |
prefers-reduced-motion | Reduced motion |
prefers-contrast | Contrast preference |
prefers-reduced-data | Data saver |
prefers-reduced-transparency | Transparency preference |
forced-colors | High contrast mode |
color-gamut | Color space (srgb, p3, rec2020) |
dynamic-range | HDR support |
scripting | JavaScript availability |
update | Refresh rate (none, slow, fast) |
overflow-block | Block axis overflow |
overflow-inline | Inline axis overflow |
grid | Grid-based screen |
14. Units
Absolute Units
| Unit | Description |
|---|---|
px | Pixels (1/96 inch) |
cm | Centimeters |
mm | Millimeters |
in | Inches |
pt | Points (1/72 inch) |
pc | Picas (12 points) |
Relative Units
| Unit | Relative To |
|---|---|
% | Parent element |
em | Current font size |
rem | Root font size |
vw | Viewport width (1% = 1vw) |
vh | Viewport height |
vmin | Smaller of vw/vh |
vmax | Larger of vw/vh |
ch | Width of ‘0’ character |
ex | x-height of font |
fr | Fraction of available space |
lh | Line height |
Angle Units
| Unit | Description |
|---|---|
deg | Degrees (360 = full circle) |
rad | Radians (2π = full circle) |
grad | Gradians (400 = full circle) |
turn | Turns (1 = full circle) |
15. Other Useful Properties
/* Visibility */
visibility: hidden;
display: none;
opacity: 0.5;
/* Overflow */
overflow: hidden;
overflow: auto;
overflow: scroll;
overflow-x: auto;
overflow-y: scroll;
/* Cursor */
cursor: pointer;
cursor: default;
cursor: not-allowed;
/* Box Shadow */
box-shadow: 2px 4px 6px rgba(0, 0, 0, 0.5);
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 6px rgba(0,0,0,0.1), 0 10px 20px rgba(0,0,0,0.05);
/* Filters */
filter: blur(5px);
filter: brightness(150%);
filter: contrast(200%);
filter: grayscale(100%);
filter: hue-rotate(180deg);
filter: invert(100%);
filter: saturate(200%);
filter: sepia(70%);
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.3));
/* Object Fit */
object-fit: cover; /* contain, fill, none, scale-down */
object-position: center;
/* Aspect Ratio */
aspect-ratio: 16 / 9;
/* Writing Mode */
writing-mode: vertical-rl;
writing-mode: horizontal-tb;
/* Scroll Behavior */
scroll-behavior: smooth;
/* User Select */
user-select: none;
/* Pointer Events */
pointer-events: none;
pointer-events: auto;
16. Best Practices
✅ Do This
/* Use box-sizing globally */
*, *::before, *::after {
box-sizing: border-box;
}
/* Use CSS custom properties for theming */
:root {
--primary: #007bff;
--text: #333;
}
.button {
background: var(--primary);
color: var(--text);
}
/* Use transform instead of top/left for animations */
.box {
transition: transform 0.3s ease;
}
.box:hover {
transform: translateY(-5px);
}
/* Use rem for font sizes */
html { font-size: 16px; }
h1 { font-size: 2rem; }
/* Use modern layout systems */
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
* { animation: none; transition: none; }
}
@media (prefers-color-scheme: dark) {
:root { --bg: #1a1a1a; --text: #f8f9fa; }
}
/* Use logical properties */
margin-inline: auto; /* Instead of margin-left/right */
padding-block: 20px; /* Instead of padding-top/bottom */
❌ Don’t Do This
/* Don't use !important */
.button { color: red !important; }
/* Don't use px for font sizes (accessibility issue) */
p { font-size: 16px; } /* Use rem instead */
/* Don't animate layout properties */
.box { transition: width 0.3s, height 0.3s; }
/* Don't use IDs for styling */
#button { } /* Use classes instead */
/* Don't use transition: all */
.box { transition: all 0.3s; } /* Specify properties */
/* Don't ignore reduced motion */
/* Always provide @media (prefers-reduced-motion: reduce) */
/* Don't use fixed pixel widths for responsive layouts */
.container { width: 1200px; } /* Use max-width + % */
17. CSS Custom Properties (Variables)
/* Define */
:root {
--primary-color: #007bff;
--secondary-color: #6c5ce7;
--text-color: #333;
--spacing: 20px;
--border-radius: 8px;
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Use */
.button {
background: var(--primary-color);
color: var(--text-color);
padding: var(--spacing);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
}
/* Fallback */
.color {
color: var(--text-color, #333);
}
/* Override for dark mode */
@media (prefers-color-scheme: dark) {
:root {
--text-color: #f8f9fa;
--primary-color: #4dabf7;
}
}
18. Functions
/* calc() — Calculations */
width: calc(100% - 40px);
height: calc(100vh - 80px);
font-size: calc(1rem + 2vw);
/* clamp() — Responsive sizing */
font-size: clamp(1rem, 2vw, 2rem);
width: clamp(300px, 50%, 800px);
/* min() / max() */
width: min(100%, 800px);
width: max(300px, 50%);
/* var() — Custom properties */
color: var(--text-color);
/* url() — Resources */
background-image: url('image.jpg');
/* Counter */
counter-reset: section;
counter-increment: section;
content: counter(section);
Quick Reference Card
SELECTORS BOX MODEL FLEXBOX GRID
───────── ───────── ─────── ────
p { } margin display: flex display: grid
.class { } padding flex-direction grid-template-columns
#id { } border justify-content grid-template-rows
* { } width/height align-items gap
[attr] { } box-sizing gap grid-area
div p { } flex grid-row / grid-column
ul > li { } POSITION flex-wrap
h1 + p { } ──────── align-self ANIMATION
h2 ~ p { } static order ─────────
relative animation
:hover { } absolute TRANSITIONS @keyframes
::before { } fixed ─────────── animation-name
sticky transition animation-duration
UNITS transition-property
───── TYPOGRAPHY transition-duration
px em rem ────────── transition-timing-function
% vw vh font-family transition-delay
fr ch deg font-size
font-weight TRANSFORMS
text-align ──────────
line-height translate
letter-spacing scale
text-transform rotate
text-shadow skew
perspective
THE END
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!