Bonus Chapter 4 - Raising Your CSS Game
Bonus Chapter 4 - Raising Your CSS Game
» Styling images
Bonus Chapter 4
Raising Your CSS Game
HTML elements enable web page designers to mark up a document’s
structure, but beyond trust and hope, you don’t have any control over your
text’s appearance. CSS changes that. CSS puts the designer in the driver’s seat.
—HÅKON WIUM LIE
I
designed the chapters in Book 3 of HTML, CSS, & JavaScript All-in-One For Dummies
to give you a top-notch education in the basics of CSS. That education that con-
tinues later in the book with the graduate programs of layout (head over to
Book 5) and animation (which you’ll find in Bonus Chapters 1 through 3 at www.
dummies.com/go/htmlcss&javascriptaiofd). For now, though, I want to send
you to a kind of finishing school for CSS, where you learn some amazing — and
amazingly powerful — techniques that you’ll turn to again and again throughout
your (hopefully long and successful) CSS coding career. With the CSS you’re about
to learn, you can o�cially declare yourself to have graduated from being a CSS
amateur to a CSS adept.
In this chapter, you unearth some true CSS gems in the form of custom properties,
handling text overflow, styling images, learning how to hide stuff, sprucing up
your bulleted and numbered lists, and querying the web browser to check whether
it supports particular CSS features.
That’s pretty sweet, but it doesn’t solve a major CSS creation and maintenance
problem: having to use a particular property value in multiple declarations
throughout a stylesheet.
Take a peek at the following CSS code (check out bk03ch08/example01.html in this
book’s example files):
main {
border: 5px outset hsl(6deg, 100%, 94%);
padding: 1rem;
text-align: center;
}
h2 {
text-shadow: 5px 5px hsl(6deg, 100%, 94%);
margin-top: 1rem;
}
button {
background-color: hsl(6deg, 100%, 94%);
box-shadow: 5px 5px hsl(6deg, 100%, 94%);
border-radius: 10px;
font-size: 1.5rem;
margin-top: 1rem;
}
Anything jump out at you? There are actually two things to notice here, one obvi-
ous and the other subtle:
»» The more obvious thing is that the color value hsl(6deg, 100%, 94%) shows
up in four different places. That’s a lot of occurrences in a code snippet that
has only ten declarations.
»» The more subtle thing is that the color value hsl(6deg, 100%, 94%) itself
doesn’t tell you anything about why this color is being used in so many places.
»» Changing the value means running a find-and-replace operation over all your
CSS code.
What drudgery! Wouldn’t it be nice if you had a way to define a value just once and
then somehow tell the browser to use that value wherever you need it?
Forget that! Wouldn’t it be nice if you had a way to somehow give certain property
values a semantic name that tells you instantly the significance of those values?
»» Provide a value: You can use any name you like (subject to certain restric-
tions, as I describe in the next section) for each custom property, so you’re
free to create semantic, descriptive names.
In other words, you can set a particular property value once using the custom
property, and then use that custom property value throughout your stylesheet.
Need to change the value? No problem: Just edit the custom property as needed
and the change automatically propagates down to every cascading variable that
uses the file. The result? Drudgery-free CSS!
Next you add your custom property to the element that you want to use as the
ancestor. Remember, the value of the custom property cascades down to every
descendant of whatever element you use. So, for example, if you’re sure that you’ll
use the custom property value only in aside elements, your rule will look like this:
aside {
--accent-color: hsl(6deg, 100%, 94%);
}
However, custom properties are at their most powerful and useful when they’re
available everywhere in your page. That means you’ll want the top of the HTML
document (that is, the html element) to be the ancestor, and in your CSS, you can
reference that element using the :root pseudo-class:
:root {
--accent-color: hsl(6deg, 100%, 94%);
}
»» fallback-value: An optional value that the browser will apply if, for some
reason, it can’t use the custom property (for example, if the custom property
name is misspelled).
:root {
--accent-color: hsl(6deg, 100%, 94%);
}
main {
border: 5px outset var(--accent-color);
padding: 1rem;
text-align: center;
}
h2 {
text-shadow: 5px 5px var(--accent-color);
margin-top: 1rem;
}
»» To change the color, I need only change the value of the custom property.
»» The code is more understandable because I can now know right away that I’m
applying an accent color to the elements.
My Container Overfloweth:
Handling Text Overflow
By default, CSS is happy to give any element whatever space it needs to contain all
its content. If you don’t set a width or height on the element, the element expands
as needed to accommodate the content: first the element’s width expands until it
reaches the width of its parent element; then the element’s height expands until
there’s enough vertical room to fit everything.
What if you set a width on the element? That’s not a usually problem because CSS
respects your width value and wraps the text at the end of each line. To make
that wrapping happen, CSS looks for so-called soft-wrap opportunities, such as a
space or a hyphen (-). Occasionally, CSS comes across a line that presents no
word-break opportunities. For example, check out the following code (bk03ch08/
example03.html):
HTML:
aside {
border: 1px solid hsl(0, 0%, 30%);
width: 175px;
}
The aside element has a set width, but the text includes a word that’s longer than
that width, so that line spills out of the aside container, as shown in Figure BC4-1.
Long words are rarely the culprit when it comes to lines spilling out the sides of a
container. A more likely suspect — and the bane of web designers everywhere —
is a long URL that doesn’t include any hyphens.
FIGURE BC4-1:
If CSS can’t wrap
a line, it extends
the line outside
its container.
What if you also set a height on an element? Now CSS has nowhere to expand the
element, so if the content is more than can fit inside the constrained element, the
aside {
border: 1px solid hsl(0, 0%, 30%);
height: 175px;
width: 175px;
}
Handling overflow
When text runs outside its parent container, that’s called overflow, and it can cre-
ate all kinds of problems because the overflow usually flows over something else
on the page! Why doesn’t CSS just restrict content to its parent? Because doing so
would mean truncating the content either vertically or horizontally (or both), and
truncating content is a no-no in CSS.
The best solution here is to let the content flow naturally, first by setting the
width large enough to avoid having long words spill out horizontally, and second
by not setting a height on the element.
• scroll: Adds a scroll bar to the element. If you’re working with overflow-x,
a horizontal scroll bar appears if you have content that would otherwise
overflow horizontally; if you’re working with overflow-y, a vertical
scroll bar appears if you have content that would otherwise overflow
vertically.
• auto: Lets the browser figure out when to show scroll bars.
• visible: Lets the content overflow out of its container (this is the default
behavior).
You can alternatively use the overflow property to set both horizontal and vertical
overflow keywords simultaneously:
If you provide a single keyword to the overflow property, the browser assigns
that value to both the horizontal and vertical behaviors.
In most situations, it’s best just to use the auto keyword and let the browser
handle things for you (bk03ch08/example05.html):
aside {
border: 1px solid hsl(0, 0%, 30%);
height: 175px;
overflow: auto;
width: 175px;
}
Figure BC4-3 shows the example elements now with horizontal and vertical
scroll bars.
FIGURE BC4-3:
With o verflow:
auto, the
browser figures
out when scroll
bars are required.
FIGURE BC4-4:
An element with
a couple of lines
that overflow
horizontally.
Rather than use overflow-x to introduce a horizontal scroll bar or (worse) to clip
the too-long text, you can tell CSS to go ahead and break words anywhere that’s
necessary to avoid horizontal overflow. You do that by adding the declaration
overflow-wrap: break-word to your container:
aside {
border: 1px solid hsl(0, 0%, 30%);
As Figure BC4-5 shows, the browser now wraps the text as needed to prevent
overflow, although without adding any hyphens to indicate where the wrapping
has occurred. Feel free to add the declaration hyphens: auto to fix this.
FIGURE BC4-5:
The long lines
now wrap
nicely inside the
container.
text-overflow: keyword;
• ellipsis: Truncates the text within the element’s content box, but also
adds an ellipsis (. . .) to the end of the visible text.
overflow: hidden;
white-space: nowrap;
The white-space property determines how CSS handles whitespace. The nowrap
value tells CSS to never wrap the text. Other possible values are pre (preserves all
sequences of whitespace characters and breaks lines at any newline characters
in the text); pre-wrap (same as pre, except lines wrap when they hit the edge of
the containing block); and pre-line (same as pre-wrap, except that sequences of
whitespace characters are not preserved).
HTML:
CSS:
span {
border: 1px solid hsl(0, 0%, 30%);
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 15rem;
}
Here you have an editable span element prompting the user for their name. You
don’t want text in this element to overflow (overflow: hidden) or wrap (white-
space: nowrap), and you want any truncated overflow to be represented by an
ellipsis (text-overflow: ellipsis). Figure BC4-6 shows how the span looks
when the user enters a name longer than the span width.
FIGURE BC4-6:
Using an ellipsis
(. . .) to represent
truncated text.
»» Images are inline elements in the sense that they’re rendered along with the
text flow.
»» Images are block elements in the sense that you can set the width and
height properties on them.
I think you’ll find that you’ll only rarely want an image to flow along with your
text. Therefore, it’s always a good idea to add the following rule somewhere near
the top of your CSS:
img {
display: block;
}
The display: block declaration turns all your images into block elements, which
makes them easier to work with.
img {
display: block;
max-width: 100%;
height: auto;
}
Setting max-width: 100% means that your images never break out of their parent
containers; and setting height: auto ensures that your images maintain their
original aspect ratio if you change the width.
object-fit: keyword;
• cover: Preserves the image aspect ratio while scaling the image until it
covers the full width and height of the element’s content box. If the image
is larger than the element, the browser crops the image to fit.
• fill: Scales the image until it’s the same dimensions as the element’s
content box. If the image has a different aspect ratio than the element, the
image is stretched as needed to fit.
FIGURE BC4-7:
The object-
fit property
in action.
object-position: x y;
filter: effect(s);
Figure BC4-9 shows the different filter property values applied to an image
(bk03ch08/example10.html). To apply multiple effects to an image, include the
filter functions you want to use, separating each with a space.
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
Keep an eye on the following Can I Use page to learn when this prefix is no longer
required:
https://caniuse.com/css-backdrop-filter
»» You may want an element to start off hidden but then gradually become
visible via animation (refer to Bonus Chapters 1 through 3).
display: none;
This declaration removes the element — and its descendants, if it has any —
entirely from the page (technically, it removes the element from the Document
Object Model; refer to Book 4, Chapter 6). From the browser’s perspective, it’s as
if the element doesn’t exist at all! Here’s an example (bk03ch08/example12.html):
HTML:
<div>
First
</div>
<div>
Second
</div>
<div class="hide-me">
Third
</div>
CSS:
.hide-me {
display: none;
}
This example creates four div elements, one of which has the hide-me class,
which declares display: none on the element. Figure BC4-10 shows that the
third div doesn’t appear on the rendered page.
FIGURE BC4-10:
The third
div doesn’t
appear on the
rendered page.
To return a hidden element to the page, you set display to the element’s intrinsic
type:
visibility: hidden;
The browser renders the element’s box but makes everything inside the box invis-
ible. Figure BC4-11 shows a page that uses the identical code from the previous
section, except now the hide-me class sets visibility: hidden on the third div
(bk03ch08/example13.html). The third div is invisible, but the space for its box
remains in the page layout.
visibility: visible;
opacity: 0;
This declaration is the same as setting visibility: hidden on the element. The
difference here is that although visibility is a toggle (that is, an element’s
visibility property is either hidden or visible), opacity can take any value
between 0.0 or 0% (completely transparent) and 1.0 or 100% (completely opaque).
I make use of this helpful fact when I talk about animating opacity in Bonus
Chapters 1 through 3.
ul {
list-style-type: type;
}
list-style-type: ">";
list-style-type: "\1F449";
FIGURE BC4-12:
Using a pointing
finger character
as a bullet.
ul {
list-style-image: url(image);
}
»» image: The path and filename for the image you want to use as the bullet.
list-style-image: url(images/eye-roll.png);
FIGURE BC4-13:
Using a custom
image as a bullet.
ol {
list-style-type: format;
}
These alternative number formats are particularly useful when you nest one num-
bered list inside another. The general nesting structure looks like this:
<ol>
<li>First main item</li>
<ol>
<li>First nested item</li>
<li>Second nested item</li>
<li>Third nested item</li>
</ol>
</li>
etc.
If you want the nested lists to use small roman numerals, you could use a rule
such as the following to target those lists:
li > ol {
list-style-type: lower-roman;
}
So, as a modern CSS developer, you now have two things you may have to worry
about:
»» You may have to support very old web browsers that never implemented
certain CSS features.
»» You may be itching to try very new features that don’t yet have wide browser
support.
You can safely handle both scenarios by using a CSS technology called the feature
query, in which you “ask” the web browser whether it supports a particular CSS
feature. Two things can happen:
»» The browser supports the feature: Great! In this case, the browser runs the
feature-related code that you’ve provided.
The feature query magic comes from the @supports at-rule, which uses the
following syntax:
Note that although you always have to supply both the property and the value
parameters, you’re querying the browser about only one or the other:
»» If you want to know only whether the browser supports a specific property,
then for the value parameter you can use anything that’s valid for that
property. For example:
@supports (initial-letter: 2)
In this case, you care only whether the browser supports the initial -letter
property (which, by the way, sets the size of the character targeted by the
::first-letter pseudo-element), so the value can be anything valid for the
property (such as an integer, in this example).
»» If you want to know only whether the browser supports a specific value of a
property, then for the value parameter you must specify the value you want
to query. For example:
In this case, you care only whether the browser supports the subgrid value of
the display property.
The way you use feature queries is to write the CSS code that you want the browser
to render if it doesn’t support the feature, and then use @supports to check for
feature support and supply some code to run if the browser has implemented the
feature. This process is known in CSS World as progressive enhancement because
you’re handling older browsers with code they can work with, and then you
h2 + p::first-letter {
color: crimson;
font-size: 32px;
}
@supports (initial-letter: 2) {
h2 + p::first-letter {
initial-letter: 5;
}
}
You can also query on multiple features. For example, to test whether the browser
supports two different features, you supply two property/value pairs, separated
by the keyword and:
To query the browser on whether it supports one feature or another, you supply
two property/value pairs, separated by the keyword or:
For example, as I write this, Safari doesn’t support the initial-letter property,
but it does support the prefixed property -webkit-initial-letter. So, your @
supports query should test the browser for one or the other, like so (bk03ch08/
example18.html):