Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]

New here?

Welcome! BoredReading is a fresh way to read high quality articles (updated every hour). Our goal is to curate (with your help) Michelin star quality articles (stuff that's really worth reading). We currently have articles in 0 categories from architecture, history, design, technology, and more. Grab a cup of freshly brewed coffee and start reading. This is the best way to increase your attention span, grow as a person, and get a better understanding of the world (or atleast that's why we built it).

124
Well-known URLs are pretty neat. I’ve even dared propose one before here on my blog. And now I’m here to propose another: .well-known/avatar The idea is: anybody that owns a domain can put their avatar in a well-known location. I’ve already implemented this for my own site[1]. You can see it here: jim-nielsen.com/.well-known/avatar In some ways, this is really just for me. I often find myself needing an avatar of me for one reason or another. At first, I put a file in my Dropbox folder so that I could access it on any device whenever and wherever I needed it. But then I thought, “What’s even easier than accessing a file through a service like Dropbox? Accessing a URL.” And, remembering that .well-known URLs are a thing, I figured why not put it at a URL I can remember and easily share with others? Think Gravatar but on a web where your domain is your social handle[2]. I don’t know how you’d actually go about making .well-known a standard — it looks complicated. And there’s probably a...
a year 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

Background Image Opacity in CSS

The other day I was working on something where I needed to use CSS to apply multiple background images to an element, e.g. <div> My content with background images. </div> <style> div { background-image: url(image-one.jpg), url(image-two.jpg); background-position: top right, bottom left; /* etc. */ } </style> As I was tweaking the appearance of these images, I found myself wanting to control the opacity of each one. A voice in my head from circa 2012 chimed in, “Um, remember Jim, there is no background-opacity rule. Can’t be done.” Then that voice started rattling off the alternatives: You’ll have to use opacity but that will apply to the entire element, which you have text in, so that won’t work. You’ll have to create a new empty element, apply the background images there, then use opacity. Or: You can use pseudo elements (:before & :after), apply the background images to those, then use opacity. Then modern me interrupted this old guy. “I haven’t reached for background-opacity in a long time. Surely there’s a way to do this with more modern CSS?” So I started searching and found this StackOverflow answer which says you can use background-color in combination with background-blend-mode to achieve a similar effect, e.g. div { /* Use some images */ background-image: url(image-one.jpg), url(image-two.jpg); /* Turn down their 'opacity' by blending them into the background color */ background-color: rgba(255,255,255,0.6); background-blend-mode: lighten; } Worked like a charm! It probably won’t work in every scenario like a dedicated background-image-opacity might, but for my particular use case at that moment in time it was perfect! I love little moments like this where I reach to do something in CSS that was impossible back when I really cut my teeth on the language, and now there’s a one- or two-line modern solution! [Sits back and gets existential for a moment.] We all face moments like this where we have to balance leveraging hard-won expertise with seeking new knowledge and greater understanding, which requires giving up the lessons of previous experience in order to make room for incorporating new lessons of experiences. It’s hard to give up the old, but it’s the only way to make room for the new — death of the old is birth of the new. Email · Mastodon · Bluesky

12 hours ago 1 votes
Tag, You’re It

