Mastering HTML and CSS Basics
Mastering HTML and CSS Basics
Alright, so what does it mean to "master" HTML and CSS? You can view it
through the lens of the history of the publishing industry. In the times of the first
printing presses, printers created documents: they set metallic typefaces, dipped
them in ink, and pressed onto paper.
This is somewhat akin to what web developers do now, but instead of metallic
typefaces, they work with HTML and CSS. The goal remains the same: to convey
information as clearly as possible. Some things haven’t changed, like choosing
fonts, header sizes, and line spacing.
Printers made a bunch of pages, which were later bound into a single book. Now,
we create HTML files and link them to form a website.
Let's clarify right away about frameworks like Bootstrap, which are designed to
simplify layout design. We won’t use them in this course for two reasons. First, to
use them, you still need to understand the basics of HTML and CSS. Secondly,
creating a site with pure HTML and CSS will almost always be a better option.
Don’t look for shortcuts; all your efforts will be rewarded. Try to solve all the
practical tasks independently and don't be in a hurry to peek at the solutions. The
more diligently you complete all the preliminary practices, the easier it will be for
you with the final projects after each chapter.
Also, avoid copying and pasting – type each line of code symbol by symbol
manually. Why? It helps to develop muscle memory, which is crucial in the early
stages. You'll think about optimization in due time, but for now, creating a page
from scratch each time shouldn't be an issue for you, just as professional Starcraft
players aren’t bothered by rebuilding their base from scratch in every new game.
To recap:
Work Tools
Everything you need to complete this course is a browser and a regular text editor.
In the text editor, we write code, and in the browser, we see the result. In the
future, other tools will be added to our arsenal, but for now, you can start with the
bare essentials.
The only requirement for the browser is its up-to-dateness. Chrome and Firefox are
the most popular browsers among developers. Safari is okay, but just barely, and
everything else is a no-go. (Yes, web development implies testing code in multiple
browsers immediately, but we'll set that aside for now.)
With the text editor, it's simpler. Even Notepad 🗒 will do. However, it's better to
install one of the specialized code editors. At the very least, they highlight syntax
and display the file structure clearly.
Within this course, we will be using Visual Studio Code🔗 (or just Code). It's
intuitive even for beginners, yet loved by professionals and available for all
operating systems. Code can also be endlessly customized to your preferences
thanks to numerous built-in plugins.
Download it right now and open it. You should be greeted with a welcome tab like
this:
Feel free to close it using the keyboard shortcut Cmd+W (Mac) or Ctrl+W
(Windows/Linux). By the way, keyboard shortcuts are extremely useful; try to use
them all the time!
Now in front of you, you'll see a prompt with keyboard shortcuts to get started,
which you'll soon know like the back of your hand.
It's considered uncool among programmers to use the mouse, so always try to use a
keyboard shortcut for any operation if one exists.
To recap:
• The primary tools of a frontend developer are a text editor and a browser.
• Mastering the text editor is a key task when learning HTML and CSS.
• VS Code is the go-to choice.
• Keyboard shortcuts are crucial too.
Working with Files Before we start writing code, let's create a new file using
Cmd+N. On Windows, use Ctrl instead of Cmd.
Enter some random text in the tab on the right, then press Cmd+S to save the file.
Let's name it [Link]:
Create another file in the same manner, but this time name it [Link].
One of the most critical aspects of a good text editor is the ability to navigate
quickly through all project files. In VS Code, we immediately see all the files we're
working with on the left or can find the necessary file through search. You can also
use Ctrl+Tab to quickly switch between files you're editing.
Very often, we need to find something specific in our files. Imagine we've
discovered a broken link on the site and want to locate it immediately to fix the
problem as quickly as possible.
The Cmd+F shortcut performs a string match search within the file. Meanwhile,
Cmd+Shift+F allows you to search across all project files. Let's try entering
'random':
The Cmd+P shortcut allows you to search by any part of the file name. Let's try
closing both tabs, pressing Cmd+P, and entering "i": the [Link] file should
appear immediately. This function is indispensable when the project grows to
dozens of files scattered across different folders.
VS Code allows us to open not only multiple file tabs simultaneously, but also
multiple workspaces. To understand what we're talking about, right-click on one of
the files and select Split Right. This file will open in a new workspace. This way,
you can view multiple files at the same time.
This is a very useful feature for cases when we need to simultaneously view related
CSS and HTML files.
We will also be working a lot outside of Code. For this, we need to open files in
our operating system. Right-click on a file in Code and select Reveal in Finder
(Reveal in File Explorer on Windows) to open the folder containing that file on our
computer.
Since we can view the structure, add new files, and create folders right in Code, we
will predominantly use this feature to open HTML files in the browser with a
double-click. If we want to open it in another browser, we can drag the file onto
that browser's icon in the dock or select it through the context menu "Open with."
Now you can edit the contents of [Link] in Code, save it, and then reload the
page in the browser using Cmd+R to see the changes. For instance, replace the
random text with: "Access denied. Access to the information resource is restricted
in accordance with the Federal Law of July 27, 2006, No. 149-FZ "On Information,
Information Technologies, and Information Protection". Now it looks like a
counterfeit of a real website on the internet.
This is what a frontend developer's routine looks like, to which we'll get
accustomed during this course. Ensure everything is clear before moving on.
Practice with hotkeys, add more files, train on nested files and folders. For
additional guides on the text editor, you can refer to video tutorials
at [Link] 🔗.
Let's Recap:
HTML defines the content of every page on the Internet. By marking up the source
content with HTML tags, we tell the browser how we want to see different parts of
the content. Where there will be a header, where a list, and where a simple
paragraph, the web designer decides at this stage.
In this lesson, we will create our first web page. Let's note upfront that it will look
quite basic. However, through examples, we will get acquainted with HTML
elements that developers use every day.
When completing the tasks from this lesson, you can use the same approach as
when working with editors like Google Docs or Microsoft Word. We'll have
similar content (headers, paragraphs, lists, etc.), but we will set them not through
a visual interface, but by coding directly in the text.
Let's start by creating a folder named 'basic' and open it as a project in Code.
Inside, we'll create a new file named [Link], in which we will work.
By the way, [Link] differs from all other .html files in that the browser will try
to load it specifically when we don't specify the file name in the URL. For
instance, if we only indicate the path to the 'basic' folder in the browser's address
bar, [Link] will open without any additional reference to it.
Recap:
Let's add HTML markup to the [Link] file - this is where the creation of any web page
should start. Typically, templates are used for this to avoid rewriting repetitive parts of the
code each time. But in this case, we are looking at the very basics of HTML, so we'll copy
the code below in full:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
</body>
</html>
First, the browser needs to understand that this is a modern HTML5 web page, which is
indicated using the <!DOCTYPE html> tag. This is a special element that the browser
recognizes when loading a web page, and it should always be at the top of the page.
Now, our entire web page must be enclosed within <html> tags. Tags like <tag> are called
"opening tags," and </tag> are "closing tags." In our example, <html> is an opening tag,
while </html> is a closing tag.
The terms "tag" and "element" are often used interchangeably, but there is a distinction.
Elements are tags with all their content (found between them). For
instance, <p>Paragraph</p> is an element consisting of a pair of p tags and the text
'Paragraph'.
Inside <html>, there are two other elements called <head> and <body>. The <head> element
contains all the service metadata of the page, like the tab name, link to the CSS file, and other
things required for rendering the page but which aren't displayed on the page itself.
The majority of our HTML markup will be located in the <body> element, which is
responsible for the visible part of the page. Note that if we open the page in a browser now,
we won't see anything, as the <body> is currently empty.
Recap:
One of the most essential components of metadata is the web page's title,
determined by the <title> element. Browsers display the title on the page's tab, and
Google displays them in search results.
When we reload the page in our browser, we'll again see an empty white space.
However, the title will now be present on the tab.
Pay special attention to the placement of HTML tags. It's crucial that tags do
not overlap each other. For instance, the <title> element must be inside the
<head>. Therefore, under no circumstances should you add the closing
</head> tag before the closing </title> tag.
Now, let's add elements that are displayed on the page itself. For example, the <p>
element (for 'paragraph'): all the text inside it is marked up as a separate paragraph.
Let's add a <p> inside the <body> of our webpage:
<!DOCTYPE html>
<html>
<head>
<title>You've won a million!</title>
</head>
<body>
<p>Claim your prize</p>
</body>
</html>
Now, there's text on our page. Note that this text should be placed precisely
between the <body></body> tags, not inside the <head></head> tags, where only
meta information should be, which is not displayed on the page.
Notice that the <p> and <title> elements are indented more to the right than the
<body> and <head>. Spacing or indentation is crucial for creating readable code
where the hierarchy of elements is clear at a glance.
Now, let's add a heading. HTML provides a whole six tags for headings – enough
even for a scientific article: <h1>, <h2>, <h3>, <h4>, <h5>, <h6>, from the most
important to the least.
Most often, <h1> will be enough for us, so let's add it right before our <p>. The
<h1> heading does not overlap with the <title>'s meaning but will also be taken
into account by search engines.
<body>
<h1>$1 000 000</h1>
<p>Claim your prize</p>
</body>
By default, browsers display less significant headings in smaller fonts. Let's insert
a second-level heading and see what happens:
<!DOCTYPE html>
<html>
<head>
<title>You've won a million!</title>
</head>
<body>
<h1>$1 000 000</h1>
<p>Claim your prize</p>
<h2>Offer expires in 59 seconds</h2>
</body>
</html>
Let's recap:
Every time we enclose a part of text in HTML tags, it adds a new meaning to the
text. The <ul> tag (unordered list) informs the browser that it is dealing with an
unordered list. The <li> tag is used to represent items in such a list.
<h2>Lists</h2>
<p>Here's how you create unordered lists:</p>
<ul>
<li>Add the 'ul' element</li>
<li>Add each item in an 'li'</li>
<li>The order in these lists doesn't matter</li>
</ul>
After adding this markup in the <body>, we should see an unordered list like this:
HTML has strict rules about which elements can be nested inside others. In this
case, the <ul> element should only contain <li> tags, so while the following code
won't break the page, it is considered invalid:
<ul>
<p>...</p>
</ul>
To understand which elements can be nested inside others, you can refer to MDN
Web Docs, which is an unmatched resource for HTML standards. Throughout the
course, we will try to cover as many basic HTML elements as possible, but if you
ever have doubts about something, just Google 'MDN <element name>'.
When dealing with an unordered list, swapping the positions of <li> elements
won't affect the semantic meaning. If you want the order of items to matter, you
should use ordered lists.
To create an ordered list, simply replace <ul> with <ol> (ordered list):
<p>Here's what an ordered list looks like:</p>
<ol>
<li>All list items are inside the 'ol' element</li>
<li>Numbering increments automatically</li>
<li>Order is crucial in such lists</li>
</ol>
When you reload the page in the browser, you'll see that the list items are
automatically numbered. When we get to CSS, we'll learn how to style these lists
and change the appearance of bullets and numbers or even disable them.
To easily remember the difference between these tags, you can note that the letters
'o' and 'l' resemble the numbers '0' and '1,' respectively. Thus, <ol> represents a list
with numeric items.
Let's recap:
So far, we have been working with block elements. Another important type of
elements is inline elements. What's the difference?
Block Elements Block elements are the foundation of web page layout. They are
intended for building the structure of a page, formatting large blocks of text, and
much more. Block elements always start on a new line and occupy the entire
available width of their parent (their immediate container). The height of a block
element depends on its content. Using CSS properties, you can set the width and
height of a block element.
Block elements can contain both inline elements and other block elements, for
which their parent will be the container. Examples of block elements include
<div>, <header>, <ul>, <h1> - <h6>, <figure>, <footer>, <form>, <li>, <nav>,
<ol>, <p>, <pre>, <section>, and more.
Inline Elements Inline elements are elements that are part of a line of text. They
occupy the space required to display their content. Inline elements cannot have
width and height set, nor can you specify top and bottom margins (margin-top and
margin-bottom) for them.
Examples of inline elements (which have display: inline by default) include <a>,
<abbr>, <audio>, <b>, <button>, <canvas>, <cite>, <code>, <em>, <i>, <iframe>,
<img>, <input>, <small>, <select>, <span>, <strong>, <td>, <textarea>, <video>,
and more.
For example, <p> is a block-level element, while <em> (short for emphasis) is an
inline element used to emphasize a portion of text and is typically displayed in
italics on the screen and with voice intonation in voice interfaces.
The text enclosed in <em> should be displayed in italics. Note that only a part of
the line is highlighted - this is a characteristic of inline elements.
Sometimes, you might wonder which tags to use, <i> (italic) or <em>, <b>
(bold) or <strong>, as they look exactly the same visually. The main difference
between <i> and <em> is that the former simply applies italic styling to the
text, while the latter (em) adds meaning and emphasizes the enclosed text. A
similar situation applies to <b> and <strong>, where the former just makes
the text bold, while the latter emphasizes it with special importance.
Let's see how <strong> looks in practice. The browser should render it as bold text,
typically using a bold font style that comes with the system. However, if that's not
available, the weight will be added programmatically, which may sometimes
annoy perfectionist designers.
<p>And sometimes, we need to emphasize a word <strong>boldly</strong> to
highlight its significance.</p>
For leaflets like 'Buying Hair,' there is an option to use the <strong> and <em> tags
in tandem, one inside the other. This way, you can have text that is both italicized
and bold:
<p><em><strong>Sometimes, we need both.</strong></em></p>
Let's recap:
• Block elements occupy the entire available width and are displayed one
below the other on new lines.
• Inline elements take up only as much space as they need and are displayed
one after the other in the same line.
• Headings, paragraphs, and lists are block elements, while images, links, and
buttons are inline elements.
• Inline elements <em> and <strong> are responsible for visually and
semantically emphasizing text.
All the elements we've encountered so far have either had textual content or have
been nested within other elements. For such elements, it's important to denote their
beginning and end with opening and closing tags.
Examples of such elements include <br> (line break) and <hr> (horizontal rule).
While the first tag simply moves the text to the next line, the second one also adds
a horizontal line.
<p>Flight to Miami<br>
Arrival at MIA<hr>
Specifically closes<br>
Didn't have time to feed the pigeons</p>
<hr>
Due to the additional spaces and line breaks in the code editor, this formatting
won't be achieved. The browser will ignore all unnecessary formatting. This can be
useful for formatting text in the editor as needed.
When we want to instruct the browser to insert a line break, we use the <br/> tag.
The <br/> element is especially useful in cases where text formatting is crucial.
Haikus, song lyrics, and captions are just a few instances where this tag can come
in handy.
But you should use the <br/> tag very carefully and avoid overusing it. It should
never be used just to add spaces between paragraphs, like this:
<!-- (Don't do this!) -->
<p>This paragraph below needs some space...</p>
<br/><br/><br/><br/><br/><br/><br/><br/>
<p>So let's add line breaks.</p>
The <hr/> element represents a thematic break in the content, such as a thematic
shift in a letter between the main content and a postscript. Here's an example of
when the <hr/> tag can be useful:
<h2>Self-Closing Tags</h2>
<p>With love,<br/>
SLSchool</p>
<hr/>
<p> P.S. While this page doesn't look great right now, we'll fix it soon
with CSS.</p>
One of the main themes of this course is the separation between content (HTML)
and presentation (CSS), and the <hr/> tag is also related to this. Just like with
<em/> and <strong/>, once we start working with CSS, we can style the <hr/>
element as we see fit.
Like <br/>, <hr/> should be used thoughtfully, not just whenever you want a line
to appear for aesthetic purposes. For styling such elements for visual beauty, it's
better to use CSS tools, which we will learn about in the next chapters.
The <hr/> element can also be seen as less significant than a new heading but more
significant than a new paragraph.
The placement of the slash (/) in all empty HTML elements is purely optional. The
code example above can be marked like this (note the absence of / in the <br/> and
<hr/> tags):
To recap:
In the previous chapter, we covered the most important HTML elements within a
single web page. Now, we'll explore links and images, which interact with external
resources. Links direct us to other HTML documents, while images display
external resources on a web page.
Before we dive into using links and images, we need to learn about another
essential element of HTML syntax: attributes. Attributes open up a whole new
world of possibilities for creating web pages.
Recap
Links are created using the <a> element, also known as an 'anchor.' This element
works similarly to all the elements we discussed in the previous chapter: when we
wrap text with the <a> tag, it changes the nature of that text. Let's see this in action
by adding the following code to the <body> of the '[Link]' file: <p>This
example is about links and <a>images</a>.</p>
When we load this page in the browser, we'll immediately notice that the <a>
element doesn't look like a typical link. That's because the <a> tag doesn't do
anything on its own.
In the same way that elements add value to the content within them, HTML
attributes define the elements they are attached to.
Different elements allow the use of different attributes. You can find more
detailed information about attribute-element correspondences on MDN 🔗.
Let's examine the <href> attribute, which determines the destination that users will
navigate to after clicking on the <a> element. Add the following code to your link:
<p>This example is about links and <a href="[Link]">images</a>.</p>
Notice how attributes are written inside the opening tag. We start with the attribute
name, followed by an equal sign (=), and finally, the attribute's value in double
quotes. This specific format distinguishes attributes from content. The <href>
attribute informs the browser that the <a> element is a link.
Now let's take a look at an example of creating an anchor link:
<a href="#chapter-one">Go to Chapter One</a>
In this example, the link's destination starts with a '#' symbol, followed by the
anchor's name ('chapter-one'). Below, we have a <h2> heading, which must have
the 'id' attribute assigned with the same value as the anchor name (without the '#').
Therefore, when you click on the link, it will take you to the corresponding
element, in this case, the "Chapter 1" heading.
To recap:
• Absolute
• Relative
• Root-relative
<ul>
<!-- Add <li> elements here -->
</ul>
• Absolute Links
Absolute links are the most detailed way to redirect to a web resource. They start
with a protocol (usually http:// or [Link] followed by the domain name of the
website and the path to the page:
Let's try creating a link that will redirect us to MDN Web Docs🔗:
<li>This is an absolute link:
<a href="[Link]
Developer Network</a>.
</li>
• Relative Links
Relative links lead to another file on our website directly from the file we are
currently editing. It is assumed that the protocol and domain name match, so you
only need to add the path.
In this example, the href attribute specifies the path to [Link] from [Link].
Since [Link] is not in the same folder as [Link], we need to add the misc
folder to the URL.
Each folder and file in the path we specify is separated by slashes (/). So, if you
want to reach a file that is inside two folders, the URL should look like this:
misc/other-folder/[Link]
But what if the page is located in a directory above the current file? Let's find out
by creating relative links to [Link] and [Link] from the [Link] file
and adding the following code:
<p>This page is about various HTML stuff, including <a
href='[Link]'>links</a> and
<a href="[Link]">images</a>.</p>
If we click on any of these links in the browser, it will show a message that the
page does not exist. Let's look at the address
bar: misc/[Link] and misc/[Link] – it turns out that the browser is trying to
load the wrong folder! This happens because our links refer to the [Link] file,
which is located in the misc folder.
To fix this, we need to add two consecutive dots '..'. They denote the path to the
root folder:
<p>This page is about various HTML stuff,
including <a href="../[Link]">links</a>
or <a href="../[Link]">images</a>.</p>
This way, we tell the browser, "I know that the [Link] file is in
the misc folder, so look for [Link] and [Link] in the directory above."
../../[Link]
Relative links are useful because they allow you to move within folders without
updating all href values in <a> elements. However, they can also be confusing
when used too frequently. Therefore, it's best to use them when you need to specify
the path to the current folder or within a specific section of the website.
For example, all the images in this course are loaded using relative links. This
allows us to update the titles of our chapters without changing the paths for loading
images.
• Root-Relative Links
Root-relative links are similar to links from the previous section, but they are
relative to the root directory of the entire website, not the current page. For
example, if our website is hosted at [Link], all root-relative links would refer
to [Link].
Unfortunately, there's one catch: throughout the course, we will be using local
HTML files from our computer rather than a website hosted on a web server.
Therefore, we won't be able to experiment with this type of link. But if we did have
a real server, a link to our homepage would look like this:
<!-- This won't work in our case -->
<li>Root-relative link, for example, to the <a href="/">homepage</a> of our
website.</li>
The only difference between a root-relative link and a relative link is that the
former starts with a slash, which indicates the root directory of the site. We can
also add additional folders and files after the first slash, just like with relative links.
This path will work regardless of where the current page is located:
/[Link]
Root-relative links are one of the most useful types of links. They are detailed
enough to avoid potential confusion, as with relative links, but not as specific as
absolute links.
• Target Attribute
Let's replace our absolute links in the [Link] file with the following code:
<li>This is an absolute link:
<a href="[Link]
target="_blank">Mozilla Developer Network</a>.
</li>
This attribute has several predefined values, with the most common one being
"_blank". This value is responsible for opening a new tab or window.
• Naming Rules
You may have already noticed that there are no spaces in the names of folders and
files. This is not a coincidence: spaces should be avoided in URLs! To see this in
practice, let's create a new file in the links-and-images project with the name
"spaces are [Link]." Add some text to it and try opening it in Google Chrome or
Safari (it won't work in Firefox, by the way):
links-and-images/spaces%20are%[Link]
In the address bar, you will immediately see that all spaces have been replaced
with %20. This happens because spaces are not allowed in URLs. Instead of
spaces, you should use hyphens, as we have been doing throughout the course. It's
also a good practice to use only uppercase letters.
It's important not to forget the direct connection between file or folder names and
the URLs of web pages: they determine a unique identifier string, also known as a
human-readable URL (or "clean URL"). This URL is visible to the user, so it
should receive as much attention as the content on the web page.
These rules apply not only to HTML, CSS, and JavaScript files but also to images.
Recap:
Unlike all the HTML elements we've worked with before, images are located
outside the page that displays them. But that's not a problem because we already
know how to specify the path to content that is outside an HTML document using
absolute, relative, and root-relative links.
To place images on a web page, we use the <img/> tag and its <src> attribute,
which specifies the file we want to display.
<img src="[Link]"/>
There are four main image formats for the web, and each of them serves a different
purpose. Understanding the purposes of each image type is key to creating a high-
quality web page.
Next, we will specifically consider when to use each of the image types.
JPG allows you to work with a wide range of colors while keeping file sizes
reasonable. Therefore, JPG images are well-suited for photographs and images
with gradients. However, this format does not support transparent pixels, which is
especially noticeable on white image borders like the one below:
Let's add the image [Link] to our [Link] page by using the following
code:
<p>This page is about the most popular image formats, but you can also find
some <a href="[Link]">links</a> and <a href="misc/[Link]">useful
bonuses</a> here.</p>
<h2>JPG</h2>
<p>JPG is excellent for photos.</p>
<img src="images/[Link]"/>
GIFs are suitable for simple animations, but they should not be used for photos due
to their limited color palette.
Let's add this illustration to the [Link] file:
<h2>GIF</h2>
<p>GIF is suitable for animations.</p>
<img src="images/[Link]"/>
PNG format can be used for everything except photographs and animated images.
PNG is commonly used for icons, diagrams, logos, and similar purposes.
Let's add this image to our project:
<h2>PNG</h2>
<p>PNG – for diagrams and icons.
</p><img src="images/[Link]"/>
Unlike all the previous formats, SVG is a vector format, not a raster one. This
means that the size of SVG can be changed without any loss in quality. It is usually
used for the same purposes as PNG:
Originally, the images above were 100x100 pixels in size, but we increased them
to 300x300 to make the difference between SVG and PNG more obvious.
SVG can also be used just like raster formats. Let's add [Link] to the
[Link] page:
<h2>SVG</h2>
<p>Try to use SVG whenever possible.</p>
<img src="images/[Link]"/>
SVG has one caveat: for proper display in different browsers, all text in the image
needs to be converted to vectors, for example, using Adobe Illustrator or Sketch.
This can significantly increase file sizes.
Recap:
By default, the <img/> element uses the dimensions of the image itself. To make
our raster images smaller, we can use the width attribute of the <img/> element.
Let's make the following changes to the [Link] file:
<!-- In the JPG section -->
<img src="images/[Link]" width="150"/>
<!-- In the GIF section -->
<img src="images/[Link]" width="150"/>
<!-- In the PNG section -->
<img src="images/[Link]" width="150"/>
<!-- In the SVG section -->
<img src="images/[Link]" width="150"/>
A highly valuable practice is to add the alt attribute to <img/> elements. This
attribute provides alternative text for the image. Such text is essential for search
engines and users with text-based browsers or screen readers.
Recap:
• The width attribute sets the width of the image.
• The height attribute sets the height.
• Don't forget about the alt attribute, which provides alternative text for
accessibility.
Now that we are more or less comfortable with HTML attributes, let's delve into
the final important points related to them. Every webpage we create should have a
specified encoding and language in which it is written.
The language of the webpage is determined by the "lang" attribute of the <html>
element. In our files, everything is written in English, so we will use "en" as the
value of the "lang" attribute:
<html lang="en">
If you are unsure about the code that corresponds to your language, you can look it
up 🔗 in the list under the Subtag section.
Encoding is like an alphabet for your browser. Encoding differs from the
document's language in that it only affects how the characters themselves are
displayed. Let's copy a couple of international characters to the "misc/[Link]"
page and see what happens:
<h2>Character Set</h2>
<ol>
<li>bir</li>
<li>iki</li>
<li>üç</li>
<li>dört</li>
<li>beş</li>
</ol>
When you load the page in the browser, instead of ü, ç, ö, and ş, you will see the
following:
If you see any strange characters instead, it means that the default encoding of the
browser does not support them. To fix this, we will add a <meta> element with the
"charset" attribute set to UTF-8 in the <head> of our "misc/[Link]" file:
<meta charset="UTF-8"/>
To recap:
HTML Entities The characters <, >, and & are called special characters because
they cannot be included in an HTML document without encoding. The reason for
this is that they already have specific meanings in the HTML language: for
example, the '<' character opens a tag, '>' closes it, and '&' defines an HTML
special character.
HTML entities always start with an ampersand (&, which you can find on your
keyboard near the digit 7) and end with a semicolon (;). Between them, a special
code is written that the browser recognizes as a character. In this specific case, the
browser interprets lt, gt, and amp as less than, greater than, and ampersand
symbols, respectively. There are plenty of HTML entities, and if you are
interested, you can explore them on your own 🔗. If you pay attention to
typography, quotation marks will become one of the most commonly used HTML
entities. There are four types of quotation marks: opening, closing, single, and
double.
• “
• ”
• ‘
• ’
In ancient times, using special characters in HTML files was prohibited, which is
why HTML entities became such a convenient tool. But now, when we use UTF-8
encoding, you can insert any characters directly into an HTML document. Today,
HTML entities are primarily used as special characters.
To recap:
In the first three chapters of this course, we focused our attention on HTML. Now it's time to
add some beauty with CSS (which stands for Cascading Style Sheets). CSS is responsible for
the design of a web page: font size, margins, and colors. At the same time, CSS uses a
language fundamentally different from HTML.
HTML is responsible for the content of our page, while CSS determines how this content will
be presented to the user. It's this fundamental difference, which we'll revisit many times
throughout the course, that underpins modern web development.
CSS provides a conceptual framework that allows us to tell the browser things like, "I want
the headers to be large, and the sidebar to be on the left." HTML simply doesn't have the
tools to do this - all we can say is, "This is a header, and this is a sidebar."
In this chapter, we will study the basics of CSS syntax and learn how to connect CSS with
our HTML documents. Our main goal is to understand how CSS and HTML interact with
each other. So don't stress if you don't remember all the technical details right away: the main
thing is to grasp the essence!
To Recap:
CSS Styles CSS styles reside in text files with the .css extension and contain CSS
rules. These are statements that specify how CSS is applied. Let's create a new file
named [Link] in the hello-css folder and add a CSS rule to check if everything
is correctly linked to our HTML pages.
body {
color: #FF0000;
}
A CSS rule always starts with a selector, which determines which HTML elements
the rule applies to. In the example above, we are working with the <body> element,
and 'body' is the selector. After the selector, there is a block with properties in
curly braces that will be applied to the elements selected by this selector. In our
case, it's the <body> element.
In this regard, rules are similar to HTML attributes because they also work with
paired parameters like "key-value." However, in the case of CSS, we work with
presentation information, not the semantic meaning of content.
If we try to load any of the HTML pages in a browser, we will see that the CSS
styles do not work – this is because we haven't connected them yet. To do this, we
use the HTML <link/> element. Let's update the <head> in our [Link] file:
<head>
<meta charset="UTF-8"/>
<title>Hello, CSS</title>
<link rel="stylesheet" href="[Link]"/>
</head>
The <link/> element allows browsers to determine how to load [Link] when
they open the [Link] page. Now all the text on our page should be colored
in a venomous red:
The <link/> element is similar to the <a> element, but it can only be used inside
the <head>. This element links metadata located outside the current document. By
the way, it is an empty element, which means it does not need a closing tag.
The 'rel' attribute defines the relationship between the resource and the HTML
document. The most common value for this attribute is 'stylesheet,' but there are
others. The 'href' attribute works the same way as in the previous chapter, but now
it points to a .css file, not a web page. The 'href' value can be an absolute, relative,
or root-relative link.
It's worth noting that there is no direct connection between the browser and CSS
styles. The browser can only find this connection through HTML markup. An
HTML page links CSS, images, and even JavaScript – essentially, this is the core
of most websites.
We've connected the styles, which means we can experiment a bit! Honestly, this
red color is just dreadful. Let's replace it with a pleasing gray:
body {
color: #414141;
}
By the way, comments in CSS are different from comments in HTML: instead of
<!-- -->, CSS uses /* */.
To Recap:
You can add as many properties as you like to a properties block. Let's change the
background color of our page by adding the following to the CSS rule:
body {
color: #414141;
background-color: #EEEEEE;
}
Of course, styles can be applied not only to the <body> element. Let's add a few
more CSS rules with different selectors. For example, you can change the font size
of <h1> and <h2>:
body {
color: #414141;
background-color: #EEEEEE;
}
h1 {
font-size: 36px;
}
h2 {
font-size: 28px;
}
Many CSS properties require the inclusion of units. There are many units, but the
most common ones are 'px' (pixels) and 'em.' Pixels are self-explanatory, but what
about 'em'? 'Em' is a value relative to the current font size, from which you can
take any proportions like 1em, 2em, and so on.
Let's consider these units with a specific example by making the following changes
to the code from the previous subsection:
body {
color: #414141;
background-color: #EEEEEE;
font-size: 18px;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.6em;
}
We set the current font size to 18px, then specified that <h1> elements should be
twice that size, and <h2> should be 1.6 times that size. If we need to change the
current font size in the future, 'em' will allow us to do it proportionally.
What if we want to apply styles to all our headings at once? It's better not to write
CSS rules for each individual heading – it can be done much faster by selecting
multiple HTML elements and separating them with commas. Let's add the
following to the [Link] file:
h1, h2, h3, h4, h5, h6 {
font-family: "Helvetica", "Arial", sans-serif;
}
In this way, we've set the font for all headings with a single CSS rule. If we ever
need to change this font, we can do it in one place. Avoiding copy-pasting and
using these techniques is a better practice.
The font-family property is another property responsible for the type of font in the
selected element. This property takes various values because the set of installed
fonts can vary from user to user. In the example above, the browser first loads
Helvetica, and if that font is not available, it falls back to Arial, and in the worst
case, it displays a sans-serif system font.
Working with system fonts has always limited web designers. But today, web fonts
have replaced system fonts. We'll explore this topic further in the "Web
Typography" chapter.
The list-style-type property allows you to change the type of markers used
in <li> elements. You should set the values of this property
in <ul> or <ol> elements:
ul {
list-style-type: circle;
}
ol {
list-style-type: lower-roman;
}
You can find other commonly used values on the [Link] page. It's worth
mentioning 'none,' which is used to style navigation menus in a <ul> list. The 'none'
value allows you to make menu items look like buttons.
By the way, menus are a clear example of separating content and presentation. A
navigation menu is an unordered list, but it's more logical to display buttons
instead of standard markers.
At first glance, adjusting text color and marker appearance might seem like minor
tasks. However, let's look at the bigger picture – the combination of such
seemingly small details contributes to the overall appearance of a great web page.
To Recap:
After setting up basic styles for one web page, it would be very convenient to use
them on all others as well. All we need to do for this is to add a <link/> element to
the page to which we want to apply the style. Let's add the following code to
the <head> of the [Link] file:
<link rel="stylesheet" href="[Link]"/>
Now the styles on both [Link] and [Link] pages should match, and
changes in [Link] will automatically affect both of our pages. This way, we can
achieve consistency across all pages of the website.
Almost always, there is one CSS style that applies to the entire site. To link styles
together, it's best to use root-relative links to avoid problems with nested pages.
For example, some-folder/[Link] can use ../[Link] to refer to our
[Link] file, but dealing with this can get confusing quickly.
Throughout the course, we will explore many more CSS properties, but for now,
let's focus on the most common ways to format text.
a {
text-decoration: none;
}
As you can easily guess from its name, the text-align property is responsible for
text alignment within an HTML element:
p {
text-align: left;
}
Other possible values are right, center, or justify. Note that this property is
applied to the entire page at once, which is not very convenient when working with
a website. Therefore, in the next chapter, we'll start exploring CSS boxes.
The font-weight property controls the thickness of the text in our element,
and font-style determines whether it's italic or not.
For example, let's say we don't want our headings to be bold. We can update our
heading-related CSS rule in the [Link] file:
h1, h2, h3, h4, h5, h6 {
font-family: "Helvetica", "Arial", sans-serif;
font-weight: normal;
}
These properties vividly illustrate the separation between content (HTML) and its
presentation (CSS).
To Recap :
The cascade part of CSS is called so because multiple rules cascade down from
multiple sources at once. So far, we have defined CSS in one place - in external
.css files. However, external stylesheets are just one of many places where you can
put your CSS code.
The CSS hierarchy for each web page looks like this:
This list is arranged in increasing order of the styles' importance, which means
each step overrides the previous ones. For example, inline styles can make the
browser ignore default styles. In the following subsections, we'll look at the last
two steps in the hierarchy because those are what web developers need to control.
The <style> element is used to add CSS rules to individual HTML documents.
The <style> element is always placed in the <head> of the web page because it
pertains to metadata rather than content. Let's update the <head> of our
[Link] page:
<head>
<meta charset="UTF-8"/>
<title>CSS Styles</title>
<link rel="stylesheet" href="[Link]"/>
<style>
body {
color: #0000FF;
}
</style>
</head>
These changes only apply to [Link] and won't affect the [Link] page.
After loading [Link] in the browser, we should see a bright blue color.
Anything we can do in [Link], we can also do in the <style> element. The CSS
syntax is the same as in external style sheets, but anything we add will override the
CSS rules in the [Link] file. In this case, we're telling the browser to ignore the
color property set in the <body> of the external style sheet and use #0000FF instead.
But this type of styling has its issues – it can be challenging to work with. You
have to copy and paste styles into the <head> of another document if you want to
apply them there. This makes it harder to locate unnecessary CSS rules since you
have to search for them in multiple HTML files, not just one CSS file.
Page-specific styles might be useful when time is limited, but most of the time, it's
better to keep CSS in external style sheets rather than in <style> elements.
You can also specify CSS rules in the style attribute of any HTML element. In the
[Link] file, we have a link that goes nowhere. Let's highlight it in red using
an inline style to emphasize that it doesn't work:
<p>Want to strike through <a href="[Link]"
style="color: #990000; text-decoration: line-through;">a dead link</a>?
Sure thing!</p>
As you can see, we're using the same CSS syntax as before, but because it's an
attribute, it needs to fit on one line. Inline styles are the most specific way to
specify CSS. The color and text-decoration properties override all other
properties.
Inline styles should be avoided because they make it impossible to change styles
from an external style sheet. If you ever decide to change your site's design, you'll
have to go through each individual page and manually modify each HTML
element with the style attribute. It's a ton of unnecessary work!
To add styles to specific HTML elements, you should use CSS classes. We'll cover
those in one of the upcoming chapters.
You can add CSS rules to multiple external style sheets at once by
using <link/> elements on the same page. This is often used to separate styles into
different sections of a site.
For example, if you have many product description pages that look different from
your blog, you can use the following code:
The order of the <link/> elements is crucial: typically, you would add your default
styles to the global style sheet ([Link]) and then supplement them with styles
related to specific sections of your site ([Link] and [Link]). This allows you
to organize CSS rules into convenient files, avoiding all the pitfalls of inline styles
and page-specific styles.
To recap:
In the previous chapter, we learned about the basic CSS properties used for text
formatting. However, that's just the tip of the web development iceberg! Now, we
will delve into another crucial aspect of CSS—web page layout, or the way a web
page looks.
The CSS Box Model is a set of rules that determine how each web page is
displayed. CSS perceives each HTML element as a box with a specific set of
properties that dictate where the box appears on the page. Until now, all our pages
consisted of elements displayed by the browser one after the other. The CSS Box
Model will become our primary tool for customizing the default layout.
You can see a great application of CSS Box Model rules here 🔗.
One of the main tasks for web developers is to use CSS Box Model rules to create
a web page. As we progress through this chapter, you might wonder, "Why bother
memorizing all these rules when we can simply upload a static image of the web
page (mockup) to the server and be done with it?"
Indeed, it's much simpler that way! However, if we don't structure the content
through HTML, search engines won't be able to understand the web page's
structure, we won't be able to make the site responsive, or add beautiful animations
and interactivity through JavaScript. All of these reasons justify the time and effort
spent on CSS.
Throughout this chapter, we'll explore the fundamental components of the CSS
Box Model: padding, borders, margins, block-level and inline boxes. Think of
them as the elementary particles of CSS layouts, as they define each box
individually. In future chapters, we'll see how the HTML structure and the CSS
Box Model combine to create complex layouts.
To Summarize:
Block-level and Inline Elements Back at the beginning of the course, we briefly
touched upon how CSS uses boxes to create the layout of web pages. Every HTML
element that appears on a page is a box. There are two types of boxes: block-level
and inline.
All the HTML elements we've worked with have their own default box type. For
example, <h1> and <p> are block-level elements, while <em> and <strong> are
inline. Block-level elements are used to structure the page, dividing content into
related blocks, while inline elements separate text to give it a specific meaning.
Let's get to know these boxes a bit better by updating the '[Link]' file:
h1, p {
background-color: #f1eeee;
}
em, strong {
background-color: #29fd2f;
}
The background-color property fills only the background of the selected box, giving
us a clear understanding of the page's structure. Our headings and paragraphs
should have a gray background, while <em> and <strong> elements should be
highlighted in green.
This example illustrates a couple of essential things about block-level and inline
boxes:
We can change the box type of HTML elements using the display property. For
example, if we want to make <em> and <strong> elements block-level instead of
inline, we can update our CSS rule in '[Link]':
em, strong {
background-color: #29fd2f;
display: block;
}
Now these elements act like headings and paragraphs, starting on a new line and
filling the entire width of the browser window. This is very useful when, for
example, we want to style <a> elements as buttons or format <img/> elements
(both of which are inline by default).
However, changing <em> and <strong> elements to block-level is not always the
best idea, so let's switch them back to inline boxes by setting the display property
value to 'inline':
em, strong {
background-color: #B2D6FF;
display: inline;
}
Review:
Content, Padding, Margin, and Border The CSS box model is essentially a set of
rules that define the sizes of each element on a web page. Every box, whether
inline or block-level, has four properties:
All this information is necessary for the browser to correctly render the box.
The content property is edited in the HTML document and is the only property
from the list with semantic meaning. All the other properties are purely
presentational and are defined through CSS rules.
Let's start with the inner padding. It is controlled by the padding property in CSS:
h1 {
padding: 50px;
}
This rule adds 50 pixels of padding on all sides of the <h1> heading. Notice how
the background fill increases; this always happens with padding because padding is
inside the border, and everything inside the border has a background.
Sometimes, we may want to edit only one side of an element. For such cases, CSS
has specific properties:
p {
padding-top: 20px;
padding-bottom: 20px;
padding-left: 10px;
padding-right: 10px;
}
You can use any units of measurement for padding; it doesn't have to be pixels. For
instance, em units are useful for proportionally adjusting padding and the base font
size.
Writing out all these properties can be tiresome, so CSS has a shorthand notation
that allows you to set the top, bottom, left, and right padding in a single line of
code. When you provide two values for the padding property, they are interpreted
as the vertical and horizontal padding. You can also think of the first value as the
Y-axis and the second as the X-axis:
p {
padding: 20px 10px; /* Top and bottom padding: 20px, left and right
padding: 10px */
}
If you specify all four values, you can change the padding for each side
individually. The values for padding are interpreted in a clockwise manner, starting
from the top:
p {
padding: 20px 0 20px 10px; /* Top: 20px, Right: 0, Bottom: 20px, Left:
10px */
}
Continuing our journey into the center of the CSS box model, we encounter the
border. The border is a line that separates the padding from the margin.
The border property requires a new syntax that we haven't encountered so far.
First, you specify the border's width, then its style, and finally, its color.
Let's add a border around the <h1> heading by adding the following to 'box-
[Link]':"
h1 {
padding: 50px;
border: 5px solid #333;
}
This rule adds a 5-pixel solid border with the color #333 around the <h1> heading.
Remember:
• The CSS box model includes content, padding, margin, and border.
• You can use shorthand or separate properties for padding.
• The border property has a width, style, and color defined in that order.
h1 {
padding: 50px;
border: 1px solid #0b24fb;
}
This CSS rule instructs the browser to display a thin blue line around our heading
<h1>. Note how the border appears right next to the padding without any space in
between. Even if we resize the browser window to make the heading span two
lines, the border and padding will still remain in their positions. To be honest, such
a border around the entire heading gives off a kind of Windows 95 vibe, so let's
keep it only below the heading. Like with padding, the border property has values
for -top, -bottom, -left, and -right.
border-bottom: 1px solid #0b24fb;
Borders are useful not only as a design element but also as a tool for finding bugs
on our page. For instance, when we are unsure if a box is displaying correctly, we
can add a one-pixel-wide bright red border. This will help us immediately see all
the padding and margins of the box using just one line of CSS. After the bug is
found, this border can be easily removed.
p {
padding: 20px 0 20px 10px;
margin-bottom: 50px;
}
The code above is a clear example of the margin property for individual sides,
which also accepts a shortened format.
The padding and margin properties can serve similar purposes in many situations,
which can sometimes make the choice between them a bit challenging. To make it
easier, we have compiled a list of the most commonly encountered 'pros' regarding
the choice of one or the other:
If none of the above helps you make a choice, don't worry—most of the time,
either option will work. The beauty of CSS is that often the same problem can be
solved in different ways.
One of the main differences between block and inline elements is how they interact
with margins. For example, inline boxes completely ignore the top and bottom
margins of an element. Let's see what happens if we add a large margin to our
<strong> element:
strong {
margin: 50px;
}
The reason for this is that in inline boxes, text is formatted within the block, so the
boxes themselves have very limited influence on the layout of the entire page.
Therefore, if we want to experiment with the vertical space of the page, it's better
to work with block-level elements.
And finally, a piece of advice: before banging your head against the wall trying to
figure out why the top or bottom margins aren't working, check the type of the
displayed element.
To recap:
Another interesting feature of the CSS box model is vertical margin collapse.
When two boxes with vertical margins are adjacent to each other, the margins
collapse. However, it's not the sum of the margins that is displayed, but rather the
largest of the margins.
For example, let's add a top margin of 25 pixels to our <p> element:
p {
padding: 20px 0 20px 10px;
margin-top: 25px;
margin-bottom: 50px;
}
Now each paragraph should have 50 pixels of margin at the bottom and 25 pixels
at the top. So, there should be a total of 75 pixels between our <p> elements, right?
Not exactly; there will be only 50 pixels between them because the smaller top
margin collapses into the larger bottom margin.
This behavior can be very useful when working with different types of elements
and wanting to ensure a consistent spacing between them. However, sometimes
this feature can be quite annoying. How can you prevent it? It's simple; you need to
insert an invisible element between the margins:
<p>Paragraph is a block-level element. <em>But</em> <em> and <strong>
– are not: they are <strong>inline</strong>.</p>
We'll talk more about the <div> element in the next section. For now, what's
important is that only consecutive elements collapse into each other. Adding an
element with a height greater than zero between our paragraphs allows both the top
margin of 25px and the bottom margin of 50px to be displayed.
A third way to avoid margin collapsing is to use only top or bottom margins. For
example, if all your elements have only a bottom margin specified, the chances of
collapsing are eliminated.
Moreover, in the modern web design approach using the 'flexbox' layout mode,
which most contemporary websites are built on, margin collapsing is not a
significant issue anymore.
In summary:
So far, all the HTML elements we've encountered have added additional value to
their contained content. This is the essence of HTML, but sometimes we need
utility boxes just to format a web page in a certain way. For such purposes, we
have the <div> and <span> tags.
The <div> and <span> elements don't affect the semantic structure of the HTML
document. However, they allow us to apply CSS styles to specific parts of a web
page. For example, sometimes we need to add an invisible box to prevent margin
collapsing, or we want to group the first few paragraphs of an article into a
summary with slightly different text formatting. Throughout the rest of this course,
we will constantly use the <div> tag. Right now, let's create a simple button by
adding the following code to the end of the '[Link]' file:
<div>Button</div>
Now, in our browser, a large green button should be displayed across the entire
width of the window.
Of course, these styles also apply to the invisible <div> element that we used to
prevent margin collapsing. The only difference between <div> and <span> is that
the former is used for block-level content, while the latter is used for inline
content.
Usually, the sizes of our HTML elements are determined automatically. The
padding, margins, and borders we worked with earlier wrap around the content
inside the box. If we add some text to our <em> element, everything around it will
stretch to accommodate these changes.
However, sometimes our layout requires specific sizes for individual elements. For
example, we may need a sidebar that is exactly 250 pixels wide. For such purposes,
CSS provides the width and height properties. These properties set the size of the
box containing the content. Let's set the size of our button to 200 pixels by adding
this property to '[Link]':
div {
/* ... */
width: 200px;
}
Now, our button doesn't stretch across the entire page. Also, note that if we make
the button's text longer, the elements will expand vertically to accommodate the
new content. We can prevent such changes using the white-
space and overflow properties.
The width and height properties determine only the size of the content within the
box. Padding and borders are added on top of all other size-related properties.
That's why we get an image that's 244 pixels wide when taking a screenshot of our
button, even though the width property is set to 200 pixels.
Of course, this can be a bit inconvenient when creating page layouts. For example,
we need to fill a container with a width of 600 pixels with three boxes, each 200
pixels wide. But they don't fit because there is a 1-pixel border between them.
Fortunately, in CSS, we can change the width using the box-sizing property. By
default, this property has a value that corresponds to the box with content, leading
to situations like the one described above. Let's see what happens if we
change content-box 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 makes the width of the box equal to 200px, including padding and borders. Of
course, this means that the width property for the content is now determined
automatically. Agree, this is much more intuitive! That's why all modern web
developers use border-box.
Horizontal alignment of boxes is one of the routine tasks for web developers, and
the CSS box model offers several solutions. We have already encountered the text-
align property, which aligns content and inline boxes inside a block-level element.
Aligning block-level boxes is a different story.
Let's add the following rule to our style sheet. Let's be clear that content alignment
will only occur inside block-level boxes, and the blocks themselves will not be
aligned. Our <div> button will be left-aligned, regardless of what happens with the
text alignment in the <body>:
body {
text-align: center;
}
There are three methods for horizontally aligning block elements: auto-margins for
centering, floats for left or right alignment, and flexbox for full control of
alignment. As you can see, block alignment does not intersect with the text-
align property.
Flexbox is a complex topic that we've dedicated a separate chapter to. But we
already have enough knowledge to understand auto-margins right now. When we
set the left and right margin values of a block element to auto, the block is
automatically centered relative to its parent element.
width: 200px;
box-sizing: border-box;
margin: 20px auto;
}
It's important to remember that this approach works only for blocks that already
have the width property set. If you remove it, our button will take up the entire
width of the browser, making center alignment pointless.
Did you notice the white border around the page? These are the default margins
that the browser adds. Different browsers have different default styles, making it
difficult to create style sheets that look the same everywhere.
Usually, these default styles are changed to a universal CSS selector (*). Let's add
this to our '[Link]' file:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
You'll find similar code at the beginning of almost any global style sheet.
However, it is usually much more complex, but even these three rules listed in the
code above already allow us to adjust the CSS box model for our needs without
worrying about different browsers.
To summarize:
We will learn more about the practical application of the CSS box model as we
progress through the course. Right now, we are simply considering it as a new tool
in our CSS arsenal. By digesting the key concepts from this chapter, it will be
easier for us to approach turning a mockup into a real web page:
• Everything is a box.
• Boxes can be inline or block-level.
• Boxes have content, padding, borders, and margins.
• There are specific rules for how these properties interact.
• Understanding the CSS box models allows us to create practically any web
page.
Just like in the previous chapter, CSS properties from this chapter may seem
simple, but once you start looking at the websites you visit through the lens of CSS
boxes, it becomes clear that these properties are used literally everywhere.
In the next chapter, we will dive deeper into the <div> and <span> tags and CSS
selectors.
In one of the previous chapters, we learned how to link HTML documents with
other files in our project. CSS selectors do something similar, but instead of
navigating through files, they allow us to define CSS rules for specific HTML
elements. This enables us to style individual elements separately without affecting
others.
Check out how this feature of CSS selectors is nicely implemented here🔗.
This is quite an important function, especially when we don't want all sections of
our website to have the exact same appearance. Thanks to selectors, we can, for
example, make one paragraph blue and another one yellow; previously, we could
only set one color for all paragraphs at once.
We are already familiar with one of the CSS selectors – the type selector. This
selector selects all matching elements on the page. In this chapter, we will explore
more advanced ways to style the appearance of a page using class selectors,
descendant selectors, pseudo-classes, and ID selectors.
To recap:
• CSS selectors allow us to define CSS rules for specific HTML elements.
• In this chapter, we will need one HTML file and one stylesheet. Let's create
a new folder named "css-selectors" and a new web page named
"[Link]" with the following markup:
• <!DOCTYPE html>
• <html lang="en">
• <head>
• <meta charset="UTF-8"/>
• <title>CSS Selectors</title>
• <link rel="stylesheet" href="[Link]"/>
• </head>
• <body>
• <h1>CSS Selectors</h1>
•
• <p>CSS selectors allow you to <em>target</em> individual HTML
elements within an HTML document. This is <strong>super</strong>
useful.</p>
•
• <p>Classes are also very important because they allow you to
select specific boxes within a web page.</p>
•
• <p>We will also work with links, so here's a link to <a
href='[Link] Coding School</a>.</p>
•
• <div>Button 1</div>
•
• </body>
• </html>
• In the same folder, create a stylesheet named "[Link]." Now, everything
is ready for us to dive into the world of CSS selectors!
Class selectors allow us to apply CSS styles to specific HTML elements. Using
class selectors requires two things:
We can use a class selector to style only the first paragraph of text on our page.
Let's start by adding the class attribute to the desired paragraph:
<p class="synopsis">CSS selectors allow you to <em>target</em> individual
HTML elements within an HTML document. This is <strong>super</strong>
useful.</p>
Now, we can style the <p class="synopsis"> in our CSS with the following code:
.synopsis {
color: #7E8184;
font-style: italic;
}
This rule applies only to elements with the corresponding class attribute. Note the
dot (.) preceding the class name; it distinguishes class selectors from type selectors,
which we encountered earlier.
The value of the class attribute in HTML can be anything, as long as it matches
the selector in our CSS. The standard naming convention for classes uses
lowercase letters and hyphens instead of spaces, similar to naming files and
folders. When choosing class names, it's best to consider their semantic meaning
rather than their visual appearance.
The class attribute works not only with <p> elements but also with any HTML
element. Thanks to class selectors, our utility boxes <div> and <span> from the
previous chapter become much more useful. We can use them to style individual
elements or arbitrary sections of web pages.
Let's start by styling individual elements, recreating the button from the previous
chapter using a class selector instead of a <div>. Add the following to [Link]:
.button {
color: #FFF;
background-color: #29fd2f;
font-weight: bold;
padding: 20px;
text-align: center;
border: 2px solid #0b24fb;
border-radius: 5px;
width: 200px;
margin: 20px auto;
}
Of course, we'll need the corresponding class attribute for this to work. Change
the <div> in [Link] to the following:
<div class="button">Button 1</div>
Unlike the previous chapter, where we edited all <p> elements at once, we can now
use this CSS rule not only for buttons but also for other elements.
Recap:
• To use a class selector, you need the class attribute and a corresponding
class selector in the stylesheet.
• It's best to choose class names based on their semantic meaning.
As we already know, <div> elements do not affect the semantic structure of the
website, making them a great tool for modifying the appearance of our site. By
enclosing HTML elements in <div> tags, we can divide our site into sections,
orienting it to the layout, without affecting how search engines see our content.
Now, regardless of how we resize our browser window, the page will maintain a
width of 600 pixels and stay centered. We've done the same thing we did with
centering the button but now encompassed several elements at once by placing
them in a <div> container.
This is how layouts work on complex web pages. For example, if we had a sidebar
on our page, we would place all our sidebar elements inside a separate <div> with
the class .sidebar. Without class selectors that differentiate our <div> elements,
this would be impossible.
The same class attribute can be applied to multiple elements within the same
HTML document. This means that we can reuse CSS rules wherever we want.
Let's create another button and add a new HTML element with the same class
attribute:
<div class="button">Button 2</div>
Now, we have a second button that looks the same as the first one, and we didn't
write a single line of CSS! This organization of similar graphical elements
significantly simplifies the life of a web developer. If, for example, we need to
change the color of the buttons, we can do it in one place for all elements at once.
But what if we want to change only the second button? Fortunately, we can apply
multiple classes to the same HTML element. Multiple classes can be included in
the same class attribute, separated by spaces:
<div class="button call-to-action">Button 2</div>
Now, this element has two separate classes, and we can use both. This gives us
new possibilities: styles applied to both buttons can be located in the .button class,
and styles applied only to the second button can be placed in the .call-to-
action class.
.call-to-action {
font-style: italic;
background-color: #0b24fb;
}
Nested Selectors
There are a couple of extremely important things related to our second button:
This means that the order of attributes in the classes of our HTML elements does
not affect the overriding of styles by each other. Multiple classes are applied to the
same element equally, and their priority is determined by the sequence of rules in
[Link]. In other words, the following elements are equivalent to each other:
<div class="button call-to-action">Button 2</div>
<div class="call-to-action button">Button 2</div>
However, this can get a bit more complicated with the introduction of CSS
specificity, which we will discuss at the end of this chapter.
The <em> tag in our first paragraph is no longer highlighted among the surrounding
text because the .synopsis rule makes the entire paragraph italic. To change
this <em> element, we can add another class, but that would make the code overly
complex. Therefore, we should consider .synopsis as an independent component,
the appearance of which we can edit directly from CSS.
This is where descendant selectors come in handy. They allow us to work only
with elements that are inside other elements. For example, we can edit
the <em> within the .synopsis paragraph using the following code in [Link]:
.synopsis em {
font-style: normal;
}
Now, the text inside the <em> tag is displayed in regular uppercase letters and
stands out among the italics. However, these changes will not affect
other <em> elements on the page.
Descendant selectors can be used not only with class selectors but can be combined
with any other group of selectors in the same way. For example, if we suddenly
wanted to change the <em> elements inside headings, we would do something like
this:
h1 em {
font-style: normal;
}
Our main goal in this chapter is to learn how to apply styles specifically to the
elements we need. Descendant selectors are perfect for this purpose.
It's very important not to overuse descendant selectors. Rules like the following
can complicate our lives significantly:
/* Try to avoid this */
.article h2 .subheading em {
/* Special styles */
}
These rules cannot be applied multiple times because they only match the
following HTML structure:
<div class="article">
<h2>
<span class="subheading">This <em>is</em> very special text</span>
</h2>
</div>
To summarize:
• You can encompass multiple elements by placing them in a
single <div> container.
• You can apply multiple classes to the same HTML element.
• Be cautious about using descendant selectors; avoid making your selectors
overly specific.
So far, all the CSS selectors we've encountered have targeted specific parts of the
HTML code directly. However, a web page contains more information than just
the HTML content we create; it also includes information about what the user is
doing.
The most obvious example is links. As developers, we create <a href> elements,
the browser renders them, and users can interact with them by hovering, clicking,
and navigating through the links.
Pseudo-classes start with a colon followed by the name of the desired class. The
most common pseudo-classes are:
:link – a link the user hasn't visited yet; :visited – a link visited by the user; :hover
– a link the user hovers over; :active – a link the user clicks on.
Let's take a closer look at these classes by adding the following code to our
stylesheet:
a:link {
color: blue;
text-decoration: none;
}
a:visited {
color: purple;
}
a:hover {
color: aqua;
text-decoration: underline;
}
a:active {
color: red;
}
If we haven't visited the homepage yet, the link will be blue. Otherwise, it will be
purple. When you hover over the link, it turns aqua, and when you click on it, it
becomes red.
The code above will work for most websites, but let's see how a:visited behaves
when we change the attribute value to a URL we've already visited. Our a:hover
style will apply to both visited and unvisited links. We can further refine our links
by combining pseudo-classes. Let's update the previous code snippet:
a:visited:hover {
color: orange;
}
Now, we have a separate style for visited links. Try hovering over the link:
unvisited links turn blue, and visited links turn orange. Cool, right? The only thing
is, our link no longer turns red when clicked.
We can fix this by using a:visited:active. Add the following code to the end of our
stylesheet:
a:visited:active {
color: red;
}
The last two sections allow us to style visited links separately from unvisited ones.
Let's start by turning our buttons from <div> elements into <a href> elements:
<a class="button" href="[Link]">Button One</a>
<a class="button call-to-action" href="[Link]">Button Two</a>
If we refresh the page in the browser, we'll notice that some styles no longer work,
even though we're using the same classes. This happens because <a> is, by default,
an inline element with its default color. So, we need to turn <a> into a block
element and change its default styling.
Let's begin with the :link and :visited pseudo-classes. We'll follow the same
principles as in the previous section, but since we're dealing with buttons now, the
colors for visited and unvisited should match. Modify the .button rule as follows:
.button:link,
.button:visited {
display: block;
text-decoration: none;
color: #FFF;
background-color: #5995DA;
font-weight: bold;
padding: 20px;
text-align: center;
border: 2px solid #5D6063;
border-radius: 5px;
width: 200px;
margin: 20px auto;
}
We added the new :link and :visited pseudo-classes to the selector. Without them,
our color won't override the default a:link style. Next, let's work on the hover state:
.button:hover,
.button:visited:hover {
color: #FFF;
background-color: #76AEED;
}
Both buttons now turn blue when we hover over them. Finally, let's make the
buttons slightly darker when they are clicked:
.button:active,
.button:visited:active {
color: #FFF;
background-color: #5995DA;
}
By the way, all these classes can be used for other elements as well. For example,
if we add the class .button to any HTML element, it will automatically turn into an
interactive button.
Now, what about the second button? It should be yellow, but we've overridden that
rule with the code from the previous section. Our .button:link selector was more
specific than our current .call-to-action rule, which is why this happened. Let's fix
this by adding pseudo-classes to our .call-to-action rule. Modify our current rule to
the following:
.call-to-action:link,
.call-to-action:visited {
font-style: italic;
background-color: #EEB75A;
}
.call-to-action:hover,
.call-to-action:visited:hover {
background-color: #F5CF8E;
}
.call-to-action:active,
.call-to-action:visited:active {
background-color: #EEB75A;
}
Since we added the .call-to-action class only to our second button, it will be the
only one that turns yellow. Of course, we still need the .button class for both <a>
elements, as it defines common styles like padding, border radius, and font weight.
There are plenty of pseudo-classes that allow you to add additional information to
an element. For example, the :last-of-type pseudo-class lets you select the final
element of a specific type within a parent element. It serves as an alternative to
class selectors and allows you to target specific elements. Let's add some space
after the last paragraph using :last-of-type:
p:last-of-type {
margin-bottom: 50px;
}
We can also use the :first-of-type pseudo-class instead of the .synopsis class. After
replacing the .synopsis rule with the following code, we should achieve the same
result:
p:first-of-type {
color: #7E8184;
font-style: italic;
}
However, this method has its limitations. For instance, it only works if the
paragraph contains a <p> element. If we ever need to create a paragraph with
multiple elements enclosed within <div class="synopsis">, we would need to
rewrite the CSS. On the other hand, using pseudo-classes allows us to style
individual elements without changing the HTML structure. This provides a clear
separation between content and presentation.
Although the pseudo-class method may be slightly more complex, it can still be
effectively used if you understand its nuances. For example, the :first-of-type and
:last-of-type selectors operate only within their parent elements. In our case, we
have one utility <div> containing the content (.page), so this isn't a problem. Let's
see what happens if we add this to the end of our .page element:
<div class="sidebar">
<p>If this page had a sidebar...</p>
<p>We would have issues with pseudo-classes.</p>
</div>
We won't be able to create a real sidebar until the next chapter, but even now, you
can see how pseudo-classes can complicate the structure. The first <p> element
here will match p:first-of-type because the scope of the pseudo-classes is limited to
the parent element.
If we want to select only the first <p> element within our <div class="page">, we'll
need to limit the scope using child element selectors like this:
.page > p:first-of-type {
color: #7E8184;
font-style: italic;
}
This is another example of how in the world of HTML and CSS, you can achieve
the same result through different methods. Some developers prefer different
approaches: some like the semantic structure of pseudo-classes, while others prefer
using detailed attributes for each HTML element.
To Recap:
ID selectors are a stricter alternative to class selectors. They work similarly, but
each element on a page cannot have the same ID as another element, so you can't
reuse styles across multiple elements. To target an element using an ID attribute,
you need to specify any HTML element that you want to select. Let's add such a
selector to our second button:
<a id="button-2" class="button" href="[Link]">Button 2</a>
The corresponding CSS selector should start with a hash (#) symbol, not a dot.
Add the following code to [Link] to change the text color of our yellow button:
#button-2 {
color: #5D6063;
}
Here's the problem: if we want to use this style for another button, we would need
to assign another unique ID attribute. If we follow this path, our CSS will start to
look quite cumbersome, to say the least:
/* (Very hard to work with) */
#button-2,
#button-3,
#checkout-button,
#menu-bar-call-to-action {
color: #5D6063;
}
ID attributes must be unique because they serve as targets for URL fragments,
which are used to point users to specific parts of a web page. They are like an ID
selector appended to the end of a URL.
For example, if we want to direct a user to the second button, we would use the
following code:
<!-- From the same page -->
<a href="#button-2">Go to Button 2</a>
If we add the first version to our [Link] page and click on it, we'll see how
the URL in the browser changes.
Earlier in this chapter, we discussed the significant importance of the order of CSS
rules in external style sheets. All else being equal, rules are applied from top to
bottom. This law allows us to predictably override rules.
However, not all CSS selectors are equal. CSS specificity refers to the weight
assigned to different categories of selectors. This means that certain selectors will
always override others, regardless of their position in the style sheet. If we add the
code below after our .call-to-action rules, it will override the previous background-
color. And if we add it to the beginning of this file, our button will no longer turn
red. Both outcomes are entirely expected.
.call-to-action:link,
.call-to-action:visited {
background-color: #D55C5F;
}
Now let's see what happens when we try to do the same with an ID selector. Before
that, remove the previous piece of code and add the following before the existing
.call-to-action rule:
#button-2 {
background-color: #D55C5F;
}
ID selectors have higher specificity than class selectors, so our button will turn red,
even though we set a different background-color using .call-to-action:link later in
our style sheet. Therefore, the concept of "last rule wins" only applies when all
rules have the same specificity.
This can be very confusing and is such a big problem that it has led to the
development of the entire "BEM" methodology. This methodology attempts to
make CSS rules more reusable by converting everything into class selectors, thus
eliminating specificity issues.
To Recap:
• IDs within the same page must be unique and should not be repeated.
• ID attributes must be unique because they serve as targets for URL
fragments.
• In this chapter, we practically explored class selectors, descendant selectors,
pseudo-classes, as well as styling links and ID selectors. The ultimate goal
of all this was the ability to work with a specific HTML element from our
CSS. Class selectors are the most convenient tool among those mentioned.
• Now, we can configure the interaction between CSS and HTML documents
in dozens of different ways. Furthermore, over the next few chapters, we
will have a clearer view of the relationship between HTML structure and
web page layout. With all this new knowledge, the question arises: 'Where
to start?'
• First and foremost, don't forget our beloved separation of content and
presentation. Start with the former, so begin with structuring content using
HTML tags. After that, you can add class attributes to your elements and
style them one by one. And when you need additional structuring (for
example, multiple elements in one sidebar), you can start wrapping content
in <div> containers.
• By the end of this chapter, we have covered almost all CSS selectors that
real websites use.
In addition to placing a single background image on our page, we can also place
multiple backgrounds, adjust their size, and set their initial position based on the
CSS box model.
CSS allows us to add multiple background images to a single box using a list of
image locations.
background-image: url([Link]), url([Link]), url([Link]);
The background property can also be used to adjust other aspects related to the
background. For example, here's a button with a separate background, bullet, and
arrow:
background: url([Link]), url([Link]) 0 50% no-repeat, url([Link])
right no-repeat;
/* Multiple images */
background-position: 0 0, center;
background-size - allows you to set the size of the background image. The image
can be left at its original size, stretched, or fitted to the available space. For
example:
background-size: cover;
background-size: contain;
background - sets all background style properties, such as color, image source,
origin, size, and repeat method in one declaration. For example:
/* Single image, centered and scaled */
background: no-repeat center cover url("../img/[Link]");
Background Size
• auto: maintains the original size and aspect ratio of the image.
• Width and height in pixels, for example, 100px 50px (100 pixels in width,
50 pixels in height). Specifying a single length value, for example, 100px, is
equivalent to 100px auto.
• Width and height as percentages of the background, for example, 50% 25%
(50% of the width of the background area, 25% of the height of the
background area). Specifying a single percentage value, for example, 50%,
is equivalent to 50% auto.
• A combination of lengths, percentages, and auto, for example, 80px auto (80
pixels in width, automatic height to maintain the original aspect ratio).
• The contain keyword, which maintains the image's original aspect ratio and
makes it as large as possible while fitting entirely within the background
area.
• The cover keyword, which maintains the image's original aspect ratio and
scales it to cover the entire background area, sometimes resulting in
cropping of the height or width.
Background-origin Property
The background-origin property determines where inside the box the background
positioning area starts. In the CSS box model, the background by default starts at
the top left corner. Suppose we have the following rule:
#burrito {
width: 400px;
height: 200px;
border: 10px solid rgba(0,255,0,.5);
padding: 20px;
background: url([Link]) 0 0 no-repeat;
}
This means that the background image should appear in the top left corner, just
after the inner edges of the green border. However, this can be changed
using background-origin. The possible values for this property are padding-box,
border-box, and content-box.
To Recap:
• You can add multiple backgrounds by listing image files using commas in
background-image.
• You can stretch or shrink background images with the background-size
property.
• And with background-origin, you can determine where inside the box the
background positioning area starts.
Linear Gradients
In the last example, we don't use the to keyword because we specify the exact
angle.
To ensure proper display of gradients in Safari and older versions of Chrome, it's a
good practice to use -webkit-linear-gradient, and you should omit the to keyword
when using this property. As a result, your gradients might look something like
this:
body {
background: #666 url([Link]) 0 0 repeat-y;
background: -webkit-linear-gradient(right, #000, #666);
background: linear-gradient(to right, #000, #666);
}
To recap:
• the linear-gradient property of the background or background-image property
allows you to add gradients to your webpage without using image files.
Radial Gradients In the case of radial gradients, the color transition occurs in
circles around a specific point rather than along a straight line. The syntax for
radial gradients is similar to linear gradients:
background: radial-gradient(yellow, green);
You can also specify the shape of the gradient fade. By default, it's an ellipse that
fills the background box, but you can make it circular regardless of the box's shape:
background: radial-gradient(circle, yellow, green);
If you want to place the starting point of the gradient at a specific location, you can
use the at keyword:
background: radial-gradient(at top left, yellow, green);
Color Stops
If you don't want a uniform color transition throughout the gradient, you can
specify where each color begins in the gradient, starting from 0% and ending at
100%:
When color stops are specified, they evenly distribute the colors. The same
happens by default when these stops are not defined:
background: linear-gradient(135deg, hsl(36,100%,50%) 10%, hsl(72,100%,50%)
60%, white 90%);
Repeating Gradients
To create repeating gradients and define color stops, you can use repeating-
linear-gradient. For example, here's how you can create alternating black and
white stripes:
background: repeating-linear-gradient(white, black 15px, white 30px);
In this chapter, we will step through all aspects of the flexbox model. Afterward,
we will have a superpower: the ability to create virtually any layout that designers
can dream up!
To recap:
•
Flexbox uses two types of boxes that we haven't encountered yet: flex containers
and flex items. A flex container is responsible for grouping multiple flex items and
defining their layout.
Every HTML element that is a direct child of a flex container is also a flex item.
While you can work with flex items individually, their layout is typically
determined by the properties of the container. The main task of flex items is to
inform the container about how many items it needs to arrange. Here's
an example🔗 of how flexbox can be implemented.
The first step in using flexbox is to turn HTML elements into flex containers. As
an example, we'll use the display property, which we're already familiar with. By
setting it to flex, we tell the browser to render everything inside the box as a flex
item.
Add this line to our .menu-container rule to turn it into a flex container:
.menu-container {
/* ... */
display: flex;
}
This enables the flexbox mode. If you don't do this, the browser will simply ignore
all flexbox properties. The need to explicitly set display: flex; each time allows
you to mix flexbox with other layout models, such as floats or positioning.
Now we have a flex container with one flex item inside. However, our page
remains unchanged because we haven't told the container how to display this item
yet.
Once we have a flex container, our next task is to determine the horizontal
alignment of items inside it. For this, we use the justify-content property. We can
use it to center our .menu:
.menu-container {
/* ... */
display: flex;
justify-content: center;
}
This achieves the same effect as using margin: 0 auto; on the .menu element. Note
how we added the justify-content property to the parent element rather than
directly to the element we want to center. In flexbox, you work with elements
through their containers.
• center
• flex-start
• flex-end
• space-around
• space-between
• space-evenly
Let's try changing the justify-content values to flex-start and flex-end. This
should align the menu to the left and right edges of the browser window,
respectively. The last three values in the list are applied only when there are
multiple flex items in the container and free space that can be distributed among
them. Before moving on to the next section, don't forget to change the justify-
content value back to center.
Recap
This code turns .menu into a nested flex container, and the space-around value
distributes the elements across its width. As a result, our page should look like this:
The flex container automatically distributes all horizontal space around each
element. The space-between value works similarly but adds space between the
elements. This is what we need, so let's change justify-content to space-between:
justify-content: space-between;
You can use values like center, flex-start, and flex-end as well, but let's stick
with space-between as it suits our needs best.
Flex containers can only position elements that are their descendants. They can't
affect the layout of elements inside these flex elements. This means that grouping
flex elements is another tool in our toolkit for creating web page layouts. Wrapping
a group of elements in a <div> can completely change the page's appearance.
Suppose we want "Register" and "Login" to appear in the top-right corner of the
page. For that, we need to wrap them in another <div>:
<div class="menu">
<div class="date">14.08.2023</div>
<div class="links">
<div class="signup">Register</div>
<div class="login">Login</div>
</div>
</div>
But now we need to deal with the .links element because it currently uses the
default layout. The solution? Nested flex containers! Add a new rule to
your [Link] file to turn the .links element into a flex container:
.links {
display: flex;
justify-content: flex-end;
/* Remove border */
}
.login {
margin-left: 20px;
}
Now, our links are where they should be! By the way, margins work here just like
in the CSS box model. You can remove the white borders as they are no longer
needed.
Recap
• A flex container automatically distributes horizontal space around each
element.
• Flex containers can only position elements that are their descendants.
Vertical Alignment and Element Wrapping So far, we have only worked with
horizontal alignment, but flex containers allow us to customize vertical alignment
as well. This is something that cannot be easily achieved with floats!
Let's add the following code to [Link] after the .menu-container element to
see how vertical alignment works:
<div class="header-container">
<div class="header">
<div class="subscribe">Subscribe▾</div>
<div class="logo"><img src="images/[Link]"/></div>
<div class="social"><img src="images/[Link]"/></div>
<div class="social"><img src="images/[Link]"/></div>
<div class="social"><img src="images/[Link]"/></div>
</div>
</div>
All of this should be familiar by now, but there's one detail: since we've specified a
height for .header, we can vertically align the elements right there. In official
specifications, this type of alignment is referred to as aligning along the
perpendicular axis to the main axis, but we can simply call it vertical alignment.
• center
• flex-start (top)
• flex-end (bottom)
• stretch
• baseline
Most of these values are intuitive. Stretch, however, deserves a closer look – it
allows each element to expand to the full length of the flex container regardless of
the content inside. Let's add the following to [Link]:
.header {
/* ... */
align-items: stretch;
}
.social,
.logo,
.subscribe {
border: 1px solid #f1eeee;
}
The box of each element stretches to the full length of the flex container, regardless
of the content inside. Usually, in such cases, columns of equal height are created
with different amounts of content inside – this is extremely difficult to achieve
with floats! Before moving on to the next section, let's remember to return our
content in .header to its previous appearance.
.photo-grid {
width: 900px;
display: flex;
justify-content: flex-start;
}
.photo-grid-item {
width: 300px;
height: 300px;
border: 1px solid #fff;
}
So far, it's pretty predictable. But let's see what happens when we add additional
elements that won't fit into our flex container anymore. Let's add two more photos
to .photo-grid:
<div class="photo-grid-item">
<img src="images/[Link]"/>
</div>
<div class="photo-grid-item last-item">
<img src="images/[Link]"/>
</div>
If we want to create a hero banner that allows the user to horizontally scroll
through a bunch of photos, this layout might work. But right now, we need
something different, so let's add the flex-wrap property, which will send the
elements that don't fit onto the next line:
.photo-grid {
width: 900px;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
To recap:
The flex-direction property determines how flex items will be arranged within a
flex container: horizontally or vertically. Up to this point, all containers we've
worked with have used horizontal alignment by default. This means that elements
are laid out in a row, one after another, and then wrap to the next row when there's
no more space.
One of the coolest features of flexbox is the ability to transform rows into columns
with just one CSS line. Let's add the following to our .photo-grid rule:
.photo-grid {
/* ... */
flex-direction: column;
}
We've just changed our page so that it now has a single vertical column.
One of the main challenges of responsive design is presenting the same HTML to
both mobile and desktop users simultaneously. This isn't the easiest task, as most
mobile layouts have a single column, while desktop layouts often arrange elements
horizontally. That's where flex-direction becomes an indispensable tool for
creating responsive designs.
Notice that the column is positioned on the left side of the flex container, despite
the justify-content: center; rule. The reason is that when we change the container's
direction, justify-content and align-items switch roles.
So, to align our column, we'll need to add the align-items property to .photo-grid:
photo-grid {
/* ... */
flex-direction: column;
align-items: center;
}
To recap:
Now both rows are displayed from right to left instead of left to right. Notice that
the order of elements only changes within each row, so the first row starts with 3
instead of 5. This is a very useful feature often used in mobile layout development.
In the next section, we'll learn how to do even more subtle things.
Throughout this chapter, we've learned how to change the position of flex items
through their parent containers. However, you can also change the position of
individual elements.
Adding the order property to a flex item determines its placement order within the
container without affecting neighboring elements. By default, this property is set to
0. By increasing or decreasing it, we can move the element left or right,
respectively. We can use the order property to, for example, swap the positions of
.first-item or .last-item in our grid. In this case, it's also a good idea to change row-
reverse to row for better clarity:
.photo-grid {
/* ... */
flex-direction: row;
align-items: center;
}
.first-item {
order: 1;
}
.last-item {
order: -1;
}
Now the first and last items have swapped positions, even though they are on
different rows.
Similar operations can be done with vertical alignment. What if we need to move
the "Subscribe" button and social media icons to the bottom of the header? It's
straightforward - we can align each element individually using the align-self
property. If we add it to our flex item, it will override the align-items rule from the
container:
.social,
.subscribe {
align-self: flex-end;
margin-bottom: 20px;
}
This should move both elements to the bottom of the .header, leaving the spacing
and margins unchanged.
• center;
• flex-start (top);
• flex-end (bottom);
• stretch;
• baseline.
All our examples until now have revolved around elements with fixed or content-
determined widths. This allowed us to focus on the positioning aspects of flexbox.
However, flex items have a primary characteristic - adaptability: flex items can
shrink and expand, adjusting to the width of their containers. The flex property
determines the width of each individual item in a flex container. More precisely, it
gives elements adaptive width and specifies how to distribute free space between
them. For example, an element with a flex value of 2 will grow twice as fast as an
element with a value of 1.
Let's return our header to its previous state and create a footer by adding the
following code 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>
.footer-item {
height: 200px;
flex: 1;
background-color: #0b24fb;
border: 1px solid #fff;
}
The last line flex: 1; tells the elements to expand in width within the .footer. Since
they all have the same value for the flex property, they will all expand equally.
Increasing the flex value of one of the elements will make that element expand
faster than the others. For example, let's make the third element expand twice as
fast as the other two by adding the following rule:
cssCopy code
.footer-three {
flex: 2;
}
You can compare this to the justify-content property, which also distributes extra
space between elements. However, flex gives you more control over how elements
are positioned within the container.
You can also mix fixed and adaptive width elements using flex: initial. Let's try to
create a footer similar to the one in the diagram with a flex item in the center and
fixed-width items on the sides. Update our stylesheet:
.footer-one,
.footer-three {
flex: initial;
width: 300px;
background-color: #f1eeee;
}
If you remove the line flex: initial;, the flex: 1; from the .footer-item rule will
override it, and the width properties will be ignored. The initial value prevents this,
resulting in a flex layout where some elements have fixed widths. If you resize the
browser, you'll see that only the middle box changes its size.
This is a common layout used not only for footers. For example, many websites
have a fixed sidebar and a changing content block, which contains the main text of
the page.
Auto margins in flexbox are a special case. They can be used as an alternative
to <div> when aligning a group of elements to the left or right of the container.
Auto margins can be seen as separators between flex items inside the container.
After reloading the page, you'll see that the elements are evenly distributed across
the menu, just like at the beginning of the chapter. To recreate the desired layout,
add auto margins between the elements you want to separate:
.signup {
margin-left: auto;
}
Auto margins consume all available space inside the flex container, so
the .signup and all subsequent elements are pushed to the right side of the
container. This gives us the same layout as before but without the need for
extra <div> elements in the code, making our HTML structure simpler.
To recap:
Flexbox has given us a bunch of cool tools for creating web pages. Let's quickly
recap the tools we've learned in this chapter:
When applying these properties, it's important to remember that they simply
instruct the browser on how to display HTML elements. So, the key here is not just
writing HTML and CSS code, but creating a concept of how the boxes should
behave and ultimately come together to form the desired design.
The first thing to do after a designer provides us with a mockup is to draw a bunch
of boxes right on it and determine how they should interact to achieve the desired
design. After that, coding everything with Flexbox isn't so difficult.
Before we delve into advanced positioning, let's define the concept of the 'flow.'
Flow refers to the order in which objects are rendered on a page. Static positioning
is related to the regular flow of a page - it's what🔗 we've been working with so
far. The CSS box model and flexbox operate within the context of the static flow,
but it's not the only way to position elements in CSS.
Other types of positioning include relative, absolute, and fixed positioning. Each of
these positioning types allows you to manually position elements using specific
coordinates. While with flexbox, we say, 'Place this box in the center of the
container,' advanced positioning allows us to say, 'Place this box 20px above and
50px to the right.'
Most elements on a page should be positioned within the static flow of the page.
Other positioning methods come into play when we need to do something more
complex. For example, changing the position of a specific element or creating
animations for a UI component without affecting neighboring elements.
To recap:
•
• Each page contains images that will help make everything more visual. Save
the images from '[Link]' in the 'images' folder and create a '[Link]'
file with the following markup:
• * {
• margin: 0;
• padding: 0;
• box-sizing: border-box;
• }
•
• body {
• height: 1200px;
• }
•
• .container {
• display: flex;
• justify-content: center;
• }
•
• .example {
• display: flex;
• justify-content: space-around;
•
• width: 800px;
• margin: 50px 0;
• background-color: #D6E9FE;
• }
•
• .item img {
• display: block;
• }
•
• There's nothing new here - just the familiar flexbox. The only unusual thing
is the height property of the <body> element, which allows us to scroll the
page up and down.
The CSS property position allows you to change the layout scheme of a specific
element. The default value of this property is static. If the value of this property is
anything other than static, then it's a positioned element.
Furthermore, all positioning schemes can be combined with each other. Despite
most of a web page needing to be statically positioned, you can often find both
relatively and statically positioned elements on the same page.
Relative positioning places elements on the page relative to their original position.
This is very useful when we need to move boxes around.
The position: relative; line transforms the element into a positioned element, and
the top and left properties allow you to specify how far the element is placed from
its initial position. It's like setting coordinates (x, y) for the element.
The top and left values are measured from the top and left edges of the box. You
can move descendants to other edges using bottom and right values.
For example, this code will move our box in the opposite direction:
.item-relative {
position: relative;
bottom: 30px;
right: 30px;
}
Note that these properties also work with negative values. For instance, top: -
30px; has the same effect as bottom: 30px;.
To recap:
Absolute positioning is almost the same as relative positioning, with the key
difference being that the offset is based on the entire browser window rather than
the element's initial position. Since there's no longer any interaction with the static
page flow, it's considered the most manual way to position an element. Here's
an example page🔗 where absolute positioning is used.
Our HTML structure is identical to the previous example, but now the black image
will be positioned in the top-left corner of the window. You can also assign values
to bottom or right to better understand what's happening.
Another interesting effect of the absolute value is that it removes the element from
the normal page flow. This is very noticeable when the elements are aligned to the
left, so let's temporarily change the justify-content property in the .example rule:
.example {
display: flex;
justify-content: flex-start; /* Update this */
/* ... */
}
In the example with relative positioning (i.e., in the first row), there are empty
spaces where the positioned elements used to be. With absolute positioning, these
spaces disappear, as if .item-absolute didn't exist for its parent and surrounding
elements. Before moving on, let's change justify-content back to space-around.
This behavior isn't always desirable because it means that everything on the page
must have absolute positioning, or else unpredictable overlaps between static and
absolute elements can occur. So, why use absolute at all?
Absolute positioning becomes useful when it's relative to some other element in
the static page flow. Fortunately, there's a way to change the coordinate system of
an absolutely positioned element.
Coordinates for absolute elements are always relative to the nearest positioned
ancestor element. An element only becomes relative to the browser when none of
its descendants are positioned. So, if we change the parent element of .item-
absolute to be relatively positioned, it should appear in the top-left corner of that
element.
.absolute {
position: relative;
}
The .absolute div element is in the normal page flow, and we can move .item-
absolute however we want. This is useful when you need to alter the normal flow
of the container, for example, for a mobile layout, any absolutely positioned
element will automatically adjust its position along with the flow.
Note how we didn't specify coordinates for the offsets of .absolute. We only used
relative positioning to make our absolutely positioned element fit within the
normal page flow. That's how we can safely combine absolute positioning with
static.
Fixed Positioning
Fixed positioning shares many similarities with absolute positioning: the element is
taken out of the normal page flow, and its 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, as seen here🔗.
This will place the blue image in the bottom-right corner of the screen. Try
scrolling the page, and you'll notice that the blue image doesn't move along with
the other elements on the page, unlike the black image with absolute positioning.
This allows for the creation of navigation menus that always stay on the screen.
Oh, and those pop-ups from the early 2000s that you couldn't seem to close!
To Recap:
• In the case of absolute positioning, the element's offset depends on the entire
browser window, not its initial position.
• Fixed positioning is identical to absolute positioning, except fixed elements
on the page do not scroll.
Now we will deviate a bit from the two main themes of the course – HTML and
CSS, and play around with JavaScript a bit. Don't be afraid, it's not very
complicated! Moreover, animation is one of the main areas of using relative and
absolute positioning, so let's move forward a bit by creating an animation for one
of our elements.
function frame() {
var element = [Link]('.item-relative');
left += 2;
[Link] = left + 'px';
This JavaScript code creates a simple animation that gradually updates the left
property of .item-relative. When we reload the page, the green image should move
to the right side of the container.
This is a fairly simple example, but it should make it somewhat clear how complex
UI animations work. If we were trying to achieve a similar effect by changing
margins or borders, we would still end up changing the layout of the boxes and the
.example element.
To recap:
Fixed positioning will allow us to place the menu at the top of the page, and
relative positioning will give us an anchor for the absolutely positioned dropdown
menu. We will also talk about how to apply pseudo-classes in this context.
To start, we need a new web page called [Link], which will have a header and
a menu:
<!DOCTYPE html>
<html lang="En">
<head>
<meta charset="UTF-8"/>
<title>Positioning</title>
<link href="[Link]" rel="stylesheet"/>
</head>
<body>
<div class="header">
<div class="logo"><img src="images/[Link]"/></div>
<ul class="menu">
<li class="dropdown"><span>Articles▾</span></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Newsletter</a></li>
<li><a href="#">About us</a></li>
</ul>
</div>
</body>
</html>
Navigation menus are almost always best created using an unordered list (<ul>)
instead of a bunch of <div> elements; this makes the menu more accessible for
search engines. Also, note how we are preparing our dropdown menu by adding a
class attribute to the first <li> in the list. This <span> will allow us to separate the
title from the submenu that will be hidden behind it.
body {
height: 1200px;
font-size: 18px;
font-family: sans-serif;
color: #000000;
}
a:link,
a:visited {
color: #000000;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.header {
width: 100%;
padding: 50px;
display: flex;
justify-content: space-between;
position: fixed;
background: #f1eeee;
}
Note the fixed positioning of .header, which allows us to keep our navigation menu
above any content on the page.
Despite navigation menus being marked as unordered lists, most websites don't
look like a list. Let's do something about the appearance of our page! We can
improve it by turning list items into inline boxes using the display property. Add
the following to [Link]:
.menu {
margin-top: 15px;
}
.menu > li {
margin-right: 50px;
display: inline;
}
The ">" symbol indicates that we are selecting child li elements of the menu
element.
Using descendant selectors is better here because we want to select only those <li>
elements that are inside .menu. This will become important once we add
submenus, which will have their own <li> elements that should not be affected by
this rule. This code also adds margins to all list items except the last <li> using the
:last-of-type pseudo-class. This is a common practice for creating spacing between
items.
Submenu
Our submenu will look the same as the top menu, but it will be nested within a list.
Let's modify the .menu element, making sure that the entire .features-menu list is
enclosed in the first <li> element of .menu.
<ul class="menu">
<li class="dropdown"><span>Articles ▾</span>
<ul class="features-menu"> <!-- Submenu begins here -->
<li><a href="#">Article 1</a></li>
<li><a href="#">Article 2</a></li>
<li><a href="#">Article 3</a></li>
<li><a href="#">Article 4</a></li>
</ul> <!-- Submenu ends here -->
</li>
<li><a href="#">Blog</a></li> <!-- This part remains unchanged -->
<li><a href="#">Newsletter</a></li>
<li><a href="#">About us</a></li>
</ul>
This code provides search engines with important information. Now Google sees
that all these new elements are related to the Miscellaneous section and form a
separate section of the website. We recommend using this structure for creating
complex navigation menus.
As for CSS, we will deal with the interactive dropdown menu a bit later. For now,
let's focus on refining the appearance of the submenu:
.features-menu {
display: flex;
flex-direction: column;
background: #29fd2f;
border-radius: 5px;
padding-top: 60px;
}
.features-menu li {
list-style: none;
border-bottom: 1px solid #FFF;
padding: 0 40px 10px 20px;
margin: 10px;
}
.features-menu li:last-of-type {
border-bottom: none;
}
Now the submenu looks as it should, but it appears in the wrong place. This was
expected because it is still statically positioned, meaning it still interacts with its
parent and the surrounding elements.
To create the desired layout, we need to apply some new CSS positioning skills.
We want the elements of our top menu to be displayed as they were before we
added the submenu. Absolute positioning will help us achieve this. Let's add a
couple of lines to the .features-menu rule:
.features-menu {
padding-top: 60px;
display: flex;
flex-direction: column;
background: #29fd2f;
border-radius: 5px;
position: absolute; /* Add this */
top: -25px; /* Add this */
left: -30px; /* Add this */
}
Great! Now the submenu is not part of the static flow of the page, and our top
menu has returned to its normal state. But the submenu should be under "Features,"
not in the corner of the window.
Okay, now the submenu is in the right place, but it overlaps "Features." We'll
address this in the next lesson!
To recap:
• Navigation menus are almost always best created using an <ul> list.
• You can style a menu to look normal by turning list items into inline boxes
using the display property.
We haven't encountered depth-related issues so far. Until now, all our HTML
elements intuitively appeared on top of or below each other. However, as we
started dealing with more complex things, we can no longer rely solely on the
browser for this.
The z-index property allows you to control the depth positioning of elements on
the page. If you imagine the screen as a 3D space, a negative z-index sends an
element deeper into the page, while a positive one brings it forward.
Therefore, the .features-menu element should have a lower z-index value than the
Miscellaneous link. The default z-index is 0, so we'll increase the values for both
elements:
.dropdown > span {
z-index: 2;
position: relative; /* This is very important! */
cursor: pointer;
}
.features-menu {
/* ... */
z-index: 1;
}
Now the Miscellaneous link should appear above the dropdown menu. It's crucial
not to forget the relative; line, as only positioned elements respond to the z-index
property.
Now that the submenu is ready, our final task is to hide it until the user hovers over
it. Remember the :hover pseudo-class? We can use it to turn the regular submenu
into an interactive dropdown.
First, we need to modify the .features-menu rule so that the submenu is only
displayed when the user hovers over it by adding the :hover selector. Update the
.features-menu selector like this:
.dropdown:hover .features-menu { /* Previously it was `.features-menu`
*/
display: flex; /* Leave everything else as it is */
flex-direction: column;
background: #B2D6FF;
/* ... */
}
Now we need to hide the submenu using the display property. Add a new rule to
[Link]:
.features-menu {
display: none; /* Add a new rule */
}
To recap:
• Z-index allows you to control the depth positioning of elements on the page.
• Relative positioning
• Absolute positioning
• Relative to absolute positioning
• Fixed positioning
We used these new techniques to create a fairly complex navigation menu. This
menu is an excellent example of starting with HTML. First, we create a semantic
structure, and only then do we write CSS to position the boxes where they need to
be.
However, our menu has one issue: it is not suitable for display on mobile devices.
We will address this in the next chapter.
Responsive design is based on the idea that a website should display equally well
on all devices, from widescreen monitors to mobile phones. This approach to web
design and development eliminates the distinction between mobile and desktop
versions of the site.
Responsive design relies on CSS media queries. Media queries allow you to apply
CSS rules under specific conditions. For example, they can instruct the browser to
ignore or apply certain rules based on the user's device.
With media queries, you can display the same content differently through multiple
CSS rules. Instead of maintaining two separate websites for smartphones and
desktops, you can use the same HTML markup and web server for both versions of
the site. This means that when you add a new post or fix a typo, it applies to both
the mobile and desktop versions simultaneously. Here's🔗 a good example of
responsive design implementation if you switch between different versions of the
site.
In this chapter, we will get acquainted with media queries, which are a relatively
simple addition to the CSS we are already familiar with.
To recap:
•
• Click on the mobile device icon (Toggle device toolbar) to activate it (it will
turn blue).
•
• Next, hover over the boundary of the workspace (the mouse icon will
change upon hovering), and while holding down the mouse button, drag it
left or right. Also, pay attention to the numbers in the top panel; as you
increase/decrease them, the size of the workspace will change.
• You can also manually enter specific dimensions for the workspace.
•
• If you click on the "Dimensions" panel, it will open a menu with the names
of various mobile devices. Here, you can choose devices and see how your
website will appear on devices such as iPhone SE, XR, and others.
• To manually adjust the workspace size, set the Dimensions value to
"Responsive."
•
• To close the panel, you can click the "x" in the upper right corner or press
the F12 key.
•
• As usual, let's start by creating a new project. Let's name it "responsive-
design" and create a file called "[Link]". This will be an extremely
barebones page to help us understand some key concepts about responsive
design:
• <!DOCTYPE html>
• <html lang="en">
• <head>
• <meta charset="UTF-8"/>
• <meta name="viewport" content="width=device-width, initial-
scale=1">
• <title>Responsive Design</title>
• <link rel="stylesheet" href="[Link]"/>
• </head>
• <body>
• <!-- There's nothing here -->
• </body>
• </html>
• Next, create an empty file called "[Link]" and download a few images
from here: [Link]. After extracting them into the folder with
"[Link]," your project should look like this:
Let's start by updating the background of the <body> element based on the device
screen resolution. This is a convenient way to test if our media queries are working
before diving into more complex styling.
We'll create three layout variations: one for mobile, one for tablets, and one for
desktops.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
As we resize the browser window, the background color will change: blue when
the window is wider than 960px, gray when it's between 401px and 960px, and
green when it's less than 400px.
Media queries always start with the @media rule, followed by a conditional
statement, and then curly braces. Inside the curly braces, we add our regular CSS
rules. The browser will pay attention to these rules only if the specified conditions
are met.
The media query "only screen" means that the styles should only apply to devices
with screens. "Min-width" and "max-width" are called media features, and they
determine the resolution of the devices we are targeting.
The examples provided cover the most common scenarios, but there are many
other conditions to consider, such as the device's orientation (portrait or landscape).
In summary:
• Media queries are a powerful tool for creating responsive designs that adapt
to different device screen sizes and orientations.
1. Flexible Layout: This layout stretches or narrows to fit the screen width,
following a similar principle to flexboxes.
2. Fixed-Width Layout: This layout maintains a consistent width regardless of
the screen resolution (we worked on a similar layout in the chapter about
CSS selectors).
In our web page, versions for mobile and tablets will use flexible layouts, while the
desktop version will have a fixed-width layout.
Setting Breakpoints
Most responsive design patterns follow a similar behavior: flexible layouts are
used for mobile and tablet versions, while fixed-width layouts are used for
desktops. There are specific reasons for this choice.
Flexible layouts allow us to focus on the range of screen sizes, rather than targeting
individual mobile devices. This is a crucial point for web designers. When they
create a mobile layout, they aim to design something that will look good on any
device, rather than trying to optimize it for specific devices like the iPhone 12,
Samsung Galaxy, or iPad mini. They create a flexible layout that adapts well to
any device.
This is why specific min-width and max-width values are not as important. What
we need to know is that, for example, the layout should display nicely on screens
roughly around 400px in width.
To Sum Up:
• Flexible layouts adapt to the screen width and are used for mobile and tablet
layouts.
• Fixed-width layouts do not change their width based on screens and are used
for desktop layouts.
Let's get to work and start creating layouts! It's better to start with the mobile
version and then smoothly transition to the desktop one. Mobile layouts are usually
simpler, so when we begin with creating a mobile version, the amount of CSS code
we can use for both layouts significantly increases. This approach is also called
'mobile first.'
To start, we need to populate the <body> element with a couple of empty boxes.
Each box will contain an image to make it easier for us to distinguish between
them:
<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/sign_up.svg"/>
</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 are our basic styles that will be applied to all three layouts. Let's add
them above the @media rules we created earlier and below the general rule
responsible for resetting margins and paddings:
.page {
display: flex;
flex-wrap: wrap;
}
.section {
width: 100%;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
}
.menu {
background-color: #0b24fb;
height: 80px;
}
.header {
background-color: #f1eeee;
}
.content {
background-color: #ffffff;
height: 600px;
}
.sign_up {
background-color: #29fd2f;
}
.feature-1 {
background-color: #ffffff;
}
.feature-2 {
background-color: #f1eeee;
}
.feature-3 {
background-color: #29fd2f;
}
If we narrow the browser window, we can see what our mobile layout looks like.
By the way, pay attention to the flex-wrap property in .page; it will allow us to
easily switch to tablet or desktop layouts.
By separating these basic styles from the media queries, we can override and
extend them as we create different layouts. This is very convenient if, for example,
we need to change the color scheme for the entire website. We won't have to
search for individual background-color values in different @media rules; we can
simply change one line in the basic styles.
Tablet Layout
Let's move on to the tablet layout. The only difference between the mobile and
tablet versions of the layout is that the "Sign Up" and "Features" sections form a
2x2 grid instead of a single column.
With flexbox, this can be done quite easily. Let's change the width of the flex items
so that they take up half of the screen, and flex-wrap will take care of the rest. Of
course, we want this to apply only to screens with tablet resolutions, so we need to
update the @media rule as well. Replace the existing media query for tablets with
the following:
/* Tablet Layout */
@media only screen and (min-width: 401px) and (max-width: 960px) {
.sign_up,
.feature-1,
.feature-2,
.feature-3 {
width: 50%;
}
}
To see these changes, make sure your browser window has a width between 400px
and 960px, then scroll to the bottom of the page.
Again, the exact screen width is not crucial. This layout will adapt to any width
within the range specified in the media query.
To sum up:
Now, let's move on to the layout for computer screens! We don't want our page to
expand infinitely, so we'll set a fixed width and center it using auto margins. Like
with tablets, we should wrap all of this in our media queries. Let's update the media
query for desktops:
/* Desktop Layout */
@media only screen and (min-width: 961px) {
.page {
width: 960px;
margin: 0 auto;
}
.feature-1,
.feature-2,
.feature-3 {
width: 33.3%;
}
.header {
height: 480px;
}
}
Additionally, let's change the colors of feature-3 and content to make the changes
more noticeable:
.content {
background-color: #f1eeee;
height: 600px;
}
.feature-3 {
background-color: #ffffff;
}
Now the width is correctly set, and we have some space freed up below the header.
Everything is almost ready; we just need to make the 'Sign Up' and 'Content' boxes
appear below the 'Materials' section.
Here, flexbox and its order property can shine. Add the following rules to the
desktop media query:
.sign_up {
height: 200px;
order: 1;
}
.content {
order: 2;
}
Voilà – the responsive website is ready! And all of this thanks to a few dozen lines
of CSS. Moreover, we didn't have to change a single line of HTML!
This was one example of how to create a responsive design. Similar techniques can
be used to work with other types of designs as well. The key is to start with basic
styles and then adjust them for different devices using separate CSS rules with
@media.
Disabling Zooming
To complete our responsive webpage, we need to add one final touch. Before
responsive design, full versions of websites were displayed on mobile phones, and
the page was zoomed in to fit the mobile screen.
This can create problems when displaying the mobile version. To disable this
function, we need to add the following element to the <head> of the document.
This element is extremely important and should be present on all pages we create.
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
To understand how this works, let's try simulating a mobile device in our browser
window. Open [Link] in Google Chrome, then follow the instructions in
the 'How to Test Responsive Websites' section to select a mobile device from the
list.
Now we should see the mobile version of the site with zooming disabled.
To sum up:
This was the straightforward part of responsive design. In the next chapter, we will
delve into a more complex aspect: responsive images.
In the previous chapter, we learned how to use media queries to create layouts for
websites on mobile, tablets, and desktops. Now, we will learn how to do the same
for images.
• Device size;
• Image size;
• Device screen resolution.
This will be a bit more complex than media queries because in their case, we only
thought about the width of the device screen. But it's not as scary as it sounds -
there are standard ways to solve all these problems. We will get to know them so
that in the future, such cool things🔗 don't seem daunting.
To summarize:
• When creating responsive images, you need to consider the device size,
image size, and device screen resolution.
• To start experimenting with responsive images, we will need a responsive
website. We will use the website from the previous chapter as our guinea
pig. Let's rename 'responsive design' to 'responsive-images' and unzip
'[Link]' into the 'images' folder:
•
• We will have several copies of PNG and JPG images so that the browser can
decide which images to load based on the device size and screen resolution.
Before we begin working with responsive images, let's talk a bit about screen
resolution. Retina screens have twice as many pixels per centimeter as standard
screens. This greatly affects how images are displayed in web browsers.
To display correctly on Retina devices, an image should be twice the final size. For
example, if we want to add an image to a page with dimensions of 500x250 pixels,
the corresponding file should have a resolution of 1000x500 pixels.
Let's clarify that we are simplifying things a bit - not all Retina screens are
exactly the same. Some screens may have three times as many pixels per
centimeter as standard screens. All examples in this chapter will be based on a
pixel density that's two times higher, but the techniques we discuss will also
apply to cases where pixel density is three times higher.
The simplest way to address all these issues is to use SVG images. Since SVG is a
vector format, it doesn't have issues with various screen resolutions. Let's see what
happens when we add an image to the '[Link]' page:
<div class="section content">
<img class="illustration" src="images/[Link]" />
</div>
Browsers automatically scale SVG images for Retina screens, so this 500x250
SVG image will look great on any device.
SVG images allow us to forget about resolution-related problems, but we still need
to resize images to fit well on mobile and tablet versions. Firefox does this
automatically, but if you open this page in Chrome and start narrowing the
browser, the image won't change in size.
To create a responsive image for Chrome, we need to make sure the illustration
always fills the container's width. Add the following rule to the base stylesheet
'[Link]':
.illustration {
width: 100%;
}
When we set the width of an image to 100%, the aspect ratio is calculated
automatically. This will solve our mobile layout problems, but it will make the
desktop version of the site huge.
This behavior might be suitable for some designs, but it's not what we want right
now. We need to return the width of the illustration to its original value of 500
pixels. We can do this with an inline style:
<div class="section content">
<img class="illustration" src="images/[Link]" style="max-width:
500px"/>
</div>
This is one of the few cases where an inline style can be used because it addresses
a specific property of the image. Image dimensions are more related to content
than presentation, so it's not surprising that we encountered them in HTML code
rather than CSS.
To Recap:
If we don't bother with optimization, working with responsive raster images is not
much more complicated than working with SVG. Let's replace '[Link]'
with a PNG file:
<div class="section content">
<div class="illustration">
<img src="images/[Link]" style="max-width: 500px"/>
</div>
</div>
We slightly modified our HTML structure by placing the <img/> inside another
container. If we don't do this, the image will be distorted because flexbox will set
the same height for it as the '.content' container. So, we also need to adjust the CSS
rule for '.illustration':
.illustration img {
width: 100%;
display: block;
}
The image file name has a suffix '-big' – it's a high-resolution PNG version with
dimensions of 1000x500 pixels. This size is required for proper display on Retina
screens. If you use the same image at a lower resolution (500x250) on regular
screens, everything will look fine, but not on Retina screens.
This is a somewhat lazy way to create responsive PNG, GIF, or JPG images
because it involves using a high-resolution image in all situations, even when it's
not necessary.
To Recap:
• Another lazy way to optimize raster images is to use the highest resolution
image for all screen types.
Optimizing for Retina Screens using srcset High-resolution images are heavy. A
file like '[Link]' takes up twice as much space as a file with the same
image at low resolution. There's no point in serving all this additional information
when it's not needed.
Adding the 'srcset' attribute to the <img/> element allows us to display our high-
resolution image only on devices with retina screens, while using the low-
resolution file for regular screens. Let's update the '.illustration' element:
<div class="illustration">
<img src="[Link]"
srcset="images/[Link] 1x,
images/[Link] 2x"
style="max-width: 500px"/>
</div>
The 'srcset' attribute points to a list of alternative image files and their properties,
which tell the browser which images to use. '1x' tells the browser to use
'[Link]' for standard-resolution screens, and '2x' indicates that
'[Link]' should be used for retina screens.
To Recap:
• Optimizing for retina screens using 'srcset' is suitable for images with a
width less than 600 pixels.
We managed to save a few bytes for devices without retina screens. However,
there's one caveat with optimizing using 'srcset': if a user has a retina smartphone,
the high-resolution image will load even when the standard resolution is sufficient.
Let's say we need to display a large photo in the '.header' element. The desktop
layout has a header with a width of 960 pixels, so our photo should be at least 1920
pixels wide to display properly on retina screens. We also need a version with a
width of 960 pixels for standard screens. Retina smartphone screens are usually
smaller than 400 pixels in portrait mode, so an image with a width of 800 pixels
will be suitable, which means the standard version of the image will work in this
case.
So, the task is to optimize large images based on their final dimensions, rather than
the device's screen resolution. Let's add the large photo to the '.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>
A quick explanation of the above code: The 'src' attribute image will be used if
'srcset' is not supported by browsers. It's also necessary for search engines. Now,
let's break down what's in the 'srcset' attribute value:
• If the maximum screen width of the device is less than or equal to 2000
pixels, it will use '[Link]' (instead of 'px,' 'w' is used in 'srcset,' just
remember that 'w' stands for the image's intrinsic width, which can be
viewed in the image properties on your computer).
• If the maximum screen width of the device is less than or equal to 1000
pixels, it will use '[Link].'
The 'sizes' attribute specifies the condition under which the image's width will be
set. If the screen width is greater than or equal to 960 pixels, the image width will
be 960 pixels; otherwise, it will be 100vw.
But image width alone is not enough to determine which image to load. We also
need to specify the final width of the displayed image. For this, we need the 'sizes'
attribute, which defines a series of media queries and the image width after these
media queries take effect.
If we specify that the screen must be at least 960 pixels wide, the image will also
be 960 pixels wide. If we set it to '100vw,' it tells the browser that the image should
occupy 100% of the screen width. You can read more about 'vw' here.
Now, we need to position the image correctly in the header. Add the following
rules to our base styles, right below the media query with mobile styles:
.header {
height: auto;
justify-content: inherit;
align-items: inherit;
}
.photo img {
width: 100%;
display: block;
}
The low-resolution image has a width of 1000 pixels, so it can be suitable for
retina screens with a screen width of 500 pixels. Now in Firefox, you can resize the
browser to display either the retina or standard version of the image.
To Recap:
For this, we'll need the <picture> and <source> elements. The <picture> element
simply contains the code, while the <source> element allows us to load images
based on media queries. Let's change our '.header' as follows:
<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>
This level of control will make any designer very happy, but there's one problem:
now the browser can't automatically choose the appropriate image. This means we
no longer have retina optimization: as long as the browser width is 401px or larger,
the browser will always use the high-resolution image.
In such cases, it's better to use the 1x or 2x version of 'srcset' for images smaller
than 600 pixels in width and use 'srcset' and 'sizes' as shown in the previous lesson
for larger photos. The <picture> element is best left for cases when the designer
and we want to do something out of the ordinary.
To Recap:
The topic of responsive images may seem quite complex, but essentially it revolves
around two tasks:
1. How to fit images into the mobile version, taking into account their actual
sizes.
2. How to avoid loading unnecessarily large files for users.
We've addressed the first task by making images always fill 100% of the container
they occupy while limiting their size with the 'max-width' inline style. For the
second task, we used 'srcset' to optimize for screen resolution, 'scrset' and 'sizes' for
device width optimization, and the <picture> element for manual control over
which image file is displayed.
Responsive design is continually evolving, and browsers have only recently started
using the image optimization techniques we covered in this chapter, despite
responsive design being a standard for over half a decade. While the technologies
used to create responsive websites may change, the fundamental challenge
remains: how to display the same content on different devices? Therefore, the
concepts we've learned in this chapter will always be valuable.
In the last five chapters, we worked with page layouts. We learned about flexbox,
advanced positioning, and how to apply these techniques to screens of varying
widths. Essentially, this is all we need to create web page layouts using HTML and
CSS. In the next chapter, we'll return to the world of HTML and get acquainted
with new elements that will make search engines love our website.
At the core of semantic markup is the idea that HTML should convey the meaning
of content rather than dictate its visual appearance. We've already engaged in
writing semantic HTML (e.g., using <strong> instead of <b>), but there's a whole
set of elements created specifically to provide additional semantic value to a web
page's layout. These elements are called sectioning elements, and they look like
this:
In this chapter, we're going back to plain HTML—there won't be any box models,
flexboxes, or positioning schemes. However, that doesn't mean you can't apply
CSS rules to these new elements. Sectioning elements are like semantic <div>s.
To Summarize:
Every HTML document has its own outline, which search engines use to
understand the hierarchy of content on a page. All headers from <h1> to <h6> define
the document's outline. Let's see how it works by creating a blog post skeleton in
the [Link] file:
<h1>Semantic Markup</h1>
<p>SL School. Published on November 5th.</p>
<p>This is an example webpage about semantic markup.</p>
<h2>Document Outline</h2>
<p>HTML5 provides several sectioning elements that affect the document's
outline.</p>
<h3>Headers</h3>
<p>The <code><header></code> element is one of the sectioning
elements.</p>
<h3>Footers</h3>
<p>The <code><footer></code> element is also a sectioning
element.</p>
The HTML5 Outliner extension is a very useful tool for exploring the document's
outline. If you copy the entire [Link] into the text field below, you should
see the outline of our example with the following structure:
• Each <h1> element creates a new section in the document's outline, and all
less significant headers become subsections of that main header. For
example, the "Semantic Markup" section has two subsections: "Document
Outline" and "Inline Semantic Markup." The same applies to <h2>, <h3>, and
so on.
• Note that the actual level of the header is not crucial; what matters is
whether it's more or less significant than the current section's header. Let's
change the <h3> headers to <h4> and run it through the HTML5 Outliner
again. Since <h4> is still less significant than <h2>, our document's outline
won't change.
How does this relate to semantic markup? Headers are one of the most semantic
elements on any web page. They play a significant role in how search engines
determine what's important on a web page. In addition, the semantic HTML
elements we're about to learn add meaning or even change the document's outline.
To recap:
We can use <article> to isolate the main content from the rest:
<article>
<h1>Semantic Markup</h1>
<p>SL School. Published on November 5th.</p>
<p>This is an example webpage about semantic markup.</p>
<!-- ... -->
<p>This was written by someone from SL School.</p>
</article>
Note how we left the copyright notice outside the <article> since it's a footer for
the entire website, not just for this particular segment. In
essence, <article> elements are mini-pages within our HTML document. They
have their headers, footers, and structure, all in complete isolation from the rest of
the site.
For blog posts, newspaper articles, or web pages on a single topic, there's usually
only one <article> element for the entire page. However, there's no rule against
using multiple <article> elements on the same page. A good example is when a
single page displays multiple blog posts. Each of them can be enclosed in
separate <article> tags, like this (this is just an example; you don't have to add
anything to [Link]):
<article>
<h1>First Post</h1>
<p>Content goes here.</p>
</article>
<article>
<h1>Second Post</h1>
<p>More content here.</p>
<h2>Subsection</h2>
<p>Explanations for the subsection.</p>
</article>
<article>
<h1>Last Post</h1>
<p>Final part of the content.</p>
</article>
This tells anyone looking at the page that there are three different articles.
Essentially, we're combining multiple HTML files into one document without
confusing search engines and browsers trying to analyze our content.
If we were to use a bunch of <div> elements with arbitrary class names instead, it
would be much harder to navigate the internet.
Sections
The <section> element is similar to <article>, but the content doesn't necessarily
have to stand alone outside the document's context. Think of <section> as a
detailed way of defining sections in the document's outline. But why do we need
this when headers can do the job for us? Often, a layout requires a section to be
enclosed in a container, so we should use the more detailed <section> element
instead of a generic <div>.
<h3>Headers</h3>
<p>The <code><header></code> element is one of the sectioning
elements.</p>
<h3>Footers</h3>
<p>The <code><footer></code> element is also a sectioning
element.</p>
</section> <!-- Add this -->
The document's outline remains the same, but we've added additional semantic
structure, along with an excellent hook for applying any CSS styles (e.g.,
background color) to this specific section.
The previous changes also have an interesting effect on the behavior of our
headers. Let's see what happens when we move <h2> down one level:
<section>
<h6>Inline Semantic Markup</h6> <!-- Change this header -->
<p>The <code><time></code> element is semantic but not
sectioning.</p>
</section>
The <h6> header is now below <h3> that precedes it, so we might expect <h6> to
become part of the "Footer" section. However, this is not the case; the document's
outline remains unchanged!
It's crucial that each <section> element contains at least one header, or else an
"untitled" section will be added to the document every time. For example, let's
update [Link] and run it through the HTML5 Outliner again:
<h2>Inline Semantic Markup</h2>
<section>
<!-- This will move into an "Untitled" section -->
<p>The <code><time></code> element is semantic but not
sectioning.</p>
</section>
As a result, a new section is created, but since there's no associated header, it's
unclear how to name it. It's best to avoid such situations when
using <section> elements.
To recap:
The <nav> element allows you to markup various navigation sections of a website.
These include the main navigation menu of the website, links to pages in the
sidebar, content, and any other groups of links. For example, we can wrap our
navigation menu in the <nav> element:
<h1>SL School</h1>
<nav> <!-- Add this -->
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About Us</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Subscribe</a></li>
</ul>
</nav> <!-- Add this -->
This is very convenient for search engines because now they can quickly identify
the structure of the entire website and, consequently, find different pages on it
faster. You can even add multiple <nav> elements to one page if you have multiple
sets of related links.
Headers
A good practice is to include the website's name/logo and the main navigation
menu in the <header>, so let's add it to our example:
<header>
<h1>SL School</h1>
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About Us</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Subscribe</a></li>
</ul>
</nav>
</header>
Without this <header>, search engines and screen readers won't understand that the
first <p> element exists separately from the main content of the article. Like
the <section> element, this is an excellent hook for CSS since the material's title
and the mention of the author's name usually have different styling from the rest of
the article. Again, <header> can be seen as a more semantic alternative to
a <div> container.
To recap:
Footers
Footers behave in the same way as <header> in the sense that they are associated
with the nearest sectioning element. We can use them for footnotes with copyright
information and author information inside <article>. Let's add the following two
footer elements to [Link]:
<article>
<header>
<h1>Semantic Markup</h1>
<p>SL School. Published on November 5th.</p>
</header>
</article>
</body>
</html> <!-- And here -->
We created a footer inside the <article> element only for the content inside it
because it contains author information. The second footer, in this case, applies to
the entire page.
Asides
Headers and footers are excellent ways to add additional information, but
sometimes we need to remove it. For example, sponsored content on a website may
contain information about the sponsoring company, but we do not want to include
this information in the main text. In this case, <aside> comes in handy.
Let's add an advertising aside directly below the header in the [Link] file:
<article>
<header>
<h1>Semantic Markup</h1>
<p>SL School. Published on November 5th.</p>
</header>
<!-- Advertising aside -->
<aside class="advert">
<img src="[Link]"/>
</aside>
Even though the image is inside the <article> element, screen readers recognize
the image as only partially related to the article's content. In addition to
advertising, <aside> is also great for highlighting definitions, statistics, or
questions. If an element within an article looks different from the rest of the
article's content, it's likely an aside.
When the <aside> element is used outside <article>, it is associated with the entire
page, just like <header> or <footer>. Let's add the following code below the
second <footer> tag:
<aside class="sidebar">
<h2>Sidebar</h2>
<p>Sidebar content</p>
<nav>
<h3>HTML and CSS Markup</h3>
<ul>
<li><a href="#">Introduction</a></li>
<li><a href="#">Basic web pages</a></li>
<li><a href="#">And so on...</a></li>
</ul>
</nav>
<nav>
<h3>JavaScript Course</h3>
<ul>
<li><a href="#">Introduction</a></li>
<li><a href="#">Hello, JavaScript</a></li>
<li><a href="#">And so on...</a></li>
</ul>
</nav>
</aside>
Let's pay attention to the class attribute: if we were concerned even a bit about
CSS in this section, we could have added styles to our <aside> elements in the
same way we did with all the <div> elements we worked with throughout the
chapter. By the way, we'll cover this in the next lesson!
It's better to use semantic markup as often as possible because it allows for
analyzing the structure of the content and, therefore, organizing pages more
efficiently. However, sometimes we need a container element if none of the
semantic HTML elements we've discussed fit the bill. So, there's nothing wrong
with using the familiar <div> for layout.
This is especially important for Flexbox since it uses many <div> elements to
group flex items.
Recap
• <footer> elements are the same as <header>, but they are placed at the end
of an article or website, not at the beginning.
• <aside> elements allow you to separate parts of a document as side notes,
labels, etc.
Dates and times can be represented in various formats. For example, November 5,
2023, can be written as "05.11.2023," "5 November," or even "this week,"
depending on the current date. Computers find it challenging to analyze such
ambiguous language, so the <time> tag is often used in such cases.
The <time> element is used to represent a specific time of day or a calendar date.
Providing information in this format allows browsers to accurately identify specific
dates.
The date itself is specified through the datetime attribute. The data in this attribute
is written from the longest period of time to the shortest: year, month, day. Even
though the year is not displayed to users, search engines receive information that
the article was published in 2023.
You can use time and time zones outside the datetime attribute as well. For
example, if you want to add 3:00 PM PST to your date, you can do it like this:
htmlCopy code
<time datetime="2023-11-5 15:00-0800">5th of November</time>
Address
The <address> element is similar to <time> in that it doesn't interact with the
overall document structure but rather adds metadata to an <article> or <body>.
This element provides contact information for the author of the article or web page
and should not be used for physical addresses.
For example, if you want to add an email address to the footer of your article, you
can do the following:
<footer>
<p>This was written by someone from SL School. This footer is intended
only for
the <code><article></code>.</p>
<address>
You can contact us here: <a href="[Link]
School</a>.
</address>
</footer>
Finally, there are elements like <figure> and <figcaption>. The first is responsible
for standalone images, diagrams, and sometimes code snippets. The latter element
links a caption to a <figure> element.
Usually, both of these elements are used to add descriptions to <img/> elements in
an article:
<section>
<h2>Document Outline</h2>
<p>HTML5 has several sectioning elements that affect the document
outline.</p>
<figure>
<img src="[Link]" alt="Diagram showing the caption for
<article>, <section>, and <nav>." />
<figcaption>Diagram showing the caption for <article>, <section>, and
<nav>.</figcaption>.
</figure>
<!-- ... -->
</section>
The alt attribute is closely related to the <figcaption> element. The alt attribute
serves as a textual replacement for the image, and <figcaption> is responsible for
the description displayed alongside the image or its text equivalent.
By using <figcaption> as shown in the example above, you can safely omit
the alt attribute without harming SEO. Sometimes it's much more convenient to
display descriptions using <figcaption> than using invisible alt attributes.
Recap:
HTML Forms
HTML forms allow us to collect information from visitors to our website. Mailing
list subscriptions, contact forms, and comments on blog posts are some of the most
obvious examples of forms for small websites. In larger organizations that rely on
their websites for revenue, forms are often at the forefront.
Forms are the pages that generate revenue. E-commerce sites use them to sell
products, SaaS companies charge for their services, and non-profit organizations
collect donations online. Many companies measure the success of their websites
through the efficiency of their forms. This often means that forms undergo endless
testing and constant optimization.
There are two aspects to a functional HTML form: the frontend user interface and
the backend server. The frontend is responsible for the appearance of the form,
while the backend is the code that processes the form. In this chapter, we will focus
entirely on the frontend, leaving the study of backend forms for the future.
Recap:
• Forms are one of the key aspects of a website that are responsible for
monetization.
• Unfortunately, designing forms can be complex, so it's always useful to have
a layout of the page you want to create before you start coding.
• In this chapter, we will create a speaker registration form for a conference. It
will include a substantial set of HTML form elements: text fields, radio
buttons, a dropdown menu, checkboxes, and a submit button.
• Let's start by creating a new project called "forms" and insert a new HTML
file named "[Link]" into it. To begin with, let's add
markup for the header. (It's time to remember semantic markup!)
• <!DOCTYPE html>
• <html lang="en">
• <head>
• <meta charset="UTF-8"/>
• <title>Speaker Registration</title>
• <link rel="stylesheet" href="[Link]"/>
• <meta name="viewport" content="width=device-width, initial-
scale=1">
• </head>
• <body>
• <header class="speaker-form-header">
• <h1>Speaker Registration</h1>
• <p><em>Want to present at the conference? Fill out this
form.</em></p>
• </header>
• </body>
• </html>
•
• Next, let's create a "[Link]" file and add the following CSS code. Here,
we're using a simple flexbox to center the header (and the form) regardless
of the browser window's width:
• * {
• margin: 0;
• padding: 0;
• box-sizing: border-box;
• }
•
• body {
• color: #0b24fb;
• background-color: #f1eeee;
• font-family: "Helvetica", "Arial", sans-serif;
• font-size: 16px;
• line-height: 1.3;
•
• display: flex;
• flex-direction: column;
• align-items: center;
• }
•
• .speaker-form-header {
• text-align: center;
• background-color: #ffffff;
• border: 1px solid #29fd2f;
• border-radius: 3px;
•
• width: 80%;
• margin: 40px 0;
• padding: 50px;
• }
•
• .speaker-form-header h1 {
• font-size: 30px;
• margin-bottom: 20px;
• }
• These basic CSS rules will allow us to create a mobile layout and provide
the foundation for a desktop layout. Later in this chapter, we will create a
media query for a fixed-width desktop layout.
Let's dive into forms! Every HTML form starts with the <form> element. You can
use several attributes within it, but the most important ones are action and method.
Let's add an empty form to our HTML document, right under the <header>:
<form action="" method="get" class="speaker-form">
</form>
The action attribute specifies the URL that the form is sent to for processing. This
is where the collected data is sent when the user clicks the "Submit" button.
Typically, it's a specific URL determined by our web server, which knows how to
handle this information. The most common backend technologies for form
handling include [Link], PHP, and Ruby on Rails.
The method attribute can take the values post or get, both of which define how the
form data is sent to the server. The method used depends on how our web server
wants to handle the form. post is usually used when we need to modify data on the
server, while get is used when we only want to retrieve data.
By leaving the action attribute empty, we tell the form to send data to the same
URL it came from. When combined with the get method, this allows us to check
the form's contents.
Of course, right now, we're looking at an empty form, but that doesn't mean we
can't add some styles to it, just like we did with the <div> container. This will turn
the form into a box that matches our <header> element:
.speaker-form {
background-color: #ffffff;
border: 1px solid #29fd2f;
border-radius: 3px;
width: 80%;
padding: 50px;
margin: 0 0 40px 0;
}
Recap
To collect user-entered data, we need a new tool: the <input/> element. Insert the
following code inside the <form> to create a text field:
<div class="form-row">
<label for="full-name">Name</label>
<input id="full-name" name="full-name" type="text"/>
</div>
We have a <div> container to help with styling. This is a common practice for
organizing input elements. We also have a <label>, which can be considered
another semantic HTML element for forms, similar to <article> or <figcaption>, but
for forms. The for attribute of the <label> should match the id attribute of the
associated <input/> element.
Finally, the <input/> element creates a text field. It's a bit different from other
elements we've encountered because it can drastically change its appearance
depending on the type attribute. In this chapter, we'll explore other values as well.
It's important to note that we won't be using ID selectors; the id attribute is used to
associate the <label> with the input element.
Conceptually, the <input/> element represents a "variable" sent to the backend
server. The name attribute defines the name of this variable, and the value is what
the user enters in the text field. We can pre-fill this value by adding
a value attribute to the <input/> element.
The <input/> element can be styled like any other HTML element. Update
the [Link] file following the concepts from previous chapters:
.form-row {
margin-bottom: 40px;
display: flex;
justify-content: flex-start;
flex-direction: column;
flex-wrap: wrap;
}
.form-row input[type='text'] {
background-color: #ffffff;
border: 1px solid #29fd2f;
border-radius: 3px;
width: 100%;
padding: 7px;
font-size: 14px;
}
.form-row label {
margin-bottom: 15px;
}
All our styles are now part of the namespace of .form-row. This style isolation
makes it easier to create different form styles. We'll see why it's convenient to
avoid global [type='text'] input and label selectors when we get to radio buttons.
Finally, let's set up these basic styles for creating a desktop layout. Add the
following media query to the end of our style sheet:
@media only screen and (min-width: 700px) {
.speaker-form-header,
.speaker-form {
width: 600px;
}
.form-row {
flex-direction: row;
align-items: flex-start; /* To avoid stretching */
margin-bottom: 20px;
}
.form-row input[type='text'] {
width: 250px;
height: initial;
}
.form-row label {
text-align: right;
width: 120px;
margin-top: 7px;
padding-right: 20px;
}
}
Here, we used the flex-direction property to make the <label> appear above
the <input/> element in the mobile layout:
The 'type' attribute of the <input/> element also allows for basic data validation.
Let's try adding another input element that only accepts email addresses:
<div class="form-row">
<label for="email">Email</label>
<input id="email"
name="email"
type="email"
placeholder="sls@[Link]"/>
</div>
This works just like 'type="text' input, except it checks whether the user has
entered an email address.
However, it's more than just validation. By telling browsers that we're looking for
an email address, we provide a more intuitive user interface. For example, when a
smartphone browser sees 'type="email," it displays a special email keyboard for
the user with easy access to the '@' symbol.
Also, notice the new 'placeholder' attribute, which allows displaying default text
when the <input/> element is empty. This is a good UX practice that encourages
users to input their data into the form.
There are many other built-in validation parameters, apart from email addresses,
which you can read about in the MDN reference in the section about <input/>.
Special attention should be given to attributes like 'required,' 'minlength,'
'maxlength,' and 'pattern.'
We want our email field to match the text field from the previous section, so let's
add another attribute selector to the existing 'input[type='text']' rule:
/* Change this rule */
.form-row input[type="text"] {
background-color: #FFFFFF;
/* ... */
}
Remember that by using 'type,' we style all our <input/> elements, including future
radio buttons and checkboxes, which can complicate the form's styling. Therefore,
it's essential to specify a value (text, email, password, etc.) for this attribute.
Let's not forget about styles for the desktop version. Update the corresponding
'input[type="text"]' rule in our media query to include the following (notice that
we're preparing for the upcoming sections with select and textarea selectors):
@media only screen and (min-width: 700px) {
/* ... */
.form-row input[type="text"],
.form-row input[type="email"], /* Add these */
.form-row select, /* selectors */
.form-row textarea { /* as well */
width: 250px;
height: initial;
}
/* ... */
}
Now that we have 'valid' and 'invalid' input values, we need to communicate this to
users. Pseudo-classes ':invalid' and ':valid' allow us to style these states separately.
For example, if a user enters an invalid value, we want to display both a border and
text in a special shade of red. Let's add the following rule to our stylesheet outside
of the media query:
.form-row input[type="text"]:invalid,
.form-row input[type="email"]:invalid {
border: 1px solid #D55C5F;
color: #D55C5F;
box-shadow: none; /* Remove the default red glow in Firefox */
}
Until we add a submit button, you'll only see this in Firefox, but the idea is clear.
There's a similar ':focus' pseudo-class that selects the element currently being filled
in by the user, giving us greater control over the form's appearance.
To recap:
Changing the 'type' property of the <input/> element to 'radio' turns it into a toggle
button. Toggle buttons (or 'radio buttons') are often displayed as circles that fill or
highlight when hovered over. Working with such buttons is slightly more complex
than with text fields because they always exist in groups, allowing users to select
one from several predefined options.
This means that for each <input/> element, we need to not only come up with
names but also find a way to group the radio buttons and label the entire group.
This is where the <fieldset> and <legend> elements come into play. Each group of
radio buttons we create should:
Our example with a toggle button satisfies all of these requirements. Let's add the
following to the <form> element below the email input field:
<fieldset class="legacy-form-row">
<legend>Talk Type</legend>
<input id="talk-type-1"
name="talk-type"
type="radio"
value="main-stage" />
<label for="talk-type-1" class="radio-label">Main Stage</label>
<input id="talk-type-2"
name="talk-type"
type="radio"
value="workshop"
checked />
<label for="talk-type-2" class="radio-label">Workshop</label>
</fieldset>
Unlike text fields, users can't input their data here, so each radio button requires a
'value' attribute. This value will be sent to the server when the user submits the
form. It's also essential that each radio button has the same 'name' attribute;
otherwise, the form won't recognize them as part of the same group.
Here, we also used the new 'checked' attribute. This is a boolean attribute, meaning
it doesn't take any values; it either exists within the <input/> element or doesn't. If
this attribute exists for a radio button or checkbox element, that element will be
selected by default.
There are a few things working against us when it comes to styling radio buttons.
First, there are many elements that require attention.
Second, <fieldset> and <legend> have default styles that can vary from browser to
browser, and they might not look appealing. Third, at the time of writing this
article, <fieldset> doesn't support flexbox.
But there's no need to panic! Floats come to our rescue as a useful fallback for
outdated/problematic elements. We haven't included the radio buttons in our
existing .form-row class; instead, we've used a new class called .legacy-form-row.
By using floats instead of flexbox, we completely separate this class from our other
elements.
Let's start with styles for mobile devices and tablets by adding the following rules
outside our media query. We want to get rid of the default styles
for <fieldset> and <legend> and then use floats to make the radio buttons and
labels appear on the same line below 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 sure that <legend> aligns with
the <label> elements from the previous section (hence the width: 120px), and also
move everything to the left so that they all appear on the same line. Let's update
our media query, including 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;
}
}
To recap:
• Toggle buttons (or 'radio buttons') are often displayed as circles that fill or
highlight when hovered over.
• Each radio button requires a 'value' attribute.
• For each <input/> element in a group of radio buttons, you need a name, a
way to group the buttons, and a label for the entire group.
Dropdown menus are an alternative to radio buttons and allow users to choose
from multiple options. Dropdown menus are created using the <select> element,
which contains a set of <option> elements, each representing an item.
<div class="form-row">
<label for="t-shirt">T-Shirt Size</label>
<select id="t-shirt" name="t-shirt">
<option value="xs">XS</option>
<option value="s">S</option>
<option value="m">M</option>
<option value="l">L</option>
</select>
</div>
Just like with radio buttons using <input/> elements, we have 'name' and 'value'
attributes that are sent to the backend server and distributed among
the <select> and <option> elements.
Usually, it's better to let the browser or device determine the best way to present
the <select> element, so our CSS will be relatively simple. Unfortunately, even
simple things can be surprisingly tricky. For example, let's try changing the font
size of our <select> element:
.form-row select {
width: 100%;
padding: 5px;
font-size: 14px; /* This won't work in Chrome and Safari */
}
This will work in Firefox but not in Chrome or Safari! To work around this, we can
use the -webkit-appearance property:
.form-row select {
width: 100%;
padding: 5px;
font-size: 14px; /* This won't work in Chrome and Safari */
-webkit-appearance: none; /* But this line will fix it */
}
The -webkit prefix will only apply in Chrome and Safari (which use the
WebKit rendering engine), while it won't affect Firefox. In fact, this is a hack,
and even MDN advises against using this CSS property🔗.
These difficulties should be taken into account when designing a form. If you need
something custom, it's better to use radio buttons or JavaScript UI
widgets. Bootstrap Dropdown🔗 and jQuery Selectmenu🔗 are the most common
JavaScript solutions for customizing the appearance of menus. You can read more
about issues with <select> here🔗.
To recap:
Fortunately, styling text areas is a relatively straightforward process. Let's 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;
}
.form-row .instructions {
color: #0b24fb;
font-size: 14px;
margin-bottom: 30px;
}
Checkboxes
Checkboxes are similar to radio buttons, but they allow users to select as many
options as they want, rather than just one. This simplifies things because the
browser doesn't need to know which items belong to the same group. In other
words, we don't need a <fieldset> element or common 'name' attributes. Let's 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 available on these dates</span>
</label>
</div>
For the mobile layout, all we need to do is override the 'margin-bottom' rule we set
for other <label> elements. Add the following to the [Link] file outside of the
media query:
.form-row .checkbox-label {
margin-bottom: 0;
}
Inside the media query, let's take into account the 120-pixel column:
@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 can use 'width: auto' to keep
the entire form field on a single line (auto adjusts the field to the size of its
content).
Submit Buttons
This also gives us an opportunity to see what the user input gets sent to the server.
Let's start by entering values into all the <input/> fields, ensuring that the email
address is correctly validated. Then, click the button and check the URL you get in
your browser. You should see something like this:
[Link]?full-
name=Rick&email=rick%[Link]&talk-typ
Everything after the '?' represents the variables from our form.
Each <input/> attribute 'name' is followed by an equals sign, and then the
attribute's value, with each variable separated by an ampersand '&'. If we had a
backend server, it could easily extract this information, send a request to a database
(or elsewhere), and tell us whether the form submission was successful.
We've already dealt with styling buttons in the pseudo-classes section of the CSS
selectors chapter. Back then, we applied these styles to <a> elements, but we can
use the same methods for <button> as well.
Let's change the default, unattractive style for <button> by adding the following to
our stylesheet:
.form-row button {
font-size: 16px;
font-weight: bold;
color: #FFFFFF;
background-color: #29fd2f;
border: none;
border-radius: 3px;
.form-row button:hover {
background-color: #0b24fb;
}
.form-row button:active {
background-color: #f1eeee;
}
Like with checkboxes, we need to account for the 120px column, so include the
following rule in our media query:
@media only screen and (min-width: 700px) {
/* ... */
.form-row button {
margin-left: 120px;
}
}
To Recap:
In this chapter, we have become acquainted with the most common types of
HTML forms. Now we have a good set of tools for collecting information from
visitors to our website:
• <input type="text"/>
• <input type="email"/>
• <input type="radio"/>
• <select> и <option>
• <textarea>
• <input type="checkbox"/>
• <button>
Although we should already be quite familiar with some HTML and CSS
techniques needed to create beautiful forms, we still lack some important skills.
The information below goes beyond the scope of this lesson, but it can still be very
useful. There are two ways to process forms:
• Using the action attribute, which sends form data to an internal URL that
redirects to a page or displays an error.
• Using AJAX requests, which can be used to submit a form without leaving
the page. Success or error messages are displayed on the same page by
adding JavaScript to the HTML code.
Working with forms may not be the responsibility of a web developer. If that's the
case, you still need to work closely with backend developers to ensure that
the <form> sends the correct "name-value" pairs. Otherwise, we will have to
manually verify that the frontend and backend of our forms match each other.
We have reached the final chapter of the course. We will conclude the topic of
working with the interface with a detailed discussion of web fonts and practical
principles of typography that every web developer should know.
Web typography is responsible for the appearance of all text on our website. It
includes basic CSS text properties, such as which font to use and whether it should
be italicized or not, but it's also a broad concept. Web typography deals with letter
spacing, word spacing, line spacing, the proportions of various parts of text, and
the history of each font family.
Many of these decisions are made by designers. The only problem is that
typography is an invisible art. Therefore, to understand what designers are asking
for, we must be able to see typography just like they do.
This chapter is not only about how to add web fonts to our site or CSS properties
for styling text. We will also learn how to use all these tools correctly to create
beautiful websites.
You may forget some specific CSS properties, but the typography concepts we will
discuss in this chapter should firmly settle in our memory. These concepts make
our content more readable and help us present what we want to convey more
effectively.
Recap:
• Web typography is responsible for the appearance of all text on the website.
We will start this chapter by exploring how to display custom fonts on a web page
because it's the most exciting aspect of modern web typography. However, web
fonts have evolved significantly in recent years, so before we can start working on
our page, we need to familiarize ourselves with the various font formats available
on the internet.
Once upon a time, developers only had access to standard or, as they are also
called, safe fonts. It was a collection of about a dozen or so fonts that were pre-
installed on most computers. There were no custom fonts available for use on
websites.
If you needed a specific font, the only option was to export text as an image and
add it to the web page using <img/>. This was a significant limitation for web
designers and made the lives of developers more complicated.
Around 2010, web browsers started to support custom web fonts. Everything was
great except for the fact that each browser and device required files in different
formats. So, most websites provided four different font files simultaneously:
File Format Browser Device
This led to the creation of the 'bulletproof @font-face syntax,' which every web
developer eventually encounters.
WOFF Fonts
Relatively recently, the WOFF (Web Open Font Format) format appeared on the
internet, which simplified things significantly. Over 90% of modern browsers
support .woff fonts, and the transition to .woff2 is gradually taking place. WOFF2
is similar to WOFF but allows for a much smaller file size (and therefore better
performance).
Soon, everyone will switch to WOFF2, but for now, it's better to provide both
WOFF and WOFF2 to ensure proper font rendering in older browsers and improve
performance in modern ones. Except for cases where users of outdated browsers
make up a significant portion of our target audience, nobody works with .ttf, .svg,
and .eot fonts anymore because they belong to the past.
There are plenty of places on the internet where you can download both free and
premium web fonts. Here are a few of our favorite resources:
• [Link]🔗
• [Link]🔗
• [Link]🔗
• [Link]🔗
By the way, sometimes these sites offer both web fonts and desktop fonts (.otf and
.ttf files). WOFF was specifically developed for the needs of the modern internet,
while desktop fonts have additional functionality useful for graphics editors like
Adobe Illustrator. So, when you download or purchase fonts, get them in both
formats – it will come in handy!
Recap:
• The primary web font format at the moment is WOFF, but it will soon be
replaced by WOFF2.
• Alright, now we can get down to business. Here's the original project, the
'Web [Link]' website, that we'll be working on. Unzip it and open
the 'web-typography' folder in VS Code.
•
• We have 6 HTML documents, and they all use the same '[Link]' stylesheet.
We'll explore various typographic principles by adding styles to each of
these HTML files.
• When we open one of the HTML files using a web browser, we'll
immediately notice that our initial project is quite close to the final example,
except for web fonts and other CSS typographic properties.
There are two different ways to add web fonts to a website: using local or external
fonts. In this chapter, we'll explore both options. Let's start by adding a local web
font to our project. This is a three-step process:
Download the web font and add it to the project. Embed the web font in the
stylesheet. Use the font within the stylesheet for various purposes. We'll be
working with the files '[Link]' and '[Link]', so let's open them in our text
editor right away.
First, we need a web font. In this example, we'll use '[Link]'. You'll
need to unzip this font and place it in the 'web-typography' project folder.
Now, let's add it to the stylesheet along with the '@font-face' rule. Web fonts
should always be placed at the beginning of the stylesheet, so add the following
code to the very beginning of '[Link]':
@font-face {
font-family: "Roboto";
src: url('[Link]') format('woff');
}
The 'font-family' property defines how we'll reference the font. It acts like an
internal label, so you can technically choose any name. However, it's usually more
intuitive to use a name related to the font, although overly specific names (e.g.,
'Roboto Light') are not advisable. As we'll see shortly, it's a good idea to keep the
name as general as possible (e.g., 'Roboto').
Next, we have the 'src' property, which specifies the path to the '.woff' file using
'url()'. The path can be absolute, relative, or root-relative. In our example above,
we've used a relative path, which refers to the '.css' file itself rather than the HTML
document. The 'format()' allows browsers to understand the font file format.
If we reload the '[Link]' page, we won't see any changes yet because
'@font-face' has only given us access to the '.woff' file. We still need to use it
somewhere in the stylesheet.
The CSS 'font-family' property determines which font an HTML element uses.
After adding our '@font-face' rule, we can use 'Roboto' as a valid value for 'font-
family' anywhere else in our stylesheet.
Let's make 'Roboto Light' the default font for our entire project by changing the
'font-family' in the 'body' rule in the '[Link]' file:
body {
font-family: "Roboto", sans-serif;
font-size: 18px;
line-height: 1.8em;
color: #5D6063;
}
Now, all text should be displayed in 'Roboto Light', which means the mapping to
the system 'sans-serif' font in '[Link]' no longer applies. We can fix this by
adding the following style to the <head> of the '[Link]' file:
<style>
.system-fonts {
font-family: sans-serif;
}
</style>
The '.system-fonts' class is applied to the second box in '[Link]'. The rule
above takes precedence over the 'body' rule in '[Link]', so when we open 'web-
[Link]' in a browser, we should see 'Roboto Light' at the top and the default
system font at the bottom.
Recap:
• Adding a local font to a page consists of three steps: downloading the web
font and adding it to the project, embedding it in the stylesheet, and using it
within the stylesheet.
Each font family includes multiple font styles, and each font style has its own
thickness and style. Thickness refers to how bold a particular font style is, while
style refers to whether it is upright, italic, condensed, extended, or some other
variation.
In our example, Roboto Light is one of the font styles within the Roboto font
family. Other font styles within this family can be visualized as follows:
"Font Families and Font Styles Each font family includes multiple font styles, and
each font style has its own thickness and style. Thickness refers to how bold a
particular font style is, while style refers to whether it is upright, italic, condensed,
extended, or some other variation.
In our example, Roboto Light is one of the font styles within the Roboto font
family. Other font styles within this family can be visualized as follows:
In CSS, font thickness is expressed as numerical values ranging from 100 to 900.
Fortunately, there are relatively standardized terms that correspond to these
numerical values and are easier to remember. For example, "Black" typically
corresponds to 900, "Bold" to 700, "Regular" to 400, and so on. As you can see
above, most font families do not have separate font styles for every possible
thickness. For example, Roboto does not have "Extra Light" (200), "Semi Bold"
(600), or "Extra Bold" (800).
It's important to note that each combination of style and thickness is considered a
separate font style. In high-quality font families, condensed styles are not simply
compressed versions of other font styles, and bold styles are not just thicker
versions of the regular font. Each letter in each font style is carefully crafted to
ensure consistent readability.
Cheat Mode
So why are font thickness and style important for us? The design of most websites
uses multiple font styles from the same font family, so we need to be able to embed
multiple .woff files representing related font styles.
But first, let's see what happens when we don't use multiple font styles
simultaneously. Let's update the first paragraph in [Link] to include <em>
and <strong> elements:
<section class="section section--gray">
<h2>Web Fonts</h2>
When we reload the page, we'll see that the bold text isn't as bold as we'd like it to
be. This happens because of font synthesis. We haven't provided a bold font for the
<strong> element, so the browser automatically makes the Roboto Light font style
thicker. The same thing happens with italics in the <em> element, although it's less
apparent. Such auto-conversion almost always leads to poor typography.
To confirm that bold and italic font styles are indeed created through conversion,
let's try adding the following rule to [Link]. The font-synthesis property
determines whether the browser is allowed to synthesize a font or not. As of the
time of writing, font-synthesis only works in Firefox (not in Chrome or Safari):
/* This will work only in Firefox */
em, strong {
font-synthesis: none;
}
Open [Link] in Firefox: now the <em> and <strong> elements will no
longer be displayed in italics or bold; the entire paragraph will be rendered in
Roboto Light.
@font-face {
font-family: "Roboto Bold";
src: url('[Link]') format('woff');
}
Then we'll need rules to use these font styles for our <em> and <strong> elements:
/* This is very inconvenient */
em {
font-family: "Roboto Light Italic", serif;
}
strong {
font-family: "Roboto Bold", serif;
}
Now we should see the correct italic and bold fonts when we reload web-
[Link] in the browser. The problem is that manually specifying font-family
every time we want to use italic or bold fonts is, to say the least, not very
convenient. To address this, we should use the CSS properties font-style and font-
weight.
To maintain the relationship between all three font styles, we need to use a
common "Roboto" value for their font-family property. To distinguish our regular,
italic, and bold font styles, we'll add font-style and font-weight properties to the
@font-face rule. Replace all the @font-face rules in [Link]:
@font-face {
font-family: "Roboto";
src: url('[Link]') format('woff');
font-style: normal;
font-weight: 300;
}
@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;
font-weight: 700;
}
Each @font-face rule can be thought of as a description of a .woff file. The first
@font-face rule states that it's a Roboto font, which is a normal font style with a
weight of 300 ("light"). The second @font-face is also part of the Roboto family
and has a weight of 300, but it's italic. Finally, the third @font-face rule lets our
browser know that [Link] contains a bold font style.
Informing the browser that the font styles are related makes our CSS code much
more intuitive. We can set the font family and weight in our body selector. Then, if
we want to use italic or bold fonts for a specific element, we can simply specify
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;
}
strong {
font-weight: bold; /* Или 700 */
}
To Recap:
Phew, that was quite challenging! Now, let's explore an easier way to use web
fonts—through Google Fonts. We can skip the first two steps from the section on
local fonts right away. Instead of adding .woff files to our project and embedding
them manually with @font-face, we can let Google Fonts do that for us.
In this section, we'll work with [Link], so let's open this file
simultaneously in a text editor and a web browser. Let's start by changing the font
for the Gothic/Blackletter section. Let's find UnifrakturMaguntia on Google Fonts.
This font should resemble something from the Middle Ages. Click on 'Select this
style.'
In the popup, you'll see an <link/> element. Copy it into the <head> above
the <link/> element that includes our [Link] stylesheet:
<link href="[Link]
rel="stylesheet">
Now that we've added the UnifrakturMaguntia web font, we can use it to style any
HTML element. Let's update the <head>:
<style>
.blackletter {
font-family: "UnifrakturMaguntia", cursive;
}
</style>
This first section has a class attribute 'blackletter,' so now it should be printed in
gothic letters.
By the way, you can generate this in Google Fonts by selecting multiple fonts
before copying the <link/> element. Then, let's add all these new fonts to
the <style> element:
.old-style {
font-family: "Sorts Mill Goudy", serif;
}
.transitional {
font-family: "Libre Baskerville", serif;
}
.didot {
font-family: "Rufina", serif;
}
.slab {
font-family: "Rokkitt", serif;
}
.fat-face {
font-family: 'Alfa Slab One', cursive;
}
.grotesque {
font-family: 'Roboto', sans-serif;
}
.geometric {
font-family: "Questrial', sans-serif;
}
.humanist {
font-family: "Lato", sans-serif;
}
.display {
font-family: "Lobster", cursive;
}
.monospace {
font-family: "Droid Sans Mono", monospace;
}
Never use this many web fonts on one page in real life. Each web font is a .woff or
.woff2 file that your browser needs to download before it can render the page. So,
the more fonts you have, the longer the page load time. The key to effective use of
web fonts is finding a balance between website performance and beautifully
typeset content.
That's all you need to know about web fonts. In the remaining part of this chapter,
we'll move on to basic typographic principles. These are simple guidelines that
often help transform an amateurish web page into a professional one.
Recap:
• Working with external web fonts is easier, but they are not as flexible as
local fonts.
• It's important to strike a balance between website performance and
appearance.
The text-indent property defines the size of the first-line indent for a specific
element (usually <p>). Let's explore this property using the [Link] page. We'll
change the existing styles for the bottom margin in the first section to use text-
indent, adding the following rules to the <style> element:
<style>
.paragraph-indent p {
text-indent: 1em;
margin-bottom: 0;
}
.paragraph-indent p:first-of-type {
text-indent: 0;
}
</style>
The first paragraph after the heading should have no indent because it's already
clear that it's a new paragraph. This is a good example of using the :first-of-type
pseudo-class.
Now, let's look at an example of what not to do. Add this to the specific page styles
in [Link]:
/* Designers won't forgive us for this */
.never-both p {
text-indent: 1em;
margin-bottom: 1em;
}
Text Alignment
Text alignment has a subconscious impact on how we read text. Our gaze doesn't
smoothly move across when we read a paragraph - it jumps from word to word and
from line to line, fixating on certain places and skipping others.
The following sections explain when to use left-align, center-align, right-align, and
justify text alignments. All these examples are based on the text-align property,
which controls the text alignment of a specific HTML element.
For readability, the majority of the text should be left-aligned. Long stretches of
text should almost always be left-aligned. There's a bit more flexibility with shorter
text snippets and headings.
By default, text-align is set to left-align, but you can add the following rule to
the <style> element in [Link]:
<style>
.left {
text-align: left;
}
</style>
Center-aligned text can make it easier to lose your place when moving to the next
line. Center alignment is best suited for short lines and special types of content like
poetry, song lyrics, and headings.
Let's center-align the second paragraph in [Link] using a different style:
.center {
text-align: center;
}
Now the page looks a bit disjointed. The second paragraph, which is centered,
breaks the flow of the first paragraph, which is left-aligned. So, text alignment
should be consistent throughout the web page. If we center-align a heading, we
should center-align all headings.
Add the <figure>, <img>, and <figcaption> elements to the "Right Align Text"
section after the paragraph to display the image:
<section class="section section--gray">
<h2>Right Align Text</h2>
<p>
Just like with centering...
</p>
<figure>
<img src="./[Link]" alt="Ligature">
<figcaption>Image description</figcaption>
</figure>
</section>
Our image is located inside <figure>, and the caption text is within <figcaption>,
so after adding the <style> element to [Link], we should achieve the
layout we need.
figcaption {
display: none;
}
figcaption {
display: block;
font-style: italic;
text-align: right;
background-color: #FFFFFF;
position: absolute;
left: -220px;
width: 200px;
}
}
Like centering text, right-aligning should only be applied in special cases because
its uneven left edge makes it difficult for readers to find the next line.
Lines of equal width are created by fine-tuning the spacing between words and
letters. Without a quality automatic hyphenation system, the spaces between words
become too large, and the text becomes harder to read.
Unfortunately, most browsers don't have any built-in hyphenation system at all, so
it's better to avoid lines of equal width altogether. Let's confirm this in practice by
adding another text-align rule to our [Link] file:
.justify {
text-align: justify;
}
Let's compare this to a left-aligned paragraph: the difference isn't gigantic, but a
left-aligned paragraph looks more uniform and is more pleasant to read.
To Summarize:
• Paragraphs are separated from each other either by the first-line indent or by
spacing between paragraphs.
• The majority of text should be left-aligned.
• It's better to avoid lines of equal width.
Just like alignment, line spacing is not an arbitrary decision. In this section, we will
discuss the thoughtful use of three CSS properties:
The first two should already be quite familiar to us: they determine the vertical
space between individual paragraphs. The line-height property, on the other hand,
determines the amount of space between lines within a single paragraph. In
traditional typography, line-height is called "leading."
Together, these properties control the "vertical rhythm" of a web page. There are
various methods for determining the optimal vertical rhythm for a layout, but the
general principles are as follows:
To demonstrate these principles in practice, let's disrupt the vertical rhythm in the
second half of our [Link] page. Let's add the following styles:
<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 {
margin-top: 0;
}
</style>
Small changes in line height, spacing, and margins can have a significant impact
on the entire page.
Line Length
Horizontal spacing should not be arbitrary either. Line length can be considered as
the number of characters or words a line can contain. The following CSS
properties are used to work with line length:
• width
• margin-left (or padding-left)
• margin-right (or padding-right)
A good empirical rule is to limit the number of characters in a line to around 80.
This affects the readability of our content. It takes some effort for our eyes to move
from the left edge of a paragraph to the right, and the further we do this, the more
quickly we become fatigued. Long lines can also cause our gaze to wander when
we finish one line and need to return to the beginning of the next.
That's why many websites use fixed-width layouts or split content into multiple
columns for wider screens. Without limiting the page width or splitting into
columns, the line length becomes unacceptably long.
Let's see what happens when we add the following to the <head> of line-
[Link]:
<style>
@media only screen and (min-width: 580px) {
.not-so-manageable {
max-width: 100%;
margin-left: 2em;
margin-right: 2em;
}
}
</style>
Now, the second section stretches across the entire width of the browser window.
The page becomes harder to read due to the increased line length. Again, the goal
of good web typography is to make it as easy as possible for users to view our
content.
All of this should be enough to get you started on the path to quality web
typography. Typography is a vast industry, and we have only scratched the surface.
Further study will take us into the realm of design, so we'll just conclude with a
few final recommendations:
• For the body element, it's best to use a font-size between 14px and 20px.
• Use HTML entities ’, ‘, ”, “ for quotes and apostrophes.
• Use correct dashes (–, —) and other symbols (©).
• Avoid using text-decoration: underline except in hover states.
• Use real italic fonts instead of converted ones if it won't be too performance-
heavy.
Practical Typography has a handy list of common rules to follow when typesetting
a document.
To Summarize:
1. To learn the mechanics of working with web fonts and the fundamental
properties of typography.
2. To understand how designers think about typography.
Perhaps after reading this chapter, we won't be able to create a perfectly designed
web page right away. But that's not so important because we've learned many
important things about the invisible art of typography!
The most important lesson of this chapter is that there is nothing random on a well-
designed web page. Font size, spacing, text alignment, line height, margins, and all
other aspects of the page must be carefully considered. All the CSS properties we
covered in this lesson are quite simple. Essentially, we were just moving boxes,
changing colors, and editing the appearance of our text.
If you have any more questions or need further assistance, please feel free to ask.
a:hover {
color: hsl(36, 100%, 50%);
}
This creates an animation where the link's color changes from a muted shade of
orange to a bright one when we hover the cursor over it.
The transition property has many features that allow us to create smooth
animations rather than abruptly switching from one state to another. It is a
shorthand property that includes the following sub-properties:
For example, in this case, the link's color will change gradually:
a:link {
transition: all 0.5s linear 0;
color: hsl(36, 50%, 50%);
}
a:hover {
color: hsl(36, 100%, 50%);
}
The transition property tells us that all properties should change linearly without
a delay over half a second.
To Summarize:
In the transition values, you can also specify the names of specific properties to
apply transitions exclusively to those properties. If you include transition: color
0.5s ease 0 in the last example, only the color will change.
If you need to involve multiple properties, you can list them by separating them
with commas:
a:link {
transition: 0.5s;
transition-property: color, font-size;
...
}
Or like this:
a:link {
transition: color 0.5s, font-size 2s;
...
}
To Summarize:
• To apply transitions to specific properties, you can list them using commas
in the transition property.
• The transition-timing-function property allows you to control the speed
of transitions.
In this section, we will discuss only transform and transform-origin, which you
may need to replace with -webkit-transform and -webkit-transform-origin for
Safari and Chrome.
The transform property can be used to rotate, skew, scale, or translate a box and its
contents.
Rotation After adding transform: rotate(-10deg);, the box and its contents will
rotate 10 degrees counterclockwise.
.note {
width: 300px;
height: 300px;
background: hsl(36, 100%, 50%);
transform: rotate(-10deg);
}
Skew Using the skew attribute, you can skew horizontally and vertically. For
example, here we skewed the x-axis by 20 degrees and the y-axis by 10 degrees.
transform: skew(20deg, 10deg);
Scaling While you can change the width and height properties, the size of the box's
content remains unchanged. With the scale attribute, you can change both the
height and width of the box and its content.
transform: scale(2);
This way, you double the size of the box and its content. To reduce the scale, you
can use any positive value less than 1.
Here, we stretched the box vertically by two times while leaving the horizontal
scale unchanged.
Translation You can move a box horizontally and vertically using transform:
translate:
Similar to position: relative; left: 100px; top: 200px;, this will move our box
100 pixels to the right and 200 pixels down.
To Summarize:
What if we want to rotate and scale a box simultaneously? We can easily do this by
separating the transform attributes with spaces like this:
transform: rotate(-10deg) scale(2);
The order of the attributes is important – the ones specified last will be applied
before the ones specified earlier. In the previous example, the box will first be
scaled and then rotated. This is different from the rule transform: scale(2)
rotate(-10deg);, where the box would be rotated first and then scaled.
As an alternative to transform, you can use the matrix attribute, which handles
rotation, skew, scaling, and translation all at once and includes 6 values:
transform: matrix(2, -0.35, 0.35, 2, 0, 0);
Learning to work with this attribute might take some time due to its mathematical
aspects, but matrix has significant advantages in terms of conciseness and
precision.
This way, the box will rotate from the top-left corner, where the first value "0"
corresponds to the horizontal axis, and the second to the vertical axis. Of course,
these values can be different – you can
use center, top, right, bottom, left, length, and percentage as values for this
property.
You can also use the transform property in three dimensions. The simplest things
you can do include rotateX and rotateY, which will rotate the box closer or farther
along the x and y axes. Additionally, there are functions like translate3d, scale3d,
and the rather complex matrix3d.
To Summarize: