 So this is my book with Hugo. And I guess you can tweet, hey, Henriya Vodka, or you can instead tweet at Hugo. I think that's funnier than tweeting to me, but you can see see me if you want. I've got a copy of the book. I'll give it to you. Find me afterwards. So I was doing my debate prep last night. And I thought this was a pretty great idea. So I'll just, I don't know, stand with the. But then I was trying to figure out our declaration of independence. So this is the DAO of web design by John Alsop on a blue screen. So maybe that's my background. I don't know. All right, yeah, so SAS maps. I've got the best SAS maps. No one has SAS maps like mine. Mine are great. So now I'm going to dodge the question and talk about design systems. I didn't build this. But it's a great design system. Design systems are really popular right now. So this is the lightning design system from Salesforce. And then MailChimp has their grids. And Lonely Planet has their colors. And there's these numbers five through eight here. Those come from the New York Transit Authority, Graphic Standards Manual, which Gina gave me. That was published in 1970. So these aren't a new thing, the style guides, pattern libraries. The great part of that one is it comes with this, which is, how do you build the sign that the numbers five through eight are going to appear on? And I love that page. Because my question is, OK, I see your pattern. How am I supposed to build it? Because consistent designs have to have consistent code behind them. You're not going to get consistent output without consistent input. So that's what I'm going to talk about. There's various ways to build things. You can get creative. Sarah Drasner talked about that. So there she is. And the context matters. So it's going to be different if you're internal versus consulting. You're going to have different solutions to these problems. You're going to need different tools. Are you insta face? A lot of the systems that come out that are popular come from these huge companies. And we're not all these huge companies. We don't all need these solutions. So keep that in mind when you're pulling in tools. Is it 1970? If it's 1970, you're going to come up with different solutions than now. This is not 1970. I don't know when that was. Sometime in the 80s, 90s that he was showing there. I also have one. So that's my team. I think before we started, I guess that's 94. I think we're doing a card sort there. This is the team now, Odd Bird. We're doing consulting. So we're moving quickly from project to project. And that means we need tools that will move with us and adapt with us. So that's very different from what Twitter needs or what Facebook needs. Because our product is the good architecture. We're working very quickly to develop a starting point for a client and then hand it off to their internal team. So what we're developing for them is less the product and more the architecture that they will work with. So we need web patterns. And web patterns start with code. And this is a grid system if you just put the math in your SAS. And this is what we were doing back in the day. This is based on Natalie Downs fluid systems. And that's ugly and meaningless. So patterns are there to add meaning. And that's what we're going to be looking at today is how do we add meaning to our code and how can SAS maps help us do that? Sarah Drasner said something like code is communication. So that's good. And in case we're confused, CSS is already for pattern making. That's the reason it was invented. We had a problem where we didn't have patterns in our code. And so we invented CSS. What did you think classes were for? Somebody else said the cascade is your friend. That's an anonymous quote. I won't out them here. But we're going to look at how to use SAS to do this. Because SAS is specifically to help add more meaning to our CSS code like you saw with the grid system. So we're going to try to create meaningful abstractions. So the first thing we'll look at is theme configuration. This is one thing that I use SAS maps for quite a bit. I need this. I need a brand color palette. So how am I going to represent that in my code? I wish I could use these variables. That's not what I'm going to do. But here's the old system. Individual variables for each color and maybe some naming system to say that they're all colors. These are brand colors and then individual ones. So that's a fine system. And I can access those pretty easily. I can just call the variable. But maps can help us make that a little more meaningful. Here I've got a map that has various colors in it and each one defined. So maps are just a variable type in SAS. Like having numbers or strings or lists, you can have a map. And it works in a similar way. And inside of it, it can also have those different types. It may look familiar. It's very similar to a JavaScript object or a Python object or any other language. These are known things. We've worked with them before. And it looks a lot like CSS. If any of you remember CSS, we used to use it back in the 90s. You have a selector and then your properties and the values. And it's very much like that. The selector is replaced by a variable name. And then we have parentheses instead of curly braces, key value, similar to property value, but comma delimited. So you can do anything you want inside of a map. They can get a little bit crazy and I dare you. This is a terrible idea. There is no reason to do most of this. I love this when you can use math as the key. And you can actually call that. Yeah, you can call that here map get OMG stop 8. 8 is the result of that math. It works. You can use emoji. You can use anything you want in there. To get something out of a map like you saw, you use the map get function. So this is a little bit less direct than a variable. So this is one of the downsides of maps is that to get anything out of the map, we have to both call the map and the key. So it's a little bit less direct. But map get colors brand blue does get us the color. And there's ways to add extra sugar around that, which I'll show you. So you can also merge maps together. So let's say we have a map of our cools and a map of our warms. Map merge cools warms. That will push them together. And we get this, one map with both of those keys. So what happens then, that's a good way to add a key and value pair to your map is to merge two maps together. There is no other way to do it. That's how you do it. If they have the same key, so here we've got brand colors blue and pink, and over there, theme colors pink. And it's a different, it's a black pink. It's sort of a blackish pink. But if you merge them, what do you think you're going to get out of that? Depends which order you merge them in. So the second map that you put in map merge, that's going to be the one that takes precedence. And we'll end up with our blue and our black pink. So the pink is overridden in that case. So one of the interesting things, the reason maps exist is because everybody asked for dynamic variable names. Being able to automatically generate a variable, and Chris and Natalie said no, and instead they added maps. And so maps exist for this ability to automatically create a name and give it a value. So here we're just doing a loop. Each name color in colors, so that's going to take each key and each value. And we're going to create a new key with a new value to add to that map. So in the end, we have, after running it through that, we have now a complement for each color in our map. So you can automatically generate from one set of names, you can generate more and keep adding to your map, which can be useful. Here's the problem. If I want to reference one color and base it on another color, so here I've got a gray that's a desaturated version of the brand blue, but the problem is I'm going to get an error there. That variable hasn't been defined yet when I'm trying to call it. So that's a problem if I'm trying to represent my colors in a map. What am I going to do about that? So I have my work around. You can decide whether it works for you. I wanted to be able to do the first part of it with self-reference. So here I've got a brand pink, which is pink again. So that's good. And then Escher is referencing brand pink. Goodall Escher, Bach, Goodall, and Kevin Bacon, finally. So at the end of this, I want Kevin Bacon to give me the brand pink. So I need to do self-reference. So I made a color function that looks up, grabs the color if it's there. And then it checks again to see if that color is still a key in the map. And if it is, it calls itself. So I've got this self-referential recursive function that will keep looking at the map until it gets to the end. So that's a good start. The other problem then was making changes to it. So I wanted to desaturate it by 80%. So what I do is I define that in the map just in my own syntax. And then my color function knows how to make that change. So that's my solution right now to using maps that can do self-reference and can make adjustments to themselves is that you define it now like this and you calculate it later in a function. And that works pretty well for me. I get my Kevin Bacon color right there. So I feel pretty good about that hack. But a lot of people tell me that it's over-engineered. So I feel a little worse about it. But the thing is you've got to know the trade-offs. With every solution, with every hack, there's trade-offs. And there's reasons to use it and reasons not to use it. And so one of the reasons that I like this is because it makes my code readable by both humans and machines. People can look at that. All of my colors are in one place. They're inside of one single variable. And the computer understands it. I can have my computer loop through that map and give me back all of my colors and do things with them. And that's useful. I can access them dynamically like this in a way that I can't with variables. I can't dynamically call a variable. But I can dynamically call a map key. So I can write mixins that access it. I can also export it to JSON, which is super useful. We use this all the time for generating our style guides. So because it's in a map, we can just pass that map to JSON, pass that into our SAS doc theme that understands it. And we have a style guide generated automatically based on the same code that's giving us our colors. Other people like to use modular scales. I've played with them some. If you believe in magic, you can use the golden ratio and then make adjustments up and down the scale. We've heard about this. But here's a map of named ratios. We've got an octave and a major seventh. People like using these musical ratios. And then a map of sizes. And again, we've got self-reference and adjustments. So I want to take the root and take it a fifth up. And so I have a size function that does that. So I can grab gutter. Gutter knows to look at rhythm. Rhythm takes the root and bumps it up a fifth. I also use maps for typography. I put all of the information about a font in a map. And there I've got a nested map. So I've got a map, the key is body and the value is another map. So I do that with each of my fonts. And that has all of the data that I need for my font. And at the end of that, I can automatically import all my web fonts because they're all listed in one place. And I can call font family body. And it knows to grab the name and the stack. And it knows what variations are available. And then having both sizes and typography in maps, I can again export them and access them directly from my style guide generator. So there I get as soon as I have the map set up, I have automatically my font specimens. So is it over-engineered? I don't know. You've probably heard this a few times today and yesterday. It depends. It depends on what you're doing. So here's another thing. I have a project called True. It's unit tests for SAS. So it gives you this output of successful tests. And True uses maps for data storage, which is not something that you ever are going to need in designing your website, most likely. But SAS is a Turing-complete language, so we can write all sorts of programs. So in this, this is what tests look like. We've got a test module and a test name and then various assertions. And it's all written in SAS. And it all runs in SAS. But this is the database for it. We've got a variable as the number of runs, the number that have passed, and the number that have failed. And we start them all at zero. And then we just increment them. Every time we finish a test, we run this True update. And it runs through that map and updates it by one. Again, we've got the dynamic key call there. So we can say whatever result we got, update that one by one. And then I wrote a map increment function that goes through each key and each value and increments by one. So then the True output can just be this. We've got our database. And we just call the various parts of our database and return them. So that's using SAS maps for a database, which you won't need. We did some more things that you won't need in Susie. And again, because we were developing toolkits, we're doing different things than what you're going to need day to day building a website. But I think it's fun. And when SAS maps came out, we were in the middle of building Susie too. And I rewrote it right away from scratch because maps changed everything we could do. You should stick around for this talk about how you don't need grid systems. She's right. And you probably don't need Susie. But it's there. And it does some cool things with maps. So I'll show it to you. So the first thing that we do is the configuration. And that gets us a really small footprint. We have one variable that's public that you can use called Susie. And all of your Susie configuration goes inside that one variable because it's a map. And then we have another map that's the Susie defaults. So that's where we give our factory settings. And then you can also pass in inline override. So every function in Susie takes a map argument if you want to pass in an override. So that's just so that everything can happen on the fly if you want to. So then to find the current settings, we just have a function here that takes any list of maps and merges them together in a row. So we start with the factory defaults. We merge in the user defaults. Then we merge in any overrides. And we can do that as far as we need to. And it gives us back one map that is all of the settings adjusted however they need to be. So you can actually then in Susie have multiple maps that are different configurations for Susie. And you can keep them all around. And you can pass them in on the fly. So you can say, in this case, we'll use the defaults here for the first one. But then inside of this media query, we'll pass in a completely different configuration. And inside of this larger media query, we'll pass in another configuration. And you can create arbitrary, complete configurations inside of maps however you want. And then pass them into the system and it understands them. So I found that pretty useful. I thought that's a great way to do a lot of user control, a lot of configuration on the fly. We also decided to do language parsing. And part of the reason for this is Susie is a grid system. Susie doesn't have an opinion about what type of grid system you use. It doesn't care whether you're using floats or Flexbox or tables or it doesn't matter. Susie is just there to do the math and it doesn't care about the system. So there's lots of different configurations that you could want. And we didn't want you having to do a list of arguments where if we add one argument for asymmetrical grids that is going to get in the way of you when you're not doing asymmetrical grids, that's a pain. That's not a great UX. So instead, we created a shorthand syntax that looks more like CSS shorthand values where we created keywords for things. So instead of 3 comma 12 comma location 1 comma container spread wide, you can say first 3 of 12 wide. And we did that using maps. So here's the first attempt. We have an options variable. And we have each setting that we want location and spread that we want keywords for and we listed out the keywords. And I forgot a comma there. You're going to want that comma there. So here we have these three keywords are going to attach to this. That's not very performant. It looks great, but it actually performs a lot better when we switch to this, which is list out each keyword as a key in the map and then say what setting it should attach to. And that's a little bit more redundant as code, but it performs much better because we don't have to do two loops. We do a single loop to access it. And that looks roughly like this. This is a super simplified parse function that takes in a list and it pulls it apart. And for each item in the list, it checks that map. And if the keyword is in that map, it replaces it or it assigns it to that setting. And in the end, we get something like this. So here's our options. If we parse first wider, it will return this map. It says we've got location as first and spread as wider. So again, we started with a map. We passed in a list. We got back a map. And this is how we do everything in Susie. It's all maps getting passed around because they're great for storing data like this. So we take this map. Oh, if you're wondering what spread is, I've got it here. Spread is the fact that when I say span 3, you don't know whether to include that gutter or this gutter. So we've got narrow, wide, and wider as options. So the next step that we need to do is normalize it because spread is actually, how many gutters do we have in relation to our columns? So we want narrow to mean minus 1. We want wide to mean 0. And we want wider to mean 1. So again, we've already parsed out that the spread is wider. Now we want to know what does wider mean. And again, we use a map to grab that value and return it. I guess I've got some overflow issues here. Sorry about that. So in the end, we can parse it, merge it, and normalize it. And we get back. We've passed in last three of six wide by 140 pixels. And it normalizes and normalizes all of that and returns a config map that the Susie internals can understand that shows all of the things, even the ones that weren't passed in. They got merged down to the factory default. And now we know everything we need to know about the grid based on this shorthand that you've passed in. Yeah. So then we take that map and we want to convert it into a map that represents CSS. Chris Epstein hates when I do this. And he has good reasons for that. But I do it anyway. He doesn't always get what he wants. And the usefulness is that I can pass around this pseudo CSS and make changes to it. And I'll show you why. But here it is. We've got a get gutters function that returns a map of the gutters we're going to want based on whether it's split or not split. So if it's split, we get a margin left and a margin right that are both half the gutter. And otherwise, we just get whatever position was sent, either left or right. And we just return that. So the results are if we wanted split, we get margin left, one rent, and margin right, one rem. And if we ask for margin left, we get it all on the margin left. And then I can just render that. And rendering is a pretty simple loop. I ask for the key and the value. And I output the key as a property and the value as a CSS value. And suddenly, I've just turned my map directly into CSS. And there's more complex versions of this. You can have a rendering function that would then do nested rendering. And you can do very complex SAS rendering that way. So that's what it looks like. The reason that we use it is for something like this. We've got multidirectional site support. So we want to be able to pass in from the user, is this left to right or right to left? And Susie otherwise just talks about before and after. For the rest of the calculation, Susie is just thinking before and after. And then once we get to rendering, we look at replace. We say, OK, if the direction is right to left, before is on the right, and after is on the left. Otherwise, before is on the left, and after is on the right. And so we can right at the rendering stage switch those out. So that's useful. Now we can just say, we want gutters right to left, and we have margin right to M. So why does Chris care that I do that and I shouldn't do that? It's because in SAS, they have a very strong belief that CSS should look like CSS. And that's useful. I think they're right. In most cases, you want users to be able to come into your code. And code is communication. You're communicating to the people that are looking at this later. And when they see CSS, it should be CSS. And when they see SAS, it should be SAS. And there's a reason that those two have very distinct syntaxes. And this breaks that rule. So keep an eye out for the content function. They're working on a way to actually manipulate CSS directly in SAS. And then we won't need to do this. Another thing you can do is store your break points in a map. And this is sort of the way that I came down to it. Initially, I was doing very complex break points stored here. And I decided the complexity should be moved out of the map. And instead, I'm just going to document the most basic data, which is where? Where is the split? And then I can put together more complex media query strings out of that data. But basically, I have a break function that's like the other functions I showed you, recursive, so that I can reference different numbers in that map. And then these prepositional mix-ins that just give me the shortcut to below, above, and in between for min, max, and both. And then I can call named queries using those. So it's just a really simple way to keep all my media queries in one place and access them by name. So there's a lot of other things you can do with maps. You can remove pairs. Map Remove is really helpful when you need to do that. I actually don't need to do that a lot, but there it is. You can fire me from the conference. Just Map Remove CSS Conf Speaker. And I'm not there anymore. You can also do some introspection. There's map has key, map keys, map values. So you can return a list of all the keys, a list of all the values. You can check to see if a key exists. Those are all handy. All of the list functions in SAS work for SAS maps. They just treat the map as a list of pairs. And you can find out more from Hugo and Yuna and other places online. There's a lot of other good ideas for using maps. So that's it. Map, map, map, map. I didn't want to have any more John Cage experiments, so we decided just to go about it this way. Fascinating, mind blown, map blown. Question for you, do maps add to compile time? Not that I've noticed. Oh, so nested maps are very hard to access. So if you're using nested maps, that does add to compile time quite a bit. A normal map function call shouldn't. Can you talk a little bit or share like your philosophy around the tools, especially accoutrements? I remember when you were working on these and we were working together, and I was very, very interested in that. So I think it'd be cool for folks to know. Yeah, my philosophy is you should over-engineer things. I think that's how I start. I mean, I think it's great. You take something that you're doing repeatedly in your code or something that you think can be turned into a component and you turn it into a component, and then when you try to reuse it, you'll find all of the places that you over-did it, that you got too specific. And in the end, you can slowly weed out. The more often you use a component like that or a tool, every time you come back to it, you can find where did I get too opinionated. And in the end, a lot of my tools end up very functions only, as few mix-ins as possible, because mix-ins are where the opinions happen. That's where you're actually generating CSS code. And so most of the tools I want to be able to take from project to project and have the actual output be different, but have the logic behind it be the same. So then I get lots of functions that figure things out, and then I can use them however I need to, adding the CSS later. Mix-ins are where the opinions happen. That's a great quote. No wonderful soundbite to close out this first half of the conference. Thank you, Miriam, for your talk.