Replicating Hulu’s Categories Mosaic with Flexbox

screenshot

The Hulu homepage contains a mosaic of show and movie categories. However, it’s a huge, single image (including the text), making the content inaccessible to screen readers and search engines. Learn how to replicate the layout two ways: once with Grid, once with Flexbox (this page).

As is often the case with CSS, there is more than one way to achieve this particular layout. The HTML is the same for both this Flexbox version and the Grid version, as is the vast majority of the CSS. The only difference here is that the layout is set to Flexbox (running and wrapping in columns, not rows) when the viewport is wide enough. Please see the comments in the CSS for more details about how it works. (Bonus tip!: there is yet a third way to do this with most of the same code: Multi-column Layout.)

Please see the Grid version of this layout for more information about how the “Show Title” and “LOGO” are positioned within each item.

The Finished Example Layout

Images courtesy of Unsplash

The Code

HTML
  <ul class="Categories">
    <li class="Cat Cat--100">
      <a href="#" class="Cat__link">
        <p class="Cat__type">New on Hulu</p>
        <p class="Cat__title">Hulu with Live TV</p>
        <p class="Cat__show">Show Title</p>
        <p class="Cat__logo">logo</p>
      </a>
    </li>
    <li class="Cat Cat--50">
      <a href="#" class="Cat__link">
        <p class="Cat__type">Groundbreaking</p>
        <p class="Cat__title">Hulu Originals</p>
        <p class="Cat__show">Show Title</p>
        <p class="Cat__logo">logo</p>
      </a>
    </li>
    <li class="Cat Cat--50">
      <a href="#" class="Cat__link">
        <p class="Cat__type">New and Classic</p>
        <p class="Cat__title">Movies</p>
        <p class="Cat__show">Show Title</p>
        <p class="Cat__logo">logo</p>
      </a>
    </li>
    <li class="Cat Cat--33">
      <a href="#" class="Cat__link">
        <p class="Cat__type">Current</p>
        <p class="Cat__title">Seasons</p>
        <p class="Cat__show">Show Title</p>
        <p class="Cat__logo">logo</p>
      </a>
    </li>
    <li class="Cat Cat--33">
      <a href="#" class="Cat__link">
        <p class="Cat__type">Exclusive</p>
        <p class="Cat__title">Past Seasons</p>
        <p class="Cat__show">Show Title</p>
        <p class="Cat__logo">logo</p>
      </a>
    </li>
    <li class="Cat Cat--33">
      <a href="#" class="Cat__link">
        <p class="Cat__type">For All Ages</p>
        <p class="Cat__title">Kids</p>
        <p class="Cat__show">Show Title</p>
        <p class="Cat__logo">logo</p>
      </a>
    </li>
  </ul>

  <p class="Credit Credit--rt">Images courtesy of <a href="https://unsplash.com/">Unsplash</a></p>

CSS
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin: 0;
}

/* Container of whole unit
----------------------------- */
.Categories {
  list-style: none;
  margin: 0 auto;
  max-width: 1600px;
  padding-left: 0;
  width: 100%;
}

/* Apply Flexbox only when viewport at least this wide */
@media only screen and (min-width: 40em) {
  .Categories {
    height: calc(100vh - 80px);
    max-height: 800px;
    min-height: 400px;
    /* Note that I've set flex to run and wrap in columns. */
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
  }
}

/* Category (Flexbox items)
----------------------------- */
.Cat {
  background-color: #222;
  background-position: center center;
  background-size: cover;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* :::: Item size types (by height) :::: */
/* Stacked vertically by default */
.Cat--100 {
  min-height: 55vh;
}

.Cat--50 {
  min-height: 30vh;
}

.Cat--33 {
  min-height: 20vh;
}

/* Three columns in these wider viewports */
@media only screen and (min-width: 40em) {
  .Cat {
    width: 33.33%;
  }
  /* Because flex-direction is set to column (see earlier), flex-basis sets the height of the items, NOT the width. */
  .Cat--100 {
    flex-basis: 100%;
  }
  .Cat--50 {
    flex-basis: 50%;
  }
  .Cat--33 {
    flex-basis: 33.33%;
  }
}

/* :::: Items content :::: */
/* Most of what follows is just about making them look nice. */
.Cat__link {
  color: #fff;
  display: block;
  padding: 12px 13px;
  position: relative;
  height: 100%;
  text-decoration: none;
}

@media only screen and (min-width: 40em) {
  .Cat__link {
    padding: 24px 26px;
  }
}

.Cat__type {
  font-size: .75em;
  letter-spacing: .1em;
  margin-bottom: 6px;
  margin-top: 0;
  text-transform: uppercase;
}

.Cat__title {
  font-size: 1.6em;
  margin-top: 0;
}

.Cat__show, .Cat__logo {
  position: absolute;
}

.Cat__show {
  bottom: 14px;
  font-size: .75em;
}

.Cat__logo {
  bottom: 4px;
  font-family: impact;
  font-size: 1.125em;
  letter-spacing: .07em;
  text-transform: uppercase;
  right: 26px;
}

/* Background imgs. In practice, replace nth-child selectors with classes per your preference. */
.Cat:nth-child(1) {
  background-image: url(../../../sites/hulu-flexbox/img/sky.jpg);
}

.Cat:nth-child(2) {
  background-image: url(../../../sites/hulu-flexbox/img/lake.jpg);
}

.Cat:nth-child(3) {
  background-image: url(../../../sites/hulu-flexbox/img/space-station.jpg);
}

.Cat:nth-child(4) {
  background-image: url(../../../sites/hulu-flexbox/img/forest.jpg);
}

.Cat:nth-child(5) {
  background-image: url(../../../sites/hulu-flexbox/img/wood-floor.jpg);
}

.Cat:nth-child(6) {
  background-image: url(../../../sites/hulu-flexbox/img/sparkler.jpg);
}