Spicy Sections is an experiment in which "good ol’ well supported HTML" is conditionally presented with different affordances; either as a tab set, or with independent collapses ("disclosure widgets") or exclusive collapses ("accordions").
Developed as part of explorations in Open UI, following some extensive research toward potentially standardizing "tabs", Spicy Sections uses a custom element wrapper (<spicy-sections>
) to wrap and progressively enhance heading and content pairs so that they can be presented with any of these affordances.
If you want to see something working, check out this demonstration and resize your browser window to see how the afforances change.
Import the custom element.
<script type="module" src="/SpicySections.js"></script>
Markup sections by wrapping good ol’ HTML with the <spicy-sections>
element.
<spicy-sections>
<!-- any heading here, followed by an element containing content -->
<h2>Ingredients</h2>
<div>A list of ingredients</div>
<!-- repeat the pattern... -->
<h2>Instructions</h2>
<div>A list of instructions</div>
</spicy-sections>
Spicy Sections lets authors to express when affordances should be presented;
using either an attribute or a CSS Custom Property.
The available affordancs are collapse
(summary/details like), tab-bar
(tabs like) and exclusive-collapse
(single select accordion like).
To present an afforance of collapse
on a smaller screen, you might use [screen and (max-width: 800px)] collapse
.
spicy-sections {
--const-mq-affordances:
[screen and (max-width: 40em) ] collapse |
[screen and (min-width: 60em) ] tab-bar
;
display: block;
border: thick var(--demo-border, solid black);
border-width: thick 0;
padding: 1em;
}
Note: The CSS Custom Property is only read only.
In normal content, markup can contain id
attributes which will be scrolled to and focus-navigation set to the first matching element when the URL contains a matching #
(hash). This element carries this idea forward and will activate tabs accordingly, whether that hash matches the heading, or content within it.
This work is developed as part of explorations in Open UI, following some extensive research toward potentially standardizing "tabs". We believe that the concept of an element which could conditionally present different interaction affordances, in much the same way that scroll panes do in the web platform today could be an important new concept allowing us to bring not just tabs, but several UI concepts to the platform.
However, there is a lot to think about - not the least of which is whether developers will adopt something like this. This custom element provides a way for us to roughly evaluate the concepts, learn more about the problem and see.
Please check it out: Play with it. Build something useful. Ask questions, show us your uses, give us feedback about what you like or don't. We'll also be watching the HTTP Archive data for adoption, and adoption is a good signal we've got something right and will help drive furtherance and priority of standards work.
There is also a post where you can read more on the concept
This element builds on progressive enhancement.
The resolution of module scripts is non-blocking and the display of different affordances can be quite different.
It is appealing then to minimize FOUC, and the one instrument that custom elements and CSS currently provide is :not(:defined)
.
Unfortunately, using :not(:defined)
to hide content fails to progressively enhance if the script fails to either load or execute;
this is an [known issue](see whatwg/html#6231).
For now, you can improve the experience by hiding the content when the script is enabled, and then race the definition.
// dynamically inject styles to hide the content
let style = document.head.appendChild(
Object.assign(document.createElement('style'), {
textContent: 'spicy-sections:not(:defined) { visibility: hidden; }'
})
);
setTimeout(() => style.remove(), 1000);