Skip to content

Write reusable and predictable CSS

You will refactor the profile-page stylesheet so repeated design decisions have one source. You will also add visible link states and use DevTools to explain why one competing declaration wins.

A stylesheet becomes harder to change when the same decision appears as unrelated values or when selectors compete without a clear reason. Shared custom properties, focused selectors, and a working model of the cascade make later changes safer and easier to review.

What you will practice

  • Define and reuse CSS custom properties for shared colors and spacing.
  • Explain how custom-property scope, inheritance, and fallback values affect a declaration.
  • Use descendant, child, attribute, and pseudo-class selectors for specific page relationships and states.
  • Create hover and keyboard-focus states that do not depend on color alone.
  • Use origin, importance, specificity, and source order to explain a cascade result.
  • Use DevTools to inspect competing declarations and remove unnecessary selector conflicts.
  • New: CSS custom properties, var(), fallback values, custom-property scope, combinators, attribute selectors, interaction pseudo-classes, specificity, source order, and a more deliberate stylesheet order.
  • Reused: The html-profile project, element and class selectors, selector lists, inheritance, the box model, Flexbox, logical properties, keyboard testing, browser reloading, and DevTools inspection.

Starting point

Before you start

  • The html-profile folder from the preceding Flexbox lesson.
  • index.html and styles.css at the same folder level.
  • A working header and navigation list, a project-list wrapper, and at least two project-entry articles.
  • A stylesheet that still contains the supplied colors, spacing, page-width rules, box-model rules, and Flexbox rules from the preceding lessons.
  • VS Code and a current version of Edge, Chrome, or Firefox.
Current state
The page works, but repeated color and spacing values appear in several rules. Links have a base color and underline, but the stylesheet does not yet define its own hover or keyboard-focus state.
First action
Open html-profile/styles.css. Use Find to locate every use of #9aa9b8 and then every use of 1rem. Do not edit either value yet.
First checkpoint
The repeated design values come from custom properties in :root, and the saved page keeps its intended appearance.
Help trigger
Use the recovery note or ask for help if a var() value does not resolve, the page changes unexpectedly during the refactor, a focus indicator is missing, or DevTools does not show which declaration wins.

You have completed the lesson when:

  • shared colors and repeated spacing values are defined as custom properties in :root;
  • the existing rules use var() instead of repeating those literal values;
  • the saved page keeps its intended colors, spacing, width, and Flexbox behavior after the refactor;
  • focused selectors remove the header-label and project-heading margins without affecting unrelated elements;
  • long email or HTTPS link text can wrap instead of forcing horizontal scrolling;
  • links keep a visible underline, show a thicker underline on hover, and show a clear outline when :focus-visible applies;
  • all links and fragment destinations still work with a mouse and keyboard;
  • the stylesheet has clear sections and contains no temporary conflict rules or unnecessary !important declarations;
  • the page remains usable at a narrow viewport and 200% browser zoom; and
  • you can use DevTools to explain one result from selector matching, specificity, and source order.

Identify the decisions that should have one source

Section titled “Identify the decisions that should have one source”

Custom properties are useful when a value represents a named design decision, appears in several places, or needs a controlled override. A custom property name begins with two hyphens.

Custom property syntax
:root {
--color-border: #9aa9b8;
}
footer {
border-color: var(--color-border);
}

: tells CSS that root is a pseudo-class selector, not an HTML element named root. :root selects the root element of the document. In an HTML page, the root element is html:

The root element in an HTML document
<html>
<head>...</head>
<body>...</body>
</html>

For this lesson, you can read :root as “select the page’s html element.” The block does not make a special global-variables area. It is an ordinary CSS rule that places custom properties on html.

You can write the same shared values in an html rule:

An equivalent HTML-page rule
html {
--color-border: #9aa9b8;
}

For this page, both selectors put the property on the same element and make it available to descendants. CSS authors often use :root because it states the intent to select the document root, rather than one specific HTML element. It also works in other document types that have a different root element. :root has higher selector specificity than html, so do not define the same custom property in both rules unless you intend to compare or override their cascade result.

The footer element is inside body, and body is inside html. Custom properties inherit by default, so footer can use the value that :root defined:

html (:root) → body → footer
--color-border: #9aa9b8 inherits to footer

This is why shared page values often go in :root: most elements descend from html and can inherit them. You can also define a custom property on a smaller element when only that element and its descendants need the value. You will test that component-level scope later in this lesson.

var(--color-border) asks the browser for the current value of --color-border. The custom property stores a value; it does not create a new CSS property or apply a style by itself.

Not every number needs a custom property. Keep a one-off value in its rule when a name would not clarify its purpose or support reuse. This lesson creates properties for the shared page colors, the focus color, and the spacing values that already serve several rules.

Add this block at the top of styles.css, before the universal selector:

html-profile/styles.css — shared design values
/* Shared design values */
:root {
--color-text: #1f2937;
--color-page: #f4f7fb;
--color-heading: #16324f;
--color-accent: #1f4e79;
--color-link: #005ea8;
--color-muted: #44546a;
--color-border: #9aa9b8;
--color-surface: #ffffff;
--color-focus: #6d28d9;
--space-medium: 1rem;
--space-section: 2rem;
}

The names state the role of each value. For example, --color-border is more stable than a name such as --gray, because a later design can change the border color without making the property name false.

The focus color does not need to match the link color. Its job is to create a strong, recognizable outline on the supplied light surfaces.

Use Find in styles.css and replace the matching values outside the :root block:

Existing value Use outside :root
#1f2937 var(--color-text)
#f4f7fb var(--color-page)
#16324f var(--color-heading)
#1f4e79 var(--color-accent)
#005ea8 var(--color-link)
#44546a var(--color-muted)
#9aa9b8 var(--color-border)
#ffffff var(--color-surface)
1rem where it controls shared layout spacing var(--space-medium)
2rem where it controls section separation var(--space-section)

Do not replace the literal definitions inside :root. A definition such as --color-border: var(--color-border); refers to itself and cannot supply the intended color.

After the replacement, these existing rule groups should use the custom properties:

html-profile/styles.css — examples after the refactor
body {
margin: 0;
color: var(--color-text);
background-color: var(--color-page);
font-family: Arial, sans-serif;
line-height: 1.6;
}
h1 {
color: var(--color-heading);
}
h2 {
color: var(--color-accent);
}
a {
color: var(--color-link);
}
section,
aside {
margin-block: var(--space-section);
}
.project-list {
display: flex;
flex-wrap: wrap;
gap: var(--space-medium);
}

Keep every declaration that is not shown in this excerpt. You will review one complete stylesheet later in the lesson.

  1. Save styles.css and reload the page.
  2. Confirm that the page keeps its previous colors, borders, spacing, width, and Flexbox layout.
  3. Inspect one project entry in DevTools.
  4. Find its computed border-color and confirm that the browser resolves var(--color-border) to #9aa9b8.
  5. Change --color-border temporarily in DevTools. Confirm that both the project borders and footer border change.
  6. Reload the page. Confirm that the saved border color returns.
  • If a declaration becomes invalid, compare the custom property name in var() with the name in :root. Custom property names are case-sensitive.
  • If one color disappears, confirm that var() has both parentheses and that the custom property name starts with two hyphens.
  • If many values stop resolving, inspect the braces around :root. Confirm that the block closes before the universal selector begins.
  • If a Find and Replace operation changed a definition inside :root, restore the literal value from the shared design-values example.

Checkpoint: Shared design values have one source

What now works
The page keeps its intended appearance, while shared colors and repeated spacing values resolve from custom properties in :root. One temporary DevTools edit can update every use of a shared value.
Files changed
html-profile/styles.css
What remains
Test custom-property scope and fallback behavior, then add focused selectors and visible link states.
Next action
Find the project-list and project-entry rules and run the temporary scope experiment in the next section.
If it does not work
Inspect one unresolved declaration, copy its custom property name, and compare that exact name with the definition in :root before changing another rule.

A custom property can be declared on any element. The declaration applies to that element and can inherit into its descendants. A fallback gives var() another value to use when the requested custom property has no usable value.

Run this controlled experiment in the saved stylesheet:

  1. In .project-entry, temporarily replace the background declaration with this version:

    Temporary fallback experiment — project entry
    background-color: var(--project-surface, var(--color-surface));
  2. Save and reload. --project-surface does not exist yet, so the nested fallback resolves to --color-surface. The project backgrounds stay white.

  3. Add this temporary declaration inside the existing .project-list rule:

    Temporary scope experiment — project list
    --project-surface: #eef4ff;
  4. Save and reload. The project entries receive the new surface color because they are descendants of .project-list and inherit its custom property.

  5. Remove --project-surface: #eef4ff; from .project-list. The project entries return to the fallback color.

  6. Restore the final project-entry declaration:

    html-profile/styles.css — restored project surface
    background-color: var(--color-surface);

