platform
platform copied to clipboard
Full support for ::part() selector based styling of components
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 thecomponents
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 addingexportparts="label remove-button"
tovaadin-multi-select-combo-box
- Nested shadow root refactorization example: Moving
vaadin-message
s out ofvaadin-message-list
's shadow DOM - Dynamic part names example: Exposing
selected
state ofrow
parts invaadin-grid
e.g. asrow-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 acomponents
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
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
?
Not only a huge mess as @knoobie mentioned but it would also result in a massive migration effort.
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@import
s 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.
Breakdown of refactorizations and features needed to support ::part()
in all components
Refactoring components' DOM structures to expose nested components for ::part()
styling
- [x] DatePicker: Move date-picker overlay content to light DOM
- [x] DatePicker: Make month calendar infinite-scroller slotted
- [x] DatePicker: Move toolbar buttons to overlay content light DOM
- [x] MenuBar: Move menu-bar-buttons into a slot
- [x] Messages: Move vaadin-message elements into a slot
- [x] Messages: Move avatar in vaadin-message to a slot
- [x] Upload: Extract file list to separate component and place it in light DOM
- [x] Upload: Create slotted elements in light DOM instead of using fallback content
- [x] CRUD: Move vaadin-crud's default grid to light dom
- [x] Move vaadin-message-input's text-area and button into slots
- [x] Avatar: Move avatars in avatar-group into a slot
- [x] Avatar: move avatar-group list-box to light DOM
- [x] Grid: Move vaadin-grid-filter text field to slot
- [x] Login: Move forgot-password button out of Shadow DOM
- [x] MultiSelectComboBox: Add ::part() selector support to chips
Reflecting part states as part names
- [x] Grid: Reflect row and cell states as part names
- [x] DatePicker: Expose date states as part names
Miscellaneous
- [x] Grid: introduce partNameGenerator API
- [x] Introduce a property in
theme.json
to disable/enable the automatic shadow dom injection of stylesheets in thecomponents
folder - [x] Any refactorization to Flow components needed after the web component changes.