What You're Building
You are going to style the HTML portfolio page you built in the HTML Capstone Project. The HTML structure is already complete — your job is to write the CSS that transforms it from unstyled HTML into a polished, dark-themed, responsive, animated developer portfolio.
Every CSS chapter is represented. Work through the 8 steps in order — each step builds on the previous one. By the end you will have a real portfolio you can actually use.
CSS Reset
Design Tokens
Typography
Flexbox Nav
CSS Grid
Positioning
Responsive
Dark Mode
Transitions
Animations
BEM Architecture
CSS Variables
📖 Project Scenario
Your Brief
You are the CSS developer on Obed Angel's personal portfolio. The HTML has been written by the markup developer (you, in the HTML course). Now it is your job to style it. Obed wants a dark, professional aesthetic — think devtools meets design agency. He wants it to feel fast, polished, and responsive on any device. The design language should feel consistent throughout — same spacing rhythm, same colour tokens, same animation personality.
Think of yourself as a CSS engineer. Your output should be production quality — not just "it looks okay", but the kind of CSS that a professional team would be proud to ship.
Step-by-Step Instructions
Step 01 of 08 CH 12CH 10CH 02
Foundation: Reset, Tokens & Base Styles
Open the Playground CSS tab. This is the very first thing you write — before any component styles. Get the foundation right and everything else becomes simpler.
- Write the modern CSS reset from memory: box-sizing, scroll-behavior, line-height, media display:block, form font:inherit
- Define a complete :root token system: colours (bg, surface, card, border, accent, text, muted), spacing scale (xs → xl), radius, shadow, and font family variables
- Set body styles using only tokens: background, color, font-family, font-size, line-height, -webkit-font-smoothing
- Load Google Fonts in the CSS tab: Syne for headings, DM Sans for body, JetBrains Mono for code
/* Step 1: Foundation */
:root {
--bg: #0a0a0f;
--surface: #111118;
--accent: #7c6af7;
--accent2: #f7706a;
--accent3: #5af7c0;
--text: #e8e8f0;
--muted: #888899;
--space-md: 16px;
--space-lg: 32px;
--radius: 8px;
--shadow: 0 8px 32px rgba(0,0,0,.4);
--font-body: 'DM Sans', sans-serif;
}
✓ Preview tab: dark background, body text visible, base styles applied — zero component CSS yet
Step 02 of 08 CH 03CH 01
Typography System
Style all text elements — headings, paragraphs, labels, code snippets. Use clamp() for all headings. Apply gradient text to the hero h1.
- h1: clamp(32px, 5vw, 72px) · Syne font · weight 800 · line-height 1.05 · gradient text effect
- h2: clamp(24px, 4vw, 40px) · Syne · weight 700 · line-height 1.2
- h3: clamp(18px, 2.5vw, 24px) · Syne · weight 600
- p: font-size 1rem · line-height 1.8 · color var(--muted)
- Monospace labels: JetBrains Mono · 0.6875rem · letter-spacing 3px · text-transform uppercase
- a: no underline · inherit colour · :hover changes to var(--accent)
- a:focus-visible: visible outline — never suppressed
✓ Preview: heading hierarchy clearly visible · hero h1 shows gradient · all text uses correct tokens
Step 03 of 08 CH 04CH 06
Navigation: Flexbox + Sticky + Animated Underline
Style the sticky navigation. It must stick to the top, stay above page content, and have animated underlines on links.
- nav: position: sticky; top: 0; z-index: 100 · background var(--surface) · border-bottom
- Inner nav container: display: flex; align-items: center; justify-content: space-between · height 64px · padding horizontal
- Logo link: Syne font · bold · var(--accent) colour
- Nav links: flexbox row · gap · no underline by default
- Animated underline: ::after pseudo-element · width: 0 → width: 100% on :hover · transition
- Active link: colour var(--accent) · underline visible by default
✓ Scroll the page — nav sticks at top · hover links show growing underline
Step 04 of 08 CH 04CH 09
Hero Section: Layout + Entrance Animations
Style the hero with flexbox centring and load-time animations. Every visible element in the hero should animate in on page load.
- Hero: min-height: 100vh · display flex · flex-direction column · justify-content center · padding
- Gradient text on h1: background linear-gradient · -webkit-background-clip: text · -webkit-text-fill-color: transparent
- Role subtitle: monospace font · var(--accent3) colour · letter-spacing
- CTA buttons: primary (var(--accent) background), secondary (transparent, border only) · border-radius · padding · flex row
- Entrance animation: @keyframes fadeUp (opacity 0 + translateY 24px → opacity 1 + translateY 0)
- Apply with staggered delays: h1 at 0s, subtitle at 0.15s, description at 0.3s, buttons at 0.45s
✓ On page load: hero content fades up in sequence · gradient is on h1 · buttons have correct styles
Step 05 of 08 CH 02CH 05CH 06CH 08
Skills & Projects: Grid Layout + Card Interactions
Build the two most complex sections. Skills uses a flex wrap layout with animated badges. Projects uses CSS Grid with card interactions.
- Skills badges: inline-flex · padding · border-radius pill · background/border using var() · fadeUp stagger entrance
- Projects section heading: h2 with bottom border accent line
- Projects grid: display: grid; repeat(auto-fit, minmax(300px, 1fr)); gap: 20px
- Project card: background var(--card) · border · border-radius · overflow: hidden · position: relative
- Card image: width 100% · height 200px · object-fit: cover · transition: transform 0.4s ease
- Card hover: translateY(-6px) + box-shadow on card · scale(1.05) on image
- PRO badge: position: absolute; top: 12px; right: 12px
- Card body: padding · h3 title · p description · tech tags row · GitHub link
✓ Project cards sit in responsive grid · hover lifts card + zooms image · badge in top-right corner
Step 06 of 08 CH 07CH 11
Contact Form: Styles + Validation States
Style the contact form inputs. Use pseudo-classes for visual validation feedback — no JavaScript required.
- Form inputs: full width · background var(--card) · border var(--border) · border-radius · padding · color var(--text)
- input:focus: outline: none · border-color var(--accent) · box-shadow accent glow
- input:focus-visible: outline always present for keyboard users
- input:valid: border-color var(--accent3) green
- input:invalid:not(:placeholder-shown): border-color var(--accent2) red
- ::placeholder: color var(--muted) · font-style italic
- Submit button: full width on mobile · var(--accent) background · transition: transform + box-shadow · :hover lift · :active press
✓ Inputs show purple glow on focus · green border on valid email · red on invalid · submit lifts on hover
Step 07 of 08 CH 07CH 10
Responsive Design + Dark Mode
Make the entire portfolio mobile-first responsive and add automatic dark mode support via CSS variables.
- Verify: all base styles written mobile-first (no overrides, only additions)
- @media (min-width: 768px): nav switches from stacked to horizontal · sidebar layout in about section · 2-column grid for projects
- @media (min-width: 1200px): max-width container centred · 3-column projects grid · larger hero padding
- Nav mobile: hide desktop links · show hamburger icon (CSS-only with :checked technique or just a flex column)
- Light mode override: @media (prefers-color-scheme: light) — swap --bg, --surface, --card, --text, --muted tokens only. Primary accent stays purple.
- @media (prefers-reduced-motion: reduce): all animation-duration to 0.01ms
✓ Resize browser: layout adapts at 768px and 1200px · light mode works on system preference change · animations disabled for reduced-motion users
Step 08 of 08 CH 12CH 01
Architecture Polish & Final Audit
Finalise the CSS with professional architecture practices and audit every rule before submission.
- Add @layer reset, base, components, utilities at the top. Move rules into appropriate layers.
- Audit: confirm zero magic numbers — every value is a token or a derived calc()
- Audit: confirm zero !important declarations
- Audit: confirm every interactive element has a :focus-visible outline
- Rename all classes to BEM naming: .nav__link, .card__title, .card--featured, .btn--primary
- Add the .sr-only utility for any visually hidden accessible text
- Run the CSS through the browser DevTools Audit panel — fix any flagged issues
✓ Zero !important · Zero magic numbers · BEM class names · @layer organisation · Focus visible on all interactive elements
🚀 Submitting Your Project
When you are happy with the result, submit both your HTML and CSS files for review. Your instructor will check the visual output, code quality, and architecture.
- Save your CSS — use the Playground's Save button or copy to your computer as
styles.css
- Update your HTML portfolio — make sure the <link rel="stylesheet" href="styles.css"> is in the <head>
- Push to GitHub — commit both index.html and styles.css to your html-capstone-project repository
- Check GitHub Pages — confirm the live site at your GitHub Pages URL shows the styled version
- Submit the GitHub Pages URL — the reviewer will see your live styled portfolio