Elf M. Sternberg

Full Stack Web Developer

Where one teaches, two learn.

Blog

A quarter-century of code experience

WEB COMPONENTS AND FORMASSOCIATED: EVERY IS WRITING THE FORM UPDATE FUNCTION WRONG

Everyone writes updateFormValues() wrong.

If you work in Web Components (like Lit, Fast, or just plain vanilla web components), under the new hotness of attachedInternals and formAssociated, DOM methods that allow you to create new web components that interact with forms. You can now write your own input controls, which is very cool, and fraught with difficulty.

Every example of a custom control includes a function named something like updateFormValues(). Forms don't automatically extract their values from the input controls at submit time; each control has to push its value to the form after it has been updated and validated. updateFormValues() is that function: it takes the input being monitored by the component and passes it to the form, so that when the form is submitted the values are sent wherever the action attribute says they should go.

Unfortunately, every example of updateFormValues I have seen is wrong.

Continue Reading

UNDERSTANDING UTOPIA SCALING, PART 2

In the last post, we discussed the foundation of the Utopia Scaling system: what a single step means for the font size. In Utopia Scaling's "step 0," the font size for your base font starts smaller on small devices, with a specific minimal size that the font size cannot go below, and grows linearly as the screen gets bigger until some maximal size the font cannot go above.

These four values, a minimal and maximal screen size, and a minimal and maximal font size, create two points on a flat graph; two points define a line, and by calculating the slope and intercept of that line we can craft a single linear expression that creates incredibly smooth scaling across a vast suite of devices.

We often needsmaller fonts for placeholders, help messages, and so on, or larger fonts for headers and titles. Utopia calls those "step 1", "step 2", and "step 3", to make them larger, and "step -1", "step -2", and so forth for smaller.

Here's how the steps are calculated.

Continue Reading

UNDERSTANDING UTOPIA SCALING, PART 1

This website is built using Utopia scaling, which means that, instead of having multiple hard breakpoints, there are only two: phone and desktop, and none of those breakpoints controls sizing or spacing. The only thing my breakpoints do is change the layout to accommodate the screen space expected in a landscape or portrait setting. Utopia scaling automatically reduces the spacing and font sizes on smaller devices, removing whitespace in a controlled way that corresponds to the trained expectation that phones are small but with excellent screens that make reading on them more comfortable than anyone might have expected 20 years ago.

But I never really understood the math behind it. A few weeks ago, I chose to fix that, and I think (I think, mind you) that I understand it now. It's all linear geometry. Let me explain how, for a chosen base font size, you calculate the base font sizes for your smallest screens, your largest screens, and a smooth, fractionally accurate range of font sizes for every possible screen size in between.

Continue Reading

HOW I WATCH MY CATS: FSWEBCAM AND V4L-UTILS

My wife and I have two cats: Necco, who is thin, elderly and living with bowel cancer; and Sienna, who is heavyset and middle-aged, and recently we had to implement a system to monitor their feeding habits. Here's how I did that with Linux, an old laptop, and a cheap used webcam.

TL;DR? This post shows you how to set up Linux, fswebcam and v4l-utils to configure and operate a USB webcam as a somewhat clumsy surveillance device.

Continue Reading

Web components

CONVERTING THE CODROPS MINZE TUTORIAL TO USE LIT

Lit!

Native web components are a pain to build and design, mostly because the API is complicated and naive implementations can have less than stellar performance. Lit is a library, originally developed by Google, that provides an alternative API for dealing with the web component lifecycle, as well as a scheduling system that decorates your existing DOM with comment fields that provide for all of the performance of a virtual DOM without any of the memory overhead. Lit also provides excellent ergonomics for defining properties, attributes, accessors to component internals, as well as powerful idioms for defining events, boolean and string passing, and passing complex JavaScript objects from one component to another.

The components here were originally developed for a similar library, Minze, which was one of several such libraries with similar intent. They, and an accompanying article, were developed by Sergej Samsoneko and published at Codrops.