I saw these going around, but didn’t think I’d ever see myself get tagged — then Eric assuaged my FOMO. As I’ve done elsewhere talking about how I blog, I’m gonna try and impose a character limit to my answers (~240). I’m not sure if that makes my job as the writer easier or harder, but it should make your job as the reader easier. Why did you start blogging in the first place? I think I started because everything I learned about building on the web came from reading other people’s blogs online, so I wanted to be a “web person” like them. What platform are you using to manage your blog and why did you choose it? At the time of this writing (April 2025): I write in iA Writer. Code for my blog and notes is on GitHub. Deployment/hosting is via Netlify. I’ve arrived at this setup less from a combination of choice and evolution. As me and my writing evolve, my process and tools evolve too. Have you blogged on other platforms before? Blogspot, way back in the day. It’s no longer up, which is probably for the best. I was posting stuff I made from following “make this in Photoshop” tutorials. Or I’d practice trying to visually express silly puns. Or I’d make visual mashups of culture at the time. How do you write your posts? For example, in a local editing tool, or in a panel/dashboard that’s part of your blog? For a detailed history of changes on how I blog, I blog about blogging under #myBlog and I blog about microblogging under #myNotes. Read any of those posts for insights into my ever-changing process. When do you feel most inspired to write? When I read other people’s thoughts. Do you publish immediately after writing, or do you let it simmer a bit as a draft? I’m a simmerer. Rarely does a post go from thought to published in one sitting. For example, here’s a screenshot of my current simmering drafts (note my sophisticated editorial process of assigning each draft a letter prefix for sorting based on my appetite for finishing it). What are you generally interested in writing about? Stuff I make. Or stuff others make. Or thoughts I think while reading thoughts others think. I have a tags page that tries to capture what I write categorically — for example, I blog notes from books I read, and podcasts I listen to — but TBH it’s not the greatest taxonomy of my writing. Reductively: I blog about web design and development. Who are you writing for? Whoa, that question got me more introspective than I expected. Gonna move on before this becomes an existential crisis. What’s your favorite post on your blog? I used to highlight some of my favs on my home page, but I stopped. Choosing favorites is hard. My blog posts are like my kids: I love them all equally, lol. I suppose my favorite blog post is the one I’ll publish next. Any future plans for your blog? Maybe a redesign, a move to another platform, or adding a new feature? Will I redesign? Lol, the question is: when will you redesign? Tag ‘em Sorry if I mention someone who’s already been tagged: Piper Haywood — Love Piper’s mix of the personal and professional. Still have bookmarked to try grandma’s recipe. Tyler Gaw — Have loved and respected this dude since I met him at my first “real” webdev job in NYC. David Bushnell — Been enjoying David’s short- and long-form writing a lot as of late. Plus we feel the same about Deno & HTTP modules. Katie Langerman - Ah gotcha, that’s not a blog link. It’s Bluesky. But I’ve followed Katie on the socials and always enjoy her perspective. Not sure she has a personal blog, so this is a vote of confidence in her starting one :) Jan Miksovsky — Jan is doing really cool stuff with Web Origami (also just a super nice guy). Sorry, I’m not gonna ping any of these folks. If they read my blog, they’ll see their names. Otherwise, dear reader, consider it a suggestion to go subscribe to their stuff. Email · Mastodon · Bluesky

4 days ago 7 votes
Flow State and Surfing

Jack Johnson is on Rick Rubin’s podcast Tetragrammaton talking about music, film making, creativity, and surfing. At one point (~24:30) Johnson talks about his love for surfing and the beautiful flow state it puts him in: Sometimes I’ll see a friend riding a wave while I’m paddling out, and the thing I’ll see them do just seems like magic...I’ll think, “How in the world did they just do that?” And then on your next ride you’re doing the exact same thing without thinking but it’s all muscle memory and it’s all in this flow that you get into. That’s a really beautiful state to get into, to do something that feels like a magic trick, like something you shouldn’t be able to do, but all of the sudden you’re doing it. I’m not a surfer, and I can’t do effortlessly cool. But I know what a flow state feels like. Johnson’s description reminds me of that feeling when you get a little time on a personal project — riding the wave of working on your personal website. You open your laptop. You start paddling out. Maybe you see an internet friend who was doing something cool and you want to try it but you have no idea if you’ll be able to do it as well as they did. And before you know it, you’re in that flow state where muscle memory takes over and you’re doing stuff without even consciously thinking about it — stuff that others might look at and perceive as magic (cough anything on the command line cough) but it’s not magic to you. Intuition and experience just take over while you ride the wave. Ok, I’m a nerd. But I don’t care. It’s a great feeling, regardless of whether it’s playing an instrument, or surfing, or programming. That feeling of sinking into a craft you’ve worked at your whole life that you don’t have to think about anymore. Email · Mastodon · Bluesky

a week ago 12 votes
Don’t Forget the Meta Theme-Color Tag

