skip to content

Build a Color Contrast Checker with Eleventy Serverless

Eleventy 1.0 has just been released, and with it comes Eleventy Serverless for on-request page generation. Ben Myers will teach us how to get started with Eleventy Serverless by building a color contrast checker with little to no client-side JavaScript.

Full Transcript

Click to expand the full transcript

Captions provided by White Coat Captioning ( Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.

JASON: Hello, everyone, and welcome to another episode of "Learn with Jason." Today on the show we're bringing back Ben Myers. Ben, how are you doing?

BEN: I'm doing great. And yourself, Jason?

JASON: I'm doing so good. So happy to have you back. I'm really looking forward to today for a few reasons, one it's always great to have you around. Two, we're going to play with Eleventy, as we learned through painful experience on this show, means we're going to do puns all day. And, three, we're going to play with serverless functions which are all things that I enjoy.

BEN: Yeah, absolutely.

JASON: So, before we get too deep into here, do you want to give us background on yourself for those not familiar with your work?

BEN: Sure, I am a software developer at Microsoft, I focus on the front end, and, specifically, I focus on accessibility and core technologies. I'm an advocate for web accessibility, both, like, internally at my company, as well as folks probably know me from -- I host a weekly stream called "Some Antics," where I bring on guests in a format very, very, very inspired from "Learn with Jason." I bring on guests to teach something about building great user experiences for the web in a hands-on way with a focus on accessibility and core web technology. Can you tell I've rehearsed that elevator pitch, Jason? Man, but, yeah, so, I stream about accessibility. I blog about accessibility. Every once in a while, Eleventy comes up. That's another favorite subject of mine. And, so, I'm super, super thrilled to be bringing that to Learn With Jason today.

JASON: I'm super happy to have you here, because I think it's going to be a lot of fun, and, yeah, I think -- color contrast checkers are something that I am fascinated by. I've watched, at one point Chris Bascardi tried to build one, it was doing luminosity checking. It was -- I think it wasn't even HSL. It was something that was really, really cool stuff. And, yeah, just a lot of exciting things happening in that space. But it's also one of those things, where I am so dependent on the robots, because I don't really know how to check color contrast. I just put my two values into a form, and if it gives me a thumbs up, I continue about my business. So, I'm really excited to talk about that, and kind of learn how it all works today.

BEN: Yeah, so, unfortunately, we're not going to get a whole lot into the science of the color contrast, or the math. This isn't so much about color contrast itself, so much as using color contrast as a use case for serverless. That said, color contrast is a fascinating, complex subject. I know you've had Todd Libby on to talk about color contrast. But in itself is a huge subject with a ton of nuance, and it's an ever-growing field, ever-changing field. So, we're going to be using the standard color contrast formula from WCAG2, that's been embedded all the places, user tested, but it's an ever-growing science. And you may hear in the coming years, Web Content Accessibility Guidelines might move to a new color contrast algorithm that's supposed to make scoring a little more natural for folks.

JASON: Sure, sure, sure. Yeah, and I do think that is a good one. Oh, and Brian is already sharing links in the chat to that Todd Lippy episode -- why do I keep saying lippy? Libby, with a "B." All right, so, let's maybe start with the basics. So, the foundation for everything that we're going to be doing today is Eleventy.

BEN: Yes.

JASON: Maybe let's just start there. What is Eleventy, and why should people be excited about it?

BEN: That is an amazing place to start. So, Eleventy is a static site generator, specifically, it is the style of static site generator, try saying that ten times fast, that really borrows from Jekyll. So, the idea here is, I have some content, I have some data. I'd like to match these two together and get static assets, usually HTML. There are, of course, tons of different ways you can do a static site generator. On the completely other side of the spectrum, you have your Gatsbies, which my understanding is they really create JavaScript apps, but with a fast forerunner of HTML that gets replaced with a bunch of JavaScript. So, the Jekylls and Eleventies of the world are focused on we're going to create HTML. It's going to be nice and lightweight, you know, your users aren't downloading a bunch of JavaScript. They are not making client-side API calls or anything like that. It's very fast, very, very cacheable. So, that's really at the heart of what Eleventy is, is just taking content, taking data, mashing them together, creating some, you know, lovely, boring HTML. Which is going to be reliable and fast for your users.

JASON: You know, what I love about that argument, though, is what we -- I feel like what we've done in our heads a little bit as developers, is we've said, like, just HTML and CSS is not enough, right. And I think at some point we kind of flipped the script on this a little bit, where we stopped thinking that, like, at the bottom of most of what we're doing, I'm trying to put some words on a page and maybe a picture or two, so that I can convey information from me to you.

BEN: Yeah.

JASON: And what I love about tools like Eleventy, is they are challenging the idea that has emerged in the, you know, the late 2000s to today, that in order to build a great website, you have to start with JavaScript. Now, I am a largely JavaScript developer, and I'm a huge fan of building really interactive sites, with a lot of JavaScript engagement, and all that good stuff. But I'm not fully sold that you can't build great websites without JavaScript.

BEN: So, what we're going to do today, is we're going to build this color contrast. And at first it's going to have zero client-side JavaScript. It's going to be -- it's going to have navigation, it's going to have some interaction even, but zero client-side JavaScript. Mainly, just to show you how far we can take the platform, so long as we have the server or serverless functions backing us up. And I think it's interesting that you note that we talk about just HTML and just CSS. Because I think there's some truth to that. But I think that's been helped by the platform itself getting a lot better over the past few years. HTML's capabilities have grown. We've only recently got the details summary element. That's a lovely bit of interactive elements that we can use that have the progressive enhancement built right into it. So, there's been that. I think there's also been this growing consciousness around accessibility. Like web accessibility is not a new subject. We've been discussing this since the early '90s. That's when the first draft of the Web Content Accessibility Guidelines came out. The first web accessibility court case was settled in 2000 or 2001 with Bank of America. This isn't a new subject by any stretch, but the growing consciousness around accessibility has caused a lot more people to interrogate their relationship with semantic markup, and how far the platform can take us when we keep things in very expressive markup that, you know, doesn't just help developers understand that, oh, this part is the header, this is the Nav, but also helps machines, devices, really understand our page and take it to the next level. Providing user experiences, such as search engines, or screen reader support. And we've started to see how these things, having a really robust foundation of our markup, really enables something far more than we expected.

JASON: Yeah, yeah. Absolutely. And that's actually something that I've really loved to see as time has gone on, is that the way that we can write with HTML, it's starting to feel like even if you don't apply CSS to the page, you can really create expressive documents now with just HTML. You know, you mentioned that detailed summary element. I live and die by that thing. I use that so much, because I always want to just toggle extra information on screen, where it's like, look, I know that you don't really need this context. But I like to have it, and I figure some nerd out there also wants to have it. So, let me say, like, if you want to go deeper, here's some side stuff. I don't really want to spin up another page for that.

BEN: And you want that with sensible defaults, too. You don't want to figure out how do I accessibly implement an accordion. Let me figure that out. That's the stuff I live and die by, right. You want something that's easy to use and provides a sensible default experience out of the box that should be roughly accessible. There's, you know, HTML is not always accessible out of the box, but it's almost definitely better than rolling your own solution, unless you've done a lot of research. So, yeah, just leverage that. The pit of success, right, make things incredibly easy to create a robust performance, accessible experience, just by using some well-named tags.

JASON: 100%. Okay, so, you got me sold on Eleventy. I am all in on this, we should be building websites that allow us to, you know, minimize the amount of stuff the user needs to deal with to get the content, which is the part that I care about, to their browser. So, the other half of this today is serverless, which you lightly touched on. But let's talk a little bit about when we're talking about this idea of delivering less JavaScript to the user, HTML and CSS, how does -- because we're going to write serverless functions in JavaScript. So, how does this play into that world of node JS?

BEN: Sorry, what JS?

JASON: No JS, like shipping no JS.

BEN: Eleventy is great. It's a great tool. I like it. It serves my mental model of I want to be as close as possible to the market. But, historically, it's had this not even problem, I would say a gap of everything is built during the build step. Right, all of your HTML is pre-rendered, it's on the CDN, it lives there. Right? So, if you wanted an Eleventy site that had any degree of dynamic or real-time content, you are going to have to ship a bunch of JavaScript in your Eleventy site. Like, you would open up a script tag or something, or script source equals, and you'd have to load in some extra JavaScript. And Eleventy doesn't really make that easy. It's not about serving client-side scripts. So, you'd have to do a whole bunch of work arounds. And those work arounds aren't going to be anything new. This is how the web has worked for a long time, is having that client-side logic. But with a lot of that, you lose kind of the cache benefit, right, because the scripts are cached, but the end result of the scripts are not cached. So, every client now has to re compute all these things, all that you were trying to avoid with Eleventy in the first place. So, there's been a gap. And, previously, if you wanted totally dynamic stuff, you might actually consider going to a different tool. Eleventy, previously, would have made this so nigh impossible for you to use, that you would just opt to say, okay, I guess I'll just build this as a React app or something like that, to get that kind of dynamic React router behavior. So, yeah, what we'll see here is by running Eleventy in a serverless function, which is what's going to happen here, we can respond to requests as they come in, create pages in realtime. Netlify's on-demand builders make it fairly straightforward for us to cache those pages. But we can respond to any arbitrary input we need without knowing that ahead of time. We're building a color contrast checker, and you think about if there's 256 values for one color channel, and there's three color channels in a given color, then there's two colors, the map on that gets exorbitant. It's something like you'd be creating 2.75 times 10 to the 14th pages to get every pair of colors represented in RBG's space. And I don't know about you, but I don't feel like doing my part to ensure the early demise of our environment by rebuilding all of those things. Just imagine building those pages, they are all built, took forever, right, and then you realize you made a mistake in the footer, there's a typo you have to correct. And now you have to rebuild that, not just in your Netlify build step, but also your dev server and all that kind of stuff. You're going to pay that pain, like, a lot. You're going to pay that cost a lot. And, so, being able to say, you know, I don't need to know about all my pages ahead of time. I can have my core site be built during the build step, but I can specify certain pages to be built by a serverless when they are requested. It lets us cut down on our build step, which makes things vastly more efficient if you have a whole bunch of pages that in all likelihood aren't going to see the light of day. And it also means that you can pull in arbitrary input from the user, such as query parameters, or path parameters in the URL, so you can get some dynamic per-page behavior that you don't even need to know all the input ahead of time. You also can get some realtime stuff. You can use that serverless function, the serverlessly invoked Eleventy, to request an API and get the latest and greatest, up to datest content.

JASON: Latest and greatest and up to datest. I love it.

BEN: It's the hot new slang. It's the latest, greatest, up to datest slang, Jason. Welcome, you've got the youths. Anyways, this is my stream now.

JASON: (Laughing). Oh, my goodness. All right, so -- So, yeah, I think this is some really exciting stuff. I think from my standpoint, what I'm hearing is that this is a great way for certain use cases to hit the right compromise of you get all the functionality you need for a dynamic content, for being able to respond to user input, but you're not trading off by saying the rest of the content on your site that isn't dynamic or responding to user input. That doesn't have to be rendered dynamically. Doesn't have to be --

BEN: Yes.

JASON: -- don't ship JavaScript to do that. And this other part of it, because this data is, what we could say functionally pure, the same input gets the same output every time. So, if we have the same URL, we know we're going to get the same page, so we can build it exactly once the first time it gets requested, and then cache it forever. As if it were statically generated in the first place.

BEN: Uh-huh, yeah. Exactly.

JASON: That is pretty powerful stuff. That gives me as a developer quite a bit of flexibility to go a long ways with what somebody might consider a static site generator. Not really so static anymore, but gives me that same benefit of build once and cache forever.

BEN: Absolutely. And you don't have to overhaul your whole project to make it work. So long as your Eleventy project is in 1.0, you can opt in to Eleventy Serverless. And Eleventy Serverless is going to be opt in on a template by template basis, which for those who are not into Eleventy spaces, I'll quickly define a template. Which is a file that has your content and is parsed with a templating language, such as nonjux, liquid, to insert your data into. So, it's a content file that gets transformed into, usually, HTML. So, when I'm saying you opt into Eleventy Serverless on a template by template basis, non-Eleventy folks should probably interpret that as you're opting into it on a page by page basis or page type by page type. Whole site isn't being served serverlessly, it's being served exactly as it was, except for this one page that's now dynamic.

JASON: So, I mean, this sounds really promising. I'm feeling pretty hyped about it. So, let me take the counter stance here and just ask, where is this going to fall down? When is this not the right solution?

BEN: Yeah, I still think the highly dynamic experiences are not really what Eleventy is for. There are projects that are trying to solve that. You had Ben Holmes on recently. My doppelganger. To talk about island's architecture and there are projects that exist, but if you're going high dynamic, high interactive experiences, Eleventy Serverless isn't going to save the day for you. You're still better suited at going to tools more focused around that interactive experience.

JASON: For sure.

BEN: That's a gap there. Maybe not even a gap, that's not what Eleventy is for. Projects don't have to be for every project. I'm not saying build everything with Eleventy or build everything without Eleventy. Eleventy is really, really great for content that's mostly static, right, could be represented as just HTML.

JASON: You're touching on something that's very near and dear to my heart. As I've gotten more experienced, as I climbed up what you would call the developer maturity curve, I've started to realize that any time that I smell somebody saying that this tool will solve all your problems, this tool will accomplish whatever you need it to, I immediately start to pull back a little bit and go, okay, it's time for me to do more research. Clearly, somebody telling me this is a universal solution is selling something.

BEN: Yeah.

JASON: Where I see a really experienced developer is looking at tools pragmatically and recognizing you're going to reach for different tools for different outcomes. Even in relatively similar spaces, you're going to find that it's more suitable to use one framework for e-commerce, versus another for paid subscription. Both, I need people to come to a website and give me money. But the use cases are just different enough that it might be a different tool that makes that more functional, more available, right?

BEN: Absolutely.

JASON: So, yeah, I love to hear people say that. You should wait, your use case, chat. Can't just go to me and say what should I use, and I rattle off whatever thing I'm excited about today is. Or go to Ben and ask the same question. You got to kind of weigh what problem does this solve. So, today, we're solving the problem that I have mostly static content, some of it will vary based on user input, but it won't vary every time it loads. It varies once per unique URL.

BEN: Yes.

JASON: And I would my build times to be fast, despite having a large possible number of combinations in that variable user input. Potentially varied.

BEN: Absolutely. So, a way to view Eleventy Serverless is absolutely, like, not the cure-all. But, rather, it closes a gap that Eleventy had, which is that Eleventy was not built to deal with dynamic inputs. Now Eleventy is built to deal with dynamic inputs. If that was a need you have, you no longer have to look elsewhere. You could better consider Eleventy for your needs.

JASON: Yeah, no, that's great. So, at this point, I think probably the best thing we can do is start showing everybody how it works. So, first, let me do a quick shout-out to the chat. Y'all, thank you all for showing up. I see a whole bunch of folks in here, Michael Chan, David, Brian Robinson, dropping links all day, Brittney, Steph, Cyrus, what's up, thank you all so much for hanging out today. I know I didn't mention everybody. My memory is not that good, and I'm sorry. Thank you all for hanging out. Thank you for the sub, I saw real Tommy was the user name, thank you for subscribing. And I appreciate that a whole lot. Also, how do I look today, y'all? I'm using a new tool. I've been using Ping for a while. And then I switched over to a new camera. This is a Sony FX3, which I'm thrilled about. And then I also got a new way to hook it in, so that I can get 1080p at 30 frames per second, something like that, I don't know. Everything is very fancy. I'm only marginally understanding it, but I should be really sharp. So, shout-out to Ping for actually letting me stream at that high quality. I'll drop a quick link to Ping in the chat, for anybody who's streaming and wants to check this out. And with that, let's switch over into paired programming view and I'll give a couple more shout-outs. Here we go. Camera two. All right. So, first and foremost, we are having this episode live captioned, like all of our episodes. I broke something on the homepage of the site, so to see the captions, you can see those right over at That's going to be all of the captions for today's show. These will also be included on the YouTube video and on the site for the episode. That is made possible through the support of our sponsors, we have Netlify, Nx, and Backlight all kicking in to make this show a little more accessible, and to help me cover all the other costs of running this show, like, for example, upgrading camera gear and paying for streaming tools. So, thank you all very much for making the show possible. You are also talking to Ben Myers today. So, make sure you go and follow Ben on the Twitter. We're talking about Eleventy. Link has already been dropped, but I'll drop it again. And we talked a little bit about Todd Libby. Make sure we get one more link to Todd out there. Ben Holmes and Slinkity, there we go. That is now the extent of what I know. Let me hand it back over to you and ask what should we do next?

BEN: So, I created a bit of a starter repo for us to work with. The reason being, that I wanted us to not spend a whole lot of time on the Eleventy setup. I also wanted to just preload things with styles. This repo is not terribly fancy at all. It's just got a few -- it's got styles wired up, basically, and has a template already. So, we're going to be adding Eleventy to this. So, yeah, and, in fact, might as well put a link to that in the chat, as well, because why not? Cool, I am allowed to post links in this chat. All too powerful. Yeah, this is -- I tried to keep things as pared down, minimal, and un-fancy as possible. I personally don't like it when pre-existing things get in the way of learning what it is we're learning. So, as you can see, we've got one template in here, which is And it's, you know, it's fairly boring. It's going to help us get started. We have a layout called page.HTML. If you pop that open, we've got ourselves a fairly standard HTML boilerplate. This contains our fonts and our style, as well. So, this is basically all we really have setup-wise. I've preloaded a bunch of things just to look pretty.

JASON: And, yeah, some pretty straightforward styles it looks like. Nothing too much, about 100 lines of CSS here. And then in our Eleventy.js, all we did was move everything into a source folder. No other bits, right?

BEN: You can see we've got add, pass through, copy, so if we change our styles, they'll get loaded into the new build. Overall, a fundamentally un-fancy project to start off with. Didn't want to spend a lot of time on your Eleventy boilerplate, more or less. Yeah, if you click the get started link, it will break. We're going to create it. I've preloaded it with query parameters, because we'll see. So, yeah. Any questions about this boilerplate before we get started?

JASON: No. I'm feeling pretty solid about this. You know, it all makes sense to me. Anything that we write in here will get dropped into this page.HTML as the content tag. Our styles are getting loaded right here. And I understand how the CSS and HTML relationship works. Anything that's in source is what Eleventy is going to target. And, yeah, I think I've got about as much context as I can get. Chat, if you've got questions, as always, please drop them in there, we'll answer them as we go.

BEN: So, I thought what we could build is a template for our color contrast ratio. Not even start it as serverless. Make a hard-coded template with hard-coded values and add in the dynamic stuff as we go.

JASON: Perfect, I'm ready.

BEN: So, let's go ahead and create a new template. Call it contrast.HTML. Markdown is the most common, but for today, we're going to get -- we're going to stay as close to the markup as possible, and I find it easier to do this as an HTML file. We'll start with a bit of front matter. We're going to add in our layout. This will get us the styles on the main tag and everything. Oh, there we go.

JASON: Very helpful extensions happening right now.

BEN: Yeah. Let's go ahead and add ourselves an H1, probably have that be something like hash ffff, and 0000. Probably the wrong number of "Fs" and zeros. But you know what I mean. Contrast ratio of -- and underneath it, we're going to use an element that's not often talked about. Folks in the chat are going to be screaming that I'm about to bring up the DL. I'm not, I'm not going to bring up the DL. That brand was so a few months ago. Instead, I'm going to introduce you to the output element, which is designed to represent the results of a form submission.

JASON: I've literally never seen this before. This is entirely new to me.

BEN: Output element, for anyone unaware, represents the forms submission, usually a calculation. It also counts as a live region, which in accessibility speak means that as you update the output element, it will announce the changes to screen readers and other assistive technologies.

JASON: That is killer! Because that's a whole thing that we struggle with, right, when we're showing output. When we put it in a div, it doesn't tell anybody that we changed it, so then we find ourselves writing all these area attributes, and those are super confusing and easy to get wrong. So, if I just use this output element instead, it will work just like a div in terms of how I can style it, but will automatically trigger the assistive tech when it changes?

BEN: Yes. Actually, technically, it works more like a span, because --

JASON: Oh, so I'd have to make it display block and all that good stuff?

BEN: Yeah, and I've done that in the styles for you. If you're curious, the specs define a whole bunch of different kinds of elements for different kinds of content. So, there's like your phrasing content tags, which are your span, B, I, strong M, anchor, et cetera, those are things that you would more or less expect to see inside a paragraph tag. Then there are your sections elements and stuff like that. So technically, output is more like a span, but whatever.

JASON: Also, chat, are you making fun of the way I just said -- it's not area, is it? It's aria.

BEN: It's aria, typically.

JASON: Fine. Okay. So, inside of this, I can just put whatever I want?

BEN: Yeah, let's go ahead and put 21.00:1. If you're unfamiliar with color contrast ratios, a 21:1 ratio is the highest contrast ratio you can have. That's what you get if you compare black and white.

JASON: Okay, I think I misunderstood, you want this inside the H1?

BEN: That's its own thing. It will be uh outside the H1. We could put the output inside the paragraph tag if we want to be very correct, but, yeah, there we go. Very correct is good. Yeah, so, let's go ahead and save this and take a look at this. This is going to have been built, so that we can access it at /contrast/. There we go.

JASON: Neat.

BEN: Yeah, decent looking -- what?

JASON: I -- okay.

BEN: Cool. So, now we've got our little template. But this is being built ahead of time, and it's being built with hard-coded values, right? So, this is not really what we want. This right here is a fundamentally useless page. Tells you exactly one contrast ratio, and it's the one that most accessible folks know off the top of their head. Black and white have a contrast ratio of 21:1, which is the max. So, we want to make it dynamic, to add serverless, so this gets rendered with whatever input you provide it.

JASON: Right.

BEN: Cool. So, let's go ahead and stop the dev server for this. And while you're at it, let's move the underscore site directory. That's the build output, because it's already created a contrast page for us, HTML page for there. We don't need that anymore. That's not going to be in the build. So, all right. Now to add serverless to our project. First of all, you're going to want to be on Eleventy 1.0. Which is what we have in the project. Let's go to our Eleventy.js file, which is -- yeah. So, this is our configuration for our project. This is how we tell Eleventy what to do, how to be, how to live its life. If you go up to the very top of this, we're going to import -- very, very top. We're going to go ahead and require something from the @Eleventy/Eleventy library. So, this is going to be a destructured import. Yeah, definitely go about it in that order. This right here is going to be a plug-in. Eleventy Serverless is technically a plug-in you have to add to your project. Start typing Eleventy, and it's going to be Eleventy Serverless bundler plug-in. All one word. So this is the plug-in that we're going to add. Now we have to add this to our configuration. So, yep, Eleventy config.addplug-in.

JASON: I love auto-complete.

BEN: You'll notice there's the above this function. I recently -- that's now in my default install. Otherwise you do not get this for your Eleventy config. So, I highly, highly recommend this js doc setup, which I think we've found. We have a question in the chat about what's a color contrast checker. You feed it two colors, in our case two hex codes, and it tells you the ratio between them. Higher ratio means the colors have a higher contrast, which means for people with visual disabilities, it's going to be a lot easier for them to make out content with that higher ratio.

JASON: We can show this straight up, right, like if I computed -- I might have to add a color on it. Let me do element style. Color will go -- we can see the numbers getting smaller. This is very tiny. Let me try again. So, as I go lower, that's harder to read and see it, saying, hey, people can't read it. My eyes are straining a bit. If we get really low, oh, boy, can't see that at all. As we make it bigger, because this is a giant text, once we get above -- what is it, 3?

BEN: According to the Web Content Accessibility Guidelines, there's basically two main threshold ratios you need to care about. A 3:1 is sufficient for large text, which is -- there's a whole definition, but basically 18-pixel font, or 14 in bold. Something like that. I forget exactly what it is. Large text needs to be 3:1, normal text needs to be a 4.5:1. That's why contrast checkers like this are helpful. They give you the two numbers that you're looking to add to your design system and go, yep, these work well together.

JASON: This particular tool I use all the time. Throwing in the two values is hugely helpful. There's another question about the JS comments at the top here. You want to give us a quick overview how this works?

BEN:, I'm going to put a link in the chat. This is a special syntax for comments. You'll notice this comment block starts with two asterisks. This is a special syntax for comments that enables you to, effectively, provide metadata about the variables and functions that you're writing. You statically build documentation pages out of your code, if you included comments about what your code does. It's a special syntax meant for it. However, VSCode has a feature where it basically uses JS stock comments as hints for its type script server. As you're describing, oh, this is a string, or this is an array of numbers, or something like that, even though it's just a comment, it ends up under the surface being interpreted as, basically, types. That means you get intellisense. If you're like me, don't want to go into type script wholesale, this is an incremental way to benefit already. Your editor does it for you. It's such a time saver. It's lovely.

JASON: I feel like this was the moment where I opened my heart to type script, really, when I realized you could use these jsdoc inclusions. Because the thing I live about type script is I hit dot, I can see what's here, see the methods. That is inbelievably helpful, as you are starting to work. It's so, so, so good. And the full on migrate everything to TypeScript is handy --

BEN: Oh, no, your code is more expressive, too. Oh, no. This is absolutely going to make it through team review. Oh, no. Absolutely, take the time to learn it, because if you don't want to make the full TypeScript conversion, this itself is entirely worth it.

JASON: Getting compliments on my shirt, it's distracting. Thank you, thank you. This is a Wes Boss shirt, can everybody see it? Wes Boss JavaScript shirt. It's a good one. I like that one. Wes just dropped another $5 sticker pack also. I don't know if he sold out yet, but they look fun. You'll have to go get those. Go look, I guess.

BEN: Shall we continue adding our serverless plug-ins?

JASON: We probably should.

BEN: So, as a second argument to the add plug-in method, we're going to pass an object. And for this object, we, unfortunately, don't get autocomplete for this, but we're going to have to give it two things. The first thing we're going to tell it is where our serverless functions are going to live. And because in this household we stand Netlify, we read the Netlify functions directory. So, let's pass it a property called functionsDir. And the second thing we're going to pass it is a bit curious. This is going to be a name. So, the property is name. And what this represents is the name of our serverless function. So, we may, depends as kind of a crunch stretch goal, we may add multiple instances of the serverless bundler plug-in to do different things, but you can actually add the serverless plug-in multiple times to your project and each instance will handle things in a different way, because they are associated with different serverless functions that can do different things like validate your parameters, for instance. Or cleanse your parameters. Right, no one wants to just willy-nilly introduce user input into their site. That's a bad idea for security reasons. So, maybe you want to do some validation. You can do that validation in your serverless function. So, now your serverless functions have, like, you have this flexibility being able to use multiple serverless functions for your Eleventy Serverless. So, let's give this the name On Request. Because, to start off, we're going to have it generate the page on every request. We're not going to start with caching with on-demand builders, because I think that should be a stretch goal. So, onrequest. So, let's save this and start our dev server and observe changes to our project.

JASON: Whoops. It's working anyways. Here we go.

BEN: All right. So, first of all, if you look at the underscore site directory in our code base, we are going to see that there is -- oh, oh, right. Okay, yes. So, there is this on-request serverless function. All of this stuff has been automatically generated for us. Really, the only thing we care about is the index.js page here. This is the serverless function that we control. Everything else gets auto-generated for us, and will be rebuilt with every CICD pipeline. So, we can ignore that. Git ignore, add a few lines real quick. So, the first line is going to be Netlify/functions/onrequest/starstar. We're going to follow this up with exclamation point, Netlify functions, onrequest, index.js. This ignores every file in our onrequest serverless function folder except index.js. That way Netlify is able to rebuild all the other things based on your project state. We don't touch those, so we let Netlify do the work for us.

JASON: Great.

BEN: Cool. So, everything else is exactly identical. Right, you can see in our site directory we still have all the same content we would have had. We have our contrast page, index.HTML. All are still being built ahead of time as it should be. We haven't changed anything about our project so far. We haven't even made any of our templates serverless. So, let's go ahead and do that real quick. Can you kill the dev server once more and delete your site directory once more? Promise we'll get to the point where we're not deleting our site, but for initial setup, what we now want to do is tell Eleventy that our contrast.HTML template is a serverless template. It should be rendered on request. And, so, to do this, you're right, it would be in the front matter. There's long been a permalink property that you can specify, and this is usually to set a different output path. So, you'd say I know the template is called this, but I want it to be accessible with a different URL.

JASON: By default it's going to use the file name. Contrast.HTML, so permalink is/contrast. If we said permalink like hello, then it ignores this, and this would be available here instead.

BEN: Yes. So, Eleventy Serverless is going to be permalink, but you're still going to have it the way you had it, but now you're going to go to a new line. Yep, you got two spaces. YAML's version of an indentation. And now what we're going to do -- JSON, JSON, yep --

JASON: (Laughing).

BEN: You're welcome. So, what we're going to do now is put the name we gave our serverless function. So, this is on request. So, this specifically matches that name attribute in our .Eleventy js. Now we can give it a path, /contrast/.

JASON: Okay.

BEN: And we're going to go ahead and save this. Hey, if someone tries to access the route/contrast/, then instead redirect them to our onrequest serverless function. And that's going to provide the output for us. We've created a YAML object. There's a whole lot of fun stuff you could do. You could technically provide an array of values. Multiple routes are governed by the same serverless function. But for today, here's how we're going to do it. So, let's go ahead and save this, rerun, and let's go to our /contrast route. There we go. If you go to that -- yeah, yeah, if you go to /contrast/, it came up exactly the way it has. However, if you check your terminal, you're going to see it actually said, yeah, that was redirecting you to the serverless function. If you were to check your underscore site, you won't actually find contrast.HTML in there anymore. The only thing -- yeah, so, that HTML does not exist anymore, but you were still served it, because it's now being generated by your serverless function. How are we doing so far?

JASON: We're killing it, even with me screwing around, you're doing a great job.

BEN: All right. But, you know, this still isn't very helpful for us. We need to start accepting some user input, right? So, 11 exposes information about the -- so, Eleventy exposes information about the user's input, typically query parameters and URL parameters, as data. Eleventy has this concept of data, which I would basically define as variables that are accumulated for every template that can be injected into your page using templating syntax. We'll see what that means. So, let's replace that first hex code you've got in your h1. Let's go ahead and replace that. And the octothorp, as well, with double curly boys. Double curly boys. I may also call them handle bars from time to time. But in here, I want you to stick, and this is not going to be trivial, Eleventy.serverless.query.foreground.

JASON: I can't even spell Eleventy. Serverless.query.foreground? How close should I get?

BEN: You got it. I'm going to reply to Cyrus here, who asked, you can access all the variables in all templates? Not quite. Eleventy has a whole order of operations for each template, so each template can have its unique set of data. This is a process called the Eleventy data cascade. And I see that Brian Robinson posted a link to a talk I gave about that. I'm also going to link a blog post that I'm really proud of about the data cascade. Unfortunately, we just don't have a whole lot of time to dive into that today, but, yeah. So, what this does, this says, hey, take the query parameters, the foreground and background query parameters, and plop that into our page. Yeah. And, so, yeah. That's what we've got. So, now if we go back to our local host in our browser, we can change those query parameters, and it should work for us. Oh, yeah, you're not actually supplying those query parameters now.

JASON: But this is good. It means now we do need to supply these. Now I can go with foreground. And let's change it. So, we'll go with --

BEN: You're not going to be able to use a hash symbol in an URL. Instead, you have to URL encode it, which is percent 23.

JASON: Okay, and then go with background, % 23. That's purple, so for contrast we want white, I think. There we go.

BEN: So, it took information from our URL, created an HTML page, plopped that on to a page. We don't have to do it, but if you were to interrogate the sources, you wouldn't find any JavaScript. This isn't JavaScript has pulled the information from the URL into the page. It was a whole new HTML page that was created for us using these values. So, I think that's incredibly cool.

JASON: Yeah, and this is exactly what we wanted, right? We want to have no JavaScript. And when we look at the application tab -- not the application tab, we want the network request. Clear this, load again. There's no JavaScript in this cascade. Like, it's not happening. Pull it down here, we can actually see what's going on a little better. Lots of helpful things coming up in the -- yeah, we've got a document, two style sheets, and a font. And that's all that's being loaded on this page.

BEN: Yeah.

JASON: No JavaScript.

BEN: Yep. Okay, it's not very fancy. It's also right now very incorrect, because those two colors do not have a contrast ratio of 21:1, unless my math is horribly wrong. What say you we add some ratios?

JASON: Yeah, let's add some ratios. Also, David is asking when all the function code was generated, do we as developers have to set up deps, or is that all magic?

BEN: Both. Dependencies are mostly set up for you if you reference your dependencies in the Eleventy JS config file or in a global data file, which is going to be a little awkward very shortly, because we're going to use something called a template data file, which is the one instance we have to manage our own dependencies, which is fun. Let's go ahead and install a library to handle the ratio calculation for us. I like a library called get-contrast. Get-contrast, with a hyphen. Yep. And, so, now we have this. Let's go ahead and, because of that dependency management, so we make sure Eleventy picks us up and bundles up our dependency with the serverless function. Let's go into our .Eleventy.js file and require get-contrast, but not do anything with it. It's gross, I'm going to file an issue with Eleventy shortly, but for now this is kind of a work around we have to do to tell Eleventy that this is something we care about. Yep. And now what we're going to do is we're going to need to find some way to transform our foreground and background properties into a ratio value. And when you have data like our foreground and your background and you want to create something new with that data, you have really two options. There's a route called filters, which is basically functions that you can use in your templating syntax. Or you can create computed data, which is new data that leverages the old data. It's a lot like the computed props in view. That's the way I prefer to do it. That's the cleaner way, in my personal opinion.

JASON: And it also feels like given that we have this data already, rather than -- because my issue that I've run into with filters is that I find myself having to pass data into the filter that I already had somewhere else.

BEN: Yeah.

JASON: Because I can't get it -- the filters don't access the data. There's, like -- I find myself jumping through hoops. So, computed data sounds very promising. I haven't really worked on it with that.

BEN: If you're interested in learning more, there's a blog post I wrote about Eleventy data cascade in the chat. But to do this we're going to create what's called a template data file. So, next to the -- yeah -- yes, if you scroll down, there's a table of contents, there's a bit that tells you.

JASON: Here we go.

BEN: Step 7, computed data. That's what we're going to be doing, actually combining this with step 5, which is template data files. Next to your contrast HTML, let's create a new file called contrast.11tydata.js. This is a magic name, has to be exactly like this. This tells Eleventy this is a file that's going to supply some data. And the fact we called it contrast will tell Eleventy to link it with contrast.HTML. So, this is a way for us to specifically provide data to be used within the contrast.HTML template. So, yeah, I've recently -- we got B. Rob in the chat. He and I spoke at the Eleventy meetup not long ago, the same day. Afterwards, oh, computed data finally makes sense.

JASON: Nice, nice.

BEN: Not always straightforward. But let us go ahead -- I've got my notes here. Let's go ahead and do const contrast equals acquire get-contrast.

JASON: Thank you for the sub, Theo, I appreciate it.

BEN: Theo, hello, we are using Ping right now.

JASON: We are.

BEN: It's working like a charm. Let's do module.exports equals, then an object.

JASON: Object?

BEN: Yep. And in here you're going to have a property called Eleventy computed. This is how we signify to Eleventy that this is data that needs to be evaluated at the end of the data cascade, using all the other data that's been aggregated already. This is an object. And now let's create a new property in here called ratio. And this will be a function that receives a data argument. Yep. And in here, let's go ahead and de-structure foreground and background off of data.Eleventy.serverless.query.

JASON: Equals data.Eleventy --

BEN: Eleventy.serverless.query.

JASON: Got it.

BEN: Yep. And we're going to just console log foreground and background for now.

JASON: Copy and paste these, so I don't have to type them out twice.

BEN: You got it. And let's go ahead and save this, and we'll check our terminal. I'm going to tell you now, this is actually going to break. Yep.

JASON: Boom, explosion.

BEN: Oh, no, it broke. It's saying can't find properties. It's trying to read query. So, when you're dealing with computed data, I wanted to show this, because this is a gotcha that tripped me up for so long. The night before I was due to give a talk about Eleventy Serverless for an event called the Eleventies, this here was tripping me up so much. Computed data, Eleventy wants to calculate basically the order of dependencies, because computed data can reference other computed data. So, it has to basically evaluate ahead of time what is the natural order of the data that has to be evaluated. That means it's going to try to evaluate your serverless data before you even have a serverless request. So, what I would say is before you de-structure, we're just going to do if not data.Eleventy.serverless, return one maybe. Just say that the ratio is one, if it's not a serverlessly invoked function. This should work for us.

JASON: Okay. Let's try again.

BEN: And now let's go ahead and, I guess, go to -- yeah.

JASON: There's our foreground and background. Let me go back to the one we added the foreground and background.

BEN: Look at that!

JASON: There we go.

BEN: So, we've successfully pulled our foreground and background. Now we need to get the ratio. To do this, we are going to return contrast.ratio. And we're going to pass it foreground and background. There we go. And let's go ahead and use this. Let's go into our contrast HTML template. Instead of 21.00 -- we're still going to need the :1, but replace with ratio. That's what we did, created a property called ratio that's calculated from the foreground and background data. It does break layout-wise, but it's functional.

JASON: So, if I go back to my -- go up here and do the black. There's our 21:1. So, we can fix this, though, by going in here. And, what, we want --

BEN: Can I propose more computed data? This is data that now depends on our ratio data. Let's open a new property in our Eleventy computed called formatted ratio.

JASON: Ah, I got you. And this is where I think that gotcha is going to become more visible to me. If I put this above ratio, it would explode, right?

BEN: No, no. Because Eleventy pre-evaluates the dependency chain and figures out what order things need to be in.

JASON: Oh, interesting. Okay. Do I want to do the same thing here?

BEN: No, because the only thing this is going to depend on is the ratio property. We're going to have computed data that depends on our ratio computed data. We just said, hey, if you need to panic, panic to the value of 1, which is a formatted number.

JASON: So if it panics, it will be 1:1?

BEN: Yeah, yeah. I figured that was a safe, fairly straightforward thing to do. Okay, so, let's return number, like Number. Data.ratio. After this, to fixed. After that, you don't really need any more decimal places of precision.

JASON: A lot of decimal places if you go too far here. So, now we're going say formatted ratio.

BEN: Yeah. Yeah, there you go.

JASON: Now it's forcing it. If I go to that one we had a really long one, 669933.

BEN: Nice. Nice and clean. So good. But, okay, here's the thing. I don't actually find URLs all that easy to work with. It's better than nothing, but this isn't very complete to me, because I want some nice, easy navigation, right, I would like to be able to pick in this interface two colors and then just take me there, right? Still doing this without any client-side JavaScript, because we're HTML developers, and we've accessed the form element. So, this is in our contrast.HTML. After our paragraph tag. Let's go ahead and spin up a form element. If you're unfamiliar with -- okay, old way of doing forms from the days before tools like React, the action attribute would tell, basically -- man, what's the best way to explain it? It would send a request to this end point. And that end point would typically be a server that would respond with a new page, which is exactly what we want here. We want to tell our server to respond with a new page. That page is going to be /contrast/. So, that's what we're going to put here in our action. You can actually also pass to a method. The default method is get. But if you want to be explicit, we can do that ourselves. Lowercase get, it does matter in this case. It's bizarre. I've got a few styles out of the box. So, if you wouldn't mind, can we create a div with the class fields?

JASON: What was the class?

BEN: Fields. Yep. And in here we're going to have two more divs. And each div is going to have a label and a color input. And we should absolutely make sure that this is accessible. So, our label is going to have the -- yep, ID of let's say foreground and background. That's what we're going to do, have users pick that. ID is foreground, and name equals foreground. And the label text should also say foreground. All of this similar for the background.

JASON: I'm going to do this one more time.

BEN: Once more with feeling.

JASON: All right. And then we can capitalize that one.

BEN: And have a submit button, which is going to be outside of our fields div. So, yes. And let's just call this get ratio. You're going to see there's already some neat styles here. However -- oh, did I -- I might have broken something.

JASON: I'm zoomed in, as well.

BEN: I'm not going to spend time debugging the styles here, but let's go ahead and now use the two color pickers to select us a foreground and background.

JASON: Let's go with a lightish purple and a darkish really -- this is going to be real bad. You ready?

BEN: Boom. All of this, without any client-side JavaScript.

JASON: That is really nice. Can we also use the --

BEN: Yes.

JASON: Data to preset these?

BEN: Yes, let's go to each of our inputs and say value equals. And then curly braces, handle bars, double curly boys, whatever you want to call it. And Eleventy.serverless.query.foreground or .background.

JASON: And now --

BEN: Boom!

JASON: There's our colors.

BEN: See, nice and lovely. Let's go ahead and go to a new page. Let's use this, go to a new page. And, you know, we've completely, without any client-side JavaScript, we've got a sense of state almost, which I absolutely dig. So, let's --

JASON: If I hit back, it sticks. This is really nice.

BEN: So, let's make this even better. I want to make a pretty demo, we've got enough time. Let's make it a pretty demo. In your contrast.HTML template, let's open up a style tag. Doesn't really matter where you put this. Put it wherever you prefer. In here we're going to target the body. We're going to give it a background image. And this is going to be a linear gradient. And I prefer -75 degrees.

JASON: -75 degrees.

BEN: Yep. We might want to break these things out into new lines, because things are going to get long. Even though we're a style tag, this is still interpreted with all of our templating syntax. So, comma, and then a new line, and now double curly boys. Eleventy.serverless.query.background. And after this, it's going to be 50%. And then comma. And then the same line down below, but with foreground. Boom! Keep going to some pages, let me know what you think.

JASON: Look at this. This is cool. All right, let's do like a greenish and come over here and do a -- let's see, how offensive can we make these colors? Hold on, chat. Make it gross. What do I do? Go with this brownish color, and then we can take it to a mint green. That's going to be bad, right? That's got to be a bad color. Let's go over here.

BEN: Incredible.

JASON: This is good stuff.

BEN: Yeah, oh, yeah.

JASON: Hot dog stand colors, we can definitely do that.

BEN: What say you we deploy that.

JASON: That is aggressive.

BEN: Truly aggressive. However, it does meet color contrast requirements for large text.

JASON: It does do that. So, for me to deploy this, I'm going to need to fork it, I think.

BEN: Yeah.

JASON: Let me --

BEN: For anyone else who's using the template repo, there is a green "use this template" button you can use. That way you'll get not even a fork, but straight-up your own project with this.

JASON: Okay. So, we've got an origin upstream, or an origin repo of mine, an upstream repo of yours, and if I get at everything, we can say get, commit, and say add serverless contrast checker. Outstream origin, off it goes. And then I can Netlify. And I want to create an configure a new site. Put there on mine, call it Eleventy Serverless contrast checker.

BEN: I'll go ahead and answer the question from the chat. I do have both the stream and the Twitter. Let me put links to both of those in the chat. If you're interested in stuff like this, I have a weekly Twitch stream called Some Antics, that I'd love to see you in the chat for, while we're deploying this. There we go. Hopefully, that helps.

JASON: Yes, here's Ben's Twitter. And here is the Twitch stream. I think does my shout-out command still work? Let's find out.

BEN: Let's find out.

JASON: It does!

BEN: Hey!

JASON: So, that is pushed, good.

BEN: All right.

JASON: And then I can go out to -- let's go look at the site here.

BEN: Hopefully, this should work.

JASON: Built nice and fast. Whole site build in 141 milliseconds. I just said "prosheshing." Sean Connery. 49 seconds, that is live on the Internet. Here we go. Here's our contrast checker. --

BEN: Click the get started link.

JASON: Yeah, we have a get started link.

BEN: There we go, cool.

JASON: Let's try a new one. This is going to fail contrast. Yeah! Now we can play with this. It's working!

BEN: And if you're ever curious about how the math behind this is working, the single most important factor in our color contrast algorithms is relative luminance, basically one color is light and one is dark. You can have a red and blue, which I can tell they are different colors, but the hue matters a lot less than the lightness and darkness of the colors. This is a low color contrast, even though to my eyes at least these are blatantly different colors. It's not how different they are. It's how readable one color is versus the other. And to do that well, you basically need to have one light and one dark.

JASON: Yeah. We've got a request from the chat for one that I assume is going to be really hard to look at. So, let's start there. And then we're going to add -- what's this one? We're going 11 -- oh, this is going to be rough. Yeah, that's --

BEN: While we're over here celebrating "The Fairly Odd Parents," right, listen, you brought the youths. I am not ripe in my age. I have a ways to go, and I have so many more, you know, '90s, late '90s kid nostalgia here. This is what you brought into this world, Jason. It's giving me a platform to do "Fairly Odd Parents" jokes.

JASON: I love it, I love it. I'm so onboard with this.

BEN: Let's see. I think we have enough time if we want to do a stretch goal for caching these pages.

JASON: Absolutely, let's do it.

BEN: So, let's -- and we're going to speed run this. I think we're going to have it by a separate template for cached pages. But overall is going to function identically. So, step one, go to our Eleventy js and add a new instance of the serverless bundler plug-in. I would call this cached. You can call it whatever you want. If you lock on the documentation page, they indicate possum, to say you can call it whatever you want, but cached, I think, works for me. And then let's go ahead and create a contrast cached.HTML maybe. This is going to be different in that when using Netlify's on-demand cache builder, you have to provide a different way of doing things. So, step one, yep, replaced that with cached. And our new route, let's call this -- let's remove the /contrast/. Let's call this ODB, for on-demand builders. After that, colon foreground/:background. And everywhere you see Eleventy.serverless.query, we need to replace the query with .path.

JASON: Okay.

BEN: And then next up we need to make sure it has access to the same data as you would have in the contrast.HTML. So, we're going to copy over our -- yep, this is going to be contrast cached. Once again, instead of .query, this is .path, which I think you only change on line 10. And, finally, we need to go to our serverless function that's been created here.

JASON: Stop and restart.

BEN: Yes, yes. We'll also need to add a whole bunch of the serverless function stuff.

JASON: Wrong thing. I want to Netlify dev.

BEN: There we go.

JASON: Okay, now I have Netlify, cached, indexed.

BEN: Okay. We also, I forgot about this step. At the very bottom it tells you how to make this use on-demand builders, which is this comment block here. Comment out line 52. You're going to un-comment 54 and 55. And then it tells you, you also need to install @Netlify/functions. Yep.

JASON: Okay.

BEN: And we're not going to be able to validate locally, because the caching aspect of on-demand builders doesn't work locally. We would, instead, want to validate through a deploy. What we've done here is we have instead provided a new way for us to access these pages. We say /ODB/1 hex code, slash a second hex code, and this will be cached now. We have to provide a new path, because we don't have access to query parameters anymore. Another thing that won't work anymore is our form, because we would need to reshape the URL.


BEN: Do we want to do that real quick?

JASON: Yeah, we got time, let's do it. So, down here.

BEN: Let me get my notes up for this again. All right, let's open up a script tag. Let's query select our form, and save it as color pickers or form. Form is better, yes, I like that. Yep.

JASON: Only got one, so we can be pretty loosy goosy with our selective.

BEN: Form.addeventlistener, submit. Prevent default. Yep. And const values equals object.fromentries. New form data. Form. Yep, like that. Const foreground equals values.foreground.replace. This is going to give us the hex codes with an octothorp, right, and we can't have those in our URL, can't have those pound signs in our URL. So, we have to replace with the URL encoded version. Replace the pound sign with %23. Similarly for background. All right. And window.location.href equals. And we can open a template string here. /ODB/foreground. /background. If all this works according to plan -- did you include a link in your permalink?

JASON: I think, let me double check. Yes.

BEN: Okay, cool, cool, cool. So, this should work. I think we can validate this locally. The only thing that won't work locally is the caching bit.

JASON: So, we can run it. Here we go, Netlify dev. That's going to fire up all our functions and stuff. We can even see here it's firing up the serverless files and doing all that good stuff. Here's our functions. And then when we come up here, we go to ODB, and we'll do like a %23.

BEN: There we go.

JASON: And if I change it --

BEN: Form should still work, fingers crossed.

JASON: It does.

BEN: Nice! All right. What say you we Yolo this.

JASON: Yeet! Chat, you've got the yeet emote now, too. Also, followers get emotes. No money, just following on Twitch, you should get special emotes on the channel. Maybe click the follow button and make some noise! Ha, look at the yeet!

BEN: Incredible.

JASON: Get at everything. Double check we did everything, added the cached non-index files to our get ignore. Get committee. Then we're going to get push. If we come back out here, we can see here that it automatically started building, because we pushed to GitHub. This should take us, hopefully, even less than 49 seconds this time, because now we've got some caching.

BEN: Yep. In the meantime, I kind of want to talk of other use cases for serverless I've seen. Both Stephanie Echols and Brian Robinson in the chat today have leveraged Eleventy Serverless to add search pages to their site, because those take arbitrary input and return arbitrary output. They both blog on Eleventy sites. So, this is a fairly natural fit. I know that there have been a couple of other experiments in this regard. I've been trying to push it to the absolute limits by building an on-demand Twitch chat generator, like themed Twitch chat generator using Eleventy Serverless that takes arguments through the URL to provide different logic. So, there is a wide variety of things you can do, and I think the more people kind of see how this stuff works -- hey, oh, I see, we got to do the -- yep.

JASON: Clean up a little bit, go to one of these. Go to one of these.

BEN: ODB instead of contrast.

JASON: ODB, that's right.

BEN: Oh, no!

JASON: URL not found. How dare you. Wait, why didn't that work?

BEN: That's a great question. I'm not too caught up about it, but that is -- let's see. Interesting. This is maybe one of the, you know, not to end on the note of like an error state, but this is one of the rooms for growth that I think Eleventy Serverless does actually have, which is that it can be very difficult to debug when your Eleventy Serverless functions go awry. And this part I did not test.

JASON: I think we've got a note from Zach, which is the generated -- it needs to include builders.

BEN: Oh, interesting, okay.

JASON: So, let's try this one more time, and if this one doesn't work, we'll open it up and dig into it. But, yeah. It might just be that we need to do a bump to the latest patch version of Eleventy to get that sorted. But -- did I push? I don't think I pushed. Oh, I needed to actually add that.

BEN: Oh, yes.

JASON: There we go.

BEN: Yeah, even if the on-demand builder stuff doesn't work, we had what I would consider a fully functioning app that even maintains state across pages, had some nice navigation, all without requiring client-side JavaScript to be sent over the wire. I don't want to pretend like having zero client-side JavaScript is inherently a virtue. However, I think it does create more lightweight experiences, more cacheable experiences, things are a little less likely to break. And also I really like that we built navigation that didn't depend on JavaScript to work. All of that, even if the CSS had failed or whatever, everything there was totally functional. I think that is really neat. So, we'll see if this works. Might not, and that's okay.

JASON: Okay, we have missed a step somewhere, for reasons I don't --

BEN: Yeah.

JASON: -- that are not immediately apparent. Also looks like it's running them as functions. Does it say on-demand builders anywhere? I'm wondering if we -- we'll publish the fix once we get it sorted. In the meantime, we can rely on this very cool setup like this, where this is working. So, you all can go play with that to your heart's content. See how shocking you can make the color contrast combos. But, Ben, this has been fantastic. Let me do a quick shout-out to a couple things here. This show, like all of our shows, is being live captioned. We've had Ashly with us all day from White Coat Captioning, so thank you very much. That's available in the StreamText player, until I fix the home page. And our captioning and other show costs are covered by our sponsors, Netlify, Nx, and Backlight. Thank you all so much for kicking in to make this show better than it could be if I was on my own. While you're looking at things on the site, make sure you go and look at the schedule. There's so much good stuff coming up. I'm not -- I don't have time to read through it, but go read through it. You can add it on Google Calendar to get the details. Make sure you go and follow Ben. Ben, if people want to dig in deeper, we have a handful of links here to the Eleventy data cascade and other things. Is there anywhere else you want people to go and look at?

BEN: Yeah, I would say the documentation is at I think it's always getting better. I also want to call out that there is an Eleventy Discord server that is a great place to ask questions. Twitter is good. You can reach me on Twitter. I stream weekly @SomeAnticsDev. There's also an Eleventy meetup, if you go to This is a monthly meetup for people really interested in Eleventy. They are always looking for new speakers. So, if you want to learn, or if you want to speak there, you should absolutely, absolutely take a look at that. Yes, Stephanie is exactly right. There's an open CFP. Please, please, please, send the team your talk ideas. I've previously given this talk in ten minutes, but I've also talked at the Eleventy meetup about the data cascade and some of the internal workers of Eleventy. So, there's tons of great resources, and more resources are getting added all the time. Now that Eleventy is at 1.0, and Netlify is sponsoring its development full time, thank you so much, Netlify, I think you're only going to see more and more of that as time goes on. So, there's tons of places to learn about Eleventy and what's going on in that space.

JASON: Here we go. We're going all-in on open source. This is really, really exciting. Yeah. Okay, Ben, so many good resources today. Super exciting stuff. I love what we were able to build. And, as always, it's been a pleasure having you on. Chat, stay tuned. We're going to find somebody to raid. Thank you all so much for hanging out, and we will see you all next time.

BEN: Bye, y'all.

Closed captioning and more are made possible by our sponsors: