Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
54
One of the ways I like to do development is to build something, click around a ton, make tweaks, click around more, more tweaks, more clicks, etc., until I finally consider it done. The clicking around a ton is the important part. If it’s a page transition, that means going back and forth a ton. Click, back button. Click, right-click context menu, “Back”. Click, in-app navigation to go back (if there is one). Click, keyboard shortcut to go back. Over and over and over. You get the idea. It’s kind of a QA tactic in a sense, just click around and try to break stuff. But I like to think of it as being more akin to woodworking. You have a plank of wood and you run it through the belt sander to get all the big, coarse stuff smoothed down. Then you pull out the hand sander, sand a spot, run your hand over it, feel for splinters, sand it some more, over and over until you’re satisfied with the result. With software, the fact is that sometimes there are just too many variables to know and test...
5 months ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Jim Nielsen’s Blog

CSS Space Toggles

I’ve been working on a transition to using light-dark() function in CSS. What this boils down to is, rather than CSS that looks like this: :root { color-scheme: light; --text: #000; } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; --text: #fff; } } I now have this: :root { color-scheme: light; --text: light-dark(#000, #fff); } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; } } That probably doesn’t look that interesting. That’s what I thought when I first learned about light-dark() — “Oh hey, that’s cool, but it’s just different syntax. Six of one, half dozen of another kind of thing.” But it does unlock some interesting ways to handling themeing which I will have to cover in another post. Suffice it to say, I think I’m starting to drink the light-dark() koolaid. Anyhow, using the above pattern, I want to compose CSS variables to make a light/dark theme based on a configurable hue. Something like this: :root { color-scheme: light; /* configurable via JS */ --accent-hue: 56; /* which then cascades to other derivations */ --accent: light-dark( hsl(var(--accent-hue) 50% 100%), hsl(var(--accent-hue) 50% 0%), ); } @media (prefers-color-scheme: dark) { :root { color-scheme: dark; } } The problem is that --accent-hue value doesn’t quite look right in dark mode. It needs more contrast. I need a slightly different hue for dark mode. So my thought is: I’ll put that value in a light-dark() function. :root { --accent-hue: light-dark(56, 47); --my-color: light-dark( hsl(var(--accent-hue) 50% 100%), hsl(var(--accent-hue) 50% 0%), ); } Unfortunately, that doesn’t work. You can’t put arbitrary values in light-dark(). It only accepts color values. I asked what you could do instead and Roma Komarov told me about CSS “space toggles”. I’d never heard about these, so I looked them up. First I found Chris Coyier’s article which made me feel good because even Chris admits he didn’t fully understand them. Then Christopher Kirk-Nielsen linked me to his article which helped me understand this idea of “space toggles” even more. I ended up following the pattern Christopher mentions in his article and it works like a charm in my implementation! The gist of the code works like this: When the user hasn’t specified a theme, default to “system” which is light by default, or dark if they’re on a device that supports prefers-color-scheme. When a user explicitly sets the color theme, set an attribute on the root element to denote that. /* Default preferences when "unset" or "system" */ :root { --LIGHT: initial; --DARK: ; color-scheme: light; } @media (prefers-color-scheme: dark) { :root { --LIGHT: ; --DARK: initial; color-scheme: dark; } } /* Handle explicit user overrides */ :root[data-theme-appearance="light"] { --LIGHT: initial; --DARK: ; color-scheme: light; } :root[data-theme-appearance="dark"] { --LIGHT: ; --DARK: initial; color-scheme: dark; } /* Now set my variables */ :root { /* Set the “space toggles’ */ --accent-hue: var(--LIGHT, 56) var(--DARK, 47); /* Then use them */ --my-color: light-dark( hsl(var(--accent-hue) 50% 90%), hsl(var(--accent-hue) 50% 10%), ); } So what is the value of --accent-hue? That line sort of reads like this: If --LIGHT has a value, return 56 else if --DARK has a value, return 47 And it works like a charm! Now I can set arbitrary values for things like accent color hue, saturation, and lightness, then leverage them elsewhere. And when the color scheme or accent color change, all these values recalculate and cascade through the entire website — cool! A Note on Minification A quick tip: if you’re minifying your HTML and you’re using this space toggle trick, beware of minifying your CSS! Stuff like this: selector { --ON: ; --OFF: initial; } Could get minified to: selector{--OFF:initial} And this “space toggles trick” won’t work at all. Trust me, I learned from experience. Email · Mastodon · Bluesky

3 days ago 7 votes
Aspect Ratio Changes With CSS View Transitions

So here I am playing with CSS view transitions (again). I’ve got Dave Rupert’s post open in one tab, which serves as my recurring reference for the question, “How do you get these things to work again?” I’ve followed Dave’s instructions for transitioning the page generally and am now working on individual pieces of UI specifically. I feel like I’m 98% of the way there, I’ve just hit a small bug. It’s small. Many people might not even notice it. But I do and it’s bugging me. When I transition from one page to the next, I expect this “active page” outline to transition nicely from the old page to the new one. But it doesn’t. Not quite. Did you notice it? It’s subtle and fast, but it’s there. I have to slow my ::view-transition-old() animation timing waaaaay down to see catch it. The outline grows proportionally in width but not in height as it transitions from one element to the next. I kill myself on trying to figure out what this bug is. Dave mentions in his post how he had to use fit-content to fix some issues with container changes between pages. I don’t fully understand what he’s getting at, but I think maybe that’s where my issue is? I try sticking fit-content on different things but none of it works. I ask AI and it’s totally worthless, synthesizing disparate topics about CSS into a seemingly right on the surface but totally wrong answer. So I sit and think about it. What’s happening almost looks like some kind of screwy side effect of a transform: scale() operation. Perhaps it’s something about how default user agent styles for these things is animating the before/after state? No, that can’t be it… Honestly, I have no idea. I don’t know much about CSS view transitions, but I know enough to know that I don’t know enough to even formulate the right set of keywords for a decent question. I feel stuck. I consider reaching out on the socials for help, but at the last minute I somehow stumble on this perfectly wonderful blog post from Jake Archibald: “View transitions: Handling aspect ratio changes” and he’s got a one-line fix in my hands in seconds! The article is beautiful. It not only gives me an answer, but it provides really wonderful visuals that help describe why the problem I’m seeing is a problem in the first place. It really helps fill out my understanding of how this feature works. I absolutely love finding writing like this on the web. So now my problem is fixed — no more weirdness! If you’re playing with CSS view transitions these days, Jake’s article is a must read to help shape your understanding of how the feature works. Go give it a read. Email · Mastodon · Bluesky

5 days ago 10 votes
Search Results Without JavaScript

I’m currently looking to add a search feature to my blog. It’s a client-side approach, which means I was planning on using my favorite progressive-enhancement technique for client-side only search: you point a search form at Google, scope the results to your site, then use JavaScript to intercept the form submission and customize the experience on your site to your heart’s content. <form action="https://www.google.com/search"> <input type="text" name="q" placeholder="Search" /> <input type="hidden" name="as_sitesearch" value="blog.jim-nielsen.com" /> <button type="submit">Search</button> </form> <script> document.querySelector("form").addEventListener("submit", (e) => { e.preventDefault(); // Do my client-side search stuff here // and stay on the current page }); </script> However, then I remembered that Google Search no longer works without JavaScript which means this trick is no longer a trick. [1] But have no fear, other search engines to the rescue! DuckDuckGo, for example, supports this trick. Tweak some of the HTML from the Google example and it’ll work: <form action="https://duckduckgo.com"> <input type="text" name="q" placeholder="Search" /> <input type="hidden" name="sites" value="blog.jim-nielsen.com" /> <button type="submit">Search</button> </form> <script> document.querySelector("form").addEventListener("submit", (e) => { e.preventDefault(); // Do my client-side search stuff here // and stay on the current page }); </script> Yahoo also supports this trick, but not Bing. You can point people at Bing, but you can’t scope a query to your site only with an HTML form submission alone. Why? Because you need two search params: 1) a “query” param representing what the user typed into the search box, and 2) a “site search” param to denote which site you want to limit your results to (otherwise it’ll search the whole web). From a UI perspective, if a search box is on your site, user intent is to search the content on your site. You don’t want to require people to type “my keywords site:blog.jim-nielsen.com” when they’re using a search box on your site — that’s just silly! That’s why you need a second search parameter you can set yourself (a hidden input). You can’t concatenate something onto the end of a user’s HTML form submission. (What they type in the input box is what gets sent to the search engine as the ?q=... param.) To add to the q param, you would need JavaScript — but then that defeats the whole purpose of this exercise in the first place! Anyhow, here are the search parameters I found useful for search engines that will support this trick: DuckDuckGo: Query: q Site search param: sites Yahoo Query: p Site search param: vs I made myself a little test page for trying all these things. Check it out (and disable JS) if you want to try yourself! Not only that, but the as_sitesearch search param doesn’t seem to work anymore either. I can’t find any good documentation on what happened to as_sitesearch, but it seems like you’re supposed to use the “programmable search” now instead? Honestly I don’t know. And I don’t care enough to find out. ⏎ Email :: Mastodon :: Bluesky #progressiveEnhancement

a week ago 12 votes
The Art of Making Websites

Hidde de Vries gave a great talked titled “Creativity cannot be computed” (you can checkout the slides or watch the video). In his slides he has lots of bullet points that attempt to define what art is, and then in the talk he spends time covering each one. Here’s a sampling of the bullet points: Art isn't always easy to recognize Art has critics Art is fuzzy Art can make us think Art can make the artist think Art can make the audience think Art can show us a mirror to reflect Art can move us Art can take a stance Art can be used to show solidarity Art can help us capture what it's like to be another person I love all his bullet points. In fact, they got me thinking about websites. I think you could substitute “website” for “art” in many of his slides. For example: Art is repeated So are websites. Think of all those websites that follow the same template. Art may contain intentions Like the intent to purposely break best practices. Art can show us futures we should not want Or the present we currently have. Art doesn’t have to fit in You can make any kind of website. It gives you agency to respond to the world the way you want, not just by “liking” something on social media. Me personally, I’ve made little websites meant to convey my opinion on social share imagery or reinforce the opinion I share with others on the danger of normalizing breakage on the web. Each of those could’ve been me merely “liking” someone else’s opinion. Or I could’ve written a blog post. Or, as in those cases, I instead made a website. Art can insult the audience It doesn’t have to make you happy. Its purpose can be to offend you, or make you outraged and provoke a response. It can be a mother fucking website. Of course, as Hidde points out, a website doesn’t have to be all of these. It also doesn’t have to be any of these. Art — and a website — is as much about the artist and the audience as it is about the artifact. It’s a reflection of the person/people making it. Their intentions. Their purpose. How’d you make it? Why’d you make it? When’d you make it? Each of these threads run through your art (website). So when AI lets you make a website with the click of a button, it’s automating away a lot of the fun art stuff that goes into a website. The part where you have to wrestle with research, with your own intentions and motivations, with defining purpose, with (re)evaluating your world view. Ultimately, a website isn’t just what you ship. It’s about who you are — and who you become — on the way to shipping. So go explore who you are. Plumb the bottomless depths of self. Make art, a.k.a make a website. Email :: Mastodon :: Bluesky

a week ago 12 votes
Software Pliability

Quoting myself from former days on Twitter: Businesses have a mental model of what they do. Businesses build software to help them do it—a concrete manifestation of their mental model. A gap always exists between these two. What makes a great software business is their ability to keep that gap very small. I think this holds up. And I still think about this idea (hence this post). Software is an implementation of human understanding — people need X, so we made Y. But people change. Businesses change. So software must also change. One of your greatest strengths will be your ability to adapt and evolve your understanding of people’s needs and implement it in your software. In a sense, technical debt is the other side of this coin of change: an inability to keep up with your own metamorphosis and understanding. In a way, you could analogize this to the conundrum of rocket science: you need fuel to get to space, but the more fuel you add, the more weight you add, and the more weight you add, the more fuel you need. Ad nauseam. It’s akin to making software. You want to make great software for people’s needs today. It takes people, processes, and tools to make software, but the more people, processes, and tools you add to the machine of making software, the less agile you become. So to gain velocity you add more people, processes, and tools, which…you get the idea. Being able to build and maintain pliable software that can change and evolve at the same speed as your mental model is a superpower. Quality in code means the flexibility to change. Email :: Mastodon :: Bluesky

2 weeks ago 17 votes

More in literature

'Merely the joy of writing'

A rare and winning combination: a serious person who seldom takes himself seriously. He keeps his ego a little off to the side, muffled, away from the business at hand. It never disappears. It grows dormant, like some cases of tuberculosis. Jules Renard is such a man and writer, an aphorist and wit with the soul of a peasant. Often, he thinks like a farmer – practical, focused, unsentimental – while writing like a satirist. Here is Renard in his Journal, bargaining with fate on October 17, 1899: “Of all that we write, posterity will retain a page, at best. I would prefer to choose the page myself.”  Renard writing as a commonsensical critic, September 6, 1902: “A great poet need only employ the traditional forms. We can leave it to lesser poets to worry themselves with making reckless gestures.”   More writerly common sense, November 27, 1895: “Keep their interest! Keep their interest! Art is no excuse for boring people.”   A lesson for “cancel culture, August 1896: “We always confound the man and the artist, merely because chance has brought them together in the same body. La Fontaine wrote immoral letters to his womenfolk, which does not prevent us from admiring him. It is quite simple: Verlaine had the genius of a god, and the soul of a pig. Those who were close to him must have suffered. It was their own fault! – they made the mistake of being there.”   Renard sounding like the premise of a story by Maupassant, September 29, 1897: “Some men give the impression of having married solely to prevent their wives from marrying other men.”   On why some of us become writers, May 9, 1898: “Inspiration is perhaps merely the joy of writing: it does not precede writing.”   Renard was born on this date, February 22, in 1864 and died of arteriosclerosis in 1910 at age forty-six. With Montaigne and Proust, he is the French writer I most rely on.   [All quoted passages are from Renard’s Journal 1887-1910 (trans. Theo Cuffe, selected and introduced by Julian Barnes, riverrun, 2020).]

16 hours ago 3 votes
Meeting the Muse at the Edge of the Light: Poet Gary Snyder on Craftsmanship vs. Creative Force

It is tempting, because we make everything we make with everything we are, to take our creative potency for a personal merit. It is also tempting when we find ourselves suddenly impotent, as all artists regularly do, to blame the block on a fickle muse and rue ourselves abandoned by the gods of inspiration. The truth is somewhere in the middle: We are a channel and it does get blocked — it is not an accident that the psychological hallmark of creativity is the “flow state” — but while it matters how wide and long the channel is, how much… read article

4 hours ago 1 votes
'Even Belles Lettres Legitimate As Prayer'

In the “Prologue” to his 1962 prose collection The Dyer’s Hand, W.H. Auden borrows a conceit from Lewis Carroll and divides all writers – “except the supreme masters who transcend all systems of classification” – into Alices and Mabels. In Alice in Wonderland, the title character, pondering her identity, says “. . . I’m sure I can’t be Mabel for I know all sorts of things, and she, oh! she knows such a very little. Beside she’s she and I’m I.” The categorization recalls Sir Isaiah Berlin’s Foxes and Hedgehogs. Of course, all of humanity can also be divided into those who divide all of humanity into two categories and those who don’t.  Leading the list of Auden’s Alices is Montaigne, followed by the names of eight other writers, including Andrew Marvell, Jane Austen and Paul Valéry. Like Alice, Montaigne knew “all sorts of things” – he is among the most learned of writers -- even while asking “Que sais-je?”: “What do I know?” Montaigne begins his longest essay, “Apology for Raymond Sebond,” (1576) with these words:   “In truth, knowledge is a great and very useful quality; those who despise it give evidence enough of their stupidity. But yet I do not set its value at that extreme measure that some attribute to it, like Herillus the philosopher, who placed in it the sovereign good, and held that it was in its power to make us wise and content. That I do not believe, nor what others have said, that knowledge is the mother of all virtue, and that all vice is produced by ignorance. If that is true, it is subject to a long interpretation.”   Montaigne distills skepticism, which isn’t the same as nihilism or know-it-all-ism. It’s closer to the absence of naiveté, credulity and mental laziness, coupled with an open mind and curiosity. Montaigne was a benign skeptic and a Roman Catholic who lived through the French Wars of Religion. Auden wrote “Montaigne” in 1940, the year France fell to the Germans.   “Outside his library window he could see A gentle landscape terrified of grammar, Cities where lisping was compulsory, And provinces where it was death to stammer.   “The hefty sprawled, too tired to care: it took This donnish undersexed conservative To start a revolution and to give The Flesh its weapons to defeat the Book.   “When devils drive the reasonable wild, They strip their adult century so bare, Love must be re-grown from the sensual child,   ‘To doubt becomes a way of definition, Even belles lettres legitimate as prayer, And laziness a movement of contrition.”   “Death to stammer” is no exaggeration. In the sixteenth century, speech defects were often equated with possession by the devil. The final stanza is a writer’s credo. Auden was born on this day in 1907. He shares a birthday with my youngest son, David, who turns twenty-two today.     [The Montaigne passage is from The Complete Essays of Montaigne (trans. Donald Frame, Stanford University Press, 1957).]

yesterday 3 votes
“Muse Circe Reclaims Her Lucre”

Five new prompts The post “Muse Circe Reclaims Her Lucre” appeared first on The American Scholar.

yesterday 3 votes