Write reusable and predictable CSS
Outcome
Section titled “Outcome”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.
Why this matters
Section titled “Why this matters”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.
What is new and what is reused
Section titled “What is new and what is reused”- 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-profileproject, 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.
Required result
Section titled “Required result”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-visibleapplies; - 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
!importantdeclarations; - 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.
:root { --color-border: #9aa9b8;}
footer { border-color: var(--color-border);}Why this block starts with :root
Section titled “Why this block starts with :root”: 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:
<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:
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 footerThis 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 the shared design values
Section titled “Add the shared design values”Add this block at the top of styles.css, before the universal selector:
/* 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.
Replace repeated literal values
Section titled “Replace repeated literal values”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:
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.
- Save
styles.cssand reload the page. - Confirm that the page keeps its previous colors, borders, spacing, width, and Flexbox layout.
- Inspect one project entry in DevTools.
- Find its computed
border-colorand confirm that the browser resolvesvar(--color-border)to#9aa9b8. - Change
--color-bordertemporarily in DevTools. Confirm that both the project borders and footer border change. - Reload the page. Confirm that the saved border color returns.
If it does not work
Section titled “If it does not work”- 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.
Test custom-property scope and a fallback
Section titled “Test custom-property scope and a fallback”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:
-
In
.project-entry, temporarily replace the background declaration with this version:Temporary fallback experiment — project entry background-color: var(--project-surface, var(--color-surface)); -
Save and reload.
--project-surfacedoes not exist yet, so the nested fallback resolves to--color-surface. The project backgrounds stay white. -
Add this temporary declaration inside the existing
.project-listrule:Temporary scope experiment — project list --project-surface: #eef4ff; -
Save and reload. The project entries receive the new surface color because they are descendants of
.project-listand inherit its custom property. -
Remove
--project-surface: #eef4ff;from.project-list. The project entries return to the fallback color. -
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:
/* 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.
Finish the base CSS Diner practice
Section titled “Finish the base CSS Diner practice”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.
- Save and reload the page.
- Inspect the first child of
header. Confirm that its default paragraph margin is removed if it is ap. - Inspect one navigation link. Confirm that
.nav-list asupplies its padding. - Inspect one project heading. Confirm that
.project-entry > h3suppliesmargin-block-start: 0. - Confirm that headings or paragraphs outside those relationships keep their own margins.
- If the contact link uses
mailto:orhttps://, make the viewport narrow and confirm that long link text can wrap.
If it does not work
Section titled “If it does not work”- If
header > pdoes 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
h3is a direct child of.project-entryand 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
hrefvalue with the text inside the selector.
Add visible link interaction states
Section titled “Add visible link interaction states”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:
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.
Check with a pointing device
Section titled “Check with a pointing device”- Move the pointer over a navigation link.
- Confirm that the underline becomes thicker.
- Move the pointer away. Confirm that the underline returns to its base thickness.
- Select the link. Confirm that it still reaches its fragment destination.
Check with a keyboard
Section titled “Check with a keyboard”- Reload the page and move focus into the page with
Tab. UseShift+Tabto move backward when necessary. - Confirm that every link shows the purple outline when it receives visible keyboard focus.
- Confirm that the outline is not clipped by the header, navigation list, project entries, or viewport edge.
- Press
Enteron each navigation link. Confirm that each link reaches its matching section. - Continue to the contact link and confirm that its focus indicator remains visible at 200% browser zoom.
If it does not work
Section titled “If it does not work”- If hover changes the color but not the underline, inspect the link and confirm that
text-decoration-thicknessfroma:hoveris active. - If keyboard focus has no outline, confirm that the selector is
a:focus-visible, then useTabinstead of a pointer click to test it. - If the outline is cut off, inspect the link’s ancestors for
overflow: hiddenand remove that clipping unless the page has a separate, valid reason for it. - If
Tabnever enters the page, select the browser page once and try again. Also confirm that each link still has anhrefattribute.
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:
<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:
.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.
- Open each HTML page directly.
- Confirm that the link with
aria-current="page"matches the open file. - Confirm that the navigation contains exactly one current-page marker.
- Confirm that the current link has a visible non-color difference and still opens its destination.
- Use Tab and confirm that the separate keyboard-focus indicator remains visible on every link.
If it does not work
Section titled “If it does not work”- 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"]aftera. - 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:
- Does the declaration apply? The selector must match the element, and the declaration must be valid for the current page state.
- Which origin and importance group wins? Browser defaults, user styles, and your stylesheet have different origins. Normal author rules usually replace normal browser defaults.
!importantchanges the importance group. - Which matching selector has higher specificity? Count IDs first, then classes, attributes, and pseudo-classes, then element names and pseudo-elements.
- 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.
Read introductory specificity
Section titled “Read introductory specificity”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.
Run a controlled specificity experiment
Section titled “Run a controlled specificity experiment”Keep the first navigation link selected in DevTools. Add these temporary rules at the end of styles.css:
.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:
- Find both matching color declarations.
- Confirm that the browser crosses out or otherwise marks the losing
acolor declaration. - 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:
.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.
- Confirm that the selector matches the intended element and state.
- Identify the declaration that wins in DevTools.
- Decide whether both rules should set the same property.
- Remove an obsolete declaration or simplify an unnecessarily specific selector.
- Increase specificity only when the HTML relationship genuinely requires a narrower rule.
- 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 it does not work
Section titled “If it does not work”- 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
ainside.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.
Organize the final stylesheet by purpose
Section titled “Organize the final stylesheet by purpose”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:
- Shared design values.
- Shared sizing and page defaults.
- Headings, links, and interaction states.
- Page width and region spacing.
- Header and navigation layout.
- 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.
/* 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.
- Confirm that :root contains one literal definition for each shared color and spacing custom property.
- Search outside :root for #9aa9b8, #1f4e79, 1rem, and 2rem and confirm that the intended repeated uses now reference custom properties.
- Reload the page and confirm that its intended colors, spacing, width, borders, and Flexbox behavior remain intact.
- Inspect one project entry and trace its computed border color from var(--color-border) to #9aa9b8.
- Confirm that header > p or your adapted direct-child selector affects only the profile label.
- Confirm that .nav-list a affects navigation links and .project-entry > h3 affects direct project headings without changing unrelated elements.
- At a narrow viewport, confirm that long mailto: or HTTPS link text can wrap without creating unintended horizontal scrolling.
- Use a pointing device and confirm that each link keeps an underline and gains a thicker underline on hover.
- Use Tab and Shift + Tab and confirm that every link receives a visible focus outline and can be activated with Enter.
- At 200% browser zoom, confirm that content remains visible, navigation can wrap, and no focus outline is clipped.
- Explain why .nav-list a outranks a later a rule, and why a later identical .nav-list a selector wins a source-order tie.
- Confirm that styles.css contains no temporary test rules, no self-referencing custom properties, and no unnecessary !important declarations.
How the browser resolves the final styles
Section titled “How the browser resolves the final styles”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.
Record the reusable-CSS checkpoint
Section titled “Record the reusable-CSS checkpoint”After the required self-check passes, record the working state in the existing html-profile repository:
git statusgit diffgit add styles.cssgit diff --stagedgit commit -m "Make profile styles reusable"git pushReload the private GitHub repository and confirm that the commit contains the completed refactor, not a temporary cascade experiment.
Next step or safe stopping point
Section titled “Next step or safe stopping point”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.