Skip to content

Consistent boolean attributes #14400

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

Closed
madmoizo opened this issue Nov 22, 2024 · 7 comments
Closed

Consistent boolean attributes #14400

madmoizo opened this issue Nov 22, 2024 · 7 comments

Comments

@madmoizo
Copy link

madmoizo commented Nov 22, 2024

The problem: Svelte is too smart

Indeed, Svelte knows that the specification is inconsistent when it comes to boolean attributes, so it tries to guess the user's intention based on what it knows about predefined attributes.

Boolean attributes

A predefined boolean attribute will be added if it is true and removed if it is false.

<button disabled={boolean}>

if true:

<button disabled>

if false:

<button>

This is the expected result for the user. 👍

Enumerated-Boolean attributes

With the same svelte syntax, a predefined enumerated-boolean attribute will automatically render a string.

<a draggable={boolean}>

if true:

<a draggable="true">

If false:

<a draggable="false">

It seems to be a smart and convenient behavior, but it is magic.

Custom attributes

For custom attributes, Svelte cannot know the user's intention, so it processes any value as a string, resulting in the behavior of an enumerated-boolean attribute. If the user wants the behavior of a boolean attribute, he must do it manually using a ternary operator:

<a data-active={boolean ? "" : undefined}>

Because even using a custom attribute as a boolean

<a data-active>

is converted to a string

<a data-active="true">

The solution: Let the linter be smart

Boolean attributes

We do not change the behavior of boolean attributes. because the current one is perfect.

Enumerated-Boolean attributes

Do not mask away the inconsistency of the spec here!
If the user needs to use an enumerated-boolean attribute, let him be explicit about it. Beginners will learn sooner than later how the platform works.
The proposed solution consists of adding quote around a boolean to make it a string.

<a draggable="{boolean}"> 

if true:

<a draggable="true">

If false:

<a draggable="false">

What happens if a user forget about adding quotes? The linter can do the smart part and automatically add quotes for you on predefined enumerated-boolean attributes.

Custom attributes

Instead of being enumerated-boolean attributes by default, which I don't think is the expected behavior when a user set a boolean. Align the default behavior on boolean attributes.
Then if the user wants a enumerated-boolean attribute behavior, he can opt-in by simply add quotes, no more weird ternary operator:

<a data-enumerated-boolean="{boolean}" data-boolean={boolean} data-other-boolean> 

if true:

<a data-enumerated-boolean="true" data-boolean data-other-boolean> 

if false:

<a data-enumerated-boolean="false"> 

With this API attribute={boolean}, attribute="{boolean}" and just attribute will always render the same thing, no matter if the attribute is predefined or custom.

Consistent output, clear user's intention, no more magic.

Importance

nice to have

@brunnerh
Copy link
Member

brunnerh commented Nov 22, 2024

Data attributes have no pre-defined semantics, so I don't think Svelte should add special behavior here.

The standards are not consistent when it comes to "boolean attributes" that do have semantics either; e.g. certain aria-attributes like aria-selected and draggable require the string values "true" or "false".

Regarding the Svelte 6 note, that should be fairly unrelated, see this issue:

@madmoizo
Copy link
Author

The spec can be inconsistent but I don't think svelte should be.

Doing the exact same thing in svelte produce 2 differents results and it's not obvious for the user.

<button disabled={boolean} custom={boolean}>
<!-- render if true -->
<button disabled custom="true">
<!-- render if false -->
<button custom="false">

My proposition aim to fixes the inconsistency:

<!-- 1: boolean -->
<button attribute={boolean}>
<!-- if true always render: -->
<button attribute>
<!-- if false always render: -->
<button>

<!-- 2: stringified boolean -->
<button attribute="{boolean}">
<!--  if true always render: -->
<button attribute="true">
<!-- if false always render: -->
<button attribute="false">

Then it's up to the user to know if he needs a boolean or a stringified boolean to match the spec

@webJose
Copy link
Contributor

webJose commented Nov 22, 2024

As much as we would like consistency, if Svelte did this, then things would stop working. It is not up to Svelte to standardize things, unfortunately. Svelte (and anyone, really) has to/must adhere to whatever browsers understand, and leave wishful thinking for the ones with power to change this where it matters: Browsers. Just my $0.02.

@dummdidumm
Copy link
Member

Closing as "works as designed". As others pointed out already, the browser is full of quirks around boolean(-ish) attributes. We can't consolidate that without breaking expectations of others. For all non-special booleanish attributes, an undefined will remove it

@dummdidumm dummdidumm closed this as not planned Won't fix, can't repro, duplicate, stale Nov 22, 2024
@madmoizo
Copy link
Author

There is maybe an opportunity to make the doc more explicit about this.

Boolean attributes are included on the element if their value is truthy and excluded if it’s falsy.
All other attributes are included unless their value is nullish (null or undefined).

It is easy to take boolean attribute as attribute with boolean value

@madmoizo
Copy link
Author

The initial presentation of my proposal was particularly poorly done, so I have updated it for posterity.

@madmoizo
Copy link
Author

madmoizo commented Dec 11, 2024

One more thing I discovered today playing with select and default "" empty value
1 - svelte input

<select>
   <option value></option>
</select>

1 - svelte output

<select>
   <option value="true"></option>
</select>

2 - svelte input

<select>
   <option value=""></option>
</select>

2 - svelte output

<select>
   <option value></option>
</select>

Once again, as a user, I expect this:
3 - svelte input

<select>
   <option value></option>
</select>

3 - svelte output

<select>
   <option value></option>
</select>

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

No branches or pull requests

4 participants