CSS-in-JS in Aurelia 2
What is CSS-in-JS?
CSS-in-JS is a styling technique where JavaScript is used to style components. When this JavaScript is parsed, CSS is generated (usually as an <style> element) and attached to the DOM. It allows you to abstract CSS to the component level itself, using JavaScript to describe styles in a declarative and maintainable way.
In the ecosystem of some client-side libraries, such as React, this method is very common. So we decided to discuss how to use CSS-in-JS in Aurelia.
Why EmotionJS?
There are multiple implementations of CSS-in-JS concept in the form of libraries but few of them are framework-agnostic so we chose EmotionJS
This is how they define EmotionJS:
Emotion is a performant and flexible CSS-in-JS library. Building on many other CSS-in-JS libraries, it allows you to style apps quickly with string or object styles. It has a predictable composition to avoid specificity issues with CSS. With source maps and labels, Emotion has a great developer experience and great performance with heavy caching in production.
EmotionJS & Aurelia 2 integration
To integrate EmotionJS and Aurelia, Follow the steps below:
Add EmotionJS framework-agnostic version to your project with the following command
npm i emotion --save
Define a custom attribute and name it Emotion just like the following code
import { inject } from "aurelia";
import { css, cache } from 'emotion'@inject(Element)
export class EmotionCustomAttribute {
constructor(private element: Element) {
} afterAttach() {
if (this.isInShadow(this.element))
cache.sheet.container = this.element.getRootNode() as HTMLElement; else
cache.sheet.container = document.head; this.element.classList.add(css(this.value));
} private isInShadow(element: Element): boolean {
return element.getRootNode() instanceof ShadowRoot;
}
}
Surely there are questions about the above code let me answer them one by one.
What is isInShadow?
This method helps us to find out our HTMLElement is inside a shadow-root or not.
Why shadow-root matter?
Because Aurelia 2 supports ShadowDOM and we need to style those HTMLElements that are inside a shadow via the emotion library.
What is cache.sheet.container?
The emotion library uses container configuration to inject styles into specific DOM. To support shadow-root we should inject our styles into the shadow block but for global styles document.head is good.
Why afterAttach?
Detecting ShadowDOM mode for an HTMLElement is possible via this life-cycle method.
Now, Register the new Emotion custom attribute in your main.ts file.
import Aurelia from 'aurelia';
import { MyApp } from './my-app';
import { EmotionCustomAttribute } from './emotion-attr';Aurelia
.register(EmotionCustomAttribute)
.app(MyApp)
.start();
Add an object in your view-model and call it cssObject.
export class MyApp {
public message = 'Hello World!';
private color = 'white'
public cssObject = {
backgroundColor: 'hotpink !important',
'&:hover': {
color: this.color
}
};
}
Go to your view and add emotion custom attribute to an HTML tag.
<div class="message" emotion.bind="cssObject">${message}</div>
Congrats. You did this. Run the project and enjoy it.
You can find the source code here.