Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

alignment with state machines #7

Open
argyleink opened this issue Jun 16, 2021 · 23 comments
Open

alignment with state machines #7

argyleink opened this issue Jun 16, 2021 · 23 comments

Comments

@argyleink
Copy link

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
Copy link
Owner

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

@davidkpiano
Copy link

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

Example: #6 (comment)

@argyleink
Copy link
Author

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
Copy link
Owner

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
Copy link

matthewp commented Jun 17, 2021

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
Copy link
Owner

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 #6 (comment).

@tabatkins
Copy link
Owner

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
Copy link
Collaborator

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

  • incrementing the state by 1
  • setting the state to a specific value

When we move past the maximum state, we allow:

  • default behavior returns to 0 (inactive)
  • sticky behavior returns to 1 (first active)

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

  • decrease the state by 1 (eg carousel prev/next, or font-size increase/decrease)
  • a linear option that only moves forward, and does not continue cycling active states (eg a carousel that doesn't wrap)

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
Copy link

matthewp commented Mar 2, 2022

@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):

  • preview: The default view when on an issue page.
  • edit: When in edit mode.
  • save: When saving to a remote API.
  • error: When the API server errors for some reason.
  • cancel: When rolling back changes from edit mode.
  • validate: When confirming the new input title is an acceptable string.

@mirisuzanne
Copy link
Collaborator

mirisuzanne commented Mar 2, 2022

@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
Copy link

matthewp commented Mar 2, 2022

@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
Copy link
Collaborator

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.

  • For that sort of question about the spec, feel free to open new issues in this repo.
  • It might make sense for me to move the explainer into this repo as well, but for now feedback on the explainer itself belongs in https://github.com/oddbird/css-sandbox/issues

@matthewp
Copy link

matthewp commented Mar 3, 2022

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:

  • Checkboxes have 3 states - the idea of "inactive" and "active" is confusing; they are all just states. This really feels like this a special casing for checkboxes only. The result of special casing is, in my opinion, a more difficult to understand feature.
  • The problem still exists for controls with states > 3, for example the GitHub title bar explained here. You should not be able to go from preview -> save or cancel -> save.

@flackr
Copy link

flackr commented Mar 3, 2022

So I think @mirisuzanne covered your > 3 states concern in #7 (comment), 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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Collaborator

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
Copy link
Owner

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

@bkardell
Copy link

bkardell commented Mar 4, 2022

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
Copy link

@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
Copy link
Collaborator

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:

  • how the syntax needs to handle named states, and proposals for doing that
  • the necessity for arbitrary transitions, outside a default looping behavior, and how that's handled
  • potential extensions to the syntax (either in v1 or v2) that would allow 'defining the valid transitions in a single place'
  • the need for a two-way JS API, that would allow both reading and writing toggle states (so there is no need to 'switch to classes' just because JS is managing some of the logic.

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:

  • CSS has very specific limitations around what it can/should be used for, including a 60fps performance requirement. It is not the right place to solve all application state issues on the web, but it would be great if it can help reflect any state from JS, and also control simpler presentation/style states.
  • A CSS solution will likely include 'defaults' that handle basic behavior of the common-cases, such as cycling through states in order. That doesn't mean we're limited to that behavior, just that we default to it.

@matthewp
Copy link

@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
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants