Progressive disclosure defaults

When I wrote about my time in Amsterdam last week, I mentioned the task that the students were given:

They’re given a PDF inheritance-tax form and told to convert it for the web.

Rich had a question about that:

I’m curious to know if they had the opportunity to optimise the user experience of the form for an online environment, eg. splitting it up into a sequence of questions, using progressive disclosure, branching based on inputs, etc?

The answer is yes, very much so. Progressive disclosure was a very clear opportunity for enhancement.

You know the kind of paper form where it says “If you answered no to this, then skip ahead to that”? On the web, we can do the skipping automatically. Or to put it another way, we can display a section of the form only when the user has ticked the appropriate box.

This is a classic example of progressive disclosure:

information is revealed when it becomes relevant to the current task.

But what should the mechanism be?

This is an interaction design pattern so JavaScript seems the best choice. JavaScript is for behaviour.

On the other hand, you can do this in CSS using the :checked pseudo-class. And the principle of least power suggests using the least powerful language suitable for a given task.

I’m torn on this. I’m not sure if there’s a correct answer. I’d probably lean towards JavaScript just because it’s then possible to dynamically update ARIA attributes like aria-expanded—very handy in combination with aria-controls. But using CSS also seems perfectly reasonable to me.

It was interesting to see which students went down the JavaScript route and which ones used CSS.

It used to be that using the :checked pseudo-class involved an adjacent sibling selector, like this:

input.disclosure-switch:checked ~ .disclosure-content {
  display: block;
}

That meant your markup had to follow a specific pattern where the elements needed to be siblings:

<div class="disclosure-container">
  <input type="checkbox" class="disclosure-switch">
  <div class="disclosure-content">
  ...
  </div>
</div>

But none of the students were doing that. They were all using :has(). That meant that their selector could be much more robust. Even if the nesting of their markup changes, the CSS will still work. Something like this:

.disclosure-container:has(.disclosure-switch:checked) .disclosure-content

That will target the .disclosure-content element anywhere inside the same .disclosure-container that has the .disclosure-switch. Much better! (Ignore these class names by the way—I’m just making them up to illustrate the idea.)

But just about every student ended up with something like this in their style sheets:

.disclosure-content {
  display: none;
}
.disclosure-container:has(.disclosure-switch:checked) .disclosure-content {
  display: block;
}

That gets my spidey-senses tingling. It doesn’t smell right to me. Here’s why…

The simpler selector is doing the more destructive action: hiding content. There’s a reliance on the more complex selector to display content.

If a browser understands the first ruleset but not the second, that content will be hidden by default.

I know that :has() is very well supported now, but this still makes me nervous. I feel that the more risky action (hiding content) should belong to the more complex selector.

Thanks to the :not() selector, you can reverse the logic of the progressive disclosure:

.disclosure-content {
  display: block;
}
.disclosure-container:not(:has(.disclosure-switch:checked)) .disclosure-content {
  display: none;
}

Now if a browser understands the first ruleset, but not the second, it’s not so bad. The content remains visible.

When I was explaining this way of thinking to the students, I used an analogy.

Suppose you’re building a physical product that uses electricity. What should happen if there’s a power cut? Like, if you’ve got a building with electric doors, what should happen when the power is cut off? Should the doors be locked by default? Or is it safer to default to unlocked doors?

It’s a bit of a tortured analogy, but it’s one I’ve used in the past when talking about JavaScript on the web. I like to think about JavaScript as being like electricity…

Take an existing product, like say, a toothbrush. Now imagine what you can do when you turbo-charge it with electricity: an electric toothbrush!

But also consider what happens when the electricity fails. Instead of the product becoming useless you want it to revert back to being a regular old toothbrush.

That’s the same mindset I’m encouraging for the progressive disclosure pattern. Make sure that the default state is safe. Then enhance.

Have you published a response to this? :

Responses

Jon Lunman

@adactio Completely agree. Another analogy I like is the elevator vs escalator. If electricity fails, an elevator becomes useless. But an escalator just becomes stairs.

Also, the opposite of “display: none” isn’t always “display: block”. There could be other rules in the stylesheet that make it use “flex” or “grid”, perhaps at different breakpoints. So having the disclosure styles set “display: none” via “:not(:has(…))” is safer than the inverse.

