In today’s fast-paced digital world, businesses are always on the lookout for new ways to connect with their audience. BlueSky, a decentralized social media platform, is quickly gaining attention as a fresh alternative to traditional platforms like Twitter and Instagram. While it’s still early days for BlueSky, its unique structure offers a big opportunity for businesses to build communities and engage with users in a more transparent, user-focused way.
I’m working on a refresh of my personal website, what I’m calling the HD remaster. Well, I wouldn’t call it a “full” redesign. I’m just cleaning things up, and Polypane is coming in clutch. I wrote about how much I enjoy developing with Polypane on my personal blog back in March 2023. In there, I say that I discover new things every time I open the browser up and I’m here to say that is still happening as of August 2024.
Polypane, in case you’re unfamiliar with it, is a web browser specifically created to help developers in all sorts of different ways. The most obvious feature is the multiple panes displaying your project in various viewport sizes:
I’m not about to try to list every feature available in Polypane; I’ll leave that to friend and creator, Kilian Valkhof. Instead, I want to talk about a neat feature that I discovered recently.
Outline tab
Inside Polypane’s sidebar, you will find various tabs that provide different bits of information about your site. For example, if you are wondering how your social media previews will look for your latest blog post, Polypane has you covered in the Meta tab.
The tab I want to focus on though, is the Outline tab. On the surface, it seems rather straightforward, Polypane scans the page and provides you outlines for headings, landmarks, links, images, focus order, and even the full page accessibility tree.
Seeing your page this way helps you spot some pretty obvious mistakes, but Polypane doesn’t stop there. Checking the Show issues option will point out some of the not-so-obvious problems.
In the Landmarks view, there is an option to Show potentials as well, which displays elements that could potentially be page landmarks.
In these outline views, you also can show an overlay on the page and highlight where things are located.
Now, the reason I even stumbled upon these features within the Outline tab is due to a bug I was tracking down, one specifically related to focus order. So, I swapped over to the “Focus order” outline to inspect things further.
That’s when I noticed the option to see an overlay for the focus order.
This provides a literal map of the focus order of your page. I found this to be incredibly useful while troubleshooting the bug, as well as a great way to visualize how someone might navigate your website using a keyboard.
These types of seemingly small, but useful features are abundant throughout Polypane.
Amazing tool
When I reached out to Kilian, mentioning my discovery, his response was “Everything’s there when you need it!”
Only Chris, right? You’ll want to view this in a Chromium browser:
CodePen Embed Fallback
This is exactly the sort of thing I love, not for its practicality (cuz it ain’t), but for how it illustrates a concept. Generally, tutorials and demos try to follow the “rules” — whatever those may be — yet breaking them helps you understand how a certain thing works. This is one of those.
The concept is pretty straightforward: one target element can be attached to multiple anchors on the page.
Wait, wait! I didn’t attach the .target to the anchors. That’s because we have two ways to do it. One is using the position-anchor property.
.target {
position-anchor: --anchor-1;
}
That establishes a target-anchor relationship between the two elements. But it only accepts a single anchor value. Hmm. We need more than that. That’s what the anchor() function can do. Well, it doesn’t take multiple values, but we can declare it multiple times on different inset properties, each referencing a different anchor.
.target {
top: anchor(--anchor-1, bottom);
}
The second piece of anchor()‘s function is the anchor edge we’re positioned to and it’s gotta be some sort of physical or logical inset — top, bottom, start, end, inside, outside, etc. — or percentage. We’re bascially saying, “Take that .target and slap it’s top edge against --anchor-1‘s bottom edge.
Notice how both anchors are declared on different properties by way of anchor(). That’s rad. But we aren’t actually anchored yet because the .target is just like any other element that participates in the normal document flow. We have to yank it out with absolute positioning for the inset properties to take hold.
In his demo, Chris cleverly attaches the .target to two elements. What makes it clever is that allows you to click and drag it to change its dimensions. The two of them are absolutely positioned, one pinned to the viewport’s top-left edge and one pinned to the bottom-right.
If we attach the .target's top and left edges to --anchor-1‘s bottom and right edges, then attach the target's bottom and right edges to --anchor-2‘s top and left edges, we’re effectively anchored to the two elements. This is what allows the .target element to stretch with the elements when they are resized.
But there’s a small catch: a is resized from its bottom-right corner. The second is positioned in a way where the resizer isn’t directly attached to the .target. If we rotate(180deg), though, it’s all good.
CodePen Embed Fallback
Again, you’ll want to view that in a Chromium browser at the time I’m writing this. Here’s a clip instead if you prefer.
That’s just a background-color on the .target element. We can put a little character in there instead as a background-image like Chris did to polish this off.
CodePen Embed Fallback
Fun, right?! It still blows my mind this is all happening in CSS. It wasn’t many days ago that something like this would’ve been a job for JavaScript.
I’m a big Lynn Fisher fan. You probably are, too, if you’re reading this. Or maybe you’re reading her name for the first time, in which case you’re in for a treat.
That’s because I had a chance to sit down with Lynn for a whopping hour to do nothing more than gab, gab, and gab some more. I love these little Smashing Hours because they’re informal like that and I feel like I really get to know the person I’m talking with. And this was my very first time talking with Lynn, we had a ton to talk about — her CSS art, her annual site refreshes, where she finds inspiration for her work, when the web started to “click” for her… and so much more.
Don’t miss the bit where Lynn discusses her current site design (~24 min.) because it’s a masterclass in animation and creativity.
The text-box-trim and text-box-edge properties in CSS enable developers to trim specifiable amounts of the whitespace that appear above the first formatted line of text and below the last formatted line of text in a text box, making the text box vertically larger than the content within.
This whitespace is called leading, and it appears above and below (so it’s two half-leadings, actually) all lines of text to make the text more readable. However, we only want it to appear in between lines of text, right? We don’t want it to appear along the over or under edges of our text boxes, because then it interferes with our margins, paddings, gaps, and other spacings.
As an example, if we implement a 50px margin but then the leading adds another 37px, we’d end up with a grand total of 87px of space. Then we’d need to adjust the margin to 13px in order to make the space 50px in practice.
As a design systems person, I try to maintain as much consistency as possible and use very little markup whenever possible, which enables me to use the adjacent-sibling combinator (+) to create blanket rules like this:
/* Whenever <element> is followed by <h1> */
<element> + h1 {
margin-bottom: 13px; /* instead of margin-bottom: 50px; */
}
This approach is still a headache since you still have to do the math (albeit less of it). But with the text-box-trim and text-box-edge properties, 50px as defined by CSS will mean 50px visually:
Disclaimer:text-box-trim and text-box-edge are only accessible via a feature flag in Chrome 128+ and Safari 16.4+, as well as Safari Technology Preview without a feature flag. See Caniuse for the latest browser support.
Start with text-box-trim
text-box-trim is the CSS property that basically activates text box trimming. It doesn’t really have a use beyond that, but it does provide us with the option to trim from just the start, just the end, both the start and end, or none:
Note: In older web browsers, you might need to use the older start/end/both values in place of the newer trim-start/trim-end/trim-both values, respectively. In even older web browsers, you might need to use top/bottom/both. There’s no reference for this, unfortunately, so you’ll just have to see what works.
Now, where do you want to trim from?
You’re probably wondering what I mean by that. Well, consider that a typographic letter has multiple peaks.
There’s the x-height, which marks the top of the letter “x” and other lowercase characters (not including ascenders or overshoots), the cap height, which marks the top of uppercase characters (again, not including ascenders or overshoots), and the alphabetic baseline, which marks the bottom of most letters (not including descenders or overshoots). Then of course there’s the ascender height and descender height too.
You can trim the whitespace between the x-height, cap height, or ascender height and the “over” edge of the text box (this is where overlines begin), and also the white space between the alphabetic baseline or descender height and the “under” edge (where underlines begin if text-underline-position is set to under).
Don’t trim anything
text-box-edge: leading means to include all of the leading; simply don’t trim anything. This has the same effect as text-box-trim: none or forgoing text-box-trim and text-box-edge entirely. You could also restrict under-edge trimming with text-box-trim: trim-start or over edge trimming with text-box-trim: trim-end. Yep, there are quite a few ways to not even do this thing at all!
Newer web browsers have deviated from the CSSWG specification working drafts by removing the leading value and replacing it with auto, despite the “Do not ship (yet)” warning (*shrug*).
Naturally, text-box-edge accepts two values (an instruction regarding the over edge, then an instruction regarding the under edge). However, auto must be used solo.
text-box-edge: auto; /* Works */
text-box-edge: ex auto; /* Doesn't work */
text-box-edge: auto alphabetic; /* Doesn't work */
I could explain all the scenarios in which auto would work, but none of them are useful. I think all we want from auto is to be able to set the over or under edge to auto and the other edge to something else, but this is the only thing that it doesn’t do. This is a problem, but we’ll dive into that shortly.
Trim above the ascenders and/or below the descenders
The text value will trim above the ascenders if used as the first value and below the descenders if used as the second value and is also the default value if you fail to declare the second value. (I think you’d want it to be auto, but it won’t be.)
text-box-edge: ex text; /* Valid */
text-box-edge: ex; /* Computed as `text-box-edge: ex text;` */
text-box-edge: text alphabetic; /* Valid */
text-box-edge: text text; /* Valid */
text-box-edge: text; /* Computed as `text-box-edge: text text;` */
It’s worth noting that ascender and descender height metrics come from the fonts themselves (or not!), so text can be quite finicky. For example, with the Arial font, the ascender height includes diacritics and the descender height includes descenders, whereas with the Fraunces font, the descender height includes diacritics and I don’t know what the ascender height includes. For this reason, there’s talk about renaming text to from-font.
Trim above the cap height only
To trim above the cap height:
text-box-edge: cap; /* Computed as text-box-edge: cap text; */
Remember, undeclared values default to text, not auto (as demonstrated above). Therefore, to opt out of trimming the under edge, you’d need to use trim-start instead of trim-both:
text-box-trim: trim-start; /* Not text-box-trim: trim-both; */
text-box-edge: cap; /* Not computed as text-box-edge: cap text; */
Trim above the cap height and below the alphabetic baseline
To trim above the cap height and below the alphabetic baseline:
text-box-trim: trim-both;
text-box-edge: cap alphabetic;
By the way, the “Cap height to baseline” option of Figma’s “Vertical trim” setting does exactly this. However, its Dev Mode produces CSS code with outdated property names (leading-trim and text-edge) and outdated values (top and bottom).
Trim above the x-height only
To trim above the x-height only:
text-box-trim: trim-start;
text-box-edge: ex;
Trim above the x-height and below the alphabetic baseline
To trim above the x-height and below the alphabetic baseline:
text-box-trim: trim-both;
text-box-edge: ex alphabetic;
Trim below the alphabetic baseline only
To trim below the alphabetic baseline only, the following won’t work (things were going so well for a moment, weren’t they?):
This is because the first value is always the mandatory over-edge value whereas the second value is an optional under-edge value. This means that alphabetic isn’t a valid over-edge value, even though the inclusion of trim-end suggests that we won’t be providing one. Complaints about verbosity aside, the correct syntax would have you declare any over-edge value even though you’d effectively cancel it out with trim-end:
text-box-trim: trim-end;
text-box-edge: [any over edge value] alphabetic;
What about ideographic glyphs?
It’s difficult to know how web browsers will trim ideographic glyphs until they do, but you can read all about it in the spec. In theory, you’d want to use the ideographic-ink value for trimming and the ideographic value for no trimming, both of which aren’t unsupported yet:
text-box-edge: ideographic; /* No trim */
text-box-edge: ideographic-ink; /* Trim */
text-box-edge: ideographic-ink ideographic; /* Top trim */
text-box-edge: ideographic ideographic-ink; /* Bottom trim */
text-box, the shorthand property
If you’re not keen on the verbosity of text box trimming, there’s a shorthand text-box property that makes it somewhat inconsequential. All the same rules apply.
/* Syntax */
text-box: [text-box-trim] [text-box-edge (over)] [text-box-edge (under)]?
/* Example */
text-box: trim-both cap alphabetic;
Final thoughts
At first glance, text-box-trim and text-box-edge might not seem all that interesting, but they do make spacing elements a heck of a lot simpler.
Is the current proposal the best way to handle text box trimming though? Personally, I don’t think so. I think text-box-trim-start and text-box-trim-end would make a lot more sense, with text-box-trim being used as the shorthand property and text-box-edge not being used at all, but I’d settle for some simplification and/or consistent practices. What do you think?
When it comes to on-page SEO, there’s one element that plays a pivotal role in both search engine rankings and user engagement: the HTML title tag. Often overlooked, this small but mighty piece of code can have a significant impact on how your website is perceived by search engines like Google and by potential visitors browsing the search engine results pages.
I collect a bunch of links in a bookmarks folder. These are things I fully intend to read, and I do — eventually. It’s a good thing bookmarks are digital, otherwise, I’d need a bigger coffee table to separate them from the ever-growing pile of magazines.
The benefit of accumulating links is that the virtual pile starts revealing recurring themes. Two seemingly unrelated posts published a couple months apart may congeal and become more of a dialogue around a common topic.
I spent time pouring through a pile of links I’d accumulated over the past few weeks and noticed a couple of trending topics. No, that’s not me you’re smelling — there’s an aroma of nostalgia in the air., namely a newfound focus on learning web fundamentals and some love for manual deployments.
Ultimately, it is not about AI replacing developers, but about developers adapting and evolving with the tools. The ability to learn, understand, and apply the fundamentals is essential because tools will only take you so far without the proper foundation.
ShopTalk 629: The Great Divide, Global Design + Web Components, and Job Titles
Chris and Dave sound off on The Great Divide in this episode and the rising value of shifting back towards fundamentals:
Dave: But I think what is maybe missing from that is there was a very big feeling of disenfranchisement from people who are good and awesome at CSS and JavaScript and HTML. But then were being… The market was shifting hard to these all-in JavaScript frameworks. And a lot of people were like, “I don’t… This is not what I signed up for.”
[…]
Dave: Yeah. I’m sure you can be like, “Eat shit. That’s how it is, kid.” But that’s also devaluing somebody’s skillset. And I think what the market is proving now is if you know JavaScript or know HTML, CSS, and regular JavaScript (non-framework JavaScript), you are once again more valuable because you understand how a line of CSS can replace 10,000 lines of JavaScript – or whatever it is.
Chris: Yeah. Maybe it’s coming back just a smidge–
Dave: A smidge.
Chris: –that kind of respecting the fundamental stuff because there’s been churn since then, since five years ago. Now it’s like these exclusively React developers we hired, how useful are they anymore? Were they a little too limited and fundamental people are knowing more? I don’t know. It’s hard to say that the job industry is back when it doesn’t quite feel that way to me.
Dave: Yeah, yeah. Yeah, who knows. I just think the value in knowing CSS and HTML, good HTML, are up more than they maybe were five years ago.
Just a Spec: HTML Finally Gets the Respect It Deserves
Jared and Ayush riffin’ on the first ever State of HTML survey, why we need it, and whether “State of…” surveys are representative of people who work with HTML.
[…] once you’ve learned about divs and H’s 1 through 6, what else is there to know? Quite a lot, as it turns out. Once again, we drafted Lea Verou to put her in-depth knowledge of the web platform to work and help us craft a survey that ended up reaching far beyond pure HTML to cover accessibility, web components, and much more.
[…]
You know, it’s perfectly fine to be an expert at HTML and CSS and know very little JavaScript. So, yeah, I think it’s important to note that as we talk about the survey, because the survey is a snapshot of just the people who know about the survey and answer the questions, right? It’s not necessarily representative of the broad swath of people around the world who have used HTML at all.
[…]
So yeah, a lot of interest in HTML. I’m talking about HTML. And yeah, in the conclusion, Lea Verou talks about we really do have this big need for more extensibility of HTML.
I’m not surprised. I mean, when someone who’s only ever used React can see what HTML does, I think it’s usually a huge revelation to them.
[…]
It just blows their minds. And it’s kind of like you just don’t know what you’re missing out on up to a point. And there is a better world out there that a lot of folks just don’t know about.
[…]
I remember a while back seeing a post come through on social media somewhere, somebody’s saying, oh, I just tried working with HTML forms, just standard HTML forms the first time and getting it to submit stuff. And wait, it’s that easy?
Yeah, last year when I was mentoring a junior developer with the Railsworld conference website, she had come through Bootcamp and only ever done React, and I was showing her what a web component does, and she’s like, oh, man, this is so cool. Yeah, it’s the web platform.
Reckoning: Part 4 — The Way Out
Alex Russell in the last installment of an epic four-part series well worth your time to fully grasp the timeline, impact, and costs of modern JavsaScript frameworks to today’s development practices:
Never, ever hire for JavaScript framework skills. Instead, interview and hire only for fundamentals like web standards, accessibility, modern CSS, semantic HTML, and Web Components. This is doubly important if your system uses a framework.
Semi-Annual Reminder to Learn and Hire for Web Standards
This is a common cycle. Web developers tire of a particular technology — often considered the HTML killer when released — and come out of it calling for a focus on the native web platform. Then they decide to reinvent it yet again, but poorly.
There are many reasons companies won’t make deep HTML / CSS / ARIA / SVG knowledge core requirements. The simplest is the commoditization of the skills, partly because framework and library developers have looked down on the basics.
The anchor element
Heydon Pickering in a series dedicated to HTML elements, starting alphabetically with the good ol’ anchor :
Sometimes, the is referred to as a hyperlink, or simply a link. But it is not one of these and people who say it is one are technically wrong (the worst kind of wrong).
[…]
Web developers and content editors, the world over, make the mistake of not making text that describes a link actually go inside that link. This is collosally unfortunate, given it’s the main thing to get right when writing hypertext.
At the risk of being old and out-of-touch: if you don’t know how to write some code, you probably shouldn’t use code that Chat GPT et al write for you.
[…]
It’s not bulletproof, but StackOverflow provides opportunities to learn and understand the code in a way that AI-generated code does not.
What Skills Should You Focus on as Junior Web Developer in 2024?
Let’s not be old-man-shakes-fist-at-kids.gif about this, but learning the fundamentals of tech is demonstrateably useful. It’s true in basketball, it’s true for the piano, and it’s true in making websites. If you’re aiming at a long career in websites, the fundamentals are what powers it.
[…]
The point of the fundamentals is how long-lasting and transferrable the knowledge is. It will serve you well no matter what other technologies a job might have you using, or when the abstractions over them change, as they are want to do.
As long as we’re talking about learning the fundamentals…
The Basics
Oh yeah, and of course there’s this little online course I released this summer for learning HTML and CSS fundamentals that I describe like this:
The Basics is more for your clients who do not know how to update the website they paid you to make. Or the friend who’s learning but still keeps bugging you with questions about the things they’re reading. Or your mom, who still has no idea what it is you do for a living. It’s for those whom the entry points are vanishing. It’s for those who could simply sign up for a Squarespace account but want to understand the code it spits out so they have more control to make a site that uniquely reflects them.
Not all this nostalgia is reserved only for HTML and CSS, but for deploying code, too. A few recent posts riff on what it might look like to ship code with “buildless” or near “buildless” workflows.
It is extraordinarily liberating. Yes, there are some ergonomic inefficiencies, but at the end of the day it comes out in the wash. You might have to copy-and-paste some HTML, but in my experience I’d spend that much time or more debugging a broken build or dependency hell.
Probably not. I’d say for production-grade development, we’re not quite there yet. Performance tradeoffs are a big part of it, but there are lots of other small problems that you’d likely run into pretty soon once you hit a certain level of complexity.
For smaller sites or side projects though, I can imagine going the buildless route – just to see how far I can take it.
If you’re thinking that your next project couldn’t possibly be made without a build step, let me tell you about a phrase I first heard in the indie web community: “Manual ‘till it hurts”. It’s basically a two-step process:
Start doing what you need to do by hand.
When that becomes unworkable, introduce some kind of automation.
It’s remarkable how often you never reach step two.
I’m not saying premature optimisation is the root of all evil. I’m just saying it’s premature.
That’s it for this pile of links and good gosh my laptop feels lighter for it. Have you read other recent posts that tread similar ground? Share ’em in the comments.
PSA: Today’s the day that Google’s performance tools officially stops supporting the First Input Delay (FID) metric that was replaced by Interaction to Next Paint (INP).
Jim hit a snag while working on a form. Placing labels next to inputs is trivial with flexbox, sure, but what happened in Jim’s case was a bit of dead-clicking between the labels and radio buttons.
The issue? Not the markup, that’s all semantic and cool. Turns out the gap he placed between the elements is non-interactive. Makes sense when you think about it, but frustrating nonetheless because it looks like a bug and feels like a bug even though there’s nothing wrong with the styles.
The solution’s easy enough: padding along the inside edge of the input extends its box dimensions, allowing the added space to remain interactive with visual spacing. Margin wouldn’t work since it’s akin to gap in that it pushes the element’s box instead of expanding it.
I’m linking up Jim’s article because it’s a perfect demonstration that CSS is capable of accomplishing the same thing in many ways. It’s easy to fall into the trap of “single-solution” thinking, but CSS doesn’t want anything to do with that. It’ll instead challenge you to adapt toward open-minded strategies, perhaps even defensive ones.
What I’ve found, though, is that the :target pseudo-class seems relatively unexplored territory in this area of CSS hacking. It’s an underrated powerful CSS feature when you think about it: :target allows us to style anything based on the selected jump link, so we have a primitive version of client-side routing built into the browser! Let’s go mad scientist with it and see where that takes us.
Unbeatable AI in CSS
Did I type those words together? Are we going to hack CSS so hard that we hit the singularity? Try to beat the stylesheet below at Tic Tac Toe and decide for yourself.
CodePen Embed Fallback
The stylesheet will sometimes allow the game to end in a draw, so you at least have a smidge of hope.
No need to worry! CSS hasn’t gone Skynet on us yet. Like any CSS hack, the rule of thumb to determine whether a game is possible to implement with CSS is the number of possible game states. I learned that when I was able to create a 4×4 Sudoku solver but found a 9×9 version pretty darn near impossible. That’s because CSS hacks come down to hiding and showing game states based on selectors that respond to user input.
Tic Tac Toe has 5,478legal states reachable if X moves first and there’s a famous algorithm that can calculate the optimal move for any legal state. It stands to reason, then, that we can hack together the Tic Tac Toe game completely in CSS.
OK, but how?
In a way, we are not hacking CSS at all, but rather using CSS as the Lord Almighty intended: to hide, show, and animate stuff. The “intelligence” is how the HTML is generated. It’s like a “choose your own adventure” book of every possible state in the Tic Tac Toe multiverse with the empty squares linked to the optimal next move for the computer.
We generate this using a mutant version of the minimax algorithm implemented in Ruby. And did you know that since CodePen supports HAML (which supports Ruby blocks), we can use it secretly as a Ruby playground? Now you do.
Each state our HAML generates looks like this in HTML:
With a sprinkling of surprisingly straightforward CSS, we will display only the currently selected game state using :target selectors. We’ll also add a .c class to historical computer moves — that way, we only trigger the handwriting animation for the computer’s latest move. This gives the illusion that we are only playing on a single gameboard when we are, in reality, jumping between different sections of the document.
/* Game's parent container */
.b, body:has(:target) #--------- {
/* Game states */
.s {
display: none;
}
}
/* Game pieces with :target, elements with href */
:target, #--------- {
width: 300px;
height: 300px; /
left: calc(50vw - 150px);
top: calc(50vh - 150px);
background-image: url(/path/to/animated/grid.gif);
background-repeat: no-repeat;
background-size: 100% auto;
/* Display that game state and bring it to the forefront */
.s {
z-index: 1;
display: inline-block;
}
/* The player's move */
.x {
z-index: 1;
display: inline-block;
background-image: url("data:image/svg+xml [...]"); /** shortened for brevity **/
height: 100px;
width: 100px;
}
/* The browser's move */
circle {
animation-fill-mode: forwards;
animation-name: draw;
animation-duration: 1s;
/* Only animate the browser's latest turn */
&.c {
animation-play-state: paused;
animation-delay: -1s;
}
}
}
When a jump link is selected by clicking an empty square, the :target pseudo-class displays the updated game state(.s), styled so that the computer’s precalculated response makes an animated entrance (.c).
Note the special case when we start the game: We need to display the initial empty grid before the user selects any jump link. There is nothing to style with :target at the start, so we hide the initial state — with the:body:has(:target) #--------- selector — once a jump link is selected. Similarly, if you create your experiments using :target you’ll want to present an initial view before the user begins interacting with your page.
Wrapping up
I won’t go into “why” we’d want to implement this in CSS instead of what might be an “easier” path with JavaScript. It’s simply fun and educational to push the boundaries of CSS. We could, for example, pull this off with the classic checkbox hack — someone did, in fact.
Is there anything interesting about using :target instead? I think so because:
We can save games in CSS! Bookmark the URL and come back to it anytime in the state you left it.
There’s a potential to use the browser’s Back and Forward buttons as game controls. It’s possible to undo a move by going Back in time or replay a move by navigating Forward. Imagine combining :target with the checkbox hack to create games with a time-travel mechanic in the tradition of Braid.
Share your game states. There’s the potential of Wordle-like bragging rights. If you manage to pull off a win or a draw against the unbeatable CSS Tic Tac Toe algorithm, you could show your achievement off to the world by sharing the URL.
It’s completely semantic HTML. The checkbox hack requires you to hide checkboxes or radio buttons, so it will always be a bit of a hack and painful horse-trading when it comes to accessibility. This approach arguably isn’t a hack since all we are doing is using jump links and divs and their styling. This may even make it — dare I say —“easier” to provide a more accessible experience. That’s not to say this is accessible right out of the box, though.