We browse the Internet in search of interesting and informative content, which we usually find in the form of plain text. To accompany this plain text, HTML provides ways to embed rich media in the form of images, audio tracks, and videos, as well as to embed content from another web page in the form of an inline frame.
The ability to include images, audio tracks, videos, and inline frames within websites has been around for some time. Browser support for images and inline frames has generally been pretty good. And while the ability to add audio tracks and videos to a website has been around for years, the process has been fairly cumbersome. Fortunately, this process has improved and is much easier with support directly from HTML.
Today, we can freely use images, audio, video, and inline frames knowing that this content is supported across all major browsers.
To add images to a page, we use the <img>
inline element. The <img>
element is a self-containing, or empty, element, which means that it doesn’t wrap any other content and it exists as a single tag. For the <img>
element to work, a src
attribute and value must be included to specify the source of the image (see Figure 9.1). The src
attribute value is a URL, typically relative to the server where a website is hosted.
In conjunction with the src
attribute, the alt
(alternative text) attribute, which describes the contents of an image, should be applied. The alt
attribute value is picked up by search engines and assistive technologies to help convey the purpose of an image. The alt
text will be displayed in place of the image if for some reason the image is not available (see Figure 9.2).
1. <img src="dog.jpg" alt="A black, brown, and white dog wearing a kerchief">
It is important to identify the size of an image in order to tell the browser how large the image should be before the page even loads; thus the browser can reserve space for the image and render the page faster. There are a few different ways to size images so that they work well on a page. One option is to use the width
and height
attributes directly within the <img>
tag in HTML.
Additionally, images may be sized using the width
and height
properties in CSS. When both the HTML attributes and CSS properties are used, the CSS attributes will take precedence over the HTML attributes.
Specifying either a width or height will cause the other dimension to adjust automatically to maintain the aspect ratio of the image. As an example, if we want an image to be 200 pixels tall but are less specifically concerned about how wide it is, we can set the height
to 200
pixels, and the width of the image will adjust accordingly. Setting both a width
and height
will work also; however, doing so may break the aspect ratio of an image, causing it to appear distorted (see Figure 9.3).
1. img {
2. height: 200px;
3. width: 200px;
4. }
While using the width
and height
attributes directly in HTML provides some semantic value by noting an image’s original size, it can be difficult to manage numerous images that all need to be the same size. In this event, it’s common practice to use CSS to resize the images.
We can use a number of different approaches to position images on a web page. By default images are positioned as inline-level elements; however, their positions may be changed using CSS, specifically the float
, display
, and box model properties, including padding
, border
, and margin
.
The <img>
element is by default an inline-level element. Adding an image without any styles to a page will position that image within the same line as the content that surrounds it (see Figure 9.4). Additionally, the height of the line in which an image appears will be changed to match the height of the image, which can create large vertical gaps within that line.
1. <p>Gatsby is a black, brown, and white hound mix puppy who loves
howling at fire trucks and collecting belly rubs. <img src=
"dog.jpg" alt="A black, brown, and white dog wearing a kerchief">
Although he spends most of his time sleeping he is also quick to
chase any birds who enter his vision.</p>
Leaving images untouched in their default positioning isn’t too common. More often than not, images are displayed as block-level elements or are floated flush to one side.
Adding the display
property to an image and setting its value to block
forces the image to be a block-level element (see Figure 9.5). This makes the image appear on its own line, allowing the surrounding content to be positioned above and below the image.
1. img {
2. display: block;
3. }
Sometimes displaying an image as inline
or block
, or perhaps even inline-block
, isn’t ideal. We may want the image to appear on the left or right side of its containing element, while all of the other content wraps around the image as necessary. To do this, we use the float
property with a value of either left
or right
.
Remembering back to Lesson 5, “Positioning Content,” we recall that the float
property was originally intended to position images to the left or right of a containing element. Now we’ll use it for that original purpose.
Floating an image is a start; however, all other content will align directly against it. To provide spacing around an image, we’ll use the margin
property (see Figure 9.6). Additionally, we can use the padding
, border
, and background
properties to build a frame for the image, if desired.
1. img {
2. background: #e8eae9;
3. border: 1px solid #c6c9cc;
4. float: right;
5. margin: 8px 0 0 20px;
6. padding: 4px;
7. }
The <img>
element is quite popular, and when it was originally added to the HTML specification it forever changed the way websites were built.
Now that we know how to add and position images on a page, let’s take a look at our Styles Conference website and see where we can add a few images.
1. Let’s begin by adding some images to our home page. Specifically, we’ll add an image within each of the teaser sections promoting a few of our pages.
Before we jump into the code, though, let’s create a new folder named “images” within our “assets” folder. Then, within the “images” folder, let’s create another folder named “home” specifically for our home page images. Within the “home” folder we’ll add three images: speakers.jpg
, schedule.jpg
, and venue.jpg
. (For reference, these images may be found on http://learn.shayhowe.com/html-css/adding-media.)
Then, inside our index.html
file, each teaser section has an <a>
element wrapping both an <h3>
and an <h5>
element. Let’s move the <h5>
element above the <a>
element and replace it with an <img>
element. The src
attribute value for each <img>
element will correspond to the folder structure and filename we set up, and the alt
attribute value will describe the contents of each image.
The HTML for our first teaser, for the Speakers page, will look like this:
1. <section class="teaser col-1-3">
2. <h5>Speakers</h5>
3. <a href="speakers.html">
4. <img src="assets/images/home/speakers.jpg" alt="Professional
Speaker">
5. <h3>World-Class Speakers</h3>
6. </a>
7. <p>Joining us from all around the world are over twenty fantastic
speakers, here to share their stories.</p>
8. </section>
Let’s continue this pattern for both the Schedule and Venue page teasers, too.
2. Now that we’ve added a few images to our home page, we’ll need to clean up their styles a bit and make sure they properly fit into the layout of our page.
Since images are inline-level elements by default, let’s change our images within the teaser sections to block-level elements. Let’s also set their maximum width
to 100%
to ensure they don’t exceed the width of their respective columns. Changing this width
value is important as it allows our images to adjust with the width of the columns as necessary.
Lastly, let’s round the corners of the images slightly and apply 22
pixels of bottom margin
to the images, providing a little breathing room.
Once we add these new styles to our existing home page styles (using the teaser
class as a qualifying selector for the <img>
elements), our CSS will look like this:
1. .teaser img {
2. border-radius: 5px;
3. display: block;
4. margin-bottom: 22px;
5. max-width: 100%
6. }
3. Next up, let’s add images of all of the speakers to the Speakers page. We’ll begin by creating a “speakers” folder within our “images” folder and placing images of all of the speakers there.
Within the speakers.html
file, let’s add an <img>
element within each of the speaker information <aside>
elements. Let’s place each <img>
element inside the <div>
element with the class
attribute value of speaker-info
, just above the <ul>
element.
The src
attribute value of each image will correspond to the “speakers” folder we set up and the speaker’s name; the alt
attribute value will be the speaker’s name.
The <aside>
element for myself, as a speaker, will look like this:
1. <aside class="col-1-3">
2. <div class="speaker-info">
3.
4. <img src="assets/images/speakers/shay-howe.jpg"
alt="Shay Howe">
5.
6. <ul>
7. <li><a href="https://twitter.com/shayhowe">@shayhowe</a></li>
8. <li><a href="http://learn.shayhowe.com/">
learn.shayhowe.com</a></li>
9. </ul>
10.
11. </div>
12. </aside>
This same pattern for adding an image should then be applied to all other speakers.
4. As we did with the images on our home page, we’ll want to apply some styles to the images on the Speakers page.
Let’s begin by applying the border-radius
property with a value of 50%
, turning our images into circles. From there, let’s set a fixed height
of 130
pixels to each image and set them to be vertically aligned to the top
of the line they reside within.
With the height
and vertical alignment in place, let’s apply vertical margins to the images. Using a negative 66
-pixel margin
on the top of the images, we’ll pull them slightly out of the <aside>
element and make them vertically centered with the top border
of the <div>
element with a class
attribute value of speaker-info
. Then, applying a 22
-pixel margin
on the bottom of the image provides space between the image and the <ul>
element below it.
When we add these new styles to our existing Speakers page styles (using the speaker-info
class as a qualifying selector for the <img>
elements), our CSS will look like this:
1. .speaker-info img {
2. border-radius: 50%;
3. height: 130px;
4. margin: -66px 0 22px 0;
5. vertical-align: top;
6. }
5. Since we are using an aggressive negative margin
on the <img>
element within the <div>
element with a class
attribute value of speaker-info
, we need to remove the padding
on the top of that <div>
element.
Previously we were using the padding
property with a value of 22px 0
, thus placing 22
pixels of padding
on the top and bottom and 0
pixels of padding
on the left and right of the <div>
element. Let’s swap this property and value out for the padding-bottom
property, as that’s the only padding
we need to identify, and use a value of 22
pixels.
The new speaker-info
class rule set looks like this:
1. .speaker-info {
2. border: 1px solid #dfe2e5;
3. border-radius: 5px;
4. margin-top: 88px;
5. padding-bottom: 22px;
6. text-align: center;
7. }
Now both our home and Speaker pages are looking pretty sharp, as shown in Figures 9.7 and 9.8, respectively.
HTML5 provides a quick and easy way to add audio files to a website by way of the <audio>
element. As with the <img>
element, the <audio>
element accepts a source URL specified by the src
attribute. Unlike the <img>
element, though, the <audio>
element requires both opening and closing tags, which we’ll discuss soon.
1. <audio src="jazz.ogg"></audio>
Several other attributes may accompany the src
attribute on the <audio>
element; the most popular include autoplay
, controls
, loop
, and preload
.
The autoplay
, controls
, and loop
attributes are all Boolean attributes. As Boolean attributes, they don’t require a stated value. Instead, when each is present on the <audio>
element its value will be set to true
, and the <audio>
element will behave accordingly.
By default, the <audio>
element isn’t displayed on a page. If the autoplay
Boolean attribute is present on the <audio>
element, nothing will appear on the page, but the audio file will automatically play upon loading.
1. <audio src="jazz.ogg" autoplay></audio>
To display the <audio>
element on a page, the controls
Boolean attribute is necessary. When it’s applied to the <audio>
element, the controls
Boolean attribute will display a browser’s default audio controls, including play and pause, seek, and volume controls (see Figure 9.9).
1. <audio src="jazz.ogg" controls></audio>
When present on the <audio>
element, the loop
Boolean attribute will cause an audio file to repeat continually, from beginning to end.
Lastly, the preload
attribute for the <audio>
element helps identify what, if any, information about the audio file should be loaded before the clip is played. It accepts three values: none
, auto
, and metadata
. The none
value won’t preload any information about an audio file, while the auto
value will preload all information about an audio file. The metadata
value sits in between the none
and auto
values, as it will preload any available metadata information about an audio file, such as the clip’s length, but not all information.
When the preload
attribute isn’t present on the <audio>
element, all information about an audio file is loaded, as if the value was set to auto
. For this reason, using the preload
attribute with a value of metadata
or none
is a good idea when an audio file is not essential to a page. It’ll help to conserve bandwidth and allow pages to load faster.
At the moment, different browsers support different audio file formats, the three most popular of which are ogg
, mp3
, and wav
. For the best browser support we’ll need to use a handful of audio fallbacks, which will be included inside an <audio>
element’s opening and closing tags.
To begin, we’ll remove the src
attribute from the <audio>
element. Instead, we’ll use the <source>
element, with a src
attribute, nested inside the <audio>
element to define a new source.
Using a <source>
element and src
attribute for each file format, we can list one audio file format after the other. We’ll use the type
attribute to quickly help the browser identify which audio types are available. When a browser recognizes an audio file format it will load that file and ignore all the others.
Because it was introduced in HTML5, some browsers may not support the <audio>
element. In this case, we can provide a link to download the audio file after any <source>
elements within the <audio>
element (see Figure 9.10).
1. <audio controls>
2. <source src="jazz.ogg" type="audio/ogg">
3. <source src="jazz.mp3" type="audio/mpeg">
4. <source src="jazz.wav" type="audio/wav">
5. Please <a href="jazz.mp3" download>download</a> the audio file.
6. </audio>
To review the previous code, the <audio>
element includes the controls
Boolean attribute to ensure the audio player is displayed within browsers that support the element. The <audio>
element does not include a src
attribute and instead wraps three different <source>
elements. Each <source>
element includes a src
attribute that references a different audio file format and a type
attribute to identify the format of the audio file. As a last fallback, if a browser doesn’t recognize any of the audio file formats, the anchor link to download the element will be displayed.
In addition to the <audio>
element, HTML5 also introduced the <video>
element, which shares quite a few similarities with the <audio>
element.
Adding video in HTML5 is very similar to adding audio. We use the <video>
element in place of the <audio>
element. All of the same attributes (src
, autoplay
, controls
, loop
, and preload
) and fallbacks apply here, too.
With the <audio>
element, if the controls
Boolean attribute isn’t specified the audio clip isn’t displayed. With videos, if the controls
Boolean attribute is not specified the video will display. However, it is fairly difficult to view unless the autoplay
Boolean attribute is also applied. In general, the best practice here is to include the controls
Boolean attribute unless there is a good reason not to allow users to start, stop, or replay the video.
Since videos take up space on the page, it doesn’t hurt to specify their dimensions, which is most commonly done with width
and height
properties in CSS. This helps ensure that the video isn’t too large and stays within the implied layout of a page. Additionally, specifying a size, as with images, helps the browser render videos faster and allows it to allocate the proper space needed for the video to be displayed.
1. <video src="earth.ogv" controls></video>
One additional attribute available for the <video>
element is the poster
attribute. The poster
attribute allows us to specify an image, in the form of a URL, to be shown before a video is played. The example below uses a screen capture from the video as the poster for the Earth video.
1. <video src="earth.ogv" controls poster="earth-video-screenshot.jpg"></video>
As with the <audio>
element, video fallbacks are also necessary. The same markup format, with multiple <source>
elements for each file type and a plain text fallback, also applies within the <video>
element.
1. <video controls>
2. <source src="earth.ogv" type="video/ogg">
3. <source src="earth.mp4" type="video/mp4">
4. Please <a href="earth.mp4" download>download</a> the video.
5. </video>
One additional fallback option that could be used in place of a plain text fallback is to use a YouTube or Vimeo embedded video. These video hosting websites allow us to upload our videos, provide a standard video player, and enable us to embed our videos onto a page using an inline frame.
Another way to add content to a page is to embed another HTML page within the current page. This is done using an inline frame, or <iframe>
element. The <iframe>
element accepts the URL of another HTML page within the src
attribute value; this causes the content from the embedded HTML page to be displayed on the current page. The value of the src
attribute may be a URL relative to the page the <iframe>
element appears on or an absolute URL for an entirely external page.
Many pages use the <iframe>
element to embed media onto a page from an external website such as Google Maps, YouTube, and others.
1. <iframe src="https://www.google.com/maps/embed?..."></iframe>
The <iframe>
element has a few default styles, including an inset border
and a width
and height
. These styles can be adjusted using the frameborder
, width
, and height
HTML attributes or by using the border
, width
, and height
CSS properties.
Pages referenced within the src
attribute of an <iframe>
element play by their own rules, as they do not inherit any styles or behaviors from the page they are referenced on. Any styles applied to a page that includes an <iframe>
element will not be inherited by the page referenced within the <iframe>
element. Additionally, links within the page referenced within the <iframe>
element will open inside that frame, leaving the page that contains the <iframe>
element unchanged.
There will be times when we’ll want to change these behaviors, and the seamless
Boolean attribute will allow us to do just that. When present on the <iframe>
element, the seamless
Boolean attribute allows styles from the page that includes an <iframe>
element to be inherited by the page referenced within the <iframe>
element. Additionally, the seamless
Boolean attribute allows links clicked on a page referenced within an <iframe>
element to be opened within the same window as the original page that includes the <iframe>
element.
1. <iframe src="contact.html" seamless></iframe>
The seamless
Boolean attribute is a new attribute introduced in HTML5. Although the browser support for this attribute is growing, it will not work within older browsers. It’s advisable to test the seamless
Boolean attribute before using it.
Inline frames provide a great way to add dynamic content to a page. Let’s give this a shot by updating our Venue page with some maps.
1. Before adding any maps or inline frames, let’s first prepare our Venue page for a two-column grid. Below the leading section of the page we’ll add a <section>
element with the class attribute value of row
to identify a new section of the page, and we’ll include some general styles, such as a white background
and some vertical padding
.
Directly inside this <section>
element let’s add a <div>
element with the class
attribute value of grid
. The class of grid
centers our content on the page and prepares for the one-third and two-thirds columns to follow.
So far the main section of our venue.html
file looks like this:
1. <section class="row">
2. <div class="grid">
3. ...
4. </div>
5. </section>
2. Within the <div>
element with the class
attribute value of grid
we’ll have two new sections, one for the conference venue and one for the conference hotel. Let’s add two new <section>
elements and give each of these <section>
elements a unique class that corresponds to its content. We’ll use these classes to add margins to the bottom of each section.
Our HTML should now look like this:
1. <section class="row">
2. <div class="grid">
3.
4. <section class="venue-theatre">
5. ...
6. </section>
7.
8. <section class="venue-hotel">
9. ...
10. </section>
11.
12. </div>
13. </section>
3. Now that we have a few classes to work with, let’s create a new section within our main.css
file for Venue page styles. We’ll add a 66
-pixel margin
to the bottom of the <section>
element with the class
attribute value of venue-theatre
to insert some space between it and the <section>
element below it.
Then, we’ll add a 22
-pixel margin
to the bottom of the <section>
element with the class
attribute value of venue-hotel
to provide some space between it and the <footer>
element below it.
The new venue section within the main.css
file looks like the following:
1. /*
2. ========================================
3. Venue
4. ========================================
5. */
6.
7. .venue-theatre {
8. margin-bottom: 66px;
9. }
10. .venue-hotel {
11. margin-bottom: 22px;
12. }
The <section>
element with the class
attribute value of venue-hotel
has a smaller bottom margin
than the <section>
element with the class
attribute value of venue-theatre
because it sits next to the padding
from the bottom of the <section>
element with the class
attribute of row
. Adding that margin
and padding
together gives us the same value as the bottom margin
on the <section>
element with the class
attribute value of venue-theatre
.
4. Now it’s time to create the two columns within each of the new <section>
elements. We’ll start by adding a <div>
element with a class
attribute value of col-1-3
to establish a one-third column. After it we’ll add an <iframe>
element with a class
attribute value of col-2-3
to establish a two-thirds column.
Keeping in mind that the column classes make both the <div>
and <iframe>
elements inline-block elements, we need to remove the empty space that will appear between them. To do so we’ll open an HTML comment directly after the closing <div>
tag, and we’ll close the HTML comment immediately before the opening <iframe>
tag.
In all, our HTML for the columns looks like this:
1. <section class="row">
2. <div class="grid">
3.
4. <section class="venue-theatre">
5.
6. <div class="col-1-3"></div><!--
7.
8. --><iframe class="col-2-3"></iframe>
9.
10. </section>
11.
12. <section class="venue-hotel">
13.
14. <div class="col-1-3"></div><!--
15.
16. --><iframe class="col-2-3"></iframe>
17.
18. </section>
19.
20. </div>
21. </section>
5. Within each of the <div>
elements with a class
attribute value of col-1-3
let’s add the venue’s name within an <h2>
element, followed by two <p>
elements. In the first <p>
element let’s include the venue’s address, and in the second <p>
element let’s include the venue’s website (within an anchor link) and phone number.
Within each of the paragraphs, let’s use the line-break element, <br>
, to place breaks within the address and in between the website and phone number.
For the <section>
element with the class
attribute value of venue-theatre
, the HTML looks like this:
1. <section class="venue-theatre">
2.
3. <div class="col-1-3">
4. <h2>Chicago Theatre</h2>
5. <p>175 N State St <br> Chicago, IL 60601</p>
6. <p><a href="http://www.thechicagotheatre.com/">
thechicagotheatre.com</a> <br> (312) 462-6300</p>
7. </div><!--
8.
9. --><iframe class="col-2-3"></iframe>
10.
11. </section>
The same pattern shown here for the theatre should also be applied to the hotel (using, of course, the proper address, website, and phone number).
6. We can search for these addresses in Google Maps (google.com/maps/). Once we locate an address and create a customized map, we have the ability to embed that map into our page. Following the instructions on Google Maps for how to share and embed a map will provide us with the HTML for an <iframe>
element.
Let’s copy the HTML—<iframe>
element, src
attribute, and all—onto our page where our existing <iframe>
element resides. We’ll do this for each location, using two different <iframe>
elements.
In copying over the <iframe>
element from Google Maps we need to make sure we preserve the class
attribute and value, col-2-3
, from our existing <iframe>
element. We also need to be careful not to harm the HTML comment that closes directly before our opening <iframe>
tag.
Looking directly at the <section>
element with the class
attribute value of venue-theatre
again, the HTML looks like this:
1. <section class="venue-theatre">
2.
3. <div class="col-1-3">
4. <h2>Chicago Theatre</h2>
5. <p>175 N State St <br> Chicago, IL 60601</p>
6. <p><a href="http://www.thechicagotheatre.com/">
thechicagotheatre.com</a> <br> (312) 462-6300</p>
7. </div><!--
8.
9. --><iframe class="col-2-3" src="https://www.google.com/maps/
embed?pb=!1m5!3m3!1m2!1s0x880e2ca55810a493%3A0x4700ddf60fcbfad6!
2schicago+theatre!5e0!3m2!1sen!2sus!4v1388701393606"></iframe>
10.
11. </section>
7. Lastly, we’ll want to make sure that both <iframe>
elements that reference Google Maps share the same height. To do this, we’ll create a new class, venue-map
, and apply it to each of the <iframe>
elements alongside the existing col-2-3
class attribute value.
The HTML for the <section>
element with the class
attribute value of venue-theatre
now looks like this:
1. <section class="venue-theatre">
2.
3. <div class="col-1-3">
4. <h2>Chicago Theatre</h2>
5. <p>175 N State St <br> Chicago, IL 60601</p>
6. <p><a href="http://www.thechicagotheatre.com/">
thechicagotheatre.com</a> <br> (312) 462-6300</p>
7. </div><!--
8.
9. --><iframe class="venue-map col-2-3" src=
"https://www.google.com/maps/embed?
pb=!1m5!3m3!1m2!1s0x880e2ca55810a493%3A0x4700ddf60fcbfad6!
2schicago+theatre!5e0!3m2!1sen!2sus!4v1388701393606"></iframe>
10.
11. </section>
Once the venue-map
class is applied to each <iframe>
element, let’s create the venue-map
class rule set within our main.css
file. It includes the height
property with a value of 264
pixels.
The venue-map
class rule set looks like this:
1. .venue-map {
2. height: 264px;
3. }
We now have a Venue page (see Figure 9.14), complete with maps for the different locations of our conference.
The source code for the exercises within this lesson can be found at http://learn.shayhowe.com/html-css/adding-media.
With HTML5 also came the introduction of the <figure>
and <figcaption>
elements. These elements were created to semantically mark up self-contained content or media, commonly with a caption. Before HTML5 this was frequently done using an ordered list. While an ordered list worked, the markup was not semantically correct.
The <figure>
block-level element is used to identify and wrap self-contained content, often in the form of media. It may surround images, audio clips, videos, blocks of code, diagrams, illustrations, or other self-contained media. More than one item of self-contained content, such as multiple images or videos, may be contained within the <figure>
element at a time. If the <figure>
element is moved from the main portion of a page to another location (for example, the bottom of the page), it should not disrupt the content or legibility of the page.
1. <figure>
2. <img src="dog.jpg" alt="A black, brown, and white dog wearing a
kerchief">
3. </figure>
To add a caption or legend to the <figure>
element, the <figcaption>
element is used. The <figcaption>
may appear at the top of, bottom of, or anywhere within the <figure>
element; however, it may only appear once. When it’s used, the <figcaption>
element will serve as the caption for all content within the <figure>
element.
Additionally, the <figcaption>
element may replace an <img>
element’s alt
attribute if the content of the <figcaption>
element provides a useful description of the visual content of the image.
1. <figure>
2. <img src="dog.jpg">
3. <figcaption>A beautiful black, brown, and white hound dog wearing
a kerchief.</figcaption>
4. </figure>
Not all forms of media need to be included within a <figure>
element or include a <figcaption>
element; only those that are self-contained and belong together as a group.
Alongside text, media is one of the largest parts of the web. Use of images, audio, and video has only grown over recent years, and it isn’t likely to slow down. Now we know how to incorporate these forms of media into our designs and how we can use them to enrich the content on our websites.
Within this lesson we covered the following:
• The best ways to add images, audio clips, videos, and inline frames to a page
• Different ways to position images in different situations
• How to provide audio and video fallbacks for older browsers
• Common attributes available to audio clips and videos
• The seamless
attribute, which allows us to make inline frames behave as if they are part of the page they are referenced from
• The semantic way to mark up self-contained content, including media
We’re coming into the homestretch of learning HTML and CSS, with only a few more components left to introduce. Next on the list are forms.
18.220.245.140