Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPIKE] Refactor checkboxes and radios #4040

Closed
wants to merge 1 commit into from
Closed

Conversation

owenatgov
Copy link
Contributor

@owenatgov owenatgov commented Aug 3, 2023

What

Makes the following changes to checkboxes and radios:

  1. attempt to make the spacing in checkboxes and radios be more dynamic and rely less on magic numbers. I've achieved this primarily using flexbox.
  2. refactor checkboxes and radios so that styling of the visual element is handled in the input itself via appearance: none as opposed to using pseudo elements.

Why

Change 1 is tied to #3898. Making the spacing more dynamic means that we avoid exacerbating the existing very subtle alignment issue on small radios and checkboxes as well as future proofing us from other architectural changes like the typography scale.

Change 2 manifested whilst trying to figure out change 1 and is the more radical change. The use case for this is that the visual construction of the elements "make more sense" ie: what the user is interacting with visually is the input itself as opposed to pseudo elements with the input operating behind the visual elements aka: it feels less hacky.

Visual changes

The visual changes here are intended to be minimal aside from the existing alignment issue. It is most prominent on the small variants of checkboxes and radios:

Before

Screenshot of current small checkboxes

After

Screenshot of new small checkboxes

Additional notes

How checkboxes and radios fit together now

The visual elements within radios and checkboxes have moved around like so:

  • the checkbox "box" and the radio "circle" have moved from the before of the label to be the input itself
  • the checkbox "check" and the radio "button" have moved from the after of the label to the after of the input
  • the before of the input now handles the touch target, which the input was previously doing

The position of the input and the label and elements within the label, including text, are handed by flexbox. The input and label positioning is specifically handled by a new container element: govuk-[checkboxes/radios]__container.

The addition of a new element in the macro unfortunately means that this is a breaking change

Differences in older browsers

Because this change uses appearance, a feature with no support on IE and below, this is potentially a significant visual regression for IE users. This isn't as much of an issue as we will only be supporting IE "functionally" from 5.0, however here is what these changes look like in versions of IE compared to how they look currently:

element IE version unchecked checked
Current checkbox 11 Screenshot of current checkbox in IE11, unchecked Screenshot of current checkbox in IE11, checked
Current checkbox 10 Screenshot of current checkbox in IE10, unchecked Screenshot of current checkbox in IE10, checked
Current checkbox 9 and 8 Screenshot of current checkbox in IE9/8, unchecked Screenshot of current checkbox in IE9/8, checked
Current radio 11 Screenshot of current radio in IE11, unchecked Screenshot of current radio in IE11, checked
Current radio 10 Screenshot of current radio in IE10, unchecked Screenshot of current radio in IE10, checked
Current radio 9 and 8 Screenshot of current radio in IE9/8, unchecked Screenshot of current radio in IE9/8, checked
New checkbox 11 Screenshot of new checkbox in IE11, unchecked Screenshot of new checkbox in IE11, checked
New checkbox 10 Screenshot of new checkbox in IE10, unchecked Screenshot of new checkbox in IE10, checked
New checkbox 9 and 8 Screenshot of new checkbox in IE9/8, unchecked Screenshot of new checkbox in IE9/8, checked
New radio 11 Screenshot of new radio in IE11, unchecked Screenshot of new radio in IE11, checked
New radio 10 Screenshot of new radio in IE10, unchecked Screenshot of new radio in IE10, checked
New radio 9 and 8 Screenshot of new radio in IE9/8, unchecked Screenshot of new radio in IE9/8, checked

Code sanitation

Because this is an impromptu spike, I haven't focused as much on keeping the code neat. Therefore there are some comments in odd places that I haven't deleted and some things that probably should have comments that don't. If anything doesn't make sense I can try to explain. Additionally, I'm not looking for advice on code formatting as much as I am on the implementation choices I've made.

@github-actions
Copy link

github-actions bot commented Aug 3, 2023

Uh oh! @owenatgov, the image you shared is missing helpful alt text. Check your pull request body.

Alt text is an invisible description that helps screen readers describe images to blind or low-vision users. If you are using markdown to display images, add your alt text inside the brackets of the markdown image.

Learn more about alt text at Basic writing and formatting syntax: images on GitHub Docs.

@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-4040 August 3, 2023 16:59 Inactive
@36degrees
Copy link
Contributor

36degrees commented Aug 4, 2023

The visual elements within radios and checkboxes have moved around like so:

  • the checkbox "box" and the radio "circle" have moved from the before of the label to be the input itself
  • the checkbox "check" and the radio "button" have moved from the after of the label to the after of the input
  • the before of the input now handles the touch target, which the input was previously doing

This is very cool, but I'm kinda surprised it works. :before and :after pseudo elements are inserted before or after the content of the originating element, but the content model for input is 'Nothing' which means that it must contain no nodes1.

There's a bug raised against Chromium to fix this 'incorrect' behaviour, which references an interesting discussion about deprecating it – ultimately it seems they decided not to because it would break too many sites.

