Blow-by-blow analysis of styling-css-04-f

Introduction

This tests a large number of selectors in combination. To pass this test, the CSS2 selectors and the specificity rules must be implemented exactly. The visual result is a 6 by 3 grid of colored rectangles; the columns of the grid are numbered A to F and the rows 1 to 3 for ease of reference. The correct rendering is shown in the following image:

image of a correct test result

The style rules

These are the style rules, numbered for ease of reference. In addition, the specificity of each rules is shown in comments /* like this */.

  1. * {stroke:red; stroke-width:1;} /* 0 */
  2. text {stroke:none; fill:black;} /* 1 */
  3. rect {stroke:black; stroke-dasharray:none;} /* 1 */
  4. #test-frame {fill:none} /* 100 */
  5. g#alpha {fill:blue} /* 101 */
  6. a#alpha {fill:red} /* 101 */
  7. #alpha * rect {fill:green} /* 101 */
  8. #alpha-2 > rect {fill:orange} /* 101 */
  9. #beta rect {fill:gold} /* 101 */
  10. g#gamma * g * * rect {fill:red} /* 103 */
  11. g#gamma * * rect {fill:purple} /* 102 */
  12. [stroke-width="1.0001"] {fill:blue} /* 10 */
  13. g#delta rect[stroke-width="1.0002"] {fill:green} /* 112 */
  14. g#delta > rect[stroke-width="1.0003"] {fill:orange} /* 102 */
  15. #delta + g > * {fill:gold} /* 101 */
  16. g#delta + g > rect + rect {fill:purple} /* 104 */
  17. #delta + g#epsilon * rect:first-child {fill:red} /* 202 */
  18. #zeta [cursor] {fill:blue} /* 110 */
  19. g#zeta [cursor="help"] {fill:green} /* 111 */
  20. g#zeta [rx~="3E"] {fill:orange} /* 111 */
  21. g#epsilon + g [stroke-dasharray|="3.1415926"] {fill:gold} /* 112 */
  22. g#epsilon + g > rect.hello {fill:purple} /* 113 */
  23. g#eta rect:first-child {fill:red} /* 102 */

The first four rules can be briefly discussed and then ignored. Rule 1 makes everything have a red stroke and a stroke-width of 1. Rue 2 sets the stroke to none (thus, it does not matter what the stroke is, as it is not drawn) and sets a black fill, for text. Rule 3 sets a black stroke and an empty dasharray for rects. As the entire test consists of rects and text, all elements used in this test are thus affected by these rules. For the purposes of the text, however, it means that by default each rect has a black fill, and a black stroke with a width of 1. Finally, rule 4 sets the test frame to have no fill (or else, it would be solid black). The remainder of the discussion will be concerned only with rules 5 to 23.

Test analysis - first row

For each portion of the test, sufficient of the source is given for the structure to be seen. Each of the 18 test rects has an id, for example B2, which allows its position in the rendered result to be clearly seen.

A1, B1, C1

 <g id="alpha">
      <rect x="30" y="70" width="67.5" height="67.5" id="A1"/>
      <g id="alpha-1">
          <rect x="100" y="70" width="67.5" height="67.5" id="B1" />
      </g>
      <g id="alpha-2">
          <rect x="170" y="70" width="67.5" height="67.5" id="C1"/>
      </g>
 </g>

For rect A1, the matching rule is:

 5. g#alpha {fill:blue} /* 101 */

therefore, A1 has a blue fill.

For rect B1, the matching rules are:

  5. g#alpha {fill:blue} /* 101 */
  7. #alpha * rect {fill:green} /* 101 */

these both have the same specificity, so document order is used, therefore, B1 has a green fill.

For rect C1, the matching rules are:

 5. g#alpha {fill:blue} /* 101 */
 7. #alpha * rect {fill:green} /* 101 */
 8. #alpha-2 > rect {fill:orange} /* 101 */

these all have the same specificity, so document order is used, therefore, C1 has an orange fill.

D1

 <g id="beta">
    <g>
        <g>
           <g>
               <g>
                   <g>
                        <rect x="240" y="70" width="67.5" height="67.5" id="D1"/>
                    </g>
                </g>
            </g>
        </g>
    </g>
 </g>           

For rect D1, the matching rule is:

 9. #beta rect {fill:gold} /* 101 */

therefore, D1 has a gold fill.This tests that an arbitrary number of interposing elements are allowed for the ancestor combinator, (not just immediate children)

E1, F1

<g id="gamma">
    <g>
        <g>
            <rect x="310" y="70" width="67.5" height="67.5" id="E1"/>
             <g>
                 <g>
                     <rect x="380" y="70" width="67.5" height="67.5" id="F1"/>
                 </g>
             </g>
         </g>
     </g>
</g>

For rect E1, the matching rule is:

11. # g#gamma * * rect {fill:purple} /* 102 */

