2. Rules and Selectors – Architecting CSS: The Programmer’s Guide to Effective Style Sheets

© Martine Dowden and Michael Dowden 2020
M. Dowden, M. DowdenArchitecting CSShttps://doi.org/10.1007/978-1-4842-5750-0_2

2. Rules and Selectors

Martine Dowden1  and Michael Dowden1
(1)
Brownsburg, IN, USA
 

While you may already be familiar with the basics of CSS, this chapter provides a quick overview of the language features at your disposal when making architectural decisions. An important part of software architecture is having a deep understanding of the tools and methods available to accomplish various tasks to achieve our system goals.

Selectors

As we saw in Chapter 1, selectors are the part of a CSS ruleset that determine exactly which elements get style declarations applied. Proficient use of selectors can go a long way toward decoupling HTML and CSS making for robust and consistently styled web sites and web applications.

Basics

The basic selectors allow elements to be selected based upon their obvious qualities as rendered in HTML: tag names, attributes, and class names. The CSS selector syntax is so expressive that there is a DOM function querySelector which accepts a CSS selector string to locate elements in the DOM tree. See Chapter 8 for more about JavaScript.

Universal Selector

The * in CSS is a universal selector that matches every element on the page. Sometimes this can be helpful, such as Listing 2-1 and Figure 2-1, which adds a visible indicator to any element selected with the keyboard.
*:focus {
  outline: 1px dotted grey;
}
Listing 2-1

Outline Selected Elements

Figure 2-1

Universal Selector

However, this convenience comes at a cost the universal selector effectively short-circuits inheritance for the associated declarations.

Unless you have a specific use case, it’s generally best to avoid the universal selector in favor of inheritance. It’s also a good idea to use the universal selector in combination with other selectors.

One use case for the universal selector is to apply a declaration to all children of another element, even if that property wouldn’t be inherited, such as a border. For this use case, consider either custom properties or mixins as an alternative to this approach.

Type Selector

Selecting an element in CSS can be as simple as using the tag name. This is called the type selector , and all HTML tags are valid selectors.

The example in Listing 2-2 and Figure 2-2 adds padding to all paragraph elements.
p {
  padding: 0.5rem;
}
Listing 2-2

Add Padding to Selected Elements

Figure 2-2

Type Selector

Class Selector

To select an element by its class, simply use a dot followed by the class name, such as .example. Since an HTML element can have multiple classes, multiple class selectors may be combined, and they can also be combined with element selectors. See Listings 2-3 and 2-4. Output is shown in Figure 2-3.
<body>
  <div>
    <p>Lorem Ipsum...</p>
    <img class="outline" src="image.png" alt="art">
    <button class="outline">Cancel</button>
    <button id="ok" class="outline bold">OK</button>
  </div>
</body>
Listing 2-3

Selector HTML

img {
  width: 200px;
}
button {
  background-color: lightblue;
}
button.outline {
  border: 1px solid green;
}
button.outline.bold {
  border: 5px solid darkgreen;
}
Listing 2-4

Class Selector CSS

Figure 2-3

Class Selector

In this example, the <img> element does not receive a border due to the type selector. The OK button has a thicker, darker border than the Cancel button.

ID Selector

The ID selector # selects an element based upon its ID attribute. Note that the use of duplicate IDs on a single page is not valid for HTML, so this selector is expected to match either 0 or 1 elements. The example in Listing 2-5 uses the HTML from the previous example in Listing 2-3. Figure 2-4 illustrates the output.
img {
  width: 200px;
}
#ok {
  font-size: 1.5rem;
  font-weight: bold;
}
p#ok {
  color: pink;
}
Listing 2-5

ID Selector CSS

Figure 2-4

ID Selector

This will make the button text bold on the OK button. The example shows that the ID selector may be combined with type selectors in the same way as the class selector. By combining these selectors in a way that doesn’t match the HTML, the contents of the paragraph tag will not become pink in this example.

Attribute Selector

The attribute selector matches an element based upon one of its attributes. This selector uses square brackets to contain the attribute match and may be optionally combined with a type selector. For example, a [rel] can be used to match all anchor tags with a provided relationship. To allow <area> tags to also match, use [rel] by itself.

In addition to testing for the presence of an attribute, this selector can test for specific values as shown in Listings 2-6 and 2-7 and Figure 2-5.
<body>
  <h1>Attribute Selector</h1>
  <form>
    <button href="" title="go back">
      <i class="material-icons">arrow_back_ios</i>
      Previous
    </button>
    <label>
      Username
      <input type="text" >
    </label>
    <label>
      Password
      <input type="password" >
    </label>
    <label>
      Avatar
      <input type="file" accept="image/png">
    </label>
    <button href="" title="Continue">
    Next
    <i class="material-icons">arrow_forward_ios</i>
    </button>
  </form>
  <blockquote cite="w3.org">
    The World Wide Web Consortium (W3C) is an...
  </blockquote>
  <blockquote cite="https://en.wikipedia.org/wiki/Wikipedia">
    Wikipedia is a multilingual online encyclopedia...
  </blockquote>
  <a href="myfile.pdf" download>PDF File</a>
  <a href="myfile.docx" download>Word Doc</a>
</body>
Listing 2-6

Attribute Selector HTML

label, input, a, button {
  display: block;
  margin-bottom: 1rem;
}
button {
  display: flex;
  align-items: center;
}
/* Matches password input fields */
input[type="password"] {
  color: red;
}
/* Strikes out any quotes cited from Wikipedia */
blockquote[cite*="wikipedia.org"] {
  text-decoration: line-through;
}
/* Underlines any element with a title attribute containing
   the word "continue" with any Capitalization.
*/
[title*="continue"] i {
  text-decoration: underline;
}
/* Display a gray border around any input which has an
   accept starting with image, such as image/png
*/
input[accept^="image"] {
  border: solid 4px gray;
}
/* Display a PDF icon beside any .pdf download links */
a[href$=".pdf"]::before {
  content: url(icon-pdf.png);
}
/* Matches a material design icon such as <i class="material-icons">arrow_back_ios</i> */
i[class|="material-icons"] {
  color: blue;
  width: 32px;
}
Listing 2-7

Attribute Selector CSS

Figure 2-5

Attribute Selector

Because class and ID are both HTML attributes, the class and ID selectors have attribute selector equivalents, shown here in Table 2-1.
Table 2-1

Attribute Selector Equivalents

 

Basic Selector

Attribute Selector

Select by ID

#contactForm

[id=contactForm]

Select by Class

.outline

[class~="outline"]

Grouping

To minimize duplication of declaration blocks, selectors can be grouped together into a comma-delimited list. For example, a, button { ... } would apply the declaration block to both anchor and button elements in the HTML.

Combinators

We’ve already seen how to combine type selectors with class and ID selectors, but what if we want to combine multiple type selectors or even attribute selectors? There are a few other combinators to make this possible, and they even provide for hierarchical context based upon the elements’ relationship within the DOM. Examples of the combinators in Table 2-2 can be found in Listings 2-8 and 2-9 and Figure 2-6.
Table 2-2

Combinators

Name

Combinator

Example

Description

Descendant

" " (space)

nav a

All anchor tags inside of a nav element

Child

">"

nav > ul > li

First list items inside a navigation list, ignoring any items after the first level

Sibling

"~"

p ~ p

All paragraphs (after the first) that share the same parent element

Adjacent Sibling

"+"

h2 + p

All paragraphs that immediately follow an <h2> tag on the same hierarchy

<body>
  <h1>Combinators</h1>
  <nav>
    <ul>
      <li><a href="">Home</a></li>
      <li>
        <a href="">Combinators</a>
        <ul>
          <li>" " (space)</li>
          <li>&gt;</li>
          <li>~</li>
          <li>+</li>
        </ul>
      </li>
    </ul>
  </nav>
  <main>
    <h2>List of Combinators</h2>
    <p>There are a few other combinators to make this...</p>
    <ul>
      <li>
        " " (space)
        <ul>
          <li>nav li</li>
          <li>nav a</li>
        </ul>
      </li>
      <li>></li>
      <li>~</li>
      <li>+</li>
    </ul>
    <p>By combining selectors together we can select...</p>
  </main>
</body>
Listing 2-8

Combinators HTML

nav a {
  display: block;
  margin: 0 1rem;
}
nav > ul > li {
  border: solid 1px gray;
  display: inline-block;
  list-style-type: none;
  vertical-align: top;
}
p ~ p {
  color: purple;
  font-weight: bold;
}
h2 + p {
  font-family: sans-serif;
}
Listing 2-9

