Journal tags: browsers

168

sparkline

Making the website for Research By The Sea

UX London isn’t the only event from Clearleft coming your way in 2025. There’s a brand new spin-off event dedicated to user research happening in February. It’s called Research By The Sea.

I’m not curating this one, though I will be hosting it. The curation is being carried out most excellently by Benjamin, who has written more about how he’s doing it:

We’ve invited some of the best thinkers and doers from from in the research space to explore how researchers might respond to today’s most gnarly and pressing problems. They’ll challenge current perspectives, tools, practices and thinking styles, and provide practical steps for getting started today to shape a better tomorrow.

If that sounds like your cup of tea, you should put February 27th 2025 in your calendar and grab yourself a ticket.

Although I’m not involved in curating the line-up for the event, I offered Benjamin my swor… my web dev skillz. I made the website for Research By The Sea and I really enjoyed doing it!

These one-day events are a great chance to have a bit of fun with the website. I wrote about how enjoyable it was making the website for this year’s Patterns Day:

I felt like I was truly designing in the browser. Adjusting spacing, playing around with layout, and all that squishy stuff. Some of the best results came from happy accidents—the way that certain elements behaved at certain screen sizes would lead me into little experiments that yielded interesting results.

I took the same approach with Research By The Sea. I had a design language to work with, based on UX London, but with more of a playful, brighter feel. The idea was that the website (and the event) should feel connected to UX London, while also being its own thing.

I kept the typography of the UX London site more or less intact. The page structure is also very similar. That was my foundation. From there I was free to explore some other directions.

I took the opportunity to explore some new features of CSS. But before I talk about the newer stuff, I want to mention the bits of CSS that I don’t consider new. These are the things that are just the way things are done ‘round here.

Custom properties. They’ve been around for years now, and they’re such a life-saver, especially on a project like this where I’m messing around with type, colour, and spacing. Even on a small site like this, it’s still worth having a section at the start where you define your custom properties.

Logical properties. Again, they’ve been around for years. At this point I’ve trained my brain to use them by default. Now when I see a left, right, width or height in a style sheet, it looks like a bug to me.

Fluid type. It’s kind of a natural extension of responsive design to me. If a website’s typography doesn’t adjust to my viewport, it feels slightly broken. On this project I used Utopia because I wanted different type scales as the viewport increased. On other projects I’ve just used on clamp declaration on the body element, which can also get the job done.

Okay, so those are the things that feel standard to me. So what could I play around with that was new?

View transitions. So easy! Just point to an element on two different pages and say “Hey, do a magic move!” You can see this in action with the logo as you move from the homepage to, say, the venue page. I’ve also added view transitions to the speaker headshots on the homepage so that when you click through to their full page, you get a nice swoosh.

Unless, like me, you’re using Firefox. In that case, you won’t see any view transitions. That’s okay. They are very much an enhancement. Speaking of which…

Scroll-driven animations. You’ll only get these in Chromium browsers right now, but again, they’re an enhancement. I’ve got multiple background images—a bunch of cute SVG shapes. I’m using scroll-driven animations to change the background positions and sizes as you scroll. It’s a bit silly, but hopefully kind of cute.

You might be wondering how I calculated the movements of each background image. Good question. I basically just messed around with the values. I had fun! But imagine what an actually-skilled interaction designer could do.

That brings up an interesting observation about both view transitions and scroll-driven animations: Figma will not help you here. You need to be in a web browser with dev tools popped open. You’ve got to roll up your sleeves get your hands into the machine. I know that sounds intimidating, but it’s also surprisingly enjoyable and empowering.

Oh, and I made sure to wrap both the view transitions and the scroll-driven animations in a prefers-reduced-motion: no-preference @media query.

I’m pleased with how the website turned out. It feels fun. More importantly, it feels fast. There is zero JavaScript. That’s the main reason why it’s very, very performant (and accessible).

Smooth transitions across pages; smooth animations as you scroll: it’s great what you can do with just HTML and CSS.

content-visibility in Safari

Earlier this year I wrote about some performance improvements to The Session using the content-visibility property in CSS.

If you say content-visibility: auto you’re telling the browser not to bother calculating the layout and paint for an element until it needs to. But you need to combine it with the contain-intrinsic-block-size property so that the browser knows how much space to leave for the element.

I mentioned the browser support:

Right now content-visibility is only supported in Chrome and Edge. But that’s okay. This is a progressive enhancement. Adding this CSS has no detrimental effect on the browsers that don’t understand it (and when they do ship support for it, it’ll just start working).

Well, that’s happened! Safari 18 supports content-visibility. I didn’t have to do a thing and it just started working.

But …I think I’ve discovered a little bug in Safari’s implementation.

(I say I think it’s a bug with the browser because, like Jim, I’ve made the mistake in the past of thinking I had discovered a browser bug when in fact it was something caused by a browser extension. And when I say “in the past”, I mean yesterday.)

So here’s the issue: if you apply content-visibility: auto to an element that contains an SVG, and that SVG contains a text element, then Safari never paints that text to the screen.

To see an example, take a look at the fourth setting of Cooley’s reel on The Session archive. There’s a text element with the word “slide” (actually the text is inside a tspan element inside a text element). On Safari, that text never shows up.

I’m using a link to the archive of The Session I created recently rather than the live site because on the live site I’ve removed the content-visibility declaration for Safari until this bug gets resolved.

I’ve also created a reduced test case on Codepen. The only HTML is the element containing the SVGs. The only CSS—apart from the content-visibility stuff—is just a little declaration to push the content below the viewport so you have to scroll it into view (which is when the bug happens).

I’ve filed a bug report. I know it’s a fairly niche situation, but there are some other issues with Safari’s implementation of content-visibility so it’s possible that they’re all related.

Mismatch

This seems to be the attitude of many of my fellow nerds—designers and developers—when presented with tools based on large language models that produce dubious outputs based on the unethical harvesting of other people’s work and requiring staggering amounts of energy to run:

This is the future! I need to start using these tools now, even if they’re flawed, because otherwise I’ll be left behind. They’ll only get better. It’s inevitable.

Whereas this seems to be the attitude of those same designers and developers when faced with stable browser features that can be safely used today without frameworks or libraries:

I’m sceptical.

Ad tech

Back when South by Southwest wasn’t terrible, there used to be an annual panel called Browser Wars populated with representatives from the main browser vendors (except for Apple, obviously, who would never venture onto a stage outside of their own events).

I remember getting into a heated debate with the panelists during the 2010 edition. I was mad about web fonts.

Just to set the scene, web fonts didn’t exist back in 2010. That’s what I was mad about.

There was no technical reason why we couldn’t have web fonts. The reason why we didn’t get web fonts for years and years was because browser makers were concerned about piracy and type foundries.

That’s nice and all, but as I said during that panel, I don’t recall any such concerns being raised for photographers when the img element was shipped. Neither was the original text-only web held back by the legimate fear by writers of plagiarism.

My point was not that these concerns weren’t important, but that it wasn’t the job of web browsers to shore up existing business models. To use standards-speak, these concerns are orthogonal.

I’m reminded of this when I see browser makers shoring up the business of behavioural advertising.

I subscribe to the RSS feed of updates to Chrome. Not all of it is necessarily interesting to me, but all of it is supposedly aimed at developers. And yet, in amongst the posts about APIs and features, there’ll be something about the Orwellianly-titled “privacy sandbox”.

This is only of interest to one specific industry: behavioural online advertising driven by surveillance and tracking. I don’t see any similar efforts being made for teachers, cooks, architects, doctors or lawyers.

It’s a ludicrous situation that I put down to the fact that Google, the company that makes Chrome, is also the company that makes its money from targeted advertising.

But then Mozilla started with the same shit.

Now, it’s one thing to roll out a new so-called “feature” to benefit behavioural advertising. It’s quite another to make it enabled by default. That’s a piece of deceptive design that has no place in Firefox. Defaults matter. Browser makers know this. It’s no accident that this “feature” was enabled by default.

This disgusts me.

It disgusts me all the more that it’s all for nothing. Notice that I’ve repeatly referred to behavioural advertising. That’s the kind that relies on tracking and surveillance to work.

There is another kind of advertising. Contextual advertising is when you show an advertisement related to the content of the page the user is currently on. The advertiser doesn’t need to know anything about the user, just the topic of the page.

Conventional wisdom has it that behavioural advertising is much more effective than contextual advertising. After all, why would there be such a huge industry built on tracking and surveillance if it didn’t work? See, for example, this footnote by John Gruber:

So if contextual ads generate, say, one-tenth the revenue of targeted ads, Meta could show 10 times as many ads to users who opt out of targeting. I don’t think 10× is an outlandish multiplier there — given how remarkably profitable Meta’s advertising business is, it might even need to be higher than that.

Seems obvious, right?

But the idea that behavioural advertising works better than contextual advertising has no basis in reality.

If you think you know otherwise, Jon Bradshaw would like to hear from you:

Bradshaw challenges industry to provide proof that data-driven targeting actually makes advertising more effective – or in fact makes it worse. He’s spoiling for a debate – and has three deep, recent studies that show: broad reach beats targeting for incremental growth; that the cost of targeting outweighs the return; and that second and third party data does not outperform a random sample. First party data does beat the random sample – but contextual ads massively outperform even first party data. And they are much, much cheaper. Now, says Bradshaw, let’s see some counter-evidence from those making a killing.

If targeted advertising is going to get preferential treatment from browser makers, I too would like to see some evidence that it actually works.

Further reading:

Web App install API

My bug report on Apple’s websites-in-the-dock feature on desktop has me thinking about how starkly different it is on mobile.

On iOS if you want to add a website to your home screen, good luck. The option is buried within the “share” menu.

First off, it makes no sense that adding something to your homescreen counts as sharing. Secondly, how is anybody supposed to know that unless they’re explicitly told.

It’s a similar situation on Android. In theory you can prompt the user to install a progressive web app using the botched BeforeInstallPromptEvent. In practice it’s a mess. What it actually does is defer the installation prompt so you can offer it a more suitable time. But it only works if the browser was going to offer an installation prompt anyway.

When does Chrome on Android decide to offer the installation prompt? It’s a mix of required criteria—a web app manifest, some icons—and an algorithmic spell determined by the user’s engagement.

Other browser makers don’t agree with this arbitrary set of criteria. They quite rightly say that a user should be able to add any website to their home screen if they want to.

What we really need is an installation API: a way to programmatically invoke the add-to-homescreen flow.

Now, I know what you’re going to say. The security and UX implications would be dire. But this should obviously be like geolocation or notifications, only available in secure contexts and gated by user interaction.

Think of it like adding something to the clipboard: it’s something the user can do manually, but the API offers a way to do it programmatically without opening it up to abuse.

(I’d really love it if this API also had a declarative equivalent, much like I want button type="share" for the Web Share API. How about button type="install"?)

People expect this to already exist.

The beforeinstallprompt flow is an absolute mess. Users deserve better.

CSS Day 2024

My stint as one of the hosts of CSS Day went very well indeed. I enjoyed myself and people seemed to like the cut of my jib.

During the event there was a real buzz on Mastodon, which was heartening to see. I was beginning to worry that hashtagging events was going to be collatoral damage from Elongate, but there was plenty of conference-induced FOMO to be experienced on the fediverse.

The event itself was, as always, excellent. Both in terms of content and organisation.

Some themes emerged during CSS Day, which I always love to see. These emergent properties are partly down to curation and partly down to serendipity.

