Explore 1.5M+ audiobooks & ebooks free for days

From $11.99/month after trial. Cancel anytime.

CSS Master
CSS Master
CSS Master
Ebook829 pages4 hours

CSS Master

Rating: 0 out of 5 stars

()

Read preview

About this ebook

CSS has grown from a language for formatting documents into a robust languagefor designing web applications. Its simplicity is deceptive, however. It belies the complexity of the box model, stacking contexts, specificity, and the cascade. CSS mastery lies in understanding these concepts and how to take advantage of them.

This book will show you how to write better, more efficient CSS, and to use the plethora of the new cutting-edge CSS features available to the front-end developer. You'll also learn to master tools that will improve your workflow.

  • Organize your CSS to create efficient, reusable, and maintainable code
  • Discover complex layout techniques: grid layouts, multi-column layouts, and more
  • Use advanced effects: transitions, transforms, filter effect, and animations
  • Re-use and dynamically control CSS values with custom properties
  • Combine CSS and SVG to create seriously powerful graphics

This edition has been thoroughly updated to cover newer CSS features and techniques, including new chapters on visual effects and managing document scroll.

LanguageEnglish
PublisherSitePoint
Release dateAug 15, 2021
ISBN9781098129484
CSS Master
Author

Tiffany B Brown

Tiffany B. Brown is a freelance web developer and technical writer based in Los Angeles. She has worked on the web for more than a decade at a mix of media companies and agencies. Before founding her consultancy, Webinista, Inc., she was part of the Opera Software Developer Relations & Tools team. Now she offers web development and consulting services to agencies and small design teams.

Related to CSS Master

Related ebooks

Internet & Web For You

View More

Reviews for CSS Master

Rating: 0 out of 5 stars
0 ratings

0 ratings0 reviews

What did you think?

Tap to rate

