An experimental bit of code that enables encapsulation of CSS styles in declarative at-rule state
blocks that can be dynamically activated/deactivated.
States are declaratively defined by adding a specially formed @supports
rule to any same-domain stylesheet or inline <style>
tag. This is what it looks like to declare a state
block:
@supports (state: foo) {
body {
background: red;
}
}
@supports (state: bar) {
body {
background: green;
}
}
Activating a state is rather simple, you just call the document.state.activate()
method with whatever state you wish to activate:
document.state.activate('foo');
You can have multiple states active at once, and if you want to know which states are active at any time, just use the getter var activeStates = document.states.active
, which returns an array with the names of all active states.
When a state is activated, the styles in the state block are applied at the same position as the @supports
block they are declared in, meaning they follow the natural positional application and subsequent cascade as any other style block would.
Just like activating a state, call document.state.deactivate()
with whatever state you want to deactivate:
document.state.deactivate('foo');
Because state blocks can only be declared in same-origin and inline style tags, you'll need to go snag the npm serve
package to serve the index.html test page. You can add serve
globally by running npm install serve -g
, then simply run serve
at the command line while inside the repo directory.
The trick behind the scenes is that the CSS parser will retain @supports
rules even if the condition it parses is unknown to it. The document.state
code includes hooks that detect new state
blocks in all stylesheet/style elements and those added dynamically (via methods like CSSStyleSheet.insertRule()
).
When new state blocks are detected, the logic generates a valid @media all {}
block with all the rules declared within the state block and caches it.
Then, upon activation of a state, the code inserts a rule just after the index of the originally declared state block, which immediately applies the containing rules in the position and cascade order you would expect.
NOTE: if a state block is declared and its state is already active, the rules are immediately applied.