February 9th, 2026 ×
We built a CSS Challenge platform
Wes Bos Host
Scott Tolinski Host
Transcript
Scott Tolinski
We decided to host a major CSS coding battle tournament here on Syntax with 14 of the greatest CSS engineers out there and then two other competitors in that competition, that being Wes and myself, competing in CSS battles.
Scott Tolinski
And we've been doing a lot of CSS battles here on Fridays and our, of the three Wes videos with CJ, Wes, and I, but we wanted to take it one step further. And so we built our own CSS coding battles platform.
Scott Tolinski
So we're gonna be talking all about how that works, the stack, how the stack works itself, what we're using for various components, why it's cool.
Scott Tolinski
And we're gonna be diving into some of the challenges that we had getting this thing up and running because let me tell you, this has been a massive sprint to build our own platform from scratch just to use for this CSS coding tournament. My name is Scott Tolinski. I'm a developer from Denver. With me, as always, is West Wes. What's up, Waz? Hey.
Wes Bos
Excited to talk about this. This has been very fun to work on the last, what, two weeks or so. I joined in about two weeks ago and helped crank out issues, but it's it's a really cool platform. It's all local first. It all syncs. It's all fast as heck since Felt.
Scott Tolinski
Really cool. Love it. Yeah. I will say it's not local first. And, I I wanna push back on that, but I I do think that's an important part of this distinction here because we'll talk about why it's not local first.
Scott Tolinski
We'll talk about the tech. It is using Deno sync, which definitely it was a platform that was initially described as being local first. But they've really kind of shifted, that message a little bit because I don't think it really truly falls under that. But I think you saying it local first is not a bad thing, Wes. I think that is, how most people might think about this and look about it. So, again, the mad CSS, March mad CSS tournament, you'll find that exclusively on our YouTube channel here. We have some of the the best competitors, Kevin Powell, Adam Argyle, Adam Wathan, Cassidy Williams, Anya, Kubo. Is that who you said her last name was?
Wes Bos
I believe so. I'm not don't don't count me on that. Sorry, Anya.
Scott Tolinski
Josh Komu.
Scott Tolinski
Wes have Kyle Cook who's also known as WebDev, Simplified, Julia Miocene, Aven Hong, Bree Hall, Chris Coyer, Jason Langstaff, Shondeh Pearson, and Amy Dutton. This thing besides
Wes Bos
the site being cool as hell, Wes, this tournament Thank you. Yeah. Is glad to be a blast..com. Go check it out because this was a really fun site to build. And Yes. We're currently working on a way to pick your own bracket. So before this thing goes live, you're gonna be able to log in to madcss.com and pick a bracket. And if you win,
Scott Tolinski
if you get it, if the the winner will be will get cash prizes. It's gonna be everything. Prizes, man. Maybe not cash, but prizes. Prizes. Prizes that did cost cash at one point for us to Wes. So, yeah, that that's kind of the crux of this thing. You might be having deja vu right now because we have talked about this before. This was initially born out of my Hack Week project.
Scott Tolinski
So we'll talk a little bit about why this is different than what the Hack Week project was. And if you want to see all of the errors in your application, you'll want to check out Sentry at century.io/syntax.
Scott Tolinski
You don't want a production application out there that, well, you have no visibility into in case something is blowing up, and you might not even know it. So head on over to century.io/syntax.
Scott Tolinski
Again, we've been using this tool for a long time, and it totally rules. Alright.
Wes Bos
Real quick. The stack here, the stuff we built this on, it's using SvelteKit because Hold on. Can you just show what it does visually first before we get into the stack? I think that will frame it a little bit better.
Wes Bos
Yes. Because people might not know what a CSS battle is.
Scott Tolinski
For people who are watching on video, you'll be able to see the UI. For people who are listening, we'll do our best. You know, I don't wanna, make this only a video only episode.
Scott Tolinski
But a CSS battle in terms of, like, what this is actually doing is people have a target, which is a thing that they were are coding towards, a thing that they are trying to recreate with CSS and HTML.
Scott Tolinski
And the way the CSS battles works is that you code your HTML and CSS, and you try to get it as close as possible to the target.
Scott Tolinski
And the person who gets there first to dead on 100% or gets the highest score where things are closest, is the winner. So if you're looking at this on YouTube, you see right now my code, which is up here in the top left. You see the target, which is over here. Then, we have a diff view and an overlay so you can see what's different, and then you even get access to your competitor's view. So that way, when you're competing and you're coding, you can see how far along they are to either stress you out or calm your nerves, slow you down.
Scott Tolinski
So that's the general crux of this JS, a a target is created with either a video, an image, or CSS and HTML. Right now, we're just using HTML and CSS targets.
Scott Tolinski
And, again, we are comparing.
Scott Tolinski
We'll talk quite a bit about the diffing part of it because that was a huge component of this. And, again, this whole thing works real time. I'll talk about the tech involved to make this work and what's unique about it. So
Wes Bos
am I am I free to talk about the stack now, Wes? Let's talk about the stack. People understand it. I think one more thing is that, like, the code that is being written, you'll notice there's no there's no code part. Right? And that's because I hate hate hate writing code in some little text box on a website when I'm trying to be as fast as possible because none of my shortcuts are there, Node of my tab completions are there, and you just you just feel weird trying to to type as quickly as you can when you don't have all of your your comforts of what you are actually used to. So the important part about this SynHacks that we built is that you it uses local file system access, and you just use your own editor. You use your own tools. You use your own completions, all of that stuff locally on your computer, and then it syncs that data from your file system to the Synhax platform.
Scott Tolinski
Yeah. It's pretty cool. We'll talk about how all that works too. So the stack, it's built in SvelteKit because I started it, and I love SvelteKit. So easy choice for me. I also we need a server and a client. So this couldn't just be a UI client. This had to be a server and a client.
Scott Tolinski
And then we used Deno sync. Now zero sync is a library for doing syncing. I'll talk a lot about how zero works after we talk about this stack just so people can understand why Xero is so good. But Xero gives us real time. It gives us really fast everything.
Scott Tolinski
It's it's really what allows this application to work the way it does as easily as it does. It's a great platform.
Scott Tolinski
I also use the Deno Svelte bindings.
Scott Tolinski
This is a, Svelte bindings that I wrote myself, and I'd like to think that the syntax is really nice. I'd like to think that it all works really easily, and it's very meteor like in how I like to work in it. So, again, it's really trying to chase that dream of of having meteor back in my life.
Scott Tolinski
Also, Drizzle.
Scott Tolinski
It uses, Drizzle for database, schemas.
Scott Tolinski
We do have to have a schema for Deno as well, but there is a Drizzle Deno package that takes your Drizzle schema and translates it to directly to a zero schema without you having to do anything. So Drizzle, great choice.
Scott Tolinski
Deno requires Postgres as a database. So just Drizzle, Postgres.
Scott Tolinski
We have the database hosted on Supabase for no reason other than ease of simplicity of getting something up and running for my Hack Week project. Yeah. I I host zero apps on Coolify using a Postgres database there. I do them all kinds of ways. So, Supabase Scott required. We're not using any of Supabase's libraries here. Just needs a Postgres database? Just needs a Postgres database. I also have the basis for the CSS using Graffiti.
Scott Tolinski
Now we're not using a ton of Graffiti stuff, but there's a number of, like, really nice graffiti utilities, whether that JS, like, stacks or splitting or those types of things that come in handy.
Scott Tolinski
We have a whole ton of colors in here too. But, in classic Wes fashion, he's not a fan of, any of the colors, so he just, overwrote all the colors himself, which is, maybe the the most common way of working with Wes.
Wes Bos
I I I was actually frustrated with with a lot of this because Yeah.
Wes Bos
You there was no, like, docs for it in the code Bos, and I just didn't know what a lot of the variables were.
Wes Bos
And, like, I'm sure I could've could've looked them up as well, but, like, we're we were on a huge time crunch. We were on a huge time crunch. I did use quite a bit. Like, over all the purple on it as well as I just overrode a lot of variables. But then, like, things like the font sizes, Scott loves to do this, like, min max Vercel. Oh, the font size system is so dope. Everything's scalable. And, like, I was like, how do I make this font bigger? And, like, there was there was no way to do that. So it just Yeah. You're just yeah. I know. So it's very systematized for sure. Yes. It is. It's great until you need to
Scott Tolinski
step outside of the system. That's how they work, Wes. And, actually, it's so funny. After our conversation about what's missing from the web, you told me we could do tabs using details and summary. And I was like, I don't know. I I went through and actually added a full on Node JavaScript
Wes Bos
tab There you go.
Scott Tolinski
Using details and summary in here now too. So that exists in tooltips via popover and anchor positioning. So I've been adding stuff to this kind of as I need it. And, for me, because I use this on every project, I'm very familiar with it. So it was quite easy for me to say, oh, let's just use my thing that I'm very familiar with.
Scott Tolinski
I I have been working on I don't I don't know if this is, like, a thing that even matters anymore, but, like, the llm's.txt is, like, really organized now Wes it's like, if you need a tag, come to the tag docs. If you need a boxes, go to the boxes docs. Like, it tries to do a good job of making the LLMs work well with it. So something I've been focusing on a little bit.
Scott Tolinski
Sync server. We run the sync server on fly.io.
Scott Tolinski
Why? Because I just needed to get it up somewhere. And if we're being entirely honest, I don't love fly.io. And it's not Fly's fault. It's a skill issue. I just have the hardest time finding the things I need to find on there, whether it's the logs or the whether did this deploy work or not or why didn't it work. Like, I get really frustrated, and I don't like how it's all set up. But you need a container somewhere for, the sync server to run, like, a long lasting container.
Scott Tolinski
And I typically run these on COOLIFY, but my COOLIFY server's over in Germany, and I wanted it to be faster here. So I I put it on fly.io.
Scott Tolinski
And you know what? Just it's something I'll change at some ESLint. But anyway works. Yeah. There is a somebody has implemented the sync server,
Wes Bos
in the Xero docs as a CloudFlare worker durable object To have that. Is really cool. So if you must be new. You need it to be long running. Right? Yeah. The CloudFlare solution to that in the serverless world is durable objects. So they have a
Scott Tolinski
Deno in their docs has a an example that you can get up and running. But, like, we were so crunched for time. It was just like, don't touch the things that are not broken. Right? That's how it works. Right? Sometimes things just exist as load bearing blocks that you're like, you know what? This works right now. It took me a long time to set up. I'm not gonna touch it. Definitely, we'll move it to CloudFlare because the the server and the UI of this is running on CloudFlare workers just via the SvelteKit CloudFlare adapter. So, the actual app is running on on CloudFlare workers. And then, again, we use the local file system JS Wes mentioned. When you first enter a battle or whatever, it asks for a folder. It creates that folder, for the given thing, and then you have code that you could just drop into your Versus Node and then start coding.
Scott Tolinski
The way that that works in the the code here is really kind of interesting where you connect to the file system, and then you're storing that file system handle in IndexedDB, and then having that persist as Svelte state.
Scott Tolinski
It is a whole thing.
Wes Bos
It's so fast, though. Like, it like, for our CSS battles, it's just as fast as, like, using Vite. Like, it just refreshes it immediately, and everything is as soon as you hit save, you can see what you're working on. Yeah. And let's talk about how that works because
Scott Tolinski
it is really interesting. I have, like, if the file has been changed, it is just updating a database record. But because of how zero sync works, it's so fast and instant. So, how does how does zero sync work really quickly? Kind of like no fluff here.
Scott Tolinski
Xero is basically a platform with a sync engine as well as a client side library to do the queries and stuff, a server side library to do the server side stuff, and then a sync server. So the big pieces. Right? So you have the client.
Scott Tolinski
The client has versions, client versions, of the queries and the mutation functions.
Scott Tolinski
These things run on the client and take place instantly. So when you click something, like, I'm gonna click new battle or something, it creates that battle instantly and saves it into your client side data using the client side queries and mutations code.
Scott Tolinski
Then in the background, connects with a WebSocket to the zero cache sync server.
Scott Tolinski
That happens so that when I save it, the database knows what data has changed, and it sends a patch message to the sync server.
Scott Tolinski
The sync server is a long running, server JS we mentioned, and that replicates Postgres to a SQLite replica file.
Scott Tolinski
It then basically determines what client rows have changed since the last updates and then sends a diff to your server.
Scott Tolinski
So, basically, the sync server is handling, like, what has changed and what messages I need to send back and forth, and it does so with, like, I've mentioned, diffs about, like, just what's changed. So it's not sending a massive amount of data on everything, which is one of the reasons why Xero is so fast. It's incredibly fast.
Scott Tolinski
It's incredibly fast because it stores your data locally, and therefore, it can load it locally very quickly.
Scott Tolinski
It's incredibly fast because the data that it does send is in patch messages for updates. And then that sends basically a message to the API that, again, lives on our app. So you have two pieces, the app, the sync server.
Scott Tolinski
App sends to the sync server, sync send server sends back to the app and the API routes. K? That API server is really cool because that's where it processes the queries and mutations server side. This is where, like, auth happens. This is where permissions happen and stuff like that. The way that zero like database like like, to be clear, zero is not a database. Right? No. Like, it's Yeah. Like, all of your rights eventually just happen in, what, like, Drizzle updates? Yeah. Yeah. You're pretty much just writing Drizzle code to do those. Well, actually, that's not true. No. You're writing sorry. The the those mutations, Wes, are happening in the exact same code that is used in the client version of that is also that same code is used in the server versions of it just being run server side.
Wes Bos
But how it how it actually saves it to the database is not part of of Xero. Right? Like, it eventually, Xero will then translate it to what? The zero Drizzle adapter and then save Node to the Postgres?
Scott Tolinski
No. The Drizzle Deno, adapter is only
Wes Bos
giving giving us the schema translation. So Oh, so if you wanted to to access the database directly with the same schema.
Scott Tolinski
Yes. Correct. Okay. I was wrong then. Okay. Yeah. In fact, I can even show you the database inserts are happening in a a transaction.
Scott Tolinski
So if this is this is actual zero code. If you're looking at the screen, you can see it. This is the zero code that is doing the insert. So it is very similar to an ORM type of thing you're doing.
Scott Tolinski
TX, which is the transaction dot mutate dot the table dot insert, and you're just passing your data in there. So, very similar to how you would do it using any kind of ORM here. Really nice.
Scott Tolinski
So the the way that auth works the way that it used to work was kind of a giant PIA.
Scott Tolinski
There was a the whole schema file, you had to write all of your permissions and auth code in this in your schema file in a, like, a real hot potato way. And then there was a Deno process that you had to then apply that permissions and auth stuff to your sync server itself. And the sync server was the thing that was doing all of this code. It was doing all the mutations and stuff for you. The way that they have since changed it is like, oh, you know what would be way easier than having the sync server try to manage all that stuff? If you just got to write a route that collected all of those queries or collected all of those mutations in your own application and just apply them in your own code. The reason why that's more simple is because, you can handle auth the same way that you would handle auth if it if there was no sync server involved.
Wes Bos
So, like and probably a lot of people that are are adding this to their app, it maybe it this is great if it's, like, a greenfield app. Right? But if you're trying to, like, just add sync to an existing Oh, yeah. Database or existing application, you probably already have all of your own authentication
Scott Tolinski
permissions code. So now you would just to incrementally adopt that, you're just creating those API routes, and you're doing whatever incremental ones you want. It it's really kinda great, and you are just essentially taking our auth system is being done in in middleware like you typically do in a SvelteKit app. It's being passed along immediately to any of the routes that exist, and we're just hot potatoing that into the context inside of Deno sync.
Scott Tolinski
Then zero sync has your user auth stuff in context. It way simplified everything. And if our auth if our our sync server and everything lived on the same, URLs and servers, it would have been way easier because we could have just used HTTP only cookies, but we had to do the whole bear auth thing, and that was obnoxious.
Scott Tolinski
Yeah. Oh, I should say we're using better auth for this too. Better auth to do the, Google OAuth, and that's it.
Scott Tolinski
But better off is involved as well.
Scott Tolinski
One question I often get about Deno sync in regards to it being local first or any of that stuff is, like, why is it, one, not local first and, two, offline and, like, data conflicts and CRDT kind of stuff? So it's not local first because Xero's really moved away from the idea of it being a good platform for offline.
Scott Tolinski
They've kinda determined that offline for this is less useful and less where they want to go with it. So because of that, Local First is still always going to be the app should work purely offline.
Scott Tolinski
You should own your Node, data. It should be easily encrypted.
Scott Tolinski
It, it you know, there can be a sync process that syncs that data and stuff like that.
Scott Tolinski
But I think the Xero folks kind of realized that this system can still be very, very impressive, very fast, load your data locally from that IndexedDB while not necessarily focusing on offline or conflict resolution.
Scott Tolinski
The way that the conflict resolution does work. So let's say Wes and I both edit the same row at the same time.
Scott Tolinski
Both of the updates happen locally on our own machines, then those messages get sent to the sync server. That server basically commits those in the order that they were received.
Scott Tolinski
So one of them might override the second one if it comes in afterwards.
Scott Tolinski
So, basically, what happens is I send a message, Wes sends a message, the first one that arrives gets applied first, the next one then gets applied, but then the sync server basically understands, what the actual convergence is there and then sends the data back out to everybody.
Scott Tolinski
So then someone's data isn't going to be stale if we both write the same thing.
Wes Bos
I write I write That's that's one killer feature that we haven't mentioned yet JS this is all entirely real time. Real time. Yes. Whenever I would make an update to anything or Wes like, sometimes, Scott would create a new battle and then invite me, and it would just immediately pop up on my dashboard without having to to do a refresh. And there was no polling or anything involved there. Simply just was, oh, this data has now changed. So this view requires this data, and it just immediately updates.
Scott Tolinski
Can I can I start a battle with you to kinda show that off how that works? Sure. Yeah. Let me let me go log in here.
Scott Tolinski
Okay. Folks who are listening on audio, you're not gonna be we're we're not gonna just, like, leave you hanging here. But I am going to just, create a quick battle. I'm inviting Wes to it. His automatically shows up. And I just wanna show you really quick how the file system stuff works or at least talk through it. I wanna, talk through the general, saving flow so you can really truly see, how fast and impressive this all is.
Scott Tolinski
So I joined a battle. It created this folder for me just by joining the battle, which is the the files. You can see how many we've done to test. I'm gonna drop this into, Versus Node, and you can see I have HTML and CSS just kind of here.
Scott Tolinski
These are the files that you will then edit for when you do the Bos battle. We're both locked into the battle. We can click lock in players. Okay. So I have a couple of views here. We have the referee view Wes it can show both people kind of side by side here. We also have the lobby, which allows you to, press play and get going on a battle. Can I start a battle if I'm a competitor?
Wes Bos
No. You shouldn't be able to. You might be able to, but you shouldn't be able to. So then if I'm the referee and the competitor? Yes. You can. That's that's
Scott Tolinski
how most of our testing has has worked. So just to show people really quick, just generally, when I'm typing in my code editor on the local file, I can do background, background red, save it. And you can see the moment I hit save, it updated. And then Wes is also going to be seeing those changes in real time. Wes, do you wanna make a change to yours and we can see Wes's computer view? Though, again, the way this is working is that when I have a file save, it triggers a, a, basically, a function call on zero that runs the mutation.
Scott Tolinski
The sync server reconciles that. Real time sends the updates out to everybody. That way, the first person to get to a 100% is the winner, and these updates you can see are just happening in real time, our progress bars. It is really, really, really slick, and the whole system just works really super great. Let's talk about the Hack Week version of this project. You may have remembered us talking about this. The way that this platform used to work is that there was voting involved.
Scott Tolinski
There was three people going at once. And what we really needed to do is transform this from being like a three people and a subjective winner to being two people at once and an objective winner, which is, done via diffing, which Wes was gonna have to go deep on. Zero two point five also changed how they do permissions and all this stuff. So the app needed to be re and in fact, I was talking to Courtney about this. I think this would have been easier to write from scratch than it would have been to me to adapt the Hack Week project.
Scott Tolinski
Between the changes in Xero, the changes in how battles are run and operated, I ended up probably rewriting most of it before you ended up getting involved, Wes. Like Yeah. I I shoulda just started from scratch and just kept the the general concepts because there was so much, that needed to change. And then the biggest one of those changes being when you came in and brought in the diffing algorithm. Do you wanna, share a little bit about how you managed to do that? Yeah. Yeah. So
Wes Bos
this is one problem that we have with the CSS battles platform JS that it's like, you can get so close and then and there's, like, one weird thing. And I think the way that the CSS battles diffing algorithm works is that it sends your code to the server. They probably have, like, Chromium running. It takes a screenshot and then dips it against the the actual target.
Wes Bos
We wanted that to happen in browser, both so that any quirks between your browser or whatever are are gone because your target and your actual thing that's running, those are both running in the exact same browser. So whatever browser is actually computing the score is is going to be rendering them out exactly the same, which is really helpful.
Wes Bos
So what we did is I ended up using this package called snap dom. You may have used HTML to Canvas in the past. So this there's, like, a way better version now called snap dom. And it essentially, you just give it a, an HTML element on the page, and it will then convert that to, like a canvas and then export it as, like, a PNG or SVG. The way that it actually works is really interesting to me is that they take your HTML, put it inside an SVG foreign object tag, and then I think since since it's then an SVG, which is an image, you can then write the image to the canvas.
Wes Bos
Right? So, like, it doesn't have to, like, do a whole bunch of I think that's how it works.
Wes Bos
There is a lot of, like, gotchas and stuff. I had a couple bugs that I burned a day on, but we got it all working, and it it works really well. And then the actual diffing algorithm itself, we went through several different ways to, like, figure out, are these things close enough? Right? So, like, we tried using vectorization.
Wes Bos
You Node? Vectorize the images and see how close they Yarn. And most vectorization LLMs will care about what is in the like like, if there's a dog in it, right, or if there if there's a red dress in it. And they don't necessarily care about pixels that much. Right? Mhmm. And then there's other things called SSSIM, which is structural similarity index measure, which is how how similar Yarn two images to each other.
Wes Bos
That worked okay ish, but it still wasn't, good enough to making it close enough to pixel perfect. Because we we didn't want it to be pixel perfect, but we want it to be close enough, whereas if you had, like, a weird, like, outline on something, and that would make you lose the whole thing. So I think we I probably went through, like, six or seven different iterations of it. We finally landed on one that had different thresholds.
Wes Bos
Scott, if you yeah. You can see the diffing engine in the bottom right here.
Wes Bos
It has different thresholds, different three different algorithms in there, different weighting of different colors. If something JS has, like, a bit of opacity in it, it weighs it a weights it a little bit less.
Wes Bos
And we finally landed on something where, like, this feels this feels really good.
Wes Bos
What else? Oh, yeah. The the big issue we had was, like, the simply putting the background color on one of those things would get you to, like, 98%.
Wes Bos
And we just it's because, like, a lot of times, the background did account for 98% of the pixels.
Wes Bos
So we ended up writing something where you could just punch out the background color from each of the targets and then not dock them or count them. Right? It just it just takes those pixels out of the equation entirely, and then that also helps with things like box shadow and whatnot, where if the box shadow wasn't perfect, it was just it was close enough, and then we we'd give you some points for that.
Wes Bos
So that was pretty fun. And then we also built these, like, a diff viewer Wes it shows you if if you click on the diff viewer there, you can see, like, red is significantly different, whereas, like, greens and blues are are not as, not losing you as many points. So if you're looking at your diff and you're you're like, what should I spend time on? You go on you wanna go for the red stuff. Right? And then we built this little overlay thing where you you can do it kinda, like, side by side. If you just click on it, you can see exactly where yours is overlaid the target.
Scott Tolinski
Yeah. And that that overlay view, I think, is really, I think JS all really helpful. One of the things that I think helped with this is that we have done so many CSS battles that we were like, I know what I would want different on this platform. Like, I I know, like, what's working and what's not working for us. And that allowed us for, like, specifically, this overlay.
Scott Tolinski
You really like the overlay and wanted it automatically whatever, but I didn't like how it was automatic. And then you came up with this idea to pull and drag. And I do love the little border and drop shadow you gave on this. And then only that all the time. You can use that with the opacity to really dial it in. I find that the the tools to be really nice for all of this.
Scott Tolinski
I found using the dev tools to be nice in this this entire process.
Scott Tolinski
The the diffing stuff, I will say, folks, the way that we dialed in the diffing everything was from sheer,
Wes Bos
dog shit.
Wes Bos
Running. Running. We ran so many tests. And every time we did a test and we thought we had it, like, something weird would come up where, like, CJ would put a div, like, that would cover the entire thing. And, like, that technically is not a background color. That's a div with the with the background color.
Wes Bos
So then we had to write something else that would detect what the background color is even if it's not the body background color. And then we also implemented, Tailwind CSS support, so that had to that had to be thrown in there as well.
Wes Bos
You had to, the sandboxing was really neat. I was like, yeah. People like like, one thing you could do early on was you could just write a script tag in your HTML, and then it would execute that on everybody else's computer.
Scott Tolinski
Yes.
Wes Bos
And I was like, man, we gotta, like, run this through, like, a sanitizer or whatever. But, like, iframes, these are all iframes.
Wes Bos
Iframes have a sandbox attribute where you can allow and disallow things. So what we did is we disallowed inline script tags or, no, we allowed JavaScript because we needed the Tailwind, CDN to load and and and compute the Tailwind Node. But then we used a, a CSP, content security policy, where we did a really good episode with Alex Sexton from Stripe on CSP. But, basically, via header tags or or headers on your HTML element, you can say what resources are allowed to be loaded in. So you're only allowed to load script tags from one specific URL, and that is the Tailwind, CDN for for their JavaScript library.
Wes Bos
And then, otherwise, you're not allowed to do any inline JavaScript. You're not allowed to there's several other things you're not allowed to do, and that worked out well.
Scott Tolinski
Yeah. Yeah. It really worked out well. I I think this whole thing, like I've mentioned, like, so much of it came down to us using it and using it and trying it and hitting bugs and hitting errors. We, for an entire week, were just like, alright. Every single morning, we're gonna use it. We're gonna test it out. We're gonna write down all of the the amount of bugs that we have. We're gonna fix those all, and we're gonna come back the next day. And I think it Wes, like so the battles were starting Monday, and I think it was, like, Thursday that I was starting to feel maybe a little bit nervous about it because it still felt like we were logging so many things. But at that point, the diffing engine had really come together.
Scott Tolinski
Things had really started. And I was recovering from engine Wes Friday, 03:00.
Wes Bos
And you guys are like, well, let's maybe, like, change how everything works. I was like, guys, we have two hours. But luckily, we could we could reuse a lot of the other background color stuff for the transparency.
Scott Tolinski
Yeah. And there there it is tough because you you end up in this, spot of, like, is it good enough? Is it going to cause problems? Like, there were so many times where it felt like it was good enough, but, like, we wanted it to be really special, I think.
Scott Tolinski
And I think that ended up really working out because at the end of the day, the entire thing like this right here, if you're looking at the screen, you can see my diff right now says I'm at 99.31.
Scott Tolinski
I would say this darn looks about 99.31%.
Scott Tolinski
And we spent so much time, like, making sure that percentage and I will say Wes spent so much time making sure that percentage matched what you would expect.
Wes Bos
Like Man, I I have a newfound appreciation for people that develop, like like, the algorithm for, like like, TikTok or or Twitter or something like that because you turn one knob Oh. Like like, that example of of those little lines right there is like, oh, well, we can we can relax, like, pixels that are like, maybe maybe it's a one pixel diff. You know? And and if the pixel to the north and the south of it or or the east and the west of it are are bang on, then maybe we just allow that one. You know? But you turn one knob, and then all of a sudden, something else changes. And you you turn another knob, and and we had so many different test cases to to go through.
Wes Bos
But, like, it really makes me appreciate people who write these algorithms for, like, Twit you know, like, every week, Twitter's showing you all kinds of new garbage. Yeah. And you're just like, man, they probably turned one knob, and then all of a sudden, something else is is totally out of whack.
Scott Tolinski
Yeah. Totally. I I, some of the biggest bugs that we had besides just, like, dialing in the diffing, we kept getting the sync engine would just start randomly saying that you were unauthorized.
Scott Tolinski
And this one was tough because you never knew if it was, like, where the the bug was. Like, what was the issue? Because it's happening on the sync server, and, yeah, we're combing through logs and we're adding, we have Sanity in here. We're checking Sentry. And the issue ended up being that the cookie that stored our auth token was long lasting, and the JWT had an expiry of fifteen minutes.
Scott Tolinski
So if we had been working on it for fifteen minutes, it would suddenly, in the sync process, tell you, oh, by the way, you're not authorized anymore, which JS a tough tough bug to find. And and luckily, we all have good enough fundamentals that we kind of were able to break down step by step Wes could this be. And CJ was the one that had the idea to be like, you know what? I gotta check this JWT to see, like, what's going on here, and he discovered the fifteen minutes expiry time.
Scott Tolinski
There's a a tough thing there with that expiry time because the way that the sync engine works for you to update that JWT with the sync engine in how we are doing it, you would have had to, like, tear down and rebuild the zero client instance. And that might have been fine, but we were, like, too late in the process for me to be like, oh, yeah. In the middle of the battle, let's just tear down and rebuild the sync instance and hope that everything works fine. I felt like that would have been opening up a door to potential bugs and issues. So the solution for us was just to make that JWT, longer lasting, at least before, these battles to get through this before, you know, maybe taking a separate look at it. For, like, a proper refresh
Wes Bos
behind the scenes.
Wes Bos
Totally. Like, you shouldn't have to refresh the page for it to work eventually, but, like,
Scott Tolinski
it was close, and we got her done. Yeah. That was a tough bug. I don't when did we push that,
Wes Bos
update? That was, like, day of or maybe Node on Friday when you guys had pushed it. You know? Like, that was after hours for for Eastern time zone. So it was it was pretty close, and we started on Monday. So Yeah. We did one final run through on Monday morning,
Scott Tolinski
and the first battle started on Monday.
Scott Tolinski
Keep in mind, we had to do all of this while keeping Wes and I in the dark about the the challenges, the nature of the challenges.
Scott Tolinski
So a lot of the data that we were working with, the test data and stuff like this, oftentimes were, like, intentionally not what we were gonna be anywhere near in the tournament because we already have enough of a home field advantage from doing CSS battles with each other that, like, we didn't want any sort of advantage for us in this tournament. It's you know, I I want to win.
Wes Bos
Speak for yourself. I want all of the advantages.
Scott Tolinski
Well, I wanna I wanna win fair, Wes. If I win, I want to win fair.
Scott Tolinski
We should say at the time of recording this getting knocked out. Yes. The battles haven't really taken place.
Scott Tolinski
And, so
Wes Bos
you'll see good results. An hour for my first one, and I'm Yeah. Feeling a bit nervous. Oh, I was nervous. I think everybody's been nervous. We got these sick jackets, though. So I'm not sure when this one comes out, but March the March is when this will start, and I think there's five videos that will play out through the rest of March.
Wes Bos
Madcss.com, we have these jackets you can buy.
Wes Bos
It's really cool. Look at this syntax in the liner.
Wes Bos
It's all embroidered. We got the mad CSS logo. We got syntax on the arm here. This is, like, the sickest jacket in the world. Really nice snaps too.
Wes Bos
Big fan. It's so cool. If you wanna grab one of these, go to,
Scott Tolinski
by the time you're listening to this, they probably won't be up in the store just yet, but stay tuned because you'll you'll be able to grab one. Yeah. And if you subscribe to this channel, like, subscribe, all that stuff, not only are you gonna get to see the battles, which are going to be a ton of fun, but you're gonna get to, be aware of that kind of stuff. There's also a really, really cool water bottle that we have for this tournament and generally cool stuff. This incredible logo designed by Caitlin Bloom on our team, we've been rocking this thing on all kinds of stuff. So, like, I bet I just bet oh, yeah, baby. Look at that. Wes has got a three d printed one, I think. Did you make that yourself?
Wes Bos
Yeah. I did.
Scott Tolinski
Surprisingly hard. I don't have the orange. I wanna make one. I know. I was
Wes Bos
the orange is not exactly the color orange, but it was close enough. This was the first run at it. So it looks sick, though. I'm like,
Scott Tolinski
this is so cool. Caitlin did an awesome job. So while we were doing this, the rest of the Syntax team was doing their own crazy amount of stuff, whether that's like, three d backgrounds for the the c d CJ as a commentator view. Or It's gonna be nuts. It's gonna be nuts. This is a full court press to keep up with the basketball theme here. Full court press on everybody doing incredible work. So shout out to the entire Syntax team for making this happen. Keep your eyes peeled for mad CSS on YouTube.
Scott Tolinski
It is going to be an absolute blast, and I sincerely hope that I walk away with the trophy. That is what I hope.
Wes Bos
I'm gonna win. Easy.
Scott Tolinski
I'm not gonna win.
Wes Bos
I would beat you. I always beat you.
Wes Bos
Sometimes.
Scott Tolinski
Wes. Always sometimes.
Wes Bos
Alright. Mattcss.com.
Wes Bos
Subscribe.
Wes Bos
It's coming in March. It's gonna be amazing. Peace.