Comprehensive Guide To Web Development
Comprehensive Guide To Web Development
DEVELOPMENT
BY
SUMMARY .............................................................................................................................. 26
SETUP....................................................................................................................................... 28
PAGE TITLES........................................................................................................................... 31
PARAGRAPHS ......................................................................................................................... 32
HEADINGS .............................................................................................................................. 34
UNORDERED LISTS............................................................................................................... 35
SUMMARY .............................................................................................................................. 47
SETUP....................................................................................................................................... 49
EXTRAS PAGE..................................................................................................................... 51
ANCHORS ................................................................................................................................ 52
LINKS ....................................................................................................................................... 52
ABSOLUTE LINKS.............................................................................................................. 55
IMAGES.................................................................................................................................... 62
CHARACTER SETS............................................................................................................. 69
QUOTES ............................................................................................................................... 71
SUMMARY .............................................................................................................................. 72
SETUP....................................................................................................................................... 74
UNITS OF MEASUREMENT.................................................................................................. 81
UNDERLINES ...................................................................................................................... 86
SUMMARY .............................................................................................................................. 94
SETUP....................................................................................................................................... 97
SUMMARY .............................................................................................................................119
SETUP..................................................................................................................................... 120
SETUP..................................................................................................................................... 146
SETUP..................................................................................................................................... 171
SETUP..................................................................................................................................... 200
SETUP..................................................................................................................................... 239
SETUP..................................................................................................................................... 258
ADDRESS............................................................................................................................... 276
SETUP..................................................................................................................................... 281
SETUP......................................................................................................................................311
The friendliest first encounter you’ll ever have with web development
Learning HTML and CSS is hard, but it doesn’t have to be. This 14-chapter tutorial is one
of the friendliest HTML and CSS guides. We’ll walk you through everything from selecting
a good text editor (which is surprisingly important) to building full-fledged, professional-
quality web pages from scratch.
We designed HTML & CSS Is Hard to be the only introduction to HTML and CSS that
you’ll ever need. If you put in the effort to read every section and write every code snippet,
this tutorial has the potential to replace hundreds or even thousands of dollars’ worth of
online courses and live training.
Our goal is to make it as easy as possible for complete beginners to become professional
web developers, so if you’ve never written a line of HTML or CSS, but you’re
contemplating a career shift, grab a cup of coffee, take a seat, and let’s get to work.
Think of HTML as the abstract text and images behind a web page, CSS as the page that
actually gets displayed, and JavaScript as the behaviors that can manipulate both HTML
and CSS.
For example, you might mark some particular run of text as a paragraph with this HTML:
Then, you can set the size and color of that paragraph with some CSS:
p{
font-size: 20px;
And, if you want to get fancy, you can re-write that paragraph when the user clicks it with
some JavaScript (we’ll save the fancy stuff for a future tutorial):
var p = [Link]('some-paragraph');
[Link]('click', function(event) {
[Link] = 'You clicked it!';
});
As you can see, HTML, CSS, and JavaScript are totally different languages, but they all refer
to one another in some way. Most websites rely on all three, but the appearance
of every website is determined by HTML and CSS. That makes this tutorial a great starting
point for your web development journey.
WEB PUBLISHING
So, what is it to “learn” HTML and CSS? We like to look at it through a historical lens into
the printing industry. Back in the days of the original printing press, printers created
documents by arranging metal characters, dipping them in ink, and pressing them onto a
piece of paper.
In a lot of ways, that’s exactly what web developers do, except instead of arranging
moveable type, they write HTML and CSS. We’re concerned with the same task as they
were: conveying content in meaningful ways. We even deal with the same presentational
issues they did, like selecting the font to use, setting the size of headings, and determining
the space between lines of text.
This tutorial is about HTML and CSS fundamentals. You’ll walk away with the ability
build pretty much anything you’ll ever need as a web developer with raw HTML and CSS.
HANDS-ON LEARNING
Interneting Is Hard is all about hands-on education. With the exception of what you’ve
already read, the entirety of this tutorial revolves around concrete examples, explaining the
conceptual aspects of HTML and CSS along the way.
To get the most of out this tutorial, you should be actively creating web pages and following
along with every single step of each chapter. If you’re serious about becoming a web
developer, you should be typing each code snippet character-by-character instead of copy-
and-pasting them into your text editor.
Why? Because this is what you’ll actually be doing as a real web developer. Typing out
code examples ingrains the muscle memory that will serve you well once you’re out in the
wild and marking up content for real websites.
The only real prerequisite for a good web browser is that it’s up to date and in mainstream
usage. Chrome and Firefox are favorites amongst web developers. Safari is alright if
you’re running OS X, too. We strongly suggest not creating websites with Internet
Explorer. Professional web development often requires an efficient way to test code on all
of these browsers, but that’s a little more complicated than what we need right now.
If you don’t already have Atom, go ahead and download it now, since you’ll be needing it
for the next chapter. Once you’ve downloaded it, open it up so we can take a brief tour of
its major features. You should see two panes with different welcome screens:
We don’t need either of these welcome screens, so close both of them by clicking the x icon
in their corresponding tabs. You can also use the Cmd+W (Mac)
or Ctrl+W (Windows/Linux) shortcut to close them (shortcuts are great, use them whenever
you can). You should be left with a single untitled tab.
CREATING A PROJECT
You should now see a sidebar on the left of the interface that says hello-atom at the top
next to a little folder icon. This is our file browser. Of course, it won’t show anything until
we add some files, so let’s do that next.
Let’s make one more file by hitting Cmd+N (Mac) or Ctrl+N (Windows, Linux). This
will create another untitled tab. As with our last file, add whatever text you want, then
save it as [Link].
That’s all fine and dandy for browsing files, but there are a lot of times when
you’re searching for a specific file. For instance, imagine discovering a broken link on
your website while you’re doing some quality assurance. You want to be able to jump into
that file with Atom to fix the link as quickly as possible.
MULTIPLE PANES
Atom not only lets you have multiple tabs, but multiple panes as well. To see what we’re
talking about, try right-clicking one of the files in the file browser and selecting Split
Right. This will open that file in a new pane, allowing you to see multiple files at the same
time.
Multiple panes are really useful for examining a CSS file and its related HTML file at the
same time.
From here, you can add new files, create folders, or open up HTML files in a web browser.
That last one is going to become a common task for the rest of this tutorial, so let’s give it
a shot with our [Link] file. Right-click it in your system’s default file browser and
select Open With > Chrome/Firefox/Safari. You should see whatever text you added to
the file rendered as a web page in your default web browser.
Now, you can edit the contents of [Link] in Atom, save it, and reload it in your web
browser by pressing Cmd+R (Mac) or Ctrl+R (Windows, Linux). This is the basic editing
workflow for all web developers, and you’ll become very, very accustomed to it by the
time you’re done working through the next 13 chapters.
The Atom stuff we just covered is going to become part of our daily routine, so make sure
you’re comfortable with everything before moving on. We encourage you to play with the
example project a bit, adding more files and practicing navigating from one to the other.
Mastering the craft of writing HTML and CSS is what distinguishes amazingly productive
developers from average ones.
Now that we’ve got a proper text editor, we’re ready to start coding up some real web
pages. We’ll start by exploring the most common HTML elements over the next two
chapters, then we’ll add some CSS to the mix.
HTML defines the content of every web page on the Internet. By “marking up” your
raw content with HTML tags, you’re able to tell web browsers how you want different
parts of your content to be displayed. Creating an HTML document with properly
marked up content is the first step of developing a web page.
In this chapter, we’ll build our first web page. It’ll look like crap because it won’t have
any CSS attached to it, but it will serve as a thorough introduction to the HTML
elements that web developers work with on a daily basis.
As you work your way through the examples, try to approach them as a more hands-on
version of a WYSIWYG editor like Google Docs or Microsoft Word. We’ll be working
SETUP
Let’s get started by creating a new project with Atom called basic-web-pages. Then,
make a new file called [Link] in that folder. This HTML file represents a single
web page, and it’s where we’ll put all our code for this chapter. If you’re not already
set up with Atom, be sure to read the Introduction for this tutorial series.
Remember that the basic workflow for web developers is to edit HTML in their text editor
and view those changes in a web browser, so this is exactly what you should be doing for
each section of this chapter.
<!DOCTYPE html>
First, we need to tell browsers that this is an HTML5 web page with the <!DOCTYPE
html> line. This is just a special string that browsers look for when they try to display our
web page, and it always needs to look exactly like it does above.
Then, our entire web page needs to be wrapped in <html> tags. The actual <html> text is
called an “opening tag”, while </html> is called a “closing tag”. Everything inside of these
tags are considered part of the <html> “element”, which is this ethereal thing that gets
created when a web browser parses your HTML tags.
The purpose of this <head>/<body> split will become clearer in a few chapters after we
start working with CSS.
PAGE TITLES
One of the most important pieces of metadata is the title of your web page, defined by the
aptly named <title> element. Browsers display this in the tab for your page, and Google
displays it in search engine results.
<!DOCTYPE html>
<html>
<head>
<title>Interneting Is Easy!</title>
</head>
<body>
</body>
</html>
Notice how all the HTML tags in our web page are neatly nested. It’s very important to
ensure that there are no overlapping elements. For instance, the <title> element is supposed
to be inside of the <head>, so you’d never want to add the closing </head> tag before the
closing </title> tag:
PARAGRAPHS
Titles are all well and good, but let’s do something we can actually see. The <p> element
marks all the text inside it as a distinct paragraph. Try adding the following <p> element
to the body of our web page:
<!DOCTYPE html>
<html>
<head>
You should now be able to see some content on the page. Again, since this is content we
want to display, it needs to go in the <body> element, not <head>.
Also note how the <p> and <title> elements are indented twice,
while <body> and <head> are indented once. Indenting nested elements like this is an
important best practice that makes your HTML easier to read for other developers (or for
yourself if you come back 5 months from now and want to change some stuff).
It’s up to you and your development team to decide if you want to use spaces or tab
characters for indents. You can set this preference in your text editor under Atom >
Preferences > Editor and scrolling down to the Tab Type setting.
The first heading on a page should typically be an <h1>, so let’s insert one above our
existing <p> element. It’s very common for the first <h1> element to match the <title> of the
document, as it does here:
<body>
<h1>Interneting Is Easy!</h1>
<p>First, we need to learn some basic HTML.</p>
</body>
By default, browsers render less important headings in smaller fonts. For example, let’s
include a second-level heading and see what happens:
<!DOCTYPE html>
<html>
<head>
<title>Interneting Is Easy!</title>
</head>
<body>
This should result in a web page that looks something like this:
Headings are the primary way you mark up different sections of your content. They define
the outline of your web page as both humans and search engines see it, which makes
selecting relevant headings essential for a high-quality web page.
UNORDERED LISTS
Whenever you surround a piece of text with HTML tags, you’re adding new meaning to
that text. Wrapping content in <ul> tags tells a browser that whatever is inside should be
rendered as an “unordered list”. To denote individual items in that list, you wrap them
in <li> tags, like so:
<ul>
<li>Add a "ul" element (it stands for unordered list)</li>
<li>Add each item in its own "li" element</li>
<li>They don't need to be in any particular order</li>
</ul>
After adding this markup to the <body> element (underneath the existing content), you
should see a bulleted list with a dedicated bullet for each <li> element:
The HTML specification defines strict rules about what elements can go inside other
elements. In this case, <ul> elements should only contain <li> elements, which means you
should never ever write something like this:
<!-- (This is bad!) -->
<ul>
<p>Add a "ul" element (it stands for unordered list)</p>
</ul>
How do we know that <ul> only accepts <li> elements and that <li> allows nested
paragraphs? Because the Mozilla Developer Network (MDN) says so. MDN is a superb
HTML element reference. We’ll try to cover as much as we can about how to use basic
HTML elements in this tutorial, but whenever you’re not sure about a particular element, do
a quick Google search for “MDN <some-element>”.
ORDERED LISTS
With an unordered list, rearranging the <li> elements shouldn’t change the meaning of the
list. If the sequence of list items does matter, you should use an “ordered list” instead. To
create an ordered list, simply change the parent <ul> element to <ol>. Append the
following content to the Lists section of [Link]:
Step-by-step procedures like recipes, instructions, and even tables of contents are good
candidates for ordered lists, while <ul> lists are better for representing item inventories,
product features, pro/con comparisons, and navigational menus.
For instance, <p> is a block-level element, while <em> is an inline element that affects a
span of text inside of a paragraph. It stands for “emphasis”, and it’s typically displayed as
italicized text. Try adding a new section demonstrating emphasized text to our example
web page:
The part wrapped in <em> tags should render as italics, as shown below. Notice how only
part of a line has been affected, which is characteristic of inline elements. In the CSS Box
Model chapter, we’ll discover how inline and block elements can have a dramatic impact on
the layout of a page.
Just in case it hasn’t sunk in yet, it’s really important that you properly nest your HTML
elements. It’s easier to mess up the order of tags when you’re using multiple inline
elements, so make sure to double-check that your markup never looks like this:
To draw even more attention your a span of text, you can nest a <strong> element in
an <em> element (or vice versa). This will give you text that is both strong and
emphasized:
As the example text suggests, this is effectively the typographic equivalent of shouting.
Have a read through the Web Typography chapter before going too crazy with the bold and
italic fonts.
The pseudo-obsolete <b> and <i> elements are classic examples of this. They used to stand
for “bold” and “italic”, respectively, but HTML5 attempted to create a clear separation
As we’ll discover in Hello, CSS, we can alter the browser’s default rendering of
the <strong> and <em> elements. This furthers the point that we shouldn’t call it out as
italicized or bold text in the HTML—that’s something for CSS to decide.
LINE BREAKS
HTML condenses consecutive spaces, tabs, or newlines (together known as “whitespace”)
into a single space. To see what we’re talking about, add the following section to
our [Link] file:
<h2>Empty Elements</h2>
<p>Regards,
The Authors</p>
This behavior may seem counter intuitive, but web developers often set their text editor to
limit line length to around 80 characters. As a programmer, it’s easier to manage code this
way, but having each of the newlines show up in the rendered page would severely mess
up the intended page layout.
To tell the browser that we want a hard line break, we need to use an explicit <br/> element,
like this:
<p>Regards,<br/>
The Authors</p>
The <br/> element is useful anywhere text formatting matters. Haiku, music lyrics, and
signatures are just a few examples where it might come in handy.
HORIZONTAL RULES
The <hr/> element is a “horizontal rule”, which represents a thematic break. The transition
from one scene of a story into the next or between the end of a letter and a postscript are
good examples of when a horizontal rule may be appropriate. For instance:
<h2>Empty Elements</h2>
<p>Regards,<br/>
The Authors</p>
<hr/>
One of the themes for this chapter has been the separation of content (HTML) from
presentation (CSS), and <hr/> is no different. Like <em> and <strong>, it has a default
appearance (a horizontal line), but once we start working with CSS, we’ll be able to render
it as more space between sections, a decorative accent character, or pretty much anything
else we want.
Like <br/>, <hr/> should carry meaning—don’t use it when you just want to display a line
for the sake of aesthetics. For that, you’ll want to use the CSS border property, which we’ll
discuss in a few chapters.
Another way to think about the <hr/> element is that it carries less significance than the
separation created by a new heading element, but more significance than a new paragraph.
It doesn’t really make a difference which convention you choose, but pick one and stick
to it for the sake of consistency. In this tutorial, we’ll be including the trailing / character
because it clearly shows that it’s a self-closing element. This will help prevent your eyes
from searching for the closing tag elsewhere in the document.
SUMMARY
This chapter may have seemed like an endless list of HTML elements, and, well, it basically
was. HTML is pretty simple when it comes right down to it. Web pages are made up of
HTML elements, each element adds a different meaning to the text it contains, and
elements can be nested inside of each other.
What we did in this chapter is always the first step in the web development process—you
need to define what you want to say (HTML) before defining how you want to say it (CSS).
Hopefully, the [Link] file we created in this chapter will serve as a useful quick-
reference of core HTML elements.
In this chapter, we’ll create a simple website composed of several HTML documents
and image files. It may be entitled Links and Images, but the central theme of this
chapter is actually around file and folder organization. As we start working with
multiple files, we’ll discover the importance of being an organized web developer.
SETUP
This chapter is all about linking web pages together, so we’ll need to create some new
HTML files before we code anything up. We’ll be working with three separate web
pages this chapter, along with a few image files of various formats:
LINKS PAGE
Next, add a new file to that folder called [Link] and insert the following HTML
template. This should be familiar to you from the previous chapter.
<!DOCTYPE html>
<html>
<head>
<title>Links</title>
</head>
<body>
<h1>Links</h1>
</body>
</html>
IMAGES PAGE
In the same folder, create another file called [Link]:
<!DOCTYPE html>
<html>
<head>
EXTRAS PAGE
Our last page will help us demonstrate relative links. Create a new folder in links-and-
images called misc, then add a new file called [Link]:
<!DOCTYPE html>
<html>
<head>
<title>Extras</title>
</head>
<body>
<h1>Extras</h1>
</body>
</html>
Note that you can create a new folder in Atom by right-clicking the file browser pane
and selecting New Folder in the contextual menu. Life is better when you never need
to leave your text editor.
IMAGE DOWNLOADS
We’ll be embedding images in our [Link] file, so be sure to download
these example mochi images, too. Unzip them in your links-and-images folder, keeping
the parent images folder from the ZIP file. Your project should now look like this:
If you load the page in a web browser, you’ll notice that the <a> element doesn’t look
like a link at all. Yes, unfortunately, the <a> element on its own doesn’t do much of
anything.
LINKS
In the same way that an element adds meaning to the content it contains, an HTML
“attribute” adds meaning to the element it’s attached to.
Notice how attributes live inside the opening tag. The attribute name comes first, then
an equal sign, then the “value” of the attribute in either single or double quotation
marks. This syntax distinguishes attributes from content (which goes between the tags).
The extra bit of information provided by the href attribute tells the browser that
this <a> element is in fact a link, and it should render the content in its default blue text:
Absolute, relative, and root-relative links refer to the value of the href attribute. The
next few sections will explain how and when to use each of them. But first, let’s add
the following content to our [Link] file:
<p>This particular page is about links! There are three kinds of links:</p>
<ul>
<!-- Add <li> elements here -->
</ul>
For example, try creating a link to the Mozilla Developer Network’s HTML element reference:
It’s possible to use absolute links to refer to pages in your own website, but hard-coding your
domain name everywhere can make for some tricky situations. It’s usually a better idea to reserve
absolute links only for directing users to a different website.
Here’s how we can link to our [Link] file from inside of [Link]:
<li>Relative links, like to our <a href='misc/[Link]'>extras
page</a>.</li>
In this case, the href attribute represents the file path to [Link] from
the [Link] file. Since [Link] isn’t in the same folder as [Link], we need to
include the misc folder in the URL.
Each folder and file in a path is separated by a forward slash ( /). So, if we were trying
to get to a file that was two folders deep, we’d need a URL like this:
misc/other-folder/[Link]
When you click either of those links in a web browser, it will complain that the page
doesn’t exist. Examining the address bar, you’ll discover that the browser is trying to
load misc/[Link] and misc/[Link]—it’s looking in the wrong folder! That’s
because our links are relative to the location of [Link], which lives in
the misc folder.
This is like saying, “I know [Link] is in the misc folder. Go up a folder and look
for [Link] and [Link] in there.”
ROOT-RELATIVE LINKS
“Root-relative” links are similar to the previous section, but instead of being relative to
the current page, they’re relative to the “root” of the entire website. For instance, if your
website is hosted on [Link], all root-relative URLs will be relative to [Link].
Unfortunately, there is one caveat to our discussion of root-relative links: this entire
tutorial uses local HTML files instead of a website hosted on a web server. This means
we won’t be able to experiment with root-relative links. But, if we did have a real server,
the link to our home page would look like this:
<!-- This won't work for our local HTML files -->
<li>Root-relative links, like to the <a href='/'>home page</a> of our website,
but those aren't useful to us right now.</li>
The only difference between a root-relative link and a relative one is that the
former starts with a forward slash. That initial forward slash represents the root of your
site. You can add more folders and files to the path after that initial slash, just like
relative links. The following path will work correctly no matter where the current page
is located (even in misc/[Link]):
/[Link]
LINK TARGETS
Attributes alter the meaning of HTML elements, and sometimes you need to modify
more than one aspect of an element. For example, <a> elements also accept
a target attribute that defines where to display the page when the user clicks the link. By
default, most browsers replace the current page with the new one. We can use
the target attribute to ask the browser to open a link in a new window/tab.
Try changing our absolute link in [Link] to match the following. Notice how the
second attribute looks just like the first, but they’re separated from each other by a space
(or a newline):
The target attribute has a few pre-defined values that carry special meaning for web
browsers, but the most common one is _blank, which specifies a new tab or window.
You can read about the rest on MDN.
links-and-images/spaces%20are%[Link]
In the address bar, you’ll see that all our spaces have been replaced with %20, as shown
above. Spaces aren’t allowed in URLs, and that’s the special encoding used to represent
them. Instead of a space, you should always use a hyphen, as we’ve been doing
throughout this tutorial. It’s also a good idea to use all lowercase characters for
consistency.
Notice how there’s a direct connection between our file/folder names and the URL for
the web page they represent. The names of our folders and files determine the slugs for
our web pages. They’re visible to the user, which means you should put in as much
effort into naming your files as you put into creating the content they contain.
These naming conventions apply to all of the files in your site—not just HTML files.
CSS files, JavaScript files, and images should avoid spaces and have consistent
capitalization, too.
Images are included in web pages with the <img/> tag and its src attribute, which points
to the image file you want to display. Notice how it’s an empty
element like <br/> and <hr/> from the previous chapter. (Don’t add this to our project
just yet. We’ll deal with concrete examples in the next section.)
<img src='[Link]'/>
Retina displays and mobile devices make image handling a little bit more complicated
than a plain old <img/> tag. We’ll leave these complexities for the Responsive
Images chapter of this tutorial. Also be sure to check out
the <figure> and <figcaption> element in the Semantic HTML chapter.
For now, let’s focus on the many image formats floating around the Internet.
IMAGE FORMATS
There’s four main image formats in use on the web, and they were all designed to do
different things. Understanding their intended purpose goes a long way towards
improving the quality of your web pages.
JPG IMAGES
JPG images are designed for handling large color palettes without exorbitantly
increasing file size. This makes them great for photos and images with lots of gradients
in them. On the other hand, JPGs don’t allow for transparent pixels, which you can see
in the white edges of the image below if you look real close:
Embed this [Link] image in our [Link] page with the following snippet (this
also includes a bit of navigation to our other pages):
<p>This page covers common image formats, but you may also be looking for <a
href='[Link]'>links</a> and <a href='misc/[Link]'>useful
extras</a>.</p>
<h2>JPGs</h2>
<p>JPG images are good for photos.</p>
<img src='images/[Link]'/>
GIF IMAGES
GIFs are the go-to option for simple animations, but the trade off is that they’re
somewhat limited in terms of color palette—never use them for photos. Transparent
pixels are a binary option for GIFs, meaning you can’t have semi-opaque pixels. This
can make it difficult to get high levels of detail on a transparent background. For this
reason, it’s usually better to use PNG images if you don’t need animation.
PNG IMAGES
PNGs are great for anything that’s not a photo or animated. For photos, a PNG file of
the same quality (as perceived the human eye) would generally be bigger than an
equivalent JPG file. However, they do deal with opacity just fine, and they don’t have
color palette limitations. This makes them an excellent fit for icons, technical diagrams,
logos, etc.
<h2>PNGs</h2>
<p>PNGs are good for diagrams and icons.</p>
<img src='images/[Link]'/>
SVG IMAGES
Unlike the pixel-based image formats above, SVG is a vector-based graphics format,
meaning it can scale up or down to any dimension without loss of quality. This property
makes SVG images a wonderful tool for responsive design. They’re good for pretty
much all the same use cases as PNGs, and you should use them whenever you can.
Despite being a vector format, SVGs can be used exactly like their raster counterparts.
Go ahead and add the [Link] to our [Link] page:
<h2>SVGs</h2>
<p>SVGs are <em>amazing</em>. Use them wherever you can.</p>
<img src='images/[Link]'/>
There is one potential issue with SVGs: for them to display consistently across
browsers, you need to convert any text fields to outlines using your image editor (e.g.,
Abode Illustrator or Sketch). If your images contain a lot of text (like the fancy
screenshots in this tutorial), this can have a big impact on file size. For this
reason, [Link] uses PNGs instead of SVGs, even though SVGs are so
awesome.
IMAGE DIMENSIONS
By default, the <img/> element uses the inherit dimensions of its image file. Our JPG,
GIF, and PNG images are actually 150×150 pixels, while our SVG mochi is only
75×75 pixels.
The width attribute sets an explicit dimension for the image. There’s a
corresponding height attribute, as well. Setting only one of them will cause the image
to scale proportionally, while defining both will stretch the image. Dimension values
are specified in pixels, and you should never include a unit (e.g., width='75px' would be
incorrect).
The width and height attributes can be useful, but it’s usually better to set image
dimensions with CSS so you can alter them with media queries. We’ll discuss this in
more detail once we get to responsive design.
TEXT ALTERNATIVES
Adding alt attributes to your <img/> elements is a best practice. It defines a “text
alternative” to the image being displayed. This has an impact on both search engines
and users with text-only browsers (e.g., people that use text-to-speech software due to
a vision impairment).
DOCUMENT LANGUAGE
A web page’s default language is defined by the lang attribute on the top-
level <html> element. Our document is in English, so we’ll use the en country code as
the attribute value (do this for all of the pages we created):
<html lang='en'>
If you’re not sure what the country code for your language is, you can look it
up here under the Subtag field.
<ol>
<li>bir</li>
<li>iki</li>
<li>üç</li>
<li>dört</li>
<li>beş</li>
</ol>
When you view this in a browser, you’ll see some weird stuff where the ü, ç, ö,
and ş characters should be:
HTML ENTITIES
Ok, so this last section doesn’t actually have anything to do with links or images, but
we do need to discuss one more thing before switching gears into CSS. An “HTML
entity” is a special character that can’t be represented as plain text in an HTML
document. This typically either means it’s a reserved character in HTML or you don’t
have a key on your keyboard for it.
RESERVED CHARACTERS
The <, >, and & characters are called “reserved characters” because they aren’t allowed
to be inserted into an HTML document without being encoded. This is because they
mean something in the HTML syntax: < begins a new tag, > ends a tag, and, as we’re
about to learn, & sets off an HTML entity.
Entities always begin with an ampersand (&) and end with a semicolon (;). In between,
you put a special code that your browser will interpret as a symbol. In this case, it
There’s a crap-ton of HTML entities. We’ll leave you to explore most of them on your
own.
QUOTES
Curly quotes are by no means necessary, but if you care about typography they’ll be
some of the most common HTML entities you’ll use. There’s four different kinds of
curly quotes (opening and closing single and double quotes):
• “
• ”
• ‘
• ’
You can use them in place of ' and " straight quotes, like so:
<p>If you’re into “web typography,” you’ll also find
yourself using curly quotes quite a bit.</p>
Unlike straight quotes, these curly quote entities should hug the text.
We also learned about a few important attributes (lang and charset) that give us the basic
template you should use as the beginning of every web page you ever create:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'/>
<title>Some Web Page</title>
</head>
<body>
<h1>Some Web Page</h1>
<!-- Rest of the page content -->
</body>
</html>
However, we’re still missing one really big piece: CSS. In the next chapter, we’ll
discover more HTML elements and attributes that will let us attach CSS styles to our
entire website. The ability to work with multiple files and link them to each other in
intelligent ways will become even more important than it was in this chapter.
The first few chapters of this tutorial focused exclusively on HTML. Now, it’s time to
make things pretty (sort of) with Cascading Style Sheets (CSS). You can think of CSS
as defining the “design” of a web page. It determines things like font size, margins, and
colors using a language entirely separate from HTML.
In this chapter, we’ll explore the basic syntax of CSS, as well as how to connect it to
our HTML documents. The goal isn’t so much to become a CSS expert or memorize
all the available styles, but rather to understand how CSS and HTML interact. CSS
typically lives in its own file, so as in the previous chapter, good file organization will
be paramount.
SETUP
To keep things simple, we’ll store the example for each chapter of this tutorial in a
separate folder. Using Atom, create a new project called hello-css. We’re going to be
styling an existing page called [Link], so go ahead and create that, then add the
following markup:
<!DOCTYPE html>
<html lang='en'>
<h2>List Styles</h2>
<ul>
<li>disc</li>
<li>circle</li>
<li>square</li>
</ul>
<ol>
<li>decimal</li>
<li>lower-roman</li>
<li>upper-roman</li>
<li>lower-alpha</li>
<li>upper-alpha</li>
<li>(and many more!)</li>
</ol>
</body>
</html>
In addition, we’ll need a small dummy page to learn how CSS styles can be applied to
multiple web pages. Create [Link] and add the following:
CSS STYLESHEETS
CSS stylesheets reside in plaintext files with a .css extension. Create a new file
called [Link] in our hello-css folder. This will house all our example snippets for this
chapter. Let’s add one CSS rule so that we can tell if our stylesheet is hooked up to our
HTML pages properly.
body {
color: #FF0000;
}
A CSS “rule” always start with a “selector” that defines which HTML elements it
applies to. In this case, we’re trying to style the <body> element. After the selector, we
have the “declarations block” inside of some curly braces. Any “properties” we set in
here will affect the <body> element.
CSS properties are kind of like HTML attributes in that they both deal with key-value
pairs. Except, here we’re defining presentational information instead of contributing to
the semantic meaning of the underlying content.
<head>
<meta charset='UTF-8'/>
<title>Hello, CSS</title>
<link rel='stylesheet' href='[Link]'/>
</head>
This <link/> element is how browsers know they need to load [Link] when they try
to render our [Link] page. We should now see blindingly red text everywhere:
The rel attribute defines the relationship between the resource and the HTML
document. By far the most common value is stylesheet, but there are a few other options.
The href attribute works the same as in the previous chapter, only it should point to
a .css file instead of another web page. The value for href can be an absolute, relative,
or root-relative link.
CSS COMMENTS
Now that our stylesheet is hooked up, let’s play with it a little bit. That red is horrible.
Let’s tone it down to a nice gray:
body {
color: #414141; /* Dark gray */
}
Notice that comments in CSS are a little different than their HTML counterparts.
Instead of the <!-- --> syntax, CSS ignores everything between /* and */ characters.
h1 {
font-size: 36px;
}
And, if you want to alter h2 headings, add another rule:
h2 {
font-size: 28px;
UNITS OF MEASUREMENT
Many CSS properties require a unit of measurement. There’s a lot of units available,
but the most common ones you’ll encounter are px (pixel) and em (pronounced like the
letter m). The former is what you would intuitively call a pixel, regardless of whether
the user has a retina display or not, and the latter is the current font size of the element
in question.
The em unit is very useful for defining sizes relative to some base font. In the above
diagram, you can see em units scaling to match a base font size of 12px, 16px, and 20px.
For a concrete example, consider the following alternative to the previous code snippet:
body {
color: #414141; /* Dark gray */
background-color: #EEEEEE; /* Light gray */
font-size: 18px;
}
h1 {
font-size: 2em;
}
h2 {
h2 {
font-family: "Helvetica", "Arial", sans-serif;
}
h3 {
font-family: "Helvetica", "Arial", sans-serif;
}
/* (etc) */
Instead, we can select multiple HTML elements in the same CSS rule by separating
them with commas. Add this to our [Link] file:
h1, h2, h3, h4, h5, h6 {
font-family: "Helvetica", "Arial", sans-serif;
}
This defines the font to use for all of our headings with a single rule. That’s great, ’cause
if we ever want to change it, we only have to do so in one place. Copying and pasting
code is usually a bad idea for web developers, and multiple selectors can help reduce
that kind of behavior quite a bit.
Relying on the user’s built-in fonts has historically been incredibly limiting for web
designers. Nowadays, system fonts have been largely superseded by web fonts. You
can read more about this in the Web Typography chapter of this tutorial.
ul {
list-style-type: circle;
}
ol {
list-style-type: lower-roman;
}
You can find other common values in the [Link] example page. Of particular
interest is none, which is commonly used when marking up menu navigation with
a <ul> list. The none value allows the menu’s list items to be styled more like buttons.
In the Advanced Positioning chapter, we’ll actually use this technique to create the
navigation menu shown below.
Defining the color of your text and the appearance of your bullets might seem trivial,
and it kind of is. But, look at the bigger picture: this is about gaining complete control
over the appearance of an HTML document. Alone, a single CSS property is silly. Put
them all together, and you’re able to create a totally customized web page.
REUSABLE STYLESHEETS
So, we just defined some basic styles for one of our web pages. It would be really
convenient if we could reuse them on our other page, too. For this, all we need to do is
add the same <link/> element to any other pages we want to style. Try adding the
following line to the <head> of [Link]:
Now, our [Link] pages should match our [Link] styles. Whenever we
change a style in [Link], those changes will automatically be reflected in both of our
web pages. This is how you get a consistent look and feel across an entire website.
UNDERLINES
The text-decoration property determines whether text is underlined or not. By setting it
to none, we can remove the default underline from all of our links. We’ll discuss link
styles in-depth later on.
a{
text-decoration: none;
}
TEXT ALIGNMENT
The aptly named text-align property defines the alignment of the text in an HTML
element.
p{
text-align: left;
}
Other accepted values are right, center, or justify, but notice how it always aligns to the
entire page:
This isn’t what you want for most websites. We’ll learn why this is the case in the next
chapter when we start talking about CSS boxes.
strong {
font-weight: normal;
font-style: italic;
}
We don’t suggest doing this for real websites though. Font weights and styles will,
however, become a lot more important once we start playing with custom fonts in
the Web Typography chapter.
THE CASCADE
The “cascading” part of CSS is due to the fact that rules cascade down from multiple
sources. So far, we’ve only seen one place where CSS can be defined: external .css files.
However, external stylesheets are just one of many places you can put your CSS code.
The CSS hierarchy for every web page looks like this:
This is ordered from least to most precedence, which means styles defined in each
subsequent step override previous ones. For example, inline styles will always make
the browser ignore its default styles. The next few sections focus on the last two options
because that’s what we have control over as web developers (in addition to the external
styles we’ve already been working with).
We made an effort to get you started down the right path with external stylesheets. It’s
important to understand page-specific and inline styles because you’ll most definitely
encounter them in the wild, but external stylesheets are by far the best place to define
the appearance of your website.
<head>
<meta charset='UTF-8'/>
<title>Dummy</title>
<link rel='stylesheet' href='[Link]'/>
<style>
body {
color: #0000FF; /* Blue */
}
</style>
</head>
These apply only to [Link]. Our [Link] page won’t be affected. If you did
it right, you should see bright blue text when you load [Link] in a browser.
Anything you would put in our [Link] file can live in this <style> element. It uses the
exact same CSS syntax as an external stylesheet, but everything here will override rules
in our [Link] file. In this case, we’re telling the browser to ignore the color property
we defined for <body> in our external stylesheet and use #0000FF instead.
Page-specific styles occasionally come in handy when you’re in a rush, but it’s almost
always better to store all your CSS in external stylesheets opposed to <style> elements.
Like page-specific styles, this is the same CSS syntax we’ve been working with.
However, since it’s in an attribute, it needs to be condensed to a single line. Inline styles
are the most specific way to define CSS. The color and text-decoration properties we
defined here trump everything. Even if we went back and added a text-decoration:
none to our <style> element, it wouldn’t have any effect.
Inline styles should be avoided at all costs because they make it impossible to alter
styles from an external stylesheet. If you ever wanted to re-style your website down the
road, you can’t just change a few rules in your global [Link] file—you’d have to go
through every single page and update every single HTML element that has
a style attribute. It’s horrifying.
MULTIPLE STYLESHEETS
CSS rules can be spread across several external stylesheets by adding
multiple <link/> elements to the same page. A common use case is to separate out styles
for different sections of your site. This lets you selectively apply consistent styles to
distinct categories of web pages.
For instance, if we had a bunch of product pages that looked entirely different than our
blog, we could use the following. (We don’t actually have these stylesheets defined, so
don’t bother adding them to our example project).
The order of the <link/> elements matters. Stylesheets that come later will override
styles in earlier ones. Typically, you’ll put your “base” or “default” styles in a global
stylesheet ([Link]) and supplement them with section-specific stylesheets
([Link] and [Link]). This allows you to organize CSS rules into manageable files
while avoiding the perils of page-specific and inline styles.
As a web developer, you’ll (hopefully) be given a polished design to work off of. Your
job is to turn that mockup into a real web page leveraging your knowledge of CSS. As
we mentioned earlier, setting individual CSS properties is actually quite simple. The
hard part is combining the overwhelming number of built-in properties to create exactly
what your web designer asked for—and do it quickly.
This chapter focused mostly on text formatting, but the Cascading Style Sheet language
can do a whole lot more. In the next chapter, we’ll start exploring how CSS defines the
layout of our web pages. On a final note, remember that you can always refer to MDN’s
CSS Reference when you’re not sure how a particular property works.
The previous chapter introduced the basic text formatting properties of CSS, but that
was only one aspect of styling pages. Defining the layout of a web page is an entirely
different beast. That’s what this chapter is about.
The “CSS box model“ is a set of rules that define how every web page on the Internet
is rendered. CSS treats each element in your HTML document as a “box” with a bunch
A big part of your job as a web developer will be to apply rules from the CSS box model
to turn a design mockup into a web page. As you work through this chapter, you might
find yourself wondering why we have to learn all these rules instead of just uploading
a giant static image of a web page (i.e., a mockup) to a web server and calling it a day.
Indeed, this would make life a lot easier; however, if we didn’t separate out our content
into HTML, search engines would have no way to infer the structure of our web pages,
we couldn’t make our site responsive, and there would be no way to add fancy
animations or interactivity with JavaScript. That’s a big enough trade-off to make CSS
a worthwhile cause.
This chapter covers the core components of the CSS box model: padding, borders,
margins, block boxes, and inline boxes. You can think of this as the “micro” view of
CSS layouts, as it defines the individual behavior of boxes. In future chapters, we’ll
SETUP
To get started, let’s create a new folder called css-box-model and stick a new web page
in it called [Link]. Add the following code:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'/>
<title>Boxes Are Easy!</title>
<link rel='stylesheet' href='[Link]'/>
</head>
<body>
<h1>Headings Are Block Elements</h1>
<p>Block elements define the flow of the HTML document, while inline elements
do not.</p>
</body>
</html>
As you should remember from Hello, CSS, this HTML file links to a CSS stylesheet
stored in a file called [Link]. Go ahead and create this file, too (you can leave it
empty for now).
Hopefully, you should be very familiar using Atom for these kinds of file and folder
creation tasks. If not, be sure to review the Atom walkthrough in the introductory
chapter of this tutorial.
All the HTML elements that we’ve been working with have a default type of box. For
instance, <h1> and <p> are block-level elements, while <em> and <strong> are inline
elements. Let’s get a better look at our boxes by adding the following to [Link]:
h1, p {
background-color: #DDE0E3; /* Light gray */
}
em, strong {
background-color: #B2D6FF; /* Light blue */
}
The background-color property only fills in the background of the selected box, so this
will give us a clear view into the structure of the current sample page. Our headings and
paragraphs should have gray backgrounds, while our emphasis and strong elements
should be light blue.
However, it’s almost never a good idea to turn <em> and <strong> into block elements,
so let’s turn them back into inline boxes by changing their display property to inline,
like so:
em, strong {
background-color: #B2D6FF;
display: inline; /* This is the default for em and strong */
}
Together, this is everything a browser needs to render an element’s box. The content is
what you author in an HTML document, and it’s the only one that has any semantic
value (which is why it’s in the HTML). The rest of them are purely presentational, so
they’re defined by CSS rules.
PADDING
Let’s start from the inside out. We’ve already been working with content, so on to
padding. The padding property…you guessed it…defines the padding for the selected
element:
h1 {
padding: 50px;
}
Sometimes you’ll only want to style one side of an element. For that, CSS provides the
following properties:
p{
padding-top: 20px;
padding-bottom: 20px;
padding-left: 10px;
padding-right: 10px;
}
You can use any unit for the padding of an element, not just pixels. Again, em units are
particularly useful for making your margins scale with the base font size.
SHORTHAND FORMATS
Typing out all of these properties out can be tiresome, so CSS provides an alternative
“shorthand” form of the padding property that lets you set the top/bottom and left/right
padding with only one line of CSS. When you provide two values to
the padding property, it’s interpreted as the vertical and horizontal padding values,
respectively.
BORDERS
Continuing our journey outward from the center of the CSS box model, we have the
border: a line drawn around the content and padding of an element. The border property
requires a new syntax that we’ve never seen before. First, we define the stroke width of
the border, then its style, followed by its color.
Drawing a border around our entire heading makes it look a little 1990s, so how about
we limit it to the bottom of the heading? Like padding, there are -top, -bottom, -left,
and -right variants for the border property:
border-bottom: 1px solid #5D6063;
Please refer to the Mozilla Developer Network for more information about border
styles.
MARGINS
Margins define the space outside of an element’s border. Or, rather, the space between
a box and its surrounding boxes. Let’s add some space to the bottom of
each <p> element:
p{
padding: 20px 0 20px 10px;
margin-bottom: 50px; /* Add this */
}
This demonstrates a side-specific variant of the margin property, but it also accepts the
same shorthand formats as padding.
Margins and padding can accomplish the same thing in a lot of situations, making it
difficult to determine which one is the “right” choice. The most common reasons why
you would pick one over the other are:
• The padding of a box has a background, while margins are always transparent.
• Padding is included in the click area of an element, while margins aren’t.
• Margins collapse vertically, while padding doesn’t (we’ll discuss this more in
the next section).
If we change margin to padding, we’ll discover that this isn’t exactly the case for a box’s
padding. It’ll display the blue background; however, it won’t affect the vertical layout
of the surrounding boxes.
So, before you start banging your head against the wall trying to figure out why your
top or bottom margin isn’t working, remember to check your display property. Trust us,
this will happen to you eventually.
For example, let’s add a top margin of 25 pixels to our <p> element:
p{
padding: 20px 0 20px 10px;
margin-top: 25px;
This behavior can be very useful when you’re working with a lot of different kinds of
elements, and you want to define their layout as the minimum space between other
elements.
<p>Block elements define the flow of the HTML document, while inline elements
We’ll talk more about the <div> element in the next section. The important part here is
that only consecutive elements can collapse into each other. Putting an element with
non-zero height (hence the padding-top) between our paragraphs forces them to display
both the 25px top margin and the 50px bottom margin.
Finally, the flexbox layout scheme doesn’t have collapsing margins, so this isn’t really
even an issue for modern websites.
Both <div> and <span> are “container” elements that don’t have any affect on the
semantic structure of an HTML document. They do, however, provide a hook for
adding CSS styles to arbitrary sections of a web page. For example, sometimes you
need to add an invisible box to prevent a margin collapse, or maybe you want to group
the first few paragraphs of an article into a synopsis with slightly different text
formatting.
We’ll use a lot of <div>’s throughout the rest of this tutorial. For now, let’s create a
simple button by adding the following to the bottom of our [Link] file:
<div>Button</div>
And here are the associated styles that need to go into [Link]. Most of these
should be familiar from the last chapter, although we did throw in a new border-
radius property:
div {
color: #FFF;
background-color: #5995DA;
font-weight: bold;
padding: 20px;
text-align: center;
border: 2px solid #5D6063;
This will give us a big blue button that spans the entire width of the browser:
Of course, these styles also apply to the invisible <div> we used to break the margin
collapse in the previous section. Obviously, we need a way to select individual <div>’s
if they’re to be of any practical use to us. That’s what class selectors are for, which we’ll
introduce in the next chapter. In lieu of those, let’s just delete or comment out that
invisible <div>.
The only real difference between a <div> and a <span> is that the former is for block-
level content while the latter is meant for inline content.
But, sometimes our desired layout calls for an explicit dimension, like a sidebar that’s
exactly 250 pixels wide. For this, CSS provides the width and height properties. These
take precedence over the default size of a box’s content.
Let’s give our button an explicit width by adding the following property to box-
[Link]:
div {
/* [Existing Declarations] */
width: 200px;
}
Also notice that if you make the button’s title longer, it will automatically wrap to the
next line, and the element will expand vertically to accommodate the new content. You
can change this default behavior with the white-space and overflow properties.
Fortunately, CSS lets you change how the width of a box is calculated via the box-
sizing property. By default, it has a value of content-box, which leads to the behavior
described above. Let’s see what happens when we change it to border-box:
div {
color: #FFF;
background-color: #5995DA;
font-weight: bold;
padding: 20px;
text-align: center;
border: 2px solid #5D6063;
border-radius: 5px;
width: 200px;
box-sizing: border-box; /* Add this */
}
This forces the actual width of the box to be 200px—including padding and borders. Of
course, this means that the content width is now determined automatically:
ALIGNING BOXES
Aligning boxes horizontally is a common task for web developers, and the box model
offers a lot of ways to do it. We already saw the text-align property, which aligns the
content and inline boxes inside of a block-level element. Aligning block boxes is
another story.
Try adding the following rule to our stylesheet. It will only align the content inside of
our block boxes—not the blocks themselves. Our <div> button is still left-aligned
regardless of the <body>’s text alignment:
body {
text-align: center;
It’s usually a good idea to override default styles to a predictable value using the
“universal” CSS selector (*). Try adding this to the top of our [Link] file:
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
This selector matches every HTML element, effectively resetting
the margin and padding properties for our web page. We also converted all our boxes
to border-box, which, again, is a best practice.
You’ll find a similar reset at the top of almost every global CSS stylesheet on the web.
They can get a whole lot more complicated, but the three simple declarations shown
SUMMARY
We’ll learn more about the practical uses of the CSS box model as we get deeper into
constructing complex web pages. For now, think of it as a new tool in your CSS toolbox.
With a few key concepts from this chapter, you should feel much more equipped to
convert a design mockup into a real-life web page:
• Everything is a box.
• Boxes can be inline or block-level.
• Boxes have content, padding, borders, and margins.
• They also have seemingly arbitrary rules about how they interact.
• Mastering the CSS box model means you can lay out most web pages.
Like the last chapter, the CSS properties we just covered might seem simple—and they
sort of are. But, start looking at the websites you visit through the lens of the CSS box
model, and you’ll see this stuff literally everywhere.
Our exploration of generic boxes (<div> and <span>) was a little bit limited because we
didn’t have a way to pluck out an individual HTML element from our web page. We’ll
fix that in the next chapter with a more in-depth discussion of CSS selectors.
Way back in the Links and Images chapter, we learned how to connect an HTML
document to other files in our project. “CSS selectors” are similar, except instead of
navigating between whole files, they let us map a single CSS rule to a specific HTML
element. This makes it possible to selectively style individual elements while ignoring
others.
Unless you want every section of your website to look exactly the same, this is a crucial
bit of functionality for us. It’s how we say things like “I want this paragraph to be blue
and that other paragraph to be yellow.” Until now, we’ve only been able to turn all our
paragraphs blue (or yellow).
The only CSS selector we’ve seen so far is called the “type selector”, which targets all
the matching elements on a page. In this chapter, we’ll explore more granular ways to
style a web page with class selectors, descendant selectors, pseudo-classes, and ID
selectors.
SETUP
We’ll only need one HTML file and a CSS stylesheet for our example this chapter.
Create a new folder called css-selectors and new web page called [Link] with the
following markup:
<!DOCTYPE html>
Go ahead and create that [Link] stylesheet in the same folder, too. This gives us
everything we need to explore CSS selectors.
If you’re just diving into this tutorial series, be sure to have a quick read through
the Introduction to get set up with the Atom text editor.
CLASS SELECTORS
“Class selectors” let you apply CSS styles to a specific HTML element. They let you
differentiate between HTML elements of the same type, like when we had
two <div> elements in the previous chapter, but only wanted to style one of them. Class
selectors require two things:
• A class attribute on the HTML element in question.
• A matching CSS class selector in your stylesheet.
Adding a class attribute doesn’t alter the semantic meaning of your HTML document at
all—it’s purely for hooking into your CSS stylesheet. However, it’s still usually a good
idea to avoid naming classes based on their appearance. If we chose to name our
class .italic, we wouldn’t be able to do much besides make it italic in our CSS without
leading to a confusing situation. Using something semantic like .synopsis gives us more
freedom for our CSS to customize how that synopsis is displayed.
For example, let’s try to create a fixed-width layout using the auto-margin
technique that we learned in the previous chapter. First, wrap our entire document in a
generic <div> and give it a unique class:
<body>
<div class='page'> <!-- Add this -->
<h1>CSS Selectors</h1>
<p class='synopsis'>CSS selectors let you <em>select</em> individual HTML
elements in an HTML document. This is <strong>super</strong> useful.</p>
<p>Classes are ridiculously important, since they allow you to select
arbitrary boxes in your web pages.</p>
<p>We’ll also be talking about links in this example, so here’s
<a href='[Link] Is Hard</a> for us to
style.</p>
<div class='button'>Button One</div>
</div> <!-- And this -->
</body>
Then, add the following to [Link]:
.page {
This is how layouts are defined in more complex web pages. For instance, if our page
had a sidebar, we would nest all the sidebar elements in another <div> with
a .sidebar class. We’ll see this in action in the next chapter. For now, the key takeaway
is that without class selectors to differentiate our <div> elements, none of this would be
possible.
This gives us a second button that looks just like the first one—without writing a
single line of CSS! Organizing similar graphical elements into reusable CSS rules
like this makes life much easier as a web developer. If we ever wanted to, say, change
the button color, we would only have to do it in one place and all our buttons would
automatically update.
.call-to-action {
font-style: italic;
background-color: #EEB75A; /* Yellow */
}
ORDER MATTERS
There’s a couple of important things going on with our second button now:
• It’s adding a new font-style declaration to the original .button rule.
• It’s overriding an existing background-color style from .button.
Overriding occurs because of the order of .call-to-action and .button in our stylesheet.
When there’s two conflicting properties in a CSS file, the last one is always the one that
gets applied. So, if you moved .call-to-action to the top of [Link], .button would have
the final word on the value of background-color, and it would remain blue.
This means that the order of the class attribute in our HTML element has no effect on
override behavior. Multiple classes on a single element are applied “equally” (for lack
of a better term), so the precedence is determined solely by the order of the rules
in [Link]. In other words, the following elements are effectively equivalent:
<!-- These result in the same rendered page -->
<div class='button call-to-action'>Button Two</div>
<div class='call-to-action button'>Button Two</div>
DESCENDANT SELECTORS
You may have noticed that the <em> in our first paragraph is no longer distinguishable
from its surround text, since our .synopsis rule made everything italic.
To alter that <em> element, we could add another class directly to it, but that won’t
result in very maintainable code. We want to treat .synopsis as its own independent
component that we can style entirely from CSS (i.e., without requiring alterations to our
HTML just for the sake of styling something.)
Descendant selectors aren’t limited to class selectors—you can combine any other
group of selectors this way. For instance, if we wanted to select only <em> elements
inside of headings, we might use something like this:
h1 em {
/* Some other styles */
}
Again, the goal of this chapter is to let you apply styles to exactly the element you want.
Descendant selectors are a great tool towards this end. You may also want to check out
the related “child selector” over at MDN if you’ve still got room in your toolbox.
DON’T OVERDO IT
You can nest descendant selectors as deep as you want, but don’t get carried away. Life
gets confusing and terrible when you start writing rules that look like this:
The classic example is a link. As a web developer, you create an <a href> element. After
the browser renders it, the user can interact with that link. They can hover over it, click
it, and visit the URL.
a:link {
color: blue;
text-decoration: none;
}
a:visited {
color: purple;
}
a:hover {
BUTTON STYLES
Let’s start with :link and :visited variants. We’re using a similar pattern as in the
previous section, but since these are buttons, we want to keep both the unvisited and
visited color the same. Change the existing .button rules to match the following:
.button:link, /* Change this */
.button:visited { /* Change this */
display: block; /* Add this */
text-decoration: none; /* Add this */
color: #FFF; /* The rest is the same */
background-color: #5995DA;
font-weight: bold;
padding: 20px;
text-align: center;
border: 2px solid #5D6063;
border-radius: 5px;
width: 200px;
margin: 20px auto;
}
.call-to-action:active,
.call-to-action:visited:active {
background-color: #EEB75A; /* Yellow */
}
Since we only added the .call-to-action class to our second button, that’s the only one
that’ll turn yellow. Of course, we still need the .button class on both <a> elements
because it defines shared styles like the padding, border radius, and font weight.
CAVEATS
Ok, so actually the pseudo-class method is a little more complicated. They’re still a
useful tool—as long as you know their ins-and-outs. The :first-of-type and :last-of-
We have a single generic <div> wrapping our content (.page), so this isn’t a problem
for us. However, consider what happens when we add this to the bottom of
our .page element:
<div class='sidebar'>
<p>If this page had a sidebar...</p>
<p>We’d have some problems with pseudo-classes.</p>
</div>
We won’t be able to make a real sidebar until the next chapter, but this does highlight
the complications of pseudo-classes for structure. The first <p> element here will also
match p:first-of-type because the pseudo-class’s scope is limited to the parent element.
If you wanted to avoid the sidebar paragraphs and select only the first <p> in our <div
class='page'>, you would need to limit its scope using a child selector, like so:
.page > p:first-of-type {
color: #7E8184;
font-style: italic;
}
All of this is yet another example of how there are many ways to do the same thing in
the wonderful world of HTML and CSS. Different developers adhere to different
schools of thought. Some like the semantic nature of pseudo-classes, while others go to
the far extreme with explicit class attributes on every HTML element.
URL FRAGMENTS
id attributes need to be unique because they serve as the target for “URL fragments”,
which we sort of glossed over in our discussion of URLs. Fragments are how you point
the user to a specific part of a web page. They look like an ID selector stuck on the end
of a URL.
Unfortunately, not all CSS selectors are created equal. “CSS specificity” is the weight
given to different categories of selectors. This means that certain selectors
will always override other ones, regardless of where they appear in the stylesheet.
Let’s start by seeing where this doesn’t break. If you add the following after our
existing .call-to-action rules, it will override the previous background-color. If you stick
it at the top of the file, it’ll get overridden later on, so our button won’t turn red. This is
expected behavior.
.call-to-action:link,
.call-to-action:visited {
background-color: #D55C5F; /* Red */
}
The specificity of selectors we’ve seen in this chapter are show below, from greatest to
least:
• #button-2
• .button:link
• a:link and .synopsis em (they’re equal)
• .button
• a
This can get very confusing. It’s such a big problem that an entire methodology called
“BEM” has evolved. BEM attempts to make CSS rules more reusable by
BEM is outside the scope of this tutorial. The takeaway here is that CSS rules are not
necessarily applied in sequential order, but you should try to make the browser do so
by writing CSS that uses the same specificity.
SUMMARY
In this chapter, we got some hands-on experience with class selectors, descendant
selectors, pseudo-classes, link styling, and ID selectors. The goal of all this was to be
able to target a specific HTML element from your CSS. Class selectors are by far the
most versatile and come with the least amount of drawbacks. As a result, they’ll become
part of your daily life as a web developer.
Like it or not, things got a lot more complicated this chapter. We’re now able to make
our CSS interact with an HTML document in half a dozen different ways. Furthermore,
over the next few chapters, we’ll begin to see a dependency between our HTML’s
structure and the layout of a web page. With all this interplay between CSS and HTML,
it can be hard to know where to start building a new web page.
The separation of content from presentation helps guide this process. You need content
before you can present it, so your first step is usually to mark up your raw content with
HTML tags. Once that’s prepared, you’re ready to add class attributes to your elements
and style them one-by-one. When you discover a need for some extra structure to create
a desired layout (e.g., turn a group of elements into a sidebar), that’s when you start
wrapping your content in container <div>’s.
FLOATS
Nº 7. of HTML & CSS Is Hard
An old-timey web dev tutorial (and a friendly intro to CSS layouts)
Over the last few chapters, we’ve learned how to manipulate the size of boxes and the
space around them, but for the most part, we were stuck with the default vertical flow
of the page. Block elements always appeared vertically one after another, effectively
limiting us to a single-column layout.
SETUP
This chapter demonstrates CSS floats with a pretty simple sample project. Instead of
working with proper HTML content as we have been in previous chapters, we’ll be
styling a bunch of empty <div> elements. We’ll end up with something that looks like
the following, which is a pretty big divergence from the types of web pages we’ve been
creating thus far.
We can get a better look at our example page by adding some background colors and
explicit heights to each of our <div> elements. Add this to [Link]:
.menu {
height: 100px;
background-color: #B2D6FF; /* Medium blue */
}
.sidebar {
height: 300px;
background-color: #F09A9D; /* Red */
}
.content {
.footer {
height: 200px;
background-color: #D6E9FE; /* Light blue */
}
This gives us a nice rainbow, which isn’t what we’re looking for, though it does
demonstrate some useful concepts.
The important part here is that each block-level element fills 100% of its parent
elements’s width (<div class='page'> in this case), and they appear vertically one after
another. Again, we’re essentially limited to a single-column layout.
Typically, you’d want to let the height of these boxes be determined automatically
based on the content they contain; however, we’re more concerned with controlling
It’s worth taking a look at what happens when we shrink an element’s width. Update
our .sidebar rule to match the following:
.sidebar {
width: 200px; /* Add this */
height: 300px;
background-color: #F09A9D;
}
The sidebar element gets narrower, but the rest of the boxes stay in the exact same
position. All the blocks are still rendered vertically one after another. This is the
behavior we’ll be changing with floats.
FLOATING AN ELEMENT
The CSS float property gives us control over the horizontal position of an element. By
“floating” the sidebar to the left, we’re telling the browser to align it to the left side of
the page. Go ahead and float our sidebar with the following line:
.sidebar {
float: left; /* Add this */
width: 200px;
You can also float elements right, as shown below (let’s keep our sidebar floated left
though). Or, if you’re overriding a float declaration, you can cancel it with
the none value. These are the most common values for the float property.
We now have all the tools necessary to align block-level elements: floats for left/right
alignment and auto-margins for center alignment. Remember that this only applies
to block boxes. Inline boxes are aligned with the text-align property, as discussed in
the previous chapter.
Let’s change this by giving our page a fixed-width layout. Once again, the auto-margin
centering technique comes in handy. Add this to [Link]:
.page {
width: 900px;
margin: 0 auto;
}
Now, we can see that .sidebar floats to the left of the .page container, opposed to the
edge of the browser window.
MULTIPLE FLOATS
Let’s examine our current magazine-style float a little bit more by adding an explicit
width to our .content block:
.content {
width: 650px; /* Add this */
height: 500px;
background-color: #F5CF8E;
}
This clearly demonstrates that our sidebar is in fact inside the .content block: if you take
a screenshot of them, you’ll have an image that’s 650 pixels wide opposed to 850 pixels
(our sidebar is 200 pixels wide).
Make sure both of them are floating left before moving on. That takes care of the layout
for the sidebar and content blocks, but it unfortunately messed up our .footer element…
AFTER A FLOAT
You probably noticed that our footer shows up in the top right, directly below .menu.
That’s because floated boxes are removed from the normal flow of the page. The height
We can see this more clearly by adding a red border around our .page element:
.page {
width: 900px;
margin: 0 auto;
border: 1px solid red; /* Add this */
}
Notice how the border is only around the .menu and .footer elements. It’s as if the
floated elements weren’t even there. There are two ways to fix this: clearing a float and
hiding overflow.
CLEARING FLOATS
“Clearing” a float is when we tell a block to ignore any floats that appear before it.
Instead of flowing around the floated box, a cleared element always appears after any
floats. It’s like forcing a box back into the default vertical flow of the page.
We can use the clear property to make our .footer drop down to the bottom of the page:
.footer {
Depending on the type of layout you’re trying to create, this is a perfectly acceptable
solution. We could stop here, but we’re going to explore float behavior more by
transforming our page into a full-bleed layout that has background colors filling the
entire browser window.
Watch what happens when we take the menu and footer out of the .page element.
Change the <body> element to match the following:
<body>
<div class='menu'>Menu</div>
<div class='page'>
<div class='sidebar'>Sidebar</div>
<div class='footer'>Footer</div>
</body>
Since .menu and .footer are outside our fixed-width .page, they’re the full width of the
window, which is exactly what we want for a full-bleed layout. However, notice
how .page has zero height again despite the fact that the footer still clears the sidebar
and content blocks.
Once again, the only elements in .page are floated, so they don’t count towards its
height. In other words, moving the footer outside of the .page container broke
our clear fix.
HIDING OVERFLOW
Clearing floats only fixes the height issue when there’s an element inside the container
element that we can add a clear property to. Now that our footer is outside .page, we
need a new way to make floated elements contribute to the height of their container.
FULL-BLEED LAYOUTS
Next, we want to make our .page background fill the entire browser window without
changing the alignment of our sidebar or content blocks. The problem is, our .page is
busy centering everything—we can’t use it for a full-bleed background because
centering requires an explicit width property.
This gives us three nested <div> elements just for laying out our page:
a .container wrapper for full-bleed background color, a fixed-width .page for centering
everything, and finally left-aligned .sidebar and .content blocks. This kind of nesting
and aligning is pretty typical of most website layouts.
Next we’re going to add three equal-width columns to our footer. Update
the <footer> element, like so:
<div class='footer'>
<div class='column'></div>
<div class='column'></div>
<div class='column'></div>
</div>
We can style each of these columns just like we laid out the rest of our page. Add a
new rule to [Link]:
.column {
float: left;
width: 31%;
Anyhoo, let’s not lose sight of the central thesis of this chapter: floats let us stack things
horizontally instead of vertically. By changing the widths of the elements we’re
floating, we can get all kinds of different layouts, from sidebars to multiple columns to
grids.
Our footer background is too short. Fortunately, we already know how to fix that. Let’s
replace the footer’s explicit height with another overflow: hidden so it can accommodate
any number of grid items:
.footer {
overflow: hidden;
background-color: #D6E9FE;
}
You can use this same technique to make grids of any size. For example, creating a
photo gallery with a bunch of thumbnails is simply a matter of putting the grid items
in .page instead of the footer and adding <img/> elements to them. But, again,
remember that flexbox is a more modern way to create these kinds of layouts.
The process for the latter is the same, it’s just nested inside the former. Let’s add some
dummy content to our .content element so we have something to play with:
<div class='container'>
<div class='page'>
<div class='sidebar'></div>
<div class='content'>
<p>Ad netus sagittis velit orci est non ut urna taciti metus donec magnis
hendrerit adipiscing mauris sit a proin ultrices nibh.</p>
</div>
</div>
</div>
We’ve got an image and several paragraphs that we can style just like our structural
divs. For example, let’s create a magazine-style layout by floating the image and letting
the text flow around it. Add a couple more rules to our stylesheet:
.content {
padding: 20px;
}
.article-image {
float: left;
width: 300px;
height: 200px;
margin-right: 20px;
margin-bottom: 20px;
}
p{
margin-bottom: 20px;
}
Let’s try creating this in our footer. In your favorite .column element, add the following:
<div class='column'>
<div class='avatar'></div>
<h3 class='username'>Bob Smith</h3>
.avatar {
float: left;
width: 60px;
height: 60px;
margin: 25px;
border-radius: 40px;
background-color: #D6E9FE;
}
.username {
margin-top: 30px;
}
.comment {
margin: 10px;
overflow: hidden; /* This is important */
}
This highlights another use case for our overflow: hidden trick. Sticking it on
our .comment box made sure that the text “horizontally cleared” (that’s not a technical
term) the floated image. Without it, the last line of the .comment text would hang
underneath the image.
SUMMARY
This chapter was our first encounter with realistic web page layouts. We learned how
to float divs to the left and right, how to deal with content after a float, and how to
combine floats with the auto-margin centering technique from the CSS Box
Model chapter. These are the tools we need to create sidebars, grids, an magazine-style
layouts.
It’s important not to lose sight of the developer’s role in the website creation process.
Your job as a web developer is to take a beautifully designed mockup and turn it into
the HTML and CSS that browsers can display to your end users. Floats are a big leap
forward towards that end, but they’re also becoming obsolete in favor of the flexbox
layout scheme.
In the next chapter, we’ll learn even more ways to lay out complex websites using
flexbox. The CSS properties will be new, but the process will be the same as it was in
this chapter: we’ll still be aligning boxes inside of other boxes, inside of other boxes,
and so on until we accomplish the desired layout.
The “Flexible Box” or “Flexbox” layout mode offers an alternative to Floats for defining
the overall appearance of a web page. Whereas floats only let us horizontally position our
boxes, flexbox gives us complete control over the alignment, direction, order, and size of
our boxes.
The web is currently undergoing a major transition, so a little discussion around the state
of the industry is warranted. For the last decade or so, floats were the sole option for laying
out a complex web page. As a result, they’re well supported even in legacy browsers, and
However, floats were originally intended for the magazine-style layouts that we covered
in Floats for Content. Despite what we saw last chapter, the kinds of layouts you can create
with floats are actually somewhat limited. Even a simple sidebar layout is, technically
speaking, a little bit of a hack. Flexbox was invented to break out of these limitations.
We’re finally at a point where browser support has hit critical mass and developers can
start building full websites with flexbox. Our recommendation is to use flexbox to lay out
your web pages as much as possible, reserving floats for when you need text to
flow around a box (i.e., a magazine-style layout) or when you need to support legacy web
browsers.
In this chapter, we’ll explore the entire flexbox layout model step by step. You should walk
away comfortable building virtually any layout a web designer could ever give you.
SETUP
The example for this chapter is relatively simple, but it clearly demonstrates all of the important
flexbox properties. We’ll wind up with something that looks like this:
.menu-container {
color: #fff;
background-color: #5995DA; /* Blue */
padding: 20px 0;
}
.menu {
border: 1px solid #fff; /* For debugging */
width: 900px;
}
Finally, download some images for use by our example web page. Unzip them into
the flexbox project, keeping the parent images directory. Your project should look like this
before moving on:
Every HTML element that’s a direct child of a flex container is an “item”. Flex items can
be manipulated individually, but for the most part, it’s up to the container to determine
their layout. The main purpose of flex items are to let their container know how many
things it needs to position.
FLEX CONTAINERS
The first step in using flexbox is to turn one of our HTML elements into a flex container.
We do this with the display property, which should be familiar from the CSS Box
Model chapter. By giving it a value of flex, we’re telling the browser that everything in the
box should be rendered with flexbox instead of the default box model.
Add the following line to our .menu-container rule to turn it into a flex container:
.menu-container {
/* ... */
display: flex;
}
This enables the flexbox layout mode—without it, the browser would ignore all the flexbox
properties that we’re about to introduce. Explicitly defining flex containers means that you can
mix and match flexbox with other layout models (e.g., floats and everything we’re going to learn
in Advanced Positioning).
The flex container automatically distributes extra horizontal space to either side of each
item. The space-between value is similar, but it only adds that extra space between items.
This is what we actually want for our example page, so go ahead and update the justify-
content line:
justify-content: space-between;
Of course, you can also use center, flex-start, flex-end here if you want to push all the items
to one side or another, but let’s leave it as space-between.
But, now we need to lay out the .links element because it’s using the default block layout
mode. The solution: more nested flex containers! Add a new rule to our [Link] file that
turns the .links element into a flex container:
.links {
border: 1px solid #fff; /* For debugging */
display: flex;
.login {
margin-left: 20px;
}
This will put our links right where we want them. Notice that margins still work just like
they did in the CSS Box Model. And, as with the normal box model, auto margins have a
special meaning in flexbox (we’ll leave that for the end of the chapter though).
We won’t need those white borders anymore, so you can go ahead and delete them if you
like.
.header {
width: 900px;
height: 300px;
display: flex;
justify-content: space-between;
}
This should all be familiar; however, the scenario is a little bit different than our menu.
Since .header has an explicit height, items can be positioned vertically inside of it. The
official specification calls this “cross-axis” alignment (we’ll see why in a moment), but for
our purposes it might as well be called “vertical” alignment.
• stretch
• baseline
.social,
.logo,
.subscribe {
border: 1px solid #5995DA;
}
The box for each item extends the full height of the flex container, regardless of how much
content it contains. A common use case for this behavior is creating equal-height columns
with a variable amount of content in each one—something very difficult to do with floats.
Be sure to delete the above changes and vertically center our content inside
of .header before moving on.
.photo-grid-container {
display: flex;
justify-content: center;
}
.photo-grid {
width: 900px;
display: flex;
.photo-grid-item {
border: 1px solid #fff;
width: 300px;
height: 300px;
}
This should work as expected, but watch what happens when we add more items than can
fit into the flex container. Insert an extra two photos into the .photo-grid:
<div class='photo-grid-item'>
<img src='images/[Link]'/>
</div>
<div class='photo-grid-item last-item'>
<img src='images/[Link]'/>
</div>
By default, they flow off the edge of the page:
If you’re trying to build a hero banner that lets the user horizontally scroll through a bunch
of photos, this might be desired behavior, but that’s not what we want. Adding the
following flex-wrap property forces items that don’t fit to get bumped down to the next
row:
.photo-grid {
/* ... */
A key tenant of responsive design is presenting the same HTML markup to both mobile
and desktop users. This presents a bit of a problem, as most mobile layouts are a single
ALIGNMENT CONSIDERATIONS
Notice that the column is hugging the left side of its flex container despite our justify-
content: center; declaration. When you rotate the direction of a container, you also rotate
the direction of the justify-content property. It now refers to the container’s vertical
alignment—not its horizontal alignment.
To horizontally center our column, we need to define an align-items property on our .photo-
grid:
.photo-grid {
/* ... */
flex-direction: column;
align-items: center; /* Add this */
}
This can be used, for example, to swap order of the .first-item and .last-item elements in
our grid. We should also change the row-reverse value from the previous section back
to row because it’ll make our edits a little easier to see:
.photo-grid {
/* ... */
flex-direction: row; /* Update this */
align-items: center;
}
.first-item {
order: 1;
}
.last-item {
order: -1;
}
Unlike setting row-reverse and column-reverse on a flex container, order works across
row/column boundaries. The above snippet will switch our first and last items, even though
they appear on different rows.
You can align elements in other ways using the same values as the align-items property,
listed below for convenience.
• center
• flex-start (top)
• flex-end (bottom)
• stretch
• baseline
FLEXIBLE ITEMS
All our examples have revolved around items with fixed- or content-defined-widths. This
has let us focus on the positioning aspects of flexbox, but it also means we’ve been ignoring
its eponymous “flexible box” nature. Flex items are flexible: they can shrink and stretch to
match the width of their containers.
The flex property defines the width of individual items in a flex container. Or, more
accurately, it allows them to have flexible widths. It works as a weight that tells the flex
First, we need a footer to experiment with. Stick this after the .photo-grid-container element:
<div class='footer'>
<div class='footer-item footer-one'></div>
<div class='footer-item footer-two'></div>
<div class='footer-item footer-three'></div>
</div>
Then, some CSS:
.footer {
display: flex;
justify-content: space-between;
}
.footer-item {
border: 1px solid #fff;
background-color: #D6E9FE;
height: 200px;
flex: 1;
}
That flex: 1; line tells the items to stretch to match the width of .footer. Since they all have
the same weight, they’ll stretch equally:
.footer-one,
.footer-three {
background-color: #5995DA;
flex: initial;
width: 300px;
}
Without that flex: initial; line, the flex: 1; declaration would be inherited from the .footer-
item rule, causing the width properties to be ignored. initial fixes this, and we get a flexible
layout that also contains fixed-width items. When you resize the browser window, you’ll
see that only the middle box in the footer gets resized.
Let’s take a look by flattening our items in .menu so that it matches the following:
<div class='menu-container'>
<div class='menu'>
<div class='date'>Aug 14, 2016</div>
<div class='signup'>Sign Up</div>
<div class='login'>Login</div>
</div>
</div>
.signup {
margin-left: auto;
}
Auto-margins eat up all the extra space in a flex container, so instead of distributing items
equally, this moves the .signup and any following items (.login) to the right side of the
container. This will give you the exact same layout we had before, but without that extra
nested <div> to group them. Sometimes, it’s nice to keep your HTML flatter.
SUMMARY
Flexbox gave us a ton of amazing new tools for laying out a web page. Compare these
techniques to what we were able to do with floats, and it should be pretty clear that
flexbox is a cleaner option for laying out modern websites:
Remember that these flexbox properties are just a language that lets you tell browsers
how to arrange a bunch of HTML elements. The hard part isn’t actually writing the
When a designer hands you a mockup to implement, your first task is to draw a bunch
of boxes on it and determine how they’re supposed to stack, stretch, and shrink to
achieve the desired design. Once you’ve got that done, it should be pretty easy to code
it up using these new flexbox techniques.
The flexbox layout mode should be used for most of your web pages, but there are some
things it’s not-so-good at, like gently tweaking element positions and preventing them
from interacting with the rest of the page. After covering these kinds of advanced
positioning techniques in the next chapter, you’ll be an HTML and CSS positioning
expert.
“Static positioning” refers to the normal flow of the page that we’ve been working with
up ’til this point. The CSS Box Model, floats, and flexbox layout schemes all operate
in this “static” flow, but that’s not the only positioning scheme available in CSS.
The other three types of positioning are “relative”, “absolute”, and “fixed”. Each of
them let you manually position elements using specific coordinates, opposed to the
more semantic options in flexbox and floats. Instead of saying “Stick this box in the
center of its container,” advanced positioning lets you say things like “Put that box 20
pixels above and 50 pixels to the right of its parent’s origin.”
The vast majority of elements on a web page should be laid out according to the static
flow of the page. These other positioning schemes come into play when you want to do
This chapter is split into two parts. We’ll start by examining relative, absolute, and fixed
positioning in isolation, then we’ll apply everything we learned to a fancy dropdown
menu.
SETUP
Start by creating a new Atom project called advanced-positioning and a new file
called [Link] with the following markup:
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'/>
<title>Positioning Is Easy!</title>
<link href='[Link]' rel='stylesheet'/>
</head>
<body>
<div class='container'>
<div class='example relative'>
<div class='item'><img src='images/[Link]' /></div>
<div class='item item-relative'><img src='images/[Link]' /></div>
<div class='item'><img src='images/[Link]' /></div>
</div>
</div>
<div class='container'>
<div class='example absolute'>
<div class='item'><img src='images/[Link]' /></div>
<div class='item item-absolute'><img src='images/[Link]' /></div>
<div class='item'><img src='images/[Link]' /></div>
</div>
</div>
<div class='container'>
This page relies on some images to make our example a little bit clearer. Keep the
parent images folder when unzipping the files into your project, as show above. Be sure to
create [Link] and populate it with the necessary base styles, as well:
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 1200px;
}
.example {
display: flex;
justify-content: space-around;
width: 800px;
margin: 50px 0;
background-color: #D6E9FE;
}
.item img {
display: block;
}
Nothing new here, just some familiar flexbox techniques to create a grid of items. The only
weird thing is the explicit height on the <body> element, which will let us scroll up and
down the page to demonstration different positioning behaviors.
It’s possible to mix-and-match different positioning schemes. Again, most of your web
page should be statically positioned, but it’s common to find relatively and absolutely
positioned elements inside of other elements that are part of the normal flow of the page.
RELATIVE POSITIONING
“Relative positioning” moves elements around relative to where they would normally
appear in the static flow of the page. This is useful for nudging boxes around when the
default flow is just a little bit off.
Relative positioning works similarly to margins, with one very important difference:
neither the surrounding elements or parent element are affected by the top and left values.
Everything else renders as if .item-relative was in its original position. Think of the offsets
as being applied after the browser finishes laying out the page.
The top and left properties measure from the original box’s top and left edges, respectively.
We can offset relative to the other edges with the bottom and right properties.
ABSOLUTE POSITIONING
“Absolute positioning” is just like relative positioning, but the offset is relative to the entire
browser window instead of the original position of the element. Since there’s no longer any
relationship with the static flow of the page, consider this the most manual way to lay out
an element.
This behavior isn’t really all that useful most of the time because it would
mean everything on your page needs to be absolutely positioned—otherwise we’d get
unpredictable overlaps of static elements with absolute elements. So, why
does absolute even exist?
Coordinates for absolute elements are always relative to the closest container that is a
positioned element. It only falls back to being relative to the browser when none of its
ancestors are positioned. So, if we change .item-absolute’s parent element to be relatively
positioned, it should appear in the top-left corner of that element instead of the browser
window.
.absolute {
position: relative;
}
The .absolute div is laid out with the normal flow of the page, and we can manually
move around our .item-absolute wherever we need to. This is great, because if we want
to alter the normal flow of the container, say, for a mobile layout, any absolutely
positioned elements will automatically move with it.
FIXED POSITIONING
“Fixed positioning” has a lot in common with absolute positioning: it’s very manual,
the element is removed from the normal flow of the page, and the coordinate system is
relative to the entire browser window. The key difference is that fixed elements don’t
scroll with the rest of the page.
This lets you create navigation bars that always stay on the screen, as well as those
annoying pop-up banners that never go away.
These advanced positioning schemes allow JavaScript to move elements around while
avoiding any kind of interaction with surrounding elements. For instance, try copying-
and-pasting the following into [Link] after the third .container element.
The <script> element should be the last thing inside of <body>.
function frame() {
var element = [Link]('.item-relative');
left += 2;
[Link] = left + 'px';
if (left >= 300) {
clearInterval(id)
}
}
This is a pretty rudimentary example, but you can hopefully see how it’s applicable to
fancy UI animations. If you were to try to achieve the same effect by manipulating
the margin or padding properties, you would inadvertently move the statically
positioned boxes and/or the containing .example element, too.
Fixed positioning will let us make the menu stick to the top of the page, and relative
positioning will give us an anchor for the absolutely positioned dropdown. We’ll also
get a chance to talk about navigation menu best practices and see some practical
applications of the pseudo-classes we talked about in CSS Selectors.
For starters, we need a new web page called [Link] that has a header and a simple
top-level menu:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'/>
<title>Awesome!</title>
<link href='[Link]' rel='stylesheet'/>
</head>
<body>
<div class='header'>
<div class='logo'><img src='images/[Link]'/></div>
body {
height: 1200px;
font-size: 18px;
font-family: sans-serif;
color: #5D6063;
}
a:link,
a:visited {
color: #5D6063;
text-decoration: none;
}
.header {
position: fixed;
display: flex;
justify-content: space-between;
width: 100%;
padding: 50px;
background: #D6E9FE;
}
This should all be familiar, but note the fixed position of the .header, which keeps our
navigation menu on top of any content that would go into the page.
.menu > li {
display: inline;
margin-right: 50px;
SUBMENUS
Our submenu is going to look just like the top-level menu, except the whole thing will
be nested inside a list item. Change the .menu element to match the following, ensuring
that the entire .features-menu list is wrapped in the first <li> of the .menu element.
<ul class='menu'>
<li class='dropdown'><span>Features ▾</span>
<ul class='features-menu'> <!-- Start of submenu -->
<li><a href='#'>Harder</a></li>
<li><a href='#'>Better</a></li>
<li><a href='#'>Faster</a></li>
<li><a href='#'>Stronger</a></li>
</ul> <!-- End of submenu -->
</li>
<li><a href='#'>Blog</a></li> <!-- These are the same -->
<li><a href='#'>Subscribe</a></li>
This provides a lot of crucial information for search engines. It allows Google to see
that all these new items are associated with the Features label and that they form an
isolated section of our website. You should always mark up complex navigation menus
with this kind of structure.
As for the CSS, we’ll deal with the interactive dropdown part later. Right now, let’s just
get our submenu looking the way we want it to. Add some simple styles so we can see
the box we’re trying to position:
.features-menu {
display: flex;
flex-direction: column;
background: #B2D6FF;
border-radius: 5px;
padding-top: 60px;
}
.features-menu li {
list-style: none;
border-bottom: 1px solid #FFF;
.features-menu li:last-of-type {
border-bottom: none;
}
The submenu itself is styled correctly, but it’s showing up in the wrong place and
severely messing up the rest of our top-level menu items. This should be expected
To create our desired layout, we need to call on our new CSS positioning skills.
The submenu resides in <li class='dropdown'>. Turning that into a positioned element
should change the coordinate system used by our absolutely positioned .features-menu:
.dropdown {
position: relative;
}
Ok, next problem. Our submenu is in the right spot, but now it’s covering up
the Features label.
Z-INDEX
We’ve never had to deal with “depth” issues before. Until now, all our HTML elements
rendered above or below one another in an intuitive way. But, since we’re doing advanced
stuff, relying on the browser to determine which elements appear on top of other ones isn’t
going to cut it.
In other words, the .features-menu element needs to have a lower z-index than
the Features label. The default z-index value is 0, so let’s make both of them higher than
that. We conveniently wrapped the Features label in a <span>, allowing us to style it via
a child selector, like so:
.dropdown > span {
z-index: 2;
position: relative; /* This is important! */
cursor: pointer;
}
.features-menu {
/* ... */
z-index: 1;
}
The Features label should now appear on top of the submenu. Take note of
that position: relative; line. It’s required because only positioned elements pay attention
to their z-index property. This is easy to forget, so make a mental note for the next time
you’re having depth issues and your CSS rules don’t seem to have any effect.
First, we need to change our existing .features-menu rule to only show the submenu
when the user hovers over it by adding a :hover descendant selector. Update
the .features-menu selector to match the following:
.dropdown:hover .features-menu { /* This used to be `.features-menu` */
display: flex; /* Leave everything else alone */
flex-direction: column;
background: #B2D6FF;
/* ... */
}
Then, we need to initially hide the submenu using the display property. Add a new
rule to [Link]:
.features-menu { /* Add this as a new rule */
SUMMARY
In this chapter, we took a look at four new CSS layout schemes:
• Relative
• Absolute
• Relatively absolute
• Fixed
Relative positioning was for tweaking the position of an element without affecting its
surrounding boxes. Absolute positioning took elements out of the static flow of the page
and placed them relative to the browser window, while relatively absolute positioning
allowed us to hook back into the static flow of the page. Finally, fixed positioning let
us make elements that didn't scroll with the rest of the page.
This menu was also a pretty good example of how starting with the HTML markup
makes life a lot easier. First, we created the semantic structure we wanted. Then, we
wrote some fancy CSS to position the boxes right where we wanted them. Whenever
you’re looking at a complicated mockup and not sure where to start, this is good way
to approach the problem.
There’s still one big issue with our menu: it’s not built for mobile devices. Smartphones
and tablets don’t have a way to hover, and our layout doesn’t display well when the
browser is narrower than 960 pixels. The former requires a little bit of JavaScript magic
(or some really advanced CSS), so we’ll leave that for another tutorial. But, we will be
able to tackle the latter problem with some responsive design in the next chapter.
“Responsive design” refers to the idea that your website should display equally well in everything
from widescreen monitors to mobile phones. It’s an approach to web design and development that
eliminates the distinction between the mobile-friendly version of your website and its desktop
counterpart. With responsive design, they’re the same thing.
Responsive design is accomplished through CSS “media queries”. Think of media queries as a
way to conditionally apply CSS rules. They tell the browser that it should ignore or apply certain
rules depending on the user’s device.
In this chapter, we’ll learn how media queries are really just a thin wrapper around the plain old
CSS that we’ve been working with up ’til this point. As we’ll soon discover, it’s actually pretty
easy to implement a responsive layout. (Responsive Images, on the other hand, are an entirely
different story).
/* Mobile Styles */
@media only screen and (max-width: 400px) {
body {
background-color: #F09A9D; /* Red */
}
}
/* Tablet Styles */
@media only screen and (min-width: 401px) and (max-width: 960px) {
body {
background-color: #F5CF8E; /* Yellow */
}
}
/* Desktop Styles */
@media only screen and (min-width: 961px) {
body {
background-color: #B2D6FF; /* Blue */
Media queries always begin with the @media “at-rule” followed by some kind of
conditional statement, and then some curly braces. Inside the curly braces, you put a bunch
of ordinary CSS rules. The browser only pays attention to those rules if the condition is
met.
The only screen “media type” means that the contained styles should only be applied to
devices with screens (opposed to printed documents, like when you hit Cmd+P in a
browser). The min-width and max-width parts are called “media features”, and they
specify the device dimensions you’re targeting.
The above media queries are by far the most common ones you’ll encounter, but there are
a lot of other conditions you can check for, including whether the device is in portrait or
landscape mode, the resolution of its screen, and whether it has a mouse or not.
There’s a few well defined patterns for how a desktop layout collapses into a mobile layout
(we’re using “layout shifter”). A lot of these decisions are in the realm of design, which is
outside the scope of this code-oriented tutorial; however, there are two concepts that you
must understand as a developer:
• A “fluid” layout is one that stretches and shrinks to fill the width of the screen,
just like the flexible boxes we covered a few chapters ago.
• A “fixed-width” layout is the opposite: it has the same width regardless of the
screen dimensions (we created one of these in the CSS Selectors chapter).
CHOOSING BREAKPOINTS
Most of those responsive design patterns have similar behavior, using fluid layouts for
mobile/tablet devices and fixed-width layouts for wider screens. There’s a reason for this.
Fluid layouts let us target a range of screen widths instead of specific mobile devices. This
is very important for web designers. When they set out to create a mobile layout, they aren’t
trying to make something that looks good on an iPhone 6s, Galaxy S7, or iPad mini—
they’re designing a fluid layout that looks good anywhere between 300 pixels and 500
pixels (or whatever).
In other words, the exact pixel values for the min-width and max-width parameters in a
media query (collectively known as the “breakpoints” for a responsive website) don’t
actually matter. Our website doesn’t care about the specific device the user is on. All it
needs to know is that it should display a layout that looks pretty at 400 pixels wide (or
whatever).
MOBILE-FIRST DEVELOPMENT
Let’s dive right into implementing the above screenshots. It’s always a good idea to start
with the mobile layout and work your way up to the desktop version. Desktop layouts are
First, we need to fill in [Link]’s <body> element with some empty boxes. Each
box has an image in it so we can tell them apart a little bit easier.
<div class='page'>
<div class='section menu'></div>
<div class='section header'>
<img src='images/[Link]'/>
</div>
<div class='section content'>
<img src='images/[Link]'/>
</div>
<div class='section sign-up'>
<img src='images/[Link]'/>
</div>
<div class='section feature-1'>
<img src='images/[Link]'/>
</div>
<div class='section feature-2'>
<img src='images/[Link]'/>
</div>
<div class='section feature-3'>
<img src='images/[Link]'/>
</div>
</div>
And here’s our base styles, which should apply to all layouts (mobile, tablet, and desktop).
Make sure to add these above the @media rules we created earlier and below the universal
selector rule that resets our margins and padding:
.page {
display: flex;
flex-wrap: wrap;
}
.menu {
background-color: #5995DA;
height: 80px;
}
.header {
background-color: #B2D6FF;
}
.content {
background-color: #EAEDF0;
height: 600px;
}
.sign-up {
background-color: #D6E9FE;
}
.feature-1 {
background-color: #F5CF8E;
}
.feature-2 {
background-color: #F09A9D;
}
.feature-3 {
background-color: #C8C6FA;
By keeping these base styles outside of the media queries, we’re able to override and add
on to them as we implement our specific layouts. This is really convenient when, for
instance, your designer wants to tweak the color scheme for the entire website. Instead of
tracking down redundant background-color declarations in several @media rules, you
only have to update it here. That change automatically applies to the mobile, tablet, and
desktop layouts.
DESKTOP LAYOUT
And that’s where our desktop layout comes in. We don’t want our web page to expand
endlessly, so we’re going to give it a fixed width and center it with auto-margins. As with
tablet styles, this needs to go into a media query. Replace the existing /* Desktop Styles
*/ media query with the following:
/* Desktop Styles */
@media only screen and (min-width: 961px) {
.page {
width: 960px;
margin: 0 auto;
}
.feature-1,
.feature-2,
This is where flexbox really shines. Trying to create this combination of mobile and
desktop layouts would be very difficult with floats. With flexbox’s order property, it’s just
a few lines of CSS. Append these rules to the desktop media query:
.sign-up {
height: 200px;
order: 1;
}
.content {
order: 2;
This was just one example of laying out a responsive site. You can use these exact same
techniques to implement all sorts of other designs. Start with the base styles that apply to
your entire site, then tweak them for various device widths by selectively applying CSS
rules with @media. You could even add another media query to, say, create a dedicated
layout for ultra-widescreen monitors.
This default behavior will prevent mobile devices from using our mobile layout, which is
obviously very terrible. To disable it, add the following element to the <head> of our
You should see the zoom-disabled version of the above diagram in your browser, since it’s
now pretending to be a mobile device. (We’ll save the in-depth discussion of Chrome dev
tools for a future tutorial.)
Alternatively, if you’re reading this chapter on a smartphone, you can navigate to the
live before and after versions of our example project to experience the effect of
our viewport changes.
SUMMARY
Believe it or not, that’s actually all you need to know to create responsive websites. If we
boil it down, we’re really only concerned with three things:
We started this chapter by learning about the difference between fluid layouts and fixed-
width layouts. Then, we went on to create a mobile-first stylesheet that used media queries
to build tablet and desktop layouts on top of a shared set of base styles. Finally, we disabled
the default viewport zoom behavior of mobile browsers.
So, that was the easy part of responsive design. In the next chapter, we’ll discover the hard
part: images. Presenting different CSS to specific devices isn’t too bad, but optimizing
images for those devices requires a bit more planning.
RESPONSIVE IMAGES
Nº 11. of HTML & CSS Is Hard
Responsive images are hard. Like, actually hard. But don’t be scared.
In Responsive Design, we learned how to use media queries to create separate mobile,
tablet, and desktop layouts. Now, we’re going to add images to the mix. Just as media
queries let us conditionally present different CSS rules, we want to display different images
based on the user’s device.
SETUP
To experiment with responsive images, we need a responsive website to work with. This chapter
will be building off of the example web page we put together in the previous chapter. We’ll be
adding two images to the page so it looks like the following. This might seem simple, but these
images will change depending on the user’s device (which is really cool).
If you’re just joining us, go ahead and download the complete example project, unzip it,
and open it up with Atom. If you’re not familiar with the Atom text editor, be sure to take
a read through the introduction of this tutorial series.
RETINA SCREENS
This is our first time worrying about retina devices, so let’s talk a little bit about screen
resolution. Retina screens have twice as many pixels per inch than standard-resolution
screens. That is to say, each retina pixel is the equivalent of 4 standard pixels. This has a
big impact on how images are displayed in a web browser.
This is actually a bit of a simplification—not all retina screens are created equal. For
instance, the iPhone 6 Plus has three times as many pixels per inch as a standard screen.
This tutorial focuses on the 2x use case, but the same techniques apply to 3x retina screens
as well.
What’s more, standard displays and smaller devices don’t need all those extra pixels in
high-resolution images, and sending that much unnecessary data usually results in a bad
user experience.
SVGs let us forget about screen resolution issues, but we do need to shrink the illustration
to fit neatly into our fluid tablet and mobile layouts. Firefox will do this automatically, but
if you open this page with Chrome and make your browser very narrow, you’ll find that
the image stays the same size.
To get a fluid image in Chrome, we need to tell the illustration to always fill the width of
its container. In [Link], put the following rule with the rest of the base styles, outside of
the media queries:
.illustration {
width: 100%;
}
When we specify 100% width on an image, it’ll assume we want to maintain its aspect
ratio and calculate its height automatically. This fixes the mobile layout, but now the
desktop version is huge:
If you’re not worried about optimization, responsive raster images really aren’t that much
harder than using SVG images. Try swapping out our existing [Link] with a PNG
file:
<div class='section content'>
<div class='illustration'>
<img src='images/[Link]' style='max-width: 500px'/>
</div>
</div>
Consider this the lazy way to create responsive PNG, GIF, or JPG images, as it assumes
everybody needs a high-resolution image, even if they don’t. That is to say, a 1000×500
pixel image is overkill for non-retina devices. We’ll get a little smarter about this in the
next section.
Adding a srcset attribute to our <img/> element lets us present our high-resolution
image only to retina devices, falling back to the low-resolution version for standard
screens. Update our .illustration element to match the following:
<div class='illustration'>
<img src='[Link]'
srcset='images/[Link] 1x,
images/[Link] 2x'
style='max-width: 500px'/>
</div>
Typically, the low-res and high-res versions of an image would be the exact same (except
for their dimensions), but we made [Link] yellow so you can easily
differentiate it from the retina version, which is blue.
It’s a little hard to see this in action without a real website, so we included the previous
snippet on this page. The image below should be blue if you’re viewing it on a retina
device. Otherwise, it will be yellow for standard-resolution screens.
The lesson here is that we want to optimize larger images based on their final rendered
dimensions, not just the device’s screen resolution. Let’s go ahead and add that big photo
to our .header element:
<div class='section header'>
<div class='photo'>
<img src='images/[Link]'
srcset='images/[Link] 2000w,
images/[Link] 1000w'
sizes='(min-width: 960px) 960px,
100vw'/>
</div>
</div>
We have the same srcset element as the last section, but instead of
the 1x and 2x descriptors, we’re providing the inherent physical width of the image.
The 2000w tells the browser that the [Link] file is 2000 pixels wide. Likewise,
the 1000w means [Link] has a width of 1000 pixels. If you’re wondering about
that w character, it’s a special unit used only for this kind of image optimization scenario.
Speaking of which, we need to make some changes to position our new header image
correctly. Add both of the following rules to our other base styles, right above the mobile
styles media query:
.header {
height: auto;
justify-content: inherit;
.photo img {
width: 100%;
display: block;
}
Remember that our low-resolution photo is 1000 pixels wide, which means that 2x retina
devices can use it as long as their screen is less than 500 pixels wide. In Firefox, you should
now be able to resize the browser to see the retina version (“Big”) when the window is
wider than 500 pixels and the non-retina version (“Small”) for narrower widths.
We’re now serving a 115KB image to mobile devices instead of forcing them to use the
high-res 445KB image. That’s a big deal, especially for websites that use a lot of photos.
It lets you optimize layouts by sending completely different images to the user depending
on their device. Compare this to the previous section, which optimized the same image for
different devices. For instance, our header photo is pretty wide. Wouldn’t it be great if we
For this, we need the <picture> and <source> elements. The former is just a wrapper, and
the latter conditionally loads images based on media queries. Try changing
our .header element to the following:
<div class='section header'>
<div class='photo'>
<picture>
<source media='(min-width: 401px)'
srcset='images/[Link]'/>
<source media='(max-width: 400px)'
srcset='images/[Link]'/>
<img src='images/[Link]'/>
</picture>
</div>
</div>
Conceptually, this is pretty similar to using media queries in CSS. In
each <source> element, the media attribute defines when the image should be loaded,
and srcset defines which image file should be loaded. The <img/> element is only used as
This level of control will make your designer very happy, but the trade off is that it doesn’t
let the browser automatically pick the optimal image. This means we lost our retina
optimization from the previous section: as long as the screen width is 401 pixels or greater,
the browser will always use the high-resolution, wide-cropped image.
While it is possible to combine the best of both worlds, it gets complicated real quick. Our
recommendation is to stick to the 1x and 2x version of srcset for images less than 600
pixels wide, use the srcset plus sizes method from the previous section for bigger photos,
and reserve <picture> for when you’re trying to do something real fancy with your
designer.
• Make images fit into mobile layouts while respecting their intrinsic size
• Avoid making the user download unnecessarily large image files
We accomplished the former by making images always stretch to fill 100% of their
container while limiting their size with an inline max-width style. For the latter, we
used srcset to optimize for screen resolution, srcset plus sizes to optimize for device width,
and finally the <picture> element for manual control over which image file is displayed.
Responsive design is an evolving topic. Browsers only recently implemented the image
optimization techniques covered in this chapter, despite the fact that responsive design has
been the standard for half a decade. While the technology used to create a responsive
website may change, the fundamental problem of presenting the same content to different
devices will never disappear. So, even if you eventually need to learn some new tools, the
foundational concepts we just introduced should stay with you forever.
These last five chapters focused entirely on layout. We explored floats, flexbox, advanced
positioning, and how to apply all those concepts to various screen widths. This is pretty
much everything you’ll ever need to lay out web pages with HTML and CSS. The next
chapter scuffles back into the world of HTML, introducing a bunch of new elements that
will make search engines much happier with our websites.
“Semantic HTML” refers to the idea that all your HTML markup should convey the
underlying meaning of your content—not its appearance. We’ve already been writing
semantic HTML (e.g., using <strong> instead of <b>), but there’s a whole set of
elements designed for the sole purpose of adding more meaning to the overall layout of
a web page. They’re called “sectioning elements”, and they look something like this:
SETUP
Our example for this chapter will be a simple unstyled HTML document. Create a
new Atom project called semantic-html with a new file in it called [Link]. Add the
following:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'/>
<title>Semantic HTML</title>
</head>
<body>
<h1>Interneting Is Easy!</h1>
<ul>
<li><a href='#'>Home</a></li>
<h3>Headers</h3>
<p>The <code><header></code> element is one such sectioning
element.</p>
<h3>Footers</h3>
<p>And so is the <code><footer></code> element.</p>
Each <h1> element creates a new section in the document outline, and any less
prominent headings that follow it are considered subsections under that top-level
heading. E.g., the Semantic HTML section has two subsections in it: The Document
Outline and Inline Semantic HTML. The same goes for <h2> and <h3> elements,
and so on down to <h6>.
How’s this document outline stuff relate to semantic HTML? Well, headings are some of
the most semantic things in a web page. They play a significant role in how search engines
determine what’s important in your web page. In addition, the semantic HTML elements
we’re about to cover add more meaning to and sometimes even alter the default outlining
behavior discussed here.
ARTICLES
The <article> element represents an independent article in a web page. It should only
wrap content that can be plucked out of your page and distributed in a completely
different context. For instance, an app like Flipboard should be able to grab
an <article> element from your site, display it in its own app, and have it make perfect
sense to its readers.
Compare this to a bunch of generic <div> elements with arbitrary class names, and you
can begin to see how semantic HTML makes the Web a much easier place to navigate.
SECTIONS
The <section> element is sort of like an <article>, except it doesn’t need to make sense
outside the context of the document. That is, an app like Flipboard wouldn’t try to pull out
all the <section>’s of your page and present them as independent pieces of content.
<h3>Headers</h3>
<p>The <code><header></code> element is one such sectioning
element.</p>
<h3>Footers</h3>
<p>And so is the <code><footer></code> element.</p>
</section> <!-- And this -->
However, you shouldn’t use the <section> element to manipulate the document outline
in this way because browsers, screen readers, and some search engines don’t properly
interpret the effect of <section> on the document outline. Instead, always define a
page’s outline via heading levels, using <section> only as a replacement for
container <div>’s when appropriate.
Also note that each <section> element should contain at least one heading, otherwise it
will add an “untitled section” to your document outline. As an example, try
updating [Link] to match the following, then run it through the outliner tool again:
<h2>Inline Semantic HTML</h2>
<section>
<!-- This will be an "Untitled Section" -->
<p>The <code><time></code> element is semantic, but it’s not
sectioning content.</p>
</section>
NAV ELEMENTS
The <nav> element lets you mark up the various navigation sections of your website.
This goes for the main site navigation, links to related pages in a sidebar, tables of
content, and pretty much any group of links. For example, we should stick our site-wide
navigation menu in a <nav> element:
<h1>Interneting Is Easy!</h1>
HEADERS
The <header> element is a new piece of semantic markup, not to be confused with
headings (the <h1>-<h6> elements). It denotes introductory content for a section,
It’s a best practice to wrap a website’s name/logo and main navigation in a <header>,
so let’s go ahead and add one to our example project:
<header>
<h1>Interneting Is Easy!</h1>
<nav>
<ul>
<li><a href='#'>Home</a></li>
<li><a href='#'>About</a></li>
<li><a href='#'>Blog</a></li>
<li><a href='#'>Sign Up</a></li>
</ul>
</nav>
</header>
Headers are only associated with the nearest sectioning element—typically
a <body>, <section>, or <article> element. This means that you can use
multiple <header> elements to add introductory content to different parts of a document.
FOOTERS
Conceptually, footers are basically the same as headers, except they generally come at end
of an article/website opposed to the beginning. Common use cases include things like
copyright notices, footer navigation, and author bios at the end of blog posts.
Footers behave the same as <header> in that they’re associated with the nearest
sectioning element. So, we can use it for our page’s copyright notice and the author
</article>
</body>
</html>
The <footer> inside the <article> element is only for the contents of that article, which
makes sense because it contains the author’s bio. The second footer, on the other hand, is
connected to the entire page.
ASIDES
Headers and footers are ways to add extra information to an article, but sometimes we
want to remove information from an article. For example, a sponsored blog post might
Let’s add a fake advertisement to our [Link] file, right underneath the article’s
header:
<article>
<header>
<h1>Semantic HTML</h1>
<p>By Troy McClure. Published January 3rd</p>
</header>
<!-- Look! A fake advertisement! -->
<aside class='advert'>
<img src='[Link]'/>
</aside>
When used outside an <article>, an <aside> is associated with the page as a whole
(much like <header> and <footer>). This makes it a good choice for marking up a site-
wide sidebar. Add the following underneath the closing </article> tag, before the
second <footer>:
<aside class='sidebar'>
<h2>Sidebar</h2>
<p>Some sidebar content</p>
<nav>
<h3>HTML & CSS Tutorial</h3>
<ul>
<li><a href='#'>Introduction</a></li>
<li><a href='#'>Basic Web Pages</a></li>
<li><a href='#'>etc...</a></li>
</ul>
</nav>
<nav>
<h3>JavaScript Tutorial</h3>
<ul>
<li><a href='#'>Introduction</a></li>
<li><a href='#'>Hello, JavaScript</a></li>
<li><a href='#'>etc...</a></li>
</ul>
</nav>
</aside>
Notice the class attributes in both of these snippets. If we were worried about CSS this
chapter, we could style our <aside> elements in exactly the same way as all the <div>’s
we’ve been working with throughout this tutorial. Which brings us to…
For instance, if we want to center our page using that familiar auto-margin technique,
we have to wrap the whole page in a container. It’s entirely presentational, so a <div> is
the best option:
<body>
<div class='page'> <!-- Start of container div -->
<header>
<h1>Interneting Is Easy!</h1>
<nav>
<ul>
<li><a href='#'>Home</a></li>
<li><a href='#'>About</a></li>
<li><a href='#'>Blog</a></li>
<li><a href='#'>Sign Up</a></li>
</ul>
</nav>
</header>
<!-- ... -->
<footer>
<p>© 2017 [Link]</p>
</footer>
</div> <!-- End of container div -->
</body>
</html>
This is particularly relevant for flexbox, as it requires lots of <div>’s to group flex items
correctly. Occasionally, you may find that a <section> or <nav> is appropriate for these
The point is, don’t use semantic elements just for the sake of using them. Implementing
them incorrectly is worse than not using them at all, so if you’re ever in doubt, use
a <div> instead.
The <time> element represents either a time of day or a calendar date. Providing a
machine-readable date makes it possible for browsers to automatically link it to users’
calendars and helps search engines clearly identify specific dates. A simple Google search
will show you the effect of including a <time> element on your page:
Let’s make the publish date of our article unambiguous by wrapping it in <time> tags:
<article>
<header>
<h1>Semantic HTML</h1>
<p>By Troy McClure. Published <time datetime='2017-1-3'>January
3rd</time></p>
</header>
<!-- ... -->
It’s possible to include times and time zones inside of datetime, too. If we wanted to add a
3:00pm PST time to our publish date, we’d use the following:
<time datetime='2017-1-3 15:00-0800'>January 3rd</time>
The time itself is in 24-hour format, and the -0800 is the time zone offset from GMT (in
this case, -0800 represents Pacific Standard Time).
ADDRESS
The <address> element is like <time> in that it doesn’t deal with the overall structure of
a document, but rather embellishes the parent <article> or <body> element with some
metadata. It defines contact information for the author of the article or web page in
question. <address> should not be used for arbitrary physical addresses.
For instance, maybe we want to add an author email address in our article’s footer:
<footer>
<p>This fake article was written by somebody at [Link], which
is a pretty decent place to learn how to become a web developer. This footer
is only for the containing <code><article></code> element.</p>
<address>
Please contact <a href='[Link]
<figure>
<img src='[Link]'
alt='Diagram showing <article>, <section>, and <nav> elements'/>
<figcaption>New HTML5 semantic elements</figcaption>
</figure>
<!-- ... -->
The alt attribute is closely related to the <figcaption> element. alt should serve as a
text replacement for the image, while <figcaption> is a
supporting description displayed with either the image or its text-based equivalent.
When using <figcaption> in the above manner, you can safely omit an
image’s alt attribute without hurting your SEO. Depending on what kind of images
CSS/LEGACY CONSIDERATIONS
And finally, a quick note on legacy browsers. The semantic HTML elements in this chapter
were introduced in HTML5. All modern browser recognize them without any extra work,
but you’ll often see something like the following in global CSS stylesheets:
section, article, aside, footer, header, nav {
display: block;
}
This makes the new semantic elements behave like <div> elements (which are block
boxes, not inline boxes) in legacy browsers.
SUMMARY
Defining graphical styles with CSS is how we convey the structure of a web page to
humans. By marking it up with <header>, <article>, <figure>, and other HTML sectioning
elements, we’re able to represent those visual styles to machines, as well.
The semantic elements we covered in this chapter are best practices for modern websites,
but keep in mind that they hardly scratch the surface when it comes to extra meaning you
can add to your web pages. Just for starters:
• [Link] microdata lets you alter the appearance of your site in search engine
results.
• Twitter cards define how your web page is displayed in tweets.
• Open Graph metadata changes how Facebook shares your content.
This kind of stuff is closer to the realm of technical SEO, so we’ll leave you to explore it
on your own. In the next chapter, we’ll switch gears again and introduce another critical
component of websites (especially e-commerce ones): forms.
HTML form elements let you collect input from your website’s visitors. Mailing lists,
contact forms, and blog post comments are common examples for small websites, but
in organizations that rely on their website for revenue, forms are sacred and revered.
Forms are the “money pages.” They’re how e-commerce sites sell their products, how
SaaS companies collect payment for their service, and how non-profit groups raise
money online. Many companies measure the success of their website by the
effectiveness of its forms because they answer questions like “how many leads did our
website send to our sales team?” and “how many people signed up for our product last
There are two aspects of a functional HTML form: the frontend user interface and the
backend server. The former is the appearance of the form (as defined by HTML and CSS),
while the latter is the code that processes it (storing data in a database, sending an email,
etc). We’ll be focusing entirely on the frontend this chapter, leaving backend form
processing for a future tutorial.
SETUP
Unfortunately, there’s really no getting around that fact that styling forms is hard. It’s
always a good idea to have a mockup representing the exact page you want to build
before you start coding it up, but this is particularly true for forms. So, here’s the
example we’ll be creating in this chapter:
Create a new Atom project called forms and stick a new HTML file in it called speaker-
[Link]. For starters, let’s add the markup for the header. (Hey look! It
has some semantic HTML!)
<!DOCTYPE html>
body {
color: #5D6063;
background-color: #EAEDF0;
font-family: "Helvetica", "Arial", sans-serif;
font-size: 16px;
line-height: 1.3;
display: flex;
flex-direction: column;
align-items: center;
}
width: 80%;
margin: 40px 0;
padding: 50px;
}
.speaker-form-header h1 {
font-size: 30px;
margin-bottom: 20px;
}
Notice that we’re adhering to the mobile-first development approach that we discussed in
the Responsive Design chapter. These base CSS rules give us our mobile layout and
provide a foundation for the desktop layout, too. We’ll create the media query for a fixed-
width desktop layout later in the chapter.
HTML FORMS
On to forms! Every HTML form begins with the aptly named <form> element. It
accepts a number of attributes, but the most important ones are action and method. Go
ahead and add an empty form to our HTML document, right under the <header>:
<form action='' method='get' class='speaker-form'>
</form>
The action attribute defines the URL that processes the form. It’s where the input collected
by the form is sent when the user clicks the Submit button. This is typically a special URL
defined by your web server that knows how to process the data. Common backend
technologies for processing forms include [Link], PHP, and Ruby on Rails, but again,
we’ll be focusing on the frontend in this chapter.
By leaving the action attribute blank, we’re telling the form to submit to the same URL.
Combined with the get method, this will let us inspect the contents of the form.
STYLING FORMS
Of course, we’re looking at an empty form right now, but that doesn’t mean we can’t add
some styles to it like we would a container <div>. This will turn it into a box that matches
our <header> element:
.speaker-form {
background-color: #F6F7F8;
border: 1px solid #D6D9DC;
border-radius: 3px;
width: 80%;
padding: 50px;
margin: 0 0 40px 0;
}
Third, the <input/> element creates a text field. It’s a little different from other elements
we’ve encountered because it can dramatically change appearance depending on
its type attribute, but it always creates some kind of interactive user input. We’ll see other
values besides text throughout the chapter. Remember that ID selectors are bad—
the id attribute here is only for connecting it to a <label> element.
.form-row input[type='text'] {
background-color: #FFFFFF;
border: 1px solid #D6D9DC;
border-radius: 3px;
width: 100%;
padding: 7px;
font-size: 14px;
}
.form-row label {
margin-bottom: 15px;
}
The input[type='text'] part is a new type of CSS selector called an “attribute selector”. It
only matches <input/> elements that have a type attribute equal to text. This lets us
specifically target text fields opposed to radio buttons, which are defined by the same
Finally, let’s tweak these base styles to create our desktop layout. Add the following media
query to the end of our stylesheet.
Also notice the new placeholder attribute that lets you display some default text when
the <input/> element is empty. This is a nice little UX technique to prompt the user to input
their own value.
There’s a bunch of other built-in validation options besides email addresses, which you can
read about on MDN’s <input/> reference. Of particular interest are
the required, minlength, maxlength, and pattern attributes.
Let’s not forget about our desktop styles. Update the corresponding input[type='text'] rule
in our media query to match the following (note that we’re preparing for the next few
sections with the select, and textarea selectors):
.form-row input[type='text']:invalid,
RADIO BUTTONS
Changing the type property of the <input/> element to radio transforms it into a radio
button. Radio buttons are a little more complex to work with than text fields because they
always operate in groups, allowing the user to choose one out of many predefined options.
This means that we not only need a label for each <input/> element, but also a way to
group radio buttons and label the entire group. This is what
the <fieldset> and <legend> elements are for. Every radio button group you create
should:
• Be wrapped in a <fieldset>, which is labeled with a <legend>.
• Associate a <label> element with each radio button.
• Use the same name attribute for each radio button in the group.
• Use different value attributes for each radio button.
We also introduced a new attribute called checked. This is a “boolean attribute”, meaning
that it never takes a value—it either exists or doesn’t exist on an <input/> element. If it
does exist on either a radio button or a checkbox element, that element will be
selected/checked by default.
But don’t fret! This is a good example of floats being a useful fallback for
legacy/troublesome elements. You’ll notice that we didn’t wrap the radio buttons in our
existing .form-row class, opting instead for a new .legacy-form-row class. This is because
it’s going to be completely separate from our other elements, using floats instead of
flexbox.
Start with the mobile and tablet styles by adding the following rules outside of our media
query. We want to get rid of the default <fieldset> and <legend> styles, then float the radio
buttons and labels so they appear in one line underneath the <legend>:
.legacy-form-row {
border: none;
margin-bottom: 40px;
}
.legacy-form-row legend {
margin-bottom: 15px;
.legacy-form-row .radio-label {
display: block;
font-size: 14px;
padding: 0 20px 0 10px;
}
.legacy-form-row input[type='radio'] {
margin-top: 2px;
}
.legacy-form-row .radio-label,
.legacy-form-row input[type='radio'] {
float: left;
}
For the desktop layout, we need to make the <legend> line up with the <label> elements
in the previous section (hence the width: 120px line), and we need to float everything to
the left so they appear on the same line. Update our media query to include the
following:
@media only screen and (min-width: 700px) {
/* ... */
.legacy-form-row {
margin-bottom: 10px;
}
.legacy-form-row legend {
width: 120px;
text-align: right;
padding-right: 20px;
}
.legacy-form-row legend {
float: left;
}
}
<div class='form-row'>
<label for='t-shirt'>T-Shirt Size</label>
<select id='t-shirt' name='t-shirt'>
<option value='xs'>Extra Small</option>
<option value='s'>Small</option>
<option value='m'>Medium</option>
<option value='l'>Large</option>
</select>
</div>
Just like our radio button <input/> elements, we have name and value attributes that get
passed to the backend server. But, instead of being defined on a single element, they’re
spread across the <select> and <option> elements.
Style difficulties like this are a serious consideration when building a form. If you need
custom styles, you may be better off using radio buttons or JavaScript UI
widgets. Bootstrap Dropdowns and jQuery Selectmenu’s are common JavaScript solutions
TEXTAREAS
The <textarea> element creates a multi-line text field designed to collect large amounts of
text from the user. They’re suitable for things like biographies, essays, and comments. Go
ahead and add a <textarea> to our form, along with a little piece of instructional text:
<div class='form-row'>
<label for='abstract'>Abstract</label>
<textarea id='abstract' name='abstract'></textarea>
<div class='instructions'>Describe your talk in 500 words or less</div>
</div>
Note that this isn’t self-closing like the <input/> element, so you always need a
closing </textarea> tag. If you want to add any default text, it needs to go inside the tags
opposed to a value attribute.
STYLING TEXTAREAS
Fortunately, styling textareas is pretty straightforward. Add the following to
our [Link] file (before the media query):
.form-row textarea {
font-family: "Helvetica", "Arial", sans-serif;
font-size: 14px;
min-height: 200px;
margin-bottom: 10px;
padding: 7px;
resize: none;
}
We also need a little tweak in our desktop layout. The .instructions <div> needs to be
underneath the <textarea>, so let’s nudge it left by the width of the <label> column. Add
the following rule to the end of our media query:
CHECKBOXES
Checkboxes are sort of like radio buttons, but instead of selecting only one option, they let
the user pick as many as they want. This simplifies things, since the browser doesn’t need
to know which checkboxes are part of the same group. In other words, we don’t need
a <fieldset> wrapper or shared name attributes. Add the following to the end of our form:
<div class='form-row'>
<label class='checkbox-label' for='available'>
<input id='available'
name='available'
type='checkbox'
value='is-available'/>
<span>I’m actually available the date of the talk</span>
</label>
STYLING CHECKBOXES
For the mobile layout, all we need to do is override the margin-bottom that we put on the
rest the <label> elements. Add the following to [Link], outside of the media query:
.form-row .checkbox-label {
margin-bottom: 0;
}
And inside the media query, we have to take that 120-pixel label column into account:
@media only screen and (min-width: 700px) {
/* ... */
.form-row .checkbox-label {
margin-left: 120px;
width: auto;
}
}
By wrapping both the checkbox and the label text, we’re able to use a width: auto to make
the entire form field be on a single line (remember that the auto width makes the box match
the size of its contents).
[Link]?full-name=Rick&email=rick%[Link]&talk-
type=workshop&t-shirt=l&abstract=Derp.&available=is-available
Everything after the ? represents the variables in our form. Each <input/>’s name attribute
is followed by an equal sign, then its value, and each variable is separated by
an & character. If we had a backend server, it’d be pretty easy for it to pull out all this
information, query a database (or whatever), and let us know whether the form submission
was successful or not.
STYLING BUTTONS
We had some experience styling buttons in the pseudo-classes section of the CSS
Selectors chapter. Back then, we were applying these styles to an <a> element, but we can
use the same techniques on a <button>.
color: #FFFFFF;
background-color: #5995DA;
border: none;
border-radius: 3px;
.form-row button:hover {
background-color: #76AEED;
}
.form-row button:active {
background-color: #407FC7;
}
As with our checkbox, we need to take that 120px label column into account, so include
one more rule inside our media query:
SUMMARY
In this chapter, we introduced the most common HTML form elements. We now have all
these tools for collecting input from our website visitors:
• <input type='text'/>
• <input type='email'/>
• <input type='radio'/>
• <select> and <option>
• <textarea>
• <input type='checkbox'/>
• <button>
You should be pretty comfortable with the HTML and CSS required to build beautiful
forms, but actually making these forms functional requires some skills we don’t have yet.
That stuff is out of scope for this tutorial, but it might help to have some context. Generally
speaking, there are two ways to process forms:
• Use the action attribute to send the form data to a backend URL, which then
redirects to a success or error page. We got a little glimpse of this in the previous
section, and it doesn’t require any JavaScript.
• Use AJAX queries to submit the form without leaving the page. Success or error
messages are displayed on the same page by manipulating the HTML with
JavaScript.
Depending on how your organization is structured, form processing may not be part of
your job role as a frontend web developer. If that’s the case, you’ll need to coordinate
closely with a backend developer on your team to make sure the <form> submits the
correct name-value pairs. Otherwise, it’ll be up to you to make sure the frontend and
backend of your forms fit neatly together.
WEB TYPOGRAPHY
Nº 14. of HTML & CSS Is Hard
A friendly tutorial about web fonts and basic typographic principles
A lot of your typography decisions will come from a designer. The only problem is that
typography is an invisible art. To actually understand what your designer is asking for, you
need to be able to see typography the same way they do.
This chapter isn’t just about the mechanics of adding web fonts to your site or the CSS
properties to move your text around. We’ll also explain how to properly leverage all these
tools to make beautiful, professional websites. By the end of the chapter, you should not
only know what your designer is talking about when they say something like, “Can we
You may forget the specific CSS properties, but the typographic concepts we’re going to
cover will stay with you for the rest of your life because they aren’t arbitrary rules—they’re
grounded in function. They make your content more readable and help you communicate
your message more effectively.
This resulted in the “Bulletproof @font-face syntax”, which you’ll likely encounter at
some point in your web development career.
Note that Font Squirrel and Fontspring offer both web fonts and desktop fonts
(.otf and .ttf files). WOFF is designed specifically for the needs of the modern web, while
desktop fonts contain extra functionality useful for graphics editing programs like Adobe
Illustrator. Be sure to download or purchase the web font version of the fonts you want to
use—not just the desktop version.
SETUP
Ok! We’re ready to experiment with web fonts. We’re going to be building this example
website. We figured you probably don’t want to start this one from scratch, so go ahead
and download the initial project. Unzip it and open up the web-typography folder with your
favorite text editor. If you don’t have a favorite text editor, you might want to check
out Atom.
We’ve got 6 HTML documents all using the same [Link] stylesheet. We’ll be
demonstrating various typographic principles by adding some page-specific styles to
each of these HTML files.
This will give you a ZIP file with a license, some instructions, and a web fonts folder
containing a ton of subdirectories. The Roboto font comes in a bunch of different font
faces like light, regular, bold, italic, and condensed. Each of those folders contains a
different face. The one we want is called roboto_light_macroman. Open up that folder and
copy the [Link] file into our web-typography project.
Next, we have the src property, which defines the path to the .woff file via
the url() notation. The path can be absolute, relative, or root-relative. If you use a relative
path like we did here, it will always be relative to the .css file—not the HTML document.
The format() notation lets browsers know which web font file format it is.
If you reload [Link] page, you won’t see any change because @font-face only
gave us access to our .woff file. We still need to use it somewhere else in our stylesheet.
Let’s make Roboto Light the default font for our entire example project by changing
the font-family in the body selector of [Link]:
body {
In our example, Roboto Light is one font face in the Roboto family. The other 17 faces in
the ZIP file we downloaded earlier can be visualized like so:
In CSS, font weights are expressed as numeric values between 100 and 900. Fortunately,
there are relatively standardized, human-friendly terms for each of these numeric values.
“Black” usually means 900, “bold” is 700, “regular” is 400, etc. As you can see above,
most families don’t supply a face for every single weight. Roboto is missing “extra light”
(200), “semi bold” (600), and “extra bold” (800).
It’s worth noting that each style and weight combination is designed as an entirely distinct
face. In a high-quality font family, the condensed styles aren’t simply squashed versions
of the roman faces, nor is the bold face merely a thicker version. Each letter in every face
is hand-crafted to ensure it provides a uniform flow to its text.
FAKIN’ IT
Why does this weight and style stuff matter to us? The design of most websites utilizes
multiple faces in the same family, so we need to know how to embed several .woff files
that represent related faces.
But first, let’s take a look at what happens when we don’t offer multiple faces. Update the
left-hand paragraph in [Link] to include an <em> and a <strong> element:
<section class='section section--gray'>
<h2>Web Fonts</h2>
To verify that the bold and italic faces really are being synthesized, try adding the following
rule to [Link]. The font-synthesis property determines if a browser is allowed to fake it
or not. At the time of this writing, only Firefox actually pays attention to font-synthesis, so
this won’t work in Chrome or Safari:
/* This will only work in Firefox */
em, strong {
font-synthesis: none;
}
Open up [Link] in Firefox, and the <em> and <strong> elements will no longer be
italic or bold—the entire paragraph will be in roman Roboto Light.
@font-face {
font-family: 'Roboto Bold';
src: url('[Link]') format('woff');
}
Then, to use these faces in our <em> and <strong> elements, we need the
following rules:
/* THIS IS A LITTLE AWKWARD */
em {
font-family: 'Roboto Light Italic', serif;
}
strong {
font-family: 'Roboto Bold', serif;
}
This will work, and you should now see proper italic and bold fonts when you reload web-
[Link] in your browser. The problem is that manually specifying the font-family every
time we want to use an italic or bold font is a little weird. We should be using the CSS font-
style and font-weight properties for this.
For this reason, you should never use the above technique to embed multiple faces that
are in the same font family. Go ahead and delete both of the above snippets before moving
on.
@font-face {
font-family: 'Roboto';
src: url('[Link]') format('woff');
font-style: italic;
font-weight: 300;
}
@font-face {
font-family: 'Roboto';
src: url('[Link]') format('woff');
font-style: normal;
Letting the browser know that our font faces are related makes our CSS much more
intuitive. We can set the default font family and weight in our body selector. Then, when
we want to use italics or bold for a particular element, we can simply specify a font-
style or font-weight and the browser will pull the corresponding .woff file:
body {
font-family: 'Roboto', sans-serif;
font-weight: 300;
/* ... */
}
em {
font-style: italic;
}
In this section, we’re going to be working on [Link], so open up that file in both
your text editor and a web browser. If you want a brief history of typography going all
the way back to the first printing press, take a quick read through the example text.
Let’s begin by changing the font for the Gothic/Blackletter section. In Google Fonts,
search for UnifrakturMaguntia. It should look like something a monk wrote in the
middle ages. Click Select this font. In the pop-up menu, you’ll see a <link/> element.
Copy this into the <head> of [Link], above the <link/> element that includes
our [Link] stylesheet.
Remember that <link/> is how we include an external stylesheet, and that’s exactly what
the above HTML is doing. However, instead of linking to a local CSS file, it’s including
some CSS defined by Google Fonts. If you paste the href value into your browser, you’ll
find the same @font-face declaration that we used in the previous section—except we
didn’t actually have to write it this time. Yay!
Google Fonts are a quick and easy solution, but professional sites should typically use
locally hosted web fonts. This gives you a lot more flexibility (you’re not limited to
Google’s font offering) and can have performance/reliability gains if you’ve optimized the
rest of your site correctly.
And that’s more than you could ever want to know about web fonts. The rest of this chapter
shifts gears into basic typographic principles. These are simple guidelines (with simple
CSS implementations) that often make the difference between a professional web page and
an amateur one.
PARAGRAPH INDENTS
Separating paragraphs from one another is one of the most fundamental functions of
typography. There’s two generally accepted solutions: either use a first-line indent or a
margin between the paragraphs. Your readers (hopefully) aren’t stupid—they don’t need
two signs that a new paragraph is happening, so never use both an indent and a margin.
That would be redundant.
And here’s a negative example so we remember what not to do. Add this to the page-
specific styles in [Link]:
TEXT ALIGNMENT
The alignment of text has a subconscious impact on how you read it. You’ve probably
never noticed it before, but your eyes don’t move in a smooth motion as they read over a
paragraph—they jump from word to word and from line to line. Your eyes fixate on certain
spots and skip over other ones.
The next few sections explain the proper times to use left, center, right, and justified text
alignment. All of these examples rely on the text-align property, which controls the text
alignment of a particular HTML element. We set up the [Link] page in our
example project with some convenient scenarios.
Left alignment is the default value for text-align, but if we wanted to be explicit, we could
add the following rule to the <style> element of our [Link] file:
<style>
.left {
text-align: left;
}
</style>
Of course, if you’re working on a website that’s in a language that’s written right-to-left
instead of left-to-right (like Arabic), you can go ahead and swap all this advice with
the Right Alignment section below.
CENTER ALIGNMENT
Center-aligned text doesn’t have that anchor, so it’s easier for the eye to get lost when it
tries to jump to the next line. It’s best suited for short line lengths (more on that later) and
for special kinds of content like poems, lyrics, and headings.
RIGHT ALIGNMENT
Another consideration when choosing text alignment is the relationship it creates with the
surrounding elements. For instance, take a look at that third section in [Link]. We
want to move the image’s caption to the left of the image and right-align it to make it look
like it’s attached to the image:
font-style: italic;
text-align: right;
background-color: #FFFFFF;
position: absolute;
left: -220px;
width: 200px;
}
}
This also happens to be a good example of advanced positioning. The relative position of
the <figure> sets the coordinate system for the <figcaption>’s absolute positioning. By
nudging the caption left by 220px and giving it an explicit width of 200px, we get a nice
20-pixel margin between the image and its caption.
Like centered text, right alignment should usually be reserved for these kinds of special
design scenarios because its jagged left edge makes it harder for the reader to find the next
line.
Unfortunately, most browsers don’t have any kind of built-in hyphenation engine, so
you’re better off avoiding justified text in HTML documents. We can take a look by adding
one more text-align rule to our [Link] file:
.justify {
text-align: justify;
}
Compare this with the left-aligned paragraph. It’s subtle, but the left-aligned paragraph is
more uniform and inviting.
Together, these properties control the “vertical rhythm” of a web page. There’s all sorts of
techniques to figure out the “optimal” vertical rhythm for a given layout, but the general
principles are:
• Give things enough space to breath.
• Use consistent spacing throughout the page.
To demonstrate this, we’re going to destroy the vertical rhythm in the second half of
our [Link] page. Go ahead and add the following page-specific styles
to [Link]
<style>
.messy {
line-height: 1.2em;
}
.messy h2 {
line-height: .9em;
}
.messy:last-of-type {
line-height: 1.5em;
}
.messy:last-of-type h2 {
margin-bottom: .3em;
}
.messy .button:link,
.messy .button:visited {
There’s a surprising amount of math and psychology that goes into calculating the vertical
rhythm of a page, but that’s a job for your designer. As a developer, you need to know the
CSS properties to implement what they’re asking for. More importantly, you have to
understand that your designer really cares about this kind of stuff, so you should be paying
very careful attention to your margin, padding, and line-height properties.
These are the reasons why so many websites (including this one) use fixed-width layouts or
split content into multiple columns on wider screens. Without constraining the width of the
page or dividing it into manageable columns, line length becomes unacceptably long.
In our example project, the [Link] file has decent measure. Let’s see what
happens when we break the bottom half of the page by adding the following to its <head>:
<style>
@media only screen and (min-width: 580px) {
.not-so-manageable {
OTHER BASIC
TYPOGRAPHY GUIDELINES
That should be enough to get you on your way towards quality web typography.
Typography is a whole industry, and we’ve barely scratched the surface. However,
getting any deeper into it would be more design than web development, so we’ll just
leave you with a few final guidelines:
• Use a font-size between 14px and 20px for the body element.
• Use “curly quotes” and apostrophes with the ’, ‘, ”,
and “ HTML entities.
• Use proper dashes (–, —) and other symbols (©).
• Don’t use text-decoration: underline except for hover states.
• Use real italic fonts over synthesized ones if not it’s too much of a performance
burden.
If you find this stuff fascinating, Practical Typography has a fantastic list of general rules
to follow when typesetting a document.
SUMMARY
The goal of this chapter was twofold:
The most important thing you should take away from this chapter is the fact that nothing
is arbitrary in a well-designed web page. The font sizes, indent style, text alignment, line
height, margins, and every other tiny facet of the page was carefully considered. There was
a purpose behind all of these decisions.
All of the CSS properties we’ve covered throughout this tutorial are actually kind of simple.
When it comes down to it, we’ve really just been moving a bunch of boxes around,
changing some colors, and altering the appearance of our text. The meaning behind these
things comes from the underlying design and the business goals of the website you’re
implementing.
But, that’s for another tutorial. Believe it or not, you’ve reached the end of HTML & CSS
is Hard. We’ve covered all the HTML elements and CSS properties you need to build
professional web pages. The only thing missing is experience. Your next step is to practice
all these new skills by building a bunch of web pages from scratch.
Stay tuned for more tutorials!