Research / Components

Custom implementations for basic HTML components, like button, checkbox, and various text inputs means you can’t use standard CSS to style them.

Custom CSS Selectors

With custom components you need to target a different element name, sometimes within shadow DOM using ::part(). You need to target custom state attributes because native pseudo-classes do not work, for example, [checked] vs :checked.

For example, compare styling a checkbox:

/* Vaadin component */
vaadin-checkbox[checked]::part(checkbox) {

}

/* HTML component */
input[type=checkbox]:checked {

}

or styling a focused text input:

/* Vaadin component */
vaadin-text-field[focus]::part(input-field) {

}

/* HTML component */
input[type=text]:focus {

}

While they are similar in selector complexity, the HTML element selectors require less explicit documentation from our part, as that is readily available online with a vast number of tutorials explaining how to style HTML elements. Also, attribute selectors are the worst performing CSS selector (the data doesn’t show pseudo-classes, I assume them to be as fast as regular classes): https://nolanlawson.com/2022/06/22/style-scoping-versus-shadow-dom-which-is-fastest/

There might have been a need to reimplement basic HTML elements before, due to older browsers. With modern browsers, we can reconsider that and avoid making theming/styling more complicated than necessary.

Using 3rd-Party CSS Libraries

Using 3rd-party CSS libraries, like Bootstrap, Skeleton, or Bulma, is a problem with Vaadin components.

The selectors defined by 3rd party libraries do not apply to custom components, and it is impossible to add classes defined by those libraries to the correct elements in Vaadin components, because they are either generated at runtime, or they are hidden inside shadow DOM, or they are not meant to be styled (for example, the native <input> element slotted inside Vaadin field components).

Proposal: Use Basic HTML When Possible

I believe we should deprecate some of the Vaadin components in favor of basic HTML components. That said, many of the current use cases covered by Vaadin components can not be easily covered with basic HTML elements, so there is still a need for convenience APIs on top of the standard elements. I’m arguing for a change in the abstraction of those APIs.

As Vaadin components already place the native <input>, <textarea>, and <label> elements in the light DOM of the components, it seems wasteful not to style those elements directly, and require developers to style elements inside shadow DOM instead.

In addition to helping developers style components more easily, using basic HTML elements improves performance as there is less JavaScript to download, parse, and run, and potentially less DOM elements to render (this depends on other implementation details). After all, performance (startup/rendering/runtime) can be seen as a major feature of any business application.

The effort required to maintain components could also reduce, as more work is delegated to the browser instead of recreating it with JavaScript.


Button

<vaadin-button><button>

Benefits:

Drawbacks:

See the prototype theme implementation for button.


Checkbox and Radio Button

Benefits:

Drawbacks:

Although it is not absolutely necessary, I would recommend a helper component that takes care of connecting the label and the input. This saves the developer from coming up with unique ID’s for all inputs. That reintroduces some amount of JavaScript, so that benefit is diminished. A shared “field” component would fix all inconsistency issue across input field components, most prominently validation-related behavior.

See the <j-field> prototype and the prototype theme implementation for checkbox and radio button.

Checkbox and Radio Groups

Grouping checkboxes and radio buttons still benefits from a component that makes it convenient to label the group and provide validation functionality (required field indicator, validation message).

See the <j-field-group> prototype.


Text Inputs

Benefits:

Drawbacks:

Similarly to checkbox and radio button, I recommend a helper component for labelling and validation purposes. See the <j-field> prototype and the prototype theme implementation for text input

For prefix content and text area autosizing, I recommend another helper component. See the <j-input-decorator> prototype. It uses JavaScript and shadow DOM, so those benefit are diminished.

To support the “clear input” button, the input decorator element could have that as a feature.

The benefit of this approach is that users can opt-in to features instead of always “paying” for all features even if they would not be using any.

Some of the related components, like <vaadin-combo-box> and <vaadin-date-picker>, which are currently based on <vaadin-text-field> should also be broken down and limited to the unique features each one provides over any other component. For example, the primary feature <vaadin-date-picker> should provide is the custom calendar overlay picker, and the main feature <vaadin-combo-box> should provide is the lazy-loading item overlay.


Progress Bar

<vaadin-progress-bar><progress>

Benefits:

Drawbacks:

See the prototype theme implementation for progress bar.


Details

<vaadin-details><details>

Benefits:

Drawbacks:

The disabled state can be mimicked with tabindex="-1" + pointer-events: none;, which is of course less convenient.

See the prototype theme implementation for details.


Accordion

<vaadin-accordion><details name="..."> + <details name="...">

Drawbacks: