-
Notifications
You must be signed in to change notification settings - Fork 22
CSS Styles
👉 Hey you! Yeah, you! 👈
Before you get started working with CSS, set up your editor with Stylelint. And be sure to configure your code editor to fix linting errors on save.
- For Microsoft VS Code: vscode-stylelint, And setup autofix with
source.fixAll.stylelint
- For Neovim: stylelint-lsp, and setup autofix with
lsp-format.nvim
Doing so provides you and your teammates with many benefits, including:
- automatically validating and updating token values whenever they're updating
- automatic CSS formatting
- unified code standards
Would you like to contribute to our CSS code standards? Open a PR to update them, and be sure to explain the reasons for your changes in your PR description, so your colleagues can digest them.
For developer ergonomics we separate our CSS from our TS files. This declutters the TS file and
helps with syntax highlighting. We then use lit-css to import and embed the styles into
the component at build time. When making a new component that contains styles, the component should
have a corresponding .css
file with the identical name as its corresponding .ts
file.
rh-footer/
rh-footer-block.ts
rh-footer-block.css
rh-footer-block.ts
import style from './rh-footer-block.css';
export class RhFooterBlock extends LitElement {
static readonly styles = style;
...
}
We have observed that sharing stylesheets by importing them twice can cause problems when bundling both importers using esbuild. To avoid this problem, share stylesheets by refering to the base class' reference;
❌ Will Break the Bundle:
import baseStyles from './BaseStyles.css';
import styles from './rh-foo.css';
@customElement('rh-jazz-hands')
export class RhJazzHands extends BaseJazzHands {
static readonly styles = [baseStyles, styles];
}
✅ Will Successfully Bundle:
import styles from './rh-foo.css';
@customElement('rh-jazz-hands')
export class RhJazzHands extends BaseJazzHands {
static readonly styles = [...BaseJazzHands.styles, styles];
}
When writing component CSS, care must be taken to avoid preventing user customization. Use the cascade and the inheriting nature of custom properties to your users' advantage.
- ✅ DO define variables as defaults, so that user preferences are respected
#label { color: var(--rh-jazz-hands-label-color, hotpink); }
- ❌ DON'T assign variables meant for user customization on the
:host
, as this will override user preferences:host { --rh-jazz-hands-label-color: hotpink; } #label { color: var(--rh-jazz-hands-label-color); }
- ✅ DO assign private variables on the host, since they are not meant to
be customized:
:host { --_label-font-size: var(--rh-font-size-heading-sm, 1.5rem); } #label { font-size: var(--_label-font-size); }
⚠️ AVOID needlessly defining private variables, use token values directly when needed.
Red Hat Design Tokens' purpose is bring uniformity of style to the design system while making developers' jobs easier. Lean into it!
- ✅ DO use existing token names wherever possible
⚠️ AVOID exposing component-specific custom properties where design tokens already exist/* GOOD */ border-radius: var(--rh-border-radius-pill, 80px); /* BAD */ border-radius: var(--rh-jazz-hands-border-radius-pill, 80px);
If you must create a new name for your component:
- ✅ DO use lower-dash-case
- ✅ DO prefix the custom property with the element name
- ❌ DON'T use double-dashes
- ✅ DO extend from the general to the specific, left to right
⚠️ AVOID abbreviations or acronyms⚠️ AVOID 'region' names, use slots and parts instead/* GOOD */ background-color: var(--rh-jazz-hands-closed-background-color, transparent); /* BAD */ background-color: var(--rh-c--jazz-hands__m-lg__BackgroundColor_____RedHat, red);
You may use "private custom properties" that are assigned to Shadow DOM elements. Assigning them to private elements in this way lets you do more in css and less in JavaScript while keeping your element's API surface small.
These "private" variables should begin with an underscore and should avoid including the custom element name.
<header>
<button id="close-button">x</button>
<slot name="header"></slot>
</header>
[part="header"] {
--_offset: var(--rh-space-sm, 8px);
}
[part="header"].mobile {
--_offset: var(--rh-space-xs, 4px);
}
#close-button {
margin-inline-start: var(--_offset);
}
While we encourage developers to style components using the built in scoping method, (i.e. using the styles class field) occasionally we need to include page level styles to target elements that we are unable to style from within the component. We refer to these as "lightdom styles" or "lightdom CSS".
In rh-footer
, we need to style the nested <li>
and <a>
tags within our component to maintain
semantic lists. In web components, we are currently unable to style slotted elements that are not
that direct children of a component using the ::slotted()
sudo selector. To solve this we include
a file called rh-footer-lightdom.css
.
Since lightdom styles are global to the page we need to scope our selectors to our component.
✅ GOOD
rh-footer [slot^="links"] li {
margin: 0;
padding: 0;
display: contents;
}
rh-footer [slot^="links"] a {
display: block;
color: var(--rh-color-text-primary-on-dark, #ffffff) !important;
font-size: var(--rh-footer-link-font-size, var(--rh-font-size-body-text-sm, 0.875rem));
}
❌ BAD
[slot^="links"] li {
margin: 0;
padding: 0;
display: contents;
}
[slot^="links"] a {
display: block;
color: var(--rh-color-text-primary-on-dark, #ffffff) !important;
font-size: var(--rh-footer-link-font-size, var(--rh-font-size-body-text-sm, 0.875rem));
}
By default, all .css
files are not published in @rhds/elements
. This is because the contents of
those files are meant to be imported into the web component at build time (see "Including styles in
your components" section above). To enable .css
files as being able to be packaged add a -lightdom.css
to the file name. We include the following
publishing pattern in our package.json for this purpose.
{
"files": [
"elements/*/*-lightdom.css",
...
]
}
Make sure that you include instructions for adding this lightdom css file in the documentation for the component.
Questions? Please contact [email protected]. Please review our Code of Conduct