TLDR:
Labels with the for attribute according to spec should not reference a span, it does work, but it is not HTML spec compliant, which means it is technically not correct and that there is no guarantee for it's stability.
Labels linked with Radio buttons add the for attribute to the label, the for attribute is only allowed for labelable elements, where the span element does not fall under. The functionality of proxying events is correct and desireable. As of the current implementation it works functionally, but it is not spec compliant. As of my understanding the solutions would mostly push to either a custom web component or an invisible native input type radio using appearance:none which is supported by all major Browsers since over a decade, where I think realistically, only the 2. option would fit this project.
import { Field, Label, Radio, RadioGroup } from '@headlessui/react'
import { useState } from 'react'
const plans = [
{ name: 'Startup', available: true },
{ name: 'Business', available: true },
{ name: 'Enterprise', available: false },
]
export default function Example() {
let [selected, setSelected] = useState(plans[0])
return (
<RadioGroup value={selected} onChange={setSelected} aria-label="Server size">
{plans.map((plan) => (
<Field key={plan.name} disabled={!plan.available} className="flex items-center gap-2">
<Radio
value={plan}
className="group flex size-5 items-center justify-center rounded-full border bg-white data-checked:bg-blue-400 data-disabled:bg-gray-100"
>
<span className="invisible size-2 rounded-full bg-white group-data-checked:visible" />
</Radio>
<Label className="data-disabled:opacity-50">{plan.name}</Label>
</Field>
))}
</RadioGroup>
)
}
sourced from https://headlessui.com/react/radio-group#binding-objects-as-values
In this example the outputted label will look something like:
<label class="data-disabled:opacity-50" id="headlessui-label-_r_3_" for="headlessui-control-_r_1_" data-headlessui-state="">Startup</label>
Is this a concern headlessui would care about or should I recreate the radio group myself in order to be HTML spec compliant? This error was noticed due to the Google Chrome Developer Tools flagging it (It does not always flag it, there are some precise parsing requirements, which is why I did not manage to reproduce it in an online playground. It also does not get flagged if the referenced element by the for attribute appears after the Label element. This are not fixes tho, they are essentially just bypassing the linter.
For reference, I can provide the code emitting that structure in my current codebase, even tho it is essentially the same as the example from the docs above
import { Field, Radio as HeadlessRadio, Label } from "@headlessui/react";
import styles from "./Radio.module.css";
interface RadioProps {
label: string;
value: string | number;
className?: string;
}
const Radio = ({ label, value, className, ...rest }: RadioProps) => {
return (
<Field className={className}>
<HeadlessRadio {...rest} className={styles.radio} value={value}/>
<Label
className={styles.label}
>
{label}
</Label>
</Field>
)};
export default Radio;
and the generated HTML from it
<div data-headlessui-state=""><span class="_radio_173cg_7" id="headlessui-control-_r_27_" role="radio" aria-checked="false" tabindex="-1" data-headlessui-state="" aria-labelledby="headlessui-label-_r_29_"></span><label class="_label_173cg_1" id="headlessui-label-_r_29_" for="headlessui-control-_r_27_" data-headlessui-state="">Ja, ich bin geschäftlich unterwegs</label><span hidden="" style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"></span></div>
In the first place I would be mostly interested in knowing if this is something that the library would solve itself or if I should take care of it.
If needed I can implement the desired solution to this issue if any changes are wanted, I would just rather first ask since the changes wuould change defaults on a project that is not mine.
TLDR:
Labels with the
forattribute according to spec should not reference aspan, it does work, but it is not HTML spec compliant, which means it is technically not correct and that there is no guarantee for it's stability.Labels linked with Radio buttons add the
forattribute to the label, the for attribute is only allowed for labelable elements, where thespanelement does not fall under. The functionality of proxying events is correct and desireable. As of the current implementation it works functionally, but it is not spec compliant. As of my understanding the solutions would mostly push to either a custom web component or an invisible native input type radio usingappearance:nonewhich is supported by all major Browsers since over a decade, where I think realistically, only the 2. option would fit this project.sourced from https://headlessui.com/react/radio-group#binding-objects-as-values
In this example the outputted label will look something like:
Is this a concern headlessui would care about or should I recreate the radio group myself in order to be HTML spec compliant? This error was noticed due to the Google Chrome Developer Tools flagging it (It does not always flag it, there are some precise parsing requirements, which is why I did not manage to reproduce it in an online playground. It also does not get flagged if the referenced element by the
forattribute appears after the Label element. This are not fixes tho, they are essentially just bypassing the linter.For reference, I can provide the code emitting that structure in my current codebase, even tho it is essentially the same as the example from the docs above
and the generated HTML from it
In the first place I would be mostly interested in knowing if this is something that the library would solve itself or if I should take care of it.
If needed I can implement the desired solution to this issue if any changes are wanted, I would just rather first ask since the changes wuould change defaults on a project that is not mine.