Up to here, the Kladde’s prototype looks like a page from the early days of the web: black type, blue links, everything one below the other. That’s intentional, because the meaning came first. Now the looks are added, with CSS (Cascading Style Sheets). By the end of this lesson, the three pages share a stylesheet, they’re easy to read on the phone and on the screen, and they switch to a dark scheme with the device’s setting.

Important up front: Almost nothing changes in the HTML. A few classes are added so that the stylesheet hits the right elements. The meaning stays as it was.

How CSS gets to the page

A stylesheet is a file of its own. Every page links it in the head, after the title:

HTML
<link rel="stylesheet" href="css/kladde.css">

A stylesheet consists of rules. A rule has a selector that says which elements are meant, and in curly braces declarations, each as property: value;:

CSS
/* Comments stand between slash-star and star-slash. */
h1 {
  font-size: 2rem;
  margin: 0 0 1rem;
}

A file of its own instead of settings directly in the HTML (style="…") has three advantages: All pages share the same rules, the browser loads the file once and keeps it in its cache, and the HTML stays readable. That’s why there are no style attributes in the Kladde.

Selectors

The most important selectors:

Selector hits Example
h1 all elements of this type every main heading
.card all elements with this class (class="card") every card
#time the element with this id the time field
[aria-current="page"] all elements with this attribute and value the link of the current page
.site-nav a all a anywhere inside an element with the class site-nav the navigation’s links
.cards > .card .card that sit directly in .cards the home page’s cards
h1, h2 both, a list all main and section headings
a:hover a state: the mouse pointer is on it a link under the mouse
:focus-visible a state: the element has the focus, and the browser considers a focus indicator necessary a link you’ve jumped to with the Tab key

For styling, use classes. They say what role an element has in the look and can be put on any number of elements. An id selector always hits only a single element and is so strong that it’s hard to override (more on that in a moment). Class names are code, so English and with hyphens: site-header, field-error.

Which rule wins: specificity

Often several rules hit the same element and set the same property. Then the selector’s specificity decides first. You count three columns:

  1. A: the number of id selectors,
  2. B: the number of classes, attribute selectors and states like :hover,
  3. C: the number of element types like a or nav.

The comparison goes from left to right, like with a date written year first, as in 2025-12-02: First A counts; only if A is equal does B count; then C.

Selector A, B, C
a 0, 0, 1
.site-nav a 0, 1, 1
nav a[aria-current="page"] 0, 1, 2
.site-nav .nav-link 0, 2, 0
#main-menu a 1, 0, 1

.site-nav .nav-link (0, 2, 0) beats nav a[aria-current="page"] (0, 1, 2), even though the second is longer: One more class weighs more than any number of element types. And a single id beats everything without an id. That’s why you don’t use an id for styling: Anyone who wants to change something later would otherwise have to counter with another id.

If the specificity is equal, the rule that comes later in the stylesheet wins.

There are two more things you should know but avoid: A style attribute in the HTML beats every selector, and !important after a value turns the whole order upside down. Both solve one problem today and cause three new ones tomorrow.

The cascade and inheritance

The cascade is the whole order by which the browser decides which value applies. Simplified:

  1. Where does the rule come from? The browser has a stylesheet of its own with defaults (which is why links are blue and h1 is big). Your rules take precedence.
  2. What specificity does the selector have?
  3. Which rule comes later?

Beside it there is inheritance: Some properties pass from the parent element to the children, above all those of the type: color, font-family, font-size, line-height. That’s why you set type and color once on the body, and all the text takes them over. Spacing, borders and backgrounds are not inherited; otherwise every paragraph in a card would have a border again.

Form fields are an exception with consequences: Browsers give input, select, textarea and button a font of their own instead of inheriting it. With font: inherit; you bring it back.

The box model

Every element is a rectangle of four layers, from the inside out:

┌─────────────── margin (outer spacing, transparent) ───────────────┐
│ ┌─────────────── border (frame) ────────────────────────────────┐ │
│ │ ┌─────────────── padding (inner spacing) ──────────────────┐  │ │
│ │ │                                                           │  │ │
│ │ │                content (text)                             │  │ │
│ │ │                                                           │  │ │
│ │ └───────────────────────────────────────────────────────────┘  │ │
│ └────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘

By the old default box-sizing: content-box, width means only the content. Padding and border are added:

CSS
.box {
  width: 20rem;          /* 320 px */
  padding: 1rem;         /* 16 px each on the left and right */
  border: 2px solid;     /* 2 px each on the left and right */
}

This box is 320 + 32 + 4 = 356 pixels wide, not 320. With box-sizing: border-box, width means the whole box up to and including the border; it’s then exactly 320 pixels wide, and the content gets what’s left, here 284 pixels. That’s much easier to calculate. Almost every stylesheet therefore begins with:

CSS
*,
*::before,
*::after {
  box-sizing: border-box;
}

Two more quirks:

  • Margins collapse. If two paragraphs stand one below the other, the upper one with a 16-pixel margin below and the lower one with 16 above, the space between them is 16, not 32. Only vertically, only with margins.
  • Block and inline. Block elements (p, div, section, h1) start a new line and take the full width. Inline elements (a, strong, span) flow along in the text; vertical spacing has hardly any effect on them. With display: inline-block, a link gets padding in all directions, so a bigger click target.

Units: why rem

  • px is a fixed pixel (on high-resolution screens, several real dots).
  • rem is a multiple of the root element’s font size, usually 16 pixels. So 1.5rem is usually 24 pixels.
  • em is a multiple of the element’s own font size; when nested it multiplies, which is rarely what you want.
  • % refers to the parent element, vw to the width of the window.
  • ch is the width of the digit 0 in the current font, good for the length of lines.

Use rem for font sizes and spacing. Anyone who has set a larger base font in their browser because they have poor eyesight then gets a larger page. With px for fonts, the page ignores that setting.

And limit the length of lines of text: From about 70 characters on, the eye has a hard time finding the start of the next line. max-width: 68ch for paragraphs keeps lines readable, even on a wide screen.

Tokens: design values in one place

Colors and spacing appear dozens of times in a stylesheet. If #1f5f5b stands everywhere, changing a color is a search through the whole file. Custom properties (often called “CSS variables”) solve that: You store a value once under a name and use it with var():

CSS
:root {
  --color-text: #1d2422;
  --color-surface: #fbfaf7;
  --space-3: 1rem;
}

body {
  color: var(--color-text);
  background: var(--color-surface);
}

Such named design values are called tokens. Two rules for good names:

  • Name by role, not by looks. --color-danger instead of --color-red. In the dark scheme the error color is a lighter pink; --color-red: #ff8a8f would then be a lie.
  • A small scale instead of arbitrary values. Five spacings (--space-1 to --space-5) are enough for a whole application, and everything looks like it’s from one mold.

Custom properties are inherited and apply at runtime. That sets them apart from the variables in SCSS, which you’ll meet in lesson 2.1.6 and which disappear at compile time. This very property is what the dark scheme needs.

A dark scheme on the same tokens

Many people set their device to a dark scheme. The page learns that through the media query prefers-color-scheme. Because all colors are tokens, it’s enough to assign the tokens anew. Not a single other rule changes:

CSS
:root {
  color-scheme: light dark;
  --color-text: #1d2422;
  --color-surface: #fbfaf7;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-text: #e6ebe9;
    --color-surface: #141a19;
  }
}

color-scheme: light dark tells the browser that the page knows both schemes. It then colors its own parts to match, such as the scroll bars and the picker in a date field’s calendar.

In both schemes, watch the contrast between text and background. The rules for it and a small program that calculates it come in lesson 2.1.7. The colors in this lesson’s solution already meet them.

Flexbox: things in a row

Flexbox arranges an element’s children in a row (or column). The Kladde’s header: logo and navigation side by side, and the navigation’s links side by side too:

CSS
.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
  • display: flex turns the children into flex items in one row.
  • flex-wrap: wrap lets them wrap when there isn’t enough room. Without this line they squeeze together or stick out past the edge, and on the phone a horizontal scroll appears.
  • align-items: center aligns them vertically in the middle.
  • gap sets the spacing between them, first vertical, then horizontal. No margin needed on individual elements.
  • justify-content: space-between distributes them across the whole width, the first at the far left, the last at the far right.

