David Khourshid

Crafting Stateful Styles (dotCSS 2019)

Conference talk at dotCSS 2019 (Paris). 18:25 long, 1.9K views on YouTube. Topics: css & animation, state machines, statecharts, visual modeling.

Watch on YouTube (dotconferences)

What is this talk about?

“You could actually affect the styles within the app, and so you can imagine if you add more than one check box, you could have multiple different dynamic styles and different things showing in your application.”

“We had to find a scalable way to just not keep making the same mistakes that we were making before and actually find scalable patterns in order to have these, well, I call stateful styles, and so we did.”

From the opening of the talk.

Key takeaways

  1. You could actually affect the styles within the app, and so you can imagine if you add more than one check box, you could have multiple different dynamic styles and different things showing in your application. 2:00
  2. We had to find a scalable way to just not keep making the same mistakes that we were making before and actually find scalable patterns in order to have these, well, I call stateful styles, and so we did. 4:00
  3. Doing this with data states means that you could do other things to really customize the styles of your stateful dynamic CSS. 10:00
  4. First of all, we set the data state attribute on the application to what state were in, and then we remove all of the data. 13:00

Quoted from the transcript.

Full transcript

From YouTube auto-captions. Punctuation and capitals were restored automatically. Technical terms were normalized; some errors remain. 3,184 words.

0:00 Introduction

Yeah. [Applause] Hello, I am so excited to be in Paris for the very first time. Talking to you about crafting stateful styles now, what do we mean by crafting stateful styles? Well, the web is getting more and more dynamic every day. We don't have just static web pages now, we have web apps and frameworks for those apps, and we're even making static web pages with those frameworks for some reason. So let's take a normal button, for instance, we have a fetch data button, and so you might think this is pretty easy to style. However, we have many different ways that this button can be represented, so this button can be in the hover state. Or it could be active when you're clicking it, or it could even be disabled. Now, these are just the browser provide that stays for this button. But when you have certain requirements from either your customers or whichever company you're working with. You might need to represent a component or UI in different ways.

Such as a button that's loading, or a button that might signal some sort of success, or a button that might signal that there's an error. And so these are all states that we have to consider when were dialing these dynamic user interfaces now, typically, what we would do, and what I have been doing for a while before, I sort of got a better idea of. What I should be doing is adding classes in order to say which state this should be in. For instance, this button has a loading class. And we're gonna take a look at why this might not be the most capable pattern. But first, let me explain my journey to how I got to where I am today. You know, talking about steam machines all over the place? I wasn't actually always good at JavaScript, and I didn't even really start off as a JavaScript developer. I started off as a pretty much a CSS developer, a web designer, as it used to be called.

And I made demos such as this, which is a really cool demo where you could just hover over one.

Play from 0:00

2:00 I found that check boxes are actually really handy and doing…

That'd be, you know, one of the weather things and see the weather for a certain day now. This was done using only CSS, and it was. These are based on the animations. By 2BIc studios. And so I try to see how far I could go with just CSS and HTML, no JavaScript at all, even making something like this calendar demo. And so, you know, I. I used a few tricks in order to do this. I found that check boxes are actually really handy and doing some of these complex dialing, so here's a trick with the check box. If you have a check box that lives right next to your app. You could use the adjacent sibling selector so that when that check box is checked, you could toggle that check box with a label that has the exact same ID as that check box ID and then using the check SU selector.

You could actually affect the styles within the app, and so you can imagine if you add more than one check box, you could have multiple different dynamic styles and different things showing in your application. All without needing any JavaScript. And so this pattern proved really handy and doing these CSS and the animations, you have check boxes where you could check one or more to show and hide certain things. But I also used radio buttons when I only wanted to show one thing at a time. And this is going to be an important concept that we're going to get into later. So I started getting more and more interesting. What kinds of things can we do with CSS without any JavaScript? And trying to push the limit as far as I can? You might remember the CSS Husky Ballet made, or recently I made if this loads, I made this. Wow, okay, it looks slightly better than the real thing, but this is the CSS cyber truck.

It's supposed to be here. I actually have a backup over here, but yes, this is made completely with CSS, and I even used that checkbox trick to make breakable windows.

Play from 2:00

4:00 You could represent each of your different states in that single…