# Posted by Jon Lunman on Wednesday, March 20th, 2024 at 5:01pm

2 Shares

# Shared by Giana ✨ on Thursday, March 21st, 2024 at 1:00am

# Shared by Ryan Townsend on Thursday, March 21st, 2024 at 1:01am

8 Likes

# Liked by Evil Jim O’Donnell on Thursday, March 21st, 2024 at 12:58am

# Liked by Tyler Sticka on Thursday, March 21st, 2024 at 12:58am

# Liked by Giana ✨ on Thursday, March 21st, 2024 at 12:58am

# Liked by John P. Green on Thursday, March 21st, 2024 at 12:59am

# Liked by Carlton Gibson 🇪🇺 on Thursday, March 21st, 2024 at 12:59am

# Liked by Jon Lunman on Thursday, March 21st, 2024 at 12:59am

# Liked by Ryan Townsend on Thursday, March 21st, 2024 at 12:59am

# Liked by nrk on Thursday, March 21st, 2024 at 1:00am

Related posts

Speaking at Web Day Out

Have you got the perfect talk for this event? Let me know!

Announcing Web Day Out

A one-day event all about what you can in web browsers today: Brighton, March 12th, 2026. Tickets are just £225+VAT!

Manual ’till it hurts

Try writing your HTML in HTML, your CSS in CSS, and your JavaScript in JavaScript.

Control

Trying to understand a different mindset to mine.

Bugblogging

Also, tipblogging.

Related links

The Great CSS Expansion | Butler’s Log

Web development follows a familiar cycle. First we glue together a solution with whatever we have — JavaScript, image hacks, Flash, anything. Then the platform matures, and CSS or HTML eventually makes that same workaround native. Rounded corners, custom fonts, smooth scrolling, sticky positioning: all of these started as JavaScript-heavy hacks before CSS turned them into a single declaration.

We are in another one of those transition moments. A new wave of long-requested CSS features is finally landing, and many of them are explicitly designed to replace patterns that used to require JavaScript. Not as approximations — as first-class platform primitives that handle the edge cases, run in the right thread, and need zero dependencies.

Tagged with

Reduce the JS Workload with no- or lo-JS options

This is an excellent one-stop shop of interface patterns:

This is an organic collection of common JS patterns that can be replaced with just HTML, CSS, and no, or very low, JS. As HTML and CSS continue to mature, this collection should expand.

Tagged with

CSS in 2026: The new features reshaping frontend development - LogRocket Blog

Jemima runs through just some of the exciting new additions to CSS:

Replacing 150+ lines of JavaScript with just a few CSS features is genuinely wild. We’re able to achieve the same amount of complexity that we’ve always had, but now it’s a lot less work to do so.

And Jemima will be opening the show at Web Day Out in Brighton on the 12th of March if you want to hear more of this!

Tagged with

699: Jeremy Keith on Web Day Out – ShopTalk

This episode of the Shop Talk Show is the dictionary definition of “rambling” but I had a lot of fun rambling with Chris and Dave!

Tagged with

NoLoJS: Reducing the JS Workload with HTML and CSS - Web Performance Calendar

You might not need (much) JavaScript for these common interface patterns.

While we all love the power and flexibility JS provides, we should also respect it, and our users, by limiting its use to only what it needs to do.

Yes! Client-side JavaScript should do what only client-side JavaScript can do.

Tagged with

Previously on this day

6 years ago I wrote Local

Think global, act local. In fact, just stay at home.

7 years ago I wrote CSS custom properties in generated content

They said it couldn’t be done.

15 years ago I wrote Reflection

A little bit of navel-gazing on this year’s geekout in Austin.

21 years ago I wrote Going up

My latest submission to the Mirror Project does quite a job at capturing the spirit of South by SouthWest.

21 years ago I wrote What's hot in Austin

Plenty of people have been writing about the contents of individual panels and presentations from South by SouthWest. I thought it might be interesting to give a broader overview and take a look at some recurring themes.

23 years ago I wrote No War On Monaghan

Let us hope that in the fog of war, no bombs intended for Iraq are used to bomb county Monaghan in Ireland.

24 years ago I wrote Pong: The Text-Based Game

There are many online emulators of classic arcade games.