leptos-rs / leptos

Build fast web applications with Rust.
https://leptos.dev
MIT License
15.28k stars 599 forks source link

Boolean aria attribute values are not handled correctly #2605

Closed SleeplessOne1917 closed 1 month ago

SleeplessOne1917 commented 1 month ago

Describe the bug

Currently, all true boolean attribute values are set to the name of the attribute when true. E.g., a disabled button ends up having the following HTML rendered:

<button disabled="disabled" >
    Do Something
</button>

This behavior comports with the HTML standard for most attributes. However, ARIA attributes with boolean values are special snowflakes in this regard.

Here is a sampling of ARIA attributes that have boolean values:

Notice that the valid value for true is "true" and not the attribute name. There are more, but I think I've made my point. I've already encountered a situation where this causes issues: using ARIA with TailwindCSS.

Leptos Dependencies

leptos = { version = "0.6" }
leptos_meta = { version = "0.6" }
leptos_router = { version = "0.6" }
leptos_actix = { version = "0.6", optional = true }

To Reproduce

Steps to reproduce the behavior:

  1. Create a simple Leptos UI (perhaps using one of the starter templates)
  2. Put a boolean ARIA attribute, e.g. aria-pressed, on one of the elements and assign it true
  3. Run cargo leptos serve
  4. Open the served page
  5. Open your browser's dev tools
  6. Inspect the element you added the boolean ARIA attribute to

Expected behavior

The attribute has the value "true".

Actual Behavior

The attribute has its own name as a value if true, e.g.

<div aria-hidden="aria-hidden"/>

Nice to have

While not causing any problems, it would be nice if non-ARIA boolean attributes only have the attribute present with no value when true, e.g.

<button disabled>
    Do Something
</button>

The amount of generated HTML would be marginally reduced this way, although I doubt it would be enough to make a difference in the vast majority of cases.

gbj commented 1 month ago

I think there's a confusion in terms here. aria-busy="true" is not a boolean attribute; it has a string value, which can either be "true" (the string, not the boolean) or "false" (the string, not the boolean)

HTML containing aria- attributes as boolean attributes is not parsed to these values:

let x = document.createElement("div");
x.innerHTML = "<span aria-hidden>foo</span>";
console.log(x.firstElementChild.ariaHidden);
// logs '', not 'true'

Boolean values on attributes in Leptos mean "include this boolean attribute." From my perspective, the correct behavior here is to pass a string ("true" or "false") or use .to_string() to convert a boolean to a String.

gbj commented 1 month ago

Just an added piece of context — I was double-checking myself on boolean attributes here and in fact the ARIA-style ="false" is specifically invalid for actual boolean attributes. Per MDN on Boolean attributes:

If the attribute is present, it can have one of the following values:

  • no value at all, e.g. attribute
  • the empty string, e.g. attribute=""
  • attribute's name itself, with no leading or trailing whitespace, e.g. attribute="attribute"

Note: The strings "true" and "false" are invalid values. To set the attribute to false, the attribute should not be present in the element tag. Though modern browsers treat any string value as true, you should not rely on that behavior.

So for true boolean attributes, like checked, checked="false" is actually true, although not technically spec compliant. (It certainly isn't false.)

Gotta love the weirdness of the web.

SleeplessOne1917 commented 1 month ago

Just an added piece of context — I was double-checking myself on boolean attributes here and in fact the ARIA-style ="false" is specifically invalid for actual boolean attributes. Per MDN on Boolean attributes:

Funnily enough, I linked that same page in my original post.