Stephan Nijman
Posted on February 14, 2022
It's not really cheating
If you have spend any time on tech twitter lately you have probably noticed the 100devs hashtag, and people being a bit "loud" about having to build the BBC homepage using floats only. Now i'm not part of the 100devs cohort but i got curious and it turns out it's actually a pretty interesting layout. So i build the thing anyway.
Since i didn't have to keep to the assignment i could throw all kinds of modern css magic at it, and in this article i want to share with you some of the more interesting things i used.
To be clear. I'm not saying these are the best ways of doing this, they are just the ways i did it.
Enough talk... let's write some code...
Placing the text over the images
One of those interesting things that i got asked about after i shared my results on twitter is how i placed the text on top of the images. Instead of using floats or position absolute i used the power of css grid.
<div class="grid">
<div class="grid__image">
<img src="https://unsplash.it/500/300" alt="">
</div>
<div class="grid__content">
Content goes here
</div>
</div>
With this html we create a container div with a class of grid, and two inner div's called grid_image and grid_content. These inner divs are the elements we will place on top of eachother
.grid {
display: grid;
grid-template: 1 / 1;
}
By attaching the css above to the .grid div we create our actual grid that is one row high and one column wide. An important concept here is that even though our grid now has only one "cell" it has 4 grid lines like shown in the image below.
Here you can see that our grid has a grid line for the left, top, right and bottom side. We can place grid items in between these grid lines by giving them a css grid-area property.
.grid__image {
grid-area: <left> / <top> / <right> / <bottom>;
}
To place the items we have to tell the grid item between which grid lines it should place itself by giving it a grid-area property and giving it the four grid line numbers. The example above shows you what the order of these values should be.
.grid__image {
grid-area: 1 / 1 / 2 / 2;
}
.grid__content {
grid-area: 1 / 1 / 2 / 2;
}
With the code above we tell both the .grid_image and the .grid_content div's to place them self between the first en second horizontal grid lines, and between the first en second vertical grid lines.
Because we place them at the same location in the grid, they will be placed perfectly on top on one another.
Pushing the text to the bottom.
In the BBC layout the text is positioned at the bottom of the image. In my demo i used flex for this, but later on i realized that i could have used grid's align-self for this task.
.grid__content {
grid-area: 1 / 1 / 2 / 2;
align-self: end;
padding: 1rem;
}
Here we added the align-self property with a value of end, which will push anything inside the .grid__content to the bottom of the div. We also give it a padding of 1rem so the text has a little breathing room.
Change the stacking order.
The order of the html determines the stacking order of your items. If you ever run into a situation where you cant change the html but want to change the stacking order, you can use z-index natively within a grid. (No need to set a position value).
.grid__content {
grid-area: 1 / 1 / 2 / 2;
align-self: end;
padding: 1rem;
z-index: 10
}
Creating the responsive hero grid
The thing that made me want to build this layout was the 5 image grid/hero header at the top of the page. Since it was an exercise i tried a view different approaches and ended up with a grid system as described below.
<div class="hero">
<div class="hero__item"></div>
<div class="hero__item"></div>
<div class="hero__item"></div>
<div class="hero__item"></div>
<div class="hero__item"></div>
</div>
Although simplified here the html is pretty straightforward. Its just a .hero container with 5 .hero__item divs, To get the grid on desktop i used a grid-template.
.hero {
display: grid;
grid-template:
"large large small-one small-two" auto
"large large small-three small-four" auto / 1fr 1fr 1fr 1fr;
gap: 0.5rem 1rem;
}
Here we first make the .hero div a grid container by setting the display mode to grid.
We then define the grid-template. This might be a bit confusing at first but basically what happens here is that the template is constructed of two lines. Each line creates a couple of named grid-areas going from left to right. Each line can end with a height for that row (Set to auto in this case) and the last line can have a height, and it can define the sizes for each column. In this example we set each column to have a width of 1fr.
This grid-template creates a "virtual" grid like shown in the image below.
Note that it has four areas with the name large! To place our .grid__items onto the grid, we have to give all five of them a grid-area to be placed in.
.hero__item:nth-of-type(1) {
grid-area: large;
}
.hero__item:nth-of-type(2) {
grid-area: small-one;
}
.hero__item:nth-of-type(3) {
grid-area: small-two;
}
.hero__item:nth-of-type(4) {
grid-area: small-three;
}
.hero__item:nth-of-type(5) {
grid-area: small-four;
}
Here we use the :nth-of-type(n) pseudo selector to select each item seperatly without the need to give them a specific class. We then set the grid-area properties to align with the names we defined earlier in our grid template.
Note that the first .grid__item gets a grid-area "large". And since that name maps to four grid-areas in out grid-tempalte it will cover all four of them, resulting in a layout like shown in the image below.
And to make it responsive we just need some media queries to move the grid area's around.
.hero {
display: block;
width: 100%;
}
@media screen and ( min-width: 568px ) {
.hero {
display: grid;
grid-template:
"large large" auto
"small-one small-two" auto
"small-three small-four" auto / 1fr 1fr;
gap: 0.5rem;
}
}
@media screen and ( min-width: 1000px ) {
.hero {
grid-template:
"large large small-one small-two" auto
"large large small-three small-four" auto / 1fr 1fr 1fr 1fr;
gap: 0.5rem 1rem;
}
}
Here we take a mobile first approach and set the whole thing to display block by default.
We then use a media query that kicks in at a screen width of 568px and sets the container to display grid, and defines the grid areas for tablet users like shown in the image below.
And finally we use another media query for screens bigger than 1000px and change the grid-template to the one we created earlier.
All this combined creates a nice grid that changes according to the width of our screen.
Tip: If you want to learn more about css grid, or need a more visual explanation, i can highly recommend you check out this and other videos by Kevin Powell
Hiding the images on mobile
Another aspect of the layout that required a creative solution is the fact that most of the images disappear on mobile devices.
Hiding images in itself isn't all that hard. We can just set a display: none; and call it a day! But our mobile users wont be very happy with us since the browser is still gonna load the images regardless.
So i reached for the html picture element. This picture element lets us define image sources for different screen sizes. Like shown below.
<picture>
<source srcset="https://unsplash.it/id/101/300/169" media="(min-width: 1000px)">
<source srcset="https://unsplash.it/id/101/500/350" media="(min-width: 568px)">
<source srcset="/bbc/hidden.gif" media="(min-width: 1px)">
<img src="https://unsplash.it/id/101/500/350" alt="…" loading="lazy">
</picture>
The magic here is that we define images for desktop, tablet and mobile screen sizes, but for the mobile view we give it a tiny 1px by 1px transparent gif image. This image will not only be invisible in our layout, but since we use the same file in every instance the browser only has to download this tiny 43 byte image once. Saving our user a large amount of bandwidth and speeding up our webpage.
Tip: Mike Masey has a article describing a similar technique more in depth!
Hiding parts of the navigation
As you can see in the little video at the top of this article i tooks some creative liberty with the navigation by adding an overlay menu and hiding parts of the horizontal nav when the screen size gets smaller.
To do this i had to hide a range of menu list items. To do so i used this funky looking css selector.
.header__list li:nth-child(n+5):nth-child(-n+9) {
display: none;
}
Here we select all li's inside the .header__list that meet the requirements of the two nth-of-type pseudo selectors. The first nth-of-type selects all li's starting from the fifth, and the second selects all li's ending at the ninth. So everything in between these numbers will be selected.
We then set them to display: none; by default.
.header__list li:nth-child(n+5):nth-child(-n+9) {
display: none;
}
@media screen and ( min-width: 568px ) {
.header__list li:nth-child(n+5):nth-child(-n+7) {
display: inline-flex;
}
}
@media screen and ( min-width: 1000px ) {
.header__list li:nth-child(n+8):nth-child(-n+9) {
display: inline-flex;
}
}
To show more items on tablet we add a media query for screen sizes bigger then 568px. Inside this media query we select li number 5 till 7 and set there display mode to inline-flex to make them visible again.
We also do the same at 1000px with another media query and select li numbers 8 till 9 and set their display mode to inline-flex as well.
This will make more items show up on bigger screens then on small/mobile devices.
Tip: if you want to know more about css pseudo-selectors i have a video on youtube that explains the basics. And
Jhey Tompkins has a great video about more advanced use cases.
Changing the text color for the navigation
In this layout, like with most sites there is a lot of switching between black and white text colors. The page text is black but the text in the navigation and over the iamges is all in white.
Back in the day we would have to set these colors for every element, but with the arrival of css custom properties we can do all this with one line of code per section.
:root {
--text-color: #111111;
}
Here we declare a custom property called --text-color on the root element and set its value to a dark grey color.
h1,
h2,
h3,
p,
a {
color: var(--text-color);
}
Next we use this variable to set the color for our text elements using the css var() function.
This will make all the text on our page that dark grey color. But in the navigation/header of our page we need our text to be white.
.header {
--text-color: #ffffff;
}
Since we have all our text colors set up using the custom property we can re declare it with a new color for that section alone. This will make every element inside the header use that white color instead of the dark grey one.
Tip: I have a youtube video explaining this technique in more detail!
Thats it...
Even though this wasn't my actual homework i had a lot of fun with this one. Since i wasn't on the clock i could experiment with different approaches and other things then usual. If nothing else i hope this article has inspired you to try this yourself.
It can be hard to find interesting things to make so you could always check out what the 100Devs people are doing (don't forget to show them some support), or you can check out the Frontend Mentor website. They have a lot interesting projects with ready made assets.
You can view a live version of my BBC homepage recreation here, and all the source code for this little project is available on my
GitHub
If you liked this article don't forget to follow me on Twitter or here on Dev.to @Vanaf1979 for more things to come.
Thanks for reading, stay safe and stay the right kind of positive!
Posted on February 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 27, 2024