tabatkins / css-toggle

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

Feedback on the tristate check example, and a suggestion #6

Closed jonathantneal closed 2 years ago

jonathantneal commented 3 years ago

Feedback

Currently, Example 8 demonstrates how one could use CSS Toggles to design a checkbox with an "indeterminate" state.

I would expect this example to show me how my own check could toggle from "indeterminate" to "checked", and then between "checked" and "unchecked" indefinitely.

.tristate-check {
    toggle: check 0/2;
}

.tristate-check:toggle(check 1) {
    /* "checked" styles */
}

.tristate-check:toggle(check 2) {
    /* "indeterminate" styles */
}

In this example, my understanding is that 0 specifies the initial state and 2 specifies the maximum state of my check.

However, because the next rules describe 1 as "checked" and 2 as "indeterminate", I think this means my check will cycle from "unchecked" (0) to "checked" (1) to "indeterminate" (2) and back to "unchecked" (0). This won’t work for me, because I want my initial state to be "indeterminate", and because I do not ever want to toggle from "checked" to "indeterminate".

With this (potential misunderstanding of the spec) in mind, I have tried to imagine the correct CSS using the spec.

.tristate-check {
    toggle: check 0/2 sticky;
}

.tristate-check:toggle(check 0) {
    /* "indeterminate" styles */
}

.tristate-check:toggle(check 1) {
    /* "checked" styles */
}

.tristate-check:toggle(check 2) {
    /* "unchecked" styles */
}

In this example, my understanding is that 0 specifies the initial state, 2 specifies maximum state, and sticky specifies that my check will only cycle between the active states (1 and 2).

I think this will work for me. However, this might not work if I’m using a group, where 0 must be the "unchecked" state. (Because 0 is inactive? Do I have that right?)

Suggestion

With that (potentially deeper misunderstanding) in mind, I have tried to imagine the correct CSS inspired by the spec.

.tristate-check {
    toggle: check 2 1 0 reverse;
}

.tristate-check:toggle(check 0) {
    /* "unchecked" styles */
}

.tristate-check:toggle(check 1) {
    /* "checked" styles */
}

.tristate-check:toggle(check 2) {
    /* "indeterminate" styles */
}

In this example, my understanding would be that 2 specifies the initial state, 1 the maximum state, 0 the minimum state, and reverse the direction incremented.

This would make more sense to me, because I think it means my check would toggle from "indeterminate" (2) to "checked" (1), and then between "checked" and "unchecked" (0) indefinitely. I would also expect this to work if I’m using a group. (If 0 must be the "unchecked" state?)

jonathantneal commented 3 years ago

I’m done editing now. Sorry for any confusion. It took me a few takes. ❤️

davidkpiano commented 3 years ago

IMO, while this technically solves the problem:

.tristate-check {
    toggle: check 2 1 0 reverse;
}

There are some acrobatics involved with figuring out the right values to get the above to work, when in reality, you want to express it as a directed graph:

CleanShot 2021-06-16 at 18 28 18@2x

This also highlights a bigger concern: linear sequencing is not sufficient (even with cycles) for all potential use-cases, and this is an obvious one, so I love that @jonathantneal pointed it out!

It would be much more beneficial to have a way of representing the directed graph in CSS, with shorthands for common use-cases like sequences (with/without cycles), so the above use-case might look something like this:

.tristate-check {
  toggle-transitions: 0 1, 1 2, 2 1;
}

# Or with names...
.tristate-check {
  toggle-transitions:
    indeterminate checked,
    checked unchecked,
    unchecked checked;
}

toggle-transitions is bike-sheddable (conflicts with CSS transitions, I know) but it represents the state transitions, or the edges between the nodes in the digraph.

What do you think?

tabatkins commented 3 years ago

Before we get into a more complicated set of arbitrary transitions, can we step back and provide some example use-cases that we're trying to address?

Right now the cases I know we want to address are:

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

matthewp commented 3 years ago

You can model anything with booleans since that's the basis for computing, but the question is whether modeling something like a checkbox which has 3 states is better done using 2 booleans or 1 active state.

davidkpiano commented 3 years ago

I can provide examples later, but IMO I wouldn't call introducing a directed graph "making things more complicated" because it's actually the opposite: instead of special-casing many common cases, you can provide a single simple abstraction that can cover all use-cases.

tabatkins commented 3 years ago

If there are many common cases that need to be addressed, then a more general mechanism that can cover all of them can indeed be simpler than trying to special-case each variant. But if there are only a few, a more general mechanism can be overkill, adding complexity that's unneeded for the vast majority of cases.

That's why I'm asking for examples of controls that you want more complicated state transitions for - all this state machine talk has hit me completely by surprise, and I'm not personally aware of any complex state machinery that people typically build into custom controls today. If y'all do know, I've love to hear about them!