Associate the ThreePartDate inputs with the original label#336
Associate the ThreePartDate inputs with the original label#336BryceStevenWilley wants to merge 2 commits into
Conversation
plocket
left a comment
There was a problem hiding this comment.
Great accessibility improvements!
From a second, visual, check of ALKiln's code these changes should have no effect on that functionality. That said, it's a complex system and testing it is even more complex at the moment because of the OS reasons we've discussed. Let me know if you think of an easy way to wrangle that. If all else fails, though, I can always patch ALKiln.
I made some suggestions for things I see as improvements, but I don't see any of them as critical, so I'll approve and you can do as you will!
| // Replace the div with a fieldset for accessibility. | ||
| $('div.da-field-container-datatype-ThreePartsDate').each(function() {{ | ||
| var $container = $(this); | ||
| var $fieldset = $('<fieldset class="da-container da-form-group row da-field-container da-field-container-datatype-ThreePartsDate">'); | ||
| for (let c of $container.children()) {{ | ||
| $fieldset.append($(c).detach()); | ||
| }} | ||
| $container.after($fieldset); | ||
| $container.remove(); | ||
| }}); |
There was a problem hiding this comment.
Alternatively, this may have a better chance of staying robust to da DOM changes and give it more of a chance to handle how da or our users are currently using the div1:
| // Replace the div with a fieldset for accessibility. | |
| $('div.da-field-container-datatype-ThreePartsDate').each(function() {{ | |
| var $container = $(this); | |
| var $fieldset = $('<fieldset class="da-container da-form-group row da-field-container da-field-container-datatype-ThreePartsDate">'); | |
| for (let c of $container.children()) {{ | |
| $fieldset.append($(c).detach()); | |
| }} | |
| $container.after($fieldset); | |
| $container.remove(); | |
| }}); | |
| // Replace the div with a fieldset for accessibility. | |
| $('div.da-field-container-datatype-ThreePartsDate').each(function() {{ | |
| const $fieldset = $(`<fieldset>`); | |
| $(this).wrap($fieldset) | |
| }}); |
Unless there's some reason we can't adjust our code to this configuration.
I've tested the code directly in the browser and functionality and appearance seemed to stay the same with this particular configuration.
Footnotes
-
The original
divcould be acted on by listeners that use event delegation. Those would be event listeners on an ancestor of thedivthat then use thedivtag to find the form element. Others could be using thedivas part of their css to style these elements. It's unlikely, but possible. Even this change doesn't solve the issue completely. E.g. css like this:form > div.da-form-groupor something similar, orform *, but I don't see folks using those kinds of selectors as much. ↩
There was a problem hiding this comment.
I tried this, and it has some issues with screen readers. Specifically, legend needs to be an immediate descendant of the fieldset, it can't be in a div or anything. https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/fieldset.
There was a problem hiding this comment.
Sorry, missed checking that functionality completely. I can play around and see if I can flip the relationship. If not, I think we should go ahead without the nesting.
There was a problem hiding this comment.
I added code below that makes the legend a child of this fieldset. I think there's an improvement to make with my code though - adding a class to the fieldset to make it more accessible to css.
| // Replace the div with a fieldset for accessibility. | |
| $('div.da-field-container-datatype-ThreePartsDate').each(function() {{ | |
| var $container = $(this); | |
| var $fieldset = $('<fieldset class="da-container da-form-group row da-field-container da-field-container-datatype-ThreePartsDate">'); | |
| for (let c of $container.children()) {{ | |
| $fieldset.append($(c).detach()); | |
| }} | |
| $container.after($fieldset); | |
| $container.remove(); | |
| }}); | |
| // Replace the div with a fieldset for accessibility. | |
| $(`div.da-field-container-datatype-ThreePartsDate`).each(function() {{ | |
| const $fieldset = $(`<fieldset class="al-fieldset-datatype-ThreePartsDate">`); | |
| $(this).wrap($fieldset) | |
| }}); |
With regular quotes:
| // Replace the div with a fieldset for accessibility. | |
| $('div.da-field-container-datatype-ThreePartsDate').each(function() {{ | |
| var $container = $(this); | |
| var $fieldset = $('<fieldset class="da-container da-form-group row da-field-container da-field-container-datatype-ThreePartsDate">'); | |
| for (let c of $container.children()) {{ | |
| $fieldset.append($(c).detach()); | |
| }} | |
| $container.after($fieldset); | |
| $container.remove(); | |
| }}); | |
| // Replace the div with a fieldset for accessibility. | |
| $('div.da-field-container-datatype-ThreePartsDate').each(function() {{ | |
| const $fieldset = $('<fieldset class="al-fieldset-datatype-ThreePartsDate">'); | |
| $(this).wrap($fieldset) | |
| }}); |
| var $label = $(this); | ||
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); |
There was a problem hiding this comment.
As with the <fieldset>, this may make the code more robust to da DOM changes. Also, instead of using our own custom css for hiding the label, we could use da's pre-existing class for that and also make the code more robust to da changes. If you want to skip da's class, see below.
| var $label = $(this); | |
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); | |
| const $label = $(this); | |
| const $legend = $(`<legend>`).addClass($label.attr(`class`)); | |
| $legend.addClass(`visually-hidden`); |
It ends up with this css:
.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption), .visually-hidden:not(caption) {
position: absolute !important;
}
.visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) {
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0,0,0,0) !important;
white-space: nowrap !important;
border: 0 !important;
}It has some of the same pitfalls as the css in this PR. Also, it doesn't include active.
Without da's class:
| var $label = $(this); | |
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); | |
| const $label = $(this); | |
| const $legend = $(`<legend>`).addClass($label.attr(`class`)); |
There was a problem hiding this comment.
Both of these suggestions don't include the label text in the legend, which is the point of adding it?
I can try changing the CSS to docassemble's and see if it works the same. Looks like it might, but would like to check it myself.
There was a problem hiding this comment.
I completely left out the text, sorry about that:
| var $label = $(this); | |
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); | |
| const $label = $(this); | |
| const $legend = $(`<legend>` + $label.text() + `</legend>`).addClass($label.attr(`class`)); | |
| $legend.addClass(`visually-hidden`); |
| var $label = $(this); | |
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); | |
| const $label = $(this); | |
| const $legend = $(`<legend>` + $label.text() + `</legend>`).addClass($label.attr(`class`)); |
There was a problem hiding this comment.
[Edit: putting a hold on this comment for the moment, I need to look into this code a bit more.]
[Edit: After a second look, I stand by what I said the first time:]
To solve the problem with the legend being a child of the fieldset, could we just add the legend directly as the first child of the outer fieldset? It would still be hidden, it just wouldn't be a sibling of the label.
| legend.da-form-label:not(:focus):not(:active) { | ||
| position: absolute; | ||
| overflow: hidden; | ||
| clip: rect(0 0 0 0); | ||
| clip-path: inset(50%); | ||
| width: 1px; | ||
| height: 1px; | ||
| white-space: nowrap; | ||
| } No newline at end of file |
There was a problem hiding this comment.
You've probably already looked into accessibility issues and found guidance on using not(:focus). In this case, I believe it will end up showing both the legend and the label at the same time. I don't think that's the typical use case. Not sure if it's necessary/wise to handle it. If we want to handle it, something like this might suffice (testing needed):
| legend.da-form-label:not(:focus):not(:active) { | |
| position: absolute; | |
| overflow: hidden; | |
| clip: rect(0 0 0 0); | |
| clip-path: inset(50%); | |
| width: 1px; | |
| height: 1px; | |
| white-space: nowrap; | |
| } | |
| legend.da-form-label:not(:focus):not(:active) { | |
| position: absolute; | |
| overflow: hidden; | |
| clip: rect(0 0 0 0); | |
| clip-path: inset(50%); | |
| width: 1px; | |
| height: 1px; | |
| white-space: nowrap; | |
| } | |
| .da-form-group:has(legend.da-form-label:not(:focus):not(:active)) label.da-form-label { | |
| display: none; | |
| } |
Also, see my note in the js about the da class.
Nit & curiosity:
What's the purpose of referencing active here. From what I can understand, that's for interactive elements that a user presses on, press enter on while in focus, or otherwise "activates".
The :active pseudo-class is commonly used on and elements. Other common targets of this pseudo-class include elements that are contained in an activated element, and form elements that are being activated through their associated .
- https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:active
There was a problem hiding this comment.
In this case, I believe it will end up showing both the legend and the label at the same time.
A lot of what I ended up doing here was informed by this post from an accessibility developer I follow: https://adrianroselli.com/2022/07/use-legend-and-fieldset.html. They included a disclaimer: "If the is never at risk of receiving focus (through some rogue script or whatever), then dump the :not(:focus):not(:active) from the selector." and I wasn't 100% sure that it was never at risk of receiving focus, so I kept that in. I would guess it's a slightly better degraded interface than keeping it hidden when focused. Some goes for the active part.
There was a problem hiding this comment.
That article is a great find!
I would guess it's a slightly better degraded interface than keeping it hidden when focused. Some goes for the active part.
Maybe! 🤷 It's a shame we can't reuse da's stuff. Regardless, I'm not dying on this sword, feel free to keep as is.
For the sake of completeness, here's some churning that may just be thoughts for People of the Future:
If I ever have time to find something to explain the reasoning for active and if it seems sane, I think it would be a decent candidate for an upstream change. I didn't see an example of :not(:focus):not(:active) or of potentially handling visual discrepancies, so I'm not sure how much the author experimented with a case where there was risk of the item gaining focus. I think the hiding I suggested might be a decent way to handle the visual discrepancy in that situation in our particular case, but more research and experimentation would be needed.
Uses `fieldset` and `legend`, replacing docassemble's given `div`. Keeps the `label` as the visual element, as `legend` has display issues on Safari [^1]. Tested on Chrome and Safari on Mac, and in Firefox (which uses Safari's engine) on iPhone. Doesn't really improve the situation on iPhone, but that is expected, as group support on iPhone is poor [^1]. Fixes #164. [^1] https://adrianroselli.com/2022/07/use-legend-and-fieldset.html
6d2be5a to
5eecbb8
Compare
| var $label = $(this); | ||
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); | ||
| $label.after($legend); |
There was a problem hiding this comment.
I believe this will do what the above suggests. Namely, ensure the legend is the child of the label's fieldset.
| var $label = $(this); | |
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); | |
| $label.after($legend); | |
| const $label = $(this); | |
| const $legend = $(`<legend>`+ $label.text() + `</legend>`).addClass($label.attr(`class`)); | |
| const $fieldset = $label.closest(`fieldset`); | |
| $fieldset.prepend($legend); |
It's feeling a little convoluted at the moment. The whole situation is fairly convoluted, so I'm not sure if it's actually more convoluted or if I've been staring at it too long.
With regular quotation marks:
| var $label = $(this); | |
| var $legend = $('<legend class="col-md-4 col-form-label da-form-label datext-right">' + $label.text() + '</legend>'); | |
| $label.after($legend); | |
| const $label = $(this); | |
| const $legend = $('<legend>'+ $label.text() + '</legend>').addClass($label.attr('class')); | |
| const $fieldset = $label.closest('fieldset'); | |
| $fieldset.prepend($legend); |
I'm not sure how far down the rabbit hole we want to go to ensure backwards compatibility 🤷
Uses
fieldsetandlegend, replacing docassemble's givendiv. Keeps thelabelas the visual element, aslegendhas display issues on Safari 1.Tested on Chrome and Safari on Mac, and in Firefox (which uses Safari's engine) on iPhone. Doesn't really improve the situation on iPhone, but that is expected, as group support on iPhone is poor 1.
Fixes #164.
Footnotes
https://adrianroselli.com/2022/07/use-legend-and-fieldset.html ↩ ↩2