A presentation at CSS Day in in Amsterdam, Netherlands by Michelle Barker
Creative CSS Layout & the Flexible Web
About me • Senior Front End Developer at Ada Mode • Writer for Smashing Magazine, Codrops and CSS Tricks • CSS tinkerer
css-irl.info
CSS Layout in 2022 and beyond
Multi-column Flexbox Custom properties Container queries aspect-ratio min() / max() / clamp() Grid :has() Logical properties Viewport units Subgrid Writing modes
“ Intrinsic web design Jen Simmons ”
Flex or Grid?
Flexbox One dimension Flexible layout Two dimensions Strict alignment Gap support Layout based on content Layout based on context Grid
centred
Flexbox
Grid 1fr auto 1fr
Flexbox
https://codepen.io/michellebarker/pen/mdXPYLo
https://codepen.io/michellebarker/pen/mdXPYLo
codepen.io/michellebarker/pen/qBxNWZP
grid-template-columns: 1fr repeat(10, minmax(0, 6rem)) 1fr; codepen.io/michellebarker/pen/qBxNWZP
grid-template-columns: 1fr repeat(10, minmax(0, 6rem)) 1fr; codepen.io/michellebarker/pen/qBxNWZP
codepen.io/michellebarker/pen/qBxNWZP
Aspect-ratio
Before ☹ .aspect-box { position: relative; } .aspect-box::before { display: block; content: ”; width: 100%; padding-bottom: calc(100% / (var(—-aspect-ratio, 3 / 2))); } .aspect-box > :first-child { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } After ☺ .aspect-box { aspect-ratio: 3 / 2; }
.item { aspect-ratio: 1; } codepen.io/michellebarker/pen/bGLpOZz
img { display: block; width: 100%; height: 100%; object-fit: cover; } codepen.io/michellebarker/pen/bGLpOZz
img { display: block; width: 100%; height: 100%; object-fit: contain; } codepen.io/michellebarker/pen/QWQqggO
No aspect ratio? No problem! codepen.io/michellebarker/pen/bGLpOZz
.item { max-height: 20rem; } @supports (aspect-ratio: 1) { .item { aspect-ratio: 1; max-height: none; } }
⚠ NOT Progressive Enhancement
codepen.io/michellebarker/pen/bGLpzjd
codepen.io/michellebarker/pen/bGLpzjd
codepen.io/michellebarker/pen/YzeqRrP
3 2 codepen.io/michellebarker/pen/YzeqRrP
Flex or Grid?
.item { flex: 1 1 19rem; }
.item { flex: 0 1 19rem; }
.flex-grid { gap: 1.5rem; }
.grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } .item { grid-column: span 2; }
.item:last-child:nth-child(3n + 1) { grid-column: span 6; }
.item:last-child:nth-child(3n + 2) { grid-column: 4 / span 2; } .item:nth-last-child(2):nth-child(3n + 1) { grid-column: 2 / span 2; }
.grid { display: grid; gap: var(—pad-sm); grid-template-columns: repeat( auto-fit, minmax(min(19rem, 100%), 1fr) ); }
.grid { display: grid; gap: var(—pad-sm); grid-template-columns: repeat( auto-fit, minmax(19rem, 1fr) ); }
.grid { display: grid; gap: var(—pad-sm); grid-template-columns: repeat( auto-fit, minmax(min(19rem, 100%), 1fr) ); } Minimum size
.grid { display: grid; gap: var(—pad-sm); grid-template-columns: repeat( auto-fit, minmax(min(19rem, 100%), 1fr) ); } Maximum size
.grid { display: grid; gap: var(—pad-sm); grid-template-columns: repeat( auto-fill, minmax(min(19rem, 100%), 1fr) ); }
auto-fit auto-fill
.grid { display: grid; gap: var(—pad-sm); grid-template-columns: repeat( auto-fit, minmax(min(19rem, 100%), 1fr) ); }
Container queries
main, aside { container: inline-size / layout; } .grid { display: grid; gap: var(—pad); } @container layout (inline-size > 25em) { .grid { grid-template-columns: repeat(2, 1fr); } } @container layout (inline-size > 65em) { .grid { grid-template-columns: repeat(4, 1fr); } }
main, aside { container: inline-size / layout; } .grid { display: grid; gap: var(—pad); } @container layout (inline-size > 25em) { .grid { grid-template-columns: repeat(2, 1fr); } } @container layout (inline-size > 65em) { .grid { grid-template-columns: repeat(4, 1fr); } }
main, aside { container: inline-size / layout; } .grid { display: grid; gap: var(—pad); } @container layout (inline-size > 25em) { .grid { grid-template-columns: repeat(2, 1fr); } } @container layout (inline-size > 65em) { .grid { grid-template-columns: repeat(4, 1fr); } }
https://codepen.io/michellebarker/pen/OJQpVKE
container: inline-size / component;
gap: max(1rem, 2vw);
min, max and clamp min(1rem, 2vw)
min, max and clamp clamp(1rem, 2vw, 3rem)
:root { —pad: clamp(1rem, 2vw, 3rem); —pad-lg: calc(var(—pad) * 2); —pad-sm: calc(var(—pad) / 2); —pad-xs: calc(var(—pad) / 4); }
.wrapper * + * { margin-top: var(—pad); }
.wrapper * + * { margin-block-start: var(—pad); }
.wrapper :is(h1, h2, h3) + * { margin-block-start: var(—pad-sm); }
—pad: clamp(1rem, 2vw, 3rem);
—pad: clamp(1rem, 2vw, 3rem);
—pad: clamp(1rem, 2vw, 3rem);
Size Viewport width
utopia.fyi
Container-relative units cqw query container width cqh query container height cqi query container inline size cqb query container block size cqmin smallest of ‘cqi’ or ‘cqb’ cqmax largest of ‘cqi’ or ‘cqb’
.card h3 { font-size: clamp(1.2rem, 5cqi + 1rem, 3rem); }
Subgrid
.card { grid-row: span 3; display: grid; gap: 0; grid-template-rows: subgrid; }
.card { grid-row: span 3; display: grid; gap: 0; grid-template-rows: subgrid; }
No subgrid? No problem!
.card { /* Code for browsers without subgrid / } @supports (grid-template-columns: subgrid) { .card { grid-template-rows: subgrid; / Code for browsers that support subgrid */ } }
figure { display: grid; grid-template-columns: subgrid; grid-template-rows: subgrid; }
img { grid-row: 1 / span 3; }
figcaption { grid-column: 4 / span 2; grid-row: 2; }
:has() (the parent selector)
.grid blockquote { grid-column: 2 / span 4; grid-row: 2 / span 2; text-align: center; }
.grid:has(img) img { grid-row: 1 / span 2; grid-column: 1 / span 3; } .grid:has(img) blockquote { grid-row: 2 / span 2; grid-column: 3 / span 4; text-align: left; }
.grid { display: grid; grid-template: 1fr / repeat(var(—cols, 3), 1fr); grid-auto-rows: 1fr; }
.grid { display: grid; grid-template: 1fr / repeat(var(—cols, 3), 1fr); grid-auto-rows: 1fr; }
.grid { display: grid; grid-template: 1fr / repeat(var(—cols, 3), 1fr); grid-auto-rows: 1fr; }
.grid { display: grid; grid-template: 1fr / repeat(var(—cols, 3), 1fr); grid-auto-rows: 1fr; }
.grid:has(:last-child:nth-child(even)) { —cols: 2; }
.grid:has(:last-child:nth-child(5)) { —cols: 2; } .grid:has(:last-child:nth-child(5)) .item:first-child { grid-column: span 2; }
codepen.io/michellebarker/pen/xxYrWzZ
Diversity is strength
🥰 Thank you @MicheBarks / @CSSInRealLife
CSS layout has moved along in leaps and bounds in the past few years. Beyond flexbox and Grid, there is aspect-ratio, min, max and clamp functions, custom properties, and logical properties, all of which can help us solve common layout challenges. Plus a whole new range of features on the horizon (with some already landing in browsers!), including subgrid, container queries and the :has() pseudo-class (or “parent selector”). As developers, the challenge is no longer whether something can be done in CSS, but which of these tools to reach for in our CSS toolbox! This session will aim to bring you up to speed with modern CSS layout, and demonstrate some creative use cases.