StyleSheet

The StyleSheet module normalizes the dynamic creation and modification
of CSS stylesheets on a page. This makes it easy to manage the
development, storage, and reapplication of personalized user
stylesheets. Because it targets styles at the CSS level, it also
allows you to modify styles applied to a CSS pseudo-element such as
p::first-letter, or pseudo-class such as
a:hover.

StyleSheet is capable of creating new stylesheets from scratch or
modifying existing stylesheets held as properties of
<link> or <style> elements. It
should be noted that not all browsers support reading or modifying
external stylesheets from other domains.

Getting Started

To include the source files for StyleSheet and its dependencies, first load
the YUI seed file if you haven't already loaded it.

Next, create a new YUI instance for your application and populate it with the
modules you need by specifying them as arguments to the YUI().use() method.
YUI will automatically load any dependencies required by the modules you
specify.

<script>
// Create a new YUI instance and populate it with the required modules.
YUI().use('stylesheet', function (Y) {
// StyleSheet is available and ready for use. Add implementation
// code here.
});
</script>

Be aware that the Same Origin
policy prevents access in some browsers to the style data of
<link> elements with hrefs pointing to
other domains. Attempts to seed a Y.StyleSheet instance with
a cross-domain <link> may result in a security
error.

Getting a StyleSheet by registered name

Y.StyleSheet supports registering instances by name allowing
them to be recalled by that same name elsewhere in your code. Internally,
Y.StyleSheet maintains a registry of all created StyleSheet
instances, using a unique generated id that the host node is tagged with.
This allows future attempts to create a StyleSheet instance from the same
node to return the previously created instance associated with that id.

Register a StyleSheet instance manually using the static
register method or pass the desired name as a second parameter
to the constructor.

If an unregistered name is passed as the first argument to the
constructor, a new empty StyleSheet will be created and registered with
that name. This allows you to use the following code pattern:

// Whichever of these executes first, an empty StyleSheet will be created
// and registered as 'MyApp'.
// In one area of your app
Y.StyleSheet('MyApp').set('.module .messages', { display: 'none' });
//...
// In another area of your app
Y.StyleSheet('MyApp').unset('.module .messages','display');

Summary of how the constructor handles the first argument

When nothing is passed as the first argument, a new StyleSheet instance is
created.

When a <style> or <link> element is
passed as the first argument, it is inspected for the id stamp that
StyleSheet tags known host nodes with. If it finds one, it will return the
associated StyleSheet from the registry. If not, it will stamp the node
and seed the instance from the node's CSS content.

When a string is passed as the first argument, StyleSheet does the
following things in order:

Check the registry for an instance associated to that name. If found,
return the instance.

Check the DOM for a <style> or
<link> node with that id. If found, check the
registry for an instance associated to its tagged id if present. If
found, return that instance. If not, use that node to seed a new
StyleSheet instance.

Check the string for a curly brace { character. If found, create a new
instance seeded with the string as initial cssText.

Create a new empty StyleSheet and register the instance by the provided
string.

Creating and modifying CSS style rules

The core method of StyleSheet instances is set(selector,
style_properties). It will create or alter a CSS rule using the
property:value pairs in style_properties targeting the
provided selector. In essence, it looks very much like
natural CSS syntax, except style properties must be in JavaScript's
camelCase.

Rather than continually add new rules that will override one another,
StyleSheet manages one rule per selector and modifies them in place. This
may be relevant if you have two or more rules with selectors of the same
specificity.

As with regular CSS syntax, comma-separated selectors are supported, but
internally StyleSheet splits them up into individual rules because browser
support for multiple selectors is not consistent. This means calling
set(..) with such a selector string will incur multiple
repaints or reflows, but limited to the number of atomic
selectors.

Some style properties are normalized

Two style properties have differing implementation between browsers, namely
float and opacity. StyleSheet instances will
normalize these properties for you.

Because "float" is a reserved word in JavaScript, it is supported
by the name cssFloat in W3C compliant browsers and
styleFloat in IE. StyleSheet will accept any of these to set
the float property.

// Any of these will work
Y.StyleSheet('MyApp').set('.foo', {
"float" : "left", // "float" must be quoted
cssFloat : "right",
styleFloat : "none"
});

IE does not support the opacity style property, but has
equivalent functionality offered by its proprietary filter
property, though using a different value syntax. StyleSheet will translate
opacity to filter for IE, but it will
not translate filter to opacity for
W3C-compliant browsers.

Removing and resetting CSS style rules

When you want to remove a particular rule or style property from affecting
the cascade, use unset(selector,propert[y|ies]).

unset(..) can be called in any of the following ways, with the
noted result:

unset('.foo') — removes the rule associated to the
selector entirely.

unset('.foo','font') — unsets the font
property and any child properties (e.g.
'font-weight','font-variant','font-size','line-height', and
'font-family'). If there are no set properties left, the rule is
removed.

unset('.foo',['font','border',...]) — same as above,
but the rule is modified only once with the final applicable
cssText.

It is important to note that there is a difference between setting a style
property to its default value and unsetting it. The former can be achieved
by calling set(selector, { property: "auto" }) (or
the respective default value for that property).

However, as the CSS is reapplied to the page, the "auto" value
will override any value for that property that may have cascaded in from
another rule. This is different than removing the property assignment
entirely, as this allows cascading values through.

Y.StyleSheet('MyApp').set('.foo', { background: 'auto' });
// is NOT the same as
Y.StyleSheet('MyApp').unset('.foo','background');

A note on selector strings

Though the StyleSheet Utility takes selector strings as input to its API,
it does not leverage the YUI selector engine. YUI's selector
functionality supplements native CSS support for DOM access, but
accomplishes this through efficient DOM traversal. Since the StyleSheet
Utility uses the browser's built-in stylesheet and rule objects, it can not
handle selectors that are not supported by the browser's native CSS
parser.

// This will not cause a style change in IE 6, for example
Y.StyleSheet('MyApp').set('input[type=checkbox]:checked', {
verticalAlign : 'super'
});

Disabling and enabling a StyleSheet

Disabling a StyleSheet effectively turns it off; no CSS from that
stylesheet is applied to the page. Disabling a StyleSheet does not remove
the host node from the page, and style can be reapplied by enabling the
StyleSheet again.

When StyleSheets are disabled, it is still possible to change their style
rules via set and unset.

Use to translate an object of style property:value pairs to a single cssText string. The optional second argument is a cssText string of a style's "before" state.

Y.StyleSheet.toCssText is used internally to assemble the
cssText strings for updating the stylesheet rules. However,
it may also be helpful for avoiding reflow overhead when substantially
modifying a single element's style.

How Y.StyleSheet works

Browsers grant access via the DOM API to stylesheets included in markup as
<link> or <style> elements. Despite
differing implementations across the browser spectrum, they all support
adding, removing, and modifying CSS rules.

CSS rules are comprised of a selector and collection of style
property:value pairs enclosed in curly braces.

In JavaScript, each rule object has a selectorText property
and a style property that operates in the same way as the
style property on regular DOM elements, such as
<p> or <strong> elements.

Arguably the most valuable property of the style collection is
cssText which corresponds to the serialized summary of
property:value pairs applied by this collection (e.g. "font-size: 100%;
color: #FF0000;"). The reason this property is important is that
modifications to the string value will cause changes to repopulate the
individual style properties while only triggering a single repaint or
reflow by the browser.

Y.StyleSheet leverages this mechanism in addition to applying
modifications at the CSS rule level rather than modifying each targeted DOM
node directly. This means changing multiple style properties on multiple
elements (that can be identified by a single selector) will only ever incur
one repaint or reflow.

It must be noted that all reflows are not the same. The scope of a reflow
is greatly affected by what element triggered it. For example, changing a
style of an absolutely positioned element will trigger a very limited
reflow because the browser understands that not much could change
as a result. Stylesheet modifications on the other hand are not tied to an
element, but the page as a whole. The CSS cascade must be recalculated and
applied, resulting in a full page reflow. This means it may be more
efficient to individually update many elements than to change the
stylesheet.

Known Issues

Unable to set style values with
!important.

CSS syntax for declaring that a style value has important
priority is to include the !important flag after the
value.

.some-class {
color: #000 !important;
}

However, the DOM API for modifying stylesheets does not parse out the
!important flag from the assigned value string, and thus
considers the entire string to be an invalid value.

el.style.color = "#000 !important"; // Error

StyleSheet will support !important in the value string in a
future release, but for now the only way to assign an
!important style is by creating a new StyleSheet, passing a
CSS text string to the constructor.