Posts in Frontend (20 found)
Thomasorus 5 days ago

Is frontend development a dead-end career?

It's been almost a year at my current job. I was hired as a frontend developer and UI/UX coordinator, but I've been slowly shifting to a project manager role, which I enjoy a lot and where I think I contribute more. We build enterprise web apps, the kinds you will never see online, never hear about, but that power entire companies. The config application for the car assembly line, the bank counsellor website that outputs your mortgage rate, the warehouse inventory systems... That's the kind of thing we do. For backend engineers that's a very interesting job. You get to understand how entire professional fields work, and try to reproduce them in code. But for us the frontend guys? The interest is, limited to put it simply. We're talking about pages with multi-step conditional forms, tables with lots of columns and filters, 20 nuances of inputs, modals... The real challenge isn't building an innovative UI or UX, it's maintaining a consistency in project that can last years and go into the hands of multiple developers. Hence my UI/UX coordinator role where I look at my colleagues work and sternly say "That margin should be , not " . Because here's the thing: this type of client doesn't care if it's pretty or not and won't pay for design system work or maintenance. To them, stock Bootstrap or Material Design is amazingly beautiful compared to their current Windev application. What they want is stability and predictability, they care it works the same when they encounter the same interface. Sometimes, if a process is too complex for new hires, they will engage into talks to make it more user friendly, but that's it. Until recently, the only generated code we used were the types for TypeScript and API calls functions generated from the backend, which saved us a lot of repetitive work. We made experiments with generative AI and found out we could generate a lot of our template code. All that's left to do is connect both, the front of the frontend and the back of the frontend , mostly click events, stores, reactivity, and so on. People will say that's where the fun is, and sometimes yes, I agree. I've been on projects where building the state was basically building a complex state machine out of dozens of calls from vendor specific APIs. But how often do you do that? And why would you do that if you are developing the backend yourself and can pop an endpoint with all the data your frontend needs? And so I've been wondering about the future. With frameworks, component libraries, LLMs, the recession pushing to deliver fast even if mediocre code and features, who needs someone who can write HTML, CSS, JS? Who can pay for the craft of web development? Are the common frontend developers folks, not the already installed elite freelancers building websites for prestigious clients , only destined to do assembly line of components by prompting LLMs before putting some glue between code blocks they didn't write?

0 views
David Bushell 1 weeks ago

Everything you never wanted to know about visually-hidden