There are a few references to allowing inputs with appearance: none to have :before and :after pseudo elements in bug trackers (example from Chromium, example from Mozilla) but I can't find anything in the spec about it. I can't find anything to suggest it's actively being worked on either, other than this comment which references a discussion from 2016 that didn't really seem to go anywhere.

So, generally this solution feels 'neater' than the current solution, but is at best in a grey area when we look at validating it against the spec. Not against exploring it further, but we need to bear this in mind and make sure we're comfortable with the risk.

We'd also need to check how recently support for :before and :after pseudo elements in inputs was introduced in the major browsers, and what happens when it's not supported.

Footnotes

  1. This is why you can't put an image in an input type="button", but you can in a <button> which allows for phrasing content

@querkmachine
Copy link
Member

I've used this technique for a few years, so in my experience it seems robust and well supported on all the modern user agents, intentionally or not.

My best guess as to why it works is that browsers are resetting some of the element's styling 'rules' in response to appearance: none so that, even though <input> is a void element, it's being treated more akin to a div/span for the purposes of styling — allowing rules that would normally be forbidden or have no effect.

This would seem to be supported by the pseudo-elements being completely ignored if you remove appearance: none.

You could argue that the CSS spec is being a bit vague here:

Also as with regular child elements, the ::before and ::after pseudo-elements are suppressed when their parent, the originating element, is replaced.

As input[type="checkbox"] and input[type="radio"] aren't replaced elements, ::before and ::after are, seemingly intentionally, not suppressed or removed.

@owenatgov
Copy link
Contributor Author

Thanks both. This is insightful stuff.

So, I personally don't feel confident introducing the risk of us going against the spec if we can avoid it, even if the problem space is quite wooly right now. It's a shame because it'd make this work a lot easier so I'm going to do some exploration on the "if we can avoid it" part. @querkmachine I haven't been able to replicate removing appearance: none ignoring pseudo elements, at least not in chrome. Where you able to?

An immediate thought which comes to mind is to have a wrapper around the input itself to store the psuedo elements but I'd like to avoid this if we can as we're already introducing extra markup to get around issues with positioning of hints and if we're introducing a wrapper, we might as well serve the styling from the label.

I'm going to investigate how well the flexbox solution works if we restrict it to the current setup of the visual input coming from the label psuedo elements next.

@querkmachine
Copy link
Member

@querkmachine I haven't been able to replicate removing appearance: none ignoring pseudo elements, at least not in chrome. Where you able to?

Hmm, seems like that's only true in Firefox actually. I might be misremembering from past experience where I shoved a bunch of the styles behind a @supports (appearance: none)

@owenatgov
Copy link
Contributor Author

To verify that above comment, I tried removing appearance: none on both safari and firefox. Safari doesn't get rid of the pseudo elements but firefox does.

@owenatgov
Copy link
Contributor Author

I did a bit of experimenting and have managed to make the current positioning solution work if the pseudo elements are moved to either the label or the container. This would mean that all we're really doing is swapping out the input handling the touch target for it instead handling the visual container whilst pseudo elements do the rest, the same as how things are now. This to me defeats the purpose of the refactor as the intent of change 2 was to try and make the pieces of the interactive input (touch target, outline, check indicator) relate to the input element. At the same time, we now know we can comfortably achieve change 1 without the need for change 2.

I've additionally explored trying to handle the styling of the check indicator solely within the input element, no pseudo elements. This is basically impossible for checkboxes due to the complexity of the shape and whilst radios are feasible using tricks like inset box shadows or background-clip, it always falls flat in forced colours mode where backgrounds are removed.

I'm going to briefly revisit if we actually need the new container element or if there's a way to not interfere with hint positioning.

Open to extra thoughts.

@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-4040 August 7, 2023 10:40 Inactive
@owenatgov
Copy link
Contributor Author

I've managed to remove the container 🎉 What I did:

  • applied display: flex and flex-wrap: wrap to the item parent elements
  • applied width: 100% to hint elements so they always wrap onto a new line
  • applied a max-width on the label's of 100%- (input size + input padding + the label's own padding) so that they never wrap

This means that this is no longer a breaking change. If folks are happy with this implementation, I think we're more or less in a position to production-ify this (including rolling back the refactoring of the input elements) and merge it.

@owenatgov
Copy link
Contributor Author

owenatgov commented Aug 14, 2023

I'm now going to close this in favour of it coming at it fresh and with code sanitation in mind. There aren't any objections to the solution used so I'm going to apply the positioning changes in this spike.

@36degrees didn't get a chance to comment before going on leave but one thing I'll keep in mind that he mentioned to me in person last week is to not use govuk-spacing for the checkbox and radio size variables. The touch targets of these inputs don't have a relationship with the spacing of our elements so flat numbers are the most appropriate thing here.

@owenatgov owenatgov closed this Aug 14, 2023
@owenatgov owenatgov deleted the checkbox-radio-rebuild branch June 28, 2024 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants