Part 10: Style with CSS Grid and Flexbox [Beginner Tutorial: Learn Vue, CSS Grid and Flexbox]

Robert Mion
7 min readApr 15, 2018

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:

(Left): No CSS; (Right): All CSS

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

Click on the gear icon next to ‘CSS’ and select ‘SCSS’ from the ‘CSS Preprocessor’ dropdown menu

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:

  1. Turns the div into a grid-container and each section inside that div into a grid-item .
  2. Uses the grid-templates-areas property to establish a 3x4 grid (3 rows, four columns) with four uniquely named areas in which to place grid-item s. These areas have the aliases: output , modifiers , operators and digits . These names correspond precisely to the names of each section ‘s class name.
  3. For extra measure, we use grid-template-columns and the convenient repeat() function to explicitly declare this grid to have four columns, each sized 25% 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:

  1. I am ‘nesting’ style declarations inside one another.
  2. 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 an order of 0 by default. Any items assigned an order greater than 0 will appear after 0 -order items, and before items with an order greater than theirs. In addition, items assigned an order less than 0 will appear before 0 -order items, and after items with an order 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.

Thank you.

--

--