LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Current route
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • Kernel Errors
      • Template Compiler Errors
      • Dialog Errors
      • Runtime HTML Errors
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page
  • Style Conventions
  • Example: Automatic Style Import
  • Shadow DOM
  • Enabling Shadow DOM
  • Webpack Configuration for Shadow DOM
  • Global Shared Styles
  • Opting In to Shadow DOM with useShadowDOM
  • Disabling Shadow DOM
  • Shadow DOM Special Selectors
  • CSS Modules
  • Webpack Configuration for CSS Modules
  • Example: Using CSS Modules in Components
  • CSS Modules' Special Selectors
  • Advanced Shadow DOM Usage
  • Example: Styling Slotted Content
  • Example: Theming with CSS Variables
  • Example: Scoped Animations in Shadow DOM
  • CSS Modules: Advanced Techniques
  • Example: Composing CSS Module Classes
  • Example: Theming with CSS Modules
  • Additional Resources

Was this helpful?

Export as PDF
  1. Components

Styling components

Styling components using CSS, CSS pre and post-processors as well as working with web components.

Aurelia 2 simplifies the process of styling components, supporting a variety of CSS flavors and encapsulation methods. Whether you prefer raw CSS, PostCSS, SASS, or Stylus, Aurelia 2 streamlines their integration into your components. Ultimately, all styles compile into standard CSS that browsers can interpret.

Style Conventions

Aurelia 2 automatically imports styles for custom elements based on file naming conventions. For instance, if you have a custom element named my-component, Aurelia 2 looks for a corresponding stylesheet named my-component.css.

Example: Automatic Style Import

Consider the following file structure:

  • my-component.ts: The TypeScript file defining the MyComponent class.

  • my-component.css: The stylesheet containing styles for MyComponent.

  • my-component.html: The HTML template for MyComponent.

Aurelia 2's convention-based approach eliminates the need to explicitly import the CSS file. When you run your application, Aurelia 2 automatically detects and applies the styles.

export class MyComponent {
  // Component logic goes here
}
.my-class {
  color: blue;
  background-color: lightgrey;
  padding: 10px;
  border-radius: 5px;
}
<template>
  <p class="my-class">Stylized content here!</p>
</template>

When MyComponent is used in the application, the associated styles are automatically applied, giving the paragraph text a blue color and a light grey background with padding and rounded corners.

Shadow DOM

The Shadow DOM API, part of the Web Components standard, provides encapsulation by hiding the component's internal DOM and styles from the rest of the application. Aurelia 2 offers several options for working with Shadow DOM:

  1. Global Shadow DOM: By default, encapsulates all components in your application.

  2. Configured Shadow DOM: Use the useShadowDOM decorator to opt-in per component.

  3. Global opt-out Shadow DOM: Enable Shadow DOM globally but disable it for specific components.

If your application uses CSS frameworks like Bootstrap, which rely on global styles, consider how Shadow DOM encapsulation may affect their behavior. The following sections guide managing global and shared styles.

Enabling Shadow DOM

To enable Shadow DOM after the initial setup, configure it in the main.ts file:

main.ts
import Aurelia, { StyleConfiguration } from 'aurelia';
import { MyApp } from './my-app';

Aurelia
  .register(StyleConfiguration.shadowDOM({
    // Configuration options here
  }))
  .app(MyApp)
  .start();

The StyleConfiguration class from Aurelia allows you to specify how styles are applied, including options for Shadow DOM.

Webpack Configuration for Shadow DOM

When using Webpack, ensure the following rule is included in the Webpack configuration:

webpack.config.js
{
  test: /[/\\]src[/\\].+\.html$/i,
  use: {
    loader: '@aurelia/webpack-loader',
    options: {
      defaultShadowOptions: { mode: 'open' }
    }
  },
  exclude: /node_modules/
}

This rule ensures that HTML files within your src directory are processed correctly to work with Shadow DOM.

Global Shared Styles

In the Shadow DOM, styles are scoped to their components and don't leak to the global scope. To apply global styles across all components, use the sharedStyles property in the Shadow DOM configuration.

Example: Using Bootstrap Globally

main.ts
import Aurelia, { StyleConfiguration } from 'aurelia';
import { MyApp } from './my-app';
import bootstrap from 'bootstrap/dist/css/bootstrap.css';

Aurelia
  .register(StyleConfiguration.shadowDOM({
    sharedStyles: [bootstrap] // Apply Bootstrap styles to all components
  }))
  .app(MyApp)
  .start();

The sharedStyles property accepts an array, allowing you to include multiple shared stylesheets.

Opting In to Shadow DOM with useShadowDOM

The useShadowDOM decorator, imported from Aurelia, lets you enable Shadow DOM on a per-component basis. Without configuration options, it defaults to open mode.

Example: Enabling Shadow DOM on a Component

my-component.ts
import { useShadowDOM } from 'aurelia';

@useShadowDOM()
export class MyComponent {
  // Component logic with Shadow DOM enabled
}

You can specify the Shadow DOM mode (open or closed) as a configuration option. The open mode allows JavaScript to access the component's DOM through the shadowRoot property, while closed mode restricts this access.

Example: Specifying Shadow DOM Mode

my-component.ts
import { useShadowDOM } from 'aurelia';

@useShadowDOM({ mode: 'closed' })
export class MyComponent {
  // Component logic with Shadow DOM in 'closed' mode
}

Disabling Shadow DOM

