Jim Nielsen’s Blog

You found my experimental HTML feed (there are also other ways to subscribe).

I HTML

Recent posts

Notes on Google Search Now Requiring JavaScript

View

John Gruber has a post about how Google’s search results now require JavaScript[1]. Why? Here’s Google:

the change is intended to “better protect” Google Search against malicious activity, such as bots and spam

Lol, the irony.

Let’s turn to JavaScript for protection, as if the entire ad-based tracking/analytics world born out of JavaScript’s capabilities isn’t precisely what led to a less secure, less private, more exploited web.

But whatever, “the web” is Google’s product so they can do what they want with it — right? Here’s John:

Old original Google was a company of and for the open web. Post 2010-or-so Google is a company that sees the web as a de facto proprietary platform that it owns and controls. Those who experience the web through Google Chrome and Google Search are on that proprietary not-closed-per-se-but-not-really-open web.

Search that requires JavaScript won’t cause the web to die. But it’s a sign of what’s to come (emphasis mine):

Requiring JavaScript for Google Search is not about the fact that 99.9 percent of humans surfing the web have JavaScript enabled in their browsers. It’s about taking advantage of that fact to tightly control client access to Google Search results. But the nature of the true open web is that the server sticks to the specs for the HTTP protocol and the HTML content format, and clients are free to interpret that as they see fit. Original, novel, clever ways to do things with website output is what made the web so thrilling, fun, useful, and amazing. This JavaScript mandate is Google’s attempt at asserting that it will only serve search results to exactly the client software that it sees fit to serve.

Requiring JavaScript is all about control.

The web was founded on the idea of open access for all. But since that’s been completely and utterly abused (see LLM training datasets) we’re gonna lose it.

The whole “freemium with ads” model that underpins the web was exploited for profit by AI at an industrial scale and that’s causing the “free and open web” to become the “paid and private web”.

Universal access is quickly becoming select access — Google search results included.


  1. If you want to go down a rabbit hole of reading more about this, there’s the TechCrunch article John cites, a Hacker News thread, and this post from a company founded on providing search APIs.

Reply via: Email :: Mastodon :: Bluesky

Tagged in: #generalNotes

Missed Connections

View

Let me tell you about one of the best feelings.

You have a problem.

You bang your head on it for a while.

Through the banging, you formulate a string of keywords describing the problem.

You put those words into a search engine.

You land on a forum or a blog post and read someone else’s words containing those keywords and more. Their words resonate with you deeply.

They’re saying the exact same things you were saying to yourself in your head.

You immediately know, “This person gets it!”

You know they have an answer to your problem. They’ve seen what you’re seeing.

And on top of it all, they provide a solution which fixes your problem!

A sense of connection is now formed. You feel validated, understood, seen. They’ve been through what you’re going through, and they wrote about it to reach out to you — across time and space.

I fell in love with the web for this reason, this feeling of connection. You could search the world and find someone who saw what you see, felt what you feel, went through what you’re going through.

Contrast that with today.

Today you have a problem.

You bang your head on it.

You ask a question in a prompt.

And you get back something.

But there’s no human behind it. Just a machine which takes human voices and de-personalizes them until the individual point of view is annihilated. And so too with it the sense of connection — the feeling of being validated, understood, seen.

Every prompt a connection that could have been. A world of missed connections.


Reply via: Email :: Mastodon :: Bluesky

HTML Minification for Static Sites

View

This is a note to my future self, as I’ve setup HTML minification on a few different projects and each time I ask myself, “How did I do that again?” So here’s your guide, future Jim (and anyone else on the internet who finds this).

I use html-minifier to minifiy HTML files created by my static site generator. Personally, I use the CLI tool because it's easy to add a CLI command as an npm postbuild step.

Example package.json:

{
  "scripts": {
    "build": "<BUILD-COMMAND>"
    "postbuild": "html-minifier --input-dir <BUILD-DIR> --output-dir <BUILD-DIR> --file-ext html <OPTIONS>"
  }
}

All the minification options are off by default, so you have to turn them on one-by-one (HTML minfication is a tricky concern). Me personally, I’m using the ones exemplified in the project README:

--collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js true

So, for a site folder named build, the entire command looks like this:

html-minifier --input-dir ./build --output-dir ./build --file-ext html --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js true

That’s it — that’s the template.

What Kind of Results Do I Get?

I use this on a few of my sites, including my notes site and this blog.

When testing it locally for my blog’s build, I:

  • Run a build and put files to ./build
  • Copy ./build to ./build-min
    • Command: cp -R build build-min
  • Run html-minifier on build-min and compare the resulting folders in macOS finder.

Here’s my results for my blog (2,501 items in ./build):

  • Directory size:
    • Before: 37MB
    • After: 28.4MB
    • Difference: ▼ -8.6MB (-23.24%)
  • Main index.html file lines of code:
    • Before: 1,484
    • After: 15 lines
    • Difference: ▼ -1,469 lines (-99%)
  • Main index.html file size over the network:
    • Before: 30.6kB
    • After: 17.6kB
    • Difference: ▼ -13kB (-42.48%)

And the results for my notes (one big index.html file):

  • File size:
    • Before: 1.5MB
    • After: 1.1MB
    • Difference: ▼ -0.4MB (-26.67%)
  • Lines of code:
    • Before: 25,974
    • After: 1
    • Difference: ▼ -25,973 lines (-99.996%)

Reply via: Email :: Mastodon :: Bluesky

Tagged in: #html

Consistency For Who? Thoughts on Overriding Basic Computing Controls

View

A note before we start: I don’t know how much of this I believe. I’m sketching out some feelings in this post and thinking through whether it actually makes any sense. I’d be curious where other folks land on this.


I’m not sure I totally understand this impulse we have on the web to override the default style and appearance of fundamental computing controls.

Everyone wants their own checkboxes, radios, and select menus that fit their brand.

But websites aren’t about you or your brand. They’re about the people you’re serving who have to use them, i.e. the users.

And their needs vary from one person to the next, based on their unique context and environment (operating system, device, etc.)

For them, a checkbox that’s visually and functionally uniform across every website is a good thing. It provides consistency and sets expectations — “Oh hey, a checkbox, I know how to use this. It looks and functions the same as a checkbox on every other website, app, or system preference on my computer.”

But where we’ve arrived on the web is consistency for brands is more important than consistency for end users.

Take Radios, For Example

Imagine a radio control in macOS. There are some design considerations in how that system-level control looks and functions that are unique to macOS.

For example, when a window loses focus in favor of another window, radio controls are de-emphasized visually because the user is now focused on something else in a different window.

Screenshot in macOS where a focused window has system blue radio controls, but an unfocused window has grayed out radio controls.

This is a unique solution for a specific computing experience where multiple windows may be on the screen at the same time and, as the user shifts focus from one window to another, additional visual help is provided to emphasize and de-emphasize the user’s focal point in the user interface.

The beauty of leveraging a system-level element is that you’re tapping into these kinds of solutions which are tailored to solve problems unique to their context and environment.

Contrast that with a radio somebody re-implemented on the web to match their brand. I highly doubt many have taken into consideration a de-emphasized state for windowed computing experiences.

Or Take Select, For Example

As another example, consider how the <select> element can break outside of the browser window because it is an OS-level control.

For example, have a list with a lot of options? A <select> element can provide users something your custom select never could: an adaptation to its environment, the operating system. If the browser window is small on screen (because, say, the user is trying to do something else within their computing environment like side-by-side windows) the <select> can break out of the browser window and accommodate more space.

Screenshot of a full Safari browser window on macOS, with the options menu for a select breaking outside the bounds of the browser window.

Similarly, though perhaps not as advantageous, on mobile devices like iOS the <select> can break outside of the browser window. Something a custom element could never do.

Screenshot of  Safari on iOS where the options for a select menu are breaking outside the browser’s viewport.

Additionally, these native controls are incredibly forward looking. If new hardware or OS appears on the scene (see visionOS), how the <select> works is handled for you. When it ships, you’re up to date (vs. a design system where now you have to go consider how, if at all, things change for your entire system and every site it supports).

Business case: there’s no more economical way to ship websites than using the platform. You get outside engineering resources to build your UIs at no cost to you! Every component you build is a liability, so what’s the least you can do to deliver value?

I get it, there are trade-offs. But when building UIs, how often do we stop to ask: What’s lost when we refuse to consider the context and environment of our users because we instead force upon them the context and environment of our brand?

Two Cents on Design Systems

We extoll the virtues of a “design system” within our brands and organizations — consistency, familiarity, uniformity, all for our users! But once they leave the walled garden of our brand, it’s ok that they suddenly lose this privilege?

If the inconsistencies across design systems for basic computing controls were within our own organizational systems, we would be enraged! But since they’re across brands (e.g. websites), it’s fine? (Below is an example of radios and checkboxes and selects across various popular design systems.)