Nobody asked for it but nevertheless, I present to you my definitive “it depends” tome on visually-hidden web content. I’ll probably make an amendment before you’ve finished reading. If you enjoy more questions than answers, buckle up! I’ll start with the original premise, even though I stray off-topic on tangents and never recover. I was nerd-sniped on Bluesky. Ana Tudor asked : Is there still any point to most styles in visually hidden classes in ’26? Any point to shrinking dimensions to and setting when to nothing via / reduces clickable area to nothing? And then no dimensions = no need for . @anatudor.bsky.social Ana proposed the following: Is this enough in 2026? As an occasional purveyor of the class myself, the question wriggled its way into my brain. I felt compelled to investigate the whole ordeal. Spoiler: I do not have a satisfactory yes-or-no answer, but I do have a wall of text! I went so deep down the rabbit hole I must start with a table of contents: I’m writing this based on the assumption that a class is considered acceptable for specific use cases . My final section on native visually-hidden addresses the bigger accessibility concerns. It’s not easy to say where this technique is appropriate. It is generally agreed to be OK but a symptom of — and not a fix for — other design issues. Appropriate use cases for are far fewer than you think. Skip to the history lesson if you’re familiar. , — there have been many variations on the class name. I’ve looked at popular implementations and compiled the kitchen sink version below. Please don’t copy this as a golden sample. It merely encompasses all I’ve seen. There are variations on the selector using pseudo-classes that allow for focus. Think “skip to main content” links, for example. What is the purpose of the class? The idea is to hide an element visually, but allow it to be discovered by assistive technology. Screen readers being the primary example. The element must be removed from layout flow. It should leave no render artefacts and have no side effects. It does this whilst trying to avoid the bugs and quirks of web browsers. If this sounds and looks just a bit hacky to you, you have a high tolerance for hacks! It’s a massive hack! How was this normalised? We’ll find out later. I’ll whittle down the properties for those unfamiliar. Absolute positioning is vital to remove the element from layout flow. Otherwise the position of surrounding elements will be affected by its presence. This crops the visible area to nothing. remains as a fallback but has long been deprecated and is obsolete. All modern browsers support . These two properties remove styles that may add layout dimensions. This group effectively gives the element zero dimensions. There are reasons for instead of and negative margin that I’ll cover later. Another property to ensure no visible pixels are drawn. I’ve seen the newer value used but what difference that makes if any is unclear. This was added to address text wrapping inside the square (I’ll explain later). So basically we have and a load of properties that attempted to make the element invisible. We cannot use or or because those remove elements from the accessibility tree. So the big question remains: why must we still ‘zero’ the dimensions? Why is not sufficient? To make sense of this mystery I went back to the beginning. It was tricky to research this topic because older articles have been corrected with modern information. I recovered many details from the archives and mailing lists with the help of those involved. They’re cited along the way. Our journey begins November 2004. A draft document titled “CSS Techniques for WCAG 2.0” edited by Wendy Chisholm and Becky Gibson includes a technique for invisible labels. While it is usually best to include visual labels for all form controls, there are situations where a visual label is not needed due to the surrounding textual description of the control and/or the content the control contains. Users of screen readers, however, need each form control to be explicitly labeled so the intent of the control is well understood when navigated to directly. Creating Invisible labels for form elements ( history ) The following CSS was provided: Could this be the original class? My research jumped through decades but eventually I found an email thread “CSS and invisible labels for forms” on the W3C WAI mailing list. This was a month prior, preluding the WCAG draft. A different technique from Bob Easton was noted: The beauty of this technique is that it enables using as much text as we feel appropriate, and the elements we feel appropriate. Imagine placing instructive text about the accessibility features of the page off left (as well as on the site’s accessibility statement). Imagine interspersing “start of…” landmarks through a page with heading tags. Or, imagine parking full lists off left, lists of access keys, for example. Screen readers can easily collect all headings and read complete lists. Now, we have a made for screen reader technique that really works! Screenreader Visibility - Bob Easton (2003) Easton attributed both Choan Gálvez and Dave Shea for their contributions. In same the thread, Gez Lemon proposed to ensure that text doesn’t bleed into the display area . Following up, Becky Gibson shared a test case covering the ideas. Lemon later published an article “Invisible Form Prompts” about the WCAG plans which attracted plenty of commenters including Bob Easton. The resulting WCAG draft guideline discussed both the and ideas. Note that instead of using the nosize style described above, you could instead use postion:absolute; and left:-200px; to position the label “offscreen”. This technique works with the screen readers as well. Only position elements offscreen in the top or left direction, if you put an item off to the right or the bottom, many browsers will add scroll bars to allow the user to reach the content. Creating Invisible labels for form elements Two options were known and considered towards the end of 2004. Why not both? Indeed, it appears Paul Bohman on the WebAIM mailing list suggested such a combination in February 2004. Bohman even discovered possibly the first zero width bug. I originally recommended setting the height and width to 0 pixels. This works with JAWS and Home Page Reader. However, this does not work with Window Eyes. If you set the height and width to 1 pixel, then the technique works with all browsers and all three of the screen readers I tested. Re: Hiding text using CSS - Paul Bohman Later in May 2004, Bohman along with Shane Anderson published a paper on this technique. Citations within included Bob Easton and Tom Gilder . Aside note: other zero width bugs have been discovered since. Manuel Matuzović noted in 2023 that links in Safari were not focusable . The zero width story continues as recently as February 2026 (last week). In browse mode in web browsers, NVDA no longer treats controls with 0 width or height as invisible. This may make it possible to access previously inaccessible “screen reader only” content on some websites. NVDA 2026.1 Beta TWO now available - NV Access News Digger further into WebAIM’s email archive uncovered a 2003 thread in which Tom Gilder shared a class for skip navigation links . I found Gilder’s blog in the web archives introducing this technique. I thought I’d put down my “skip navigation” link method down in proper writing as people seem to like it (and it gives me something to write about!). Try moving through the links on this page using the keyboard - the first link should magically appear from thin air and allow you to quickly jump to the blog tools, which modern/visual/graphical/CSS-enabled browsers (someone really needs to come up with an acronym for that) should display to the left of the content. Skip-a-dee-doo-dah - Tom Gilder Gilder’s post links to a Dave Shea post which in turn mentions the 2002 book “Building Accessible Websites” by Joe Clark . Chapter eight discusses the necessity of a “skip navigation” link due to table-based layout but advises: Keep them visible! Well-intentioned developers who already use page anchors to skip navigation will go to the trouble to set the anchor text in the tiniest possible font in the same colour as the background, rendering it invisible to graphical browsers (unless you happen to pass the mouse over it and notice the cursor shape change). Building Accessible Websites - 08. Navigation - Joe Clark Clark expressed frustration over common tricks like the invisible pixel. It’s clear no class existed when this was written. Choan Gálvez informed me that Eric Meyer would have the css-discuss mailing list. Eric kindly searched the backups but didn’t find any earlier discussion. However, Eric did find a thread on the W3C mailing list from 1999 in which Ian Jacobs (IBM) discusses the accessibility of “skip navigation” links. The desire to visually hide “skip navigation” links was likely the main precursor to the early techniques. In fact, Bob Easton said as much: As we move from tag soup to CSS governed design, we throw out the layout tables and we throw out the spacer images. Great! It feels wonderful to do that kind of house cleaning. So, what do we do with those “skip navigation” links that used to be attached to the invisible spacer images? Screenreader Visibility - Bob Easton (2003) I had originally missed that in my excitement seeing the class. I reckon we’ve reached the source of the class. At least conceptually. Technically, the class emerged from several ideas, rather than a “eureka” moment. Perhaps more can be gleaned from other CSS techniques such a the desire to improve accessibility of CSS image replacement . Bob Easton retired in 2008 after a 40 year career at IBM. I reached out to Bob who was surprised to learn this technique was still a topic today † . Bob emphasised the fact that it was always a clumsy workaround and something CSS probably wasn’t intended to accommodate . I’ll share more of Bob’s thoughts later. † I might have overdone the enthusiasm Let’s take an intermission! My contact page is where you can send corrections by the way :) The class stabilised for a period. Visit 2006 in the Wayback Machine to see WebAIM’s guide to invisible content — Paul Bohman’s version is still recommended. Moving forward to 2011, I found Jonathan Snook discussing the “clip method”. Snook leads us to Drupal developer Jeff Burnz the previous year. […] we still have the big problem of the page “jump” issue if this is applied to a focusable element, such as a link, like skip navigation links. WebAim and a few others endorse using the LEFT property instead of TOP, but this no go for Drupal because of major pain-in-the-butt issues with RTL. In early May 2010 I was getting pretty frustrated with this issue so I pulled out a big HTML reference and started scanning through it for any, and I mean ANY property I might have overlooked that could possible be used to solve this thorny issue. It was then I recalled using clip on a recent project so I looked up its values and yes, it can have 0 as a value. Using CSS clip as an Accessible Method of Hiding Content - Jeff Burnz It would seem Burnz discovered the technique independently and was probably the first to write about it. Burnz also notes a right-to-left (RTL) issue. This could explain why pushing content off-screen fell out of fashion. 2010 also saw the arrival of HTML5 Boilerplate along with issue #194 in which Jonathan Neal plays a key role in the discussion and comments: If we want to correct for every seemingly-reasonable possibility of overflow in every browser then we may want to consider [code below] This was their final decision. I’ve removed for clarity. This is very close to what we have now, no surprise since HTML5 Boilterplate was extremely popular. I’m leaning to conclude that the additional properties are really just there for the “possibility” of pixels escaping containment as much as fixing any identified problem. Thierry Koblentz covered the state of affairs in 2012 noting that: Webkit, Opera and to some extent IE do not play ball with [clip] . Koblentz prophesies: I wrote the declarations in the previous rule in a particular order because if one day clip works as everyone would expect, then we could drop all declarations after clip, and go back to the original Clip your hidden content for better accessibility - Thierry Koblentz Sound familiar? With those browsers obsolete, and if behaves itself, can the other properties be removed? Well we have 14 years of new bugs features to consider first. In 2016, J. Renée Beach published: Beware smushed off-screen accessible text . This appears to be the origin of (as demonstrated by Vispero .) Over a few sessions, Matt mentioned that the string of text “Show more reactions” was being smushed together and read as “Showmorereactions”. Beach’s class did not include the kitchen sink. The addition of became standard alongside everything else. Aside note: the origin of remains elusive. One Bootstrap issue shows it was rediscovered in 2018 to fix a browser bug. However, another HTML5 Boilterplate issue dated 2017 suggests negative margin broke reading order. Josh Comeau shared a React component in 2024 without margin. One of many examples showing that it has come in and out of fashion. We started with WCAG so let’s end there. The latest WCAG technique for “Using CSS to hide a portion of the link text” provides the following code. Circa 2020 the property was added as browser support increased and became deprecated. An obvious change I not sure warrants investigation (although someone had to be first!) That brings us back to what we have today. Are you still with me? As we’ve seen, many of the properties were thrown in for good measure. They exist to ensure absolutely no pixels are painted. They were adapted over the years to avoid various bugs, quirks, and edge cases. How many such decisions are now irrelevant? This is a classic Chesterton’s Fence scenario. Do not remove a fence until you know why it was put up in the first place. Well we kinda know why but the specifics are practically folklore at this point. Despite all that research, can we say for sure if any “why” is still relevant? Back to Ana Tudor’s suggestion. How do we know for sure? The only way is extensive testing. Unfortunately, I have neither the time nor skill to perform that adequately here. There is at least one concern with the code above, Curtis Wilcox noted that in Safari the focus ring behaves differently. Other minimum viable ideas have been presented before. Scott O’Hara proposed a different two-liner using . JAWS, Narrator, NVDA with Edge all seem to behave just fine. As do Firefox with JAWS and NVDA, and Safari on macOS with VoiceOver. Seems also fine with iOS VO+Safari and Android TalkBack with Firefox or Chrome. In none of these cases do we get the odd focus rings that have occurred with other visually hidden styles, as the content is scaled down to zero. Also because not hacked into a 1px by 1px box, there’s no text wrapping occurring, so no need to fix that issue. transform scale(0) to visually hide content - Scott O’Hara Sounds promising! It turns out Katrin Kampfrath had explored both minimum viable classes a couple of years ago, testing them against the traditional class. I am missing the experience and moreover actual user feedback, however, i prefer the screen reader read cursor to stay roughly in the document flow. There are screen reader users who can see. I suppose, a jumping read cursor is a bit like a shifting layout. Exploring the visually-hidden css - Katrin Kampfrath Kampfrath’s limited testing found the read cursor size differs for each class. The technique was favoured but caution is given. A few more years ago, Kitty Giraudel tested several ideas concluding that was still the most accessible for specific text use. This technique should only be used to mask text. In other words, there shouldn’t be any focusable element inside the hidden element. This could lead to annoying behaviours, like scrolling to an invisible element. Hiding content responsibly - Kitty Giraudel Zell Liew proposed a different idea in 2019. Many developers voiced their opinions, concerns, and experiments over at Twitter. I wanted to share with you what I consolidated and learned. A new (and easy) way to hide content accessibly - Zell Liew Liew’s idea was unfortunately torn asunder. Although there are cases like inclusively hiding checkboxes where near-zero opacity is more accessible. I’ve started to go back in time again! I’m also starting to question whether this class is a good idea. Unless we are capable and prepared to thoroughly test across every combination of browser and assistive technology — and keep that information updated — it’s impossible to recommend anything. This is impossible for developers! Why can’t browser vendors solve this natively? Once you’ve written 3000 words on a twenty year old CSS hack you start to question why it hasn’t been baked into web standards by now. Ben Myers wrote “The Web Needs a Native .visually-hidden” proposing ideas from HTML attributes to CSS properties. Scott O’Hara responded noting larger accessibility issues that are not so easily handled. O’Hara concludes: Introducing a native mechanism to save developers the trouble of having to use a wildly available CSS ruleset doesn’t solve any of those underlying issues. It just further pushes them under the rug. Visually hidden content is a hack that needs to be resolved, not enshrined - Scott O’Hara Sara Soueidan had floated the topic to the CSS working group back in 2016. Soueidan closed the issue in 2025, coming to a similar conclusion. I’ve been teaching accessibility for a little less than a decade now and if there’s one thing I learned is that developers will resort to using utility to do things that are more often than not just bad design decisions. Yes, there are valid and important use cases. But I agree with all of @scottaohara’s points, and most importantly I agree that we need to fix the underlying issues instead of standardizing a technique that is guaranteed to be overused and misused even more once it gets easier to use. csswg-drafts comment - Sara Soueidan Adrian Roselli has a blog post listing priorities for assigning an accessible name to a control. Like O’Hara and Soueidan, Roselli recognises there is no silver bullet. Hidden text is also used too casually to provide information for just screen reader users, creating overly-verbose content . For sighted screen reader users , it can be a frustrating experience to not be able to find what the screen reader is speaking, potentially causing the user to get lost on the page while visually hunting for it. My Priority of Methods for Labeling a Control - Adrian Roselli In short, many believe that a native visually-hidden would do more harm than good. The use-cases are far more nuanced and context sensitive than developers realise. It’s often a half-fix for a problem that can be avoided with better design. I’m torn on whether I agree that it’s ultimately a bad idea. A native version would give software an opportunity to understand the developer’s intent and define how “visually hidden” works in practice. It would be a pragmatic addition. The technique has persisted for over two decades and is still mentioned by WCAG. Yet it remains hacks upon hacks! How has it survived for so long? Is that a failure of developers, or a failure of the web platform? The web is overrun with inaccessible div soup . That is inexcusable. For the rest of us who care about accessibility — who try our best — I can’t help but feel the web platform has let us down. We shouldn’t be perilously navigating code hacks, conflicting advice, and half-supported standards. We need more energy money dedicated to accessibility. Not all problems can be solved with money. But what of the thousands of unpaid hours, whether volunteered or solicited, from those seeking to improve the web? I risk spiralling into a rant about browser vendors’ financial incentives, so let’s wrap up! I’ll end by quoting Bob Easton from our email conversation: From my early days in web development, I came to the belief that semantic HTML, combined with faultless keyboard navigation were the essentials for blind users. Experience with screen reader users bears that out. Where they might occasionally get tripped up is due to developers who are more interested in appearance than good structural practices. The use cases for hidden content are very few, such as hidden information about where a search field is, when an appearance-centric developer decided to present a search field with no visual label, just a cute unlabeled image of a magnifying glass. […] The people promoting hidden information are either deficient in using good structural practices, or not experienced with tools used by people they want to help. Bob ended with: You can’t go wrong with well crafted, semantically accurate structure. Ain’t that the truth. Thanks for reading! Follow me on Mastodon and Bluesky . Subscribe to my Blog and Notes or Combined feeds. Accessibility notice Class walkthrough Where it all began Further adaptations Minimum viable technique Native visually-hidden Zero dimensions Position off-screen

6 views
David Bushell 1 weeks ago

Web font choice and loading strategy

When I rebuilt my website I took great care to optimise fonts for both performance and aesthetics. Fonts account for around 50% of my website (bytes downloaded on an empty cache). I designed and set a performance budget around my font usage. I use three distinct font families and three different methods to load them. Web fonts are usually defined by the CSS rule. The property allows us some control over how fonts are loaded. The value has become somewhat of a best practice — at least the most common default. The CSS spec says: Gives the font face an extremely small block period (100ms or less is recommended in most cases) and an infinite swap period . In other words, the browser draws the text immediately with a fallback if the font face isn’t loaded, but swaps the font face in as soon as it loads. CSS Fonts Module Level 4 - W3C That small “block period”, if implemented by the browser, renders an invisible font temporarily to minimise FOUC . Personally I default to and don’t change unless there are noticeable or measurable issues. Most of the time you’ll use swap. If you don’t know which option to use, go with swap. It allows you to use custom fonts and tip your hand to accessibility. font-display for the Masses - Jeremy Wagner Google Fonts’ default to which has performance gains. In effect, this makes the font files themselves asynchronous—the browser immediately displays our fallback text before swapping to the web font whenever it arrives. This means we’re not going to leave users looking at any invisible text (FOIT), which makes for both a faster and more pleasant experience. Speed Up Google Fonts - Harry Roberts Harry further notes that a suitable fallback is important, as I’ll discover below. My three fonts in order of importance are: Ahkio for headings. Its soft brush stroke style has a unique hand-drawn quality that remains open and legible. As of writing, I load three Ahkio weights at a combined 150 KB. That is outright greed! Ahkio is core to my brand so it takes priority in my performance budget (and financial budget, for that matter!) Testing revealed the 100ms † block period was not enough to avoid FOUC, despite optimisation techniques like preload . Ahkio’s design is more condensed so any fallback can wrap headings over additional lines. This adds significant layout shift. † Chrome blog mention a zero second block period . Firefox has a config preference default of 100ms. My solution was to use instead of which extends the block period from a recommended 0–100ms up to a much longer 3000ms. Gives the font face a short block period (3s is recommended in most cases) and an infinite swap period . In other words, the browser draws “invisible” text at first if it’s not loaded, but swaps the font face in as soon as it loads. CSS Fonts Module Level 4 - W3C This change was enough to avoid ugly FOUC under most conditions. Worst case scenario is three seconds of invisible headings. With my website’s core web vitals a “slow 4G” network can beat that by half. For my audience an extended block period is an acceptable trade-off. Hosting on an edge CDN with good cache headers helps minimised the cost. Update: Richard Rutter suggested which gives more fallback control than I knew. I shall experiment and report back! Atkinson Hyperlegible Next for body copy. It’s classed as a grotesque sans-serif with interesting quirks such as a serif on the lowercase ‘i’. I chose this font for both its accessible design and technical implementation as a variable font . One file at 78 KB provides both weight and italic variable axes. This allows me to give links a subtle weight boost. For italics I just go full-lean. I currently load Atkinson Hyperlegible with out of habit but I’m strongly considering why I don’t use . Gives the font face an extremely small block period (100ms or less is recommended in most cases) and a short swap period (3s is recommended in most cases). In other words, the font face is rendered with a fallback at first if it’s not loaded, but it’s swapped in as soon as it loads. However, if too much time passes, the fallback will be used for the rest of the page’s lifetime instead. CSS Fonts Module Level 4 - W3C The browser can give up and presumably stop downloading the font. The spec actually says that and “[must/should] only be used for small pieces of text.” Although it notes that most browsers implement the default with similar strategies to . 0xProto for code snippets. If my use of Ahkio was greedy, this is gluttonous! A default would be acceptable. My justification is that controlling presentation of code on a web development site is reasonable. 0xProto is designed for legibility with a personality that compliments my design. I don’t specify 0xProto with the CSS rule. Instead I use the JavaScript font loading API to conditionally load when a element is present. Note the name change because some browsers aren’t happy with a numeric first character. Not shown is the event wrapper around this code. I also load the script with both and attributes. This tells the browser the script is non-critical and avoids render blocking. I could probably defer loading even later without readers noticing the font pop in. Update: for clarity, browsers will conditionally load but JavaScript can purposefully delay the loading further to avoid fighting for bandwidth. When JavaScript is not available the system default is fine. There we have it, three fonts, three strategies, and a few open questions and decisions to make. Those may be answered when CrUX data catches up. My new website is a little chunkier than before but its well within reasonable limits. I’ll monitor performance and keep turning the dials. Web performance is about priorities . In isolation it’s impossible to say exactly how an individual asset should be loaded. There are upper limits, of course. How do you load a one megabyte font? You don’t. Unless you’re a font studio providing a complete type specimen. But even then you could split the font and progressive load different unicode ranges. I wonder if anyone does that? Anyway I’m rambling now, bye. Thanks for reading! Follow me on Mastodon and Bluesky . Subscribe to my Blog and Notes or Combined feeds.

0 views
Jim Nielsen 2 weeks ago

Unresponsive Buttons on My Fastest Hardware Ever

This is one of those small things that drives me nuts. Why? I don’t know. I think it has something to do with the fact that I have a computer that is faster than any computer I’ve ever used in my entire life — and yet, clicking on buttons results in slight but perceptible delays. Let me explain. Imagine a button that looks like this: For SPA apps, when the user clicks that button it takes a split second (even on a fast connection) for anything to happen because: When clicking on that button, even on a fast connection, my brain glitches for a second, my thought process going something like: Granted those thoughts occur in my brain in under a second, but I hate that pause of indetermination. I clicked, I want (perceptibly) instant feedback. If something is happening, tell me! For SPA apps, you could put some state in there, like: This would provide more immediate feedback. But it also raises a whole set of other questions: Oh boy, this is getting complicated isn’t it? This is why, I assume, lots of apps just don’t deal with it. They accept there will be a slight delay in the responsiveness of the UI (and that it might error, but the user can just click again) and justify that it’s really not that big of a deal if there’s a slight, almost imperceptible delay between clicking a button and seeing the UI respond. “We’ve got bigger fish to fry.” And it makes sense. I mean, a slight delay in UI responsiveness, is that why people will or won’t buy your thing? Seems like a small detail. Who’s got the time to spend on details like this?Who cares? I care. That’s why I’m writing this post. To my original point, every piece of hardware I currently own is the fastest version of that device I’ve ever had in my life. And yet, everywhere I go I encounter lag. Lag everywhere. And I’m grumpy about it, hence this post. Reply via: Email · Mastodon · Bluesky The browser makes a request to the server The server talks to Stripe to get a session The server responds with the session data to the client The client redirects [nothing happens] I think “Did that work?” Just as I’m about to click again, I see the URL bar change I think, “Oh, ok, it’s doing something .” I stop myself from clicking again while I wait for the UI to redraw Is that actually the interaction you want, where the text changes? That’s probably gonna shift layout. Maybe you want something different, like a spinner in place of the text. How do you handle that? What if you have multiple places to upgrade? Do you have to implement state in all those places too? What if the trigger in each place is slightly different? A button here, some text there, and icon over yonder? How do you handle all of those different interactions in a standard, immediate way? Errors. What if it fails? Well, we already weren’t handling that in the first code example were we? But maybe we should…

0 views
Jim Nielsen 4 weeks ago

The Browser’s Little White Lies

So I’m making a thing and I want it to be styled different if the link’s been visited. Rather than build something myself in JavaScript, I figure I’ll just hook into the browser’s mechanism for tracking if a link’s been visited (a sensible approach, if I do say so myself ). Why write JavaScript when a little CSS will do? So I craft this: But it doesn’t work. is relatively new, and I’ve been known to muff it, so it’s probably just a syntax issue. I start researching. Wouldn’t you know it? We can’t have nice things. doesn’t always work like you’d expect because we (not me, mind you) exploited it. Here’s MDN : You can style visited links, but there are limits to which styles you can use. While is not mentioned specifically, other tricks like sibling selectors are: When using a sibling selector, such as , the adjacent element ( in this example) is styled as though the link were unvisited. Why? You guessed it. Security and privacy reasons. If it were not so, somebody could come along with a little JavaScript and uncover a user’s browsing history (imagine, for example, setting styles for visited and unvisited links, then using and checking style computations). MDN says browsers tell little white lies: To preserve users' privacy, browsers lie to web applications under certain circumstances So, from what I can tell, when I write the browser is telling the engine that handles styling that all items have never been (even if they have been). So where does that leave me? Now I will abandon CSS and go use JavaScript for something only JavaScript can do. That’s a good reason for JS. Reply via: Email · Mastodon · Bluesky

0 views
Andy Bell 1 months ago

Fun shadow coding challenge

Timehop just reminded me of this prototype I made 8 years ago . Scroll it and you’ll see that the central light source (orange line) affects the shadow. CSS has come on a lot in those 8 years, so could you do better? Would love to see it, so let me know if you do .

0 views
Chris Coyier 1 months ago

Tubes

Me, Stacey, and Miriam kicking it talkabout about CSS Scope & Mixins: Daniel and I chattin’ about playing the long game

0 views
Cassidy Williams 1 months ago

Making interesting borders with CSS corner-shape

I stumbled upon the CSS property recently and it’s pretty cool. It allows you to specify the shape of an element’s corners and works with . I threw together this demo to show it in action: See the Pen corner-shape beveled and rounded navbar by Cassidy ( @cassidoo ) on CodePen . I remember in the olden days (as in like… 10 years ago, as evidenced by this old Pen of mine ), making beveled corners involved a bunch of pseudo-elements and triangles to fake a bevel. It’s so so cool that you can “just” do this now with a couple lines of CSS! In addition to beveled corners, it also lets you do scoops, notches, and even squircles: See the Pen Rounded corners vs. Squircle by Cassidy ( @cassidoo ) on CodePen . There’s also a function, which defines any of the values you can pass to numerically, which could make for some interesting animations and transitions. Definitely play with it if you get a chance! I think there’s some cool CSS “drawing” things you can do as you dabble with the different shapes. does have fairly limited availability currently (Safari and Firefox don’t support it yet at the time of writing), but hopefully that’ll change soon!

0 views
Jim Nielsen 1 months ago

CTA Hierarchy in the Wild

The other day I was browsing YouTube — as one does — and I clicked a link in the video description to a book. I was then subjected to a man-in-the-middle attack, where YouTube put themselves in the middle of me and the link I had clicked: Hyperlinks are subversive . Big Tech must protect themselves and their interests. But link hijacking isn’t why I’m writing this post. What struck me was the ordering and visual emphasis of the “call to action” (CTA) buttons. I almost clicked “Back to YouTube”, which was precisely the action I didn’t want. I paused and laughed to myself. Look how the design pattern for primary/secondary user interface controls has inverted over time: It seems like everywhere I go, software is increasingly designed against me. Reply via: Email · Mastodon · Bluesky Classic software: Primary CTA: what’s best for you Secondary CTA: an alternative for you Modern software: Primary CTA: what’s best for us Secondary CTA: what’s acceptable to us

0 views

Easy (Horizontal Scrollbar) Fixes for Your Blog CSS

Read on the website: There are narrow screen CSS problems I often email people because of. These three fixes should be enough for most.

0 views
Cassidy Williams 2 months ago

Pausing a CSS animation with getAnimations()

It’s Blogvent, day 9, where I blog daily in December! CSS animations are cool, but sometimes you want them to just cool it . You can pause them by using the method ! When you call on an element, you get an array of all of the objects on said element, which includes CSS animations. There’s various things you can do with the returned object, like getting the of the animation’s timeline, or the playback state of the animation ( ), or in our case, actually pausing the animation with . We could loop through every Animation object in that array and pause it, like so: Or, if you just want one animation to pause, you can filter from the returned results. Here’s a real demo where there’s only one animation happening, so we pause it based on the current . See the Pen getAnimations() demo by Cassidy ( @cassidoo ) on CodePen . Hope this was helpful!

0 views
iDiallo 2 months ago

Let users zoom in on mobile devices

This is a bit of a rant. Maybe my eyes are not as good as they used to be. When I read an article that has pictures on them, I like to zoom in to see the details. You might think this makes no sense, I just have to pinch the screen to zoom in. You would be right, but some websites intentionally prevent you from zooming in. Here is an example, the straw that broke the camel's back so to speak. I was reading an interesting article on substack about kids who ran away in the 60s , and it has these pictures of letters from those kids. Handwritten letters that complement the story and I really wanted to read. But have you tried reading text from a picture in an article on a phone? Again, it could just be what happens when you spend 35 years in front of screens. CSS alone is not enough to properly turn a page responsive on a mobile device. The browser needs to know how we want to size the viewport properly. For that we have a viewport property that gives the browser a hint on how to size the page. Since we've started making pages responsive yesteryear, I've relied on a single configuration and have rarely ever found a reason to change it: The is set to the current device's width, mobile or desktop, it doesn't matter. The is set to 1. The documentation is a bit confusing, I consider the scale to just be the initial zoom level. That's really all you need to know about the viewport if you are building a webpage and want to make it display properly on a mobile device. But of course, the article I'm complaining about has different settings. Here is what they have: The properties I'm complaining about are and . The first one says users can't zoom in period. Why would you prevent users from zooming in? This is such a terrible setting that you can set your browser to ignore this setting. But for good measures, they added , which means even if you are allowed to zoom, the maximum zoom level is one... which means you can't zoom. Yes, I disabled zoom to make a point It's a terrible experience all the way around. When I read articles that have pictures, I can't zoom in! I can't properly look at the pictures. There are a few platforms that I've noticed have these settings. Substack and Medium are the most annoying. Now, when I know an article is from those platforms, I just ignore them. The only time you ever need to override users from zooming is if it's a web game. Other than that, it's just plain annoying.

