#730: Early design review: CSS Toggles

Visit on Github.

Opened Apr 15, 2022

Braw mornin' TAG!

I'm requesting a TAG review of CSS Toggles.

CSS toggles are a mechanism for associating toggleable state with a DOM element. This state can be defined and connected to activations through CSS properties, and can be queried in CSS selectors. This provides a declarative mechanism for specifying state that describes behaviors like the existing HTML behaviors for checkboxes or radio buttons.

Further details:

  • I have reviewed the TAG's Web Platform Design Principles
  • The group where the incubation/design work on this is being done: Open UI (W3C info)
  • The group where standardization of this work is intended to be done: CSS Working Group (W3C info), at least assuming it continues to be a CSS feature
  • Existing major pieces of multi-stakeholder review or discussion of this design:
    • There's been some discussion of this in meetings of the Open UI CG.
  • Major unresolved issues with or opposition to this design:
    • There has been some discussion about to what extent this makes sense to be in CSS. To some degree this breaks down into two separate questions: (1) whether the intended uses are things that should be done in CSS and (2) whether it enables doing other things in CSS that should not be done in CSS.
  • This work is being funded by: Google

You should also know that...

This is actually an early-stage review. The goal of what I'm doing in Chromium is to build an implementation that can be used to guide the design of this feature (by giving those involved an implementation to test), not to build something that we'd like to ship quickly.

Before we would consider shipping something in this space, we would (among other things) want to do further testing to validate that the result is more accessible than what people use today (or learn that we need to build in more distinctions that can be reflected into accessibility roles), and do additional development of the things that we hope can be built on top of toggles to validate that it works as a primitive.

We'd prefer the TAG provide feedback as: 💬 leave review feedback as a comment in this issue and @-notify users listed above

Discussions

Discussed Apr 25, 2022 (See Github)

Lea: can we punt?

Peter:

Discussed Apr 25, 2022 (See Github)

bring to plenary

Discussed May 9, 2022 (See Github)

Lea: we have to take a look...

Dan: let's tee up for the plenary

Comment by @LeaVerou May 11, 2022 (See Github)

Hi there,