list-style: none takes the bullets off the list items, but not the meaning: For the screen reader it remains a list.

Grid: things in rows and columns

Grid arranges an element’s children in a grid. The home page’s two cards stand one below the other on the phone and side by side on the screen:

CSS
.cards {
  display: grid;
  gap: var(--space-3);
}

@media (min-width: 48rem) {
  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

grid-template-columns: repeat(2, 1fr) means: two columns that share the room equally (fr is a share of the free space). Without this, a grid has a single column, and the cards stand one below the other.

As a rule of thumb: flexbox for a row whose elements are as wide as their content (navigation, buttons side by side), grid for a grid where the columns set the measure (cards, forms in columns, whole page regions).

Media queries: phone first

A media query applies rules only under a condition, such as from a minimum window width. You write the rules for the smallest device first, without a media query, and then add for larger screens with min-width. That’s called mobile first.

CSS
h1 {
  font-size: 2rem;
}

@media (min-width: 48rem) {
  h1 {
    font-size: 2.5rem;
  }
}

Why in this direction? The simple layout, everything one below the other, is the one for the phone. It needs little CSS. For wide screens, layout is added. The other way around, you’d have to take apart again for the phone what was built for the screen. The breakpoint in rem (48rem is 768 pixels with the standard font) has an advantage: Anyone who has set a larger font gets the simple layout for longer, and that’s right for them.

So that a page doesn’t slide sideways on the phone, three rules apply: the viewport entry from lesson 2.1.2, no fixed widths above about 320 pixels, and images with max-width: 100%. Long words without a break point, such as email addresses in a table, may wrap with overflow-wrap: anywhere.

The focus stays visible

Anyone working with the keyboard has to see where they are. Browsers draw a focus indicator for that, often thin and barely visible on some backgrounds. Strengthen it instead of removing it:

CSS
:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

:focus-visible applies when the browser considers the indicator necessary, so with the keyboard, not on every mouse click on a button. Never write outline: none without an equivalent replacement. It’s one of the most common mistakes on websites at all, and lesson 2.1.7 checks exactly that.

Exercise 1: Who wins?

Given this HTML:

HTML
<nav class="site-nav" id="main-menu">
  <a class="nav-link" href="index.html" aria-current="page">Home</a>
</nav>

and this stylesheet:

CSS
#main-menu a { color: orange; }
.site-nav .nav-link { color: purple; }
nav a[aria-current="page"] { color: red; }
.site-nav a { color: green; }
a { color: blue; }
  1. Determine the specificity of each of the five selectors as A, B, C.
  2. What color does the link have?
  3. What color does it have if you delete the first rule?
  4. And if you then delete the second one too?
  5. The last rule a { color: blue; } stands at the very bottom, so the latest. Why does it never win anyway?

Check your answers in the browser: Put both in one file, open it and delete the rules one after the other.

Solution 1

  1. #main-menu a: 1, 0, 1. .site-nav .nav-link: 0, 2, 0. nav a[aria-current="page"]: 0, 1, 2. .site-nav a: 0, 1, 1. a: 0, 0, 1.
  2. Orange. Only this selector has an id, and A is compared first.
  3. Purple. Between 0, 2, 0 and 0, 1, 2, B decides, and 2 is more than 1. The two element types of the red selector count only when B is equal.
  4. Red. 0, 1, 2 against 0, 1, 1: A and B are equal, C decides.
  5. The order decides only with equal specificity. 0, 0, 1 is smaller than every other selector here.

Checked in the browser: With all five rules the link is orange, without the first purple, without the first two red.

Exercise 2: Calculating with the box model

The base font is 16 pixels. Calculate without a browser:

  1. How wide is an element with width: 20rem; padding: 1rem; border: 2px solid; with box-sizing: content-box? And with border-box? How wide is its content then?
  2. Two paragraphs stand one below the other. The upper one has margin-bottom: 1.5rem, the lower one margin-top: 1rem. How big is the space between them?
  3. A person sets their browser’s base font to 20 pixels. How wide is the element from question 1 with border-box now?

Solution 2

  1. With content-box: 320 + 2 × 16 + 2 × 2 = 356 pixels. With border-box: exactly 320 pixels; the content is 320 − 32 − 4 = 284 pixels wide.
  2. 24 pixels. Vertical margins collapse, and the larger one wins.
  3. 400 pixels, because 20rem is now 20 × 20. The border stays 2 pixels because it’s given in px; the content is 400 − 40 − 4 = 356 pixels wide. That’s exactly as it should be: With the larger font the box grows along, and the text still fits in.

Exercise 3: Why isn’t the border red?

A stylesheet contains these two rules, in this order:

CSS
.field input {
  border: 1px solid var(--color-muted);
}

[aria-invalid="true"] {
  border: 2px solid var(--color-danger);
}

On the booking’s error page, the email field has aria-invalid="true", and it stands in a div with the class field. Yet its border is gray and thin. Explain why, and change one selector so that the red border wins, without !important and without changing the order of the rules.

Solution 3

.field input has the specificity 0, 1, 1, [aria-invalid="true"] only 0, 1, 0. An attribute selector counts like a class; the element type input gives the first rule its lead in column C. That the second rule comes later doesn’t help it, because the order counts only on a tie.

The solution is a second part in the selector:

CSS
.field [aria-invalid="true"] {
  border: 2px solid var(--color-danger);
}

With 0, 2, 0 it now wins. input[aria-invalid="true"] (0, 1, 1) would work too, because on a tie it then comes later; but that no longer hits a select. In lesson 2.1.5 you’ll find such cases in seconds with the browser’s tools: There the lost rule stands struck through.

Exercise 4: Styling the prototype

Give the prototype’s three pages and the error page a shared stylesheet prototype/css/kladde.css. Proceed in this order and look in the browser after each step:

  1. Classes in the HTML. In all four pages: class="site-header" on the header, class="site-nav" on the nav, class="site-footer" on the footer and the link to the stylesheet in the head. On the home page, the two sections stand as section class="card" in a div class="cards". In the forms, the div around label and field gets the class field, the div around radio buttons and checkboxes the class choice, hints the class field-hint, error messages the class field-error and the error list at the top the class error-summary.
  2. Tokens. Colors for text, muted text, background, cards, border, accent, text on the accent, links, focus and errors; spacing in five steps; a radius; the font; the line length.
  3. Basics. box-sizing, font and colors on the body, headings, line length of paragraphs, links, the focus indicator, images.
  4. Page structure. Header with flexbox, navigation as a row, the main content centered with a maximum width, footer, cards with grid.
  5. Table and forms. Header cells and rows with lines; labels above the fields, fields at full width up to a maximum width, radio buttons beside their label, hints muted, errors in the error color with text and a thicker border; the button in the accent color.
  6. Wider screens. From 48rem: navigation on the right in the header, two cards side by side, a larger h1.
  7. Dark scheme. All color tokens once more in @media (prefers-color-scheme: dark).

The exercise is checked when all four points are met:

  • At a window width of 360 pixels there is no horizontal scroll on any page. The easiest way is to set the width in the browser’s tools: F12, then Ctrl+Shift+M (on the Mac Cmd+Shift+M) for the device emulation. More on this in lesson 2.1.5. As a test, type into the tools’ console document.documentElement.scrollWidth <= document.documentElement.clientWidth; the answer must be true.
  • At 1280 pixels, logo and navigation stand in one row and the two cards side by side.
  • With the device’s dark scheme, or in the tools under “Rendering” with “Emulate CSS media feature prefers-color-scheme: dark”, every page is dark, without a rule other than the tokens appearing twice.
  • The validator still reports nothing for the four pages, and the W3C CSS validator reports no error for kladde.css. You may ignore a warning that it can’t check custom properties.

Solution 4

The stylesheet:

CSS
/* kladde.css: the look of the prototype */

/* 1. Tokens: all design values in one place */
:root {
  color-scheme: light dark;
  --color-text: #1d2422;
  --color-muted: #4a5552;
  --color-surface: #fbfaf7;
  --color-surface-raised: #ffffff;
  --color-border: #c9cfcc;
  --color-accent: #1f5f5b;
  --color-on-accent: #ffffff;
  --color-link: #1a5c8a;
  --color-focus: #b35c00;
  --color-danger: #a4262c;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2.5rem;
  --radius: 0.375rem;
  --font-body: system-ui, "Segoe UI", Roboto, sans-serif;
  --measure: 68ch;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-text: #e6ebe9;
    --color-muted: #b4bfbb;
    --color-surface: #141a19;
    --color-surface-raised: #1d2524;
    --color-border: #3d4a47;
    --color-accent: #7cc4bd;
    --color-on-accent: #0d1413;
    --color-link: #8cc4f0;
    --color-focus: #ffb366;
    --color-danger: #ff8a8f;
  }
}

