Work Text:
What is your favourite colour?
I also like !
What is your least favourite colour?
Yuck, !
Unlike with using details, this method only requires one click
to "select" a particular value and "deselect" the rest. Unlike with using
:target, this allows for multiple indepedent sets of buttons.
How it works
This method utilises the .expand and .shuffle CSS
class, whose on-click behaviours are defined in
application.js.
On click, an .expand element will have the inline HTML style
style="display: none;". The sibling .shuffle elements after it
will then have their inline style be set to display: block; or whatever their
initial display value is.
A radio-group consists of a set of radio buttons with the classes .expand.shuffle.
When one button is clicked, it will have style="display: none;",
and all the buttons after it will not. Therefore,
the user selected button is the last occuring element with .expand[style*="none"].
We can select this element with :nth-last-child(1 of .expand[style*="none"]).
Accessibility
To ensure that the radio buttons can be selected using keyboard,
use <a> tags for the buttons and set the href value to
a non-existent target. This is to prevent navigating away from the button.
Alternatively, if you are using :target for something
else, add the .check_none class to the radio buttons. Also seen in application.js,
this class will set the link to have event.preventDefault().
Unexpected behaviour on Chromium Browsers
#workskin .my-form:has(.radio-group > :nth-child(1):nth-last-child(1 of .expand[style*="none"])) .output::before {
content: "Red";
background: red;
color: white;
}
During testing, I encountered an issue on Edge and Chrome
where the above selector would not act as intended, unless I made changes to the code and
"forced" the browsers to re-evaluate the CSS. Therefore .output would not
respond to inline style attribute changes.
I was not able to pinpoint the exact cause of this, but it appears to be
a known issue with Chromium browsers, :has(), and complicated statements with attribute selectors.
Firefox does not seem to have this issue.
The fix is to add a "dummy" CSS declaration with :has
and an inner [style] selector.
/* chromium bug fix */
#workskin .radio-group:has([style*="none"]) {
display: block;
}
CSS
/* appearance for radio buttons */
#workskin .radio-group .expand,
#workskin .radio-group .expand:visited,
#workskin .radio-group .expand:hover,
#workskin .radio-group .expand:active {
display: block !important; /* ignore html inline styling */
cursor: pointer;
color: currentColor;
border: none;
outline: none;
}
#workskin .radio-group .expand:hover::before {
filter: brightness(.7);
}
/* keyboard navigation focus */
#workskin .radio-group .expand:focus-visible {
outline: 1px dotted currentColor;
}
#workskin .radio-group .expand::before {
display: inline-block;
content: "";
width: .7em;
aspect-ratio: 1;
margin-right: .5em;
border: 1px solid currentColor;
border-radius: 50%;
box-sizing: border-box;
}
/* appearance for selected radio buttons */
#workskin .radio-group :nth-last-child(1 of .expand[style*="none"])::before {
border: 2px solid transparent;
box-shadow: 0 0 0 1px dodgerblue, inset 0 0 0 1em dodgerblue;
}
/* output results */
#workskin .output::before {
content: "colours";
color: currentcolor;
}
#workskin .my-form:has(.radio-group > :nth-child(1):nth-last-child(1 of .expand[style*="none"])) .output::before {
content: "Red";
background: red;
color: white;
}
#workskin .my-form:has(.radio-group > :nth-child(2):nth-last-child(1 of .expand[style*="none"])) .output::before {
content: "Blue";
background: blue;
color: white;
}
#workskin .my-form:has(.radio-group > :nth-child(3):nth-last-child(1 of .expand[style*="none"])) .output::before {
content: "Green";
background: green;
color: white;
}
#workskin .my-form:has(.radio-group > :nth-child(4):nth-last-child(1 of .expand[style*="none"])) .output::before {
content: "Yellow";
background: yellow;
color: black;
}
/* chromium bug fix */
#workskin .radio-group:has([style*="none"]) {
display: block;
}