Thanks for the detailed explainer and for working on solving this very common problem! Some thoughts and questions as I look through this (just from me, we haven't discussed this in a call yet):

  • I see that toggle triggers become focusable (which is great). How does that interact with other ways to control focusability?
  • toggle-visibility seems like a pretty thin abstraction to be worth the extra API surface. Isn't it possible to do with the rest of the syntax with only a couple lines of code?

Avoiding recursive behavior with toggle selectors

Not sure if I misunderstood something, can't you create a cycle like this:

div:toggle(foo 1) {
	toggle-root: foo 2 at 2;
}

div:toggle(foo 2) {
	toggle-root: foo 2 at 1;
}
  • I found it pretty hard to understand some of the more advanced examples, e.g. toggle-root: slides 4 at 1 sticky, toggle-trigger: page set saving;, possibly because syntax and values (at, set keywords and author-defined identifiers) are mixed and on the same level.
Comment by @tabatkins May 11, 2022 (See Github)

toggle-visibility seems like a pretty thin abstraction to be worth the extra API surface. Isn't it possible to do with the rest of the syntax with only a couple lines of code?

Having the connection between toggle and content-visibility be expressed explicitly like this should aid in reliably inferring accessibility semantics and relationships; this exact behavior is a vital part of several common and important aria patterns.

But also, achieving the full functionality here is not actually trivial. It's not hard, but it requires some scripting to, for example, activate the toggle when the element becomes automatically shown (due to becoming relevant to the user). Making the whole suite work together in some simple package is quite valuable for what we expect to be a super common use-case.

Not sure if I misunderstood something, can't you create a cycle like this:

Nope, the at <state> part of toggle-root is only used to establish the initial state of the toggle when the property causes one to be freshly created. It has zero effect on a toggle after creation; there is no way, in CSS, to set the current active value of a toggle. (It can only be set by user activation, or direct scripting, neither of which can loop in a relevant way.)

possibly because syntax and values (at, set keywords and author-defined identifiers) are mixed and on the same level.

Yeah, this has been a minor concern of mine as well. It might be worth making both counter names and state names use dashed-ident; it's a little more typing (and some checking in the scripting API), but it makes it immediately obvious what is author-defined vs CSS-defined, and avoids potential future issues with syntax ambiguity.

How does that interact with other ways to control focusability?

Great question, and still somewhat open. We need to give more thought to how, in particular, it interacts with the proposed focusgroup attribute.

Discussed Jun 20, 2022 (See Github)

Lea: still haven't found time for substantial review.

Lea: proposal for where authors use the checkbox hack. CSS has a checked pseudoclass. CSS authors need state and there's no way to have state without writing js they end up making apps that use hidden checkboxes to maintain state, and sibling selectors and the checked pseudoclass to affect parts of the page, and use labels to check and uncheck these without having visible checkboxes. This lets authors turn any element into a togglable state, either between two or multiple states.

Dan: making CSS more apocalyptically smart? Good use case to not have to rely on a hack

Lea: user needs are quite extensive.. tabs, accordians, sumamry & details, etc... [lists loads]

Dan: what's the concern?

Lea: none, just substantial proposal and early stage, so we could influence, so want to look at it properly. Syntax is entirely new.

Dan: developers are doing this anyway so lets give them a proper way?

Lea: yes

Dan: multistakeholder support?

Lea: OpenUI CG.. people working on it are google

Amy: any alternative proposals for the same use cases that are competing?

Lea: not that I know about

Dan: in alternatives considered there's a previous year old proposal css toggles, and declarative show/hide.

Lea: looks like evolution

Dan: worth asking about level of consensus in CSS WG?

Peter: declarative show/hide is just prior art not competing. Visual state that is not part of the application state - can't reproduce from the URL bar. I can get my UI into a state and share the link and they're not going to see what I see. Or bookmarking after clicking on things. I know we've talked before about the difference between document state and visible state. Is this something we should consider a mechanism to encode this somehow that can be recreated?

Dan: this proposal would have that issue, but the current use of checkboxes would also have that issue. if we're doing something to actually fix or create a proper way to do this then maybe it should also have this state preservation mechanism.

Peter: in theory the checkbox hack does have the problem.. lots of apps that have state that don't refelct in the URL. The checkbox hack at least lets me serialize the document and bring it back in, because its stored in the DOM, it's presreved. But here there's no way to preserve the state unless you use a script

Lea: if you want to be able to share state through URLs even with checkbox hack you would need to do something..

Peter: right

Dan: they explicitly list this in their non-goals - managing application state

Lea: line is blurry between application state and css state. Which tab you have open from an accordian, is that application state or css state?

Peter: presentational state.. I haven't switched a mode in the app or doc or whatever. If i have the same app oepn in two windows I don't necessarly want that change to refelct in the other window. If I'm looking at something and I want you to see a link and see the same thing I'm seeing, I want to send a URL and have you see the same view.

Lea: that's a good example. The other example about toggling dark mode. if you toggle in one tab you probably want it to toggle in the other tab. Does that make it application state?

Peter: could be. I think there's a mechanism for reflecting this because there will be a js api

Lea; regardless of whether this was designed for application state or not people are probably going to use it as well assuming it's easy.

Peter: in which case the spec should have clear guidence as to these are the types of things you do and don't want to use it for, and the reasons why. We have a lot of examples of apps that don't use urls properly. I'd like to not create a whole new way of web apps doing things badly.

Lea: not sure it's realistic to require the entire page state to be passed through the URL

Peter: no. There's probably lots of examples of places where you wouldn't want the state to be passed - eg. scroll position. It's a judgement call as to what should be reproduced. Some things like if I give you a url that is targetin gan anchor then one might expect css to apply to expose that anchor, eg. if it's inside a details element that should be open. I don't know how that works here.

Dan: noted it's in a personal repo

Peter: what's their normal work pattern?

Dan: leaves comment

Dan: but is there a more substantive comment we can leave about state?

Peter: I agree it's a non-goal but there is the other kind of hybrid display state [blurry line] to that end - does the spec have any text explaining that people should not use it for application state and explaining the difference? People will use it for application state and get it wrong.

Peter: I can leave a comment.

Comment by @torgo Jun 20, 2022 (See Github)

Hi @tabatkins you've said the work is happening in the Open UI CG. Is it an official work item in that CG? Should it maybe be in the openui repo https://github.com/openui to be able to track provenance of contributions prior to moving the work to the CSS wg as indicated?

Discussed Jul 18, 2022 (See Github)

Lea: Dan left a comment...

Dan: I don't think this should be blocking progress... Did Tab address the issues you raised?

Lea: one of the main concerns was about cycles...

Dan: Should we close it?

Lea: I think so.

Peter: a little concerned that this is just managing state. We've had discussions about where state should live. This puts state entirely on the presentational layer.

Lea: realistically it will be used for all kinds of state.

Peter: if i want to send you a link to something how is the state preserved?

Lea: this is the kind of state that is similar to details and summary.. you're not meant to see the same thing. presentational local state.

Peter: linking to a specific tab ...

Peter: i think the css wg can work this out. I think there's a larger arch issue about managing state... it's come up in other issues as well.

Dan: concerned that developers misuse it... (to save other kinds of state than intended) can devs be encouraged to not do that?

Lea: maybe give devs tools to read, save and restore state...

Peter: that's what I'm thinking...

Lea: that's a huge need in the web platform. I think this is intended as a stop-gap solution.

Peyer: this should be layerd on top... we need a model that explains this feature.

Lea: a javascript API for managing state that has a CSS counterpart?

Peter: possibly - a system for managing state - general purpose - CSS toggles could be explained using that... later, a JS API...

Lea: there's a section about a JS API for CSS toggles but not sure it's the right framing.. it should be for managing state.

Peter: leaves comment

Comment by @plinss Jul 18, 2022 (See Github)

We've had other discussions about application state that isn't stored in way that can be encoded in HTML (see e.g. https://github.com/w3ctag/design-principles/issues/289) and we do see a need for what we've been describing a presentational state vs document state (e.g. state encoded as elements and attributes and can be encoded in HTML).

This feature clearly falls into the presentational state category. We have two architectural concerns:

  1. There needs to be a mechanism for preserving, recovering, and encoding presentational state. e.g. If I'm looking at something in an app that's only exposed three layers of state down and send the current URL to someone else, how do they recreate the state so they can see what I'm linking to? We're thinking something like having a holistic presentational state model, which CSS toggles can expose and interact with, but so can other features, such as presentational properties on DOM nodes. This feature could then be built so that the underlying state is explained by that model, which can then perhaps have it's own JS API (rather than something specific to toggles) and mechanisms for storing/loading and encoding the state in a manner suitable for URLs.
  2. We're also concerned that authors will use presentational state where they really should be using document state (and vice-versa), has there been any thought on ways of nudging authors to choose the correct one? (Spec text and examples are an obvious mechanism, but we're wondering if something can be done to add or reduce friction to certain uses to help nudge authors...)
Discussed Aug 15, 2022 (See Github)

Dan: did say it's a big chunk of work

Lea: seem to remember this wouldn't happen async

Dan: schedule a separate call for it?

Peter: no response to my comment, but it was more bigger picture concerns

Dan: concerned with state.

Peter: this is all about state

Dan: can we get dbaron to join us for a special session? We talked about issues with people using this as a state preservation mechanism. They haven't responded. Maybe we should reach out for a discussion.

Peter: happy with that. Feels like something that should be discussed more within the CSS WG.

Dan: that's another option. We can give them parameters of discussion.

Lea: I've heard that Chrome plans to ship this. They feel it will help with a lot of pain points for developers.

Dan: Let's get David on the call and have more back and forth

Lea: that would be great

Dan: who else should we invite?

Lea: Maybe Tab and Miriam?

[scheduling - start at half past Breakout A next week]

Comment by @plinss Aug 15, 2022 (See Github)

@dbaron, @tabatkins, @mirisuzanne are you available to join us for a call to discuss this further? We're thinking 8/22 @16:30 UTC but we're flexible.

Comment by @bkardell Aug 15, 2022 (See Github)

Sorry, I'm going to add some noise... I don't want to hijack or confuse this issue but I'm very interested in this one actually and also it's potential connections to several other developing ideas. One thing that I've started thinking about in the past year or two, especially due to involvement in OpenUI research is where delineations are between things that are unchanging controls vs something that is really in pursuit mainly of adapting presentation (an affordance). An example of this, perhaps, is that many/most native toolkits start with something simple - a ScrollPane. That is a component with its own API - but the web didn't choose to make a <scrollpane> element, but rather chose to say that overflow is a matter of style and whether it is functionally acting like a scrolling pane or not is thus clearly in the domain of CSS. When something becomes a scrollable area it becomes a sort of pseudo-interactive element. It is, unfortunately, I think still a little under-defined as to what exactly that means but it affects focus in some browsers and some kind of keyboard handling. We probably need some more focus here on even providing keyboard parity (see https://github.com/w3c/html-aam/issues/358, for example).

You can contrast this with perhaps the only other non-form participating element we've done before dialog, <details>. Here we said <details> has a component nature - but that means that it is not possible to say "On a big screen I'd like it to be open because there's plenty of room" or "when it is printed, it's just content". We raised new questions about searching and linking and so on there too.

Along the way in our discussions we came to some ideas (some of which Toggles may potentially provide an explanation of the magic behind) about being able to provide some simple UI patterns that could offer affordances for some simple common patterns. I'm bringing this up because we've not submitted that for even an early review - there are too many questions still. Among them perhaps were some questions raised related to whether things with affordances really were 'the same' as their actual counterpart components which exist in 'applications'. To this end, we've split off some work with some discussion in an issue filed by me and Sarah Higley: https://github.com/openui/open-ui/issues/559 .. It might be interesting or helpful to have a look if you can find the time.

Comment by @mirisuzanne Aug 15, 2022 (See Github)

@plinss I'm available at that time.

Comment by @dbaron Aug 15, 2022 (See Github)

That time works for me as well.

Comment by @tabatkins Aug 17, 2022 (See Github)

Yup, that's fine for me.

Comment by @plinss Aug 17, 2022 (See Github)

Cool, invites sent

Discussed Aug 22, 2022 (See Github)

Guests joining: Tab, David Baron, Miriam

Peter: our biggest concern was the fact that this was dealing with state - and managing state somewhere other than the DOM itself... how does that fit?

Tab: it is stored int he Dom - there is a DOM interface... I assume you mean about preserved...

Peter: when I say stored in the DOM i mean serialized .. preserved... loaded back in. If I'm in a web app that is in an inetresting state and I send the link to you then you won't see the same state because it's not recoverable ...

Tab: yes - that's true but also the current state of thing. so this doesn't change anything...

Peter: wasn't a criticism but - exposes a hole in web arch - creates another way for authors to use this - could there be a better way to manage this? Not necisarilly putting this all on you but...

Tab: we haven't thought about it because this isn't a new problem.... this seems like a pretty big project - and fully seperable from this in concept...

Peter: don't think it has to be solved as part of this issue - but the more we expose this to developers the more it will become a problem... may be something that needs to be solved in parallel with this... that we have a strong arch model ... that this isn't doing something new and weird...

Tab: my initial thoughts are : this shouldn't be doing anything that makes that harder... the fact that this is the case... suggests that something as relatively simple as .. toggles .. falls into the same realm of stuff..

Lea: assuming someone does want to serialize the state somehow - can you do that? I see there's a toggle pseudoclass but that's for matching.

Tab: walk the DOM... Everyonce in a while if you need to know that then it's not a bad solution but it could be exposed [in a better way]...

David: one other related question we've had discusion of - how does this relate to session history... do we want toggle state to be restored in session history? i think the answer is "yes". 2nd question: can the APIs involved in Toggles be connected to a push state mechanism.

Tab: yes... I think. There's an issue open on this. Similarly being able to opt into session history.. things that would be a good idea. It's purely stylistic..

David: I'd add that if we add a mechanism to some push state mechanism then we might want to do the same for form controls.

Tab: Yes. that would be connected to auto-serializing it into the URL?

David: not fully thought out... i'm envisioning something that would allow you to save and restore state to some kind of thing in a fragment identifier... Some question as to how much belongs in the implementation.

Tab: i think the serialization issue could benefit from iteration in libraries first. See what feels right - produces URLs that people want to share that are meaninfgully deserializable back into application state... Even ignoring that the session history issue I am inetrest in... remember some info that it would be complicated from an implementer PoV.

Peter: do we know of anyone actively working in this space?

Tab: we have implementers with experience in this area - can ask Chris H....

Peter: is there any work going on in w3c ...?

Tab: to my knowledge, no... some text in HTML but not very prescriptive.

Tab: can you elaborate on ..... document..

Peter: come up several times in other reviews - multiple things that act on state - coming up with this idea of document state vs. presentaitonal state. Nothing written down anywhere. If you copy a doc and paste it soemwhere else what goes with that?

Tab: Yes currently an inconsistent ... that said, toggles have this property that you can restore a toggle state using style properties.. even if you can't manipulate... you can go through an element with a toggle, serialize and emit that as an element .. that will result in the same toggles/states. Some manual work. I don't think we want to autoreflect ... but make it easy as possible to do this seralization.. I will record it as an issue and record it in the document that it's an explicit goals.

Peter: bigger picture - beyond toggles there's state reflect back that probably shouldn' tbe... as the TAG our concern is that there's a hole int he architecture here... before we add yet another type of state... maybe important that someone does this work ... rather than building something else that's bolted onto the side... Not putting the onus on boiling the ocean ... but this work shoul be done somewhere...

Lea: relevant to that - any plans to explain via toggles other things like checkboxes?

Tab: it's not as an explict goals because the legacy requirements of existing form controls... maybe not worthwhile.. but at the least making things parallel would be good.

Rossen & Lea: observability & accessibility ARIA roles?

Tab: I'm working with A11Y people on this... we want to make sure the toggles are reflected into the A11Y tree... from our investigation we can't do it automatically but can record the role.. (radio group, tab, etc...) and from there set up the appropriate role patterns.

Lea: you can figure from these heuristics.....

Tab: this working well with A11Y is an explicit requirement for this. Pretty important.

Peter: toggles looks and acts like a checkbox... if something is acting as a form element maybe it should be managed as form state rather than presentational state.

Tab: Yes... i think we can do some auto-assigning. Some way to say "here's a toggle - add this to my form data" would be good.

Peter: or "here's some form state - reflect as a toggle"...

Peter: is the advice to the authors good so authors know how to use this in the right situation...?

Tab: given that Toggles ... whatever we do in this space is integrateable...

Comment by @yisibl Oct 18, 2022 (See Github)

Yeah, this has been a minor concern of mine as well. It might be worth making both counter names and state names use dashed-ident

@tabatkins Yes, changing the syntax to dashed-ident would be clearer.

Discussed Nov 14, 2022 (See Github)

Peter: we had the joint meeting and epxressed our concerns about having a way to serialise and resotre the state, and unify this with other notions of presentational state. They took that feedback positively, but don't know what happened since.

Dan: what did we decide about this issue? Let's record something about where this stands. Are we just waiting on them to do something?

Lea: I don't think we're waiting, we had the meeting and there's no outstanding request.

Peter: it was early review, so we can close with message of bring it back when you've got more

Dan: [closes]

Comment by @torgo Nov 14, 2022 (See Github)

So on the basis of our discussion on 8-22 we're happy to close this. Thanks for flying TAG.