therefore, as there are at least two interposing elements between the g with id="gamma" and the rect E1, it has a purple fill.

For rect F1, the matching rules are:

10. g#gamma * g * * rect {fill:red} /* 103 */
11. g#gamma * * rect {fill:purple} /* 102 */

as there are four interposing elements between the g with id="gamma" and the rect E1, and the second interposing element is a g, both rules match (these are ancestor, not sibling, selectors). The two rules have different specificity, therefore the more specific rule 10 wins despite being earlier in document order in the stylesheet. Therefore the rect has a red fill.

Test analysis - second row

A2, B2, C2

<g id="delta">
    <rect x="30" y="140" width="67.5" height="67.5" stroke-width="1.0001" id="A2"/>
    <rect x="100" y="140" width="67.5" height="67.5" stroke-width="1.0002" id="B2"/>
    <rect x="170" y="140" width="67.5" height="67.5" stroke-width="1.0003" id="C2"/>
</g>

For rect A2, the matching rule is:

12. [stroke-width="1.0001"] {fill:blue} /* 10 */

therefore the fill is blue. This tests attribute value selectors.

For rect B2, the matching rule is:

13. g#delta rect[stroke-width="1.0002"] {fill:green} /* 112 */

giving rect A2 a green fill. This tests attribute value selectors in combination with element and ancestor selectors.

For rect C2, the matching rule is:

14. g#delta > rect[stroke-width="1.0003"] {fill:orange} /* 102 */

therefore the rect C2 has an orange fill. This tests attribute value selectors plus child combinators.

D2, E2, F2

<g id="delta"> ... as above ...
</g>
<g id="epsilon">
    <rect x="240" y="140" width="67.5" height="67.5" id="D2"/>
    <rect x="310" y="140" width="67.5" height="67.5" id="E2"/>
    <g>
        <rect x="380" y="140" width="67.5" height="67.5" id="F2"/>
    </g>
</g>

The following rule matches D2 because it is the immediate child of a g which is a sibling of the group with id="delta"

15. #delta + g > * {fill:gold} /* 101 */

therefore the rect has a gold fill, testing adjacent sibling selectors.

For rect E2, the following rule matches

16. g#delta + g > rect + rect {fill:purple} /* 104 */

therfore the rect has a purple fill; this tests multiple adjacent sibling selectors.

For rect F2, the following matches since the * matches the g element which is a parent of F2 and the rect F2 is that g element's first child.

17. #delta + g#epsilon * rect:first-child {fill:red} /* 202 */

Therefore the rect has a red fill, testing the :first-child pseudo-class. (The other pseudo-classes are tested in different tests, not this one).

Test analysis - third row

A3, B3, C3, D3, E3

<g id="zeta">
      <rect x="30" y="210" width="67.5" height="67.5" cursor="default" id="A3"/>
      <rect x="100" y="210" width="67.5" height="67.5" cursor="help" id="B3"/>
      <rect x="170" y="210" width="67.5" height="67.5" 
          stroke-dasharray="5, 3.14" id="C3" style="fill:orange"/>
      <rect x="240" y="210" width="67.5" height="67.5" rx="3E-6" 
          stroke-dasharray="3.1415926,8" id="D3" style="fill:gold"/>
      <rect x="310" y="210" width="67.5" height="67.5" class="hello" id="E3"/>
</g>

For rect A3, the following rule matches because of the cursor attribute being present (regardless of the attribute value):

18. #zeta [cursor] {fill:blue} /* 110 */

For B3, the following rule matches because of the cursor attribute having the value "help"

19. g#zeta [cursor="help"] {fill:green} /* 111 */

For C3, the presence of the scientific notation on rx, which has a hyphen in the value, means that the following rule matches (selectors considering the value as a hyphen separated list of tokens):

20. g#zeta [rx~="3E"] {fill:orange} /* 111 */

For D3, the presence of the space separated list of tokens on stroke-dasharray, means that the following rule matches. This subtest also tests sibling selectors, because the group before the one with id="zeta" has id="epsilon".

21. g#epsilon + g [stroke-dasharray|="3.1415926"] {fill:gold} /* 112 */

Note that for C3 and D3, the rectangles render with a solid stroke not a dashed one; the presentation attributes have specificity zero while the following rule has a higher specificity of one:

3. rect {stroke:black; stroke-dasharray:none;} /* 1 */

For E3, the following rule matches because the previous group had id="epsilon", and the present g has a child whose class is "hello". This tests single class selectors, plus sibling and child selectors.

22. g#epsilon + g > rect.hello {fill:purple} /* 113 */

F3

<g id="eta">
    <rect x="380" y="210" width="67.5" height="67.5" id="F3"/>
</g>

For F3, the group with id="eta" does indeed have a first child which is a rect, so the following rule matches.

23. g#eta rect:first-child {fill:red} (/* 102 */