Combinators CSS

Figure 2-6

Combinators

By combining selectors together, we can select elements to style based upon their natural location and ordering within an HTML document. This can help us separate concerns between layout, theming, and content for more manageable rulesets.

Pseudo Elements

Pseudo elements allow you to select elements that do not exist within the HTML document, but show on the screen visually. Both ::first-letter and ::first-line select a portion of text within an element.

While the effect of ::first-letter could be reproduced by adding a <span> tag around the desired letters, for fluid layouts there is actually no other way to select the entire first line of a text block than ::first-line. This is because this rule is applied after the layout has been calculated so that the browser knows which words should be affected by the rule. See Listings 2-10 and 2-11 and Figure 2-7.
<body>
  <h1>Pseudo Elements</h1>
  <p>Lorem ipsum dolor sit amet, consectetur...</p>
  <p>Cras id blandit risus. Nunc dictum, elit...</p>
  <p>Quisque euismod tempus erat, sit amet pharetra...</p>
</body>
Listing 2-10

Pseudo Elements HTML ::first-line and ::first-level

p::first-letter {
  color: red;
  font-size: 3rem;
  line-height: 0;
  display: block;
  float: left;
  margin-top: .125rem;
  margin-right: .5rem;
}
p::first-line {
  color: red;
}
Listing 2-11

Pseudo Elements CSS ::first-line and ::first-level

Figure 2-7

Pseudo Elements ::first-line and ::first-level

The ::after and ::before pseudo elements use the content property to insert content (either text or an image) based upon specific criteria. We see this in action in Listings 2-12 and 2-13 and Figure 2-8.
<body>
  <a href>First Link</a>
  <a href>Second Link</a>
  <a href>Third Link</a>
</body>
Listing 2-12

Pseudo Elements HTML ::before and ::after

a {
  display: block;
}
a::before {
  content: url(link.png);
  display: inline-block;
  margin-right: .5rem;
  vertical-align: middle;
}
a::after {
  content: ' (link)'
}
Listing 2-13

Pseudo Elements CSS ::before and ::after

Figure 2-8

Pseudo Elements ::before and ::after

Have you ever wanted to customize the placeholder text on an input element? You can do that with input[type=text]::placeholder (see Listings 2-14 and 2-15 and Figures 2-9 and 2-10).
<form>
  <label>
    Username:
    <input type="text" placeholder="Example: user@email.com">
  </label>
</form>
<video width="100%" height="250" controls>
  <source src="" type="video/mp4">
</video>
Listing 2-14

Pseudo Elements HTML ::placeholder, ::selection, and ::backdrop

input {
  box-sizing: border-box;
  border-radius: 4px;
  border: solid 1px gray;
  padding: .5rem 1rem;
  font-size: 1rem;
  width: 100%;
}
input[type=text]::placeholder {
  font-family: cursive;
}
::selection {
  background-color: cornflowerblue;
  color: white;
}
::backdrop {
  background: cornflowerblue;
}
Listing 2-15

Pseudo Elements CSS ::placeholder, ::selection, and ::backdrop

Figure 2-9

Pseudo Elements ::placeholder and ::selection

Figure 2-10

Pseudo Elements ::backdrop

Have you selected text on a web site and noticed that the selection highlight was in the site’s brand colors? This can be accomplished with *::selection {background-color: cornflowerblue}.

The background in full-screen browsing mode can be customized using ::backdrop.

Both are also seen in the preceding example.

Note

The CSS specification calls for a two-colon prefix before a pseudo element, such as ::after. However, most browsers support pseudo elements with just a single colon (:after) without throwing an error. It is likely that you will see this usage in the style sheets you encounter and it is important to understand why it works. In general, we recommend the standard two-colon prefix for two reasons: (1) it adheres to the CSS specification and (2) it clearly distinguishes pseudo elements from pseudo classes.

Pseudo Classes

Pseudo classes select elements based upon information that is not available in the HTML document. This may include state or context metadata.