The last few years of CSS Day have felt like getting a firehose of astonishing new features being added to the language. There was still plenty of cutting-edge stuff this year—masonry! anchor positioning!—but there was also a feeling of consolidation, asking how to get all this amazing new stuff into our workflows.

Matthias’s opening talk on day one and Stephen’s closing talk on the same day complemented one another perfectly. Both managed to inspire while looking into the nitty-gritty practicalities of the web design process.

It was, astoundingly, Matthias’s first ever conference talk. I have no doubt it won’t be the last—it was great!

I gave Stephen a good-natured roast in my introduction, partly because it was his birthday, partly because we’re old friends, but mostly because it was enjoyable for me to watch him squirm. Of course his talk was, as always, superb. Don’t tell him, but he might be one of my favourite speakers.

The topic of graphic design tools came up more than once. It’s interesting to see how the issues with them have changed. It used to be that design tools—Photoshop, Sketch, Figma—were frustrating because they were writing cheques that CSS couldn’t cash. Now the frustration is the exact opposite. Our graphic design tools aren’t capable of the kind of fluid declarative design we can now accomplish in web browsers.

But the biggest rift remains not with tools or technologies, but with people and mindsets. Our tools can reinforce mindsets but the real divide happens in how different people approach CSS.

Both Josh and Kevin get to the heart of this in their tremendous tutorials, and that was reflected in their talks. They showed the difference between having the bare minimum understanding of CSS in order to get something done as quickly as possible, and truly understanding how CSS works in order to open up a world of possibilities.

For people in the first category, Sarah Dayan was there to sing the praises of utility-first CSS AKA atomic CSS. I commend her bravery!

During the Q&A, I restrained myself from being too Paxmanish. But I did have l’esprit d’escalier afterwards when I realised that the entire talk—and all the answers afterwards—depended on two mutually-incompatiable claims:

  1. The great thing about atomic CSS is that it’s a constrained vocabulary so your team has to conform, and
  2. The other great thing about it is that it’s utility-first, not utility-only so you can break out of it and use regular CSS if you want.

Insert .gif of character from The Office looking to camera.

Most of the questions coming in during the Q&A reflected my own take: how about we use utility classes for some things, but not all things. Seems sensible.

Anyway, regardless of what I or anyone else thinks about the substance of what Sarah was saying, there was no denying that it was a great presentation. They were all great presentations. That’s unusual, and I say that as a conference organiser as well as an attendee. Everyone brings their A-game to CSS Day.

Mind you, it is exhausting. I say it every year, but it always feels like one talk too many. Not that any individual talk wasn’t good, but the sheer onslaught of deep dives into the innards of CSS has my brain exploding before the day is done.

A highlight for me was getting to introduce Fantasai’s talk on the design principles of CSS, which was right up my alley. I don’t think most people realise just how much we owe her for her years of work on standards. The web would be in a worse place without the Herculean work she’s done behind the scenes.

Another highlight was getting to see some of the students I met back in March. They were showing some of their excellent work during the breaks. I find what they’re doing just as inspiring as the speakers on stage.

In fact, when I was filling in the post-conference feedback form, there was a question: “Who would you like to see speak at CSS Day next year?” I was racking my brains because everyone I could immediately think of has already spoken at some point. So I wrote, “It would be great to see some of those students speaking about their work.”

I think it would be genuinely fascinating to get their perspective on what we consider modern CSS, which to them is just CSS.

Either way I’ll back next year for sure.

It’s funny, but usually when a conference is described as “inspiring” it’s because it’s tackling big galaxy-brain questions. But CSS Day is as nitty-gritty as it gets and I found it truly inspiring. Like, I couldn’t wait to open up my laptop and start writing some CSS. That kind of inspiring.

Browser support

There was a discussion at Clearleft recently about browser support. Rich has more details but the gist of it is that, even though we were confident that we had a good approach to browser support, we hadn’t written it down anywhere. Time to fix that.

This is something I had been thinking about recently anyway—see my post about Baseline and progressive enhancement—so it didn’t take too long to put together a document explaining our approach.

You can find it at browsersupport.clearleft.com

We’re not just making it public. We’re releasing it under a Creative Commons attribution license. You can copy this browser-support policy verbatim, you can tweak it, you can change it, you can do what you like. As long you include a credit to Clearleft, you’re all set.

I think this browser-support policy makes a lot of sense. It certainly beats trying to browser support to specific browsers or version numbers:

We don’t base our browser support on specific browser names and numbers. Instead, our support policy is based on the capabilities of those browsers.

The more organisations adopt this approach, the better it is for everyone. Hence the liberal licensing.

So next time your boss or your client is asking what your official browser-support policy is, feel free to use browsersupport.clearleft.com

Speculation rules and fears

After I wrote positively about the speculation rules API I got an email from David Cizek with some legitimate concerns. He said:

I think that this kind of feature is not good, because someone else (web publisher) decides that I (my connection, browser, device) have to do work that very often is not needed. All that blurred by blackbox algorithm in the browser.

That’s fair. My hope is that the user will indeed get more say, whether that’s at the level of the browser or the operating system. I’m thinking of a prefers-reduced-data setting, much like prefers-color-scheme or prefers-reduced-motion.

But this issue isn’t something new with speculation rules. We’ve already got service workers, which allow the site author to unilaterally declare that a bunch of pages should be downloaded.

I’m doing that for Resilient Web Design—when you visit the home page, a service worker downloads the whole site. I can justify that decision to myself because the entire site is still smaller in size than one article from Wired or the New York Times. But still, is it right that I get to make that call?

So I’m very much in favour of browsers acting as true user agents—doing what’s best for the user, even in situations where that conflicts with the wishes of a site owner.

Going back to speculation rules, David asked:

Do we really need this kind of (easily turned to evil) enhancement in the current state of (web) affairs?

That question could be asked of many web technologies.

There’s always going to be a tension with any powerful browser feature. The more power it provides, the more it can be abused. Animations, service workers, speculation rules—these are all things that can be used to improve websites or they can be abused to do things the user never asked for.

Or take the elephant in the room: JavaScript.

Right now, a site owner can link to a JavaScript file that’s tens of megabytes in size, and the browser has no alternative but to download it. I’d love it if users could specify a limit. I’d love it even more if browsers shipped with a default limit, especially if that limit is related to the device and network.

I don’t think speculation rules will be abused nearly as much as client-side JavaScript is already abused.

Speculation rules

There’s a new addition to the latest version of Chrome called speculation rules. This already existed before with a different syntax, but the new version makes more sense to me.

Notice that I called this an addition, not a standard. This is not a web standard, though it may become one in the future. Or it may not. It may wither on the vine and disappear (like most things that come from Google).

The gist of it is that you give the browser one or more URLs that the user is likely to navigate to. The browser can then pre-fetch or even pre-render those links, making that navigation really snappy. It’s a replacement for the abandoned link rel="prerender".

Because this is a unilateral feature, I’m not keen on shipping the code to all browsers. The old version of the API required a script element with a type value of “speculationrules”. That doesn’t do any harm to browsers that don’t support it—it’s a progressive enhancement. But unlike other progressive enhancements, this isn’t something that will just start working in those other browsers one day. I mean, it might. But until this API is an actual web standard, there’s no guarantee.

That’s why I was pleased to see that the new version of the API allows you to use an external JSON file with your list of rules.

I say “rules”, but they’re really more like guidelines. The browser will make its own evaluation based on bandwidth, battery life, and other factors. This feature is more like srcset than source: you give the browser some options, but ultimately you can’t force it to do anything.

I’ve implemented this over on The Session. There’s a JSON file called speculationrules.js with the simplest of suggestions:

{
  "prerender": [{
    "where": {
        "href_matches": "/*"
    },
    "eagerness": "moderate"
  }]
}

The eagerness value of “moderate” says that any link can be pre-rendered if the user hovers over it for 200 milliseconds (the nuclear option would be to use a value of “immediate”).

I still need to point to that JSON file from my HTML. Usually this would be done with something like a link element, but for this particular API, I can send a response header instead:

Speculation-Rules: “/speculationrules.json"

I like that. The response header is being sent to every browser, regardless of whether they support speculation rules or not, but at least it’s just a few bytes. Those other browsers will ignore the header—they won’t download the JSON file.

Here’s the PHP I added to send that header:

header('Speculation-Rules: "/speculationrules.json"');

There’s one extra thing I had to do. The JSON file needs to be served with mime-type of “application/speculationrules+json”. Here’s how I set that up in the .conf file for The Session on Apache:

<IfModule mod_headers.c>
  <FilesMatch "speculationrules.json">
    Header set Content-type application/speculationrules+json
   </FilesMatch>
</IfModule>

A bit of a faff, that.

You can see it in action on The Session. Open up Chrome or Edge (same same but different), fire up the dev tools and keep the network tab open while you navigate around the site. Notice how hovering over a link will trigger a new network request. Clicking on that link will get you that page lickety-split.

Mind you, in the case of The Session, the navigations were already really fast—performance is a feature—so it’s hard to guage how much of a practical difference it makes in this case, but it still seems like a no-brainer to me: taking a few minutes to add this to your site is worth doing.

Oh, there’s one more thing to be aware of when you’re implementing speculation rules. You have the option of excluding URLs from being pre-fetched or pre-rendered. You might need to do this if you’ve got links for adding items to shopping carts, or logging the user out. But my advice would instead be: stop using GET requests for those actions!

Most of the examples given for unsafe speculative loading conditions are textbook cases of when not to use links. Links are for navigating. They’re indempotent. For everthing else, we’ve got forms.

Baseline progressive enhancement

Support for view transitions for regular websites (as opposed to single-page apps) will ship in Chrome 126. As someone who’s a big fan—to put it mildly—I am very happy about this!

Hopefully Firefox and Safari won’t be too far behind. But it’s still worth adding view transitions to your website even if not every browser supports them. They’re the perfect example of a progressive enhancement.

The browsers that don’t yet support view transitions won’t be harmed in any way if you give them the CSS for view transitions. They’ll just ignore it. For users of those browsers, nothing changes.

Then when those browsers do ship support for view transitions, your website automatically gets an upgrade for those users. Code you’ve already written starts working from one day to the next.

Don’t wait, is what I’m saying.

I really like the Baseline initiative as a way to track browser support. It’s great to see it in use on MDN and Can I Use. It’s very handy having a glanceable indication of which browser features are newly available and which are widely available.

But…

Not all browser features work the same way. For features that work as progressive enhancements you don’t need to wait for them to be widely available.

Service workers. Preference queries. View transitions.

If a browser doesn’t support one of those features, that’s fine. Your website won’t break in that browser.

Now that’s not true of all browser features, particularly some JavaScript APIs. If a feature is critical for your site to function then you definitely want to wait until it’s widely supported.

Baseline won’t tell you the difference between those two different kinds of features.

I don’t want Baseline to get too complicated. Like I said, I really like how it’s nice and glanceable right now. But it would be nice if there way some indication that a newly-available feature is a progressive enhancement.

For now it’s up to us to make that distinction. So don’t fall into the trap of thinking that just because a feature isn’t listed as widely-available you can’t use it yet.

Really you want to ask two questions:

  1. How widely available is this feature?
  2. Can this feature be used as a progressive enhancement?

If Baseline tells you that the answer to the first question is “newly-available”, move on to the second question. If the answer to that is “no, it can’t be used as a progressive enhancement”, don’t ship that feature in production just yet.

