Adapt a layout to different screen sizes
Outcome
Section titled “Outcome”You will adapt the profile page so it uses a deliberate narrow layout by default and a wider arrangement when the content has enough room. You will verify that the result keeps its content, reading order, links, and focus indicators at narrow and wide viewport sizes and at 200% browser zoom.
Why this matters
Section titled “Why this matters”Web pages do not receive one predictable window size. A resilient layout responds to the space available to its content instead of assuming a specific phone, tablet, monitor, or user setting.
What you will practice
- Distinguish a browser viewport from a physical screen or named device.
- Combine percentages, rem units, maximum widths, wrapping content, and flexible media in a narrow-first layout.
- Choose a media-query breakpoint from the needs of the content.
- Use a min-width media query to change Flexbox placement without changing meaningful HTML order.
- Find and correct common causes of unintended horizontal scrolling.
- Test responsive behavior with browser tools, keyboard navigation, and 200% browser zoom.
What is new and what is reused
Section titled “What is new and what is reused”- New: The viewport as a layout constraint, narrow-first CSS, media-query syntax, content-based breakpoints, flexible media, and a repeatable responsive test process.
- Reused: The
html-profileproject, the viewportmetaelement, percentages,rem,max-width, border-box sizing, Flexbox wrapping, custom properties, source order, link states, DevTools, and keyboard testing.
Starting point
Before you start
- The html-profile folder from the preceding reusable CSS lesson.
- index.html and styles.css at the same folder level.
- A viewport meta element in index.html.
- A working header and navigation list, a project-list wrapper, and at least two project-entry articles.
- The refactored stylesheet with custom properties, fluid page regions, Flexbox rules, wrapping links, and visible hover and focus states.
- VS Code and a current version of Edge, Chrome, or Firefox.
- Current state
- The page uses 90% of the available width up to a 48rem maximum. Its header, navigation, and project list can wrap, but the header starts as a row at every width and no media query defines a deliberate change in arrangement.
- First action
- Open html-profile/index.html and confirm that head contains the viewport meta element. Then open the page and make the browser viewport narrow without editing the CSS.
- First checkpoint
- Before any media query applies, the saved page uses a clear narrow layout with a stacked header and one project entry per row.
- Help trigger
- Use the recovery note or ask for help if the viewport setting is missing, a media query never changes state, content causes horizontal scrolling, source order and visual order differ, or keyboard focus becomes hidden or clipped.
Required result
Section titled “Required result”You have completed the lesson when:
index.htmlcontains the existing viewportmetaelement;- the base CSS works as a complete narrow layout without depending on a media query;
- page regions use a fluid percentage width and retain their
48remmaximum width; - project entries form one column before the breakpoint and can share a row after the breakpoint;
- the header stacks before the breakpoint and can form a row after the breakpoint;
- one
min-widthmedia query changes only the declarations needed for the wider arrangement; - the layout does not use a named-device breakpoint or CSS
orderto rearrange meaningful content; - images cannot exceed their containing block if the page contains an image now or receives one later;
- no content creates unintended horizontal scrolling at the tested widths or 200% browser zoom;
- navigation and project content keep the same meaningful source and visual order; and
- every link remains visible, operable by keyboard, and marked by a visible focus indicator.
Identify the viewport that CSS responds to
Section titled “Identify the viewport that CSS responds to”The viewport is the browser area available to render the page. It is not the complete browser window, a physical screen measurement, or a device model.
CSS layout uses CSS pixels. A CSS pixel is a logical unit that browsers can scale. Browser zoom, operating-system scaling, display density, and mobile viewport behavior can change the relationship between CSS pixels and physical pixels. For this reason, a query such as min-width: 42rem describes available layout space, not a particular device.
Open index.html and confirm that head contains this existing element:
<meta name="viewport" content="width=device-width, initial-scale=1">On a mobile browser, width=device-width asks the layout viewport to use the device’s CSS-pixel width. initial-scale=1 sets the initial zoom scale. The element does not make the page responsive by itself. It gives the browser the intended viewport basis so the responsive CSS can behave as designed.
Keep the user’s ability to zoom. Do not add maximum-scale=1 or user-scalable=no.
Record the current layout before changing it
Section titled “Record the current layout before changing it”A short baseline test gives you evidence about what the CSS must change.
- Save both project files and open
index.htmlin the normal browser view. - Make the viewport wide enough for the profile label and navigation to share one row.
- Reduce the width slowly. Watch the header, navigation links, project entries, long contact link, and page edges.
- Record the first layout problem you observe. For example, the header might wrap at an awkward point, or two project entries might remain beside each other after their text becomes cramped.
- Confirm that all content remains present. Responsive work must not remove required content to make the layout fit.
Do not add a media query yet. The next section first creates a complete narrow layout that works when no condition matches.
You can name which parts already adapt through fluid width or Flexbox wrapping and which parts need a deliberate change in arrangement.
If it does not work
Section titled “If it does not work”- If the browser shows an older result, save both files and reload the same
index.htmlfile. - If the page is already wider than the viewport, inspect for a fixed
width,min-width, long unbroken string, or oversized image before you continue. - If the current stylesheet differs from the preceding lesson, keep meaningful project choices. Compare the behavior, not every literal line.
Make the narrow layout the default
Section titled “Make the narrow layout the default”A narrow-first stylesheet defines the complete narrow arrangement in its ordinary rules. A later min-width media query adds only the changes that need more space.
The page already has a fluid outer width:
header,main,footer { width: 90%; max-width: 48rem; margin-inline: auto;}These declarations work together:
width: 90%follows the containing block and leaves space at both viewport edges.max-width: 48remprevents lines from growing without limit on a wide viewport.margin-inline: autocenters each region when unused inline space exists.- The existing
box-sizing: border-boxrule keeps padding and borders inside declared widths.
No media query is needed for this fluid behavior.
Stack the header before the breakpoint
Section titled “Stack the header before the breakpoint”Find the existing header rule. Change flex-direction, align-items, and justify-content to these narrow-layout values. Keep the other header declarations:
header { display: flex; flex-direction: column; flex-wrap: wrap; gap: var(--space-medium); align-items: flex-start; justify-content: flex-start;}The main axis now runs from top to bottom. align-items: flex-start keeps the label and navigation aligned at the start of the inline direction instead of stretching them across the header. The navigation list remains its own wrapping flex container.
Give each project entry one narrow row
Section titled “Give each project entry one narrow row”In the existing .project-entry rule, change only flex-basis from 16rem to 100%:
.project-entry { flex-basis: 100%; flex-grow: 1; flex-shrink: 1; margin: 0; border: 2px solid var(--color-border); background-color: var(--color-surface); padding: 1.25rem;}The project list still uses a row main axis and wrapping. A 100% basis asks one entry to use the complete row before free space is distributed, so the next entry wraps to a new line.
Keep images inside their containing block
Section titled “Keep images inside their containing block”Add this rule near the page-default rules:
img { display: block; max-inline-size: 100%; block-size: auto;}An image has an intrinsic size from its source file. Without a limit, a large intrinsic width can exceed a narrow containing block.
max-inline-size: 100% limits the rendered image to the available inline size without forcing a smaller image to grow. block-size: auto preserves the image’s aspect ratio when its inline size changes. display: block removes the small baseline gap that an inline image can create.
The current profile assignment does not require an image. Keep this rule as a safe media baseline even when it matches no element yet. If you later add an informative image, the HTML still needs useful alt text. A decorative image needs alt="".
Check the base layout without a media query
Section titled “Check the base layout without a media query”- Save
styles.cssand reload the page. - Confirm that no
@mediarule exists yet. - Make the viewport narrow.
- Confirm that the profile label appears above the navigation.
- Confirm that each project entry occupies its own row.
- Confirm that all navigation links wrap when necessary and remain visible.
- Confirm that the page has no unintended horizontal scrollbar.
If it does not work
Section titled “If it does not work”- If the header remains a row, inspect it and confirm that the active
flex-directionvalue iscolumn. - If project entries still share a row, confirm that
flex-basis: 100%is in.project-entry, not.project-list. - If the project entries become unusually tall, confirm that
.project-liststill uses the default row direction. A percentageflex-basisfollows the main axis. - If an image looks distorted, confirm that it has
block-size: autoand no conflicting fixed height. - If a long contact link leaves the page, keep the earlier
overflow-wrap: anywhererule on the relevant links.
Checkpoint: The narrow layout works without a condition
- What now works
- Before any media query applies, the header stacks, project entries form one column, navigation links wrap, fluid page regions retain their edge space, and all content remains visible.
- Files changed
html-profile/styles.css- What remains
- Choose a breakpoint from the content and add the wider header and project arrangement.
- Next action
- Increase the viewport width slowly and find the first range where the profile label, navigation, and two project entries have enough room for wider placement.
- If it does not work
- Inspect the header and one project entry. Confirm the active flex-direction, align-items, justify-content, and flex-basis values before changing another declaration.
Choose the breakpoint from the content
Section titled “Choose the breakpoint from the content”A breakpoint is the boundary where a media-query condition changes between false and true. Choose it from the space needed by the current content.
Use this process:
- Start from the confirmed narrow layout.
- Increase the viewport width slowly.
- Find the range where the header label and navigation can share a row without crowding or overlap.
- Confirm that two project entries can also fit at a readable width with their gap.
- Choose a nearby stable value that gives the content some room on both sides of the transition.
This lesson uses 42rem as the shared project breakpoint. With a browser’s usual initial text size, that is approximately 672 CSS pixels. The rem unit keeps the condition connected to the browser’s initial text-size basis instead of naming a device category.
Your content can differ from the reference profile. If 42rem does not fit the actual header content, record the observed problem and choose a nearby rem value that does. Keep one breakpoint for this required lesson.
Add one min-width media query
Section titled “Add one min-width media query”Place this media query at the end of styles.css, after the ordinary header and project rules:
@media (min-width: 42rem) { header { flex-direction: row; align-items: center; justify-content: space-between; }
.project-entry { flex-basis: 16rem; }}The condition reads: when the viewport width is at least 42rem, apply the declarations inside this block.
The query does not replace the base stylesheet. The contained rules join the cascade only while the condition is true. They use the same selectors as the base rules and appear later, so source order lets the conditional declarations override the matching base declarations.
Only the changed values belong inside the query:
- The header returns to a row and distributes its two direct children across that row.
- Project entries return to a
16rempreferred basis, so two entries can share a row when space permits. - Width limits, colors, spacing, wrapping, link states, and box sizing continue to come from the base rules.
This structure keeps the narrow layout complete. Browsers that do not match the condition still receive a usable page.
Test both sides of the condition
Section titled “Test both sides of the condition”- Save and reload the page.
- Set a viewport width below
42rem. Confirm that the header stacks and project entries use one row each. - Increase the width above
42rem. Confirm that the header forms a row and project entries can share a row. - Reduce the width again. Confirm that the narrow arrangement returns without reloading.
- Inspect
headerin DevTools on both sides of the breakpoint. Confirm that the media-query declarations appear active only when the condition matches.
If it does not work
Section titled “If it does not work”- If the query never applies, check the
@mediaspelling, both pairs of braces, the colon inmin-width: 42rem, and the unit on42rem. - If the wider rules apply at narrow widths, confirm that the condition uses
min-width, notmax-width. - If the complete stylesheet stops after the query, check for a missing closing brace in the rule immediately before
@media. - If the header changes but the project entries do not, inspect a project entry and confirm that
.project-entrymatches its class. - If the layout changes at an unexpected width, confirm the current zoom level and the viewport width shown by DevTools. Do not infer the width from the physical monitor.
Checkpoint: One content-based breakpoint changes the arrangement
- What now works
- Below the breakpoint, the narrow base layout stacks the header and project entries. At and above the breakpoint, the media query creates the wider arrangement while all base styles remain active.
- Files changed
html-profile/styles.css- What remains
- Confirm source order, find any horizontal overflow, and complete the responsive test matrix with keyboard and zoom checks.
- Next action
- Compare the HTML order of the header, navigation links, and project entries with their visual order below and above the breakpoint.
- If it does not work
- Inspect one changed property on each side of the breakpoint and confirm whether its conditional declaration is active before editing the breakpoint value.
Keep source order and visual order consistent
Section titled “Keep source order and visual order consistent”Responsive CSS can change placement without changing the DOM. Keep the semantic and reading order in index.html:
- profile label, then primary navigation;
- main heading and introduction;
- About, Skills, Projects, current focus, and Contact content; and
- project entries in the order that they should be read.
Do not add order to move these items for one viewport size. CSS order changes visual placement but does not rewrite the DOM. A conflicting visual and source order can create different sequences for sighted reading, keyboard movement, speech output, and other tools.
The required media query changes only flex-direction, alignment, distribution, and the preferred entry size. Flex wrapping preserves the sequence of the direct children.
Check the sequence
Section titled “Check the sequence”- Read the relevant elements in
index.htmlfrom top to bottom. - Compare that order with the narrow visual layout.
- Compare it again with the wide visual layout.
- Press
TabandShift + Tabthrough every link. - Confirm that focus moves through the links in the same meaningful sequence and remains visible at both widths.
If the visual order differs, remove any order, row-reverse, or column-reverse declaration that changes the required sequence. Change the HTML source only when the semantic order itself is wrong at every width.
Find and correct horizontal overflow
Section titled “Find and correct horizontal overflow”An unintended horizontal scrollbar is evidence that at least one box extends beyond the viewport. Hiding the scrollbar does not correct the box.
Common causes and focused corrections include:
| Cause | Evidence to inspect | Focused correction |
|---|---|---|
A fixed width such as width: 700px |
The computed width stays larger than the containing block | Use a fluid width, a suitable max-width, or remove the unnecessary fixed width |
| A large image | The image’s rendered width exceeds its containing block | Keep max-inline-size: 100% and block-size: auto |
| A long URL or email address | One unbroken string crosses the box edge | Keep overflow-wrap: anywhere on the relevant link |
| A flex item with an unnecessary minimum width | The item refuses to shrink or wrap with its siblings | Remove or reduce the fixed minimum and test the content again |
| Padding and border added outside a declared width | The rendered box is wider than the declared width | Confirm that box-sizing: border-box applies |
Use this diagnostic process at the width where the problem appears:
- Scroll horizontally to locate the area that extends beyond the right edge.
- Inspect the widest visible element in that area.
- In the Computed or Layout panel, compare its rendered width with its containing block.
- Disable one suspected width, minimum-width, margin, transform, or media declaration in DevTools.
- If the overflow disappears, correct that declaration in
styles.cssand reload. - Repeat the narrow-width test.
Do not use overflow-x: hidden on body as the required repair. It can conceal content, clipped focus outlines, or an off-screen control while leaving the layout defect in place.
At the narrowest tested width, the page does not scroll sideways, every text line and image remains within its containing block, and no focus indicator is clipped at a page edge.
If it does not work
Section titled “If it does not work”- If you cannot identify the overflowing box, temporarily disable page sections in DevTools one at a time. The section whose removal ends the overflow contains the cause. Reload to restore the page before editing the source.
- If the overflow appears only at 200% zoom, repeat the inspection at 200%. Zoom changes the effective CSS-pixel space available to the layout.
- If a focus outline creates a small scroll area, keep the visible outline and provide enough surrounding space. Do not remove the indicator.
Test responsive behavior with evidence
Section titled “Test responsive behavior with evidence”Browser responsive-design tools let you set a viewport width and inspect media-query transitions. They emulate a viewport; they do not reproduce every physical device, browser, input method, or user setting.
Use responsive-design mode for repeatable width checks, then confirm keyboard and zoom behavior in the normal page view.
| Test state | Expected result |
|---|---|
| About 320 CSS pixels wide | Header is stacked, navigation wraps, project entries use one row each, and no content scrolls sideways |
| Below the chosen breakpoint | The complete narrow base layout remains active |
| Above the chosen breakpoint | Header forms a row and project entries can share a row when their content fits |
| A wide viewport | Page regions stop growing at 48rem and remain centered |
| Normal page view at 200% zoom | Content reflows, every link remains reachable, focus stays visible, and no required content is lost |
Complete this test sequence:
- Open responsive-design mode and set a viewport near 320 CSS pixels wide.
- Move through widths below, at, and above the chosen breakpoint.
- At each width, select every fragment-navigation link and confirm that it still reaches the intended section.
- Check the complete top and bottom edges for a horizontal scrollbar.
- Exit responsive-design mode.
- In the normal page view, set browser zoom to 200%.
- Use
Tab,Shift + Tab, andEnterto reach and activate every link. - Confirm that text wraps, focus outlines remain visible, and no content overlaps or disappears.
- Return zoom to your usual setting after the test.
The test passes when the content and interactions remain usable. The layout does not need to keep the same number of columns or the same line breaks at every width.
Checkpoint: Responsive behavior is verified across width, input, and zoom
- What now works
- The page passes the narrow, breakpoint, wide, normal-view zoom, fragment-link, keyboard, focus, source-order, and horizontal-overflow checks.
- Files changed
html-profile/styles.css- What remains
- Compare the final stylesheet with the reference structure, then complete the self-check.
- Next action
- Review the final responsive rules and remove any temporary DevTools experiments or duplicate declarations.
- If it does not work
- Return to the last confirmed width, test one element or interaction, and change one declaration only after the inspection identifies it as the cause.
Review one complete responsive stylesheet
Section titled “Review one complete responsive stylesheet”This reference combines the final state from the CSS lessons. Compare structure and behavior before literal values. Keep any meaningful content-based breakpoint adjustment that you tested in your own page.
/* 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;}
/* Predictable sizing */* { box-sizing: border-box;}
/* Page defaults */body { margin: 0; color: var(--color-text); background-color: var(--color-page); font-family: Arial, sans-serif; line-height: 1.6;}
img { display: block; max-inline-size: 100%; block-size: auto;}
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);}
/* Narrow header and navigation layout */header { display: flex; flex-direction: column; flex-wrap: wrap; gap: var(--space-medium); align-items: flex-start; justify-content: flex-start;}
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;}
/* Narrow project-list layout and reusable project styles */.project-list { display: flex; flex-wrap: wrap; gap: var(--space-medium);}
.project-entry { flex-basis: 100%; 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);}
/* Wider arrangement when the content has enough room */@media (min-width: 42rem) { header { flex-direction: row; align-items: center; justify-content: space-between; }
.project-entry { flex-basis: 16rem; }}The stylesheet has one complete base and one focused conditional layer. The final media query repeats selectors, not complete rule sets, because only four declarations need to change.
Self-check
Complete these checks against the required result.
- Confirm that index.html contains meta name="viewport" with width=device-width and initial-scale=1, without a zoom restriction.
- Below the breakpoint, confirm that the header stacks and every project entry uses its own row.
- Confirm that header, main, and footer use 90% width, stop at 48rem, and remain centered at a wide viewport.
- Confirm that styles.css contains a flexible img rule with max-inline-size: 100% and block-size: auto.
- At and above the breakpoint, confirm that the header can form a row and project entries can share a row when their content fits.
- Inspect header below and above the breakpoint and identify which declarations come from the media query.
- Explain why the 42rem breakpoint describes available layout space rather than a named device.
- Compare the source and visual order of the navigation links and project entries at narrow and wide widths.
- Search styles.css for order, row-reverse, and column-reverse and confirm that none changes the required content sequence.
- At about 320 CSS pixels, confirm that text, navigation, project entries, and any image stay within the viewport without unintended horizontal scrolling.
- At 200% browser zoom in the normal page view, confirm that every required part remains visible and usable.
- Use Tab and Shift + Tab to confirm that every link receives a visible, unclipped focus indicator in a meaningful sequence.
- Activate every fragment link with Enter and confirm that it still reaches the intended section.
- Confirm that styles.css contains no temporary diagnostic rules, duplicate experimental declarations, named-device comments, or overflow-x: hidden repair.
How the responsive layers work together
Section titled “How the responsive layers work together”The browser first lays out the page from the ordinary rules. Percentages let page regions follow their containing block. Maximum widths stop growth. Text, navigation, and Flexbox items wrap when their content needs another line. Flexible media cannot exceed its containing block.
The browser then evaluates the media-query condition against the viewport. When the condition is false, nothing inside the query participates in the cascade. When it is true, the later conditional declarations override the matching base declarations. The HTML and its meaning remain unchanged.
This approach uses a breakpoint for a change in arrangement, not for every small size difference. Fluid sizing handles the continuous changes between breakpoints. Flexbox handles available space within each arrangement. The media query handles the point where the content benefits from a different arrangement.
Responsive design is broader than making boxes fit. A technically correct result also preserves content, reading order, keyboard operation, visible focus, readable text, and zoom support.
Record the responsive-layout checkpoint
Section titled “Record the responsive-layout checkpoint”After the required self-check passes, record the working state in the existing html-profile repository:
git statusgit diffgit add index.html styles.cssgit diff --stagedgit commit -m "Add responsive profile layout"git pushIf index.html did not change, stage only styles.css. Reload GitHub and confirm that the newest commit contains the tested narrow base and content-based media query.
Next step or safe stopping point
Section titled “Next step or safe stopping point”The required lesson is complete when the narrow base works without a media query, one content-based breakpoint creates the wider arrangement, and the complete page passes the width, zoom, keyboard, focus, order, and overflow checks.
Continue to Practice: Style and adapt a profile page to apply the complete CSS and layout module as one independent result.
If you stop here, leave yourself this resume note: The profile page has a tested narrow base and one wider media query. Next, open the profile-page CSS assignment and compare its required result with the current project.