The experiment shows a valid component-level override. The final page does not need that override, so remove the extra abstraction when the test is complete.

Select elements by relationship and attribute

Section titled “Select elements by relationship and attribute”

The selector should express the smallest useful relationship. A long selector is not automatically more precise or more maintainable.

Add these rules in the relevant stylesheet sections:

html-profile/styles.css — relationship and attribute selectors
/* Remove the default margin from the profile label only. */
header > p {
margin: 0;
}
/* Give links inside the navigation a larger interactive area. */
.nav-list a {
display: inline-block;
padding-block: 0.5rem;
padding-inline: 0.25rem;
}
/* Remove the top margin from direct project headings only. */
.project-entry > h3 {
margin-block-start: 0;
color: var(--color-heading);
}
/* Let long contact destinations wrap on a narrow viewport. */
a[href^="mailto:"],
a[href^="https://"] {
overflow-wrap: anywhere;
}

Each selector describes a different relationship:

Selector Type Matches
header > p Child combinator A p whose parent is header
.nav-list a Descendant combinator Any a nested anywhere inside .nav-list
.project-entry > h3 Child combinator An h3 whose parent is .project-entry
a[href^="mailto:"] Attribute selector An a whose href value starts with mailto:
a[href^="https://"] Attribute selector An a whose href value starts with https://

The ^= operator means “starts with.” The attribute selectors therefore leave the navigation’s fragment links, such as href="#projects", unchanged.

The original profile reference uses a p for the profile label inside header. If your valid page uses another element for that label, adapt header > p to the element in your source. Do not change meaningful HTML only to make this example selector match.

Return to CSS Diner after you complete the relationship and attribute-selector rules above.

Base-material target: Continue from level 4 and complete level 11. Together with levels 1–3 from the CSS foundations lesson, this completes the supported CSS Diner practice for this course.

Levels 4–8 combine element, ID, and class selectors or match a descendant. Levels 9–11 practice a selector list, the universal selector, and a descendant selector. Use the game’s help panel when you need it. Do not add a more complex selector to styles.css only because you used it in the game. Selectors should still describe a real relationship in your HTML.

  1. Save and reload the page.
  2. Inspect the first child of header. Confirm that its default paragraph margin is removed if it is a p.
  3. Inspect one navigation link. Confirm that .nav-list a supplies its padding.
  4. Inspect one project heading. Confirm that .project-entry > h3 supplies margin-block-start: 0.
  5. Confirm that headings or paragraphs outside those relationships keep their own margins.
  6. If the contact link uses mailto: or https://, make the viewport narrow and confirm that long link text can wrap.
  • If header > p does not match, inspect the header’s direct children and adapt the element name to the saved HTML.
  • If a project heading keeps its top margin, confirm that the h3 is a direct child of .project-entry and that no wrapper sits between them.
  • If the navigation padding affects unrelated links, confirm that the selector begins with .nav-list.
  • If the attribute selector does not match, compare the beginning of the saved href value with the text inside the selector.

A pseudo-class selects an element in a particular state. :hover applies when a pointing device hovers over an element. :focus-visible applies when the browser determines that a visible focus indicator is needed, such as during keyboard navigation.

Replace the existing a rule with this complete base rule. Then add the two state rules after it:

html-profile/styles.css — link appearance and states
a {
color: var(--color-link);
text-decoration-line: underline;
text-decoration-thickness: 0.1em;
text-underline-offset: 0.2em;
}
a:hover {
text-decoration-thickness: 0.2em;
}
a:focus-visible {
border-radius: 0.125rem;
outline: 3px solid var(--color-focus);
outline-offset: 3px;
}

The link underline stays visible in the base state. Hover makes the underline thicker, so the change does not depend on color alone. Keyboard focus uses an outline outside the link box, so it remains distinct from both the text color and underline.

Do not add outline: none. Removing the browser outline without a visible replacement can make keyboard position impossible to find.