Ever used a website where you toggle from light mode to dark mode and the web site changes but the chrome around the browser doesn’t? To illustrate, take a look at this capture of my blog on an iPhone. When you toggle the theme from light to dark, note how the website turns white but status bar stays black. Only once I refresh the page or navigate does the status bar then turn white. When the user changes the theme on my site, I want it to propagate all the way to the surrounding context of the browser. In this case, to the status bar on the iPhone. Like this: There we go! That’s what I want. So what was wrong? A popular way to indicate the active theme is to put a class on the root of the document, e.g. <html class="dark"> <style> html { background: white } html.dark { background: black } </style> </html> Then we simply add/remove the dark class when the user toggles the theme. But that will only change the in-page styles. It won’t tell the browser to update the color of whatever ambient user interface elements its drawing. For that, you’ll need the meta theme-color tag: The theme-color value for the name attribute of the <meta> element indicates a suggested color that user agents should use to customize the display of the page or of the surrounding user interface. So when you respond to the user changing their theme, don’t forget to update the <meta name='theme-color'> tag in addition to whatever you do to modify the in-page styles. That’ll give you the effect you want in the surrounding browser UI (for browsers that support it). Oh, and it’s worth pointing out: don’t forget the color-scheme property either. That’s what will tell the browser to update other in-page UI elements it draws. So, when responding to a user preference to update a website’s theme: Toggle some global attribute that triggers style changes for all your custom, in-page elements. Set the color-scheme property so the browser draws the things its responsible for correctly (form controls, scroll bars, etc.). Set the <meta name='theme-color'> value appropriately so contextual browser UI can adapt to your site’s styles. I wrote this post as a friendly reminder, because friends don’t let friends forget the meta theme-color tag. Email · Mastodon · Bluesky

a week ago 15 votes
The Value of Experience

Adam Silver has an article titled “Do you trust design advice from ChatGPT?” wherein he prompted the LLM: How do you add hint text to radio buttons? It gave various suggestions, each of which Adam breaks down. Here’s an an example response from ChatGPT: If you want the hint to appear when the user hovers on the radio button, use a tooltip for a cleaner design Adam’s response: ‘If you want’ Design is not about what you want. It’s about what users need. ‘use a tooltip’ If a hint is useful, why hide it behind a difficult-to-use and inaccessible interaction? ‘for a cleaner design’ Design is about clarity, not cleanliness. Adam’s point-by-point breakdowns are excellent. The entire article is a great example of how plausible-sounding ideas can quickly fall apart under scrutiny from an expert who reframes the issue. It’s funny how prevalent this feels in our age of fast-paced information overload. You read an argument and it seems rational — that is, if you don’t think about it too long, which who has the time? But an expert with deep experience can quickly refute these mediocre rationales and offer a more informed perspective that leaves you wondering how you ever nodded along to the original argument in the first place. Humorously, it reminds me of the culture of conspiracy theories where the burden of proof is on you to disprove the bare assertions being made (a time-consuming job). Hence the value of experience (and what’s experience but an investment of time?) to pierce through these kinds of middle-of-the-road rationales. Experience helps clarify and articulate what lesser experience cannot see, let alone articulate. That all leads me back to Adam: ChatGPT pulls unreliable, uninformed and untrustworthy design advice from the internet and delivers it with confidence. I mean you can certainly listen to its advice. But I think it’s better to develop the instinct to ask the right questions and be able to recognise bad advice when you see it. There’s no shortcut to gaining experience. You can’t consume enough content to get it. You have to do. Email · Mastodon · Bluesky

a week ago 13 votes

More in programming

1995 Was the Most Important Year for the Web

The world changed a lot in 1995. And for the web, it was a transformational year. The post 1995 Was the Most Important Year for the Web appeared first on The History of the Web.

20 hours ago 3 votes
Solving a "Layton Puzzle" with Prolog

