Arrange pages like a pro with Flexbox and Grid, make them work on every phone, and bring them to life with animation.
Every example opens in a live browser preview. Click ✎ Edit on any preview, or press L, to open the Code Lab.
display: flex on the parent, then simple words: space-between, center, wrap.display: flex on the parent (the container)flex-direction: column swaps the axesdisplay: flex on the items instead of the parent. Flex goes on the box that contains the things you want to arrange.Drag the slider: 4 cards in a row on desktop, 2 on tablet, 1 on mobile – with no media query!
flex-wrap: wrap lets items move to the next linegap adds space between items (no margin tricks)flex: 1 1 160px = grow, shrink, start at 160px wide| Property | Meaning |
|---|---|
flex-grow | Share of the EXTRA space (0 = do not grow) |
flex-shrink | Can it shrink when space is short? (1 = yes) |
flex-basis | Starting size before growing/shrinking |
flex: 1 | Shorthand: grow 1, shrink 1, basis 0 – equal columns |
order | Change visual order without changing HTML |
align-self | One item overrides align-items |
| Pattern | Key CSS |
|---|---|
| Navbar | justify-content: space-between; align-items: center |
| Media object (avatar + text) | display: flex; gap; align-items: flex-start |
| Footer stays at bottom | page: flex-direction: column; min-height, main: flex: 1 |
| Evenly spread links | justify-content: space-around |
space-between to space-around. Add flex-direction: column to the header. Put 3 boxes in the hero and give the middle one flex: 2.header with display flex, space-between, align-items centerul turned into a flex row (no bullets)flex: 1 1 180px, white, rounded, shadowmargin-top: auto)margin-top: auto on the button eats all free space above it, pushing it to the bottom. All buttons line up even when titles have different lengths.flex: 1 1 160px.msg: display flex, gap, align-items flex-startflex: 0 0 44px)flex: 1).reply indented with margin-left.me uses flex-direction: row-reverse and a green bubbleModule 14: CSS Grid – rows AND columns together for galleries and dashboards.
Q1. Logo left, links right. Which value?
Q2. Centre vertically in a flex row:
Q3. Where do you write display: flex?
| Flexbox | Grid | |
|---|---|---|
| Direction | Row OR column | Rows AND columns |
| Thinks from | The content outwards | The layout inwards |
| Best for | Navbars, button rows, small parts | Page layouts, galleries, dashboards |
| Overlapping items | Hard | Easy |
1fr 2fr 1fr splits into 4 parts: 1/4, 2/4, 1/4. Like dividing a cake.| You write | Means |
|---|---|
repeat(3, 1fr) | Same as 1fr 1fr 1fr |
repeat(2, 100px 1fr) | 100px 1fr 100px 1fr |
minmax(200px, 1fr) | At least 200px, at most 1 share |
repeat(auto-fit, minmax(200px, 1fr)) | As many 200px+ columns as fit; stretch them to fill |
Drag the slider and watch the columns change from 6 to 1.
Grid lines are numbered from 1. 1 / -1 = from the first to the last line (full width).
. means an empty cell.repeat(auto-fit, minmax(140px, 1fr)). Add a 5th tile. Try grid-auto-flow: dense.display: grid with 3 equal columns and gap 12pxrepeat(auto-fit, minmax(160px, 1fr)) for responsivenessrepeat(auto-fit, minmax(200px, 1fr))var(--col) is a CSS variable. You will learn them properly in Module 16.grid-column-start: 7.sun instead!Module 15: Responsive Design – one website that works on phones, tablets and laptops.
Q1. 1fr 2fr splits the width:
Q2. Make an item span the full width of the grid:
Q3. Best tool for a whole page layout with sidebar?
In India, most people visit websites on a mobile phone.
| Part | Meaning |
|---|---|
width=device-width | Page width = phone screen width |
initial-scale=1.0 | No zoom when the page opens |
Drag to 400px: the red box breaks out, the green box adapts.
max-width instead of a fixed widthimg { max-width: 100%; height: auto; } – images never overflowDrag the slider across 600px and 992px.
| Query | True when |
|---|---|
(min-width: 600px) | Screen is 600px or wider |
(max-width: 599px) | Screen is 599px or narrower |
(orientation: landscape) | Wider than tall |
print | When printing the page |
At 420px: tap ☰ in the preview. Then drag above 700px.
#menu-toggle:checked ~ nav shows the menu| Tool | Use when |
|---|---|
max-width: 100% | Always (basic rule) |
srcset + sizes | Same picture, different file sizes → phones download smaller files |
<picture> | Different picture or crop for mobile (art direction) |
loading="lazy" | Load images only when scrolled near |
A phone on 4G should not download the 480 KB desktop version.
| Checklist | |
|---|---|
| No sideways scrolling at 320px | ☐ |
| Text at least 16px, readable without zoom | ☐ |
| Buttons/links at least 44px tall to tap | ☐ |
| Menu usable on mobile | ☐ |
| Images not stretched or cut badly | ☐ |
| Forms use the right keyboards (tel, email) | ☐ |
font-size: clamp(1.5rem, 4vw, 3rem); grows with the screen but stays between a minimum and maximum (Module 16).:checked ~ nav shows it as a vertical listfont-size: clamp(1.6rem, 6vw, 3.2rem).table-wrap with overflow-x: automin-width: 600pxposition: sticky; left: 0Module 16: CSS Variables, Dark Mode & Functions – themes and smart sizing.
Q1. Which tag is required for media queries to work properly on phones?
Q2. Mobile-first CSS uses mostly:
Q3. Stop images from overflowing:
| Rule | Example |
|---|---|
Name starts with -- | --brand: #e34c26; |
Use with var() | color: var(--brand); |
Global in :root | :root = the html element |
| Fallback value | var(--accent, orange) |
| Local override | .dark-card { --brand: gold; } |
| Case sensitive | --Brand ≠ --brand |
body:has(#dark:checked) means "the body that contains a ticked #dark checkbox". It works in all modern browsers.| Function | Example | Result |
|---|---|---|
calc() | calc(100% - 150px) | Mix units with + - * / |
min() | min(90%, 400px) | Smaller of the two |
max() | max(60px, 10vh) | Bigger of the two |
clamp() | clamp(1.4rem, 5vw, 3rem) | min, preferred, max |
aspect-ratio: 16 / 9 keeps video thumbnails the right shape at any widthaspect-ratio: 1 makes perfect squares (and circles with radius 50%):is(h3, h4):hover is short for h3:hover, h4:hover:where() is the same but has zero specificity (easy to override).card { &:hover { ... } } – nesting like Sass, without any tool.--brand to orange. In dark mode, also change --brand to a lighter green. Add --radius and use it on the button.:root with --primary, --secondary, --text, --radius.theme-green and .theme-blue on body that override the variables:has().body:has(#mode:checked)prefers-color-scheme: darkclamp(1.75rem, 5vw, 3.5rem)clamp(1.25rem, 3vw, 2rem)clamp(1rem, 1.2vw, 1.15rem)clamp(12px, 4vw, 48px)calc(100% - 240px)calc(100vh - 80px) (here use 300px)grid-template-columns: 220px 1fr) does this more easily, but calc() is still used everywhere for sizes.repeat(auto-fit, minmax(180px, 1fr))aspect-ratio: 16 / 9 and object-fit cover-webkit-line-clampModule 17: Transitions, Transforms & Animations – bring your pages to life.
Q1. How do you use a variable called --brand?
Q2. clamp(1rem, 4vw, 2rem) on a very small phone gives:
Q3. Where are global variables usually defined?
transition on the normal state, not only on :hovertransition: all 0.3s is quick to write but can be slowdisplay: none to block does not transition. Use opacity + visibility instead.| Function | Feels like | Use for |
|---|---|---|
| linear | Robot, constant speed | Spinners, tickers |
| ease (default) | Natural start and stop | Most UI |
| ease-in | Slow start, fast end | Things leaving the screen |
| ease-out | Fast start, gentle stop | Things entering the screen |
| ease-in-out | Slow-fast-slow | Moving across the screen |
| cubic-bezier() | Your own curve (even bounce) | Playful effects |
perspective on the outer box gives 3D depthtransform-style: preserve-3d on the inner boxposition: absolute and backface-visibility: hiddenrotateY(180deg)| Property | Example | Meaning |
|---|---|---|
| animation-name | bounce | Which @keyframes |
| animation-duration | 1s | One cycle |
| animation-timing-function | ease-in-out | Speed curve |
| animation-delay | 0.5s | Wait before start |
| animation-iteration-count | 3 / infinite | How many times |
| animation-direction | alternate | Forwards then backwards |
| animation-fill-mode | forwards | Keep the last frame |
| animation-play-state | paused | Pause (e.g. on hover) |
Changing size or position forces the browser to recalculate the whole layout on every frame.
prefers-reduced-motionscale(1.1) to scale(1.3) rotate(3deg). Make the tag pulse forever with a new @keyframes. Change the timing to linear.:hover and :focus-withintabindex="0" so keyboard users can focus a card@keyframes spin to 360deg, linear, infinitestyle="--w: 85%"@keyframes fill from width 0 to var(--w)@keyframes scroll from translateX(100%) to translateX(-100%)animation-play-state: pausedprefers-reduced-motionPart 4: Pure-CSS components, professional workflow and 5 capstone websites.
Q1. Where should you write the transition property?
Q2. Which is smoothest to animate?
Q3. Loop an animation forever:
You have finished Part 3 of the Complete HTML + CSS Course.
Keep all your practice files in D:\my-website. You will reuse them in the capstone projects.