platform icon indicating copy to clipboard operation
platform copied to clipboard

Full support for ::part() selector based styling of components

Open rolfsmeds opened this issue 2 years ago • 4 comments

Description

Make it possible to style components using the ::part() selector instead of shadow DOM CSS injection, to simplify styling.

Use cases

As a developer I want to use the ::part() selector to style Vaadin components So that I can style components without the hassles of shadow dom css injection

What is the ::part() selector, and what's the benefit?

It's a CSS selector that allows you to style elements that are inside a web component's shadow DOM, provided that the elements have a part attribute. As an example, vaadin-grid::part(cell) {...} can be used to style the cells of a Grid, thanks to the part="cell" attribute on the cell elements. This is a simpler and more flexible approach than the current component styling mechanism in Vaadin (via themeFor or the theme components folder) in a number of ways:

  • Component styles can be placed in any stylesheet, with any name, anywhere in the theme folder (or elsewhere)
  • Different components can be styled in the same stylesheet
  • No need to deal with obscure selectors like :host, ::slotted, and [part~="..."]
  • Component parts can be styled based on the component's parent element (e.g. #app-header > vaadin-grid::part(cell) {...})
  • Pure native CSS, rather than a Vaadin-specific mechanism

Acceptance criteria

  • [ ] Refactor components to remove nested shadow roots where the above would be otherwise difficult
  • [ ] Introduce additional, dynamic part names to reflect part states that are currently only modelled as attributes
  • [ ] Expose parts of remaining nested stylable web components (if any) using the exportparts attribute
  • [ ] Introduce a way to generate part names to Grid cells
  • [ ] Introduce a property in theme.json to disable/enable the automatic shadow dom injection of stylesheets in the components folder. (In 23.x, injection will still need to be enabled by default)
  • [ ] Rewrite Vaadin styling documentation to reflect new recommended approach based on ::part()

Examples of the above

  • Exportparts example: Exposing parts of vaadin-multi-select-combo-box-chip by adding exportparts="label remove-button" to vaadin-multi-select-combo-box
  • Nested shadow root refactorization example: Moving vaadin-messages out of vaadin-message-list's shadow DOM
  • Dynamic part names example: Exposing selected state of row parts in vaadin-grid e.g. as row-selected part name

Notes

  • It is already possible to style many parts in Vaadin components using ::part() today, but many parts are inaccessible due to nested shadow roots, and many part states cannot be targeted since they're not reflected as part names (and attributes on parts cannot be targeted through the ::part() selector).
  • The current mechanisms of injecting CSS into components' shadow DOM using the components folder and/or the @CssImport themeFor parameter would still be supported. The components folder approach would be opt-in/out so that it wouldn't conflict with having a components folder for light dom stylesheets.

General criteria

  • [ ] APIs reviewed
  • [ ] UX/DX tests in Alpha
  • [ ] Performance: (ensure that dynamic part names are applied without noticeable delay)
  • [ ] Documentation:
  • [ ] How to test?
  • [ ] Limitations:

Links

  • Discuss: https://github.com/vaadin/platform/discussions/3176

rolfsmeds avatar Aug 03 '22 11:08 rolfsmeds

I'm in favor of reducing complexity, just wondering if the old components structure could be re-used somehow / would still works after this? Because for me it's really helpful to have a style-sheet for every component based on it's name. Merging them all together again, sounds like huge mess :)

Or should those files be updated to use ::part internally and be imported inside the styles.css?

knoobie avatar Aug 04 '22 08:08 knoobie

Not only a huge mess as @knoobie mentioned but it would also result in a massive migration effort.

simasch avatar Aug 04 '22 09:08 simasch

Ah, we should rephrase that a bit: The intention is not

  • to force developers to put all styles in a single stylesheet – only to make it possible to put styles wherever you like, instead of being bound by the current components/element-name structure. (styles.css will be loaded by default like now, and you can add more stylesheets with @imports there)
  • to remove the components/element-name mechanism – we'd still provide that as an option for backwards compatibility and for those who prefer shadow dom injection. Also @CssImport(...themeFor="element-name") would still work.

rolfsmeds avatar Aug 04 '22 10:08 rolfsmeds

Breakdown of refactorizations and features needed to support ::part() in all components

Refactoring components' DOM structures to expose nested components for ::part() styling

Reflecting part states as part names

Miscellaneous

rolfsmeds avatar Oct 05 '22 09:10 rolfsmeds