CSS turns the HTML structure of your app into the visual interface users actually see. Every box, every color, every spacing decision, every layout in React is ultimately CSS. This reference covers the foundations you need to read and write CSS confidently for the program — and what each concept maps to in Tailwind CSS, the utility-class system we use daily from Week 1 onward.
Every section has a snippet you can paste straight into the browser DevTools (right-click anything on a page → Inspect → Elements panel → edit the Styles pane on the right) to see the effect live. Don’t just read — type, change values, watch what breaks. That’s how CSS becomes reflexive. You’ll meet DevTools in detail in the Browser DevTools Tour.
1. The Box Model
Every HTML element is a rectangular box. The box has four concentric layers, each contributing to its total size:
┌──────────────── margin ────────────────┐
│ ┌────────────── border ──────────────┐ │
│ │ ┌──────────── padding ───────────┐ │ │
│ │ │ content │ │ │
│ │ └────────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
└────────────────────────────────────────┘
- Content — the text/image/child elements
- Padding — space inside the border, around the content
- Border — the visible edge of the box
- Margin — space outside the border, separating this box from its neighbors
CSS for each:
.card {
width: 300px;
padding: 16px;
border: 1px solid #ccc;
margin: 24px;
}
By default, width only sets the content width — padding and border are added on top. So width: 300px + padding: 16px (×2 sides) + border: 1px (×2 sides) = actual rendered width of 334px. This is rarely what you want.
The fix is box-sizing: border-box:
* {
box-sizing: border-box;
}
.card {
width: 300px; /* now includes padding + border */
padding: 16px;
border: 1px solid #ccc;
}
/* Card renders at exactly 300px wide */
This is the universal modern default. Every CSS reset, every Tailwind project, every Next.js starter includes it.
Tailwind equivalent: w-[300px] p-4 border border-gray-300 m-6 (where p-4 = 1rem = 16px in the default theme; box-sizing: border-box is preset).
Why this matters in React: Every component you build sits in a box. When components don’t line up, it’s almost always a box-model misunderstanding — usually padding or border pushing things wider than expected. Lean on padding (inside the box) over margin (outside) when you can — it composes more predictably.
2. Selectors and Specificity
CSS rules need to target elements. A selector says which elements to style.
/* Type / element selector */
button { background: #007aff; }
/* Class selector — most common */
.btn-primary { background: #007aff; }
/* ID selector — rare in modern React */
#hero { background: #f4f4f4; }
/* Descendant — any .btn anywhere inside .toolbar */
.toolbar .btn { margin-left: 8px; }
/* Child — only .btn that's a direct child of .toolbar */
.toolbar > .btn { margin-left: 8px; }
/* Attribute selector */
input[type="email"] { font-family: monospace; }
Pseudo-classes match elements in a particular state:
.btn:hover { background: #0055cc; }
.btn:focus { outline: 2px solid blue; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
a:visited { color: purple; }
li:first-child { margin-top: 0; }
Pseudo-elements style a piece of the element:
.required::after { content: " *"; color: red; }
::placeholder { color: #999; }
Specificity decides which rule wins when two target the same element. From strongest to weakest:
- Inline
style="..."attribute #idselectors.class,[attribute],:pseudo-classselectorstag,::pseudo-elementselectors
So .btn.primary (two classes) beats button (one tag); #submit beats .btn; style="..." beats every other selector — but don’t reach for inline styles or !important to win specificity fights. Restructure the selector instead.
Tailwind: Tailwind sidesteps specificity by giving every utility class the same low specificity (one class each). The cascade order in your HTML decides which one wins, which is much easier to reason about than nested rules.
Why this matters in React: In modern React you rarely write complex selectors by hand — Tailwind’s utility classes target individual elements directly. But you’ll still read existing CSS in third-party components and use pseudo-classes constantly. hover:bg-blue-700 in Tailwind expands to :hover { background: ... } in CSS.
3. Display — Block vs Inline vs Flex vs Grid
The display property controls how an element flows on the page. The values you’ll meet:
block— takes the full available width and stacks vertically. Default for<div>,<h1>,<p>,<section>,<header>.inline— flows in text, ignoreswidth/height. Default for<span>,<a>,<strong>,<em>.inline-block— flows like inline but acceptswidth/height. Useful for buttons mid-paragraph.flex— children become flex items, laid out in one dimension (§4).grid— children become grid cells, laid out in two dimensions (§5).none— removed from the layout entirely (not just hidden — gone).
.row {
display: flex; /* lay children horizontally */
}
.card-grid {
display: grid; /* lay children as a grid */
grid-template-columns: repeat(3, 1fr);
}
.hidden {
display: none; /* removes from the layout entirely */
}
Tailwind: block, inline-block, inline, flex, grid, hidden.
Why this matters in React: Every component’s outermost element gets a display mode (often flex or grid). Knowing how each one flows prevents the universal beginner bug of “why isn’t my element next to the other one?“
4. Flexbox — One-Dimensional Layout
Flexbox is the layout system you’ll use for almost every component-internal layout. The container gets display: flex; its direct children become flex items.
<div class="row">
<div>A</div>
<div>B</div>
<div>C</div>
</div>
.row {
display: flex;
gap: 12px; /* space between children */
justify-content: space-between; /* horizontal distribution */
align-items: center; /* vertical alignment */
}
Container properties
flex-direction—row(default) orcolumn. Sets the main axis.justify-content— alignment along the main axis:flex-start,flex-end,center,space-between,space-around,space-evenly.align-items— alignment along the cross axis:stretch(default),flex-start,flex-end,center,baseline.flex-wrap—nowrap(default) orwrap(lets items flow onto new lines when they overflow).gap— space between items (modern replacement for awkward sibling margins).
Item properties
flex: 1— grow to fill available space.flex: 0 0 200px— fixed width, don’t grow or shrink.align-self— overridealign-itemsfor a single item.
Common patterns
Horizontal toolbar with even spacing:
.toolbar {
display: flex;
gap: 8px;
}
Page-level vertical stack with main taking remaining space:
.page {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.page > main {
flex: 1; /* main fills the gap between header and footer */
}
Card with text on the left and a button pinned to the right:
.card {
display: flex;
justify-content: space-between;
align-items: center;
}
Tailwind: flex, flex-col, gap-2, justify-between, items-center, flex-1. Every Tailwind project is mostly flex utilities.
Why this matters in React: Every header bar, every card row, every form layout, every nav menu — flexbox. By Week 2 you’ll write flex items-center justify-between gap-4 reflexively.
5. Grid — Two-Dimensional Layout
Where flexbox handles one row or one column, CSS Grid lays out items in a two-dimensional grid.
<div class="grid">
<div>A</div> <div>B</div> <div>C</div> <div>D</div>
</div>
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3 equal-width columns */
gap: 16px;
}
The fr unit is “fraction of remaining space” — 1fr 2fr 1fr makes the middle column twice as wide as the sides.
Common patterns
Sidebar + main content:
.app {
display: grid;
grid-template-columns: 240px 1fr;
min-height: 100vh;
}
Responsive card grid that automatically reflows on smaller screens — no media queries needed:
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
}
That auto-fill + minmax() pattern is a workhorse: each card stays at least 280px wide and the browser packs as many per row as fit.
Tailwind: grid, grid-cols-3, gap-4, and grid-cols-[240px_1fr] for arbitrary tracks.
Why this matters in React: Page-level layouts (sidebar + main, dashboard tiles, photo galleries) use Grid; component-internal layouts use Flex. Mixing both on the same page is normal and expected.
6. Positioning
The position property takes an element out of normal flow and lets you place it precisely.
.tooltip {
position: absolute;
top: 0;
left: 100%;
}
The five values:
static(default) — normal flow, ignorestop/left/etc.relative— normal flow, buttop/left/right/bottomshift the element from where it would have been. Also establishes the containing block for anyabsolutechildren inside it.absolute— removed from flow; positioned relative to the nearest positioned ancestor (any ancestor withpositionother thanstatic). Use for tooltips, dropdowns, badges-on-icons.fixed— removed from flow; positioned relative to the viewport. Use for sticky headers, modal overlays.sticky— flows normally until it would scroll out of view, then sticks. Useful for table headers and sidebar section labels.
The classic pattern is relative parent + absolute child — the child stays anchored to a spot inside the parent:
.avatar { position: relative; }
.avatar .badge {
position: absolute;
top: -4px;
right: -4px;
}
z-index controls stacking order when boxes overlap. Higher number = on top. Only works on positioned elements. Common values: 0 (default), 10 (dropdowns), 100 (modals), 1000 (toasts).
Tailwind: relative, absolute, fixed, sticky, top-0, right-2, z-10.
Why this matters in React: Modals, dropdowns, tooltips, image overlays, toast notifications — they all rely on positioning. The relative parent + absolute child pattern is the foundation of half of them.
7. Colors, Typography, and Spacing
Colors
.card {
color: #1a1a1a; /* text */
background-color: #ffffff; /* fill */
}
Color formats:
- Hex:
#0c1e3dor shorthand#fff - RGB / RGBA:
rgb(12 30 61)/rgba(12 30 61 / 0.5)for alpha transparency - HSL:
hsl(220 67% 14%)— easier to pick related shades (change lightness, keep hue)
Typography
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 16px;
line-height: 1.5;
color: #1a1a1a;
}
h1 {
font-size: 2.5rem; /* 40px when root is 16px */
font-weight: 600;
letter-spacing: -0.015em;
}
Units — when to use what
px— fixed pixels. Use for borders and other tiny, fixed details.rem— multiple of root font size (16px default). Use for almost everything else — fonts, padding, margin. Respects user font-size preferences.em— multiple of current element’s font size. Use sparingly; cascades unpredictably.%— percentage of the parent’s corresponding dimension. Use for layout widths.vw/vh— viewport width/height percentages. Use for full-screen layouts (min-height: 100vh).
The convention: rem for fonts and spacing, px for borders and tiny details, % and fr for layout.
Spacing scale
Pick a consistent scale. Tailwind defaults to multiples of 4px: 4, 8, 12, 16, 24, 32, 48, 64. Use it for padding, margin, gap. Don’t invent arbitrary values like 13px or 27px — your UI will look amateur.
Tailwind: text-base, font-semibold, text-gray-900, bg-white, p-4, m-6.
Why this matters in React: Design tokens — a fixed color palette and a fixed spacing scale — are what make a UI look professional vs. amateur. Stick to your scale; resist the urge to invent values.
8. Responsive Design
Modern web apps work on any screen size. The convention is mobile-first: write the baseline styles for small screens, then add @media rules to adjust for larger ones.
/* Mobile (default): single column */
.cards {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
/* Tablet (≥768px): two columns */
@media (min-width: 768px) {
.cards {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop (≥1024px): three columns */
@media (min-width: 1024px) {
.cards {
grid-template-columns: repeat(3, 1fr);
}
}
Common breakpoints (Tailwind’s defaults — adopt them unless you have a reason not to):
| Name | Min-width |
|---|---|
sm | 640px |
md | 768px |
lg | 1024px |
xl | 1280px |
2xl | 1536px |
Don’t forget the viewport meta tag in your HTML <head> (Next.js adds this automatically):
<meta name="viewport" content="width=device-width, initial-scale=1" />
Without it, mobile browsers render your page at desktop width and zoom out — your layout will look tiny and broken on a phone.
Tailwind: grid-cols-1 md:grid-cols-2 lg:grid-cols-3 — read as “one column by default, two at md and up, three at lg and up.” Three media queries in one line.
Why this matters in React: Your capstone has to look reasonable on a phone (judges scan from Demo Day audience seats). Use the DevTools mobile emulator (Cmd + Shift + M) to test as you build, not as a final-day polish.
9. The Cascade and CSS Variables
The cascade
When two CSS rules target the same element with the same specificity, the later one wins:
.btn { background: blue; }
.btn { background: red; } /* wins — button is now red */
When specificities differ, higher specificity wins (see §2). Combined with the !important hammer (don’t use it) and inline styles, this is “the cascade.”
CSS variables (custom properties)
Declare a variable with --name, use it with var(--name):
:root {
--color-primary: #0c1e3d;
--color-accent: #21a79b;
--space-md: 1rem;
}
.btn {
background: var(--color-primary);
padding: var(--space-md);
}
.btn:hover {
background: var(--color-accent);
}
Variables enable theming (dark mode, brand customization) and design tokens:
body[data-theme="dark"] {
--color-primary: #ffffff;
--color-accent: #21a79b;
}
One variable change cascades through every rule that uses it.
Tailwind: Tailwind’s theme config (tailwind.config.ts) compiles to CSS variables. You rarely write var() by hand in a Tailwind project — but you’ll read CSS variables in shadcn/ui components and theme files, and you’ll occasionally override them for theming.
Why this matters in React: Theming, dark mode, brand customization — all CSS variables. Most modern component libraries (shadcn/ui, Radix) ship with a defined variable palette you can override for your capstone’s brand.
Practice exercises
For each, open any web page in Chrome → right-click → Inspect → Elements → edit the Styles pane on the right. Or paste the CSS into a quick style.css file paired with a small index.html.
/* 1. Box-model fix: what property + value makes width
include padding + border? */
* {
/* ??? */
}
/* 2. Center a div both vertically and horizontally with flexbox.
Two properties on the parent: */
.parent {
display: flex;
/* ??? */
/* ??? */
}
/* 3. Three equal columns with Grid — one property to add: */
.cards {
display: grid;
/* ??? */
}
/* 4. Tooltip pinned to the top-right corner of its parent.
What's the position value on each, plus the offset properties? */
.avatar { /* ??? */ }
.badge {
/* ??? */
/* ??? */
/* ??? */
}
/* 5. Stack vertically on mobile, horizontally on desktop (≥768px).
Fill in the media query: */
.row {
display: flex;
flex-direction: column;
}
@media /* ??? */ {
.row {
flex-direction: row;
}
}
Cheat sheet — keep this open while you code
| Concept | CSS | Tailwind |
|---|---|---|
| Box-sizing | box-sizing: border-box; | (default) |
| Padding all sides | padding: 16px; | p-4 |
| Padding axis | padding: 12px 16px; | py-3 px-4 |
| Margin center | margin: 0 auto; | mx-auto |
| Border | border: 1px solid #ccc; | border border-gray-300 |
| Display: flex | display: flex; | flex |
| Flex direction | flex-direction: column; | flex-col |
| Justify content | justify-content: center; | justify-center |
| Align items | align-items: center; | items-center |
| Gap | gap: 16px; | gap-4 |
| Grid columns | grid-template-columns: repeat(3, 1fr); | grid-cols-3 |
| Responsive grid | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); | (use a plugin / arbitrary) |
| Position absolute | position: absolute; top: 0; right: 0; | absolute top-0 right-0 |
| Hover | .btn:hover { ... } | hover:... |
| Hide | display: none; | hidden |
| Media query (md+) | @media (min-width: 768px) { ... } | md:... |
| CSS variable | var(--name) | bg-[var(--brand)] |
What’s next
You’ve now seen the CSS surface you need to read existing styles, write new ones, and understand what every Tailwind utility class actually does under the hood. You’ll write Tailwind classes daily from Week 1 onward — and every one of them maps to a CSS property in this reference.
When you’re ready, post in #wins on Discord: “Module 0.5 complete.” See you in the next module.