There is another, however. An element whose state depends on that of other elements, and it's even more general. A form element moves between :valid and :invalid based on its inputs, allowing us to use form:{in}valid with any of the descendant, child, sibling or adjacent combinators. A hidden required checkbox is sufficient. Radio inputs work too (tip: you can clear radios back to :invalid with an input type="reset").
The really, truly monstrous part of this, however, is that the associated input doesn't even have to be a child of the form. Using the form attribute (i.e., <input form="whatever">) means they can be anywhere on the page, and they can themselves be hidden and targeted from somewhere else on the page again, with a <label> element.
I once documented the horrifying potential of this in a company wiki, along with a lovely modal slideover that was wrapped in a <form> element and transitioned to visible based on form:valid via a hidden required radio button, and whose backdrop was a reset button, and this was rightly labelled NSFW and banned by popular acclaim from ever appearing in our HTML.
For further inspiration, I recommend reading the entirety of the HTML Living Standard and the current CSS Snapshot about once a year. I'm currently excited about the opportunities for [open], particularly with the <details> and <dialog> elements.
My biggest disappoints of the year are with <datalist>, unfortunately, which is so mysteriously half-baked it's practically worthless; followed by Webkit dragging its heels over <dialog> support and customized built-in element support.
<form id="foo"></form>
<div class="target">...</div>
<div class="doesnt-matter">
<input type="checkbox" form="foo" required>
</div>
You can target `.target` with: #foo:invalid ~ .target
Because the checkbox is required/unchecked. (And the inverse if it’s checked using `:valid`.)1. The input element(s) can now be anywhere on the page. You can, for example, use adjacency to style a control button, without torturing your HTML to squeeze the button into being a sibling of whatever it's activating.
2. You can use form:valid as a parent selector i.e. as a wrapper element. You couldn't use an input:checked as a parent selector because the <input> element's content model is "nothing" i.e. there are no child elements under an <input>, but you sure can with a <form>. The result is less brittle to code-rot.
This is extremely freeing and allows you to use, for example, a :checked adjacency for styling an activation button and a wrapper element (a form:valid) for the large complex component (modal? drop-down? slideover? video content?) that it activates. Heck, it can even be an iframe. Here's the bones of an example:
https://codepen.io/inopinatus/pen/vYxrOeR?editors=1100
There are two downsides:
1. Behaviour of some elements is different inside a <form>. Most obviously, don't nest a <form> inside with the same scope.
2. Your HTML maybe become a mess of scattered <form>, <input>, and <label> elements that many other developers will look at and say "WTF was this person thinking" and "what does this button do?"
I loved this when I first learned it but was convinced by some accessibility-minded people that it usually doesn’t accurately convey the state to assistive technologies. Instead, use a <button> with an appropriate ARIA attribute conveying a state (pressed, expanded, etc.) that’s toggled by JavaScript. Or use <details> <summary> elements if it’s a disclosure widget.
A typical failure scenario is toggles in forms, that appeared to have been set by button-press, but have only changed cosmetically. The silent errors that creep in this way are painful to unravel. Relying on input element state avoids not merely the tail wagging the dog, but the tail wagging only itself because TypeError: dog is not an object.
When overused, or used in a cargo-cult fashion, ARIA is also notorious for making pages less accessible, and unfortunately this is frequently the case.
If you want to build accessible pages, then write simple and semantic HTML. Out of the box this is likely to be fairly accessible from the go. Then annotate using the minimum of JS to set the minimum of ARIA attributes that clearly improve accessibility, and if someone's relying on ARIA then you can, by definition, assume that JS is available, since it is (I'll reiterate this) right there in the name, and what's more if it it's broken or unavailable, then your fallback plain-old-HTML retains some semblance of utility and accessibility for assisted and non-assisted visitors alike.
The path through the swamp doesn't have to be smothering it in concrete.
Yes, like aria-expanded="true" on a <button> that opens a custom disclosure widget instead of using the CSS-only :checked ~ .foo hack I was responding to. The actual hiding/revealing of the related content can still be done with CSS alone using [aria-expanded="true"].
Here's a bunch of checkbox hack examples [0]. The custom radio buttons and checkboxes and the push toggles are fine (the use-case is fine, I didn't closely examine everything about the implementations), the rest should be done some other way that probably involves buttons with JavaScript.
<label>
<input type="checkbox">
<span class="label">Some label</span>
</label>
input[type="checkbox"] {
appearance: none;
/* ... */
}
input[type="checkbox"]:checked { /* ... */ }
input[type="checkbox"]:indeterminate { /* ... */ }
input[type="checkbox"]:disabled { /* ... */ }There are still more things you can do within a label. This is a good article on improving methods of visually hiding the actual inputs.
https://www.sarasoueidan.com/blog/inclusively-hiding-and-sty...
Get comfortable navigating web pages using the various functionalities available (the ability to scan only headlines, for example).
I also open the accessibility tab in the web inspector in Chrome or Firefox to see the accessibility tree, which is like a simplified version of the DOM tree that gets exposed to assistive technologies.
- you want to at least test with JAWS, NVDA and VoiceOver
- automated tooling isn’t great and you probably need to manually verify things
- axe does help spot problems just don’t rely on it completely
That doesn't mean that Orca doesn't do a good job or that it works very differently than other screen readers. The A11ySupport site [1] does have some data on Orca's support for specific code. For instance, it fully supports the aria-expanded attribute but has no support for description list elements.
If you have an iOS or Android device, using their built-in screen readers might be more instructive.
[0] https://webaim.org/projects/screenreadersurvey8/ [1] https://a11ysupport.io/
It depends on what the stakes are but if you’re consistently confirming things work with just one screen reader, that makes most of the difference.
NVDA with Firefox or Chrome is probably the best to test with; it’s free, probably the most common choice now, and it has a lot in common with JAWS. But if testing with Windows is inconvenient, testing VoiceOver with Safari for Mac is still very useful, in part because it’s very similar to VoiceOver on iOS.
Simple things like 'outline' on focused or activated elements are easy things to pick up. Elements that are changed by JavaScript should not only change on a mouseclick or touch, but also on pressing Enter or ESC.
This seems 100% backward. What happened to tabindex? My experience has been that HTML+CSS (I wouldn't even call most of these "hacks", they are normal intended features of CSS) is much more likely to be navigable by keyboard than anything using JS.
What kind of state do you mean? Could you give an example?
<label> <input class”real-check” type=checkbox /> <span class”fake-check”/> <strong>this is the text label</strong> </label>
- visually hide the checkbox - use a selector like “.real-check:checked + .fake-check and draw whatever you want in the fake check. - the fake check and the text label are both inside the label element. Interacting with them will toggle the check box.
except not really because there's no CSS equivalent for rowspan/colspan. wish they would add this, it'd also be useful for making tables responsive.
Thanks for the attr tip though, somehow that one had completely slipped by me!
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_La...
You couldn't control colspan/rowspan with CSS before, only HTML, which isn't responsive. (Ignoring the `column-span` property which isn't very flexible.)
> I have to rebuild the table layout
The "layout" meaning the visual appearance, or the markup?
There IS a "css equivalent of colspan/rowspan for table-layouts," put another way--a way to make a column or row (<td> or <tr>) appear to span multiple columns or rows. It can be achieved with flexbox or with CSS grid.
You don't need to change anything about the markup (HTML) to make this work.
> to make it responsive
"Responsive" as in "responds to the size of the viewport?" The colspan and rowspan HTML attributes do not do that. They have the same value regardless of the size of the viewport. CSS (with media queries) can respond.
> the column-span property is unrelated to tables
Incorrect, it is certainly related, and works there just fine (along with other in-flow block-level elements).
there is no equivalent of colspan/rowspan in css, as in not some convoluted workaround but doing exactly the same thing only via css instead of a html attribute. I don't really think I've been that unclear about this being what I want to be added. I was not talking about changing the html, but turning a table into a grid is more than just doing table {display:grid;}, you have to rebuild the whole structure, i.e. layout of the table using css. if i could use rowspan (as in actual literal rowspan not some other workaround), i would not have to do this.
yes responsive as in hide one or multiple rows of table cells on some screen resolutions to make things fit better. as I've previously said.
>Incorrect, it is certainly related, and works there just fine (along with other in-flow block-level elements).
it does not manipulate the amount of table rows a table-cell-display element takes up.
Yes! What's wrong with this approach? We have a lot of old HTML attributes that were used for styling that have been replaced by CSS.
> Yes, I know there are workarounds using flexbox, grid, etc
It's not a "workaround!" You're controlling the appearance using the styling language. What's wrong with this example?
> "responsive" as in hide rows of table cells on some resolutions to make things fit better
HTML attributes (like colspan) don't do this! CSS does.
> it does not manipulate the amount of table rows a table-cell-display element takes up
I know, I mentioned it before :) it's related though.
With HTML attributes the layout is tied to your markup. Like you said it's more than table {display:grid;} but is adding .colspan-2 a lot more cruft than adding [colspan=2]?
All I want is two new css properties that mirror the behaviour of the colspan/rowspan html tag attributes. for things that are display:table-cell; (i.e. <td> tags). So that you can do light table manipulation without having to use grid or flexbox, which adds extra cruft that would be made redundant in situations where you want to do something very simple like hide a row.
One last time, i understand this is possible with a whole bunch of css to recreate the entire structure of the table using grid, but if we had these two css properties, you would not need to do all this. it would be more convenient.
If you still do not understand what I was trying to say, I'm sorry, I give up. This conversation has started to genuinely frustrate me at this point.
- checkbox/radio inputs can be used to toggle state with `:checked ~ .foo` selectors
- `:focus` (and now `:focus-within`, and `:active` though it’s less useful) can be used similarly, but also allow child selection [Edit to add: `tabindex="-1"` makes anything focusable with a pointer input, but doesn’t capture keyboard tab or iteration with assistive tools]
- `:target` can be used similarly, paired with fragment links [Edit to add: but beware history entries, this can be a poor UX]
- `<label>` can be used to not only set those states but also trigger scrolls (including within `scroll-snap` parents) without creating navigation history entries
- the `attr()` function can be used to reference server-dynamic HTML data for display with `content` in pseudo-elements
- I have to assume CSS animations are adopted widely enough that people aren’t using JS for that where it isn’t needed; but you can also use declarative, even interactive, animations in SVG
- speaking of which, inline SVG (even `<use>` references) are part of the CSS cascade, and you can change any CSS-addressable property with `currentColor`
- and you can nest HTML in SVG with `<foreignObject>` if you want to use SVG techniques in HTML
- probably not worth mentioning but in case you don’t know... if you miss table layouts, you can use them with `display` on basically anything; if you want table semantics without tabular rendering you can override `display` as well
Alllllll of that being said, if you use these techniques check your stuff with assistive technologies!