Continue Reading

Web components

USING THE CSS `::PART` SELECTOR FOR STATEFUL STYLING OF WEB COMPONENTS

If you're a web component developer, you know that you can use the css ::part pseudoelement to give external developers permission to style, well, parts of your component. But did you know that you can use ::part to selectively style those parts based on the state of the component?

I'll show you how you can declare parts dynamically and serially, just as if they were child selectors, and use that dynamism to allow developers to style your component differently if it's disabled, or readonly, or "successful!" This example will use Lit, but it should work with raw web components or with any other library like Svelte, Ornament, or Minze.

Continue Reading

AI: THE SPACE BETWEEN VS. THE SPACE ABOVE

A famous essay, "Programming as Theory Building" (MW: PDF), proposes that every computer program starts with an idea that the programmer seeks to prove is true and possible. There is a problem and the programmer seeks to prove that there is a solution to it.

Beethoven famously wrote some of his best music when he was deaf. He saw the notes on the page and could read them with the same clarity with which one can read well-written source code, and he could spend hours alone with pen and paper, crafting notes and bars and sounds and instruments he could only remember, no longer hear, into a literal symphony that could move those who understood it to tears.

AIs (LLM Large Language Models, AMLM Advanced Machine-Learning Models) lack this capacity.

Continue Reading

Css

BEM IS BACK, BABY!

BEM (Block, Element, Modifier), the CSS discipline in which CSS components are defined in ways that clarify how they're used, has long been lamented for its verbosity. Take this example from the home page banner on the BEM advocacy site GetBEM:

.opinions-box__view-more--is-disabled {
    color: gray;
}

This means that there's a high-level, class-based component, opinions-box, with a child element (a button, perhaps) with the class view-more, and this is the class you apply to that button when it's enabled.

These rule names can get absurdly long.

Personally, I find BEM to be about as unweildy as a Swiss Army Sword. It still insists that you memorize certain semantics and focus on how they interact with the rest of the world. We tried to tame the beast, but somehow, recently, it has once again gotten out of hand.

Continue Reading

Professionalism

DOESN'T MEAN ANYTHING: PROGRAMMING WITH AI IS NOT SIMPLE

I've had a lot of bad experience with trying to use AIs to develop my codebase. My experiences so far had been full of failures. But this week I had a... sort-of successful time.

What I really discovered, though, is that is programming with an AI "assisstant" is like having a first-year, over-eager programming student from some alien world armed with a huge library of templates and a lot more time and energy to stitch them together. It's process for producing code isn't based on intent, so what you get is a template you're expected to modify.

The problem is, so many vibe-coders think they can code without understanding what the code does. The problem is, AIs don't understand what the code does either.

Continue Reading

Professionalism

MOUNTAINS AND MOLEHILLS: FIXING TREE-SITTER-SCSS

In my last adventure, I complained mightily about how a tiny bug in tree-sitter-scss was all that stood between me and my next heroic work accomplishment. Although I didn't mention it at the time, I had little faith that my submitted issue would be addressed anytime soon, since when looking at the tree-sitter grammar repository I could see that the SCSS parser had last been updated in early April of 2024 and had been idle for eight months.

I took matters into my own hands.

Continue Reading

Professionalism

WHEN YOU CAN SEE THE PROMISED LAND, BUT YOU CAN'T GET THERE FROM HERE

Update: Contrary to my whining down below, I spent the weekend playing with tree-sitter, found the bug, and have submitted a PR.

When you've got twenty years of developer experience, there is one source of frustration at work that can be greater than any other: when you can see the promised land but you know you can't get there and, worst of all, the wall between you and there is barely ankle high.

I am not, by any stretch of the imagination, an expert on parsers and scanners. I've written a few DSLs in my time and always leaned on something commonly available, usually either s-expressions, a readily available configuration language like YAML or TOML, or just a cut-down version of the host language with specificity for my DSL needs.

