 While Luke John was a developer at Seven West Media, he led the adoption of CSS in JS at the West Australian and at Perth Now, and he's also a leading contributor to the CSS in JS framework Glamorous. He's going to tell us all about using CSS in JS with type systems like TypeScript or Flow with TypeSafe CSS in JS. Please welcome Luke. So as you just heard, I'm here today to talk about type safety and CSS in JS, both of which are very shiny, quite new, and are really fun to work with. So just a bit of background around how we got to type safety and CSS in JS. We've had this massive rise of JS everywhere. We've got frameworks like React and Angular, and as those frameworks got bigger and more mature, we've started to see type systems from the likes of Facebook and Microsoft, where you'll have some level of type safety with your JavaScript, and that language will just compile to JavaScript, and it helps make those large code bases a bit more maintainable and flexible for future usage. So on the CSS in JS side of things, we should probably start with what CSS outside JS is. With these modern frameworks where you're doing component-based development, your markup is a function of state. It's quite easy to understand based on this URL and flowing through your JSX if you're using React exactly what markup you're going to be getting. When it comes to styling side of things, it's a bit more of an unknown. A lot of frameworks will be importing perhaps four different CSS external files, then they might be using Webpack loaders for SAS to have some level of component-based styling, but the CSS is not necessarily determinable based on just state. CSS in JS, both your markup and your styling is a function of your state, so based on a URL or a user being logged in, you can work out exactly what styles, and only those styles are actually created for the page. So it enables pattern things like critical CSS and really helps out if you're doing server-side rendering to only shoot down the CSS which is needed for any particular page. So it's really awesome. So as we started there before, a lot of this talk is going to be using React. However, CSS in JS exists with a bunch of the other frameworks now, but when you add type safety to something like React and CSS in JS, you start to live in the happily ever after where things are really shiny and you enjoy coding all the things. So we'll just start with where CSS in JS started. The term was first popularized by inline styles in React, where as you're doing your templating inside your JSX there, there's a style property available which you can just add some inline styles. Those will get added inline in your resulting HTML markup, but then later on other frameworks saw that pattern and thought this is really awesome, and they created some other handy helpers that would extract all of that and put it into your head or do other things, but the basic pattern was very similar. And this example here is actually got it turned on with TypeScript. We'll just have a look at the typings for where we started with type safety in CSS in JS. They just typed a subset of CSS properties. For some of them, they had pretty strong type safety, so this inline content property here, you'll get autocomplete on the key and the value, and if you do a misspelling, which is quite common in CSS, you'll get immediate feedback about that. Unfortunately, there's a lot of CSS properties that allow quite flexible values, border 10px solid red. You can't give type safety for that sort of thing just using this pattern, so they're just typed in strings. So this was the inline styles popularized by React. What was happening in JS about the same time is the language was getting new features. One particular new features were template literals, of which the more advanced version was the tagged template literals, and these would allow you to pass template literals with function. That's that bottom one, that's just grabbed from MDN the examples, and so people who were doing a lot of React development who had traditionally been putting a lot of work into other solutions for CSS at scale saw this and thought, wow, we could do some really awesome stuff with this. That slide is far too small to see. I apologize, but essentially you've got a function up the top there that you've set up, which would allow users to use a string as an API for writing their CSS within JavaScript and then have expressions within that. Of course, this is a bit of a back step when it comes to type safety for CSS in JS, because we've gone back to a string interface, and you're not going to get a lot of safety there. However, it really popularized the based styling pattern where you could have functions which you could pass props to, so where you're doing component-based development, you could pass props into what was ever creating your CSS, and rather than having three different class names that you're going to select from based on those props, you could just write the exact CSS in each case, which was really handy. And here's just an example using styled components. You get autocomplete on the DOM tags of the interface, but then once you're inside that tag template, you have no real safety, but then down below there, what you can see is you're using, so that then returns a React component, which you can just use inside your JSX or inside your templating, and it has the props flowing through quite nicely. So then we had Glamrs came out early-ish last year, I think about April, and it followed on from some of the APIs that had been pioneered by the styled components people, but they quite liked the CSS object interface for writing your CSS, so they mixed that pattern of having component factories with CSS objects, and this was really exciting for where I was working at the time we'd been using processes, peer review conventions, such as BEM, to protect our code base from bad things happening, and this was working for most of the time for our front-end devs with strong CSS knowledge, however that wasn't all of us, we had people with a range of experience across domains, and so bad things were still happening, CSS and JS with something like Glamrs, which we would be able to type, look like it offered some technical solutions to the problems we were having. So the initial typings, somebody was obviously using it on a project, they'd written some typings internally, and they went to contribute that back to the community. What had been happening a lot of that time was people would put the typings on definitely typed, and just sort of, which is a, it's a repo set up by Microsoft, which makes all the types available to install, but about this time people were starting to bundle definitions with the frameworks, so somebody thought, oh yeah, I'll try and get those landed there. The person who'd led the development of it wasn't quite sure about it, but the feedback he got, and it's quite true, is that it's a lot better for consumers when you have bundle typed, it implies that the project is type-friendly, be those bundle types flow or typescript, and when you change or add something, you can expect a PR shortly to add type support for that, and a bunch of other things, it does however lead to a bit of extra contributor overhead because you've got more activity going on. So those initial definitions, they just covered a small subset of the API of Glomerous, and there was also a clear expectation set around how those types would be maintained, it would be up to typescript users to maintain those, which was everybody accepted and was great. What was also very helpful was the project had very clear contribution guidelines, this provided clear guidance for the initial PR to add them, but also for anybody who wanted to extend the typings or add additional features, it was very clear how they could add those features and get them landed. So if you're adding types to a framework, highly recommend having contribution guidelines well laid out. So initially it was set up with the types separated out into a typings folder, which is a fairly standard thing within typescript definitions and also flow ones, and then there's just a field you put in your package.json with the location of where the typings are for when typescript does type acquisition on usage, and it also just had a simple test file. The way testing works for a lot of typescript definitions is that they just have a single file that just goes through all the uses that the types provide and make sure that the compiler doesn't generate any error output. As you can imagine, there's some problems with that, but we'll touch on that in a bit. Is that showing up there? Oh, fantastic. So this is just some of the APIs that Glamorous had to give an idea as we jump into the typings that it has, what sort of support it needed, and the range of types. So we should just start with some Glamorous DOM and built-in component factories. The second signature there, Glamorous.divs, just a shortcut to the first one. It also had built-in components, so you could just import a div from Glamorous nameshaced exports, and also Glamorous.div would work as well. This actually presented a bit of a challenge with typescript, because there's no easy way to share named exports with interfaces for typings, so there was a fair bit of type duplication, also with the Glamorous.div for the other built-in component factories. Then Glamorous could also take components when you set up a component factory, so that needed to be typed, and it also had options which would then change the behavior of the resulting component. So if you set that props.css overrides is equal to true, when you finally use that component, you then have available all of the CSS properties as props on that component, which will get added up into the CSS. And then there's also with component API, which you can change what component the eventuating Glamorous component factory returns. So a huge API service with things at the top level impacting things a few levels down, which was quite interesting. So in terms of type coverage, we'll just touch on initially the CSS and SVG properties. So we'll start with the, these were very similar to the React typings. Initially, we just used the React CSS property typings. Then we had a great PR from a contributor who vended those, but then went through property by property, and checking the MDM documentation, and tidying up the types. So for something such as display or float, I think, where the React typings for would just accept any string. It was tightened up a bit. So for the autocomplete, it was really helpful. And for misspellings, it would avoid that. Then we just also needed to support the pseudo properties. So that's how you would use that. And then when you're using that anywhere where it's being typed, you're going to get type safety, which is really handy. You also get autocomplete. It's quite wonderful. Then we had a lossy version of the CSS properties. The way that a lot of the CSS and JS frameworks work with their CSS objects is they support something similar to SAS and other things where you can have contextual selectors, but also just for media queries where you have another object underneath that. Because of this particular type index that would be getting added onto it, it meant that for the previous CSS objects, you actually lose type safety. So whilst if you've typed float correctly, you get type safety on the properties and autocomplete, if you accidentally type flat, it's going to pass, which is a bit sad. There are some other alternative CSS and JS frameworks that have alternative syntax for this sort of stuff. They're not quite as widely adopted, but should be interesting in the future. And so then just using it all together, it's kind of like CSS. It's just using an JS object syntax, but it all fits together quite well. So those are just some examples. So style arguments. These are the things that when we saw earlier on the Glamorous API, you would set up a component factory with either a DOM tag or a component. These are the arguments that you then pass to that to create your CSS. It can take a function, which would return some properties or a string or also an array of properties and strings. It would also accept a style array, so an array of all of those things. And then a style argument, sort of a combination of those, plus a couple of other signatures that I've just taken out of that for privacy's sake. But then for the style arguments, that's for dynamic styles. You can see their props are getting passed to as a generic, so that they're available in the style functions. A lot of the time when you do in CSS and JS, you won't actually use props flowing through. And so there's also a simpler static style argument which also helps out with the compiler output if something goes wrong. It's a bit simpler in those cases. It's a bit easier to read. And so here's an example of how those types are then checked on a component. So here that will pass because it's a CSS properties object and the values are correct. Here this would pass because it's a function. I haven't actually typed this but the props, if I had typed on the example component, would be typed to something and then you can use that in determining the output of the display. Here it would pass because it's going to return a string. Here it would pass because it's going to return an array of all those things. So quite a lot of different things you could do with style arguments or with support. And so we'll next look at just a single one of the glamorous API shapes that we saw earlier where you pass a component factory to the glamorous function which will return you a glamorous component factory. So this is a heavily trimmed down version of the types. So we'll start with the main export from the definitions which is this glamorous interface. Then the glamorous interface. This looks really noisy and messy. Essentially it's just a object which takes a component with some optional objects and returns a glamorous component factories. We've had to use a lot of generics here because of all the different changes that happen downstream in that glamorous component factory and then the resulting glamorous component that is created from that in order to get type safety there. But you don't actually have to pass any of those generics all the time. Most of the time if you're doing something dynamic with that component you will just pass the props and the rest will just all match together if you've typed it on that component which is really nice. We use overloading with this signature because depending on the options that you pass in again the signature of the API of the resulting glamorous component is quite different. It will accept if you've set the props a CSS overrides options to true a bit down you can just use those props and so they need to be typed as well. So that's the initial creation. Then we get into the actual component factory so that returns a component factory. Component factories just take those generic props and initially we start with a overload for the static styles. We put that at the top because even though it should match down the bottom as well. So the next two we'll see. There was a bit of a memory leak issue with TypeScript and this just has CSS properties as arguments pretty much. You can't do any functions or anything in there. Then we have an overload for if your props have got a theme. So there's a concept of a theme providing glamorous which I won't go into but essentially there's some magical props that can end up on that component and be available for whilst you're doing your styling and we also use as part of this. So we say that when they set up the props for the component factory so for those functions that we turn the styles the theme is going to be available but then when we actually return the glamorous component from this all those props that are there they need to be set up but the theme doesn't. TypeScript doesn't have subtraction types and it kind of gives something similar but it does actually pollute the IntelliSense and compiler output so I would recommend avoiding where possible. And then finally we just have an overload for the component factory where there's just the props no theme which is quite handy and just keeps the types a bit simpler. And then finally the component factory returns a glamorous component which is just a react component factory with various external props and extra glamorous props so the external props in some case will just be the props sometimes it will be the CSS properties if we had to set that props or CSS overrides earlier on and then it also has some methods available on it such as with component which we saw earlier which will let you have all the props and all the typing but for a slightly different component which is this one here and there's a couple of other ones there. So when it was getting typed initially it started off with just a very small subset of what was available in glamorous it was quite understandable easy to use but there was a lot of stuff that you couldn't do. Then we started to add overloads and generics to provide support for both the alternative interfaces but also the generics to get better type safety on some things that initially that were essentially such as props just a any so you didn't have that much safety. Then we introduced the omit type to support the theme being excluded. It seemed like a really good idea at the time there was probably an alternative syntax that we could have used using an additional generic but this less magical type and had we have used that it would have led to much cleaner compiler output and when users are having errors easy to understand what's going on and then eventually we started having to use overloads coupled with generics which would be extended from things so that we could limit this particular generic to match this shape and then we'll do this thing and it all got very complicated very fast that was a very small subset in some places there were extra overloads for things like CSS properties versus SVG properties unfortunately a limitation of the compiler TypeScript compiler at this point in time meant that so the IntelliSense this point in time meant that if you try and union that there you would just lose autocomplete it would still type but the developer experience would get worse and so pretty massive amount of types as you can imagine things go wrong when you've got such large types and the standard definition strategy of just testing what is meant to work and making sure that the compiler succeeds wasn't really sufficient to allow additional contributors to easily add things and not have to worry about accidentally breaking something else so we introduced some additional testing we just set up a should fail file adjacent to the test file and then we just used just snapshotting to run the compiler on that file as well make sure that everything that would set up to fail was ending up in that snapshot and that did pick up quite regularly where people would sort of think oh I can just make this change here and not realize that that would actually knock out something else with one of the other glamorous interfaces so really handy pattern there's some other strategies there now that a little less sort of hacky here we're just using spawn to run the compiler and snapshot it but definitely recommend testing both sides and also just on the testing side of things you'll often find that if you're using glamorous as part of something that else which you're generating definitions from it has subtly different behavior with the compiler so recommend testing in definitions declarations mode with your tests another thing that we did with the types in some places was we did things that weren't strictly necessary but they improved the developer experience in one case if we used a union for type the you would get type safety but when you're using vs code or another editor that's using IntelliSense you would lose autocomplete which was a really handy feature and people who aren't using type script are also getting benefits off it so we would do some sort of hacky things it's worthwhile commenting when you do that to make sure that in a couple of months time both flow and type script are moving really rapidly a lot of things this one we found after about two months we could actually just move to a spread and a union and it all worked nicely so worth commenting those things sometimes you need to get a bit creative when you're setting up types as well or using it with a type system we had a fun one where the roll-up compilation for glamorous was incompatible with how type script was doing the importing so we actually added this to the main glamorous source file just to allow usage with type script but where you do get creative with things like this again it's really worthwhile adding some comments so that the next person knows what's going on and it's a pretty massive amount of typing there a large surface area with the API there were some pretty massive mistakes that were made which were in most cases quite avoidable a couple of those one we had this issue opened up it was quite an interesting one so there was a memory leak with the typings which meant that when you were using it with a project with a lot of glamorous components pretty quickly the compiler would barf and you would get out of memory there was it was interesting because the compiler worked fine with tests and depending on project size and also usage of the types it would just never show up and the way that it part of why it happened was there a bunch of changes to the types getting added to a vnext branch so nobody was testing them in production all of a sudden vnext launched there were all these new types that landed and how they interacted led to this memory leak so it took a few days to sort of work out where that was and get an update going another one was the definitions can sometimes get quite overwhelming this was a very politely worded issue from somebody where they had gone to use the glamorous with TypeScript and the autocomplete was just out of control as you start to type in the arguments you would get this enormous window pop up with all the different options and it would hijack your arrow keys as to go through the different options so this one was quite well in some ways easily solved went back in and restructured some of the typings so that the output for things like IntelliSense but also just compile output if there's an error was more readable and more clear a bit more concise and yeah so definitely recommend limiting type coverage when you're typing the framework like this if the framework doesn't intend a usage don't type it there were a couple of PRs landed early on that were landed purely to support some some stuff that had started happening by mistake and was never really intended by the framework or the lead contributors and because that got typed early it's hard to remove type coverage for anything that you've added because people will complain when they do a update and things break for a non-major also try to ensure types don't make workflows difficult like IntelliSense as you just saw and should always use comments to help consumers this will win you a lot of love for both TypeScript and Flow and just JS developers because in things like IntelliSense for APIs that aren't immediately clear just off the name of that property they get a little bit of extra information about how to use it it's also really important with this sort of thing to enable contributors something really awesome that's happened in the last year is that there's now online play boxes where you can set up a full TypeScript and Flow project and import glamorous and have the sort of definitions being used as your typing online and getting error output in browser so setting up a sandbox for people who are having issues to be able to quickly set up a reproduction along with the issue rather than having to set up a whole repo it's really handy and also just obvious things like asking for the version of TypeScript they're using if they are having an issue with the bundle declarations is really handy so we'll now just have a quick look at how you use the CSS and JS with TypeScript so here we have a glamorous component factory with the styles where we're getting autocomplete on both the property name and then the values that it accepts really handy really useful we'll jump straight from that to this is something we're going to now create with CSS and JS and TypeScript so it's a reproduction of the LCA schedule where the it starts at a calendar then just collapse down to a talk by talk by talk and gives us some really clean HTML without lots of wrapping divs or reused CSS or anything like that so we'll start with a timetable react component which if we jump into that and have a look at that's just a glamorous emotion component factory another CSS and JS framework we've got our CSS being returned from this we're going to use the CSS grid we're able to use the props that that's been typed with which has a number of active rooms to set up the amount of columns that we want on that so then if we jump back to the timetable inside of that timetable we're going to have time starts which are just going to show up on the left-hand side as we saw pass that through the information it needs to do that and it's just another react component which then has the time start h3 and then that's going to use the props to set up the CSS to put it in the correct row which is nice and handy there and the correct column as well then we'll jump on to the slot so that's the actual talks as we go through they're going to have a use another component we'll just have a look at the style slot we won't go through all of it that takes a bunch of props which go into the styling again I've collapsed this so it doesn't include all the styles required but it's going to grab the using the room index it's going to run another method which gets room color that just takes a CSS color value and adjusts the hue left and right so that we get the nice colors going along and we can have sort of infinite amount of rooms or just a few and then it's going to select the column and the amount of rows that talk should span based on its time start and its time offset so as we've been going through here only the CSS that is absolutely required for each of these slots is being generated in cases where the CSS is in common it will de-dupe those styles automatically for you so there's no need for by hand de-duping it will be producing a really nice clean markup there's no need for wrapping divs to sort of enable this kind of nice visual structure and it's really really easy and fast so this whole thing took about 20 minutes to get running which normally with CSS would take an enormous well might take a bit longer to get all of those grids and calls if you weren't doing it inside of JS and being able to use those patterns so we'll just have a look at the future of CSS in JS and some of the things that TypeScript and Flow are enabling the first one this is very limited to TypeScript in particular there's this really cool thing called TypeScript server plugins which give you that string API for CSS that we talked about earlier on when that first came around there's no way to type a string these server plugins actually allow you to create your own sort of type safety for certain things and give autocomplete in editors using IntelliSense and whatnot so here we're actually getting type safe CSS strings in JS which is really powerful and I think very exciting for the future there's also some plays around how you generate types at the moment for Glamrs as you saw there was the glamrs.div coal glamrs with the div string there was the export so import div from Glamrs there was the glamrs.uppercase div which are all doing fairly similar stuff and using the same sets of types but they have to be typed separately and so with generated typings you can sort of automate some of that which both makes it a bit easier for contributors when they want to make a change to some of those big files they can just make it in one place but also gives you a bit more confidence in the definitions because as it is there's a fair bit of copy pasting these are just four of the different files each of which are a couple of hundred lines long another really exciting thing are regex types there's a PR open on the typescript repository at the moment it's probably not going to land for a couple of major couple of versions but it's been actively discussed by the typescript team and looks really exciting not just for CSS and JS but a whole range of other applications and so this isn't actually necessary this isn't anything to do with typescript and CSS and JS this is just really cool things you can do with CSS and JS because it's so shiny you can do CSS snapshotting alongside your markup for your components so if you have things go wrong it's easier to pick up in peer review and also just whilst you're developing traditionally your UI snapshots might contain a couple of class names which you may or may not have set and really no information about the style rules but this sort of opens up a lot more confidence does generate very noisy tests however which brings us to visual snapshots where on a unit level it's really easy when you're using something like CSS and JS to test that that component is pixel perfect for changes so what you can see here and that isn't working is actually you'd never see that if you were just looking at it NPR by running the code but here on the bottom one something's just a couple of pixels off and you pick that stuff up immediately it's really really amazing so just to summarize some of the points we've touched on type systems really help keep code maintainable and really help enable growth of applications when you think about CSS and JS a really nice way to think about it is it's turning it's adding the styling to be a function of the state in addition to your markup you can do inline styles you can have styles thrown up into your head you can do both string and object as an API and you've now got type safety with both of them thanks to things like the server plugins the component factories really enable you as a developer with component based dev to have props impact your styling in terms of when you're actually setting up definitions and decorations for frameworks developers love them it's useful if you keep the type simple it both helps the users and also helps people who want to contribute be able to contribute and you should test types fail as well as succeed it sounds really obvious but within the type script and also flow communities the way that testing is happening currently that is not as common as it should be you can type a lot more than you think but you probably shouldn't it'll just make it harder for people to use the types and contribute to them in the future um compilers aren't perfect but there's lots of ways to work around them and uh yeah types should enable users and not overwhelm them if you're interested in css in js there's a excellent repo on github css in js 101 i highly recommend checking it out it has a thorough overview of all the different options that are out there currently how you would use them and the advantages of the various options these are also some great people to follow on twitter basura in particular when it comes to typescript he wrote a css in js framework with just typescript and is really excellent and all of the other ones kent dodds is the creator of glomerous and the other people up there have created other css in js frameworks really awesome people and thank you very much that is css in js and type safety and i think we've just got a little bit of time for questions about three minutes yep just with generated typing so you've got uh so we've generated so i think the question was we've generated typings uh what's the best way to do that currently where you've got various variations but with common things uh haven't actually done that um yet um there's some interesting ones i think uh ts spoon um was one uh and a few others uh but um there's also things like a babel prevail where if you just have some uh code that you want to use to generate other code on build um enable that quite easily but i haven't actually dived into that yet and it just looks really exciting all right