Sanding the interface
Jim Nielsen describes how he develops in Sanding UI (September 2024): build something, click around a ton, make tweaks, click around more, until he considers it done. The clicking is the important part. For a page transition that means going back and forth every way a person might: click and hit the back button, click and use the context menu’s Back, click and use the in-app navigation, click and use the keyboard shortcut, over and over. It is a kind of QA, he admits, but he prefers the woodworking picture: run the plank through the belt sander for the coarse stuff, then take the hand sander to a spot, run your hand over it, feel for splinters, and repeat. He sands the interface “until I finally cannot give myself any more splinters.” That is what sanding UI means.
His example is small enough to miss. A radio control and its label sat in a flexbox with gap between them, and the gap turned out to be a dead spot: clicking there did not toggle the control. Flexbox had made the visual spacing easy, and the spacing had quietly become a dead zone of interaction. He removed the gap and put padding on the label instead, which made the whole thing clickable. The moral is not the fix but the accounting: “lots of small splinters lead to an agonizing experience.” In Sanding UI, pt. II (February 2025), the splinter is a thumbs up/down widget that, clicked a lot, selects its SVG icon as if it were text, a selection that is misleading because there is no text. One fix is user-select: none. The point again is the method: clicking around a lot means the UI splinters are ones you get and fix instead of your users.
Rauno Freiberg’s Crafting the Next.js Website is a catalogue of the same kind of sanding, done at the level of individual CSS declarations. Start with focus. Consistent focus styles, he writes, are rare on the web: pick any site and tab through it and you will find rings in different colours and shapes. His best example is the Discord web app, which always uses a custom blue, matches the radius of the trigger, offsets the ring with enough padding, gives even backgroundless elements like links a radius when focused, and does not clip rings that overflow a container. For a single page that is not hard; the difficulty is covering a large surface area shared by many teams. His own focus ring is two box-shadows on :focus-visible: box-shadow: 0 0 0 2px var(--app-background), 0 0 0 4px var(--focus-color); with transition: none so the shadow is not animated. The first shadow creates padding around the element in the page background colour; the second offsets and draws a 2px outline. It assumes the element sits on the app background, which he found barely noticeable when false. He would have used outline and outline-offset, but Safari did not follow the element’s radius until 16.4, and since Safari updates are tied to macOS versions he judged it early to rely on. The companion trick for links is border-radius: 2px; padding-left: 2px; margin-left: -2px;: a slight radius, a little padding, and a negative margin that cancels it, so the focus state looks better even though the radius is invisible until focused.
Two more details from the same essay. The quote marks in testimonials are set in Georgia rather than Inter, a detail Rauno first saw on the Radix UI site, and they hang outside the text with text-indent: -0.4em (credited to Shu) so each line stays vertically aligned. And a code habit: instead of mapping variants to class names, pass the variant to a data-variant attribute and style against it. None of these is visible in a screenshot of the page. Every one of them is visible to someone tabbing, reading, or maintaining it.
Paco Coursey’s Custom text underlines (2020) is sanding applied to a single line. Because text-decoration: underline gave too little control over styling and position while the CSS Text Decoration Module was still pending, his approach was a linear-gradient background: background-size: 100% 1px at the left bottom, no repeat. Move the underline by changing the vertical value of background-position (he shows left 1.05em); thicken it by raising the background height (100% 0.25em); hide it around descenders with a text-shadow offset a little left and right in the background colour (0.1em 0 var(--background), -0.1em 0 var(--background)), remembering to set text-shadow: none in ::selection; and dash it with a repeating-linear-gradient sized 1ch, the width of the zero glyph, so the dashes align naturally. It works across multiple lines. His px vs rem in font-size (2022) is the same care turned on a habit: since nearly every site sets a hard-coded font size on html or uses px, which opts out of browser font scaling, he concludes that “when browser scaling is ignored, there’s no a11y reason to use rem over px.” The unit is not the virtue; respecting the reader’s setting is, and the next section comes back to how.
Sanding has a direction, and Jim supplies it in Good Design (2022). Design is about purpose and intent, so to ask whether something is good design you also have to ask: good for who? An unsubscribe button that dodges the cursor is good design for whoever wants to keep you subscribed and devilishly bad for you. Dribbble shots may be good design if the intent is attention and likes, but “It’s hard to know whether a static picture depicting dynamic software helps a real-world human achieve a particular goal.” Where the interests of the software’s owner and its user diverge, he recommends stating a priority of constituencies so that the good in good design has a referent.
So: decide who you are sanding for, then use the thing the way they will, repeatedly, through every entry and exit, until you stop getting splinters. Check the click target against the visual gap, the focus ring against the radius, the selection against the SVG, the underline against the descenders. Each is a small fix. The agonizing experience is made of the ones you skipped.