But if the answer to that second question is “hell yeah, it’s a progressive enhancement!” then go for it, regardless of the answer to the first question.

Y’know, there’s a real irony in a common misunderstanding around progressive enhancement: some people seem to think it’s about not being able to use advanced browser features. In reality it’s the opposite. Progressive enhancement allows you to use advanced browser features even before they’re widely supported.

Displaying HTML web components

Those HTML web components I made for date inputs are very simple. All they do is slightly extend the behaviour of the existing input elements.

This would be the ideal use-case for the is attribute:

<input is="input-date-future" type="date">

Alas, Apple have gone on record to say that they will never ship support for customized built-in elements.

So instead we have to make HTML web components by wrapping existing elements in new custom elements:

<input-date-future>
  <input type="date">
<input-date-future>

The end result is the same. Mostly.

Because there’s now an additional element in the DOM, there could be unexpected styling implications. Like, suppose the original element was direct child of a flex or grid container. Now that will no longer be true.

So something I’ve started doing with HTML web components like these is adding something like this inside the connectedCallback method:

connectedCallback() {
    this.style.display = 'contents';
  …
}

This tells the browser that, as far as styling is concerned, there’s nothing to see here. Move along.

Or you could (and probably should) do it in your stylesheet instead:

input-date-future {
  display: contents;
}

Just to be clear, you should only use display: contents if your HTML web component is augmenting what’s within it. If you add any behaviours or styling to the custom element itself, then don’t add this style declaration.

It’s a bit of a hack to work around the lack of universal support for the is attribute, but it’ll do.

Pickin’ dates on iOS

This is a little follow-up to my post about web components for date inputs.

If you try the demo on iOS it doesn’t work. There’s nothing stopping you selecting any date.

That’s nothing to do with the web components. It turns out that Safari on iOS doesn’t support min and max on date inputs. This is also true of any other browser on iOS because they’re all just Safari in a trenchcoat …for now.

I was surprised — input type="date" has been around for a long time now. I mean, it’s not the end of the world. You’d have to do validation on inputted dates on the server anyway, but it sure would be nice for the user experience of filling in forms.

Alas, it doesn’t look like this is something on the interop radar.

What really surprised me was looking at Can I Use. That shows Safari on iOS as fully supporting date inputs.

Maybe it’s just semantic nitpickery on my part but I would consider that the lack of support for the min and max attributes means that date inputs are partially supported.

Can I Use gets its data from here. I guess I need to study the governance rules and try to figure out how to submit a pull request to update the currently incorrect information.

Rotten Apple

The European Union’s Digital Markets Act is being enforced and Apple aren’t happy about it.

Most of the discussion around this topic has centred on the requirement for Apple to provision alternative app stores. I don’t really care about that because I don’t really care about native apps. With one exception: I care about web browsers.

That’s the other part of the DMA that’s being enforced: Apple finally have to allow alternative browsing engines. Hallelujah!

Instead of graciously acknowledging that this is what’s best for users, Apple are throwing a tantrum.

First of all, they’re going to ringfence any compliance to users in the European Union. Expect some very interesting edge cases to emerge in a world where people don’t spent their entire lives in one country.

Secondly, Apple keep insisting that this will be very, very bad for security. You can read Apple’s announcement on being forced to comply but as you do you so, I’d like you to remember one thing: every nightmare scenario they describe for the security of users in the EU is exactly what currently happens on Macs everywhere in the world.

This includes risks from installing software from unknown developers that are not subject to the Apple Developer Program requirements, installing software that compromises system integrity with malware or other malicious code, the distribution of pirated software, exposure to illicit, objectionable, and harmful content due to lower content and moderation standards, and increased risks of scams, fraud, and abuse.

Users of macOS everywhere are currently exposed to all the risks that will supposedly overwhelm iOS users in the European Union. Weirdly, the sky hasn’t fallen.

It’s the same with web browsers. I just got a new Mac. It came with one browser pre-installed: Safari. It’s a good browser. But I also have the option of installing another browser, like Firefox (which I’ve done). A lot of people just use Safari. That’s good. That’s choice. Everyone wins.

Now Apple need to provide parity on iOS, at least for users in the EU. Again, Apple are decribing this coming scenario as an absolute security nightmare. But again, the conditions they’re describing are what already exist on macOS.

All Apple is being asked to do is offer than the same level of choice on mobile that everyone already enjoys on their computers. Rather than comply reasonably, Apple have found a way to throw their toys out of the pram.

As of the next update to iOS, users in the EU will no longer have homescreen apps. Those web apps will now launch in a browser window. Presumably they’ll also lose the ability to send push notifications: being a homescreen app was a prerequisite for that functionality.

This is a huge regression that only serves to harm and confuse users.

I have a website about traditional Irish music. Guess where a significant amount of the audience is based? That’s right: Ireland. In the European Union.

There is no native app for The Session, but you can install it on your phone nonetheless. Lots of people have done that. After a while they forget that they didn’t install it from an app store: it behaves just like any other app on their homescreen.

That’s all about to change. I’m going to get a lot of emails from confused users wondering why their app is broken, now opening in a regular browser window. And I won’t be able to do anything about it, other than to tell them to take it up with Apple.

Presumably Apple is hoping that users will direct their anger at the EU commission instead. They’re doing their best to claim that they’re being forced to make this change. That’s completely untrue. A lie:

This is emphatically not required by the EU’s Digital Markets Act (DMA). It’s a circumvention of both the spirit and the letter of the Act, and if the EU allows it, then the DMA will have failed in its aim to allow fair and effective browser and web app competition.

Throughout all their communications on this topic, Apple are sticking to their abuser logic:

Look what you made me do!

This is going to hurt me more than it hurts you.