The useShadowDOM decorator also allows disabling Shadow DOM for a specific component by passing false.

Example: Disabling Shadow DOM for a Component

my-component.ts
import { useShadowDOM } from 'aurelia';

@useShadowDOM(false)
export class MyComponent {
  // Component logic without Shadow DOM
}

Shadow DOM Special Selectors

Shadow DOM introduces special selectors that offer additional styling capabilities. These selectors are part of the CSS Scoping Module specification.

:host

The :host selector targets the custom element itself, not its children. You can use the :host() function to apply styles based on the host element's classes or attributes.

Example: Styling the Host Element

app-header.css
:host {
  display: block;
  border: 1px solid #000;
}

:host(.active) {
  background-color: #f0f0f0;
}

In this example, all app-header elements have a solid border, and those with the active class have a grey background.

:host-context

The :host-context selector styles the custom element based on its context within the document.

Example: Styling Based on Context

app-header.css
:host-context(.dark-mode) {
  color: white;
  background-color: #333;
}

Here, app-header elements will have white text on a dark background inside an element with the dark-mode class.

CSS Modules

When Shadow DOM does not meet your needs, CSS Modules balance style encapsulation and global accessibility. CSS Modules transform class names into unique identifiers, preventing style collisions.

Webpack Configuration for CSS Modules

To use CSS Modules, include the following loader configuration in your Webpack setup:

webpack.config.js
{
  test: /\.css$/i,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        modules: true
      }
    }
  ],
  exclude: /node_modules/
}

This rule processes CSS files, enabling CSS module functionality.

Example: Using CSS Modules in Components

Define your styles, and reference the class names in your HTML templates. Webpack will handle the conversion to unique class names.

my-component.module.css
.title {
  font-size: 24px;
  color: #333;
}
my-component.html
<template>
  <h1 class="title">Hello, Aurelia!</h1>
</template>

After processing, the title class may be transformed to a unique identifier like title_1a2b3c.

CSS Modules' Special Selectors

CSS Modules support the :global selector for styling global elements without transformation.

Example: Using Global Selectors

my-component.module.css
:global(.button-primary) {
  background-color: #007bff;
  color: white;
}

This style will apply globally to elements with the button-primary class, maintaining the class name without transformation.

Advanced Shadow DOM Usage

Shadow DOM encapsulates a component's styles, preventing them from leaking into the global scope. Aurelia 2 offers granular control over Shadow DOM usage, including global and per-component configuration.

Example: Styling Slotted Content

When using Shadow DOM, you can style content passed into slots using the ::slotted() pseudo-element.

tab-panel.css
::slotted(.tab-content) {
  padding: 15px;
  border: 1px solid #ddd;
}
tab-panel.html
<template>
  <slot name="tab-content"></slot>
</template>

In this example, content assigned to the tab-content slot will receive padding and a border. At the same time, the encapsulation ensures that these styles don't affect other elements outside the tab-panel component.

Example: Theming with CSS Variables

CSS variables can be used within Shadow DOM to create themeable components:

button-group.css
:host {
  --button-bg-color: #eee;
  --button-text-color: #333;
}

button {
  background-color: var(--button-bg-color);
  color: var(--button-text-color);
}
button-group.html
<template>
  <button><slot></slot></button>
</template>

Users of the button-group component can then define these variables at a higher level to theme the buttons consistently across the application.

Example: Scoped Animations in Shadow DOM

Animations can be defined within Shadow DOM to ensure they are scoped to the component:

animated-banner.css
@keyframes slide-in {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

.banner {
  animation: slide-in 1s ease-out forwards;
}
animated-banner.html
<template>
  <div class="banner">Welcome to Aurelia!</div>
</template>

The slide-in animation is encapsulated within the animated-banner component, preventing it from conflicting with any other animations defined in the global scope.

CSS Modules: Advanced Techniques

CSS Modules provide a powerful way to locally scope class names, avoiding global conflicts. With Aurelia 2, you can leverage CSS Modules to create maintainable, conflict-free styles.

Example: Composing CSS Module Classes

CSS Modules support composing classes from other modules, promoting reusability.

styles.css
.baseButton {
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.primaryButton {
  composes: baseButton;
  background-color: #007bff;
  color: white;
}
my-component.html
<template>
  <button class="primaryButton">Click Me</button>
</template>

In this example, primaryButton composes the baseButton styles, adding its own background and text color. CSS Modules ensures that these class names are unique to avoid styling conflicts.

Example: Theming with CSS Modules

CSS Modules can also facilitate theming by exporting and importing variables.

theme.css
:export {
  primaryColor: #007bff;
  secondaryColor: #6c757d;
}
button.css
@value primaryColor, secondaryColor from "./theme.css";

.primaryButton {
  background-color: primaryColor;
  color: white;
}

.secondaryButton {
  background-color: secondaryColor;
  color: white;
}
my-component.html
<template>
  <button class="primaryButton">Primary</button>
  <button class="secondaryButton">Secondary</button>
</template>

By defining and exporting theme variables in theme.css, they can be imported and used in other CSS Modules to ensure consistent theming across the application.

Additional Resources

For more guidance on class and style bindings in Aurelia applications, please take a look at the [CSS classes and styling section](.. templates/class-and-style-bindings.md). This section covers strategies for dynamically working with classes and inline styles.

PreviousBindable propertiesNextSlotted content

Last updated 2 months ago

Was this helpful?