## CSS Toggle States
This is heavily based on a previous proposal by Tab; the active work happening in OpenUI around popups, tabs, and accordions; and a work-in-progress proposal by Nicole Sullivan & Robert Flack.
The key feature here is that named toggles work in the same way as CSS counters:
- They flow to consecutive siblings, and also inherit to descendants
- They can be updated or reset by either siblings or descendants
- Proposal at <https://tabatkins.github.io/css-toggle/>
## Non-exclusive Accordion (with or without cards)
```html
<accordion>
<!-- Express initial states for a given toggle in html,
optionally with one of multiple states (e.g. `--card 3`)
-->
<card toggled="--card">
<tab>...</tab>
<content>...</content>
</card>
<card>
<tab>...</tab>
<content>...</content>
</card>
<card>
<tab>...</tab>
<content>...</content>
</card>
</accordion>
```
```css
tab {
toggle: --card 2;
/* shorthand for:
* toggle-states: --card 2;
* toggle-set: --card;
*/
}
content:checked(--card) {
/* Toggles use counter scoping rules
* So this sees the toggle (and value) established
* by its previous sibling <tab>
*/
…
}
```
## Accordion again, but with unpredictable tab/content order
```html
<accordion>
<card>
<content>...</content>
<tab>...</tab>
</card>
<card>
<tab>...</tab>
<content>...</content>
</card>
<card>
<tab>...</tab>
<content>...</content>
</card>
</accordion>
```
```css
card {
toggle-states: --card 2;
}
tab {
/* toggle established by parent,
* so <tab> just opts into *manipulating*
* the toggle
*/
toggle-set: --card;
}
content:checked(--card) {
/* This time, the toggle comes from the parent
}
```
## Details element
```html
<details2>
<summary>...</>
<content>...</>
</details2>
<style>
details2 {
toggle-states: --show 2;
}
summary {
toggle-set: --show;
}
content {
display: none;
}
content:checked(--show) {
display: block;
}
```
## Self-contained Checkbox w/ Named States
```html
<check-box></>
<style>
check-box {
toggle-self: 3 off on unknown;
/* toggle-self limits the toggle's scope
* to just the element itself.
* We *think* this is just a safety improvement,
* not actually a needed new ability.
*/
}
check-box:checked(self off) {...}
```
## Tabs / Exclusive Accordion (with or without cards)
```html
<tabs>
<card>
<tab>...</tab>
<content>...</content>
</card>
<card>
<tab>...</tab>
<content>...</content>
</card>
<card>
<tab>...</tab>
<content>...</content>
</card>
</tabs>
```
```css
tabs {
toggle-group: --show 2;
/* -group establishes a scope for sub-counters,
* of which only one can be active at a time
* Same grammar as -states, so all sub-counters are identical.
*/
}
tab {
toggle-item: --show;
/* creates a sub-counter tied to the --show group */
/* because --show is a group, only one can be active */
}
content:checked(--show) {
/* sees the --show counter from its sibling */
}
```
## Arbitrary toggler element & target positions
```html
<html toggle-scope="colors">
<button toggle-btn="colors">…</>
<section toggle-target="colors">…</>
<style>
[toggle-scope] {
toggle-states: attr(toggle-scope) [...];
/* toggles can be named by a string
* instead of a custom ident
*/
}
button {
toggle-set: attr(toggle-btn);
}
[toggle-target]:checked(attr(toggle-target)) {
/* Requires some magic here,
* but attr information *is* known
* during selector evaluation.
* Hopefully okay?
*/
}
```
## Tabs up front ❌
The code here represents some of our early attempts, but we do not consider this use-case solved -- or part of the current proposal.
```html
<tabs>
<tab>...</tab>
<tab>...</tab>
<tab>...</tab>
<content>...</content>
<content>...</content>
<content>...</content>
</tabs>
<style>
/* tab-bar mode */
tabs {
display: grid;
grid-template-rows: auto 1fr;
counter-reset: tabs contents;
toggle-states: --tab group 2;
}
tabs::grid-cell(1 / 1) {
area-name: foo;
display: flex;
}
tab {
flow-into-grid-area: foo;
counter-increment: tabs;
toggle-set: --tab counter(tabs);
}
content {
grid-row: 2;
counter-increment: contents;
toggle-read: --tab counter(contents);
}
.radiogroup {
toggle-states: --radio group 2;
}
.radio {
toggle-set: --radio;
toggle-read: --radio;
}
```
Is this a potential solution?
```css
tabs {
toggle-group: --show 2;
counter-reset: tabs contents;
}
tab {
toggle-item: --show counter(tabs);
/* toggle-item could generically allow named items,
* and accept counter() as one way to assign those names
*/
counter-increment: tabs;
}
content {
counter-increment: contents;
}
content:checked(--show counter(contents)) {
/* sees the --show counter from its sibling */
}
```
## Open Questions & Potential Issues
- All syntax needs bikeshedding…
- clarity around toggle-names, state-counts, and state-names
- is `:checked()` the right syntax for a pseudo?
- what are the default states, and can we name them (e.g. on/off)?
- Can a11y be built-in and handled automagically?
- different toggle types (show/hide, files, etc) may need different a11y handling,
- ways to opt-into those semantics?
- **it should not be easy to create in-accessible interfaces**
- How do we handle content-visibility?
- Especially for linking into hidden tabs, etc?
- How does it interact with animations & transitions?
- Can state be maintained across navigation, like form controls?