So if you see that, yeah, thank you, at least my demo went a little bit better than Ulong's. So anyway, so, as you know, mentioned, I do the key framers almost every week with my friend Shaw. And we bring these imaginative user interfaces to life from dribble or from other places where there's all these sort of dynamic interactions that need to happen. And we use HTML, CSS in a little. Bit of JavaScript. But the more that we were doing these, the more that we noticed these patterns of working with states. And we had to find a scalable way to just not keep making the same mistakes that we were making before and actually find scalable patterns in order to have these, well, I call stateful styles, and so we did. This problem presents itself in another way, too. Let's say that you're working with a company you know, your designer gives you a certain design.

And you realize that there's many different ways that this application can be users, many different things that the user could do, and so the designs that the designer gives you aren't enough. So it really comes from both sides, because developers - we tend to think of just like, oh, let's throw this dial here, let's, you know, do this. So we might have a button with a class up loading, a class of success, but what happens if we have both the loading and the success class on the button? And how can you prevent that from happening? It doesn't really make sense. I mean, we see this error exhibited in other ways, too, so we want to prevent these impossible states from happening. And so you might be thinking, is there a better way than to model these dynamics dates for these dynamic user interfaces? And so I took a look at other prototyping tools such as Origami or Envision app. Where you define each of your screens and the transitions that go between those screens and make really cool animations such as this or principle for Mac, if you've used Principal, that sort of works in the same way.

It's like magic move on steroids, you could just drag things around and it goes to the correct size and also overflowed. IO, which is the visual representation of the user flows that represent each of the states that your app can be in. And so tools like this are really good. And I started to see that all of these have certain patterns. They all represent your user flows as transitions between certain states, and these transitions happen due to events. Now, this is also known as a finite state machine. And this is what I'm going to be talking about. Aria, the W3C in Aria. Defines a state as a, you know, a dynamic property that expresses characteristics of these components. And it also shows you that when a components in a certain state, it says nothing about the character of the component, but more about what can be done. For example, if a button is disabled, you can't click it, it won't do anything.

And so we see this exhibited in different ways as well. And you might have been using these finite states and CSS without even knowing it, you might know. Hover, focus, active and valid, empty and all sorts of other pseudo selectors, but there's actually a proposal for a native state pseudo selector for custom components. And so, if you're interested, please go this link and tell them that, hey, this would be interesting to have in all browsers. But what I'm going to be using is data attributes, which are really fun to work with, and data attributes have this unique property where you could only have one at a time. And we're going to be talking about what exactly that means, but data attributes are also really easy to work with. For example, I could just set the states to loading, or I could set the states to success just by changing the property value, or I could just delete the property and then that data attribute is gone.

So here's what I mean with data states. We're no longer polluting our class names or anything with extra states for styling. And we can no longer reach that impossible state of having something be both success and error, or loading, or disabled and not disabled. So you could represent each of your different states in that single data state attribute. And so here's how this fits into a finite state machine. In the finite state machine, you have an initial state and you have a finite number of states. And the rule with the finite state machine is you could only be in one of those states at any given time. But the second part, too, is there's these things called transitions, which dictate when this event happens in your finite state machine, and you're in one state. You will always go to the states where all you have to do is follow the arrow, and you know which state you're going to go to every single.

Time so you could use finite state machines. By doing switch case statements, where you give it the current state, you give it what events happen, and then you use those NESET switch case statements to figure out what the next state is. But I'd like using objects mapping, and with object mapping, it's sort of the same thing. You need a helper function to sort of just walk through that object and figure out what the next state is going to be. But the idea is exactly the same I created XState, and X8 is essentially that same idea. Wrapped into a library, you call the machine function, you pass it in your configuration, which looks very close to that object that I just showed you. And you could determine your finite states now. This is very useful for doing dynamic things in applications such as drag and drop. Where you have two states, you have dragging and idle, and in each of the states, depending on what you're doing, you're changing certain values.

So this I'm actually using, I'm using the data state attribute in a pseudo selector to display what state I'm in. As I'm dragging this around. And I also have specific styling for each of those different states. So doing this with data states means that you could do other things to really customize the styles of your stateful dynamic CSS. For example, I could specify what the previous date was, and doing this allows me to define certain Styles based on which transition I'm going to. So I might want to do something different if I'm going from idle to loading, or something different if I'm going from error to loading. And so we could just combine both of those Dallas date and data predates attributes together and figure that out. Or you could do it a different way - I like using a data transition attribute, which has both the previous date and the current state. And then you could use the these darts, the start attribute selector, the data of whatever you call it.