I have a lot in the works for the this month's Logic for Programmers release. Among other things, I'm completely rewriting the chapter on Logic Programming Languages. I originally showcased the paradigm with puzzle solvers, like eight queens or four-coloring. Lots of other demos do this too! It takes creativity and insight for humans to solve them, so a program doing it feels magical. But I'm trying to write a book about practical techniques and I want everything I talk about to be useful. So in v0.9 I'll be replacing these examples with a couple of new programs that might get people thinking that Prolog could help them in their day-to-day work. On the other hand, for a newsletter, showcasing a puzzle solver is pretty cool. And recently I stumbled into this post by my friend Pablo Meier, where he solves a videogame puzzle with Prolog:1 Summary for the text-only readers: We have a test with 10 true/false questions (denoted a/b) and four student attempts. Given the scores of the first three students, we have to figure out the fourth student's score. bbababbabb = 7 baaababaaa = 5 baaabbbaba = 3 bbaaabbaaa = ??? You can see Pablo's solution here, and try it in SWI-prolog here. Pretty cool! But after way too long studying Prolog just to write this dang book chapter, I wanted to see if I could do it more elegantly than him. Code and puzzle spoilers to follow. (Normally here's where I'd link to a gentler introduction I wrote but I think this is my first time writing about Prolog online? Uh here's a Picat intro instead) The Program You can try this all online at SWISH or just jump to my final version here. :- use_module(library(dif)). % Sound inequality :- use_module(library(clpfd)). % Finite domain constraints First some imports. dif lets us write dif(A, B), which is true if A and B are not equal. clpfd lets us write A #= B + 1 to say "A is 1 more than B".2 We'll say both the student submission and the key will be lists, where each value is a or b. In Prolog, lowercase identifiers are atoms (like symbols in other languages) and identifiers that start with a capital are variables. Prolog finds values for variables that match equations (unification). The pattern matching is real real good. % ?- means query ?- L = [a,B,c], [Y|X] = [1,2|L], B + 1 #= 7. B = 6, L = [a, 6, c], X = [2, a, 6, c], Y = 1 Next, we define score/33 recursively. % The student's test score % score(student answers, answer key, score) score([], [], 0). score([A|As], [A|Ks], N) :- N #= M + 1, score(As, Ks, M). score([A|As], [K|Ks], N) :- dif(A, K), score(As, Ks, N). First key is the student's answers, second is the answer key, third is the final score. The base case is the empty test, which has score 0. Otherwise, we take the head values of each list and compare them. If they're the same, we add one to the score, otherwise we keep the same score. Notice we couldn't write if x then y else z, we instead used pattern matching to effectively express (x && y) || (!x && z). Prolog does have a conditional operator, but it prevents backtracking so what's the point??? A quick break about bidirectionality One of the coolest things about Prolog: all purely logical predicates are bidirectional. We can use score to check if our expected score is correct: ?- score([a, b, b], [b, b, b], 2). true But we can also give it answers and a key and ask it for the score: ?- score([a, b, b], [b, b, b], X). X = 2 Or we could give it a key and a score and ask "what test answers would have this score?" ?- score(X, [b, b, b], 2). X = [b, b, _A], dif(_A,b) X = [b, _A, b], dif(_A,b) X = [_A, b, b], dif(_A,b) The different value is written _A because we never told Prolog that the array can only contain a and b. We'll fix this later. Okay back to the program Now that we have a way of computing scores, we want to find a possible answer key that matches all of our observations, ie gives everybody the correct scores. key(Key) :- % Figure it out score([b, b, a, b, a, b, b, a, b, b], Key, 7), score([b, a, a, a, b, a, b, a, a, a], Key, 5), score([b, a, a, a, b, b, b, a, b, a], Key, 3). So far we haven't explicitly said that the Key length matches the student answer lengths. This is implicitly verified by score (both lists need to be empty at the same time) but it's a good idea to explicitly add length(Key, 10) as a clause of key/1. We should also explicitly say that every element of Key is either a or b.4 Now we could write a second predicate saying Key had the right 'type': keytype([]). keytype([K|Ks]) :- member(K, [a, b]), keytype(Ks). But "generating lists that match a constraint" is a thing that comes up often enough that we don't want to write a separate predicate for each constraint! So after some digging, I found a more elegant solution: maplist. Let L=[l1, l2]. Then maplist(p, L) is equivalent to the clause p(l1), p(l2). It also accepts partial predicates: maplist(p(x), L) is equivalent to p(x, l1), p(x, l2). So we could write5 contains(L, X) :- member(X, L). key(Key) :- length(Key, 10), maplist(contains([a,b]), L), % the score stuff Now, let's query for the Key: ?- key(Key) Key = [a, b, a, b, a, a, b, a, a, b] Key = [b, b, a, b, a, a, a, a, a, b] Key = [b, b, a, b, a, a, b, b, a, b] Key = [b, b, b, b, a, a, b, a, a, b] So there are actually four different keys that all explain our data. Does this mean the puzzle is broken and has multiple different answers? Nope The puzzle wasn't to find out what the answer key was, the point was to find the fourth student's score. And if we query for it, we see all four solutions give him the same score: ?- key(Key), score([b, b, a, a, a, b, b, a, a, a], Key, X). X = 6 X = 6 X = 6 X = 6 Huh! I really like it when puzzles look like they're broken, but every "alternate" solution still gives the same puzzle answer. Total program length: 15 lines of code, compared to the original's 80 lines. Suck it, Pablo. (Incidentally, you can get all of the answer at once by writing findall(X, (key(Key), score($answer-array, Key, X)), L).) I still don't like puzzles for teaching The actual examples I'm using in the book are "analyzing a version control commit graph" and "planning a sequence of infrastructure changes", which are somewhat more likely to occur at work than needing to solve a puzzle. You'll see them in the next release! I found it because he wrote Gamer Games for Lite Gamers as a response to my Gamer Games for Non-Gamers. ↩ These are better versions of the core Prolog expressions \+ (A = B) and A is B + 1, because they can defer unification. ↩ Prolog-descendants have a convention of writing the arity of the function after its name, so score/3 means "score has three parameters". I think they do this because you can overload predicates with multiple different arities. Also Joe Armstrong used Prolog for prototyping, so Erlang and Elixir follow the same convention. ↩ It still gets the right answers without this type restriction, but I had no idea it did until I checked for myself. Probably better not to rely on this! ↩ We could make this even more compact by using a lambda function. First import module yall, then write maplist([X]>>member(X, [a,b]), Key). But (1) it's not a shorter program because you replace the extra definition with an extra module import, and (2) yall is SWI-Prolog specific and not an ISO-standard prolog module. Using contains is more portable. ↩

12 hours ago 3 votes
Market Ending Moves

Startup CEOs should ask themselves what crazy ideas can turn into a move that just ends a market's competitive dynamic

13 hours ago 3 votes
Why we won't hire a junior with five years of experience

We just opened a search for a new junior programmer at 37signals. It's been years since we last hired a junior, but the real reason the listing is turning heads is because we're open about the yearly salary: $145,849*. That's high enough that programmers with lots of experience are asking whether they could apply, even if they aren't technically "junior". The answer is no. The reason we're willing to pay a junior more than most is because we're looking for a junior who's better than most. Not better in "what do they already know", but in "how far could they go". We're hiring for peak promise — and such promise only remains until it's revealed. Maybe it sounds a little harsh, but a programmer who's been working professionally for five years has likely already revealed their potential. What you're going to get is roughly what you see. That doesn't mean that people can't get better after that, but it means that the trajectory by which they improve has already been plotted. Whereas a programmer who's either straight out of school or fresh off their first internship or short-stint job is essentially all potential. So you draw their line on the basis of just a few early dots, but the line can be steep. It's not that different from something like the NFL scouting combine. Teams fight to find the promise of The Next All-Star. These rookies won't have the experience that someone who's already played in the league for years would have, but they have the potential to be the best. Someone who's already played for several seasons will have shown what they have and be weighed accordingly. This is not easy to do! Plenty of rookies, in sports and programming, may show some early potential, then fail to elevate their game to where the buyer is betting it could be. But that's the chance you take to land someone extraordinary. So if you know a junior programmer with less than three years of industry experience who is sparkling with potential, do let them know of our listing. And if you know someone awesome who's already a senior programmer, we also have an opening for them. *It's a funnily precise number because it's pulled directly from the Radford salary database, which we query for the top 10% of San Francisco salaries for junior programmers.

21 hours ago 2 votes
A Data Engineering Perspective of LLMs

Data engineering is a field I would categorize as a subspecialty of software engineering. It shares the same concerns as software engineering—scalability, maintainability, and other “-ilities”—but its primary focus is on data. It’s a unique discipline because data is inherently messy, and as a result, no standard enterprise framework has emerged to dominate the space—and […]

7 hours ago 2 votes