Apple’s petulant policy of malicious compliance is extremely maddening. What they’re about to do to users in the EU is just nasty.

This is a very dark time for the web.

I feel bad for the Safari team. They’ve been working really hard recently to make Safari a very competitive browser with great standards support with a quicker release cycle than we’ve seen before. Then it all gets completely torpedoed at the level of the operating system.

I really hope that Apple won’t get away with their plan to burn down web apps on iOS in the EU. But hope isn’t enough. We need to tell the EU commission how much damage this will do.

If you’ve ever built a web app, then your users will suffer. Remember, it’s a world wide web, including the European Union.

Create a PDF with the following information:

  • Your company’s name.
  • Your name.
  • That your company operates or services the EU.
  • How many users your service has in the EU (approximately).
  • The level of impact this will have on your business.
  • The problems this will cause your business.
  • Whether or not the submission is confidential.

The submission can be as short or long as you want. Send it to [email protected], ideally before Monday, February 19th.

I know that’s a lot to ask of you on your weekend, but this really matters for the future of the web.

At the very least, I encourage to get involved with the great work being done by the Open Web Advocacy group. They’re also on Discord.

Please don’t let Apple bully an entire continent of users.

Stuck in the dock

I was impressed with how Safari now allows you to add websites to the dock:

It feels great to have websites that act just like other apps. I like that some of the icons in my dock are native, some are web apps, and I mostly don’t notice the difference.

Trys liked it too:

For all intents and purposes, this is a desktop application created without a single line of Swift or Objective-C, or any heavy Electron wrappers.

Oh, and the application can work offline! Service workers, and browser storage are more than stable enough to handle a variety of offline loading patterns. These are truly exciting times to be building for the web!

There was one aspect that I was particularly pleased with. External links:

Links within a Safari-installed web app respect your default browser choice.

Excellent! Except it’s no longer true. At least not in some cases. The behaviour is inconsisent but I’m running the latest version of Safari on the latest version of Sonoma, and now external links in a Safari-installed web app are broken. They just stay in the same application.

I thought maybe it was related to whether the website’s manifest file has the display value set to “standalone” rather than, say, “minimal ui”. Maybe the “standalone” instruction is being taken literally? But even when I change the value I’m still getting the broken behaviour.

This may sound like a small thing, but it completely changes the feel of using the web app. Instead of feeling like “I’m using an app that just happens to be on the web”, it now feels like “I’m using a web browser but with fewer features.”

I’ve been loving having Mastodon as a standalone app in my dock. It used to be that if I clicked on a link in a Mastodon post, it would open in my browser of choice (Firefox) where I could then bookmark it, or do any other tasks that my browser offers me. Now if I click on a link in Mastodon, I’m stuck in the same “app”. It feels horribly stifling.

I can right-click on a link and get options that still keep me in the same app, like “Open link” or “Open Link in New Window.” To actually open the link in my web browser, I have to select “Copy Link”, then go to my web browser, open a new tab, and paste the link in there.

This is broken. I hope it isn’t intentional. Maybe I’m just at the receiving end of some weird glitch. If this stays this way, I’ll probably just remove the Safari-installed web apps from my dock. They feel pointless if they’re just roach motels.

I’d love to file a bug for this, but this isn’t a Webkit bug, it’s a Safari bug (and the Webkit bug tracker is at pains to point out that Webkit and Safari are not the same thing). But have you ever tried to file a bug with Apple? Good luck!

Anyway, I sincerely hope that this change will be walked back. Otherwise websites in the dock are dead in the water.

button invoketarget=”share”

I’ve written quite a bit about how I’d like to see a declarative version of the Web Share API. My current proposal involves extending the type attribute on the button element to support a value of “share”.

Well, maybe a different attribute will end up accomplishing what I want! Check out the explainer for the “invokers” proposal over on Open UI. The idea is to extend the button element with a few more attributes.

The initial work revolves around declaratively opening and closing a dialog, which is an excellent use case and definitely where the effort should be focused to begin with.

But there’s also investigation underway into how this could be away to provide a declarative way of invoking JavaScript APIs. The initial proposal is already discussing the fullscreen API, and how it might be invoked like this:

button invoketarget="toggleFullsceen"

They’re also looking into a copy-to-clipboard action. It’s not much of a stretch to go from that to:

button invoketarget="share"

Remember, these are APIs that require a user interaction anyway so extending the button element makes a lot of sense.

I’ll be watching this proposal keenly. I’d love to see some of these JavaScript cowpaths paved with a nice smooth coating of declarative goodness.

Lost in calculation

As well as her personal site, wordridden.com, Jessica also has a professional site, lostintranslation.com.

Both have been online for a very long time. Jessica’s professional site pre-dates the Sofia Coppola film of the same name, which explains how she was able to get that domain name.

Thanks to the internet archive, you can see what lostintranslation.com looked like more than twenty years ago. The current iteration of the site still shares some of that original design DNA.

The most recent addition to the site is a collection of images on the front page: the covers of books that Jessica has translated during her illustrious career. It’s quite an impressive spread!

I used a combination of CSS grid and responsive images to keep the site extremely performant. That meant using a combination of the picture element, source elements, srcset attributes, and the sizes attribute.

That last part always feels weird. I have to tell the browser what sizes the images will displayed at, which can change depending on the viewport width. But I’ve already given that information in the CSS! It feels weird to have to repeat that information in the HTML.

It’s not just about the theoretical niceties of DRY: Don’t Repeat Yourself. There’s the very practical knock-on effects of having to update the same information in two places. If I update the CSS, I need to remember to update the HTML too. Those concerns no longer feel all that separate.

But I get it. Browsers use a look-ahead parser to start downloading images as soon as possible, so I understand why I need to explicitly state what size the images will be displayed at. Still, it feels like something that a computer should be calculating, not something for a human to list out by hand.