Screenshot of radios, checkboxes, and selects across popular design systems, showing a variety of differences.

In the end, it’s the user who has to deal with these inconsistencies. But isn’t that what “systems” are meant to solve in the first place?

In other words, the default, un-styled, system-level controls for radios, switches, checkboxes, etc., are the original design system before our branded design systems overrode them.

Screenshot of form controls like radio, checkbox, and select in macOS.

Are Organizational Design Systems User-Centric?

Your organization’s design system lacks the sensibilities of your users’ platforms.

“We made our own radios! They’re great! They’re ‘on-brand’ and consistent across all our stuff.”

But they’re not consistent across all your users’ stuff.

In other words, you made a radio for your company without considering what makes a radio a radio on the computer it will be used on.

You oriented a visual and functional experience around you and your environment, rather than the person you’re serving and their context and environment.

And I just tend to think we’re losing out on something with that choice — to say nothing of its cost.

Disclaimers

Disclaimer 1: I know I’m cheating here. Not all native system controls have been standardized in a way that serves the varied needs of complex applications. But, on the other side of this coin, a simple healthcare form that would be perfectly suited to some basic radio controls and a plain <select> menu instead rolls its UI for no other reason than to make it “on-brand” and it’s worse in almost every way: visually, functionally, accessibly.

Disclaimer 2: Yeah I know, this puts us as developers at the mercy of browser vendors and OS platforms and the paltry level of access they give us to system controls. For example, it’s still not easy to mark a checkbox with an indeterminate state in HTML alone. I get that. But perhaps if we spent more time advocating for these kinds of enhancements (instead of re-theming a checkbox for the nth time) maybe we’d get what we ask for?

Disclaimer 3: In case it’s not clear, I am not advocating every website everywhere should only use form controls provided by the web platform. The web is a big place, it’s silly to make universal statements for something so big. What I’m trying to do is bring attention to the fact that maybe you don’t need to roll your own. Maybe design systems should consider the computing context and environment of their users over the context and environment of their own brand.

Disclaimer 4: I get that system-level consistency is a kind of branded consistency. If you choose an Apple product, you’re choosing an Apple-branded experience for native form controls. I realize these things are not totally brand-agnostic. But consumers make a choice when they buy a computing device, and maybe we should honor that choice rather than try overriding it.

Disclaimer 5: Having disclaimers clears me of any and all criticism lol.


Reply via: Email :: Mastodon :: Bluesky

Relationship Advice for AI

View

You know what’s really helpful in solving my own problems? Writing them down, sending them to someone, and not hearing back.

You ever do that? For me, it’s a bulletproof method to solving problems.

It’s akin to those moments when you go to someone with a problem, you talk it through, you find a solution, you thank them for their help, and they say, “Well I didn’t even say anything, but you’re welcome.”

If I have a friend, co-worker, or collaborator who I know is on the other end of a chat box, typing out my problem and not hearing back from them can be a tremendous help.

Here’s an example of how it often goes:


Jim Nielsen, Friday at 12:53 PM
I’m having an issue where the deployment isn’t working. Failiures are coming from lines 123-125 of the build script...

Jim Nielsen, Friday at 12:59 PM
Oh, it looks like something changed in commit abc123e in the lock file...

Jim Nielsen, Friday at 1:02 PM
This is so weird, I hate troubleshooting this crap. Why is everything in the world garbage?

Jim Nielsen, Friday at 1:03 PM
Ok, I can’t figure this out. I'm going to need your help when you have a second.

Jim Nielsen, Friday at 1:09 PM
Oh hey, actually I think I know what the problem is...

Jim Nielsen, Friday at 1:11 PM
Ok, it’s fixed now. Nevermind, I don’t need your help. Thanks!

Co-worker, Friday at 4:03 PM
You're welcome, glad I could help!


In contrast, AI is too eager to respond back with something when nothing would be much more helpful.

Knowing another human is there to connect with — available, listening, but not speaking — has helped me many times as I express my thinking step-by-step.

So let me give you some relationship advice, AI. Sometimes you don’t need to say or do anything. You just need to listen.

Cool? Thanks.


Reply via: Email :: Mastodon :: Bluesky

Tagged in: #ai

Tools As Ways of Being

View

I took notes from Sean Voisen’s call for more hybrid tools. He speaks for a moment on generative AI and its inclusion into existing tools, but reading between the lines the insight I found was how our tools can trigger empathy for people and disciplines:

One of the greatest goals we can have for [making] tools…is that in expanding all of our respective capabilities, we do not replace our human teammates, but rather we participate more deeply in the creative process together.

A good tool improves your output.

A great tool improves your output and your understanding and empathy for others and their disciplines.

If designing tools is designing ways of being — “we shape our tools and they shape us” — then the tools we use together are shared ways of being. They facilitate us not only getting stuff done together, but being together. And that being can bring a better understanding of each other.

I don’t think that’s too crazy to assert, especially when you look at communities that coalesce around tools, like Clojure. People love their tools, and they identify with the principles they embody and the communities that support them.

Our tools are ways of being. How important, then, that we carefully consider their design as well as proliferate their diversity.


Reply via: Email :: Mastodon :: Bluesky

Using Locally-Installed CLI Tools In Node Projects

View

You have a dependency that provides a CLI tool, how do you use it?

Even though you did npm i from your project root, if you run <tool> <command> it won’t work because that tool is not in your global path.

You could install <tool> globally, but then if you have <tool> in multiple projects and you run <tool> <command> in another project it might not be the same version of the tool.

For example, you might have tool@0.1.0 in one project and tool@0.2.0 in another, and they might have different CLI commands. So you want to run/test whatever version of tool is installed in your local project (e.g. the one in package.json), you need something other than the globally installed version.

Over time, there are three ways I’ve learned to deal with this problem. I’m listing them here for myself.

For the examples, I am going to be using the ori command line tool from Web Origami, but they can be generalized to any CLI tool.

Use npx

You can run a package you have installed locally — or one you don’t have installed at all! — using npx.

Note: npx is part of npm. If you have npm installed, you also have npx.

npx ori version

Pros:

  • Short 3-character command to do what you want.

Cons:

  • ??? just that you have to know it exists, I guess.
  • Update: Tyler Mercer emailed me noting that this approach is susceptible to typosquatting attacks which is a really great point. If you accidentally type npx oro instead of npx ori and oro is a malicious package created for just such occasions when you mistype something, well then you now have malicious code installed and running on your machine. Be careful!

Invoke Directly From node_modules

You can reference the locally-installed CLI tool directly in node_modules and run it.

When npm installs stuff, it sticks executables in a special .bin folder, which is where you’ll find any CLI tools you installed.

# From your project root
./node_modules/.bin/ori version

Pros:

  • Let’s you run the CLI tool dependency that’s installed locally to the project.

Cons:

  • Lots of extra typing each time you want to run an executable.

Add a Script to package.json

If the CLI tool is a dependency in the project, you can formulate the command as a script in package.json

"scripts": {
  "ori-version": "ori version"
},
"dependencies": {
  "@weborigami/ori": "^0.2.5"
}

Then run it with:

npm run ori-version

This will use the locally-installed version of the ori command line tool.

Pros:

  • Useful for things like build commands on remote servers (e.g. tell Netlify to run npm run build).

Cons:

  • When you’re iterating with a CLI tool locally, the feedback cycle for this method is incredibly cumbersome: type the command in package.json, save the file, run npm run <command>, see if it worked, repeat.

Reply via: Email :: Mastodon :: Bluesky

Tagged in: #node

Gotchas in Naming CSS View Transitions

View

I’m playing with making cross-document view transitions work on this blog.

Nothing fancy. Mostly copying how Dave Rupert does it on his site where you get a cross-fade animation on the whole page generally, and a little position animation on the page title specifically.

Animated gif of a mouse clicking on a blog post title and it animating to the top on the next HTML page.

To animate the page title, I need a unique ID to target the element I want to transition between pages, e.g.

<!-- 1st page HTML -->
<a
  href="/2024/i-love-kitkats"
  style="view-transition-name: kitkats">
  I Love KitKats
</a>

<!-- 2nd page HTML -->
<h1 style="view-transition-name: kitkats">
  I Love KitKats
</h1>

The problem with the above is that, if I have page that lists all my blog posts and I have another one about KitKats, what will the ID be?

Well I already have a globally-unique ID for each post: the post’s URL path!

So, in my static site generator, I think “I’ll just use my post’s path as the transition name!”

<a
  href="/2024/i-love-kitkats"
  style="view-transition-name: /2024/i-love-kitkats">
  I Love KitKats
</a>  

I’m not actually sure if this will work because of the forward slashes, but I try it.

No dice.

“Maybe I need to wrap it in quotes, like a the name of a value in font-family?”

So I try that:

<a
  href="/2024/i-love-kitkats"
  style="view-transition-name: '/2024/i-love-kitkats'">
  I Love KitKats
