diff --git a/packages/form-core/src/FormControlMixin.js b/packages/form-core/src/FormControlMixin.js index 21eb8b63a..7b5d18311 100644 --- a/packages/form-core/src/FormControlMixin.js +++ b/packages/form-core/src/FormControlMixin.js @@ -43,6 +43,7 @@ const FormControlMixinImplementation = superclass => name: { type: String, reflect: true }, readOnly: { type: Boolean, attribute: 'readonly', reflect: true }, label: String, // FIXME: { attribute: false } breaks a bunch of tests, but shouldn't... + labelSrOnly: { type: Boolean, attribute: 'label-sr-only', reflect: true }, helpText: { type: String, attribute: 'help-text' }, modelValue: { attribute: false }, _ariaLabelledNodes: { attribute: false }, @@ -186,6 +187,12 @@ const FormControlMixinImplementation = superclass => */ this.label = ''; + /** + * The label will only be visible for srceen readers when true + * @type {boolean} + */ + this.labelSrOnly = false; + /** * The helpt text for the input node. * When no value is defined, textContent of [slot=help-text] will be used @@ -699,6 +706,20 @@ const FormControlMixinImplementation = superclass => color: var(--disabled-text-color, #767676); } + :host([label-sr-only]) .form-field__label { + position: absolute; + top: 0; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(100%); + clip: rect(1px, 1px, 1px, 1px); + white-space: nowrap; + border: 0; + margin: 0; + padding: 0; + } + /*********************** {block} .input-group *********************/ diff --git a/packages/form-core/test/FormControlMixin.test.js b/packages/form-core/test/FormControlMixin.test.js index bbc5e373b..df18d36b8 100644 --- a/packages/form-core/test/FormControlMixin.test.js +++ b/packages/form-core/test/FormControlMixin.test.js @@ -80,6 +80,43 @@ describe('FormControlMixin', () => { expect(el.label).to.equal(''); }); + /** + * N.B. For platform controls, the same would be achieved with + * However, since FormControl is usually not the activeElement (_inputNode is), this + * will not have the desired effect on for instance lion-input + */ + it('supports "label-sr-only" to make label visually hidden, but accessible for screen reader users', async () => { + const el = /** @type {FormControlMixinClass} */ ( + await fixture(html` + <${tag} label-sr-only> + + ${inputSlot} + `) + ); + + const expectedValues = { + position: 'absolute', + top: '0px', + width: '1px', + height: '1px', + overflow: 'hidden', + clipPath: 'inset(100%)', + clip: 'rect(1px, 1px, 1px, 1px)', + whiteSpace: 'nowrap', + borderWidth: '0px', + margin: '0px', + padding: '0px', + }; + + const labelStyle = window.getComputedStyle( + // @ts-ignore + el.shadowRoot?.querySelector('.form-field__label'), + ); + Object.entries(expectedValues).forEach(([key, val]) => { + expect(labelStyle[key]).to.equal(val); + }); + }); + it('can have a help-text', async () => { const elAttr = /** @type {FormControlMixinClass} */ ( await fixture(html`