It has a little caret in an equal, and that's what that is. So let's see demo time. And so I use this technique over here when I actually created this. Because the Styles do something different depending on where you're going in, so that's why. You see NaV one from starts, start from NAV one, and then you can also go to this date. So we have nap two and back over here. And so this is the type of dynamic user interface that really benefits from having these strict Styles based on transitions. You know, depending on which state you're going to, in which state you're coming from, this is another example code thing that I did. It's based on a triple by Gal Shearer, who is an amazing designer. And this is a fun. Edit your week app, or you could just delete all the weekdays and stretch out your weekend. And hopefully it does it for you in real life. But I had to break down this animation and figure out what exactly is going on.

So I realized there's a start. States. And then there's a selecting state when you're dragging your mouse, and then there's a selected state, and so from the selected state. Now, dragging your mouse does something difference. Now we go to the dragging state, and so when you drag and you move over the trashcan, it goes to the disposed state. And then once you start grabbing a state's now moving, your mouse does something different. So these are the exact same events, but they're all doing something completely different, depending on which state you're on. And so I use the finite state machine. I used X data actually in order to create this and to style it using CSS as well. And so with this, I was able to generate a graph of my application logic automatically using that exact same nights, a machine that I define within my application.

So not only does it help me for styling, but it helps me visualize the logic I have. And this is why I think there's so much potential in defining your user interfaces as finite state machines and statecharts. Because you can do things like this and more so in more complex user interface animations. I even had to use things like data show and data hide. Now these are my own data attribute selectors that I've created, and you are more than welcome to create your own. This sort of reminds me of Angular version 1, but I sort of like it still. So we have data show loading, which means. Show this element when we're in a loading state and then we have data hide loading, which means the opposite hi this element when we're in the loading state. And so what we could do is we could add a little bit of JavaScript, and that JavaScript can help us determine which one of these should be active.

And so this is just a three-step process. First of all, we set the data state attribute on the application to what state were in, and then we remove all of the data. Active attributes. Just so that we have a nice clean slate. And then we determine which one of the elements should be showing or hiding, basically which one of the elements should have that data active attributes. So I use this in this application over here, which is also available on CodePen. If you go to the link. Where you could do different things in different states, and things are hiding or showing based on which state they are. Which means that now we have everything contained in one nice screen, instead of having to go to another screen to another screen. Or having things just jump out and hide and disappear immediately. Like you're so used to seeing in the applications today, basically, I'm saying that we have an opportunity to create nicer styles and a nicer user experience for our users by using these data attribute selectors and using these finite state machines in our application.

Play from 4:00

15:00 Instead, we should make our code and organize it in a…

And the key is, instead of thinking and just events and states, we think in transitions. We don't think about just what happened, don't think about just what state were in. We think about what's the previous dates, what events occurred to transition from that previous dates to the next states, and was the overall transition. Then we could visualize this in our application and create these state machines and statecharts that we could even hand off to a designer or another stakeholder and get them to either approve or reject, or say this needs to be that. And just have a nice visual idea of how our application is both supposed to look and behave. Because our apps aren't just about how they look anymore, they're about how are they supposed to behave. And what does the user expect when they're playing around with the app and doing certain? Things so with XState? You could also visualize your state machines, just like I talked about, by copying and pasting your machine code right into the visualizer.

And it shows you in a very dynamic way what happens when you're in a certain state and a certain event happens. And then what the next state is going to be? And there's other tools like this in the wild, such as sketch systems, which is a really, really cool tool. It shows you all of your states in its own little DSL, so it's not exactly that object format. It's arguably easier to type, but you visually see which state your application is in. And then you could write a little bit of prototype code, such as this Google map viewer that you see over here. And you could, you could play around with it. And you could manually trigger states and trigger events and get a very good idea of what are all the possible states that your app could be in. So there's a lot of resources for you available if you want to get more into this subject.

I, I could only cover so much in 18 minutes. There's the world of statecharts, where it's a nice, gentle introduction to why we should be using finite state machines and statecharts. There's also the spectrum community, where you could ask any question, and it's a nice format for just discussing all of these certain things. And also, of course, the exit documentation at ext.js. Org, Slash docs. And so basically, my goal is that we want to make our code do more than just doing whatever we're asked to do. Like, we don't want to make our code just feel like class, let's not add loading, we don't just want to make our code do. Like, I don't know, disabling a Pion or something. Instead, we should make our code and organize it in a way where we could analyze our application and know exactly how many states are in our application. What all these states are, how these states connects to each other, what events happen, and much more than that.

And we could even use this to automatically test, visualize, analyze, simulate our applications, and more. So, basically, I want us to make our code do more. Thank you so much, CSS.

Play from 15:00

Related talks