Bastion Fennell
Posted on March 22, 2019
This is the second part of a three part series:
Part 1, Layout with HTML
Part 2, Styling with CSS
Part 3, Interaction with JS
In this part, we'll be learning CSS! In the first part of this series, we built out the bare layout of our clicker game using HTML. Now we're going to take that layout and make it look better!
When I initially got into web development, CSS was one of the first things to really hook me. Being able to really manipulate what the users are seeing and interacting with... It was like magic! And just like magic, the rules can be complex and arcane. Nevertheless, it gets the job done!
With any luck, by the end of this post our clicker game will look like this:
If you're already familiar with CSS, take a stab at styling it yourself! You'll see we did add a little bit more content as well, just to make the game more user friendly. The general HTML is the same shape as what we built in part 1 though.
What is CSS?
CSS stands for Cascading StyleSheets. It is essentially a set of rules that tells the browser how the HTML should be rendered. The idea is that HTML is the content, while CSS is the presentation.
Separating out presentation and content is also a way to help make our sites more understandable and accessible for things like screen readers. They can just pick up what they need from the HTML and ignore the CSS!
So, what does it look like? Typically a little something like this:
body { // tag
background-color: black;
color: white;
}
.upgrade-button { // classname
width: 200px;
height: 50px;
}
#header { // id
height: 50%;
}
First, you put an identifier followed by {}
, then put the rules to apply to that identifier between the {}
. What are these identifiers? Well, they can be tags like <div>
or <body>
. They can also be what are called class names or IDs. Classes and IDs are attributes that you can define on an HTML element so that you can apply specific styles to it.
<div class="container wide" id="main-container"></div>
These attributes are a space separated list of identifiers. Classes can appear multiple times on the page, so they're good for recurring elements. IDs, however, should only appear once on a page. Use it to make sure that you only have one of an element.
In CSS, we use a .
to say we're styling a class, and a #
to say we're styling an ID. So, to style the div above we could use:
-
div {}
to style based on the tag -
.container {}
, or.wide {}
to style based on the classes -
#main-container {}
to style based on the ID
Cascading
You can also combine identifiers to make them more specific. For example, div.container.wide {}
will only target div tags that have both the container and wide class applied to them.
If multiple styles apply to the same tag, the more specific one will win. This is where the term cascading comes from. If your stylesheet looked like
div {
background-color: black;
width: 100px;
}
div.wide {
width: 200px;
}
Then any div will get that initial div styling. If you add the wide
class to a div it will still get background-color: black
but the width: 200px
will override the width that only applies to div tags.
Another way to add specificity is by selecting elements within other elements. If you add a space between the selectors, you're telling the browser, "anything within this element that matches this selector should get these styles."
div.wide a { }
This will target any <a>
tag within a <div class="wide">
and apply the stylings. If you had another styling that just targeted <a>
tags, it would override them.
There are a lot of rules to remember with CSS, but there's just one last one that we care about right now. If two selectors match something with the same specificity, the last one in the stylesheet will be applied.
div.wide {
width: 200px;
}
div.override {
width: 150px;
}
If you had a <div class="override wide">
then the override styles would be applied instead of the wide ones.
That's enough theoretical stuff for now. If you want to dig into CSS specificity more, check out this post by Chris Coyier on exactly this topic!
Getting Our Classes in Order
Let's get to building!
If you followed along with our last post, you probably have something that looks a little bit like this:
So how do we make it look a little bit nicer? When styling, the first thing we should do is organize our HTML with classes that are clear and understandable. That way, when you come back to your code, it's easy to understand what was going on.
Let's take our first section, with the actual clicker button. If you look at the pen (codepen example) at the beginning of the post, we want to make the part with the clicker button more prominent. Luckily, it's already in its own div! We just need to add the class names.
// Original
<div>
Lines of Code: 0
<div>
<button> Write Code </button>
</div>
</div>
// With classes
<div class="clicker">
Lines of Code: 0
<div class="clicker__button-area">
<button class="clicker__button"> Write Code </button>
</div>
</div>
Looking at that pen again, you'll see we did make a few tweaks to the layout itself. Let's adjust our HTML to match that new layout while we're here. You'll hardly ever get the layout right on your first try, but one of the awesome things about web development is how fast the feedback loop is! Change something, check the browser, adjust it, check it, etc. etc.
<div class="clicker">
<div class="clicker__button-area">
<div> 0 Lines of Code </div>
<button class="clicker__button"></button>
</div>
</div>
Perfect! Now, the next two sections of "buyables" are pretty much the same, so we're going to use the same classes for both of them.
// Original
<div>
Number of Extra Hands: 0
Price: 10
<div>
<button> Buy Hands </button>
</div>
</div>
// With Classes
<div class="buyable">
Number of Extra Hands: 0
Price: 10
<div class="buyable_button-area">
<button class="buyable__button"> Buy Hands </button>
</div>
</div>
You might ask yourself why I'm writing the classes like this? These classes are roughly built on the BEM or block, element, modifier spec. It's a good way to keep your classes organized!
If we take another look at the first pen, you'll see we made some changes to this section as well! So, let's update the layout to match. Let's also wrap the whole section in a new div to differentiate it from the clicker section.
<div class="buyables">
<div class="buyable">
<div class="buyable__amount"> 0 </div>
<div class="buyable__text">
<div class="buyable__title">
<span class="buyable__title-text"> Extra Hands </span>
<span class="buyable__title-price"> 10 Lines of Code </span>
</div>
<div class="buyable__description"> More hands on the keyboard = more typing </div>
<div class="buyable__effect"> Adds 1 line of code per click </div>
</div>
<button class="buyable__button"> Buy </button>
</div>
...
</div>
We should be able to use these same classes to update the other buyable as well! Go ahead and do just that.
Actually Writing CSS
If all goes well, we should have something that looks like this.
Still looks kind of ugly, but we're only dealing with the skeleton for now. Let's go ahead and get into the CSS and make it all pretty!
First, let's add a stylesheet.css
file right next to our index.html
file. This is the file that we'll be writing all of our CSS into. Now, we need to tell the HTML where to get its' styles from. In the <head>
of the HTML, add
<link rel="stylesheet" type="text/css" href="stylesheet.css" />
This tells the HTML what the stylesheet is and where it is, so it'll actually load our styles onto the page. While we're adding links, let's also use one to bring in the Google Open Sans font
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
Before we go any further, let's check to make sure that worked. In your CSS, just put:
body {
background: red;
}
Now, if you load up the page, you should immediately regret the decision and be bombarded by bright red. Now that we know they're linked, let's remove that line and add some nicer backgrounds for each section.
.clicker {
background: #002b35;
}
.buyables {
background: #fdf6e3;
}
The colors we're using come from a palette called Solarized that I use development all the time. I figured it would be fitting for a game about writing code.
There we go! We got the basic colors done, but it's all weirdly proportioned. Let's fix that. We want each main section to take up half the page, which we can easily do with the height
attribute;
.clicker {
background: #002b35;
height: 50%;
}
.buyables {
background: #fdf6e3;
height: 50%;
}
Now there's a weird margin around the page still. Turns out, browsers add a default margin
. The margin
property defines the amount of extra space you want around the component. We'll go into a little bit more detail on that later, but for now, let's just override it.
body {
margin: 0;
}
While we're at it, let's update the font-family to the open sans that we imported earlier!
body {
font-family: "Open Sans";
margin: 0;
}
Perfect, now we have our canvas written out! Next step, let's tackle the top section. We want to have our lines of code and button content centered. You have a few options with this, but we'll use the one most of the industry is trending towards, flex-box. Flex box has a lot of intricacies, but for now think of it as a way to lay out content that behaves predictably.
We'll want our main clicker area to be a flex box with some added attributes to make it center both horizontally and vertically.
.clicker {
display: flex; // Makes this div a flexbox
align-items: center; // This centers things vertically
justify-content: center; // This centers things horizontally
background: #002b35;
height: 50%;
}
That should have centered our content, now let's actually style that content. This introduces you to three new concepts:
-
border-radius
the roundness of the corners of a div. -
padding
the space between the div content and its border. For spacing, in order you have padding, border, then margin. -
opacity
how transparent the div is
.clicker__button-area {
background: #fdf6e3;
border-radius: 8px;
opacity: .8;
padding: 30px;
}
Last touches here, let's do some text formatting. We can use font-size and text-align to make our button a bit clearer and nicer looking.
.clicker__button-area {
font-size: 24px;
text-align: center;
background: #fdf6e3;
border-radius: 8px;
opacity: .8;
padding: 30px;
}
As a final touch for this second, let's make the button look like a keyboard! I just pulled this image from the wikipedia commons to use for our button. Let's add a public/
folder next to index.html
and put this at public/keyboard.png
.
When styling things like buttons or dropdowns, you have to override some of the browser's default styling. In this case, the default background, border, and outlines. The only new thing here is outline, which is basically a way to show users what element has focus.
.clicker__button {
background-color: #fdf6e3;
border: none;
outline: none;
}
Now, let's introduce the idea of a background-image here as well. There are a few different ways to do images, but this will show us how to manipulate backgrounds! The main strange thing here is background-size. We're using background-size: cover
, which will stretch the image to fill the entire container. For a few other options to play with, check out the Mozilla docs.
.clicker__button {
background-image: url("public/keyboard.png");
background-size: cover;
height: 150px;
width: 250px;
background-color: #fdf6e3;
border: none;
outline: none;
}
Great, if all went well here, it should look something like this:
We've actually already pretty much used all of the CSS properties we'll need for the next section, with the exception of font-weight
and some higher level flexbox shenanigans. Go ahead and try to style the rest of the app yourself before continuing down this page!
The Rest of the Owl
All that's left really is the "buyables" half of the screen. This is where everything buyable lives, hence the name. Right now it's just extra hands and code monkeys, but down the road we might have more items and even things like upgrades here!
When styling, I like to work my way from the biggest container down to the smallest one. Since we already styled the .buyables
area, that means the next step is the buyable itself.
Since we're reusing classes here, if we style one card correctly it should style the other ones the way we want as well! Let's start by defining the size of the buyable cards.
.buyable {
width: 600px;
height: 75px;
}
Right now we're giving everything fixed heights and widths for the sake of simplicity. This means we're essentially styling to a single page width. What we really want is for users to be able to access our apps from any device, meaning any screen width, and it still look good. This idea is called Responsive Design and it's becoming more and more important to consider.
That being said, we'll save that for another time! For now, let's introduce you to borders!
Borders wrap around our tags and sit between the padding and the margin. They have a few properties that you can define on them, like border-radius
that you've already been introduced to. A few others are:
border-color: white | red | #123456;
border-width: thin | thick | 10px;
border-style: solid | dotted | dashed;
Most of the properties are self explanatory, they tell the browser the color, size, and type of border you want. It's a lot to type out every time we want a border though. Luckily, there's some shorthand we can use!
border: 10px solid #123456;
This lets us define all three properties at once! Using that, let's give our cards a border now.
.buyable {
border: 2px solid #002b36;
border-radius: 8px;
width: 600px;
height: 75px;
}
Finally, let's add some padding to give our inner tags some breathing room, and margin to give the cards themselves some space.
.buyable {
padding: 10px 20px;
margin: 10px;
border: 2px solid #002b36;
border-radius: 8px;
width: 600px;
height: 75px;
}
This introduces you to some new shorthand! We've seen padding: 30px
before as a way to add spacing, but what does it mean when there are two values? padding
is shorthand similar to border
, except that it's used for the more explicit values:
padding-top
padding-right
padding-bottom
padding-left
If there's just one value, it defines all of them to be that value. If there are two values, it's equivalent to this:
padding: 1 2;
padding-top: 1;
padding-right: 2;
padding-bottom: 1;
padding-right: 2;
The way I remember which ones define which is that the first value always defines the top. You can also define all four, which will go in clockwise order starting from the top.
padding: 1 2 3 4;
padding-top: 1;
padding-right: 2;
padding-bottom: 3;
padding-left: 4;
Last step, let's declare these cards as a flexbox container so we can arrange the inner tags a little easier!
.buyable {
display: flex;
padding: 10px 20px;
margin: 10px;
border: 2px solid #002b36;
border-radius: 8px;
width: 600px;
height: 75px;
}
With this, we should have something a little like this.
Uh oh, what's going on with that white space between our two sections? Turns out there's a thing called margin collapsing that can make margins behave unexpectedly! To solve this, we'll add an overflow: auto
to the .buyables
div. The overflow
property tells a tag what to do if certain content goes outside of its box, but in our case it solves our collapsing margin issue.
.buyables {
overflow: auto;
background-color: #fdf6e3;
height: 50%;
}
We've got our card container defined, now all that's left is the inside! Let's start all the way left with the amount. The first new flexbox property we're going to introduce here is align-self
. This property tells the tag how to align itself vertically in this flexbox, similar to align-items
earlier, so we can more easily center it.
.buyable__amount {
align-self: center;
font-size: 24px;
}
Let's move on to the text section! The second flexbox concept we're introducing here is flex-grow
. flex-grow
is a way to tell the browsers which tags we want it to expand within the flexbox first. It's a bit more complex than that, but that's good enough for now. We're going to use it to make the text section fill up all the space between amount and the buy button, minus a small margin.
.buyable__text {
flex-grow: 1;
margin: 0 20px;
}
Perfect! Let's dig into that text section, shall we?
We have two elements within it that we want arranged fairly specifically, so we're going to use another flexbox.
Let's reintroduce you to one more flex box property, justify-content
. This property tells the flex box how to position the elements within it relative to each other. We're using space-between
to keep our elements are far from each other as possible.
We're also going to use the more specific margin-bottom
property, similar to the padding-bottom
we talked about earlier.
.buyable__title {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
}
Within this title section, we're changing the fonts a bit. We want the title to stand out, so we're going to bold it using font-weight
. Some fonts let you get more specific with their weight, but we just need bold for now!
.buyable__title-text {
font-weight: bold;
}
Just add a smaller font-size to the effect, and we've only got one thing left!
.buyable__effect {
font-size: 12px;
}
Now all we have is the button! You can probably do this one by yourself, go give it a shot! If you want to get it pixel perfect like our codepen at the top of the page though, here's the CSS.
.buyable__button {
align-self: center;
background: none;
border: 2px solid #859900;
border-radius: 8px;
color: #859900;
font-size: 20px;
height: 30px;
width: 80px;
}
We Made It!
Success! Our final product!
That was a lot to slog through, I know. However, these are the exact same things I use at work every day. With only a handful of exceptions, all of the CSS properties I need are contained within this post.
What I really want to try and get across though is how to think about CSS. Starting from the outside and working your way in. Starting to look at a website and get an idea of how you'd need to break something up to organize it correctly for styling.
Try taking what you learned here and rebuilding something that you've seen in real life! A tweet, a dashboard, maybe even this post! Try and tackle it like we have so far in the series. Break down what you think the HTML should be, build out the skeleton, apply the classes, then add styling.
In our next post in this increasingly long winded series, we'll add some interactivity to our app using Javascript!
Posted on March 22, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 23, 2024