Some of the pseudo classes make it possible to adjust styles based upon user interaction.
  • :hover – Match when an element is being hovered over (such as using the mouse)

  • :focus – Match an element selected with the keyboard (by tabbing), or with the mouse (by clicking the element)

  • :active – Match an element in the process of being activated (such as clicking, while the mouse button is depressed)

  • :target – Select an element that has an ID matching the URL’s fragment (the portion after the #)

Displaying tabular data with beautiful formatting is made easy with the positional pseudo classes. Select the first and last rows of a table with tr:first-of-type and tr:last-of-type, respectively. Use the same technique to select the first and last columns using <td>. Highlight every other row using tbody > tr:nth-child(even).

Managing forms and showing helpful indicators can use some of the following pseudo classes:
  • :in-range, :out-of-range – Numeric value compared to defined range

  • :placeholder-shown – If the placeholder text is currently visible

  • :invalid, :valid – Checks the validation status of form fields for error and success indicators

  • :checked, :indeterminate Used to select a checkbox or radio button that is currently selected or if the selected option cannot be determined

  • :default – Matches only if this element is the default in a group of elements (such as the default submit button or the default radio option on a form)

  • :disabled, :read-only, :read-write – Matches the current status of a form field based on availability to user interaction

  • :optional, :required – Matches fields based upon their required status

Another important pseudo class is the :not() selector, which select elements that do not match a list of selectors. While many of the pseudo classes have their inverse state defined (e.g., :optional vs. :required), there are many other scenarios where negation can be useful. For example, you can select every direct child tag of an <article> that is not an <img> by using article > *:not(img) { ... }.

Many of these pseudo classes provide capabilities to CSS that would otherwise require JavaScript involvement in designing the user experience. By leveraging CSS for context-sensitive UI implementation, we keep application and view logic separate, improving the maintainability and performance of our web sites and web applications. Examples of some of the previously mentioned pseudo classes can be found in Listings 2-16 and 2-17 illustrated in Figure 2-11.
<body>
  <h1>Pseudo Classes</h1>
  <table>
    <thead>
      <th>Name</th>
      <th>Email</th>
      <th>Zip Code</th>
    </thead>
    <tbody>
      <tr>
        <td>Jane</td>
        <td>jane@email.com</td>
        <td>15978</td>
      </tr>
      <tr>
        <td>John</td>
        <td>john@email.com</td>
        <td>11458</td>
      </tr>
      <tr>
        <td>Alex</td>
        <td>alex@email.com</td>
        <td>68978</td>
      </tr>
    </tbody>
  </table>
  <form>
    <label>
      Name:
      <input type="text" maxlength="20" required>
    </label>
    <label>
      Email
      <input type="email" maxlength="100" required>
    </label>
    <label>
      Zip Code:
      <input type="number" max="99999">
    </label>
    <button type="submit">Submit</button>
  </form>
</body>
Listing 2-16

Pseudo Classes HTML

table {
  border-collapse: collapse;
  margin-bottom: 1rem;;
  width: 100%;
}
tr {
  border-top: solid 1px lightgrey;
  border-bottom: solid 1px lightgrey;
}
tbody tr:nth-last-of-type(odd) {
  background: lightblue;
}
th, td {
  padding: .5rem 1rem;
  text-align: left;
}
form {
  margin-top: 2rem;
}
form > *:not(button) {
  border-radius: 4px;
  box-sizing: border-box;
  display: block;
}
label {
  margin-bottom: .5rem;
}
input {
  border: solid 1px lightblue;
  padding: .5rem 1rem;
  width: 100%;
}
input:hover, input:active {
  border-color: slategray;
}
input:invalid {
  border-left: solid 5px red;
}
input:valid {
  border-left: solid 5px green;
}
button {
  padding: .5rem 1.5rem;
  border: solid 1px lightblue;
  border-radius: 3px;
  background: white;
  margin-top: .5rem;
}
button:hover, button:active {
  outline: dotted 1px blue;
  outline-offset: 2px;
}
Listing 2-17

Pseudo Classes CSS

Figure 2-11

Pseudo Classes

Declarations

Selecting the elements doesn’t do any good if we don’t apply styles to those elements. The declarations section of each ruleset is where the individual style properties, and their values, are specified for the matching elements.

Properties

The properties in CSS refer to the various aspects of layout and style that can be affected. Many properties are available for some elements and not others. Sometimes the visibility of a property will depend upon the display setting of an element. For instance, the height property is ignored on elements with display: inline, but is rendered on display: inline-block.

Some of the CSS properties are a shorthand notation for a number of individual properties. Consider the example in Listing 2-18 where two declaration blocks produce the same results.
p {
  border-width: 2px;
  border-style: solid;
  border-color: #666666;
}
p {
  border: 2px solid #666666;
}
Listing 2-18

Border Property

Each of the individual border properties is available as an optional value parameter to the border shorthand property. Some of the other shorthand properties include background, box-shadow, font, padding, margin, and outline. Each of these has a different list of properties they summarize, and they have a specific order in which the properties should be provided. Be sure to check a reference when using these until you become comfortable with the syntax of each.

Some property and value combinations may produce results that seem similar but are actually quite different. Table 2-3 lists a few of these with an explanation on how they differ.
Table 2-3

Property Disambiguation

First Property

Second Property

Description

margin: 2px;

padding: px;

Margin is outside the box model and can be collapsed when adjacent. Padding is inside the box.

border: 2px solid black;

outline: 2px solid black;

Border adds to the box model dimension and exists between margin and padding. The outline exists outside the border and takes up no space on the box.

visibility: hidden;

display: none;

A hidden element still exists on the page and can take up space and receive events. An element that is not displayed effectively doesn’t exist in the render tree.

The outline property is very useful for highlighting elements on the screen where you do not want the item to reflow. This is commonly used to highlight elements in combination with the :focus pseudo class. See Chapter 4 for details of the box model, as well as on properties related to layout, including display, grid, and flex.

A comprehensive review of the available CSS properties and values is outside of the scope of this book. For an excellent reference, we recommend the MDN CSS Reference from Mozilla, which can be found at https://developer.mozilla.org/docs/Web/CSS/Reference.

Units

There are a number of CSS properties that expect a <length> data type. This length is a scalar (numeric) value with an associated unit of measure. Selecting the correct units can make the difference between a nice fluid, responsive layout and one that breaks anytime the user resizes the window or zooms. The correct units can also have a tremendous impact on the amount of work it takes to achieve certain layouts.

There are three basic categories of units that we’ll discuss. The first category includes absolute measures that are established at the time of the design. The second category is font-relative, meaning that if the user zooms the page or changes their default font size, the meaning of these values will change relative to one another. And the third category contains viewport-relative lengths, meaning they will change relative to the browser size or the specific display on the user’s device.

Absolute

px The traditional unit of measure for computer graphics; this is only suitable for screen-based displays.

in Inch. 1in. = 6pc = 72pt = 2.54cm. This will be a true inch on printers, but defined relative to a reference pixel for screens which is 96px regardless of the screen resolution.

pc Pica. A traditional unit of measure in typography.

pt Point. A traditional unit of measure in typography.

cm Centimeter. 1cm = 10mm. See the earlier note on inches relating to printers and screens.

mm Millimeter.

Note

Absolute units of measure do not scale relative to user settings such as font-size. As a result, the use of these units (especially on-screen) is likely to cause significant issues for accessibility and is not recommended.

Font-Relative

ch Represents the width of the “0” character in the element’s font (consisting of both typeface and size).

ex Represents the height of the “x” character in the element’s font (consisting of both typeface and size).

em The calculated font-size of the element. If this unit is used on the font-size property, it will be relative to the inherited font-size.

rem Exactly the same as em, but always relative to the font-size of the root element (which is the <html> for HTML documents). This is the preferred default unit for many web designers as it allows for manageable fluid layouts while addressing accessibility concerns.

Viewport-Relative

vh Equal to 1% of the height of the viewport

vw Equal to 1% of the width of the viewport

vmin Equal to the smaller of vh or vw

vmax Equal to the larger of vh or vw

Percentage

Many CSS properties will accept a <percentage> or a <length-percentage> (meaning either a length or a percentage). While the rem is the best choice for many purposes, especially those relating to content and accessibility, percentage works relative to any inherited size including font-relative, view-relative, or even absolute units.

Functions

While CSS does not allow user-defined functions, there are a large number of available functions to perform a variety of tasks, some of which are described as follows:

Shape There are a number of nonrectangular shapes supported through the functions circle(), ellipse(), inset(), and polygon(). Combine with the shape-outside property to wrap text to a specific shape, or with clip-path to crop an image or container.

Transformation There are a large number of transformation functions, including rotateX(), scale(), and skewY(). There are also 3D transformations such as perspective(), matrix3d(), and scaleZ(). These transformations can adjust the shape, orientation, and position of elements on the screen to create a wide range of visual effects and layouts.

Gradients There are a large number of functions to support the creation of gradients, including linear-gradient(), radial-gradient(), repeating-linear-gradient(), and repeating-radial-gradient(). The blending of colors enabled by gradients supports a large number of visual effects.

Effects There are other visual effects beyond just gradients. The blur() function will produce a Gaussian blur on the selected element, even an image. This can be useful for the backdrop of a modal dialog. The drop-shadow() adds some dimension to a theme. And opacity() allows elements to be somewhere between fully opaque and fully transparent, to allow dimensional overlays. (Note that if you want opaque text but a semitransparent background, you may want to consider using the rgba() or hsla() color functions as described in the following text.)

Color The most common way of specifying color in CSS is with the 3- or 6-digit hex code preceded by a hash symbol, such as #FF0000 for the color red. Colors can also be specified by hue, saturation, and lightness using the hsl() and hsla() functions, or as RGB (red, green, blue) using rgb() or rgba(). The “a” in each of these function sets refers to the alpha channel which specifies level of opacity or transparency.

Colors can also be manipulated in a consistent fashion using the filter property with alterations such as contrast(), saturate(), and hue-rotate() and effects applied such as grayscale() or sepia(). These functions are particularly useful because they can apply to an image as well as text on the page.

Resources The url() function is used to add image resources to a design through CSS. This allows the <img> tag in HTML to be reserved for images that are relevant to the content, rather than to the layout and design.

Counting The counting functions counter(), counters(), and symbols() are used to manage counter variables. See more about counters in the following “Variables” section.

Math Sometimes the built-in units aren’t enough and you need to calculate size or position based upon other elements. The calc() function makes it possible to do some basic math with a mix of units. Addition, subtraction, multiplication, and division are supported along with parentheses. As an example, you could use height: calc(10vh - 1rem) to calculate the height of a header that was 10% of the viewport height, but accounted for a 1rem border.

Listings 2-19 and 2-20 show the source code for Figure 2-12.
<body>
  <h1>Functions</h1>
  <div class="shape"></div>
  <div class="shape"></div>
  <div class="shape"></div>
</body>
Listing 2-19

Functions HTML

.shape {
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
  display: inline-block;
  position: relative;
  height: calc(100vw / 3);
  width: calc(100vw / 3);
}
.shape:nth-of-type(1) {
  background: rgba(255, 0, 255, 0.31);
  transform: rotate(-25deg);
  filter: saturate(15%);
}
.shape:nth-of-type(2) {
  background: rgb(255,116,0);
  background: linear-gradient(90deg, rgba(255,116,0,1) 0%, rgba(255,237,0,1) 47%, rgba(255,167,0,1) 100%);
  filter: opacity(.75);
  transform: translate(0, -50px);
  left: calc((100vw / 3) - 200px);
}
.shape:nth-of-type(3) {
  background: hsl(189, 100%, 50%);
  transform: rotate(25deg);
  opacity: .33;
  left: calc((100vw / 3) - 100px);
  top: -200px;
}
Listing 2-20

Functions CSS

Figure 2-12

Functions

The example shown in Figure 2-12 highlights a number of functions. The position of the stars in the example is dependent upon the size of the browser window since calculations are based upon the vw and vh units.

Variables

There are a few ways to use dynamic data within CSS (examples found in Listings 2-21 and 2-22 and Figure 2-13):

Custom properties These variables are defined much like any other CSS property and can contain any value that would be legal in CSS. They can then be referenced later in a style sheet using the var() function.

Attributes Using the attr() function , you can pull in the value from an HTML attribute. Combine this with the content property to display attribute data in unique ways.

Counters Every HTML element can have 0 to many named counters associated within the document tree and manipulated using CSS. HTML lists generate a “list-item” counter automatically, incrementing by 1 with every list element unless explicitly reset. This also includes unordered lists. Use counter-set, counter-increment, or counter-decrement properties to adjust the counters and use counter() or counters() to display the value of a named counter in a way you choose. This exists to support nested lists, but may have many other uses.
<body>
  <h1>Variables</h1>
  <ul>
    <li category="fruit">Apple</li>
    <li category="vegetable">Lettuce</li>
    <li category="starch">Corn</li>
  </ul>
</body>
Listing 2-21

Variables HTML

ul {
  counter-reset: li;
}
li:before {
  content: counter(li)"-" attr(category)": ";
  counter-increment: li;
  text-transform: capitalize;
  background: lightblue;
  display: inline-block;
  padding: .5rem 1rem;
  border-radius: 25px;
  margin: 0 1rem 1rem 0;
}
Listing 2-22

Variables CSS

Figure 2-13

Variables

At-Rules

The CSS at-rules (so-named because of the @ or “at” symbol in the name of each) are language features that provide some control over the structure of your styles. Among other things these rules provide a mechanism for collecting or grouping other rulesets.

@import

In Chapter 1 we looked at three ways to include CSS in an HTML document, including the <link> element. The @import at-rule provides a similar function for CSS. Both of these include pull mechanisms in a CSS file, effectively inserting its contents at the position of the import statement.

This is very useful as it allows us to break up style sheets into more logical and manageable files without any impact on the HTML document. See Chapter 7 for a more in-depth discussion of @imports along with other mechanisms for pulling in external style sheets.

@supports

The @supports at-rule allows rules to be applied based upon specific support of CSS features by the user agent. This is a way to provide styling and formatting based upon what a web browser declares support for, rather than using old-school hacks in an attempt to detect if a given rule will work as expected.

This at-rule allows you to start taking advantage of the very latest CSS today, by making it possible to provide alternate rules for cutting-edge browsers (or perhaps alternate rules for older browsers) such as in Listings 2-23 and 2-24.
<body>
  <h1>At-Rules</h1>
  <p>Hello World</p>
</body>
Listing 2-23

At-Rules HTML

p {
  text-decoration: underline;
  text-underline-offset: 1rem;
}
@supports not (text-underline-offset: 1rem) {
  p {
    text-decoration: none;
    padding-bottom: 1rem;
    border-bottom: solid 3px orange;
    display: inline-block;
  }
}
Listing 2-24

At-Rules CSS

Because text-underline-offset is supported by Firefox (Figure 2-14), Firefox ignores the @support not code. Opera, however, at the time of the writing of this book, does not support text-underline-offset and therefore uses the fallback code provided in the @supports not (Figure 2-15).
Figure 2-14

At-Rules, text-underline-offset Supported in Firefox

Figure 2-15

At-Rules, text-underline-offset Not Supported in Opera

@media

The CSS media at-rule is used to perform queries against the system, environment, or user agent. This use is called a media query .

Media Query

“Media Queries allow authors to test and query values or features of the user agent or display device, independent of the document being rendered. They are used in the CSS @media rule to conditionally apply styles to a document, and in various other contexts and languages, such as HTML and JavaScript.”

—Media Queries Level 41

These media queries can be used to build responsive layouts as discussed in Chapter 4, but they have many other uses. For example, a printer-friendly theme can be created that hides navigation and banners while retaining content as shown in Listing 2-25.
@media print and monochrome {
  nav, .banner { display: none; }
}
Listing 2-25

Printer-Friendly Design

Additional control over printing can be obtained by providing page-specific instructions using the @page at-rule.2

It’s also possible to adjust the layout for devices that don’t have a pointing device (such as a mouse) that supports hover, as is common with tablets and mobile devices. In Listing 2-26, we display the target URL beside every link, but only on devices that don’t support hover.
@media (not(hover)) {
  a::after {
    content: attr(href);
    font-size: x-small;
    position: absolute;
  }
}
Listing 2-26

Tablet-Friendly Icon

Summary

This chapter covered the basic building blocks of CSS rulesets. You’ve learned how to
  • Specify printer-specific layout and designs

  • Insert document icons based upon the file extension using nothing but CSS

  • Combine multiple selectors together into more advanced expressions

  • Highlight alternating rows on a table

  • Provide alternate styles based upon browser and device capabilities

The next chapter covers the cascading part of Cascading Style Sheets and demystifies the process by which a user agent decides the values of every property of every element on the page.