0 views
Susam Pal 2 months ago

CSS Fizz Buzz with Ordered List

A version of my CSS Fizz Buzz that uses ordered list ( ) to reduce code. However, I don't quite like how misaligned the numbers and the words look. Correcting that would call for extra code that would cancel out the bytes saved. Read on website | #web

0 views
Alex White's Blog 2 months ago

It's Snowing!

In celebration of the snowy, wintery season I've transformed this site into a winter wonderland! Click the button at the top of any page to enable snow! Snow is opt-in so as not to cause performance issues on older hardware. Want to add snow to your own site? Just drop in the script from my Github repo for this blog. It's under . You'll also need to add a button somewhere in your layout to start the snow. The snow script uses a single Javascript file with a custom (very very barebones) particle effect. Each flake is given a downward velocity and can "drift" (randomly go left or right). The screen is bucketed to create "snow banks" so that snow can accumulate on the bottom of the display. The script will automatically create and remove an HTML Canvas element that covers the full display (with pointer events passing through).

0 views
Rob Zolkos 2 months ago

Vanilla CSS is all you need

Back in April 2024, Jason Zimdars from 37signals published a post about modern CSS patterns in Campfire . He explained how their team builds sophisticated web applications using nothing but vanilla CSS. No Sass. No PostCSS. No build tools. The post stuck with me. Over the past year and a half, 37signals has released two more products (Writebook and Fizzy) built on the same nobuild philosophy. I wanted to know if these patterns held up. Had they evolved? I cracked open the source code for Campfire, Writebook, and Fizzy and traced the evolution of their CSS architecture. What started as curiosity became genuine surprise. These are not just consistent patterns. They are improving patterns. Each release builds on the last, adopting progressively more modern CSS features while maintaining the same nobuild philosophy. These are not hobby projects. Campfire is a real-time chat application. Writebook is a publishing platform. Fizzy is a full-featured project management tool with kanban boards, drag-and-drop, and complex state management. Combined, they represent nearly 14,000 lines of CSS across 105 files. Not a single line touches a build tool. Let me be clear: there is nothing wrong with Tailwind . It is a fantastic tool that helps developers ship products faster. The utility-first approach is pragmatic, especially for teams that struggle with CSS architecture decisions. But somewhere along the way, utility-first became the only answer. CSS has evolved dramatically. The language that once required preprocessors for variables and nesting now has: 37signals looked at this landscape and made a bet: modern CSS is powerful enough. No build step required. Three products later, that bet is paying off. Open any of these three codebases and you find the same flat structure: That is it. No subdirectories. No partials. No complex import trees. One file per concept, named exactly what it does. Zero configuration. Zero build time. Zero waiting. I would love to see something like this ship with new Rails applications. A simple starting structure with , , , and already in place. I suspect many developers reach for Tailwind not because they prefer utility classes, but because vanilla CSS offers no starting point. No buckets. No conventions. Maybe CSS needs its own omakase. Jason’s original post explained OKLCH well. It is the perceptually uniform color space all three apps use. The short version: unlike RGB or HSL, OKLCH’s lightness value actually corresponds to perceived brightness. A 50% lightness blue looks as bright as a 50% lightness yellow. What is worth noting is how this foundation remains identical across all three apps: Dark mode becomes trivial: Every color that references these primitives automatically updates. No duplication. No separate dark theme file. One media query, and the entire application transforms. Fizzy takes this further with : One color in, four harmonious colors out. Change the card color via JavaScript ( ), and the entire card theme updates automatically. No class swapping. No style recalculation. Just CSS doing what CSS does best. Here is a pattern I did not expect: all three applications use units for horizontal spacing. Why characters? Because spacing should relate to content. A gap between words feels natural because it is literally the width of a character. As font size scales, spacing scales proportionally. This also makes their responsive breakpoints unexpectedly elegant: Instead of asking “is this a tablet?”, they are asking “is there room for 100 characters of content?” It is semantic. It is content-driven. It works. Let me address the elephant in the room. These applications absolutely use utility classes: The difference? These utilities are additive , not foundational. The core styling lives in semantic component classes. Utilities handle the exceptions: the one-off layout adjustment, the conditional visibility toggle. Compare to a typical Tailwind component: And the 37signals equivalent: Yes, it is more CSS. But consider what you gain: If there is one CSS feature that changes everything, it is . For decades, you needed JavaScript to style parents based on children. No more. Writebook uses it for a sidebar toggle with no JavaScript: Fizzy uses it for kanban column layouts: Campfire uses it for intelligent button styling: This is CSS doing what you used to need JavaScript for. State management. Conditional rendering. Parent selection. All declarative. All in stylesheets. What fascinated me most was watching the architecture evolve across releases. Campfire (first release) established the foundation: Writebook (second release) added modern capabilities: Fizzy (third release) went all-in on modern CSS: You can see a team learning, experimenting, and shipping progressively more sophisticated CSS with each product. By Fizzy, they are using features many developers do not even know exist. CSS Layers solve the specificity wars that have plagued CSS since the beginning. It does not matter what order your files load. It does not matter how many classes you chain. Layers determine the winner, period. One technique appears in all three applications that deserves special attention. Their loading spinners use no images, no SVGs, no JavaScript. Just CSS masks. Here is the actual implementation from Fizzy’s : The keyframes live in a separate file: Three dots, bouncing in sequence: The means it automatically inherits the text color. Works in any context, any theme, any color scheme. Zero additional assets. Pure CSS creativity. The default browser element renders as a yellow highlighter. It works, but it is not particularly elegant. Fizzy takes a different approach for search result highlighting: drawing a hand-drawn circle around matched terms. Here is the implementation from : The HTML structure is . The empty exists solely to provide two pseudo-elements ( and ) that draw the left and right halves of the circle. The technique uses asymmetric border-radius values to create an organic, hand-drawn appearance. The makes the circle semi-transparent against the background, switching to in dark mode for proper blending. Search results for: webhook No images. No SVGs. Just borders and border-radius creating the illusion of a hand-drawn circle. Fizzy and Writebook both animate HTML elements. This was notoriously difficult before. The secret is . Here is the actual implementation from Fizzy’s : The variable is defined globally as . Open Dialog This dialog animates in and out using pure CSS. The rule defines where the animation starts from when an element appears. Combined with , you can now transition between and . The modal smoothly scales and fades in. The backdrop fades independently. No JavaScript animation libraries. No manually toggling classes. The browser handles it. I am not suggesting you abandon your build tools tomorrow. But I am suggesting you reconsider your assumptions. You might not need Sass or PostCSS. Native CSS has variables, nesting, and . The features that needed polyfills are now baseline across browsers. You might not need Tailwind for every project. Especially if your team understands CSS well enough to build a small design system. While the industry sprints toward increasingly complex toolchains, 37signals is walking calmly in the other direction. Is this approach right for everyone? No. Large teams with varying CSS skill levels might benefit from Tailwind’s guardrails. But for many projects, their approach is a reminder that simpler can be better. Thanks to Jason Zimdars and the 37signals team for sharing their approach openly. All code examples in this post are taken from the Campfire, Writebook, and Fizzy source code. For Jason’s original deep-dive into Campfire’s CSS patterns, see Modern CSS Patterns and Techniques in Campfire . If you want to learn modern CSS, these three codebases are an exceptional classroom. Native custom properties (variables) Native nesting Container queries The selector (finally, a parent selector) CSS Layers for managing specificity for dynamic color manipulation , , for responsive sizing without media queries HTML stays readable. tells you what something is, not how it looks. Changes cascade. Update once, every button updates. Variants compose. Add without redefining every property. Media queries live with components. Dark mode, hover states, and responsive behavior are co-located with the component they affect. OKLCH colors Custom properties for everything Character-based spacing Flat file organization View Transitions API for smooth page changes Container queries for component-level responsiveness for entrance animations CSS Layers ( ) for managing specificity for dynamic color derivation Complex chains replacing JavaScript state

0 views
Josh Comeau 3 months ago

Brand New Layouts with CSS Subgrid

Subgrid allows us to extend a grid template down through the DOM tree, so that deeply-nested elements can participate in the same grid layout. At first glance, I thought this would be a helpful convenience, but it turns out that it’s so much more. Subgrid unlocks exciting new layout possibilities, stuff we couldn’t do until now. ✨

0 views
Jim Nielsen 3 months ago

Leveraging a Web Component For Comparing iOS and macOS Icons

Whenever Apple does a visual refresh in their OS updates, a new wave of icon archiving starts for me. Now that “Liquid Glass” is out, I’ve begun nabbing the latest icons from Apple and other apps and adding them to my gallery. Since I’ve been collecting these icons for so long, one of the more interesting and emerging attributes of my collection is the visual differences in individual app icons over time. For example: what are the differences between the icons I have in my collection for Duolingo? Well, I have a page for that today . That’ll let you see all the different versions I’ve collected for Duolingo — not exhaustive, I’m sure, but still interesting — as well as their different sizes . But what if you want to analyze their differences pixel-by-pixel? Turns out, There’s A Web Component For That™️. Image Compare is exactly what I was envisioning: “A tiny, zero-dependency web component for comparing two images using a slider” from the very fine folks at Cloud Four . It’s super easy to use: some HTML and a link to a script (hosted if you like, or you can vendor it ), e.g. And just like that, boom, I’ve got a widget for comparing two icons. For Duolingo specifically, I have a long history of icons archived in my gallery and they’re all available under the route for your viewing and comparison pleasure . Wanna see some more examples besides Duolingo? Check out the ones for GarageBand , Instagram , and Highlights for starters. Or, just look at the list of iOS apps and find the ones that are interesting to you (or if you’re a fan of macOS icons, check these ones out ). I kinda love how easy it was for my thought process to go from idea to reality: And I’ve written the post, so this chunk of work is now done. Reply via: Email · Mastodon · Bluesky “It would be cool to compare differences in icons by overlaying them…“ “Image diff tools do this, I bet I could find a good one…“ “Hey, Cloud Four makes a web component for this? Surely it’s good…” “Hey look, it’s just HTML: a tag linking to compiled JS along with a custom element? Easy, no build process required…“ “Done. Well that was easy. I guess the hardest part here will be writing the blog post about it.”

1 views