tabatkins / css-toggle

Proposal for a CSS Toggle spec
Creative Commons Zero v1.0 Universal
28 stars 1 forks source link

alignment with state machines #7

Open argyleink opened 3 years ago

argyleink commented 3 years ago

would love if i could make/share charts for the ~state machines i'll be building with toggle, be great for tooling and greater community adoption if verbiage and functionality were more aligned.

tabatkins commented 3 years ago

I'm not sure what you mean here. Could you elaborate?

davidkpiano commented 3 years ago

I'm not sure what you mean here. Could you elaborate?

Example: https://github.com/tabatkins/css-toggle/issues/6#issuecomment-862774300

argyleink commented 3 years ago

imagine devtools had this built in and could map out the toggle logic https://xstate.js.org/viz/ and could be shared with design teams and PMs:

image

the verbiage doesnt necessarily need to match 1:1, but if it could be functionally mapped to the core ideas in state machines, it opens up a lot of already existing methodologies, folks and tooling to converge with CSS's new feature.

tabatkins commented 3 years ago

I'm sorry, I still don't understand. I suppose you could treat a toggle as an extremely simple sort of state machine, one that simply cycles thru N states before returning to the beginning, but I'm not sure what value you would get out of that, or what alignment in verbiage you'd be looking for.

matthewp commented 3 years ago

A toggle is a simple state machine. So let's just implement state machines instead of a boolean toggle 😀. It could be scoped to be small, not full state machines at first, probably not even events, but N states that you can transition between rather than a boolean toggle doesn't sound very much of a feature-creep over this spec. A toggle-group is already like a machine. Complete pseudo-code:

panel-set {
  machine: tab;
}

panel-tab:machine-state(tab initial) {
  machine-transition: enabled;
}

panel-tab:machine-state(tab enabled) {
  machine-transition: disabled;
}

If you wanted to get crazy and add events to a transition:

panel-tab:machine-state(tab initial) {
  machine-transition: :active enabled;
}
tabatkins commented 3 years ago

So let's just implement state machines instead of a boolean toggle

Again, I'm not sure what value we'd get out of this. I asked this same question in https://github.com/tabatkins/css-toggle/issues/6#issuecomment-863636692.

tabatkins commented 2 years ago

I still haven't gotten any reasonable elaboration on what, precisely, is meant by "state machines" and what sort of page design would be helped by this, which can't be reasonably done with the current design.

(Note: I am 100% aware of what state machines, DFAs, etc are. I'm just asking for examples of what is actually meant by using them.)

mirisuzanne commented 2 years ago

Thinking through this as I work on an explainer, we currently support:

When we move past the maximum state, we allow:

I can see two extensions here that have fairly clear use-cases:

When it comes to more complicated logic around the order of states, that should generally be possible by changing the toggle-trigger value based on a current :toggle() state selector. That might not feel like the simplest syntax to manage - but it could be pre-processed, and it doesn't seem like a core use-case to me.

matthewp commented 2 years ago

@tabatkins It would be easier to give better feedback here once the explainer is ready. Personally I don't understand what this spec is doing yet; I see several properties, I see 0/2 and my brain doesn't understand how this corresponds to true/false, but I believe that it does and once I see an explainer I can hopefully give better feedback.

I can answer the question that you asked in that other issue though:

What other sorts of controls are we trying to model that aren't satisfied by these transition sets?

Almost all of them. I can't think of too many controls that don't have > 2 states. If you want a concrete example, the title of a GitHub issue has at least 6 states (names made up by me):

mirisuzanne commented 2 years ago

@matthewp I think the explainer is far enough along to answer most of your questions here - for example, the proposal does support >2 states, and the explainer covers that, and how to trigger movement between them. The n/x syntax is also explained there (which is how you set the number of states and also the initial state).

matthewp commented 2 years ago

@mirisuzanne Is it correct to say that what represents activation is unclear at this point? Will that be user-defined some how? Happy to give general explainer feedback somewhere else if preferred.

mirisuzanne commented 2 years ago

Is it correct to say that what represents activation is unclear at this point? Will that be user-defined some how?

It is a bit unclear. Currently the idea is something like existing link/button activation (which are themselves slightly different). There are plans to try and extend that to other interactions, such as scroll/snapping. If there are multiple types of interaction, I would expect those are defined in the spec, and authors have access to hook into the interaction-type desired.

Happy to give general explainer feedback somewhere else if preferred.

matthewp commented 2 years ago

Ok, after having read it, I can see that this supports more than 2 states. What is missing that FSM gives you is the ability to define valid state transitions.

The explainer shows using toggle-trigger shorthand to cycle through states; but you would almost never use this when you have > 2 active states; you don't cycle through 6 states sequentially, you have a specific path that they can go.

The inactive state idea solves this for the checkbox example but has the following downsides:

flackr commented 2 years ago

So I think @mirisuzanne covered your > 3 states concern in https://github.com/tabatkins/css-toggle/issues/7#issuecomment-1057347177, if you want to only have specific state transitions, you could accomplish that by not using the automatically cycling toggle-trigger but instead using the one that has a particular next value, e.g. for your github example:

/* states: 0 = preview, 1 = edit, 2 = save, 3 = cancel */
button.preview:toggle(state 1) {
  toggle-trigger: state 0; /* preview */
}
button.preview:toggle(state 0) {
  toggle-trigger: state 1; /* edit */
}
button.save:toggle(state 1) {
  display: block; /* only visible from edit state */
  toggle-trigger: state 2;
}
button.cancel:toggle(state 1) {
  display: block; /* only visible from edit state */
  toggle-trigger: state 3;
}
/* no user activatable trigger from state 2 or 3 */

Once you reach state 2 (save) or state 3 (cancel) you would then use the not-yet-defined scripting API to update the state once the action had been taken.

Note: There are probably shorter ways to write this, i.e. if .save and .cancel are only visible in edit mode there's no reason they couldn't always have the toggle-trigger style.

tabatkins commented 2 years ago

sticky behavior returns to 1 (first active)

I'm... not sure why I specified it that way. It should clearly just stay in that last state. I'll fix that.

tabatkins commented 2 years ago

If you want a concrete example, the title of a GitHub issue has at least 6 states (names made up by me):

In what sense is this a toggleable state, however? These don't appear to be states that you'd advance thru or to by activating some buttons; at least most of them are caused/indicated purely by script action. You can certainly use a toggle to hold that state, but it's no different than using class to hold that state.

I'm looking for examples of things that users would "toggle" via some user action that need a more complex set of state transitions than just incrementing/decrementing a state. If users aren't toggling things via button presses or similar, then we don't need to represent the transitions in CSS; they're JS-driven and you can do arbitrarily complex state graphs in JS. (And can apply them to any value store; CSS toggles are one possible such value store.)

mirisuzanne commented 2 years ago

I think there are cases where it makes sense to cycle active-only states - without being able to return to an initial state. So I think the current definition of sticky is useful. I think we would want all three options.

tabatkins commented 2 years ago

Ah yeah, that's probably what I intended to happen; still cycling, just staying "activated". All three options are indeed useful, then.

bkardell commented 2 years ago

Unhelpful comment, probably, but I think you changed this after our earlier comments/discussions and similar confusions. I think you need them all, though, I feel like it is hard to keep track of, personally. I am probably not representative tho

matthewp commented 2 years ago

@flackr Yes, you are absolutely right, you could do this manually, and prevent unwanted state transitions, just without the convenience of defining the valid transitions in a single place.

You could do the same for the indeterminate state of checkboxes too, though, right? Why special-case this state into the spec, making it more complicated to understand, if we're not going to make it more generically useful for other scenarios?


@tabatkins

If users aren't toggling things via button presses or similar, then we don't need to represent the transitions in CSS; they're JS-driven and you can do arbitrarily complex state graphs in JS. (And can apply them to any value store; CSS toggles are one possible such value store.)

It seems unnecessarily constraining to have a state feature that you can use up until you need script, and then you need to rewrite it to use classes instead. The appeal of this feature to me, is using the declarative language of CSS drive the state and not imperative, buggy JavaScript.

mirisuzanne commented 2 years ago

I'm frustrated that the conversation here keeps assuming our proposal can't or won't do things that it very much can, and very likely will do. Or that the missing pieces in an early draft spec are somehow impossible for us to fill in without starting over. Or that we've somehow failed to imagine more flexible use-cases in addition to the common defaults, simply because the first draft is focused on those common cases. None of that is remotely true.

The linked explainer covers in detail:

We're asking for use-cases, not because we think this feature should only handle a few hard-coded situations, but because we need a full test suit of examples to make sure the solution is significantly flexible. We don't need explanation of how our half-done proposal is half-done, we need examples of the wide range of use-cases people would like to achieve. Explicit examples go a real long way in testing out an idea.

But we also need to keep a few things in mind for developing new CSS features:

matthewp commented 2 years ago

@mirisuzanne That's great to hear and I apologize if I have been a source of frustration. The owner of this repository has said things like "I'm not sure what value we'd get out of this" in this thread; I'm not sure how to read that as anything other than being against the idea. If opinions have shifted off-thread that is fantastic news.

I did not see the @machine section of the explainer before. I'm not sure how I missed it. Reading it now it looks exactly like a FSM which is what this thread is asking for.

I'm happy to help with more example controls if given the criteria that is being looked for. The original request didn't mention not wanting controls that use script, so I provided one that did. I think most controls will use script in some way. The example fetch from the explainer uses script, for example. But if that is a criteria that you are looking for I will do my best to find some.

tabatkins commented 2 years ago

It seems unnecessarily constraining to have a state feature that you can use up until you need script, and then you need to rewrite it to use classes instead. The appeal of this feature to me, is using the declarative language of CSS drive the state and not imperative, buggy JavaScript.

Ah, that wasn't what I meant.

What I mean is, CSS toggles are an output mechanism - they let you push a certain type of state out to CSS so it can be responded to via Selectors. In this regard they're no different from classes, attributes, or any other way to plumbing some abstract notion of "state" into an element so selectors can pick up on it.

CSS toggles only add two new things here - a way to automatically plumb user activation gestures as input into the state (nothing new here theoretically, identical to what you can do with some trivial event listeners and tabindex attributes), and a degree of inferred a11y structure/communication, so the "easy route" of using toggles for simple UI state management works well for all users, not just sighted mouse/touchscreen users.

None of these three bits of functionality have to do with program state in general, or transitions between such states. That's a complex and multi-dimensional subject, which can take many kinds of inputs as state-transition triggers, and cause many kinds of behaviors as state-transition results. All those multifarious inputs and outputs aren't, probably shouldn't be, and likely can't be integrated into a CSS mechanism, at least not without massively overcomplicating things.

All this isn't to say that managing the values of toggles in more complex ways can't be expressed in declarative CSS. The spec already has the ability to dictate the number of states, the behavior when a transition would go "off the edge" of the states, and the ability to bind multiple toggles' states together. These are all just elaborations on existing HTML tech (checkboxes and radio buttons), but we're not required to limit ourselves to that in the future. But we need examples for this, which show a clear use-case of complex state transitions that only use user interaction as input and only use Selectors-relevant element state as output. If these use-cases commonly need more input than just user interaction (such as API responses, etc) and/or commonly need more powerful types of output (such as toggling or setting attributes with non-trivial behavior, like disabled), then that's an argument that it should instead be handled by a different mechanism, likely just ordinary JS, with CSS toggles just used as components of its input/output.

And, as Miriam said, toggles will be reflected fully into JS anyway; JS can create toggles and manipulate their state, and can listen for toggle state changes triggered by the declarative mechanisms, so any sort of overall program state management that wants to use toggles as input or output should be able to do so easily.