Hover is not available on every input method. Do not place required information or behavior only in a hover state.

  1. Move the pointer over a navigation link.
  2. Confirm that the underline becomes thicker.
  3. Move the pointer away. Confirm that the underline returns to its base thickness.
  4. Select the link. Confirm that it still reaches its fragment destination.
  1. Reload the page and move focus into the page with Tab. Use Shift + Tab to move backward when necessary.
  2. Confirm that every link shows the purple outline when it receives visible keyboard focus.
  3. Confirm that the outline is not clipped by the header, navigation list, project entries, or viewport edge.
  4. Press Enter on each navigation link. Confirm that each link reaches its matching section.
  5. Continue to the contact link and confirm that its focus indicator remains visible at 200% browser zoom.
  • If hover changes the color but not the underline, inspect the link and confirm that text-decoration-thickness from a:hover is active.
  • If keyboard focus has no outline, confirm that the selector is a:focus-visible, then use Tab instead of a pointer click to test it.
  • If the outline is cut off, inspect the link’s ancestors for overflow: hidden and remove that clipping unless the page has a separate, valid reason for it.
  • If Tab never enters the page, select the browser page once and try again. Also confirm that each link still has an href attribute.

Checkpoint: Selectors and link states serve specific page needs

What now works
Focused relationship selectors change only the intended header label, navigation links, and project headings. Long contact links can wrap, hover changes underline thickness, and keyboard focus has a visible outline.
Files changed
html-profile/styles.css
What remains
Use a controlled conflict to inspect specificity and source order, then organize and verify the final stylesheet.
Next action
Open DevTools, inspect the first navigation link, and keep the Styles or Rules panel visible for the cascade experiment.
If it does not work
Inspect one target element and check whether the intended selector matches before you change its specificity or add another rule.

Mark and style the current page in navigation

Section titled “Mark and style the current page in navigation”

A multi-page website should identify which navigation link matches the open page. Add aria-current="page" to that link. This attribute gives assistive technology the current-page information and gives CSS a precise attribute selector.

This is a reusable pattern for a future multi-page project. The current profile page uses fragment links on one page, so do not add this marker to that project unless you convert it into separate pages.

The Home document in a multi-page site can use this navigation:

Example multi-page navigation in index.html
<nav aria-label="Primary navigation">
<ul class="nav-list">
<li><a href="index.html" aria-current="page">Home</a></li>
<li><a href="projects.html">Projects</a></li>
</ul>
</nav>

On projects.html, keep both links but move aria-current="page" to the Projects link. Each page must have exactly one current-page marker in this navigation. The current item remains a link, so the navigation structure and link order stay consistent.

Use the attribute in a selector and add a visual difference that does not depend on color:

A non-color current-page state
.nav-list a[aria-current="page"] {
font-weight: bold;
text-decoration-thickness: 0.2em;
}

The square brackets select an element by its attribute. This rule combines a heavier font weight with a thicker underline. A color change can support the state, but color must not be the only difference.

  1. Open each HTML page directly.
  2. Confirm that the link with aria-current="page" matches the open file.
  3. Confirm that the navigation contains exactly one current-page marker.
  4. Confirm that the current link has a visible non-color difference and still opens its destination.
  5. Use Tab and confirm that the separate keyboard-focus indicator remains visible on every link.
  • If the wrong link looks current, move aria-current="page" in that HTML file. Do not change the shared link order.
  • If every link receives the current style, confirm that the selector includes [aria-current="page"] after a.
  • If the current state is visible only through color, add a text, underline, border, or shape difference.

Use the cascade to explain which declaration wins

Section titled “Use the cascade to explain which declaration wins”

The cascade is the CSS algorithm that chooses among declarations that set the same property on the same element. Specificity is one part of that algorithm, not the complete algorithm.

For this page, which uses normal author CSS without cascade layers, animations, transitions, inline styles, or @scope, use these questions in order:

  1. Does the declaration apply? The selector must match the element, and the declaration must be valid for the current page state.
  2. Which origin and importance group wins? Browser defaults, user styles, and your stylesheet have different origins. Normal author rules usually replace normal browser defaults. !important changes the importance group.
  3. Which matching selector has higher specificity? Count IDs first, then classes, attributes, and pseudo-classes, then element names and pseudo-elements.
  4. If specificity is equal, which declaration appears later? The later declaration wins when the earlier questions produce a tie.

User !important styles can override author !important styles. This protects user preferences such as stronger contrast or larger text. Do not use !important to repair an ordinary selector conflict.

Write specificity as three counts for the selector types used in this stylesheet:

Selector IDs Classes, attributes, and pseudo-classes Elements Specificity
a 0 0 1 0-0-1
.nav-list a 0 1 1 0-1-1
.nav-list a:hover 0 2 1 0-2-1
#contact a 1 0 1 1-0-1

