Security News
JavaScript Leaders Demand Oracle Release the JavaScript Trademark
In an open letter, JavaScript community leaders urge Oracle to give up the JavaScript trademark, arguing that it has been effectively abandoned through nonuse.
@vaadin/vaadin-themable-mixin
Advanced tools
With the addition of Shadow DOM, styles on a webpage can be divided into two groups:
On a regular website, all elements are inside the same global style scope, and can be styled with global stylesheets (either <link rel="stylesheet">
or <style>
elements), using regular CSS selectors.
The only thing we can style using global styles is the whole <vaadin-text-field>
element, but nothing inside it, like the label or the input field.
For example, we could have the following styles in an imported stylesheet:
vaadin-text-field {
border: 1px solid gray;
}
And it would produce the following result:
Styling Web Components (or custom elements to be more precise), which use shadow DOM, is different from styling regular websites.
When a custom element has its own shadow DOM, the browser creates a new style scope for the elements that are placed inside the shadow DOM hierarchy, and CSS selectors in global stylesheets can’t affect those elements.
The only CSS selectors that can affect the elements inside a shadow DOM need to be in a <style>
element which is somewhere inside the shadow DOM hierarchy. At the same time, the styles inside a shadow DOM can’t affect elements outside the shadow DOM. The styles are placed in the “local style scope” of the shadow DOM.
Only the <style>
element highlighted in the inspector can affect the elements inside the <vaadin-text-field>
element’s shadow DOM.
For example, if we move the same styles from the previous example inside the <style>
element inside the shadow DOM, the result is the same as without the style rules:
#shadow-root (open)
<style>
vaadin-text-field {
border: 1px solid gray;
}
</style>
That is because there are no <vaadin-text-field>
elements inside the shadow DOM. If we want the same result as with the global stylesheet, we need to use the :host
selector to match the element which is the “host” for this shadow DOM or style scope:
#shadow-root (open)
<style>
:host {
border: 1px solid gray;
}
</style>
Then we get the same result as with the global stylesheet:
If we wanted to move the border to the actual text input element, we would need to inspect the shadow DOM hierarchy and see which selector would match that particular element. For <vaadin-text-field>
, the correct selector would be [part="input-field"]
:
#shadow-root
<style>
[part="input-field"] {
border: 1px solid gray;
}
</style>
Read the documentation about Styles Scopes before continuing.
Currently, the only global CSS that can affect styles inside a shadow DOM’s local scope are properties that are inherited. Properties can be inherited by default, like font-family
or all CSS Custom Properties, or explicitly by the custom element, using the inherit
property value.
<custom-style>
<style>
vaadin-combo-box {
--vaadin-combo-box-overlay-max-height: 50vh;
}
</style>
</custom-style>
Note: for cross-browser compatibility, use the
<custom-style>
element.
Note: check the API documentation of each element for the custom properties they expose. See the API documentation for vaadin-combo-box for example.
#shadow-root
<style>
:host {
background-color: inherit;
}
</style
The web platform doesn’t currently provide a way to write selectors in the global scope that would match elements in a local scope (Oct 2017). There’s a CSS spec proposal that will add that, but for now, we need to work around this limitation using proprietary solutions, like Vaadin’s ThemableMixin
.
Custom elements extending ThemableMixin
allow you to inject styles into their local scope by defining new style modules in the global scope. You specify the targeted element using the theme-for
attribute.
<!-- Define a theme module (in index.html or in a separate HTML import) -->
<dom-module id="my-theme-module" theme-for="my-element">
<template>
<style>
/* Styles which will be included in my-element local scope */
</style>
</template>
</dom-module>
You can place these “theme module” definitions directly in your index.html
or in a separate HTML import.
Note: a theme module needs to be imported and registered in the DOM before the element(s), which the module targets with the
theme-for
attribute, are registered and upgraded (before the first instantiation of the component).
The id
attribute of the theme module should be unique. You can also re-use an existing id if you want to override a previously defined/imported module.
The value of the theme-for
attribute can be a space-separated list of element names, and can contain wildcard element names as well.
theme-for
attribute values:"vaadin-button"
"vaadin-overlay vaadin-date-picker-overlay"
"vaadin-*"
When creating a theme module for an element, the styles in that theme module will apply to all instances of that element. The styles are always “global” in that sense and can’t be scoped by default with anything.
Read the documentation about Styles Scopes and Adding Styles to Local Scope before continuing.
The host element is the main element which has a shadow DOM, for example <vaadin-text-field>
.
In addition to the host element, only certain elements inside a themable element (i.e. an element extending ThemableMixin
) should be styled.
Other elements should be considered as internal implementation details, and you should not expect these elements to remain constant or be available in the future.
The stylable elements have the part
attribute, which gives the elements a descriptive name.
You can expect these part names to remain constant and rely on the hierarchy of these parts (i.e. the value
part will always be contained within input-field
).
The stylable parts of each Vaadin component are listed in their API documentation. See the API documentation for vaadin-text-field for example.
The host element can be targeted using the :host
selector.
<dom-module id="my-button" theme-for="vaadin-button">
<template>
<style>
:host {
/* Styles for vaadin-button element */
}
</style>
</template>
</dom-module>
The stylable elements (marked with a part
attribute) should only be targeted using the [part="..."]
attribute selector.
<dom-module id="my-text-field" theme-for="vaadin-text-field">
<template>
<style>
[part="input-field"] {
/* Styles for vaadin-text-field's input-field part */
}
</style>
</template>
</dom-module>
Use part~="..."
to match a part which might have multiple names, for example the cells inside a <vaadin-grid>
which can have multiple names like "cell"
and "body-cell"
.
You can use this kind of attribute selector in all cases, if you want to be safe. It will work for parts with only one name as well.
<dom-module id="my-grid" theme-for="vaadin-grid">
<template>
<style>
[part~="cell"] {
/* Styles that affect all grid cells, including header, body and footer cells */
}
[part~="body-cell"] {
/* Styles that only affect all body cells */
}
</style>
</template>
</dom-module>
Do not rely on the element type which a part applies to. For example, given <input type="text" part="value">
, you should not rely on the information that the element is actually a native <input>
element. This is considered as an internal implementation detail, and the element type could change in the future (while the part name stays the same), for example to <div contenteditable="true" part="value">
.
Some custom elements expose some of their internal state as top-level attributes for styling purposes. You can find these attributes in the elements API documentation.
For example, you can target styles for a disabled <vaadin-button>
using the disabled
attribute in combination with the :host
selector:
<dom-module id="my-button" theme-for="vaadin-button">
<template>
<style>
:host([disabled]) {
/* Styles for disabled vaadin-button element */
}
</style>
</template>
</dom-module>
You can also target any named parts in a specific state of the host. For example, you can add a red border for the input-field
part of a <vaadin-text-field>
which is marked invalid
:
<dom-module id="my-text-field" theme-for="vaadin-text-field">
<template>
<style>
:host([invalid]) [part="input-field"] {
border: 1px solid red;
}
</style>
</template>
</dom-module>
Similarly to the host element, the named parts can also expose state attributes for themselves, which can be used for styling. These are also listed in the element’s API documentation.
For example, you can target a selected date in a <vaadin-date-picker>
:
<dom-module id="my-month-calendar-styles" theme-for="vaadin-month-calendar">
<template>
<style>
[part~="date"][selected] {
/* Styles for a selected date */
}
</style>
</template>
</dom-module>
The styles defined in a “theme module” affect all the instances of the element the module targets with the theme-for
attribute.
There are two ways to scope the styles that you write in a theme module.
theme
attribute. The downside of this approach is that you end up adding the selectors and properties to all instances, even though only some instances will need those styles (they won’t apply unless the scoping selector is used on the host element).<!-- Define the theme module (in index.html or in a separate HTML import) -->
<dom-module id="my-text-field-theme" theme-for="vaadin-text-field">
<template>
<style>
[part="input-field"] {
background-color: var(--input-field-background-color, #fff);
}
</style>
</template>
</dom-module>
<!-- Use the new custom property -->
<custom-style>
<style>
.some-part-of-my-app vaadin-text-field {
--input-field-background-color: #eee;
}
</style>
</custom-style>
<div class="some-part-of-my-app">
<vaadin-text-field></vaadin-text-field>
</div>
<!-- Define the theme module (in index.html or in a separate HTML import) -->
<dom-module id="my-text-field-theme" theme-for="vaadin-text-field">
<template>
<style>
:host(.special-field) [part="input-field"] {
background-color: #000;
color: #fff;
border: 2px solid #fff;
border-radius: 9px;
...
}
</style>
</template>
</dom-module>
<!-- Use the new scoping selector anywhere in your app -->
<div>
<vaadin-text-field class="special-field"></vaadin-text-field>
</div>
FAQs
vaadin-themable-mixin
The npm package @vaadin/vaadin-themable-mixin receives a total of 72,359 weekly downloads. As such, @vaadin/vaadin-themable-mixin popularity was classified as popular.
We found that @vaadin/vaadin-themable-mixin demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 12 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
In an open letter, JavaScript community leaders urge Oracle to give up the JavaScript trademark, arguing that it has been effectively abandoned through nonuse.
Security News
The initial version of the Socket Python SDK is now on PyPI, enabling developers to more easily interact with the Socket REST API in Python projects.
Security News
Floating dependency ranges in npm can introduce instability and security risks into your project by allowing unverified or incompatible versions to be installed automatically, leading to unpredictable behavior and potential conflicts.