But wait! Most of the images on that page also have a loading attribute with a value of “lazy”. That tells browsers they don’t have to download the images immediately. That sort of negates the look-ahead parser.

That’s why the HTML spec now includes a value for the sizes attribute of “auto”. It’s only supposed to be used in conjunction with loading="lazy" (otherwise it means 100vw).

Browser makers are on board with this. You can track the implementation progress for Chromium, WebKit, and Firefox.

I would very much like to see this become a reality!

A memex in every web browser

When Mathew Modine’s character first shows up in Christopher Nolan’s Oppenheimer, I figured the rest of the cinema audience wouldn’t have appreciated me shouting out “VANNEVAR BUSH IN THE HOUSE!” so I screamed it on the inside.

The Manhattan Project was not his only claim to fame or infamy. When it comes to the world we now live in, Bush’s idea of the memex has been almost equally influential. His article As We May Think became a touchstone for Douglas Engelbart and later Tim Berners-Lee.

But as Matt Thompson points out:

…the device he describes does not resemble the internet or anything I’ve ever found on it.

Then he says:

What Bush was describing sounds to me like what you might get if you turned a browser history — the most neglected piece of the software — into a robust and fully featured machine of its own. It would help you map the path you charted through a web of knowledge, refine those maps, order them, and share them

Yes! This!! I 100% agree with the description of browser history as “the most neglected piece of the software.” While I wouldn’t go as far as Chris when he says web browsers kind of suck, I’m kind of amazed that there hasn’t been more innovation and competition in this space.

If anything we’ve outsourced the management of our browsing history to services like Delicious and Pinboard, or to tools like Obsidian and Roam Research. Heck, the links section of my website is my attempt to manage and annotate my own associative trails.

Imagine if that were baked right into a web browser. Then imagine how beautiful such a rich source of data might look.

Like Matt says:

I don’t think anything like this exists. So Bush’s essay still transfixes me.

Making the Patterns Day website

I had a lot of fun making the website for Patterns Day.

If you’re interested in the tech stack, here’s what I used:

  1. HTML
  2. CSS

Actually, technically it’s all HTML because the styles are inside a style element rather than a separate style sheet, but you know what I mean. Also, there is technically some JavaScript but all it does is register a service worker that takes care of caching and going offline.

I didn’t use any build tools. There was no pipeline. There is no node_modules folder filling up my hard drive. Nothing was automated. The website was hand-crafted the long hard stupid way.

I started with the content. I wrote out the words and marked them up with the most appropriate HTML elements.

A screenshot of an unstyled web page for Patterns Day.

Time to layer on the presentation.

For the design, I turned to Michelle for help. I gave her a brief, describing the vibe of the conference, and asked her to come up with an appropriate visual language.

Crucially, I asked her not to design a website. Instead I asked her to think about other places where this design language might be used: a poster, social media, anything but a website.

Partly I was doing this for my own benefit. If you give me a pixel-perfect design for a web page and tell me to code it up, either I won’t do it or I won’t enjoy it. I just don’t get any motivation out of that kind of direct one-to-one translation.

But give me guardrails, give me constraints, give me boundary conditions, and off I go!

Michelle was very gracious in dealing with such a finicky client as myself (“Can you try this other direction?”, “Hmm… I think I preferred the first one after all!”) She delivered a colour palette, a type scale, typeface choices, and some wonderful tiling patterns …it is Patterns Day after all!

With just a few extra lines of CSS, the basic typography was in place.

A screenshot of the web page for Patterns Day with web fonts applied.

I started layering on the colours. Even though this was a one-page site, I still made liberal use of custom properties in the CSS. It just feels good to be able to update one value and see the results, well …cascade.

A screenshot of the web page for Patterns Day with colours added.

I had a lot of fun with the tiling background images. SVG was the perfect format for these. And because the tiles were so small in file size, I just inlined them straight into the CSS.

By this point, I felt like I was truly designing in the browser. Adjusting spacing, playing around with layout, and all that squishy stuff. Some of the best results came from happy accidents—the way that certain elements behaved at certain screen sizes would lead me into little experiments that yielded interesting results.

I’m not sure it’s possible to engineer that kind of serendipity in Figma. Figma was the perfect tool for exploring ideas around the visual vocabulary, and for handing over design decisions around colour, typography, and texture. But when it comes to how the content is going to behave on the World Wide Web, nothing beats a browser for fidelity.

A screenshot of the web page for Patterns Day with some changes applied.

By this point I was really sweating the details, like getting the logo just right and adjusting the type scale for different screen sizes. Needless to say, Utopia was a godsend for that.

I was also checking back in with Michelle to get her take on design decisions I was making.

I could’ve kept tinkering but the diminishing returns were a sign that it was time to put this out into the world.

A screenshot of the web page for Patterns Day with the logo in place.

It felt really good to work on a web page like this. It felt like I was getting my hands into the soil of the web. I don’t think it’s an accident that the result turned out to be very performant.

Getting hands-on like this stops me from getting rusty. And honestly, working with CSS these days is a joy. There’s such power to be had from using var() in combination with functions like calc() and clamp(). Layout is a breeze with flexbox and grid. Browser differences are practically non-existent. We’ve never had it so good.

Here’s something I noticed about my relationship to CSS; my brain has finally made the switch to logical properties. Now if I’m looking at some CSS and I see left, right, top, or bottom, it looks like a bug to me. Those directional properties feel loaded with assumptions whereas logical properties feel much more like working with the grain of the web.

Websites in the dock

I updated my Mac to the newest operating system, Sonoma. I did this to try out the new “add to Dock” feature in Safari. It’s like the “add to Homescreen” action on iOS.

Before I get into what’s good, I have to start by ranting about a big problem on both desktop and mobile: discovery.

First of all, you have to know that a square with an arrow sticking out of it means “share”.

