Pure CSS Material 3 (Part 1)
I hate JavaScript. It’s slow and annoying to use. So I made a pure-CSS implementation of the Material 3 components! That’s actually what this website is using.
A Variable Start
To start off, we need to gather all of the Material 3 variables. These variables store the colours, font weights, font sizes, etc. for our Material 3 app. I’ll walk through all of the steps for finding the variables here, but you can also find their default values in the GitHub repo for my website. They’re in _sass/variables.scss.
Colours
You can find all of the default Material 3 colours on the Material 3 Figma. I just took all of these variables and plugged them into a giant list of variables (cropped here for brevity):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
:root {
/* Surface colours */
--surface: #1c1b1f;
--surface-1: #25232a !important;
--surface-2: #2a2831;
--surface-3: #302d38;
--surface-4: #322e3a;
--surface-5: #35323e;
--on-surface: #e6e1e5;
/* Variant colours */
--surface-variant: #49454f;
--on-surface-variant: #cac4d0;
--outline: #938f99;
--outline-variant: #49454f;
/* Themed colours */
--primary: #d0bcff;
--on-primary: #381e72;
--primary-container: #4f378b;
--on-primary-container: #eaddff;
/* Cropped here, you get the idea */
}
These colours handle background colours at different elevations, as well as the themed colours for buttons, outlines, etc. You should store all of the colours for your theme (light or dark) - it’s a lot of variables, but you’ll use all of them. You probably won’t need the variables for the other theme, unless you plan on making that toggleable. I stuck with the dark theme colours.
Fonts
Material 3 recommends the Roboto font, so I used Roboto Flex (Roboto, but it’s a flex font). In addition, Material 3 has 5 typing levels - from largest to smallest, they are: Display, Headline, Title, Body, Label. Each typing level is designed for different use cases, and they have different sizes and weights. You can find information about each level, their sizes, and their weights here. I added the font weights and sizes for each typing level as variables as well:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
:root {
/* Colours above here */
/* Font sizes */
--display-large: 57px;
--display-medium: 45px;
--display-small: 36px;
/* Cropped again for brevity */
/* Font weights (it's all Roboto) */
--display-font: 475;
--headline-font: 400;
--title-font: 500;
--label-font: 500;
--body-font: 400;
}
State Layer
Something introduced in Material 3 is the idea of a “state layer” - effectively, a coloured layer that lies in between a component’s background and content. By default, the state layer is completely invisible; however, when certain events are triggered (hovering, clicking, etc), the state layer gains some opacity. This changes the component’s colour a little bit, visually showing the user that they are affecting the component. You can read more here, but for my purposes, I just stored the opacity level of the state layer for different events. It looks like this:
1
2
3
4
5
6
7
8
9
10
:root {
/* Colours above here */
/* Fonts above here */
/* State layer opacities */
--state-layer-opacity-hover: 8%;
--state-layer-opacity-focus: 12%;
--state-layer-opacity-press: 12%;
--state-layer-opacity-drag: 16%;
}
And that’s it for variables! I also stored the margin size, width of the navigation bar, and default shadow style for my site - but you might not need those for your application.
Making Components
This post is already quite long, so I’m going to stop here. In Part 2, we’ll start making some Material 3 components!
View my GitHub profile
View my Twitter profile
Email me
View my blog feeds