Comment on page
Styling components
Styling components using CSS, CSS pre and post-processors as well as working with web components.
Aurelia makes it easy to work with component styles. From the type of CSS flavor you prefer to work with (CSS, PostCSS, SASS, Stylus) to how you use those styles in your components. At the end of the day, regardless of what CSS flavor you choose, it all compiles into CSS.
This section will teach you how to style components using conventions and Shadow DOM to encapsulate your CSS styles.
Not always, but sometimes when you are creating components, you also want styles for your component. Default conventions for custom elements will be imported automatically if you create a custom element and a CSS file with a matching filename.
my-component.ts
my-component.css
my-component.html
export class MyComponent {
}
.myclass {
color: red;
}
<p class="myclass">This is red!</p>
Notice how we didn't import
my-component.css
But when we run the app, it gets imported automatically? That is Aurelia's intuitive conventions at work.One of the most exciting web specifications is Web Components. Part of the Web Components specification is Shadow DOM API. Shadow DOM allows us to encapsulate HTML and styles within our web applications, allowing us to deal with a problem that has plagued web development since the beginning: global styles.
In Aurelia, you can work with Shadow DOM in three different ways:
- 1.Global Shadow DOM — all components in your application will be encapsulated
- 2.Configured Shadow DOM — allows you to opt-in to using Shadow DOM on some components using the decorator
useShadowDOM
- 3.Global opt-out Shadow DOM is a mix of the previous two configuration options. You can turn on Shadow DOM globally but then disable it on a per-component basis.
If you use CSS libraries such as Bootstrap, which rely on global styles, you must account for this when using Shadow DOM. See below in the "Global shared styles" section for more information.
If you didn't choose Shadow DOM during the initial CLI setup phase using Makes, you can manually configure Aurelia to use Shadow DOM inside of
main.ts
.import Aurelia, { StyleConfiguration } from 'aurelia';
import { MyApp } from './my-app';
Aurelia
.register(StyleConfiguration.shadowDOM({}))
.app(MyApp)
.start();
Importing the
StyleConfiguration
class allows us to configure Aurelia to work with different style modes, including Shadow DOM.If you are using Webpack, additional configuration work needs to be done. The Aurelia CLI output of
makes
will add the following rule into your Webpack configuration.{
test: /[/\\]src[/\\].+\.html$/i,
use: {
loader: '@aurelia/webpack-loader',
options: {
defaultShadowOptions: { mode: 'open' }
}
},
exclude: /node_modules/
}
Unlike the traditional way CSS works (populating the global scope), the concept of global styles in Shadow DOM does not exist. However, you can configure shared styles if you require reusable global-like styles.
A great example of shared styles is using the Bootstrap CSS library.
import Aurelia, { StyleConfiguration } from 'aurelia';
import { MyApp } from './my-app';
import bootstrap from 'bootstrap/dist/css/bootstrap.css';
Aurelia
.register(StyleConfiguration.shadowDOM({
// optionally add the shared styles for all components
sharedStyles: [bootstrap]
}))
.app(MyApp)
.start();
You can use the
sharedStyles
configuration property to share any CSS styles. Furthermore, the sharedStyles
property accepts an array of shared styles, so you can have more than one shared stylesheet that all components will get access to.As we mentioned earlier, Aurelia provides a decorator that allows you to opt in and opt out of using Shadow DOM in your web applications. This decorator is called
useShadowDOM
and can be imported from the main aurelia
package.Using the decorator without providing any configuration options will enable Shadow DOM on your component in the default mode of
open
.import { useShadowDOM } from 'aurelia';
@useShadowDOM()
export class MyComponent {
}
The
useshadowDOM
decorator allows you to specify the mode as a configuration property. For reference on the available modes open
and closed
please see below for more details. You can also consult MDN docs here.import { useShadowDOM } from 'aurelia';
@useShadowDOM({mode: 'closed'})
export class MyComponent {
}
Or, to explicitly specify your component as open:
import { useShadowDOM } from 'aurelia';
@useShadowDOM({mode: 'open'})
export class MyComponent {
}
The Shadow DOM API provides two modes you can use for your components. These modes are part of the specification itself and not Aurelia-specific.
Open mode is the default setting for Shadow DOM. This allows you to access the DOM of a component using Javascript. The DOM is accessible via the shadowRoot property accessible on the element.
The closed mode does the exact opposite of
open
in that the shadowRoot property of your component will return null
as the component is closed. This is useful for instances where you want components to only be accessible from within.Where possible, it is recommended that you use
open
mode for your Shadow DOM-based components. Open mode allows you to use e2e testing and libraries that rely on the DOM being accessible.The
useShadowDom
decorator allows you to change the mode but can also accept a false
value to disable Shadow DOM on a specific component.import { useShadowDOM } from 'aurelia';
@useShadowDOM(false)
export class MyComponent {
}
When working with Shadow DOM component styles, you have access to various special selectors, allowing you to style components in particular ways. These selectors can be found in the CSS Scoping Module level 1 specification.
Every custom element in Aurelia has an HTML tag name. If you have a custom element called
app-header
your HTML tags, this leads us to the use of the :host
selector. The host element is the HTML element itself.app-header.css
:host {
border: 1px solid #000;
}
This would add a border to the
app-header
element but not the child elements. Using the function form :host()
allows you to target elements inside of the custom element.app-header.css
:host {
border: 1px solid #000;
}
:host(.active) {
font-weight: bold;
}
The
host-context
selector allows you to target your elements based on the outer context. A valid use for this type of selector is to implement theme functionality allowing your components to be styled. This would be especially helpful if you have a light or dark mode feature.:host-context(.dark-mode) {
border: 1px solid white;
}
In some instances, Shadow DOM is not the right fit for your web application needs, particularly the constraints around global styles. If you need an in-between solution that allows you to encapsulate and use global styles, CSS Modules might be a desired option.
To use CSS Modules, the process is a lot more straightforward if you use the
npx makes aurelia
CLI tool and choose CSS Modules as your preferred styling option. Otherwise, you will have to configure your application manually.Please note the following loader configuration only applies to Webpack.
webpack.config.js
{
test: /[/\\]src[/\\].+\.html$/i,
use: {
loader: '@aurelia/webpack-loader',
options: { useCSSModule: true }
},
exclude: /node_modules/
}
You need to do nothing special to use CSS Modules in your components. Specify your class names and then reference them in your HTML. The bundler turns that class value into a randomly generated string value.
.madeup-style {
font-style: italic;
}
In your HTML, you might reference this class like this:
<a class="madeup-style">Italic link</a>
And once the CSS Modules functionality rewrites the class, it might end up looking like this:
<a class="nNe4iytdPCf3HfNJUXiz">Italic link</a>
Much like the Shadow DOM styling option, you can use some special selectors when using CSS Modules.
The global selector allows you to style global CSS selectors without transforming them. For some CSS classes, you want them to be global for active states. In fact, the default Aurelia app generated using Makes comes with an example of
:global
being used.This selector especially comes in use when working with CSS libraries like Bootstrap, allowing you to target global CSS selectors.
:global(.load-active) {
background-color: lightgray;
}
You should consult the CSS classes and styling section to learn how to work with classes and styles in your Aurelia applications, where strategies for working with classes as well as inline CSS styles are discussed.
Last modified 9mo ago