The counts are separate categories, not a decimal number. One ID outweighs any number of classes in the next category. The universal selector * and combinators such as > do not add specificity.

Higher specificity is not a quality score. Use only the specificity that the intended relationship needs.

Keep the first navigation link selected in DevTools. Add these temporary rules at the end of styles.css:

Temporary specificity experiment — remove after the test
.nav-list a {
color: var(--color-accent);
}
a {
color: var(--color-link);
}

Save and reload the page. Both selectors match a navigation link. The later a rule has lower specificity, so .nav-list a supplies the accent color.

In the DevTools Styles or Rules panel:

  1. Find both matching color declarations.
  2. Confirm that the browser crosses out or otherwise marks the losing a color declaration.
  3. Confirm in the Computed panel that the final color resolves from .nav-list a.

Now replace the two temporary rules with this equal-specificity pair:

Temporary source-order experiment — remove after the test
.nav-list a {
color: var(--color-accent);
}
.nav-list a {
color: var(--color-link);
}

Both selectors now have the same specificity. The later declaration supplies the link color because source order resolves the tie.

Remove both temporary rules. Save and reload. The navigation links must return to the base link color from the real a rule.

Resolve conflicts by removing unnecessary competition

Section titled “Resolve conflicts by removing unnecessary competition”

When an intended rule loses, do not immediately add an ID, repeat a class, or add !important.

  1. Confirm that the selector matches the intended element and state.
  2. Identify the declaration that wins in DevTools.
  3. Decide whether both rules should set the same property.
  4. Remove an obsolete declaration or simplify an unnecessarily specific selector.
  5. Increase specificity only when the HTML relationship genuinely requires a narrower rule.
  6. Repeat the visible behavior and DevTools checks.

For example, header nav ul.nav-list a adds element names that the current navigation style does not need. .nav-list a already states the useful relationship. A shorter selector makes later changes easier to predict.

  • If the temporary color does not change, confirm that both test rules are outside every existing rule and that the file was saved.
  • If DevTools shows only one rule, reload the page and confirm that you selected an a inside .nav-list.
  • If the equal-specificity test gives the unexpected color, compare the two selectors character by character and confirm that the intended winner appears later.
  • If the final page keeps a temporary color, remove all four test declarations and reload the saved page.

Comments can make a short stylesheet easier to scan when each comment names a useful section. They do not need to describe every declaration.

Use this order:

  1. Shared design values.
  2. Shared sizing and page defaults.
  3. Headings, links, and interaction states.
  4. Page width and region spacing.
  5. Header and navigation layout.
  6. Project-list layout and reusable project styles.

Compare your saved file with this complete required stylesheet. Keep any valid content-specific styles that your profile needs, but remove temporary experiment rules and duplicate declarations.

