Replicating Hulu’s Categories Mosaic with Flexbox
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
-
New on Hulu
Hulu with Live TV
Show Title
logo
-
Groundbreaking
Hulu Originals
Show Title
logo
-
New and Classic
Movies
Show Title
logo
-
Current
Seasons
Show Title
logo
-
Exclusive
Past Seasons
Show Title
logo
-
For All Ages
Kids
Show Title
logo
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);
}