</a>  

Nope, that doesn't work either.

Ok, fine. I’ll just strip out the slashes in the path. I don’t need the slashes for it to be a unique identifier, e.g.

postPath.replace(/\//g, '')

Which gives me HTML like this:

<a
  href="/2024/i-love-kitkats"
  style="view-transition-name: 2024i-love-kitkats">
  I Love KitKats
</a> 

Still no dice.

After trying to get it working without looking at the manual, I concede to looking up view-transition-name on MDN. The docs say I have to use “a distinct identifying name (a <custom-ident>)”.

“What is a <custom-ident>?” I follow that link and read about it.

The docs throw some shade at me:

A <custom-ident> must not be placed between single or double quotes as this would be identical to a <string>.

Whoops.

Ok, so it’s an identifier that has some special rules, like that you can’t use an already-reserved global CSS name like “inherit”, “unset”, or “auto”.

Also you can’t use a forward slash (my bad).

And you can’t start the string with a number (my bad).

So I stick a prefix on each, e.g.

'title-${postPath}'.replace(/\//g, '')

Giving me:

<a
  href="/2024/i-love-kitkats"
  style="view-transition-name: title-2024i-love-kitkats">
  I Love KitKats
</a> 

Boom! It works!

So there you go. Way more than you ever wanted to know about the gotchas of a creating a unique view-transition-name.

And if you didn’t know about <custom-ident> in CSS, now you do!

Update 2024-01-13

Bramus reached out to let me know the cutting edge will let put id attributes on elements and then use auto (Safari-only at the time of this writing) or attr() which can parse a value into an custom-ident (Chrome 133+ at the time of this writing).

Here’s some example code from his Codepen:

<div class="cards">
  <div class="card" id="card-1">1</div>
  <div class="card" id="card-2">2</div>
  <div class="card" id="card-3">3</div>
</div>
<style>
.card[id] {
    view-transition-name: attr(id type(<custom-ident>), none);
    view-transition-class: card;
}
</style>

Pretty cool!


Reply via: Email :: Mastodon :: Bluesky

Tagged in: #css

Don’t Miss the Product for the Artifacts

View

Ever hear that idiom, “Don’t miss the forest for the trees”? The idea being, you miss the bigger picture because you’re focused on the minutia?

Feels like the tech equivalent is: Don’t miss the product for the artifacts.

Here’s Robin Rendle in a recent piece on design artifacts:

There’s a factory-like production of the modern design process which believes that the assets are more important than the product itself.

Nailed it 🎯

Too often we confuse 1) the artifact created in support of the deliverable, with 2) the deliverable itself.

For example, some designers are awesome at Figma. True wizards at the things they can make. But we’re not Figma designers. We’re app designers, web designers, product designers. Figma is just the tool that facilitates creating the thing we actually want to make. Yet we can spend so much time thinking “being good at Figma” is our purpose, when in fact it’s to use Figma to build something great for people!

Even “website” or “app” can be misleading. A web developer makes what they were hired for: a web site. An app developer makes an app.

But we’re not making websites or apps. We’re making tools and experiences that help people solve their problems.

In that sense, delivering a website is still just delivering an artifact.

And we’re not making artifacts.

We’re trying to help people make progress: solve their problems, find information, complete tasks.

Artifacts are always in service of progress, not progress itself.


Reply via: Email :: Mastodon :: Bluesky

Social Inflation

View

Imagine you’re on a social network and you start getting tons of followers.

You love it! Your follower count is going up!

Instead of a nobody with a couple hundred followers, you’ve bypassed the 1k+ mark and it keeps going!

You’re ecstatic! This is the “next step” you were aspiring to.

But then you start looking around.

The people you’re following with 1k+ followers are now at 10k+.

It hits you: everyone else’s follower count is going up too.

The network owners announce: “We’re having a bot problem at the moment. We’re aware of it and working on it.”

1k followers isn’t what it used to be. Now you need 10k. Or 100k.

Everyone’s follower count is being inflated.

Now when my kids ask, “Why can’t we just print more money?” I think I can analogize currency inflation to their social network following. Your follower count going up makes you feel good, but it’s merely the illusion of growth. What seems like increase is actually dilution, as relative value remains consistent and genuine value hasn’t changed.

Economic disclaimer: this content is for fun only and should not be considered professional economic theory. Please do not send me emails or messages beginning with: “Well, actually…”


Reply via: Email :: Mastodon :: Bluesky