So let's talk about Shatterfly.

Continue Reading

Webdriverio

TIP: DELETE LIT'S HIDDEN CACHE WHEN USING WEBDRIVERIO TO TEST LIT-ELEMENT COMPONENTS.

I recently had a horrible experience where I could not get Lit to run the same test twice using the WebdriverIO Component Testing Guidelines for Lit.

The test is simple: Before each test, render the component, click on it, see if its internal state is what I expect, and then remove the component at the end of the test. I expected that if I ran the exact same test twice, it would work (or fail) twice.

It turns out Lit is being passive-aggressive about caching what you've done, won't re-render cached content preferring to re-connect it instead, and won't help you figure out that it doesn't exist anymore and can't be re-connected.

Continue Reading

Async rust

ASYNC RUST: SERVER-SIDE EVENTS WITH A REMOTE HEARTBEAT

This project took me forever to figure out, so I'm going to document it as much as possible. What I've been trying to get to is a simple, Async Rust codebase that would allow me to plug in web-based server-side events or web socket events and then send them over to one or more clients as needed. A lot of this code comes from a variety of examples that I stitched together, and getting them to work well was a headache, so now you get to share in it.

Continue Reading

Chat

PEOPLE SERIOUSLY USE CHATGPT TO WRITE CODE? I DON'T BELIEVE IT.

Today's experience with ChatGPT-4o convinces me that this codebase is absolutely not ready for use, and programmers depending on it will be in deep, deep trouble. I opened a file today with less than ten lines of code in a clear and well-documented pattern, and asked ChatGPT to help me finish it. It did not go well.

Continue Reading

Society

NORMAL PEOPLE HAVE WEIRD IDEAS ABOUT COMPUTERS

Normal people have very weird ideas about how computers work under the covers.

Omaha and I visit a cafe near our home nearly every day. Today, I got into a weird conversation with someone there about computers and, of all things, lists. I said a list was one dimensional, and she got angry at me. "A list is two dimensional. It goes down the paper and across the paper." I objected that that's not how it works inside a computer; it's just a single line, from beginning to end, of numbers. A list is just a line of numbers, in pairs: the address where to find each item on the list, and how much to read in until you've seen the whole item. The items themselves are somewhere else along the line. There's no multi-dimensionality at all to memory (or hard drive) accessing; it's just one dimension.

"But that's still two dimensional, right?" she insisted. "You have an address for each item, like, going down the page, but each item goes across it." I said that was an artifact of human interpretation; computer addressing was a single number, one for each place in RAM or on a disk.

She refused to believe that.

Continue Reading

Web components

ASYNCHRONOUS DEPENDENCY INJECTION AND PENDING EVENT MANAGEMENT WITH WEB COMPONENTS

The DevFest Nantes 2019 presentation of Building Complex Applications with Web Components is probably one of the most important to Lit developers; everyone references Justin Fagnani's presentation of how to do routing, lazy loading, and context management. The presentation includes a section on dependency injection: how an object can request its dependencies from other components higher up in the DOM tree.

One of the problems I have with the code as presented is that it fails to handle the case where the dependency supplier must perform some asynchronous work before sending the dependency to the requester. I'll show you how to extend the example to handle that, and what capabilities that extension gives you.

Continue Reading

Web components

DON'T USE CUSTOMEVENT IN JAVASCRIPT. INHERIT FROM EVENT INSTEAD.

It's commonplace in Web Components to use custom events. The custom event type is an inheritor of the Event class, but it includes a new field, detail: any, that allows you to attach data to the Event, which is useful for passing data up to the listener, right?

Wrong. It's a trap. You could, theoretically, inherit from CustomEvent and narrow the content of detail to what you specifically want, and I've seen lots of code where people do exactly that.

But there's a better way: just inherit from Event. Skip the CustomEvent class and just create your own events. I'll show you how.

Continue Reading