html-profile/styles.css — complete required stylesheet
/* Shared design values */
:root {
--color-text: #1f2937;
--color-page: #f4f7fb;
--color-heading: #16324f;
--color-accent: #1f4e79;
--color-link: #005ea8;
--color-muted: #44546a;
--color-border: #9aa9b8;
--color-surface: #ffffff;
--color-focus: #6d28d9;
--space-medium: 1rem;
--space-section: 2rem;
}
/* Shared sizing and page defaults */
* {
box-sizing: border-box;
}
body {
margin: 0;
color: var(--color-text);
background-color: var(--color-page);
font-family: Arial, sans-serif;
line-height: 1.6;
}
/* Headings, links, and interaction states */
h1 {
color: var(--color-heading);
}
h2 {
color: var(--color-accent);
}
a {
color: var(--color-link);
text-decoration-line: underline;
text-decoration-thickness: 0.1em;
text-underline-offset: 0.2em;
}
a:hover {
text-decoration-thickness: 0.2em;
}
a:focus-visible {
border-radius: 0.125rem;
outline: 3px solid var(--color-focus);
outline-offset: 3px;
}
a[href^="mailto:"],
a[href^="https://"] {
overflow-wrap: anywhere;
}
/* Page width and region spacing */
header,
main,
footer {
width: 90%;
max-width: 48rem;
margin-inline: auto;
}
section,
aside {
margin-block: var(--space-section);
}
aside {
border-inline-start: 0.25rem solid var(--color-accent);
padding-inline-start: var(--space-medium);
}
footer {
margin-block-start: var(--space-section);
border-top: 2px solid var(--color-border);
padding-block: 1.5rem;
color: var(--color-muted);
}
/* Header and navigation layout */
header {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: var(--space-medium);
align-items: center;
justify-content: space-between;
}
header > p {
margin: 0;
}
.nav-list {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-list a {
display: inline-block;
padding-block: 0.5rem;
padding-inline: 0.25rem;
}
/* Project-list layout and reusable project styles */
.project-list {
display: flex;
flex-wrap: wrap;
gap: var(--space-medium);
}
.project-entry {
flex-basis: 16rem;
flex-grow: 1;
flex-shrink: 1;
margin: 0;
border: 2px solid var(--color-border);
background-color: var(--color-surface);
padding: 1.25rem;
}
.project-entry > h3 {
margin-block-start: 0;
color: var(--color-heading);
}

If your header label is not a p, keep the adapted selector that matches your valid HTML. The final stylesheet is a reference state, not a reason to discard a meaningful project difference.

Checkpoint: The cascade and stylesheet order are explainable

What now works
DevTools shows why the temporary specificity and source-order results differ. The final stylesheet is grouped by purpose and contains only selectors and custom properties that serve the saved page.
Files changed
html-profile/styles.css
What remains
Complete the final mouse, keyboard, narrow-viewport, zoom, and DevTools checks.
Next action
Reload the saved page and complete the self-check from top to bottom.
If it does not work
Remove every temporary conflict rule, reload, then inspect one final declaration at a time from its matching selector to its computed value.

Self-check

Complete these checks against the required result.

  1. Confirm that :root contains one literal definition for each shared color and spacing custom property.
  2. Search outside :root for #9aa9b8, #1f4e79, 1rem, and 2rem and confirm that the intended repeated uses now reference custom properties.
  3. Reload the page and confirm that its intended colors, spacing, width, borders, and Flexbox behavior remain intact.
  4. Inspect one project entry and trace its computed border color from var(--color-border) to #9aa9b8.
  5. Confirm that header > p or your adapted direct-child selector affects only the profile label.
  6. Confirm that .nav-list a affects navigation links and .project-entry > h3 affects direct project headings without changing unrelated elements.
  7. At a narrow viewport, confirm that long mailto: or HTTPS link text can wrap without creating unintended horizontal scrolling.
  8. Use a pointing device and confirm that each link keeps an underline and gains a thicker underline on hover.
  9. Use Tab and Shift + Tab and confirm that every link receives a visible focus outline and can be activated with Enter.
  10. At 200% browser zoom, confirm that content remains visible, navigation can wrap, and no focus outline is clipped.
  11. Explain why .nav-list a outranks a later a rule, and why a later identical .nav-list a selector wins a source-order tie.
  12. Confirm that styles.css contains no temporary test rules, no self-referencing custom properties, and no unnecessary !important declarations.

The browser matches selectors against the current HTML and page state. It resolves competing declarations through the cascade. The resulting values can inherit, and a var() reference uses the custom property value available on the selected element.

The selector does not copy a custom property into every matching rule. It supplies a value through the cascade and inheritance. This distinction makes a shared change possible: changing --color-border in :root changes every declaration that references the property without changing each rule separately.

Specificity matters only when competing declarations have already reached the same relevant cascade group. Source order matters only when the preceding cascade decisions still produce a tie. DevTools exposes this result by showing matched rules, inactive declarations, and computed values.

Predictable CSS does not mean that every selector is short or every value is a variable. It means that each abstraction and selector has a stated job, and you can explain where the final value comes from.

After the required self-check passes, record the working state in the existing html-profile repository:

Commit and push the CSS refactor
git status
git diff
git add styles.css
git diff --staged
git commit -m "Make profile styles reusable"
git push

Reload the private GitHub repository and confirm that the commit contains the completed refactor, not a temporary cascade experiment.

The required lesson is complete when the page keeps its intended layout, shared values have one source, link states work with a pointer and keyboard, narrow and zoomed views retain all content and focus indicators, and you can trace a final value through selector matching, the cascade, and var() resolution.

Continue to Adapt a layout to different screen sizes to combine the current fluid widths and Flexbox behavior with content-based media queries.

If you stop here, leave yourself this resume note: The stylesheet is refactored and the link states work. Next, open the screen-sizes lesson and test the profile page from a narrow viewport before adding a media query.