|
| 1 | +<h1>CSS Extensions</h1> |
| 2 | +<pre class='metadata'> |
| 3 | +Group: CSSWG |
| 4 | +Shortname: css-extensions |
| 5 | +Level: 1 |
| 6 | +Status: ED |
| 7 | +ED: http://dev.w3.org/csswg/css-extensions |
| 8 | +Editor: Tab Atkins, Google, http://xanthir.com/contact/ |
| 9 | +Abstract: This specification defines methods for authors to extend and enhance various CSS features. |
| 10 | +</pre> |
| 11 | + |
| 12 | +<h2 id='intro'> |
| 13 | +Introduction</h2> |
| 14 | + |
| 15 | + When authoring CSS, |
| 16 | + one often encounters significant repetition in certain features. |
| 17 | + For example, a given media query might be repeated in several places, |
| 18 | + or a selector meant to apply to all heading elements |
| 19 | + requires specifying '':matches(h1, h2, h3, h4, h5, h6)'' in every location that uses it. |
| 20 | + |
| 21 | + This repetition makes stylesheets more verbose and difficult to read, |
| 22 | + and also affects maintenance, |
| 23 | + as the author has to keep each repetition in sync when making any changes. |
| 24 | + |
| 25 | + This specification defines methods for extending several CSS features |
| 26 | + so that a long or repeatedly-used value can be given a short, memorable name instead, |
| 27 | + or a feature can be given a more complex definition controlled by a scripting language. |
| 28 | + This makes stylesheets easier to read, |
| 29 | + and more powerful in general, |
| 30 | + as authors can extend the feature-set of CSS themselves |
| 31 | + rather than waiting for standards bodies to define new features for them. |
| 32 | + |
| 33 | +<h2 id='extension-name'> |
| 34 | +Extension Names</h2> |
| 35 | + |
| 36 | + All extensions defined in this specification use a common syntax for defining their ”names”: |
| 37 | + the <<extension-name>> production. |
| 38 | + An <dfn><extension-name></dfn> is any <a>identifier</a> that starts with two dashes (U+002D HYPHEN-MINUS), |
| 39 | + like ''--foo'', or even exotic names like ''--'' or ''------''. |
| 40 | + The CSS language will never use identifiers of this form for any language-defined purpose, |
| 41 | + so it's safe to use them for author-defined purposes |
| 42 | + without ever having to worry about colliding with CSS-defined names. |
| 43 | + |
| 44 | +<h2 id='custom-selectors'> |
| 45 | +Custom Selectors</h2> |
| 46 | + |
| 47 | + A <dfn>custom selector</dfn> is defined with the ''@custom-selector'' rule: |
| 48 | + |
| 49 | + <pre class='prod'><dfn>@custom-selector</dfn> = @custom-selector <<extension-name>> <<selector>> ;</pre> |
| 50 | + |
| 51 | + This defines a <a>custom selector</a> which is written as a <a spec=selectors>pseudo-class</a> with the given <<extension-name>>, |
| 52 | + and represents a '':matches()'' selector using the provided <<selector>> as its argument. |
| 53 | + |
| 54 | + <div class='example'> |
| 55 | + For example, if an author wanted to easily refer to all heading elements in their HTML document, |
| 56 | + they could create an alias: |
| 57 | + |
| 58 | + <pre> |
| 59 | + @custom-selector :--heading h1, h2, h3, h4, h5, h6; |
| 60 | + |
| 61 | + :--heading { /* styles for all headings */ } |
| 62 | + :--heading + p { /* more styles */ } |
| 63 | + /* etc */ |
| 64 | + </pre> |
| 65 | + </div> |
| 66 | + |
| 67 | +<h3 id='script-custom-selectors'> |
| 68 | +Script-based Custom Selectors</h3> |
| 69 | + |
| 70 | + <div class='issue'> |
| 71 | + This one's more complicated than MQs. |
| 72 | + Brian Kardell came up with a good proposal for evaluating selectors as JS functions that return a boolean, |
| 73 | + which had decent performance characteristics by specifying the qualities of the element it was based on |
| 74 | + (which determined when it would be called). |
| 75 | + |
| 76 | + <pre> |
| 77 | + <script> |
| 78 | + CSS.customSelector.set("_foo", |
| 79 | + {"predicate": function(el){...}, |
| 80 | + "matches": "a"}); |
| 81 | + </script> |
| 82 | + </pre> |
| 83 | + |
| 84 | + "matches" is an optional selector specifying what subset of elements the custom selector is valid for. |
| 85 | + The selector is automatically false for elements that don't match, |
| 86 | + and the predicate isn't called. |
| 87 | + |
| 88 | + By default, the predicate is called whenever there's a mutation in an element that matches the "matches" selector, |
| 89 | + or one of its descendants. |
| 90 | + |
| 91 | + You should be able to suppress the auto-calling, |
| 92 | + and be able to trigger the predicate to run manually. |
| 93 | + That way you can use mutation listeners manually to only call the predicate when necessary. |
| 94 | + |
| 95 | + We should probably offer some sugar for filtering the list of mutations that trigger the predicate to be called. |
| 96 | + Maybe just a list of attributes that you'll be caring about? And/or tagnames? |
| 97 | + |
| 98 | + Maybe let the pseudo-class also accept an argument, |
| 99 | + and pass it (as a serialized string) as a second argument to the predicate. |
| 100 | + '':_foo'' would pass <code>null</code>, |
| 101 | + while '':_foo()'' would pass <code>""</code>. |
| 102 | + </div> |
| 103 | + |
| 104 | +<h3 id='custom-selectors-cssom'> |
| 105 | +CSSOM</h3> |
| 106 | + |
| 107 | + <p class='issue'> |
| 108 | + Fill in. |
| 109 | + |
| 110 | +<h2 id='custom-functions'> |
| 111 | +Custom Functions</h2> |
| 112 | + |
| 113 | + <div class='issue'> |
| 114 | + Interesting possibilities here. |
| 115 | + Definitely need some way to define custom functions in CSS. |
| 116 | + This would, for example, let people define whatever color function they want, |
| 117 | + such as implementing the <a href="http://www.boronine.com/husl/">HUSL</a> color space. |
| 118 | + |
| 119 | + Definitely need a JS interface. |
| 120 | + What options are needed? |
| 121 | + |
| 122 | + Call time/frequency: |
| 123 | + |
| 124 | + <ul> |
| 125 | + <li> |
| 126 | + Default should probably treat the function as a preprocessor, |
| 127 | + calling the JS function once per instance in the stylesheet |
| 128 | + and substituting in the returned value. |
| 129 | + |
| 130 | + <li> |
| 131 | + Should probably have an option to allow calling per element/instance combo, too. |
| 132 | + Gets called more as match results change. |
| 133 | + </ul> |
| 134 | + |
| 135 | + We can take some cues from my thoughts on a random() function. |
| 136 | + It needs per-instance, |
| 137 | + per-element&instance, |
| 138 | + and per "identifier", so you can reuse the same value in multiple spots. |
| 139 | + That last one can probably be handled manually by the JS, |
| 140 | + so we don't have to privilege a particular argument as an identifier. |
| 141 | + |
| 142 | + We'd need to provide the context in which it's used. |
| 143 | + Which property, for example. |
| 144 | + Should we allow them to be used in other places, |
| 145 | + or should we just define more contextual locations as we go? |
| 146 | + That is, should we allow custom-defined functions in @supports with this API, |
| 147 | + or should we add a <code>.customSupports</code> map? |
| 148 | + I suspect that individual cases will have their own useful contextual information, |
| 149 | + so it's better to specialize each instance of custom functions. |
| 150 | + |
| 151 | + How much can we do in pure CSS? |
| 152 | + Being able to substitute values depending on MQs or support queries would be useful. |
| 153 | + To get *real* use out of it, though, I suspect we'd need fuller support for conditionals, |
| 154 | + likely in the form of SASS's ''@if'' or something similar. |
| 155 | + </div> |
| 156 | + |
| 157 | +<h2 id='custom-combinators'> |
| 158 | +Custom Selector Combinators</h2> |
| 159 | + |
| 160 | + <div class='issue'> |
| 161 | + Selectors are made of two pieces: |
| 162 | + simple selectors, |
| 163 | + and combinators. |
| 164 | + We should allow custom combinators too. |
| 165 | + |
| 166 | + This is JS-only, because it's transforming elements, not filtering them, |
| 167 | + and you can't express any useful transformations in pure CSS. |
| 168 | + |
| 169 | + You provide a function which, |
| 170 | + when given an element, |
| 171 | + produces a list of zero or more elements. |
| 172 | + |
| 173 | + For examples, with ''div /--foo/ span'', |
| 174 | + the CSS engine will match the first part of the selector |
| 175 | + and find all the div elements. |
| 176 | + It passes that list to the function registered for the --foo combinator, |
| 177 | + and expects to get a new list of elements returned. |
| 178 | + It then continues on its way, |
| 179 | + filtering that list to include only span elements, etc. |
| 180 | + |
| 181 | + A child combinator would be something like: |
| 182 | + |
| 183 | + <pre> |
| 184 | + CSS.customCombinator.set("--child", function(el) { |
| 185 | + return el.children; |
| 186 | + }); |
| 187 | + </pre> |
| 188 | + |
| 189 | + Then ''div /--child/ span'' would be identical to ''div > span''. |
| 190 | + |
| 191 | + If we generalize a selector with a custom combinator to ''A /--custom/ B'', |
| 192 | + then the UA would automatically call the --custom function |
| 193 | + whenever new elements match ''A''. |
| 194 | + If elements stop matching ''A'', |
| 195 | + it won't bother; |
| 196 | + it'll just drop them from the result. |
| 197 | + |
| 198 | + Alternately, the function could take a list of elements |
| 199 | + (all the elements matching ''A'') |
| 200 | + and return a new list of elements. |
| 201 | + This would be a bit more complicated for the author, |
| 202 | + but would allow more variety in the types of combinators that could be defined, |
| 203 | + as you could define things that depend on the entire set of matched elements. |
| 204 | + For example, you could define ''A /nth 1/ B'' |
| 205 | + to give only the first element from the set of ''A'' matches. |
| 206 | + |
| 207 | + (Maybe we allow both variants, |
| 208 | + since the per-element one is easier to optimize and program against, |
| 209 | + but the per-set one allows some useful stuff.) |
| 210 | + |
| 211 | + Similarly to custom pseudo-classes, |
| 212 | + we'd allow arguments, |
| 213 | + with them parsed eagerly per-instance |
| 214 | + and passed to the combinator function. |
| 215 | + |
| 216 | + If we do the per-element combinator function, |
| 217 | + we could potentially cache the results, |
| 218 | + so that it never needs to be called again for the same element. |
| 219 | + Possibly have a flag that turns off this behavior, |
| 220 | + so that you're guaranteed to be called again. |
| 221 | + </div> |
| 222 | + |
| 223 | +<h2 id='custom-atrules'> |
| 224 | +Custom At-Rules</h2> |
| 225 | + |
| 226 | + <div class='issue'> |
| 227 | + This one's even less developed, |
| 228 | + but it would be interesting to allow custom at-rules as well. |
| 229 | + It's definitely pure-JS as well. |
| 230 | + |
| 231 | + Unsure exactly what's best here. |
| 232 | + Possibly register a callback per rule, |
| 233 | + which is called with the prelude/contents of the at-rule? |
| 234 | + |
| 235 | + Should we do the callback approach, |
| 236 | + or just maintain a list of custom at-rules |
| 237 | + and let scripts parse them themselves? |
| 238 | + Unfortunately, the latter means we'd have to have a special mechanism to alert scripts |
| 239 | + when new at-rules get added or removed. |
| 240 | + |
| 241 | + For a lot of these at-rules, we may want a way to know when they're "applied"-- |
| 242 | + when, according to the built-in at-rules like @media and @supports, |
| 243 | + the rule would be applied. |
| 244 | + </div> |
0 commit comments