CSS Overview & Syntax
Understanding what CSS is, how it works, and how to connect it to HTML
CSS stands for Cascading Style Sheets. It is a language used to describe the visual presentation of HTML documents — colours, fonts, spacing, layout, animations, and more. While HTML defines the structure and content of a webpage, CSS controls how that content looks.
The word "Cascading" is crucial. It means that multiple style rules can apply to the same element, and there is a defined order of priority (the cascade) that decides which rule wins. This is the "Theory of Proximity" — the closer a style rule is to the element, the more priority it generally has.
- Separation of concerns — HTML handles content; CSS handles appearance. This makes code easier to maintain.
- Consistency — One CSS rule can style hundreds of elements at once across an entire website.
- Reusability — A single external CSS file can be shared across all pages of a website.
- Accessibility — CSS controls font sizes, colour contrast, and layout for different screen sizes.
- Performance — The browser downloads and caches the CSS file once, speeding up all subsequent pages.
Every CSS rule is made up of three parts: a selector, a property, and a value. Together they form a declaration block.
CSS can be written in three different places. Understanding the difference helps you choose the right approach for each situation.
1. Inline CSS — style attribute
Written directly on an HTML element using the style attribute. Highest specificity but hardest to maintain.
2. Internal CSS — <style> tag
Written inside a <style> tag in the <head>. Good for single-page projects or page-specific overrides.
When multiple CSS rules target the same element, the browser must decide which one wins. This decision follows a strict order of priority — often called the Theory of Proximity or the CSS Cascade:
Priority order (highest to lowest):
Inline style → Internal style → External stylesheet → Browser default
Additionally, within the same level, a rule written later in the file overrides one written earlier. This is why the order of your CSS rules matters.
✏️ Day 6 Exercise
- Every student should be able to state the Theory of Proximity (CSS cascade order) in their own words with an example.
- Create an HTML page that demonstrates all three ways to add CSS — inline, internal, and external — on the same page, showing which one wins.
CSS Text & Font Styling
Controlling how text looks — direction, spacing, alignment, decorations, and fonts
| Property | Values & Example | What it does |
|---|---|---|
| direction | ltr | rtl | Sets text direction — left-to-right or right-to-left (for Arabic, Hebrew etc.) |
| letter-spacing | 2px | -1px | normal | Space between individual characters |
| word-spacing | 5px | normal | Space between words |
| text-indent | 30px | 2em | Indents the first line of a paragraph |
| text-align | left | right | center | justify | Horizontal alignment of text within its block |
| text-decoration | none | underline | overline | line-through | Adds or removes lines on text |
| text-transform | uppercase | lowercase | capitalize | none | Changes letter casing without editing HTML |
| text-shadow | 2px 2px 4px #000 | Adds shadow behind text (X offset, Y offset, blur, colour) |
| color | red | #ff0000 | rgb(255,0,0) | Sets the foreground (text) colour |
CSS supports many formats for specifying colour. The most common are names, hexadecimal, and RGB/RGBA.
| Property | Values | What it does |
|---|---|---|
| font-family | 'Arial', sans-serif | Sets the typeface. Always provide a fallback (generic family like sans-serif) |
| font-style | normal | italic | oblique | Makes text italic |
| font-variant | normal | small-caps | Displays text in small capitals |
| font-weight | normal | bold | 100–900 | Controls thickness. 400=normal, 700=bold |
| font-size | 16px | 1em | 1rem | 2vw | 120% | Size of the text |
Choosing the right unit is one of the most important decisions in CSS. Units divide into two categories: absolute (fixed, real-world size) and relative (scale based on something else).
| Unit | Type | Meaning | Best Used For |
|---|---|---|---|
| px | Absolute | One screen pixel. Always the same size. | Borders, shadows, exact dimensions |
| % | Relative | Percentage of the parent element's size | Fluid layouts, widths |
| em | Relative | Relative to the current element's font-size. 2em = 2× current font | Spacing, padding that scales with text |
| rem | Relative | Relative to the ROOT element's font-size (usually 16px). Consistent & predictable. | Font sizes, consistent spacing |
| vw | Relative | 1% of the viewport (screen) WIDTH | Full-width sections, hero text |
| vh | Relative | 1% of the viewport HEIGHT | Full-screen sections |
| cm / in | Absolute | Physical centimetres / inches | Print stylesheets only |
1rem = 16px, 1.5rem = 24px, 2rem = 32px. When a user increases their browser font size for accessibility, rem-based layouts scale correctly — px-based ones don't.✏️ Day 7 Exercise
- Create a formal letter format page using proper text alignment, text-indent for paragraphs, font-family choices, and correct use of spacing properties.
- Experiment: set the root font-size to 10px on the body, then use rem units for all font sizes — so 1.6rem = 16px, 2.4rem = 24px. This makes rem arithmetic easier.
The CSS Box Model
Background, border, padding, margin, outline, box-sizing, box-shadow, and border-radius
Every HTML element is a rectangular box. The CSS Box Model describes the four layers that make up every box, from inside out:
Content → Padding → Border → Margin
Content — the actual text or image. Sized by width and height.
Padding — transparent space INSIDE the border, between content and border.
Border — the visible line surrounding the padding and content.
Margin — transparent space OUTSIDE the border, separating this element from others.
| Property | Values | Description |
|---|---|---|
| background-color | red | #fff | rgba(0,0,0,0.5) | Fills the element's background with a solid colour or transparent colour |
| background-image | url('img.jpg') | none | Sets an image as the background |
| background-repeat | repeat | no-repeat | repeat-x | repeat-y | Controls whether/how the background image tiles |
| background-attachment | scroll | fixed | local | fixed creates a parallax-like effect where the image stays still as you scroll |
| background-position | center | top left | 50% 50% | Where to position the background image within the element |
| background-size | auto | cover | contain | 100px 200px | cover fills the element (may crop). contain fits the whole image (may leave gaps). |
By default, width and height only size the content area. Padding and border are added on top, making elements larger than expected. This is called content-box behaviour and causes constant confusion.
box-sizing: border-box changes this so that width includes the padding and border. This is what virtually every professional uses.
✏️ Day 8 Exercise
- Create a frame for an image using all box model properties — border, padding, box-shadow (with inset shadow) and border-radius.
- Create a transparent form using RGBA background colours and outline styling.
Glassmorphism, CSS Dimensions & Images
Mastering image display, filters, dimensions, and the popular glass-card UI effect
Glassmorphism is a UI design trend that creates a frosted-glass appearance — transparent or semi-transparent surfaces with a blur effect on the background behind them. It relies on three key CSS properties: background with RGBA, backdrop-filter: blur(), and a subtle border.
The filter property applies visual effects to elements — blur, brightness, contrast, colour shifts, and more. These work on images, divs, or any HTML element.
| Filter Function | Unit | Effect |
|---|---|---|
| blur(px) | px | Applies Gaussian blur. blur(5px) |
| brightness(%) | 0–1 or % | 0 = black, 1 = normal, 2 = double brightness |
| contrast(%) | % | 0% = grey, 100% = normal, 200% = high contrast |
| invert(%) | % | 100% = completely inverted (like a negative photo) |
| hue-rotate(deg) | deg | Rotates colours around the colour wheel |
| saturate(%) | % | 0% = greyscale, 100% = normal, 200% = very vivid |
| sepia(%) | % | Adds warm brown vintage tone. 100% = full sepia |
| opacity(%) | % | 100% = fully visible, 0% = fully transparent |
| grayscale(%) | % | 100% = black and white image |
| drop-shadow() | x y blur colour | Like box-shadow but follows the image outline (not the box) |
✏️ Day 9 Exercise
- Fade an image using RGBA — overlay a semi-transparent colour on top of an image using a pseudo-element or stacked backgrounds.
- Build a glassmorphism card with a gradient background, a blurred glass panel on top, containing text and an icon.
CSS Links, Lists, Tables & Forms
Styling interactive elements for a polished, usable interface
Links have four states that must be styled in a specific order to work correctly: Link → Visited → Hover → Active. Writing them in the wrong order causes some states to be overridden and never visible.
✏️ Day 10 Exercise
- Create a Ludo Game Design using CSS grid, borders, and colours.
- Build a styled contact form with focus states, placeholder styling, and a styled submit button.
Div, Class, ID, Positioning & Float
The building blocks of CSS layout — understanding how elements flow and stack
<div> — a generic block-level container. It creates a new line before and after itself. Used to group other elements for styling or layout.
<span> — a generic inline container. It does not create a new line. Used to style a word or part of a sentence without disrupting the text flow.
class — a reusable label. Multiple elements can share the same class. Targeted with .className in CSS.
id — a unique identifier. Only ONE element per page should have a given id. Targeted with #idName in CSS. Has higher specificity than class.
| Value | Behaviour | Common Use |
|---|---|---|
| static | Default. Element follows the normal document flow. top/left/right/bottom have no effect. | Default for all elements |
| relative | Element stays in the document flow but can be nudged with top/left/right/bottom relative to where it would normally sit. Creates a positioning context for children. | Small adjustments; parent for absolute children |
| absolute | Removed from document flow. Positioned relative to the nearest ancestor with position: relative (or the page if none). Other elements ignore it. | Tooltips, badges, overlays, dropdowns |
| fixed | Removed from document flow. Always positioned relative to the browser viewport. Stays in place when you scroll. | Sticky headers, nav bars, cookie banners, chat buttons |
| sticky | Behaves like relative until the user scrolls past a threshold, then acts like fixed. Returns to relative when the parent scrolls away. | Section headers that stick while reading, table headers |
When positioned elements overlap, z-index controls which one appears on top. Higher values appear in front. It only works on elements with position other than static.
✏️ Day 11 Exercise
- Create a page with an image placed at each of the four corners using
position: absoluteon aposition: relativeparent. - Create a bull's-eye target using
border-radius: 50%andposition: absolutewith centring. - Create the form provided by the instructor using proper positioning techniques.
Icons & CSS Layout — Display Property
Using icon libraries and mastering Flexbox and Grid for modern layouts
Icon libraries provide scalable vector icons as CSS classes. They load a font file where each character maps to an icon shape. You add one <link> to your HTML head and then use <i> tags with specific class names.
| Value | Behaviour |
|---|---|
| block | Takes the full available width. Creates a new line before and after. Width/height/margin work fully. |
| inline | Only as wide as its content. No new lines. Width and height are ignored. Vertical margin/padding mostly ignored. |
| inline-block | Sits inline with text but respects width/height. Best of both worlds. |
| none | Element is removed from the page completely — invisible and takes up no space. |
| flex | One-dimensional layout. Aligns children in a row or column with powerful alignment tools. |
| grid | Two-dimensional layout. Defines rows AND columns simultaneously. |
Flexbox makes it trivially easy to centre content, distribute items with equal spacing, and create responsive row/column layouts. It operates on two axes: the main axis (set by flex-direction) and the cross axis (perpendicular to it).
✏️ Day 12 Exercise
- Create a navigation bar using Flexbox with a logo on the left, nav links in the centre, and action buttons on the right.
- Create a full-page layout using CSS Grid with a header, two-column body (sidebar + content), and footer.
Visibility, Cursors, Overflow & CSS Selectors
Fine-grained display control and the full power of CSS selector syntax
| Selector | Syntax | What it targets |
|---|---|---|
| Universal | * | Every single element on the page |
| Type | p, h1, div | All elements of that HTML tag |
| Class | .className | All elements with that class attribute |
| ID | #idName | The one element with that id attribute |
| Grouping | h1, h2, p | Multiple selectors sharing the same rules |
| Descendant | .nav a | All <a> anywhere inside .nav (any depth) |
| Child | .nav > a | Only DIRECT <a> children of .nav |
| Adjacent Sibling | h1 + p | Only the first <p> immediately after an h1 |
| General Sibling | h1 ~ p | All <p> siblings that come after h1 |
| Attribute | input[type="email"] | Elements with a specific attribute value |
✏️ Day 13 Exercise
- Create a simple image carousel using only HTML and CSS (using
:checkedand adjacent sibling selectors). - Create a Draught (Checkers) Game board design using CSS Grid and
:nth-childfor the alternating squares. - Create a Testimonial section and a Landing Page as provided.
Dropdown Menus & CSS Animation
Building interactive dropdowns and controlling element movement with @keyframes
CSS Animations allow elements to change from one set of styles to another over time, without JavaScript. You define the animation with @keyframes, then apply it to an element with the animation property.
animation-fill-mode: forwards means the element keeps the final keyframe styles after the animation ends. Without it, the element snaps back to its original style.✏️ Day 14 Exercise
- Add a dropdown menu to the navbar built in Day 12, with a smooth fade/slide animation.
- Create an animated car that drives from one side of the screen to the other and back — a to-and-fro movement — using
@keyframesandanimation-direction: alternate.
Transition, Transformation & Gradients
Smooth state changes, visual warping, colour gradients, and browser compatibility
Transitions create a smooth change from one CSS state to another — like a button changing colour when hovered. Unlike animations, transitions only happen when a property value changes (usually triggered by :hover, :focus, or a class toggle via JavaScript). You define how long the change takes and how it accelerates.
Older browsers sometimes required a vendor prefix before they supported standard CSS properties. While mostly unnecessary in 2025 for modern browsers, you may still encounter them in legacy codebases or when supporting older mobile browsers.
✏️ Day 15 Exercise
- Create a rotating image — an image that spins continuously using
@keyframesand therotate()transform. - Fill a box using
linear-gradient— experiment with direction, multiple stops, and hard stop lines.
Media Queries, CSS Rule Override, Nesting & Logical Properties
Building responsive, maintainable, and internationally adaptable CSS
A media query applies CSS conditionally — only when a specified condition about the user's device or environment is true. The most common condition is the screen width, enabling different layouts for phones, tablets, and desktops from a single HTML file.
Every CSS selector has a specificity score. When two rules conflict, the higher score wins — regardless of order. The scoring is: inline style (1-0-0-0) > ID (0-1-0-0) > class/pseudo-class (0-0-1-0) > tag (0-0-0-1).
!important overrides ALL specificity rules. It should be used sparingly — mainly to override third-party library styles you cannot modify.
✏️ Day 16 Exercise
- Take the landing page created in Day 13 and make it fully mobile-responsive using media queries — it should look good at 375px, 768px, and 1280px widths.
- Refactor your CSS to use CSS Custom Properties (var) for all colours and spacing values.
CSS Effects, Filters & Advanced Pseudo Classes
Professional visual effects using pure CSS
::before and ::after create a virtual element as the first or last child of the selected element. They require the content property. The injected content is not in the HTML — it exists only in CSS. They are block-level by default only when given display: block; otherwise they are inline.
CSS Custom Properties — var()
Creating reusable, dynamic CSS variables for maintainable, themeable stylesheets
CSS Custom Properties (also called CSS Variables) allow you to store a value in a named variable and reuse it throughout your stylesheet. If you want to change your brand colour, you change it in one place and every property using that variable updates automatically.
They are defined with two dashes: --variable-name. They are used with the var() function: var(--variable-name). They follow the cascade and can be scoped to any element.
SCSS & CSS Preprocessors
Writing smarter CSS with Sass/SCSS, LESS, Stylus, and PostCSS
A CSS preprocessor is a tool that extends CSS with programming-like features — variables, nesting, functions, loops, conditions — then compiles your enhanced code down to standard CSS that browsers understand. The most popular is Sass/SCSS.
While native CSS has added many of these features (custom properties, nesting), preprocessors still offer power for large projects: file splitting, mixins, loops, and build-time calculations.
| Tool | Variable Syntax | Strengths | Status |
|---|---|---|---|
| Sass/SCSS | $variable | Most features, huge community, excellent tooling, file splitting | ⭐ Most popular |
| LESS | @variable | Similar to SCSS, was used in Bootstrap 3 | Declining |
| Stylus | variable (bare) | Most flexible syntax, optional colons/braces | Niche use |
| PostCSS | — | Plugin-based transformation — Autoprefixer, future CSS, minification | ⭐ Widely used in build tools |
@layer — plain CSS covers most needs. Learn SCSS when joining teams that use it, or for very large multi-file projects.CSS Math Functions — calc(), clamp(), min(), max()
Performing calculations directly in CSS for fluid, responsive layouts
calc() lets you perform mathematical operations (+ - * /) inside a CSS value — and you can mix different units. This is impossible to do without it. The most powerful use is subtracting a fixed amount from a percentage.
clamp(minimum, preferred, maximum) lets a value scale fluidly between a minimum and maximum. It's perfect for fluid typography — text that grows with the viewport without hard breakpoints.
clamp(min, preferred, max) is equivalent to max(min, min(preferred, max)). Use clamp for fluid typography — it replaces dozens of media query breakpoints for font sizes.Modern CSS — What's New & What to Learn Next
Container Queries, @supports, clip-path, aspect-ratio, @layer, scroll-behavior
@supports works like @media but tests whether the browser supports a specific CSS property and value. It lets you write fallback code for browsers that don't support modern features — the same concept as progressive enhancement.
Media queries respond to the viewport width. Container queries respond to the width of the element's container. This means a card component can adapt its layout based on how much space it's given — not the screen size. The same card can be a full row in a wide sidebar and a compact thumbnail when in a narrow column.
.card component no longer needs to know where it lives — it just adapts to whatever space it's given. This is the future of CSS.clip-path crops an element to a custom shape — triangles, circles, hexagons, or any polygon. Content outside the clip path is hidden. It's used for hero sections with diagonal cuts, image shapes, and decorative elements.
@layer lets you explicitly organise your CSS into named layers and define their priority order. Styles in higher-priority layers always win, regardless of specificity. This solves the age-old problem of fighting specificity wars with third-party libraries.
@layer is what frameworks like Tailwind CSS and modern design systems use under the hood. Understanding it gives you control over even the most complex CSS architectures.Suggested Learning Path & Topics Added
A summary of new additions and recommended study order
| Topic Added | Where It Fits | Why It Matters |
|---|---|---|
| CSS Custom Properties (var) | After Day 16 | Essential for maintainable, themeable CSS. Used in every professional project. |
| SCSS / Preprocessors | After Day 16 | Used widely in team/company codebases. Required knowledge for most frontend jobs. |
| calc() | Day 8 / Day 15 | Mixing units — e.g. calc(100% - 260px) — is impossible without it. |
| clamp(), min(), max() | Day 15 / Day 16 | Fluid responsive typography without breakpoints. Modern best practice. |
| @supports | Day 16 | Progressive enhancement — graceful fallbacks for unsupported features. |
| Container Queries (@container) | After Day 16 | The evolution of media queries. Component-level responsiveness. |
| clip-path | Day 17 | Creating non-rectangular element shapes. Widely used in modern UI. |
| aspect-ratio | Day 9 / Day 12 | Replaces the old padding-hack for maintaining image/video proportions. |
| scroll-behavior + scroll-padding-top | Day 13 / Day 16 | Smooth anchor scrolling with sticky header offset. Two lines of CSS. |
| @layer | Day 16 Advanced | Explicit cascade management. Used in Tailwind, Bootstrap 5+ and all modern frameworks. |
| CSS Math in specificity | Day 16 | :is(), :where(), :has() — powerful modern selectors that simplify complex rules. |
- CSS :is(), :where(), :has() — Modern pseudo-classes that simplify grouping selectors and enable parent selection (e.g.
.card:has(img)targets a card that contains an image). - CSS Grid — Named Areas — Using
grid-template-areasto define layouts with human-readable names like "header", "sidebar", "main", "footer". - CSS Scroll-snap — Already mentioned in overflow; worthy of deeper coverage as a full carousel/slider technique.
- CSS Custom Fonts with @font-face — Loading self-hosted fonts, not just Google Fonts. Important for performance and offline projects.
- CSS Variables + JavaScript — Reading and writing CSS custom properties from JavaScript for dynamic theming.
- Accessibility in CSS —
prefers-reduced-motionmedia query, focus-visible, sufficient colour contrast, and skip links. - CSS Subgrid — Allows grid items to participate in the parent grid's rows/columns — solves complex alignment problems in nested grids.
- Print Stylesheets — A dedicated day on
@media print— hiding nav, adjusting fonts,page-break-before/after. - CSS Performance — Understanding reflow vs repaint, GPU-composited properties (transform, opacity), and the
will-changehint. - Introduction to CSS Frameworks — A survey day comparing Bootstrap, Tailwind CSS, and Bulma — when to use a framework vs writing custom CSS.
✏️ Final Capstone Project
- Build a fully responsive personal portfolio website from scratch using only HTML and CSS — no frameworks.
- Requirements: sticky navigation with dropdown, hero section with glassmorphism card, skills section using Grid, project cards with hover effects, contact form with validation styling, CSS custom properties for all colours, and a dark mode toggle using
prefers-color-scheme. - Bonus: Refactor the CSS into SCSS with variables, mixins, and file partials. Compare the before/after and present your findings.
Tailwind Foundations, Utility Thinking & Setup
Understand Tailwind's utility-first model, when it helps, and how to start with the CDN or a proper build setup.
Tailwind CSS is a utility-first framework. Instead of creating a custom class and then writing CSS for it, you compose small single-purpose classes directly in HTML. This removes a large part of the constant context-switching between HTML and CSS files.
It does not replace your CSS knowledge. It compresses that knowledge into a naming system. If you already understand spacing, colour, layout, states, and responsiveness, Tailwind becomes very fast to read and write.
1. CDN for learning
Add the Tailwind CDN script to an HTML file and start typing utility classes immediately. Best for practice, demos, and classwork.
2. Build process for real projects
Use npm and a Tailwind config file so unused classes are removed in production and your design tokens are controlled properly.
✏️ Day 11 Exercise
- Create a blank HTML file and add Tailwind via CDN.
- Build a profile card with a heading, paragraph, button, and shadow using only utility classes.
- Write a short comparison explaining when you would choose raw CSS and when you would choose Tailwind.
Utilities, Layout, Spacing & Responsive Design
Learn the utility vocabulary for text, colour, spacing, flexbox, grid, and breakpoint-driven layout changes.
| Tailwind Class | Meaning | Use |
|---|---|---|
| text-3xl | Large text size | Headings and section titles |
| font-semibold | Weight 600 | Buttons, labels, headings |
| bg-slate-900 | Dark background | Hero sections, dark cards |
| p-6 / px-6 / py-3 | Padding utilities | Internal spacing |
| rounded-xl | Large radius | Cards and panels |
| shadow-lg | Box shadow | Elevation and depth |
| flex / items-center / justify-between | Flexbox utilities | One-dimensional layout |
| grid / grid-cols-3 / gap-6 | Grid utilities | Cards, dashboards, galleries |
| md:grid-cols-2 | Breakpoint modifier | Tablet and desktop layouts |
| hidden md:flex | Visibility by screen size | Desktop nav, responsive menus |
Tailwind is mobile-first. Classes without prefixes apply everywhere. A prefix like md: means “apply this from 768px and above”. You solve the mobile layout first, then enhance the design as screen space increases.
✏️ Day 12 Exercise
- Build a 4-card feature grid that stacks on mobile, becomes 2 columns on tablets, and 4 columns on desktop.
- Create a responsive navigation shell with a desktop link row and a mobile hamburger button.
- Recreate a pricing section using only utility classes.
Interactive States, Dark Mode, Motion & Hover Patterns
Use Tailwind for hover, focus, active, group interactions, animations, and dark mode-ready interfaces.
State variants are prefixes such as hover:, focus:, active:, disabled:, and group-hover:. They let you create rich interactions without writing custom CSS selectors.
Tailwind supports dark mode with the dark: prefix. In real projects, most teams use a class-based strategy so the user can toggle themes and save that preference.
- transition, transition-colors, transition-transform control what animates.
- duration-150, duration-300, duration-500 control timing.
- hover:scale-105, hover:-translate-y-1, rotate-180 create motion without manual keyframes.
- animate-spin, animate-pulse, animate-bounce cover the most common built-in animation needs.
✏️ Day 13 Exercise
- Create a button system with hover, active, focus-visible, and disabled states.
- Add dark mode styling to a card, form, and page background.
- Build a small dashboard panel that uses group hover and a subtle lift animation.
Flowbite Components, Config, JIT & Production Best Practices
Move from raw utilities to reusable components and production-ready Tailwind workflows.
Flowbite is a Tailwind-based component library. It gives you prebuilt navbars, modals, dropdowns, tables, drawers, and forms without forcing a totally different styling model. You can still edit classes directly because the components are made with standard Tailwind utilities.
- Use
tailwind.config.jsfor brand colours, fonts, and spacing tokens. Repeated arbitrary values should become named utilities. - Use arbitrary values like
w-[420px]only when the scale does not cover the design requirement. - Use
@applysparingly to extract repeated button, input, and card combinations into readable component classes. - Do not ship the CDN build to production. Use the build process so unused utilities are removed.
- Keep HTML readable. Split long class strings across lines when components become complex.
✏️ Tailwind Capstone
- Build a landing page using Tailwind and Flowbite with a sticky responsive navbar, hero, features grid, pricing section, modal CTA, and footer.
- Add a dark mode toggle and save the user's preference.
- Extract repeated patterns into reusable component classes or shared utility patterns.