Okay, I can buy it. It’s no better or worse than the three horizontal lines of a hamburger icon, or the three horizontal dots of a kebab icon. And whereas the hamburger and kebab icons are used as a catch-all cupboard to sweep all your rubbish into, at least this icon has a specific meaning. It means share, right?

Well, it used to. But now it’s also home to “add to Homescreen” and “add to Dock”. Neither of those actions are sharing.

Stretching semantics, I suppose you could say you’re sharing something with yourself.

Anyway, this is the biggest issue with progressive web apps on both iOS and Mac. Regardless of how well they work, there’s not much point if most people don’t know the option exists.

(Update: Jen rightly points out that you can also get to “add to Dock” from the file menu. Doh! How did I miss that‽)

Discovery aside, I was interested to see how Safari handles web apps on desktop compared to how Chrome does it.

I’ve had one or two web apps in my dock for a while, installed through Chrome. Google Meet is one of them. I use it quite a bit, and honestly it feels no different to using a native desktop app.

One annoyance is that the Chrome browser also automatically launches whenever I launch the Google Meet icon in my dock. This wouldn’t matter if Chrome were my default browser, but I use Firefox. So whenever I’m using the Google Meet web app, there’s a Google Chrome icon hanging around in my dock, saying “gizza job, I can do that.”

I opened Google Meet in Safari and then selected “add to Dock” from the square with an arrow sticking out of it. Then I quit Safari. I was delighted to see that when I launched the Google Meet web app from the dock, it didn’t automatically launch Safari! Excellent!

Even better, links within a Safari-installed web app respect your default browser choice. What I mean is, previously when I had Google Meet installed via Chrome, if I clicked an external link in Google Meet it opened in Chrome. But now with the Google Meet installed via Safari, external links open in Firefox, my browser of choice. Very good!

But the Safari-installed version of Google Meet is, alas, over-zealous with permissions. I have to grant access to my microphone and camera every single time I launch it. Previously—with the version installed via Chrome—it remembered my choice.

Now I don’t know if the behaviour in the Safari-installed version is a deliberate choice made for security reasons, or if it’s a bug. I suspect it’s a bug. After all, on iOS you get access to more permissions if a site is added to the homescreen—it’s the only way to ask for permissions for notifications, for example.

I added a few more sites to my dock: mastodon.social and thesession.org. They both work really well as standalone apps.

Interestingly, if I click a link to thesession.org from within the mastodon.social standalone web app (or the other way around), the link opens in my default browser. So the web apps don’t “own” the domains. That’s fine, although I wonder if it violates the principle of least surprise—perhaps the expectation is that the installed web app is the preffered owner of that link.

I also tried adding Google Calendar to my dock. Ironically, I can only do this with Safari. For some reason, Google have chosen not to make their calendar a progressive web app …which means there’s no option to install it from Google Chrome.

They’re missing a trick there. It’s the perfect candidate for being a standalone app.

Actually, I take that back. Gmail is the perfect candidate for being a standalone app …and yet it’s not a progressive web app. Very odd!

With Safari, you can add any website to the dock. It doesn’t need to be a progressive web app. But the installation experience works best if there’s a manifest file pointing to some nice icons.

As it turned out, Google Calendar runs like an absolute dog in Safari (and therefore as a Safari-installed web app). Before you cry conspiracy, note that it runs absolutely fine in Firefox. I know because I use it in Firefox every day. But I can’t add it to my dock from Firefox because Mozilla turned their back on progressive web apps a while back. A bad decision.

Google Calendar isn’t the only thing that runs slowly in Safari’s engine. This page on The Session has a very large DOM and a badly-coded in-page search (I know, I know, I need to improve it). It works fine in other browsers but Safari struggles mightily.

(Update: I tried using Google Calendar from Safari again and it seems to be running just fine now. Maybe I caught it on a bad day? In any case, I’ve added it to the dock now and it’s feeling good as a standalone app.)

Still, aside from a few annoying little things around permissions and performance—and the situation with discovery—it feels great to have websites that act just like other apps. I like that some of the icons in my dock are native, some are web apps, and I mostly don’t notice the difference.

I wonder if there’s much point using wrappers like Electron any more? I feel like they were mostly aiming to get that parity with native apps in having a standalone application launched from the dock.

Now all you need is a website.

Button types

I’ve been banging the drum for a button type="share" for a while now.

I’ve also written about other potential button types. The pattern I noticed was that, if a JavaScript API first requires a user interaction—like the Web Share API—then that’s a good hint that a declarative option would be useful:

The Fullscreen API has the same restriction. You can’t make the browser go fullscreen unless you’re responding to user gesture, like a click. So why not have button type=”fullscreen” in HTML to encapsulate that? And again, the fallback in non-supporting browsers is predictable—it behaves like a regular button—so this is trivial to polyfill.

There’s another “smell” that points to some potential button types: what functionality do browsers provide in their interfaces?

Some browsers provide a print button. So how about button type="print"? The functionality is currently doable with button onclick="window.print()" so this would be a nicer, more declarative way of doing something that’s already possible.

It’s the same with back buttons, forward buttons, and refresh buttons. The functionality is available through a browser interface, and it’s also scriptable, so why not have a declarative equivalent?

How about bookmarking?

And remember, the browser interface isn’t always visible: progressive web apps that launch with minimal browser UI need to provide this functionality.

Šime Vidas was wondering about button type="copy” for copying to clipboard. Again, it’s something that’s currently scriptable and requires a user gesture. It’s a little more complex than the other actions because there needs to be some way of providing the text to be copied, but it’s definitely a valid use case.

  • button type="share"
  • button type="fullscreen"
  • button type="print"
  • button type="bookmark"
  • button type="back"
  • button type="forward"
  • button type="refresh"
  • button type="copy"

Any more?