[size=large][size=large]This document provides guidelines for optimizing CSS code for use in the Mozilla UI. It applies to CSS for XUL code, not for general-purpose HTML pages; see the main CSS section for information on general-purpose CSS.
The first section is a general discussion of how the Mozilla style system categorizes rules. The following sections contain guidelines for writing rules that take advantage of Mozilla's style system implementation.
How the style system breaks up rules
The style system breaks rules up into four primary categories:
1.ID Rules
2.Class Rules
3.Tag Rules
4.Universal Rules
It is critical to understand these categories, as they are the fundamental building blocks of rule matching.
I use the term key selector in the paragraphs that follow. The key selector is the last part of the selector (the part that matches the element being matched, rather than its ancestors).
For example, in the rule…
view plaincopy to clipboardprint?
01.a img, div > p, h1 + [title] {…}
…the key selectors are img, p, and [title].
ID Rules
The first category consists of those rules that have an ID selector as their key selector.
Example
view plaincopy to clipboardprint?
01.button#backButton {…} /* This is an ID-categorized rule */
02.#urlBar[type="autocomplete"] {…} /* This is an ID-categorized rule */
03.treeitem > treerow > treecell#myCell:active {…} /* This is an ID-categorized rule */
Class Rules
If a rule has a class specified as its key selector, then it falls into this category.
Example
view plaincopy to clipboardprint?
01.button.toolbarButton {…} /* A class-based rule */
02..fancyText {…} /* A class-based rule */
03.menuitem > .menu-left[checked="true"] {…} /* A class-based rule */
Tag Rules
If no class or ID is specified as the key selector, the next candidate is the tag category. If a rule has a tag specified as its key selector, then the rule falls into this category.
Example
view plaincopy to clipboardprint?
01.td {…} /* A tag-based rule */
02.treeitem > treerow {…} /* A tag-based rule */
03.input[type="checkbox"] {…} /* A tag-based rule */
Universal Rules
All other rules fall into this category.
Example
view plaincopy to clipboardprint?
01.[hidden="true"] {…} /* A universal rule */
02.* {…} /* A universal rule */
03.tree > [collapsed="true"] {…} /* A universal rule */
How the Style System Matches Rules
The style system matches rules by starting with the key selector, then moving to the left (looking for any ancestors in the rule’s selector). As long as the selector’s subtree continues to check out, the style system continues moving to the left until it either matches the rule, or abandons because of a mismatch.
The most fundamental concept to learn is this rule filtering. The categories exist in order to filter out irrelevant rules (so the style system doesn’t waste time trying to match them).
This is the key to dramatically increasing performance. The fewer rules required to check for a given element, the faster style resolution will be.
For example, if an element has an ID, then only ID rules that match the element’s ID will be checked. Only Class Rules for a class found on the element will be checked. Only Tag Rules that match the tag will be checked. Universal Rules will always be checked.
Guidelines for Efficient CSS
Avoid Universal Rules
Make sure a rule doesn’t end up in the universal category!
Don’t qualify ID Rules with tag names or classes
If a rule has an ID selector as its key selector, don’t add the tag name to the rule. Since IDs are unique, adding a tag name would slow down the matching process needlessly.
Exception: When it’s desirable to change the class of an element dynamically in order to apply different styles in different situations, but the same class is going to be shared with other elements.
BAD button#backButton {…} BAD .menu-left#newMenuIcon {…} GOOD #backButton {…} GOOD #newMenuIcon {…}
Don’t qualify Class Rules with tag names
The previous concept also applies here. All class names are unique.
One convention you can use is to include the tag name in the class name. However, this may cost some flexibility; if design changes are made to the tag, the class names must be changed as well. (It’s best to choose strictly semantic names, as such flexibility is one of the aims of separate stylesheets.)
BAD treecell.indented {…} GOOD .treecell-indented {…} BEST .hierarchy-deep {…}
Use the most specific category possible
The single biggest cause of slowdown is too many rules in the Tag Category. By adding classes to our elements, we can further subdivide these rules into Class Categories, which eliminates time spent trying to match rules for a given tag.
BAD treeitem[mailfolder="true"] > treerow > treecell {…} GOOD .treecell-mailfolder {…}
Avoid the descendant selector
The descendant selector is the most expensive selector in CSS. It is dreadfully expensive—especially if the selector is in the Tag or Universal Category.
Frequently, what is really desired is the child selector. Using the descendant selector is banned in User Interface CSS without the explicit approval of your skin’s module owner.
BAD treehead treerow treecell {…} BETTER, BUT STILL BAD (see next guideline) treehead > treerow > treecell {…}
Tag Category rules should never contain a child selector
Avoid using the child selector with Tag Category rules. This will dramatically lengthen the match time (especially if the rule is likely to be matched) for all occurrences of that element.
BAD treehead > treerow > treecell {…} GOOD .treecell-header {…}
Question all usages of the child selector
Exercise caution when using the child selector. Avoid it if you can.
In particular, the child selector is frequently used with RDF trees and menus like so:
BAD treeitem[IsImapServer="true"] > treerow > .tree-folderpane-icon {…}
Remember that REF attributes can be duplicated in a template! Take advantage of this. Duplicate RDF properties on child XUL elements in order to change them based on the attribute.
GOOD .tree-folderpane-icon[IsImapServer="true"] {…}
Rely on inheritance
Learn which properties inherit, and allow them to do so!
For example, XUL widgets are explicitly set up such that a parent’s list-style-image or font rules will filter down to anonymous content. It’s not necessary to waste time on rules that talk directly to anonymous content.
BAD #bookmarkMenuItem > .menu-left { list-style-image: url(blah) } GOOD #bookmarkMenuItem { list-style-image: url(blah) }
In the above example, the desire to style anonymous content (without leveraging the inheritance of list-style-image) resulted in a rule that was in the Class Category, when the rule should have ended up in the ID Category—the most specific category of all!
Remember: Elements all have the same classes—especially anonymous content!
The above “bad” rule forces every menu’s icons to be tested for containment within the bookmarks menu item. Since there are many menus, this is extraordinarily expensive. Instead, the “good” rule limits the testing to the bookmarks menu.
Use -moz-image-region!
Putting a bunch of images into a single image file and selecting them with -moz-image-region performs significantly better than putting each image into its own file.
Use scoped stylesheets
If you specify a stylesheet as an XBL resource, the styles only apply to the bound elements and their anonymous content. This reduces the inefficiency of universal rules and child selectors because there are fewer elements to consider.
[size=medium][/size][/size][size=x-large][/size][/size]
The first section is a general discussion of how the Mozilla style system categorizes rules. The following sections contain guidelines for writing rules that take advantage of Mozilla's style system implementation.
How the style system breaks up rules
The style system breaks rules up into four primary categories:
1.ID Rules
2.Class Rules
3.Tag Rules
4.Universal Rules
It is critical to understand these categories, as they are the fundamental building blocks of rule matching.
I use the term key selector in the paragraphs that follow. The key selector is the last part of the selector (the part that matches the element being matched, rather than its ancestors).
For example, in the rule…
view plaincopy to clipboardprint?
01.a img, div > p, h1 + [title] {…}
…the key selectors are img, p, and [title].
ID Rules
The first category consists of those rules that have an ID selector as their key selector.
Example
view plaincopy to clipboardprint?
01.button#backButton {…} /* This is an ID-categorized rule */
02.#urlBar[type="autocomplete"] {…} /* This is an ID-categorized rule */
03.treeitem > treerow > treecell#myCell:active {…} /* This is an ID-categorized rule */
Class Rules
If a rule has a class specified as its key selector, then it falls into this category.
Example
view plaincopy to clipboardprint?
01.button.toolbarButton {…} /* A class-based rule */
02..fancyText {…} /* A class-based rule */
03.menuitem > .menu-left[checked="true"] {…} /* A class-based rule */
Tag Rules
If no class or ID is specified as the key selector, the next candidate is the tag category. If a rule has a tag specified as its key selector, then the rule falls into this category.
Example
view plaincopy to clipboardprint?
01.td {…} /* A tag-based rule */
02.treeitem > treerow {…} /* A tag-based rule */
03.input[type="checkbox"] {…} /* A tag-based rule */
Universal Rules
All other rules fall into this category.
Example
view plaincopy to clipboardprint?
01.[hidden="true"] {…} /* A universal rule */
02.* {…} /* A universal rule */
03.tree > [collapsed="true"] {…} /* A universal rule */
How the Style System Matches Rules
The style system matches rules by starting with the key selector, then moving to the left (looking for any ancestors in the rule’s selector). As long as the selector’s subtree continues to check out, the style system continues moving to the left until it either matches the rule, or abandons because of a mismatch.
The most fundamental concept to learn is this rule filtering. The categories exist in order to filter out irrelevant rules (so the style system doesn’t waste time trying to match them).
This is the key to dramatically increasing performance. The fewer rules required to check for a given element, the faster style resolution will be.
For example, if an element has an ID, then only ID rules that match the element’s ID will be checked. Only Class Rules for a class found on the element will be checked. Only Tag Rules that match the tag will be checked. Universal Rules will always be checked.
Guidelines for Efficient CSS
Avoid Universal Rules
Make sure a rule doesn’t end up in the universal category!
Don’t qualify ID Rules with tag names or classes
If a rule has an ID selector as its key selector, don’t add the tag name to the rule. Since IDs are unique, adding a tag name would slow down the matching process needlessly.
Exception: When it’s desirable to change the class of an element dynamically in order to apply different styles in different situations, but the same class is going to be shared with other elements.
BAD button#backButton {…} BAD .menu-left#newMenuIcon {…} GOOD #backButton {…} GOOD #newMenuIcon {…}
Don’t qualify Class Rules with tag names
The previous concept also applies here. All class names are unique.
One convention you can use is to include the tag name in the class name. However, this may cost some flexibility; if design changes are made to the tag, the class names must be changed as well. (It’s best to choose strictly semantic names, as such flexibility is one of the aims of separate stylesheets.)
BAD treecell.indented {…} GOOD .treecell-indented {…} BEST .hierarchy-deep {…}
Use the most specific category possible
The single biggest cause of slowdown is too many rules in the Tag Category. By adding classes to our elements, we can further subdivide these rules into Class Categories, which eliminates time spent trying to match rules for a given tag.
BAD treeitem[mailfolder="true"] > treerow > treecell {…} GOOD .treecell-mailfolder {…}
Avoid the descendant selector
The descendant selector is the most expensive selector in CSS. It is dreadfully expensive—especially if the selector is in the Tag or Universal Category.
Frequently, what is really desired is the child selector. Using the descendant selector is banned in User Interface CSS without the explicit approval of your skin’s module owner.
BAD treehead treerow treecell {…} BETTER, BUT STILL BAD (see next guideline) treehead > treerow > treecell {…}
Tag Category rules should never contain a child selector
Avoid using the child selector with Tag Category rules. This will dramatically lengthen the match time (especially if the rule is likely to be matched) for all occurrences of that element.
BAD treehead > treerow > treecell {…} GOOD .treecell-header {…}
Question all usages of the child selector
Exercise caution when using the child selector. Avoid it if you can.
In particular, the child selector is frequently used with RDF trees and menus like so:
BAD treeitem[IsImapServer="true"] > treerow > .tree-folderpane-icon {…}
Remember that REF attributes can be duplicated in a template! Take advantage of this. Duplicate RDF properties on child XUL elements in order to change them based on the attribute.
GOOD .tree-folderpane-icon[IsImapServer="true"] {…}
Rely on inheritance
Learn which properties inherit, and allow them to do so!
For example, XUL widgets are explicitly set up such that a parent’s list-style-image or font rules will filter down to anonymous content. It’s not necessary to waste time on rules that talk directly to anonymous content.
BAD #bookmarkMenuItem > .menu-left { list-style-image: url(blah) } GOOD #bookmarkMenuItem { list-style-image: url(blah) }
In the above example, the desire to style anonymous content (without leveraging the inheritance of list-style-image) resulted in a rule that was in the Class Category, when the rule should have ended up in the ID Category—the most specific category of all!
Remember: Elements all have the same classes—especially anonymous content!
The above “bad” rule forces every menu’s icons to be tested for containment within the bookmarks menu item. Since there are many menus, this is extraordinarily expensive. Instead, the “good” rule limits the testing to the bookmarks menu.
Use -moz-image-region!
Putting a bunch of images into a single image file and selecting them with -moz-image-region performs significantly better than putting each image into its own file.
Use scoped stylesheets
If you specify a stylesheet as an XBL resource, the styles only apply to the bound elements and their anonymous content. This reduces the inefficiency of universal rules and child selectors because there are fewer elements to consider.
[size=medium][/size][/size][size=x-large][/size][/size]