CSS Day clone by David Powell
03 Mar 2024 - Brent Jacobs
This post is following along to the Can I clone this fun effect from CSS Day using modern CSS? YouTube video by David Powell.
You can use the browser developer tools to inspect the below example. I will also show code snippets .
- Miriam Suzanne
: MC - Jeremy Keith
: MC - Tab Atkins
: Anchor Positioning - Josh Comeau
: How to Teach CSS - Rachel Andrew
: MC - Matthias Ott
: MC - Stephen Hay
: MC - Julia Miocene
: Character Modelling - Roel Nieskens
: Typography - Roma Komarov
: Impactful Experimentation - Sarah Dayan
: Utility First CSS - Carmen Ansio
: Scroll-Enhanced Experiences - Kevin Powell
: Writing Robust CSS
This clone demonstrates some interesting uses and quirks of grid. The code below defines a grid which has 5 columns and 5 rows.
.speakers-grid {
--column-count: 3;
--row-count: 5;
--column-size: 100px;
--row-size: 50px;
display: grid;
gap: 1rem;
grid-template-columns:
repeat(var(--column-count), var(--column-size)) 60px [list-start] 1fr [list-end];
grid-template-rows:
[list-start] auto repeat(var(--row-count), var(--row-size)) auto [list-end];
}
In the code above, repeat() is used along with the four CSS variables, --column-count, --row-count, --column-size, --row-size, to define the size of the grid that will contain the images of the speakers. A grid area called list has also been defined which spans the last column and all the rows. This will contain the list of speakers.
As the images are not child elements of the .speakers-grid grid, a hack needs to be introduced to place them with grid-template-columns and grid-template-rows. By positioning the images absolutely within the .speakers-grid grid, they can be positioned within the grid.
.speakers-grid {
position: relative;
img {
position: absolute;
left: 0;
top: 0;
}
li:nth-child(3n + 1) img {
grid-column: 1 / 2;
}
li:nth-child(3n + 2) img {
grid-column: 2 / 3;
}
li:nth-child(3n + 3) img {
grid-column: 3 / 4;
}
li:nth-child(-n + 3) img {
grid-row: 2 / 3;
}
li:nth-child(n + 4):nth-child(-n + 6) img {
grid-row: 3 / 4;
}
li:nth-child(n + 7):nth-child(-n + 9) img {
grid-row: 4 / 5;
}
li:nth-child(n + 10):nth-child(-n + 12) img {
grid-row: 5 / 6;
}
li:nth-child(n + 13):nth-child(-n + 15) img {
grid-row: 6 / 7;
}
}
One thing that stands out is the duplication required to set images to specific grid positions. Specifically, we need to know in advance how many rows of images there will be. In order for more rows of images to be added, additional styling is needed.
It would be nice to have access to the value of n to use in calculations within the style block. This could greatly simplify the CSS:
li img {
/* Calculate the column position */
grid-column: calc((n - 1) % 3 + 1) / span 1;
/* Calculate the row position */
grid-row: calc((n - 1) / 3 + 2) / span 1;
}
A transition is used to enlarge and reveal the full image on hover.
.speakers-grid {
--hover-animation-ms: 200ms;
img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 0.5rem;
transition: var(--hover-animation-ms);
}
a:is(:hover, :focus) {
img {
scale: 2;
z-index: 2;
width: initial;
height: initial;
object-fit: initial;
}
}
}
Due to the transition, the z-index changes abruptly when moving the cursor off of a link. This causes other images to be rendered on top of the collapsing image. An animation is used to correct this by setting the z-index for the duration of the transition.
.speakers-grid {
--hover-animation-ms: 200ms;
a:not(:hover, :focus) img {
animation: z-index-hack var(--hover-animation-ms);
}
@keyframes z-index-hack {
0%, 100% { z-index: 1; }
}
}
I have selected a brownish colour as a base for the text of the list. The link and link hover uses shades of this base colour. The shades are created using color-mix() with differing amounts of black or white.
.speakers-grid {
--primary-color: #986960;
--text-color: var(--primary-color);
--link-color: color-mix(in srgb, var(--primary-color), black 40%);
--link-hover-color: color-mix(in srgb, var(--primary-color), white 90%);
--link-hover-background-color: color-mix(in srgb, var(--primary-color), black 40%);
li {
color: var(--text-color);
}
a {
color: var(--link-color);
}
a:is(:hover, :focus) {
color: var(--link-hover-color);
background-color: var(--link-hover-background-color);
}
}