/* 2. Basics */
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--color-text);
  background: var(--color-surface);
}

h1,
h2 {
  line-height: 1.2;
}

h1 {
  font-size: 2rem;
  margin: 0 0 var(--space-3);
}

h2 {
  font-size: 1.375rem;
  margin: var(--space-4) 0 var(--space-2);
}

p {
  max-width: var(--measure);
}

a {
  color: var(--color-link);
}

:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

img {
  max-width: 100%;
  height: auto;
}

/* 3. Page structure */
.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav a {
  display: inline-block;
  padding: var(--space-2) var(--space-1);
}

.site-nav a[aria-current="page"] {
  font-weight: 700;
  text-decoration-thickness: 3px;
}

main {
  max-width: 60rem;
  margin: 0 auto;
  padding: var(--space-4) var(--space-3) var(--space-5);
}

.site-footer {
  padding: var(--space-3);
  border-top: 1px solid var(--color-border);
  color: var(--color-muted);
  font-size: 0.875rem;
}

.site-footer p {
  margin: 0;
}

.cards {
  display: grid;
  gap: var(--space-3);
}

.card {
  padding: var(--space-3);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

.card h2 {
  margin-top: 0;
}

/* 4. Tables */
table {
  width: 100%;
  border-collapse: collapse;
}

caption {
  padding-bottom: var(--space-2);
  font-weight: 700;
  text-align: start;
}

th,
td {
  padding: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  text-align: start;
  vertical-align: top;
}

thead th {
  border-bottom-width: 2px;
}

td a {
  overflow-wrap: anywhere;
}

/* 5. Forms */
fieldset {
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

legend {
  padding: 0 var(--space-1);
  font-weight: 700;
}

.field {
  margin-bottom: var(--space-3);
}

.field label {
  display: block;
  margin-bottom: var(--space-1);
  font-weight: 600;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  max-width: 30rem;
  padding: var(--space-2);
  font: inherit;
  color: inherit;
  background: var(--color-surface-raised);
  border: 1px solid var(--color-muted);
  border-radius: var(--radius);
}

.field [aria-invalid="true"] {
  border: 2px solid var(--color-danger);
}

.choice {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.choice input {
  width: 1.25rem;
  height: 1.25rem;
  margin: 0;
  accent-color: var(--color-accent);
}

.field-hint,
.field-error {
  margin: var(--space-1) 0 0;
  font-size: 0.875rem;
}

.field-hint {
  color: var(--color-muted);
}

.field-error {
  color: var(--color-danger);
  font-weight: 700;
}

.error-summary {
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: 3px solid var(--color-danger);
  border-radius: var(--radius);
}

.error-summary h2 {
  margin-top: 0;
}

button {
  padding: var(--space-2) var(--space-4);
  font: inherit;
  font-weight: 700;
  color: var(--color-on-accent);
  background: var(--color-accent);
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
}

/* 6. Wider screens */
@media (min-width: 48rem) {
  .site-header {
    justify-content: space-between;
    padding-inline: var(--space-4);
  }

  .cards {
    grid-template-columns: repeat(2, 1fr);
  }

  h1 {
    font-size: 2.5rem;
  }
}

The home page with its classes:

HTML
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Kladde</title>
  <link rel="stylesheet" href="css/kladde.css">
</head>
<body>
  <header class="site-header">
    <a href="index.html"><img src="logo.svg" alt="Kladde, home page" width="48" height="48"></a>
    <nav class="site-nav" aria-label="Main navigation">
      <ul>
        <li><a href="index.html" aria-current="page">Home</a></li>
        <li><a href="contacts.html">Contacts</a></li>
        <li><a href="appointment.html">Book an appointment</a></li>
      </ul>
    </nav>
  </header>
  <main>
    <h1>Kladde</h1>
    <p>The Kladde is the workbook of a small IT office: contacts, appointments and public tenders in one place.</p>
    <div class="cards">
      <section class="card">
        <h2>What the Kladde can do</h2>
        <ul>
          <li>Keep contacts with company and email address</li>
          <li>Plan appointments in the office</li>
          <li>Book an appointment online without calling</li>
        </ul>
      </section>
      <section class="card">
        <h2>Office hours</h2>
        <dl>
          <dt>Monday to Thursday</dt>
          <dd>9:00 to 17:00</dd>
          <dt>Friday</dt>
          <dd>9:00 to 13:00</dd>
        </dl>
        <p><a href="appointment.html">Book an appointment online</a></p>
      </section>
    </div>
  </main>
  <footer class="site-footer">
    <p>Kladde, a practice project. All people and companies are made up.</p>
  </footer>
</body>
</html>

In the forms, a field with a hint now looks like this, and so does every other field with its classes:

HTML
<div class="field">
  <label for="email">Email</label>
  <input type="email" id="email" name="email" autocomplete="email" required aria-describedby="email-hint">
  <p class="field-hint" id="email-hint">We'll send the confirmation to this address.</p>
</div>
<div class="choice">
  <input type="checkbox" id="reminder" name="reminder" value="yes">
  <label for="reminder">Remind me by email the day before the appointment</label>
</div>

On the error page, the list at the top gets class="error-summary" and each message at the field class="field-error":

HTML
<div class="error-summary">
  <h2>Please check two details</h2>
  <ul>
    <li><a href="#time">Choose a time.</a></li>
    <li><a href="#email">The email address needs an @, for example name@example.com.</a></li>
  </ul>
</div>

On the phone, the navigation stands below the logo, the cards stand one below the other, and the email addresses of the contact list wrap in their column instead of widening the page. On the screen, the navigation stands on the right, the cards side by side, and the paragraphs still stay narrow enough to read. Your colors may be different; but keep the names of the tokens by role, and have the contrasts calculated in lesson 2.1.7.

Three places where it often snags:

  • The type in the fields is smaller than the text. font: inherit is missing on the fields and the button.
  • Radio buttons are huge in width. The rule for fields also hits input type="radio" if it’s written as input instead of .field input. That’s why radio buttons stand in .choice and not in .field.
  • Something stays light in the dark scheme. Somewhere a color stands directly in the stylesheet instead of as a token. Search for # outside :root.

The Kladde: the styled prototype

On a branch of their own, commit the stylesheet and the four changed pages, with a message like “Style the prototype for phone and desktop, with a dark scheme”, and open a pull request. Attach two screenshots to the description, one at 360 and one at 1280 pixels, each light and dark. In a review of CSS, a picture says more than any explanation.

In lesson 2.1.6 you’ll break this one stylesheet into smaller SCSS files. Afterward, the page has to look exactly the same; so keep the screenshots.

What you can do now

  • You link a stylesheet and select elements with type, class and attribute selectors, with combinators and states.
  • You determine a selector’s specificity and predict which rule wins; you style with classes, not with id.
  • You explain the cascade and inheritance and bring the font back to form fields with font: inherit.
  • You calculate with the box model, set box-sizing: border-box and use rem for fonts and spacing.
  • You lay out with flexbox and grid, phone first, and extend with min-width for wide screens.
  • You keep colors and spacing as tokens and build the dark scheme through the tokens alone.

Read more