Review must be at least 10 words

    Book preview

    CSS Master - Tiffany B Brown

    Chapter 1: Selectors

    Understanding selectors is key to writing maintainable, scalable CSS. Selectors are the mechanism by which CSS rules are matched to elements. There are various ways to do this, and you’re probably familiar with most of them. Element type, class name, ID, and attribute selectors are all well supported and widely used.

    In this chapter, we’ll firstly review the types of selectors. Then we’ll look at the current browser landscape for CSS selectors, with a focus on newer selectors defined by the Selectors Level 3 and Selectors Level 4 specifications.

    This chapter stops short of being a comprehensive look at all selectors, as that could take up a whole book in itself. Instead, we’ll focus on selectors with good browser support that are likely to be useful in your current work. Some material may be old hat, but it’s included for context.

    Types of Selectors

    Selectors can be grouped into four basic types: simple, compound, combinator, and complex.

    Simple selectors are the oldest form of CSS selector, and may be the type used most often. Simple selectors specify a single condition for matching elements. The universal selector (*) is a simple selector. So are type (or element) selectors such as p and pseudo-element selectors such as ::first-letter. Attribute selectors such as [hidden], class selectors such .message-error, and ID selectors such as #masthead also fall into this category.

    Compound selectors, such as p:last-child or .message.error, are a sequence of simple selectors that reflect a set of simultaneous conditions to meet when applying rules to an element. In other words, .message.error will match

    message error>, but not
    message> or
    error>.

    Combinator selectors express a relationship between elements. There are four:

    the descendant combinator, as in article p

    the child combinator (>), as in .sidebar > h2

    the adjacent sibling combinator (+), as in ul + p

    the general sibling combinator (~), as in p ~ figure

    Rules are applied to the right-most element in a combinator selector when it fits the condition indicated by the combinator. We’ll discuss combinator selectors in detail later in the chapter.

    Lastly, there are complex selectors. Complex selectors consist of one or more compound selectors separated by a combinator. The selector ul:not(.square) > a[rel=external] is an example of a complex selector.

    Selectors can be grouped into what’s known as a selector list by separating them with a comma. Selector lists apply styles to elements that match any of the selectors in the list. For example, the rule article, div { padding: 20px; } adds 20 pixels of padding to both

    and
    elements.

    Knowing what kind of selectors you’re working with will help you grasp one of the more confusing aspects of CSS: specificity. Keeping specificity low increases the reusability of your CSS rules. A selector such as #menu > .pop-open means that you can only use the .pop-open pattern when it’s a direct descendant of #menu, even if there are similar interactions elsewhere in your project.

    We’ll return to specificity in Chapter 2, "CSS Architecture and Organization". For the rest of this chapter, however, we’ll discuss specific groups of selectors: combinators, attribute selectors, pseudo-elements, and pseudo-classes.

    Combinators

    As we saw above, a combinator is a character sequence that expresses a relationship between the selectors on either side of it. Using a combinator creates a complex selector. Using complex selectors can, in some cases, be the most concise way to define styles.

    In the previous section, we listed the four combinators: descendant (via whitespace), child (>), adjacent sibling (+), and general sibling (~).

    Let’s illustrate each of these combinators. We’ll use them to add styles to the HTML form shown below.

    The HTML form that we’ll style using combinators

    The form pictured above was created using the following chunk of HTML:

    GET action=/processor>

       

    Buy Tickets to the Web Developer Gala

       

    Tickets are $10 each. Dinner packages are an extra $5. All fields are required.

       

            Tickets and Add-ons

     

           

               

                help>Limit 8

                number value=1 name=quantity id=quantity step=1 min=1 max=8>

       

     

           

               

                help>Serves 2

                number value=1 name=quantity id=quantity step=1 min=1 max=8>

           

     

       

       

            Payment

           

               

                help>No spaces or dashes, please.

                text id=ccn name=ccn placeholder=372000000000008 maxlength=16 size=16>

           

           

               

                help>Two-digit month>MM/Four-digit Year>YYYY

                text id=expiration name=expiration placeholder=01/2018 maxlength=7 size=7>

           

     

       

       

            Billing Address

           

               

                text id=name name=name placeholder=ex: John Q. Public size=40>

           

           

               

                text id=name name=name placeholder=ex: 12345 Main Street, Apt 23 size=40>

           

     

           

               

                text id=city name=city placeholder=ex: Anytown>

           

     

           

               

                text id=state name=state placeholder=CA maxlength=2 pattern=[A-W]{2} size=2>

           

     

           

               

                text id=zip name=zip placeholder=12345 maxlength=5 pattern=0-9{5} size=5>

           

       

     

       

    The Descendant Combinator

    You’re probably quite familiar with the descendant combinator. It’s been around since the early days of CSS (though it lacked a proper name until CSS2.1). It’s widely used and widely supported.

    The descendant combinator is simply a whitespace character. It separates the parent selector from its descendant, following the pattern A B, where B is an element contained by A. Let’s add some CSS to our markup from above and see how this works:

    form h1 {

        color: hsl(231, 48%, 48%);

    }

    We’ve just changed the color of our form title, the result of which can be seen below.

    The effect of a descendant combinator

    Let’s add some more CSS, this time to increase the size of our pricing message (Tickets are $10 each). We’ll also make it hot pink:

    form p {

        font-size: 36px;

        color: #c2185b;

    }

    There’s a problem with this selector, however, as you can see in the image below. Our selector is too broad.

    Oops! Our selector is too broad

    We’ve actually increased the size of the text in all of our form’s paragraphs, which isn’t what we want. How can we fix this? Let’s try the child combinator.

    The Child Combinator

    In contrast to the descendant combinator, the child combinator (>) selects only the immediate children of an element. It follows the pattern A > B, matching any element B where A is the immediate ancestor.

    If elements were people, to use an analogy, the child combinator would match the child of the mother element. But the descendant combinator would also match her grandchildren, and great-grandchildren. Let’s modify our previous selector to use the child combinator:

    form > p {

        font-size: 36px;

    }

    Now only the direct children of form are affected, as shown in the image below.

    The effect of the child combinator

    The Adjacent Sibling Combinator

    With the adjacent sibling combinator (+), we can select elements that follow each other and have the same parent. It uses the pattern A + B. Styles are applied to B elements that are immediately preceded by A elements.

    Let’s go back to our example. Notice that, in the Billing Address section, our labels and inputs sit next to each other. That means we can use the adjacent sibling combinator to make them sit on separate lines:

    label + input {

        display: block;

        clear: both;

    }

    You can see the results in the image below.

    Adjacent combinator to the rescue

    You can see in the image above that some of our labels remain on the same line as their input fields. In those instances, there’s a element between the

    Let’s look at another example that combines the universal selector (*) with a type selector:

    * + fieldset {

        margin: 5em 0;

    }

    This example adds a 5em margin to the top and bottom of every

    element, as shown in the image below.

    Using the adjacent sibling combinator to adjust the bottom margin for our fieldset elements

    Since we’re using the universal selector, there’s no need to worry about whether the previous element is another

    or

    element.

    More Uses of the Adjacent Sibling Selector

    Heydon Pickering explores more clever uses of the adjacent sibling selector in his article "Axiomatic CSS and Lobotomized Owls".

    The General Sibling Combinator

    With the general sibling combinator (~) we can select elements that share the same parent without considering whether they’re adjacent. Given the pattern A ~ B, this selector matches all B elements that are preceded by an A element.

    Let’s look at the Number of Tickets field again. Its markup looks like this:

       

        help>Limit 8

        number value=1 name=quantity id=quantity step=1 min=1 max=8>

    Our element follows the

    label ~ input {

        display: block;

    }

    Now all of our elements sit on a separate line from their

    The ~ combinator targets sibling elements, regardless of whether they’re adjacent

    Because the general sibling combinator matches any subsequent sibling, you’ll want to use it judiciously. Consider the markup and CSS below:

      en-US>

     

            utf-8>

            In This Essay, I Will

           

            h1 ~ p {

              background: yellow

            }

            h2 + p {

              outline: 5px dotted #009;

            }

           

       

       

           

    In This Essay, I Will

     

           

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce odio leo, sollicitudin vel mattis eget.…

     

           

    Nulla sit amet neque eleifend diam aliquam rhoncus. Donec id congue est. Aliquam sagittis euismod tristique.…

     

           

    Show how the general sibling combinator works

     

           

    Proin condimentum elit sapien, ut tempor nisl porta quis. …

       

    Here we’ve used the general sibling combinator with an

    element. As a result,

    every paragraph element that follows an

    element has a yellow background. This includes the paragraph that follows the

    heading, as shown below.

    Remember, the general sibling combinator matches every element B that follows A, regardless of its position in the document

    If you have control over the document’s markup, I’d recommend using a class selector instead of the general sibling combinator. The general sibling combinator makes it too easy to accidentally style more elements than you intended to.

    Attribute Selectors

    Introduced with the CSS Level 2 Specification, attribute selectors make it possible to style elements based on the presence of an attribute, such as [controls] for a media player, or [disabled] for a form field.

    You can also use attribute selectors to match elements based on the presence of an attribute and its value. For example, to style submit buttons, you might use the following CSS:

    [type=submit] {

      background: seagreen;

      border: 0;

      border-radius: 1000px;

      color: #fff;

      font-size: 18pt;

      padding: 10px 20px;

    }

    There are also several attribute selectors for partially matching attribute values, as well as substrings. Being able to target partially matching attribute values is one of my favorite features of CSS selectors. When used thoughtfully, they can reduce the number of rules and declarations you need to write. We’ll look at them shortly. Most of the attribute selectors we’ll cover are old hat. I’ve included them, however, for context and completeness.

    A Note About Quotes

    Quoting the values of attribute selectors is optional in most cases. Both [type=checkbox] and [type=checkbox] are valid and well-supported syntaxes for attribute selectors. Use quotes when the attribute’s value contains spaces or punctuation characters, such as [class=piechart animated], [data-action=modal:close] or [id='section-2.2'].

    Matching Space-separated Attribute Values

    Although we can select elements based on an attribute value, as discussed above, a selector such as [rel=external] won’t match / rel=external citation>, because the rel value isn’t exactly external. Instead, we need to use a selector that can accommodate space-separated values, which takes the form of [att~=val].

    The space-separated attribute value selector matches elements with the attribute (att) and a list of values, one of which is val. This can be any attribute that accepts space-separated values, including class or data-*.

    Space-separated lists of attributes are admittedly uncommon. They’re sometimes used with the rel attribute and microformats to describe relationships between people and documents. As an example, we might mark up external links like so:

      http://bob.example.com/ rel=external friend>Bob

      http://maria.example.com/ rel=external acquaintance>María

      http://ifeoma.example.com/ rel=external colleague>Ifeoma

    We can then use this presence-based attribute selector to match links that contain friend as one of its attribute values:

    [rel~=friend] {

        font-size: 2em;

        background: #eee;

        padding: 4px;

        text-decoration: none;

        border-bottom: 3px solid #ccc;

    }

    [rel~=friend]:link, [rel~=friend]:visited {

        color: #34444C;

    }

    [rel~=friend]:hover{

        background: #ffeb3b;

        border-color: #ffc107;

    }

    The result of this is shown in the image below.

    A link to Bob’s website styled using an attribute selector

    Matching Hyphenated Attribute Values

    One of the more interesting tasks we can do with attribute selectors is to use [attr|=val] to match the first part of an attribute value before the first hyphen. For example, [lang|=en] would match an element like

    en-US>.

    The main purpose of this selector is for working with languages and language codes, such as en-US and es-MX.

    Let’s say we have some markup like this:

    fr-FR>Tout le monde

    All the world, or Everyone

    We can italicize our French text and add language-appropriate angle quotes (« and ») to either side of it:

    [lang|=fr] {

        font-style: italic;

    }

    [lang|=fr] q:before{

        content: '\00AB'; /* Left angle quote */

    }

    [lang|=fr] q:after{

        content: '\00BB';  /* Right angle quote */

    }

    What’s cool about this selector is that it works even if there’s no hyphen. So the styles above would also apply to

    fr>. And we can further limit the scope of these selectors by adding an element selector, such as p[lang|=fr].

    This selector isn’t limited to language codes. We can use it with any hyphenated attribute value. Consider the following markup:

    promo>

       

    U.S. Meets Climate Goals 5 Years Early

       

    Lorem ipsum dolor sit amet, consectetur adipisicing ....

     

    promo-entertainment>

       

    Prince-Bythewood, Duvernay Among Nominees At Oscars

       

    Lorem ipsum dolor sit amet, consectetur adipisicing ....

     

    promo-sports>

       

    New York Knicks win NBA title

       

    Lorem ipsum dolor sit amet, consectetur adipisicing ....

     

    promo-business>

       

    GrubDash to Hire 3,000 Drivers, Offer Benefits

       

    Lorem ipsum dolor sit amet, consectetur adipisicing ....

    These are all article promos or teasers. They share some of the same visual characteristics and behavior, along with classes prefixed with promo. Here, too, we can use the hyphenated attribute selector to match these class names:

    [class|=promo] {

        border-top: 5px solid #4caf50;

        color: #555;

        line-height: 1.3;

        padding-top: .5em;

    }

    [class|=promo] h3 {

        color: #000;

        font-size: 1.2em;

        margin:0;

    }

    [class|=promo] p {

        margin: 0 0 1em;

    }

    Follow this up with specific border colors for each section type, and you’ll achieve something along the lines of the layout pictured below.

    Using hyphenated attributes to style elements

    We can also use this selector with ID names. For example, [id|=global] would match #global-footer, #global-menu, and so on.

    Matching Attribute Values by Substring

    We can also select elements when the attribute values match a particular substring using [att^=val], [att$=val] and [att*=val].

    The ^= selector matches a substring at the beginning of an attribute value. For example, think about links using tel: (non-standard) or mailto:. Since they behave differently from other hyperlinks, it makes sense to style them differently just as a hint to the user. Take a Call this business link:

    tel:+14045555555>Call this business

    We can select this and other tel: links by using [href^=tel:]. Let’s add some declarations:

    [href^=tel:] {

        background: #2196f3 url(../../Images/phone-icon.svg) 10px center / 20px auto no-repeat;

        border-radius: 100px;

        padding: .5em 1em .5em 2em;

    }

    You can see the result in the image below.

    Our new “Call this business” button

    The $= selector matches a substring at the end of an attribute value. If, for example, we wanted to give a special color to PDF file links, we could use a[href$=.pdf]:

    a[href$=.pdf] {

        color: #e91e63;

    }

    This selector would also be handy for matching elements whose attribute values end with the same suffix. For example, you could match both

    The *= selector matches a substring in any position within the attribute value. Using the selector [class*=sidebar], we could select an element with a class of sports-sidebar-a, along with elements with the classes sports-sidebar and arts-sidebar.

    Matching Attribute Values by Case

    CSS is, for the most part, a case-insensitive language. Both color: tomato and COLOR: TOMATO do the same thing. Both p {…} and P {…} will style paragraphs in HTML, whether the HTML uses

    or

    . The same applies with attribute names, where [href] and [HREF] will both match href= and HREF=.

    However, the same doesn’t apply to attribute values. Letter case matters with these. In the following markup, the ID attribute for our

    tag mixes uppercase and lowercase letters:

    MixedCaseIDExample>

        The identifier for this tag mixes uppercase and lowercase letters.

    To style the

    , we might use its ID selector—that is, #MixedCaseIDExample. But we’d have to use it exactly as it appears in the HTML. Using #mixedcaseidexample, for example, wouldn’t cut it.

    But there is an alternative. We could instead use case-insensitive attribute matching. It’s a feature defined by the Selectors Level 4 specification.

    Case-insensitive attribute matching uses the i flag to indicate that these styles should be applied to any case combination:

    [id=mixedcaseidexample i] {

      color: blue;

    }

    Now our selector will match the ID attribute whether its value is mixedcaseidexample, MixedCaseIDExample, or mIxEdCaSeIdExAmPlE.

    In some cases, you may want to enforce case-sensitive value matching. To enforce case-sensitive matching, use the s flag:

    [id=mixedcaseidexample s] {

      color: orange;

    }

    The s flag matches #mixedcaseidexample, but not #MixedCaseIDExample or #mIxEdCaSeIdExAmPlE.

    Pseudo-classes and Pseudo-elements

    Most of the new selectors added in CSS3 and CSS4 are not attribute selectors at all. They’re pseudo-classes and pseudo-elements.

    Though you’ve probably used pseudo-classes and pseudo-elements in your CSS, you may not have thought about what they are or how they differ from each other.

    Pseudo-classes let us style objects based on information—such as their state—that’s distinct from the document tree, or that can’t be expressed using simple selectors. For example, an element can only have a hover or focus state once the user interacts with it. With the :hover and :focus pseudo-classes, we can define styles for those states. Otherwise, we’d have to rely on scripting to add and remove class names.

    Pseudo-elements, on the other hand, let us style elements that aren’t directly present in the document tree. HTML doesn’t define a firstletter element, so we need another way to select it. The ::first-letter pseudo-element gives us that capability.

    Beware of Universal Selection

    Using pseudo-classes and pseudo-elements without a simple selector is the equivalent of using them with the universal selector. For a selector such as :not([type=radio]), every element that lacks a type attribute and value of radio will match—including and . To prevent this, use :not() as part of a compound selector, such as with a class name or element, as in p:not(.error).

    In the same way, using class names, IDs and attribute selectors on their own applies them universally. For example, .warning and [type=radio] are the same as *.warning and *[type=radio].

    Pseudo-elements

    The CSS Pseudo-elements Module Level 4 specification clarifies behavior for existing pseudo-elements and defines several new ones. We’ll focus on the ones that currently have browser support:

    ::after inserts additional generated content after the content of an element

    ::before inserts additional generated content before the content of an element

    ::first–letter selects the first letter of an element

    ::first–line selects the first line of an element

    ::marker styles bullets and numbers for list items and the

    element

    ::placeholder styles placeholder text for form controls using the placeholder attribute

    ::selection styles text selected by the cursor

    Of these, ::first–letter, ::first–line, ::selection, ::marker and ::placeholder affect content that’s part of the document source. The ::before and ::after pseudo-elements, on the other hand, inject content into a document. Let’s look at each of these pseudo-elements more closely.

    ::before and ::after

    Most pseudo-elements allow us to select content that’s already part of the document source—that is, the HTML you authored—but that’s not specified by the language. But ::before and ::after work differently. These pseudo-elements add generated content to the document tree. This content doesn’t exist in the HTML source, but it’s available visually.

    Why would you want to use generated content? You might, for example, want to indicate which form fields are required by adding content after their label:

    /* Apply to the label element associated with a required field */

    .required::after {

        content: ' (Required) ';

        color: #c00;

        font-size: .8em;

    }

    Required form fields use the required HTML property. Since that information is already available to the DOM, using ::before or ::after to add helper text is supplemental. It isn’t critical content, so it’s okay that it’s not part of the document source.

    Generated Content and Accessibility

    Some screen reader and browser combinations recognize and read generated content, but most don’t. You can’t be sure that content generated using ::before or ::after will be available to assistive technology users. You can read more about this in Leonie Watson’s piece "Accessibility support for CSS generated content".

    Another use case for ::before or ::after is adding a prefix or suffix to content. For example, the form mentioned above might include helper text, as shown here:

    post action=/save>

       

            Change Your Password

           

               

                password id=password name=password>

           

           

               

                password id=password2 name=password2>

           

           

    helptext>Longer passwords are stronger.

           

       

    Let’s enclose our helper text in red parentheses using ::before and ::after:

    .helptext::before,

    .helptext::after {

        color: hsl(340, 82%, 52%);

    }

    .helptext::before {

        content: '( ';

    }

    .helptext::after {

        content: ')';

    }

    The result is shown below.

    Using ::before and ::after to add supplemental content

    Both ::before and ::after behave similarly to other descendant elements. They inherit the inheritable properties of their parent, and are contained within it. They also interact with other element boxes as though they were true elements.

    One Pseudo-element per Selector

    Currently, only one pseudo-element is allowed per selector. A selector such as p::first-line::before is invalid and unsupported.

    This means that we can use ::before and ::after with CSS Grid and Flexbox. One use case is decorated headings, such as the one shown below.

    Using Grid layout with ::before and ::after to create decorated headings

    The CSS required to create this heading is as follows:

    h1 {

        display: grid;

        grid-template-columns: 1fr auto 1fr;

        gap: 3rem;

    }

    h1::before,

    h1::after {

        content: '\00a0';

        background: url('decoration.svg') repeat-x center / 50% auto;

    }

    You can read more about CSS Grid and Flexbox layout in Chapter 5, "Layouts".

    Creating Typographic Effects with ::first-letter

    While the ::before and ::after pseudo-elements inject content, ::first-letter works with content that exists as part of the document source. With ::first-letter, we can create initial letter effects, such as drop caps, as you might see in a magazine or book layout.

    Initial and Drop Caps

    An initial capital is an uppercase letter at the start of a block of text that’s set in a larger font size than the rest of the body copy. A drop capital (or drop cap) is similar to an initial capital, but is inset into the first paragraph by at least two lines.

    This CSS snippet adds an initial capital letter to every

    element in our document:

    p::first-letter {

        font-family: serif;

        font-weight: bold;

        font-size: 3em;

        font-style: italic;

        color: #3f51b5;

    }

    The result is shown below.

    Creating initial caps with the ::first-letter pseudo-element

    As you may have noticed from the image above, ::first–letter will affect the line-height of the first line if you’ve set a unitless line-height for the element. In this case, each

    element inherits a line-height value of 1.5 from the element. There are three ways to mitigate this:

    Decrease the value of line-height for the ::first–letter pseudo-element. A value of .5 seems to work well most of the time, depending on the font.

    Set a line-height with units on the ::first–letter pseudo-element.

    Set a line-height with units on either the or the ::first–letter parent.

    The first option preserves the vertical rhythm that comes with using a unitless line-height. The second option limits the side effects of using a fixed

    Enjoying the preview?
    Page 1 of 1