← All guides

CSS · Responsive layout

Castaway: fixing responsive layout, from problem to result

Author: Grundy · Checked: October 6, 2026

This review documents actual changes to the educational Castaway demo. We measured overflow and inspected CSS constraints, fixed the implementation and repeated the checks. Each section explains the problem, cause, implemented change and result. Code excerpts show the relevant rules; the updated demo is linked below.

1. Recorded the overflow

Before the fixes, a 375px window produced a 935px-wide page. At 768px, the page was still 935px wide. Episodes, the contact panel and reviews were the main contributors. Fixed 70px side paddings, horizontal flex cards and three review columns could not fit together. The original grid below required at least 790px before section padding.

.reviews {
  display: grid;
  grid-template-columns: repeat(3, minmax(250px, 1fr));
  gap: 20px;
}

2. Made section spacing flexible

In section.module.css, replaced fixed left and right paddings with padding-inline and clamp. Spacing now ranges from 16px to 70px. Narrow-screen section top spacing is reduced to 56px. This gives content room instead of masking overflow with overflow-x: hidden.

.section {
  padding-inline: clamp(16px, 5vw, 70px);
}

3. Stacked the hero and restored normal flow

The hero stayed horizontal and Listen on used a relative top: 74px offset. At widths up to 900px, switched to a column, allowed children to shrink with min-width: 0 and removed the links’ offset. The photo now fits its container, and the decoration no longer extends to the left on small screens.

.hero { gap: clamp(24px, 4vw, 60px); }
.hero > * { min-width: 0; }
.listenOn { position: static; padding-left: 0; margin-top: 32px; }
@media (max-width: 900px) {
  .hero { flex-direction: column; align-items: stretch; }
  .underline { width: 100%; height: auto; }
}

4. Resized headings and their child spans

The hero used 70px text and child fragments at 75px and 80px. Reducing only the parent would leave those fragments oversized. Added clamp sizing and inherited child sizes, with relative line-height. Episode, host and subscription headings were adjusted too. The decorative underline scales on narrow screens.

.heroTitle {
  font-size: clamp(2rem, 1rem + 4vw, 4.375rem);
  line-height: 1.2;
  letter-spacing: 0.03em;
}
.heroTitle_bold, .heroTitle_span {
  font-size: inherit;
  letter-spacing: inherit;
}

5. Fixed card constraints and the invalid gap

Cards had non-shrinking images, a 53px gap and 40px padding. Made the images flexible, added min-width: 0 to text and stacked cards up to 900px. The episode list also had gap: 80 without a unit, which the browser ignored. Corrected it to 80px, then used clamp(32px, 6vw, 80px) for flexible spacing.

.episode { padding: clamp(20px, 3vw, 40px); gap: clamp(24px, 4vw, 53px); }
.content { min-width: 0; }
.image { flex: 0 1 389px; min-width: 0; }
.image img { width: 100%; height: auto; }
@media (max-width: 900px) {
  .episode { flex-direction: column; }
  .image { flex-basis: auto; width: 100%; }
}

6. Kept three desktop columns, added two and one

The old grid always required three tracks at least 250px wide. Replaced that minimum with minmax(0, 1fr), added two columns up to 1100px and one up to 650px. Three desktop columns preserve the design’s composition. Smaller card padding and type sizes make the narrower layouts usable.

.reviews { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1100px) {
  .reviews { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 650px) {
  .reviews { grid-template-columns: minmax(0, 1fr); }
}

7. Preserved photo proportions

In hero-image.module.css, made the wrapper flexible and gave the main photo its own photo class with width: 100% and height: auto. These rules do not scale the decorative SVG. The host photo is flexible too, and the about section stacks on small screens. Kept next/image dimensions, improved photo alt text and used empty alt text for decoration.

.heroImageContainer { width: min(100%, 650px); flex: 0 1 50%; }
.photo { width: 100%; height: auto; border-radius: 12px; }
@media (max-width: 900px) { .heroImageContainer { flex: initial; } .cornerImage { top: 0; left: 0; } }

8. Fixed the subscription panel and footer

The contact panel stayed in a row, fields lacked a width constraint and a non-breaking space in Subscribe for updates prevented wrapping. Removed that space, constrained inputs, allowed flex items to shrink and stacked the panel up to 900px. The footer now uses two columns up to 900px and one up to 500px. Demo subscription controls remain disabled.

.title, .form { min-width: 0; flex: 1; }
.input { width: 100%; min-width: 0; }
@media (max-width: 900px) { .panel { flex-direction: column; } }

10. Compared against the baseline

After fixing the layout, checked 320px, 375px, 768px, 900px, 1024px and 1440px. At each width, page scrollWidth matched clientWidth: horizontal overflow was removed without hiding it. Verified native menu opening and Escape closing with focus returning to the button. The table records measurements in the same browser; usable width is 15px less than window width because of its scrollbar. These checks cover the listed sizes, not every device.

Page width before and after fixes, px
WindowAvailableBeforeAfter
375360935360
768753935753
1024100910091009
1440142514251425