Part 10: Style with CSS Grid and Flexbox [Beginner Tutorial: Learn Vue, CSS Grid and Flexbox]
For those that skipped here hoping to grab the final code…
It’s at the end of Part 9. Go nuts. Shame on you.
First, a before and after of Part 10:
In other parts, I discouraged copy-pasting. Here, I encourage it.
I will still do my best to explain how CSS Grid and Flexbox properties help achieve the final layout as I introduce each code snippet.
Update you Codepen to use a CSS Preprocessor
Step 1: Remove default spacing and make page span entire viewport
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
Step 2: Center the app on the page with Flexbox
body {
display: flex;
justify-content: center;
align-items: center;
}
display: flex
turns the body
into a flex container
. The body
has a single child, a div
with the id
of app
. justify-content: center
centers the div
horizontally. align-items: center
centers the div
vertically. This all was only possible by making the body
and the html
have a width
and height
of 100%
.
Step 3: Remove most default styles from all buttons
button {
margin: 0;
padding: .75rem .25rem;
border: none;
}
The buttons will now look like normal text on the page. We will style them more appropriately soon.
Step 4: Create a grid, assign named areas, and set column sizes
#app {
display: grid;
grid-template-areas:
"output output output output"
"modifiers modifiers modifiers operators"
"digits digits digits operators";
grid-template-columns: repeat(4, 25%)
}
This declaration targets our div
with the id
of app
and specifies three pivotal statements:
- Turns the
div
into agrid-container
and eachsection
inside thatdiv
into agrid-item
. - Uses the
grid-templates-areas
property to establish a 3x4 grid (3 rows, four columns) with four uniquely named areas in which to placegrid-item
s. These areas have the aliases:output
,modifiers
,operators
anddigits
. These names correspond precisely to the names of eachsection
‘sclass
name. - For extra measure, we use
grid-template-columns
and the convenientrepeat()
function to explicitly declare this grid to have four columns, each sized25%
of the available space allotted.
After adding this CSS to your app, things will look all kinds of funky. Not to worry, in the next step everything will literally fall into place when we assign each section to the appropriate grid-template-area
.
Step 5: Place each section in its corresponding grid area
section {
&.output {
grid-area: output;
}
&.modifiers {
grid-area: modifiers;
}
&.digits {
grid-area: digits;
}
&.operators {
grid-area: operators;
}
}
If this syntax looks confusing, read this note. If you’re used to SCSS, feel free to skip this section
Using a CSS preprocessor like SCSS lets us write smarter CSS. In this example, I am using two key features of the SCSS preprocessor:
- I am ‘nesting’ style declarations inside one another.
- I am using a special character
&
as a convenient reference to the parent selector
For comparison, the above SCSS will compile to the following CSS:
section.output {
grid-area: output;
}
section.modifiers {
grid-area: modifiers;
}
section.digits {
grid-area: digits;
}
section.operators {
grid-area: operators;
}
This may not seem like a big deal yet, but as I expand upon each section — nesting additional selectors within each — it becomes an incredible time-saver.
After adding these CSS rules, your calculator should look a bit less funky.
Output section
section {
&.output {
grid-area: output;
background-color: #666;
color: #fff;
padding: 0 1rem;
> * {
margin: 0;
} h1 {
font-size: 3rem;
line-height: 0;
}
}
}
The above code snippet is everything inside .output
unrelated to CSS Grid or Flexbox.
section {
&.output {
grid-area: output; background-color: #666;
color: #fff;
padding: 0 1rem;
display: flex;
align-items: baseline; > * {
margin: 0;
} h1 {
font-size: 3rem;
line-height: 0;
order: 1;
margin-left: auto;
}
}
display: flex
turns section.output
into a flex-container
and each of the two headings into flex-items
. align-items: baseline
vertically aligns both headings to their respective baselines, so they appear to share a common ground. order: 1
causes the h1
to appear after the h2
even though it comes first in the HTML. margin-left: auto
pushes the h1
to the far right side of .output
.
Modifiers section
section {
&.modifiers {
grid-area: modifiers; button {
background-color: #ddd;
font-size: 1.4rem;
}
}
}
Again, the above code shows all non-Grid and -Flexbox related styles.
section {
&.modifiers {
grid-area: modifiers; display: flex; button {
background-color: #ddd;
font-size: 1.4rem; flex: 0 0 33.3%;
}
}
}
We turn section.modifiers
into another flex-container
and thereby each button
into a flex-item
. The flex
property sets three properties simultaneously: flex-grow: 0
, flex-shrink: 0
and flex-basis: 33.3%
. Combined, these prevent the buttons from taking up any size other than 33.3% of their containing space, which itself takes up three columns, or 75% of its containing space. So here, each button takes up roughly 25% of the horizontal space within #app
.
Operators section
section {
&.operators {
grid-area: operators; display: flex;
flex-direction: column; button {
background-color: orange;
font-size: 1.4rem;
color: #fff;
}
}
}
Here’s everything for section.operators
. The new bit from Flexbox is flex-direction: column
. This in essence instructs the browser to layout all buttons inside this section vertically instead of horizontally.
Flexbox has two axis: a main axis and a cross axis. By default, flex-direction
is set to row
and the main axis runs horizontally while the cross axis runs vertically. Items always naturally flow in the direction of the main axis. So, setting flex-direction
to column
swaps the axis, making the cross-axis run horizontally and the main axis run vertically. But items always flow in the direction of the main axis, so here they flow vertically.
Digits section
I’ll break this section into three parts:
Flex-wrap: wrap-reverse
section {
&.digits {
grid-area: digits; display: flex;
flex-wrap: wrap-reverse;
}
}
By default, flex-items
will flow left-to-right and not wrap onto new lines. We want items to wrap, so we could just set flex-wrap: wrap
to make the digits flow top-to-bottom, too. But we want the digits to flow left-to-right, bottom-to-top. Luckily, flex-wrap: reverse
does just the trick.
But we’re not done quite yet.
Setting up the buttons
section {
&.digits {
grid-area: digits; display: flex;
flex-wrap: wrap-reverse; button {
background-color: #efefef;
font-size: 1.4rem; flex: 0 0 33.3%;
}
}
}
Thanks to flex
and it’s three bundled properties, almost all buttons are now the correct size. But one is out of order, and another needs to double in size.
Sizing and positioning the 0 and decimal point
section {
&.digits {
grid-area: digits; display: flex;
flex-wrap: wrap-reverse; button {
background-color: #efefef;
font-size: 1.4rem; flex: 0 0 33.3%; &:first-child {
flex-basis: 66.6%;
order: -2;
}
&:last-child {
order: -1;
}
}
}
}
The syntax &:first-child
is the same as button:first-child
. Here, :first-child
is targeting digit 0
and :last-child
is targeting the decimal point, because of their order in the HTML.
flex-basis: 66.6%
makes the 0
grow to double the other buttons’ size.
order
works as follows:
- All
flex-item
s are assigned anorder
of0
by default. Any items assigned anorder
greater than0
will appear after0
-order items, and before items with anorder
greater than theirs. In addition, items assigned anorder
less than0
will appear before0
-order items, and after items with anorder
less than theirs.
In this case, I want the decimal point to appear before all digits except 0
. Therefore, since all digits start with an order
of 0
, I have to assign the decimal point an order
of -1
and the digit 0 an order
of -2
to make it appear first, in front of the decimal point.
Full CSS for the app
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: #000;
}body {
display: flex;
justify-content: center;
align-items: center;
}button {
margin: 0;
padding: .75rem .25rem;
border: none;
}#app {
display: grid;
grid-template-areas:
"output output output output"
"modifiers modifiers modifiers operators"
"digits digits digits operators";
grid-template-columns: repeat(4, 25%)
}section {
&.output {
background-color: #666;
color: #fff;
padding: 0 1rem; grid-area: output; display: flex;
align-items: baseline; > * {
margin: 0;
} h1 {
font-size: 3rem;
line-height: 0; margin-left: auto;
order: 1;
}
} &.modifiers {
grid-area: modifiers; display: flex; button {
background-color: #ddd;
font-size: 1.4rem; flex: 0 0 33.3%;
}
} &.digits {
grid-area: digits; display: flex;
flex-wrap: wrap-reverse; button {
background-color: #efefef;
font-size: 1.4rem; flex: 0 0 33.3%; &:first-child {
flex-basis: 66.6%;
order: -2;
}
&:last-child {
order: -1;
}
}
} &.operators {
grid-area: operators; display: flex;
flex-direction: column; button {
background-color: orange;
font-size: 1.4rem;
color: #fff;
}
}
}
You’re done!
I hope your app works and looks as expected.
I hope you learned a lot about Vue, CSS Grid and Flexbox.
If you did, I hope you’ll share this tutorial with others.