• universal

    *

  • element

    p

  • class

    .note

    p.note

  • ID

    #menu

    div#menu

  • not

    :not(s)

    div:not(#menu)

    p:not(.gloss, .nb)

    a:link:not(:hover)

  • descendant

    div p

    div p b

    .biblio cite

    #menu ul li

  • child

    div > p

  • first child

    :first-child

  • nth child (css3)

    :nth-child(2)

    :nth-child(odd)

    :nth-child(2n+0)

  • grandchild

    div > * > p

  • beyond child

    div * p

  • general sibling

    h2 ~ p

  • adjacent sibling

    h2 + p

    h2 + p + p

  • attribute

    a[href]

    a[href="/engl"]

    a[href~="/engl"]

If you're new to CSS, Google can deliver truckload after truckload of information to your screen. 456bereastreet gives a good 3-part overview. Maxdesign provides clear, short references on several topics. And W3C schools provides interactive examples. After browsing one or two of the links above, the summary to the right might jog your memory.

The point of this page is not to invent a 19th wheel for the overloaded rigs lumbering down the information highway. Instead, it provides variations on a theme — style by context, not class. The following selectors can help.

Child Selector

The nested list below contains one item. That item contains another list with 3 items. The list is assigned to the "example" class, so it can be formatted distinctly from other lists.

<ul class="example">
   <li>Animal
      <ul>
         <li>bear</li>
         <li>possum</li>
         <li>racoon</li>
      </ul>
   </li>
</ul>

Quick Review: the first LI is a child of the outer UL. The LI containing "bear" is the great-grandchild of the outer UL. (Here's a good tutorial about the document tree and here's a brief refresher.)

The browser renders the markup above as you'd expect:

Say you want to wrap the "Animal" category in a border. A descendant selector won't help here. It selects all descendants, producing this mess:

However, a child selector ( > ) limits selection to children, so this rule:

.example > li {
  border: 3px double #aaa;
  }

produces:

The important point is this: the HTML markup has not changed a bit. You could add more declarations to the rule, and the same selector could set "Animal" to bold, to a different color, a different font, etc.

But first, you'd need to control "inheritance".

Not all style properties inherit their values, but over 30 do — among them, properties that format text: font, size, weight, color, spacing, alignment, etc.. If no other rule defines them, they'll inherit their values from their parent elements.

That's why, if this were the only style rule for your page,

body { 
  color: green;
  border: 1px solid black;
  }

every scrap of text would be green, but only BODY would have a border. Color is inherited. Borders are not.

Inheritance is a great convenience when it works as you'd expect. But sometimes it's tricky. If your page is governed by the following style sheet,

body { color: #000; }
:not(p) { color: red; }

you might expect paragraphs to be black, other text, red. Instead, the entire page is red.

Here's why: The 1st rule sets all text to black. Because all displayed elements are children of BODY, they receive their color by inheritance. Then the 2nd rule selects all elements which are NOT P and colors them red, including BODY.

No rule of higher specificity has given P its color, so it continues to inherit color from BODY. It becomes red, too. A color rule for P would fix this, trumping inheritance with specificity. (Of course, this contrived example ignores the simpler solution: set BODY to red, and P to black. For useful examples of the :not() selector, see Kilian Valkhof's overview.)

You can guess, then, what happens if you use the child selector to set "Animal" to bold:

.example > li { 
  font-weight: bold; 
  }

Because font weight is inherited, the nested list becomes bold, too.

The child selector appears to select all descendants, but it only alters the font-weight for the first LI. The nested LI elements — the grandchildren of the first LI — then receive their style through inheritance.

The solution is the same — specificity fixes the problem. As soon as you define a generic font-weight for all LI elements:

li { 
  font-weight: normal;
  }

you thwart inheritance and get the expected results:

In general, if you decorate the boxes around text, you don't need to control for inheritance. If you decorate the text itself, you do.

For an excellent exploration of inheritance, see maxdesign's home page. (Look down the page for Presentations and click through the CSS Inheritance screen shots.)

Child selection can be handy in managing longer lists, too:

To save a little screen real estate, the same selector can float all the child LI's in a row, leaving grandchild LI's vertically aligned. This rule limits "float: left" to children:

.example > li {
  float: left;
  border: 2px solid #aaa;
  font-weight: bold;
  }

to produce:

In either orientation, though, the borders between the list items need attention.

This problem crops up in many guises. And it's easy to fix.

First Child

The :first-child pseudo element solves a host of issues. A bordered table, for instance, often needs its first or last row styled differently from inner rows. The list above, with its chubby borders, poses a similar problem. The tools provided by CSS suggest how to approach it.

Look at the list to the right. The list itself has a double border. Each list item has a bottom border to separate it from its siblings. This causes the extra line at the bottom of the list.

Unfortunately, with the style set in this way, there's no way to fix the border problem by context. Instead, you'd need to patch it up by class. You'd create something like a "noborder" class:

.noborder {
  border-bottom: none;
  }

and insert a class attribute in the last LI:

<li class="noborder">

That would solve the problem, but it isn't ideal. When you style by class, you create extra work for yourself. Similar lists will need the same extra class attribute, and when you shift your list items around, you must remember to transfer the "noborder" class to the LI that winds up last.

The ":first-child" pseudo element is the key to the problem. First, you rework your style rule to place the border at the top of each list item.

Now, it's a simple matter to cancel the top border on only the first LI in the list:

ul li:first-child {
  border-top: none;
  margin-top: 0;
  }

Then you can shift your list items around without altering your markup.

The same approach fixes the extra thick borders between the list items above. For the horizontal list, you alter the child rule so that no LI's have left borders. Then you create a new rule for the first-child LI and restore its left border:

.example > li {
  float: left;
  border: 3px double #aaa;
  border-width: 3px 3px 3px 0;
  font-weight: bold;
  }
.example > li:first-child {
  border-left-width: 3px;
  }

All three categories now have uniform borders:

Note: this strategy will fail if the horizontal list grows wide enough to wrap to another line. The initial item on the 2nd line will lack a left border.

Once CSS3's ":last-child" selector gains wider support, the exception could occur at either end of the list. For now, when dealing with borders and spacing, make the initial element the exception so you can alter it with ":first-child".

An annotated bilbiography can be created using ":first-child", too. I mention bibliographies in particular because the web sports numerous bad examples of how to mark them up. Many contain broken HTML tags. Some are housed in tables (it's 2010, for god's sake). And even those that conform to current standards, often do so inefficiently, scattering class attributes redundantly throughout all citations to achieve the hanging indentation.

A normal bibliography is not a data table, and it's not a sequence of paragraphs. It's a list of references. As a list, it makes more semantic sense to mark each entry as a list item:

<ul class="bib">
  <li>citation . . .</li>
  <li>citation . . .</li>
</ul>

However, an annotated bibliography complicates matters. MLA style requires an annotation to continue immediately after the citation, not on a new line. So annotated bibliographies in MLA format are composed of a list of references. But each reference is composed of at least one, and possibly several, paragraphs.

First-child is indispensible for solving this formatting challenge. If you mark the annotated bibliography this way:

<ul class="bib">
  <li>
    <p>citation and annotation</p>
    <p>optional annotation</p>
  </li>
  <li>
    <p>citation and annotation</p>
    <p>optional annotation</p>
  </li>
</ul>

:first-child can select only the initial paragraph of any entry:

.bib {
  padding-left: 3em;
  }
.bib li p:first-child {
  text-index: -3em;
  }

producing this:

No additional markup is needed for any of the LI elements to achieve the hanging indentation.

This is not a complicated example, admittedly, but often, even complex sections of a page can be reached with no additional markup. This requires expanding your selectors beyond children, while keeping them more specific than generic descendants.

Grandchild

CSS does not define a "grandchild" selector. I'm refering to the results of selecting the child of a child. This limits selection to the 2nd generation only, to grandchildren, just as the child selector limits selection to the 1st generation. (You could keep going, of course, and limit selection to any generation of descendants as far down the tree as necessary.)

Consider a DIV identified as "plant". Assume it contains several elements: headings, paragraphs, and nested lists. The next selector targets any LI which is the child of UL which is the child of "plant".

#plant > ul > li

This allows you to declare color, margin, padding, font-weight, etc. on LI's which are "plant's" grandchildren. Then you can take a simple nested list, like this:

and drop it into the "plant" DIV, with this result:

Oregon Grape

Scientific name: Mahonia aquifolium

Oregon Grape is the state flower of Oregon, best known for its production of blue-black berries which somewhat resemble clusters of grapes.

  • Taxonomy
    • Family: Berberidaceae
    • Type: Evergreen shrubs
    • Native: Yes
  • Plant Requirements
    • Zone: 5 to 9
    • Sun: Partial shade
    • Moisture: Dry

(The data above come from WSU's Pacific Northwest plants database.)

Again, you don't tweak the markup — the list receives its style simply by existing inside "plant".

Look at the selector again. Both > symbols are essential.

#plant > ul > li

Without the first, every nested UL would be selected as a descendant. Without the 2nd, every LI would be selected for the same reason. If you omit either one, the color, borders, and spacing will propogate through all list items, no matter how deeply nested.

As for the horizontal alignment beneath "Taxonomy" and "Plant Requirements", a 2nd selector,

#plant ul ul > li > b

targets the nested lists' bold children (the great-great-great-grandchildren of "plant"). These bold elements are floated left and assigned a width, causing subsequent text to line up to the right. (Note: the selector targets only these bold children, so an explanatory paragraph inside the nested list could employ bold with no strange spacing.)

If you need ordered and unordered lists to be styled similarly, you can replace UL with the universal selector in the rules above. For example:

#plant > * > li

For the next strategy, the universal selector is indispensible.

Beyond Child

During the many years that Internet Explorer did not support selecting by "child", you could mimic the selector by declaring 2 rules: the 1st for descendants, the 2nd for descendants after children. The 2nd rule curtailed the scope of the first, limiting its selection to children, only.

For example, the next rules set all LI's red, and all LI's after the 1st generation to black.

ul li {
  color: red;
  } 
ul * li {
  color: black
  } 

The combination of the 2 rules, limits red to children of the list. If that's your goal, though, the child selector achieves the same result more economically:

ul > li {
  color: red;
  }

But sometimes you need to skip over children, and select all subsequent descendants. This strategy is handy with lists, which can contain several generations. In the next example, a "coffee" DIV contains an unordered list. "Grandchild" selection targets its list items:

#coffee > ul > li

Each outer list item contains another list, whose items are selected this way:

#coffee > ul * li

The "grandchild" and "beyond child" selections above allow you to style the outer and inner lists individually. In this case, the inner list is arranged horizontally, thanks to declarations for float and width.

As a result, the unstyled list that begins this way:

becomes this when dropped into the "coffee" DIV:

Peru Organic Cusco Canelon

Tomas Ovalle

Tomas is a producer in the Canelon area of Cusco, and a member of the Capacy cooperative that we work with in this area. In fact, he is one of the founding members and his coffees always seem to be in the top tier of the lots I cup from this association.

  • Origin
    • Country: Peru
    • Region: Canelon, Cusco
    • Mark: Farmer: Tomas Ovalle Arredondo
  • Harvest
    • Processing: Wet Processed
    • Crop: Jan 2010
    • Varietal: Typica
  • Characteristics
    • Prime Attribute: Medium intensity / Milk chocolate, mild fruits, body, balance.
    • Roast: City+ to Full Citry+. C+ roasts will have a nutty accent and is my favorite (sweetest) roast. FC+ is simpler, with nice chocolate.
    • Compare to: Interestingly, has some flavor aspects of Oaxaca coffees from Mexico, which are also Typica cultivar.

(The text above is extracted from a review by Tom Owens, owner of Sweet Maria's, the background image from Stock.XCHNG.)

The "beyond child" selection

#coffee > ul * li

lets the universal selector stand in for the first-level LI. It then relies on the fact that the style will propogate to any LI's descended from it. You could achieve the same result with this more specific selection, if you wanted:

#coffee > ul li ul li
     (or)
#coffee > ul li * li

(The universal selector in the latter of the 2 rules allows you to use either OL or UL in the nested list.)

Regardless of how you make the selection, the rules apply to any subsequently nested lists, floating them, too. If you need to prevent this, you'd abandon the beyond child selection, and gang up several child selectors to target only the LI's of the 2nd-level list :

#coffee > ul > li > * > li

:Nth-Child

Here we venture into CSS3 territory, whose selectors are well supported in current versions of Firefox, Chrome, Opera, and Safari. Unfortunately, cash-starved Microsoft is lagging behind, focusing on better CSS2 support in its IE8 browser. Hence, IE does not support the ":nth-child" selector. Pity. It's powerful, making zebra-striped tables simple to create, without the need for extra classes or javascript.

These two rules:

tr:nth-child(even) {
  background: #fff;
  }
tr:nth-child(odd) {
  background: #edebd5;
  }

create alternating background colors for table rows:

type of selector example selects
element p <p>
pseudo element :link <a href="??">
    user action :hover <a href="??">
class membership .note <?? class="note">
p.note <p class="note">
id #menu <?? id="menu">
ul#menu <ul id="menu">
attribute p[class] <p class="??">
*[id] <?? id="??">
descendant div p <div><p>

You can combine "tr:nth-child" with "td:nth-child":

tr:nth-child(odd) td:nth-child(odd),
tr:nth-child(even) td:nth-child(even) {
  background: #666 url(img/black_sq.png);
  }
tr:nth-child(odd) td:nth-child(even),
tr:nth-child(even) td:nth-child(odd) {
  background: #fff url(img/white_sq.png);
  }

to produce this:

Or, you can select individual children specifically. The rules below:

ul > li:nth-child(1) {
  background: #feff7f;
  }
ul > li:nth-child(2) {
  background: #b8cd23;
  }
ul > li:nth-child(3) {
  background: #78cbed;
  }

set the backgrounds for each child LI of the outer list:

Nth-child is far more powerful than these simple examples suggest. For a good overview of it and other CSS3 selectors, try 456 Berea St or sitepoint.

NOTE! Chrome v.2 is buggy about spaces in ":nth-child". The selectors below are equivalent to :nth-child(odd). Both are valid:

:nth-child(2n+1)
:nth-child(2n + 1)

But Chrome only recognizes the 1st.

Adjacent Sibling

Notice that this line differs from most others. It's styled by context, too, but it receives its style by proximity, not descent. The adjacent-sibling selector and the pseudo element :first-line make this possible.

The style rule below selects the first line of any paragraph that immediately follows an H2:

h2 + p:first-line {
  font-variant: small-caps;
  }

This particular effect might not interest you, but the adjacent sibling should. Its ability to select by context fixes the same potential problem we saw above when list items receive special spacing or borders.

Say you add a top border to H3 headings, to set them apart more pointedly from regular paragraphs. If an H3 follows an H2 you'll most likely want to defeat the border.

Creating a "notop" class and inserting class attributes:

<h2>Title</h2>
<h3 class="notop">Heading</h3>

fixes the problem, but at the cost of increased maintenance.

An adjacent sibling fixes this more simply. The rule below suppresses H3's top border whenever it immediately follows a higher-level heading:

h1 + h3,
h2 + h3 {
  border-top: none;
  }

As before, this approach reduces page-weight, and allows you to shift your headings around without loss of formatting.

Test your CSS rules in several browsers to make sure they render similarly enough to be usable. Google's Chrome, for example, doesn't implement the "text-transform" property, and it renders small-caps smaller than Firefox or IE. The OS/X version of Safari ignores line-spacing when indicated in the "font" property (although the Windows version parses it correctly).

“What about the dropcap?” you might ask. It's made possible by the :first-letter pseudo element. (As you can see, leading punctuation is considered part of the :first-letter.) The effect requires a "float" and a change in font size:

h2 + p:first-letter {
  float: left; 
  font-size: 4em;
  margin: 0 .15% -6px 0;
  padding-top: 8px;
  }

Browsers differ in how they adjust padding, margins, and line-height on :first-letter, so a bit of trial and error goes into finding a solution that looks good enough between the major browsers. (Consult the style rules in "context.css" to see the other declarations for this dropcap.) The point is that an adjacent sibiling selector and the :first-letter pseudo element let you create dropcaps without extraneous markup.

If something other than a paragraph follows your H2 section heading, and you'd like it to receive the same dropcap, you can include it in your rules by adding another selector. The next rules select the paragraphs and H3's that immediately follow H2's:

h2 + p:first-line,
  h2 + h3:first-line { 
  }
h2 + p:first-letter,
h2 + h3:first-letter {
	}

Or, you can use the universal selector:

h2 + *:first-line { }
h2 + *:first-letter { } 

If you'd like the formatting applied to a list that follows your H2, you'll need to combine selectors more precisely. (Valid markup prevents an LI from ever being an adjacent sibling of a heading. A nephew/niece relationship is the closest it can come.)

The next selector pin-points the first LI that's a child of a UL that immediately follows an H2:

h2 + ul > li:first-child:first-line

The selector works this way:

h2 + ul
find a UL that immediately follows an H2
ul > li
look for an LI in UL's children (not in grandchildren, etc.)
li:first-child
find only the LI which is UL's first child

Combining selectors in this way is more complex, but results in simpler documents.

Judicious compromise can't always be avoided, though. On this page, you've noticed that the first-line and first-letter effects aren't limited to new sections. This paragraph, for example, follows a normal paragraph. It marks a shift in focus, not a section. Because it occurs after a normal paragraph, it can't be reached through an adjacent sibling selector.

To style it and others of its ilk, I've added a class selector to the rule:

h2 + p:first-line,
.transition

and assigned the "transition" class to some selected paragraphs:

<p class="transition"> . . . </p>

This flexibility comes at the costs mentioned above. It's the sort of mucking about in the document you'd like to avoid, if possible. So the first question to ask is whether or not HTML already defines an element that will serve your purpose. (You don't want to rely on a class to style paragraphs as headings, for example, when HTML already provides H1 through H6.)

But HTML defines only a handful of elements any document might possess. No predefined element matches the sort of transition I'm trying to flag visually. That might be a good thing. Considering the number of ways a generic paragraph can function (as introduction, development, conclusion, transition, recapitulation, etc.) it would be burdensome to mark each paragraph by specific function. (Attempting to define every paragraph's function unambiguously would be even worse.)

You could use other approaches. You could co-opt another element (H6, maybe) to mark such transitions. But that violates semantic markup. The H6 wouldn't mark a heading at all, just a paragraph with special style. Or, you could wrap the transitional paragraph in a specially marked DIV. That would add even more markup to the page, not less. So using a "class" seems like an OK compromise, here.

(Perhaps I should ask if I need to flag transitions within sections in this visual way.)