I use CSS grid and Sass, and I use different grid layouts for different screen sizes (phone
, tablet
, desktop
). But for some pages, I would like the same layouts but chosen at slightly bigger or smaller screens than for other pages.
Is something like that possible? Or am I approaching it from the wrong angle? My current solution (see below) works, but duplicates the styles a lot.
In more detail:
I have 3 different grids that are chosen according to the screen size.
.hero {
&__container {
grid-template-areas:
"header"
"image"
"text";
}
@media min-width: 1000px {
grid-template-areas:
"header header header"
"text ... image"
"text ... image";
}
// other definitions for this screen size
}
@media min-width: 1300px {
grid-template-areas:
"header header image"
"text ... image"
"text ... image";
}
// other definitions for this screen size
}
}
&__header {
grid-area: header;
font-size: 2.5rem;
@media min-width: 1000px {
font-size: 2.8rem;
}
@media min-width: 1300px {
font-size: 3.2rem;
}
}
...
}
They are used in about 20 similar web pages.
<div class="page_a">
<div class="hero">
<div class="hero__container">
<div class="hero__header">...</div>
<div class="hero__text">...</div>
<div class="hero__image">...</div>
</div>
</div>
</div>
The layout is very similar, but I would like to switch to different layouts at a different point based on the specifics of the content: the header text length, the size & importance of the image, etc.
What I would like to do is something like this:
.page_a {
.hero {
// redefine nothing, use defaults
}
}
.page_c {
.hero {
// the header is longer so we need a bigger screen to switch to the biggest layout
// somehow say that the 1300px layout should be used from 1500px
}
}
The only thing I managed to do is to simply redefine all the grids at each possible point (the default points + the custom points), which means the code is very repetitive:
.page_c {
.hero {
// use 1000px layout also for 1300px - the whole thing has to be repeated
@media min-width: 1300px {
grid-template-areas:
"header header header"
"text ... image"
"text ... image";
}
// other definitions for this size
}
// use 1300px layout for 1500px - the whole thing has to be repeated
@media min-width: 1500px {
grid-template-areas:
"header header image"
"text ... image"
"text ... image";
}
// other definitions for this size
}
}
}
Which means that every time I change some layout I have to go to all the places it is used at various size and change it too.
Your problem could be solved with SASS or SCSS, more precisely with a @mixin
. I'm using SCSS because I'm more familiar with it, but you could also use SASS.
As stated on the official SASS website:
Mixins allow you to define styles that can be re-used throughout your stylesheet.
First you define a @mixin
then you call it later in your code with an @include
. Every @mixin
should have a unique name. For example, define a @mixin layout_600
and call it with an @include layout_600
.
Two things are important when defining a @mixin
:
@mixin
should be defined before you call it with an @include
. Otherwise, SCSS will try to call something that isn't yet defined (it's defined later in your stylesheet).@mixin
should be defined outside of your nested code (ideally at the top of your stylesheet). If you define a @mixin
inside your nested code, you won't be able to call it later when you want to change default styles. The easiest way for you to understand what I mean is to show you the correct way and the wrong way.Correct:
@mixin layout_600 {
font-size: 3rem;
color: blue;
font-weight: 700;
}
.hero {
&__header {
@media (min-width: 600px) {
@include layout_600;
}
}
}
.page_b {
.hero {
// Use the 600px layout also for the 1000px
&__header {
@media (min-width: 1000px) {
// This will work
@include layout_600;
}
}
}
}
Wrong:
.hero {
&__header {
@media (min-width: 600px) {
@mixin layout_600 {
font-size: 3rem;
color: blue;
font-weight: 700;
}
}
}
}
.page_b {
.hero {
// Use the 600px layout also for the 1000px
&__header {
@media (min-width: 1000px) {
// This will not work
@include layout_600;
}
}
}
}
You need to write a @mixin
for each layout that you want to have (e.g., 600px
). You only need to do it once for every layout, but you can call a particular @mixin
n-times. This is perfect because:
@mixin
with an @include
as many times as you want.@mixin
. The style will be changed in every place that is referring to this @mixin
.I defined three @mixin
s like this:
600px
,600px
< window width < 1000px
and1000px
.As you can see, the font-size
and color
are different at different window widths. The font is getting bigger, and the color goes from black to blue to red as the window gets wider. By the way, in the right upper corner, I added a div
that shows the current window width.
I decided that for page_b
I would use the 600px
layout (i.e., @mixin layout_600
) also for the 1000px
. This can easily be done by calling the @mixin layout_600
with the @include layout_600
like this:
.page_b {
.hero {
// Use the 600px layout also for the 1000px
&__header {
@media (min-width: 1000px) {
@include layout_600;
}
}
}
}
As you can see, the style of the page_b
when the window width is actually 1000px
is the same as if the window width was 600px
(i.e., smaller font and blue color).
Also, it's possible to customize a @mixin
if you want. For example, I used the 600px
layout (i.e., @mixin layout_600
) but changed the color
from red to green. This can be done like this:
.page_b {
.hero {
// Use the 600px layout also for the 1000px
&__header {
@media (min-width: 1000px) {
@include layout_600;
color: green; // Customize the mixin
}
}
}
}
As you can see, the color
should be blue (as in @mixin layout_600
), but it's green.