Week 2: Lookin’ Good with CSS
Continuing with the physical building analogy from Week 1, if HTML represents the frame and structure of a website or web app, then CSS is the paint, wallpaper, artwork, fixtures, furniture, and overall style.
CSS stands for Cascading Style Sheets and represents the presentational layer in the web development stack. It allows us to customize how our HTML markup is styled and displayed by browsers. With CSS we can determine color, layout, typography, and other aspects of how our web projects appear.
CSS Syntax
The basic syntax of CSS is fairly straightforward, but can become increasingly complex as our project requires. This is the good and bad of CSS — it’s relatively easy to get started and grasp basic concepts, but its flexibility and power make it equally difficult to fully grasp. The deeper your knowledge of CSS grows, the more you realize there is to learn.
“ CSS is like chess: easy to learn, but hard to master.” - Stephen Hay
CSS syntax begins with a selector, which represents the element or elements in our HTML markup we want to style. CSS selectors can range from simple element selectors (such as h1
) to complex, multi-level selectors (body > div.content-section p#intro-paragraph
, for example). We’ll dive deeper into CSS selectors later in this lesson.
After choosing a selector for our styles, we create a code block using opening and closing curly braces like this: {}
. Inside of the code block, we write a declaration, which consists of a property-value pair ending in a semicolon. Each property-value pair is responsible for styling a specific part of our markup (e.g. text size, image width, content layout, background color, etc.).
All of these parts together — selector, code block, and declaration — make up a CSS rule.
Let’s take a look at each part of the above CSS rule in further detail:
h1#main-heading
is the selector of the CSS rule. It would target anh1
element with anid
attribute ofmain-heading
in our markup (e.g.<h1 id="main-heading">
).- Inside of the rule’s code block there are two declarations:
font-size: 30px;
andcolor: blue;
. The first declaration sets the font size of the selector to 30 pixels. The second declaration sets the selector’s font color to blue.
Here’s a diagram of a basic CSS rule with the each bit of the syntax labeled:
CSS Selectors
As mentioned earlier, CSS selectors allow us to target the element or elements in our HTML markup to be styled. The most basic selectors (aka “simple selectors”) generally fall into one of three categories, each with varying levels of specificity (we’ll talk more about specificity later on in this lesson):
Type Selectors
Type selectors include tag/element selectors (like h1
, p
, or body
), as well as pseudo-elements (like ::before
and ::after
), which we won’t go into in this lesson. Type selectors have the lowest level of specificity among simple selectors.
We use type selectors to match all elements of a given type within our markup. For example, if we wanted to make the <h2>
headings blue in the following HTML markup:
Our CSS would look like this:
Class Selectors
Class selectors match elements based on the class
attribute value. They are more specific than Type selectors, but less specific than ID selectors. If we wanted to update the background color and font size of both <article>
elements in our example markup, we’d write the following rule in our CSS:
ID Selectors
ID selectors have the highest level of specificity among the simple selector types we’ve discussed. They match elements based on the value of id
attributes. Going back to our example markup, to change the font family in the second <article>
element to Raleway, we’d write the following CSS rule:
Child and Descendant Selectors
In addition to the three main categories of selector types outlined above, there are combinators that allow us to combine simple selectors in order to target elements with even greater specificity.
The two types of combinators we’ll focus on are Child and Descendant selectors. Both of these selectors tap into the structural relationships between elements in our HTML markup that we discussed in Week 1 (Relationships and Nesting).
Child selectors target the direct child elements nested inside of a given element using the >
symbol. For example, if we wanted to change the text color of the <p>
elements of both <article>
elements in our example markup, we’d use the child selector like this:
Descendant selectors operate in a similar way, except that they select all elements that are descendants (i.e. nested inside) of a given element. Rather than a >
symbol like we use for child selectors, descendant selectors simply use a space to indicate the intended target relationship in CSS.
Say we wanted to set all of the <p>
elements in our example markup to bold. With the descendant selector, our CSS would be:
To help stay on top of the different CSS selector types, freeCodeCamp has a handy CSS Selector Cheat Sheet. Another good resource is MDN’s comprehensive list of CSS Selectors. It’s conveniently organized by selector types (simple selectors, combinators, pseudo-classes, etc.). I highly recommend bookmarking both of these!
Inline, Internal, and External CSS Styles
There are three main ways to write CSS and apply styles to our HTML markup: inline, internal, and external CSS styles.
Inline CSS
Inline CSS is written inside of our markup, with styles being applied directly to individual HTML elements using the style
attribute. Here’s an example of styling HTML with inline CSS:
While technically valid, this method of applying CSS styles to our markup is not preferred and is generally considered bad practice. For starters, it contradicts the Separation of Concerns principle of computer science, which basically states that computer programs should be divided into distinct sections that handle unique responsibilities. In our case, that means separating the structural layer (HTML) from the presentational (CSS) and behavioral (JavaScript) layers.
Another downside to inline CSS is that it doesn’t allow us take advantage of inheritance, a major concept of CSS that makes our code more flexible and helps avoid duplication (we’ll discuss this concept later in this lesson). With inline CSS, we have to write the same CSS rules to every element we want the rules applied to. In the above inline CSS example, we would have to add the style="color: blue;"
attribute to every p
element in our markup that we wanted to be blue. Obviously, this isn’t very scalable.
Internal CSS
Another way to apply styles to HTML markup is by writing internal CSS. These style rules are also written in the same HTML file as our markup, but they grouped together between <style></style>
tags that are nested inside the head
section of our markup.
Rather than adding style
attributes to individual elements, the CSS rules we write inside of the <style></style>
tags will be applied to every matching element on the page. Check out the example below of internal CSS:
Internal CSS styles are preferred over inline styles because they take advantage of inheritance and make our code more flexible. However, we’re still not separating our concerns with internal CSS, which makes it less than ideal. Also, since internal styles only apply to HTML elements on the same page, it means we have to re-write the same styles for every page of markup in a web project. Again, not very scalable at all.
External CSS
Finally, we have external CSS. This is the preferred way to style HTML markup, as it separates structure (HTML) from presentation (CSS) and makes our code much more flexible and easier to maintain.
With external CSS, we write our styles once in a separate stylesheet (e.g. style.css
) and link to this external stylesheet in the head
section of any HTML document in our project using the <link>
element.
For example, if we have the following markup in an HTML document:
Then our style.css
stylesheet might look like this:
Inheritance, Specificity, and Cascade
These concepts make up the “holy trinity” of CSS and are the key to unlocking the awesomeness of writing CSS. Inheritance, specificity, and cascade may seem like fuzzy ideas at first, but don’t worry — the more CSS you write, the more these concepts will make sense.
Inheritance
The concept of inheritance refers to the passing on of CSS styles from parent to child and descendant elements based on the structural relationships in our HTML markup. Some property styles are not inherited by default (such as border
, margin
, padding
, and all background
properties), but for those properties that are, we are able to write far fewer CSS rules in order to style our markup.
Take a look at the example CSS below. Here we are applying some font styles to the <body>
element that will be inherited by all its descendant elements unless we specify otherwise.
Because we’ve written alternative declarations for <h1>
elements with a class
of main-heading
, as well as all <p>
elements, those rules will only inherit the CSS declarations from our body
rule that have not been explicitly overridden. For example, both the h1.main-heading
and p
rules will inherit the font-family: sans-serif;
declaration, with the p
rule also inheriting the color
and font-weight
declarations from body
.
Specificity
Specificity is another important concept in understanding which CSS rules are ultimately applied to our markup. As we discussed earlier in this lesson, CSS selectors have varying degrees of specificity. Here are the CSS selectors we’ve covered so far, in order of least to most specific:
- Type Selectors (e.g. tag/element selectors like
h1
,p
,body
, etc.) - Class Selectors (e.g.
.class-name
) - ID Selectors (e.g.
#id-name
)
So, if we had the following <p>
element in our markup:
And our stylesheet contained these CSS rules:
Our “Hello World!” sentence would have:
- Green text (
color: green;
declaration in id selector wins out) - 18 pixel font size (
font-size: 18px;
declaration in id selector wins out) - Oswald font (
font-family: 'Oswald';
declaration in class selector wins out) - Normal font weight (
font-weight: normal;
declaration in type (element) selector is applied)
Cascade
It shouldn’t come as a surprise that the cascade is a key concept of CSS —it represents the “C” in the acronym! The cascade is all about the order in which CSS rules are applied to our markup. CSS rules will be applied in a cascading, top-down fashion in the following order:
- Importance: Any declarations marked as
!important
will always win out over any other conflicting declarations. Best practice is to use!important
sparingly, if at all, in your CSS rules.
- Specificity: Once declarations marked as !important have been applied, CSS will look next to specificity to in applying styles. As we know, CSS selectors have varying degrees of specificity. If conflicting CSS rules exist for a given element or set of elements, the more specific selectors will win out (i.e. have their styles applied) over less specific ones.
- Source Order: Finally, if there are multiple CSS rules with the same importance and degree of specificity, the winning rule will be the one that is declared last in the source order of the CSS stylesheet. In other words, when all else is equal, the CSS cascade will select the last rule declared.
Size, Spacing, and The Box Model
A helpful rule of thumb to help in understanding the interplay between HTML and CSS is this: Every HTML element is a box. As we learned in Week 1, these “boxes” will have different default characteristics depending on whether they are block or inline elements.
We can take these default characteristics even further with CSS, manipulating the size and spacing properties of our element “boxes” such as width
, height
, border
, margin
, and padding
in order to establish a rudimentary layout of our page content.
Width and Height
The width
and height
properties are applied to an element in our markup to explicitly declare the element’s size. These declarations will override the element’s default block or inline width and height values, and define the actual size of the content in our markup (text, images, etc.).
We set the value of an element’s width
and height
properties in either absolute (pixels, points, centimeters, millimeters, inches, etc.) or relative (rem/em units, percentages) length units. You’ll probably see pixels, rems/ems, and percentages used most often.
For example, if we had the following CSS:
We’d see something like this rendered in the browser:
Margin, Border, Padding
Spacing outside, inside, and between given elements can be declared with the margin
, border
, and padding
properties. The margin
property creates space around a given element, while the padding
property creates space inside an element. The border
property defines the outer edge of an element’s content and padding, and can be have a specified width, color, style, etc.
Each of these properties can be set independently of each other, and can also be declared on one, some, or all sides of an element. Additionally, depending on the property and value, shorthand declarations can sometimes be used to add margin, border, or padding to an element:
Going back to our blue box example, if we update our stylesheet to include declarations for margin, border, and padding like so:
Our rendered markup would now look like this:
The Box Model
Grouped together, the properties above make up the CSS box model. The box model is a representation of how CSS declarations for margin, border, padding, and the actual content will ultimately be rendered in the browser.
The key thing to understand about the box model is how the total width and height of an element are calculated according to the box-sizing
property. By default, the box-sizing
property has a value of content-box
. This means that any values for border or padding will be calculated in addition to the total height and width of the content. Conversely, setting the box-sizing
property to border-box
ensures values for border or padding are calculated along with the values for the content’s width and height.
Take a look at the example below. Both Box 1 and Box 2 have the same width (250px). Because Box 1 has box-sizing
set to content-box
, however, the actual width rendered by the browser also includes the border (5 pixels) and padding (40 pixels) values, giving Box 1 an actual width of 340 pixels.
Box 2 has its box-sizing
property set to border-box
. This means that regardless of the border and padding values set, the element will only ever be as wide or as tall as the values of the width
and height
properties. In this case, the actual width of Box 2 matches the width
property value of 250 pixels.
MDN provides a good explanation of content-box
and border-box
:
“
content-box
gives you the default CSS box-sizing behavior. If you set an element’s width to 100 pixels, then the element’s content box will be 100 pixels wide, and the width of any border or padding will be added to the final rendered width.
border-box
tells the browser to account for any border and padding in the values you specify for an element’s width and height. If you set an element’s width to 100 pixels, that 100 pixels will include any border or padding you added, and the content box will shrink to absorb that extra width. This typically makes it much easier to size elements.”
Spend some time playing around with this Box Model Diagram on CodePen to see how the rendered size of elements changes between content-box
and border-box
.
To make our lives easier when working with the box model (and decrease the amount of math we have to do), the best practice is to declare box-sizing: border-box;
for all elements in our project using the universal selector (*
) like so:
Display and Positioning
The display
and position
properties allow us to move elements around and control the layout of our HTML markup.
Display
Layout in CSS starts with the display
property. In Week 1 we learned that most HTML elements are divided into two categories: block
and inline
. Block elements (e.g. <div>
, <body>
, <p>
, etc.) begin on a new line and take up the full width available. Inline elements (e.g. <span>
, <a>
, <img>
, etc.) work within the flow of the surrounding content and only take up the required width.
Here’s what the rendered markup would look like for the block and inline styles above:
There are several other display values besides the default block
and inline
, although the only other one you’re likely to work with is display: none;
. This declaration hides the element and removes it completely from the flow document.
Position
Next up we have the position
property, which as you probably guessed, establishes the position of a given element. There are four main position values that we’ll focus on:
position: static;
is the default position for all HTML elements. Elements withposition
ofstatic
are rendered in the order that they appear in the flow of the markup, and are said to be “not positioned.” Elements with any of the other position values below are said to be “positioned.”position: relative;
elements behave the same way as those withposition: static;
until we add properties to thetop
,right
,bottom
, andleft
properties (e.g.top: 20px;
orright: 1.2rem;
). Setting values for these properties will move an element away from its normal position.position: absolute;
is the trickiest of the four to understand. Elements withposition; absolute;
can take advantage of the sametop
,right
,bottom
, andleft
properties, but setting values will move the element relative to its nearest positioned ancestor. If there aren’t any positioned ancestors, then absolutely positioned elements will adjust their position relative to the document body (i.e. the<body>
element).position: fixed;
elements are pretty straight forward. They are positioned relative to the viewport using thetop
,right
,bottom
, andleft
properties, and will remain fixed in place during scrolling.
Let’s take a look at what all of this might look like when rendered in the browser:
Exercises
Resources
- CSS Syntax: A good introduction to CSS syntax from Mozilla Developer Network (MDN).
- CSS Basics: The Syntax That Matters & The Syntax That Doesn’t: CSS-Tricks is one the best resources available for developers looking for help with CSS concepts, and this CSS syntax guide is no exception.
- How CSS Selectors Work: Solid summary of how different types of CSS selectors can be used to style markup.
- CSS Diner: A fun little exercise to get some extra practice using CSS selectors.
- Specifics On CSS Specificity: High-level overview of the ins and outs of specificity in CSS.
- SpeciFISHity: Visual aid that shows CSS selector specificity (going from least to most specific selector).
- Cascade and Inheritance: Comprehensive overview of the concepts of cascade and inheritance from MDN.
- Learn CSS Layout: Position: Great overview of the position property and what each value does in terms of laying out your content.
- CSS Position: More information on the position property from CSS Tricks.