Christopher Bloom
Frontend developer, lover of design systems, CSS architecture, and all things javascript.
(and other CMSs)
Adam Juran & Christopher Bloom
Source: Pixabay
Principal Developer, Knapsack.cloud
Design systems advocate
Former maintainer of the Particle project
Swears too much in professional settings
Frontend Architect, 1xINTERNET.de
Design systems advocate
Standard-bearer of best practices
Trained opera singer, black belt in Kung Fu
<ds-card mode="light">
<ds-image slot="card-header" image="/desk.jpg" lazyload></ds-image>
<ds-title slot="card-title" variant="large">
Boost your conversion rate!
</ds-title>
<div slot="card-button">
<ds-cta link="/prices" text="Click here!"></ds-cta>
<ds-avatar url="dudeface.jpeg"></ds-avatar>
</div>
Card body text goes here!
</ds-card>
"Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps."
—https://www.webcomponents.org/
Because that means you can create frontend components that are usable within CMSs and other applications.
<ds-card></ds-card>
We've been searching for the Holy Grail of design systems for over a decade.
It exists.
Web Components have been a web standard since 2011, with widespread browser support since 2018.
// index.js
class MyHello extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML =
`<h1>Hello ${this.getattribute('name')}</h1>`
}
}
customElements.define('my-hello', MyHello);
<!-- index.html -->
<my-hello name="world"></my-hello>
// index.js
class MyHello extends HTMLElement {
connectedCallback() {
const shadow =
this.attachShadow({ mode: "open" })
shadow.innerHTML =
`<h1>Hello ${this.getAttribute("name")}</h1>`
}
}
customElements.define("my-hello", MyHello)
<!-- index.html -->
<my-hello name="world"></my-hello>
import {LitElement, html, css} from 'lit';
import {customElement} from 'lit/decorators';
@customElement('my-hello');
class MyHello extends LitElement {
static get styles() {
return css`
:host {
background: var(--primary-color, rebeccapurple);
display: inline-block;
color: white;
padding: 10px;
border-radius: 5px;
font-family: sans-serif;
}
`;
}
render() {
return html`I'm styled!`;
}
}
LIT 2.0!
CSS variables provide flexibility.
// index.js
class MyHello extends LitElement {
static get styles() {
return css`
:host {
background: var(--primary-color, rebeccapurple);
}
`;
}
}
<!-- index.html -->
<div class="css-var-example">
<my-hello name="world"></my-hello>
</div>
Default
/* styles.css */
:root {
--primary-color: red;
}
/* styles.css */
:root {
--primary-color: red;
}
.css-var-example {
--primary-color: blue;
}
:root variable overrides default
scoped variable overrides :root
<!-- block--my-hello.html.twig -->
<div{{ attributes }}>
{{ title_prefix }}{{ title_suffix }}
{% block content %}
<my-hello name="{{ content.field_headline|field_raw('value') }}"></my-hello>
{% endblock %}
</div>
That's it. Treat custom elements like Twig partials.
Publish your design system (web components) as an NPM package.
GitHub NPM package repository
GitHub git repository
Consume your design system within your Drupal/React/etc app.
{
"name": "drupal-project",
"version": "0.0.1",
"description": "Frontend functionality for this Drupal 9",
"author": "Christopher Bloom",
"license": "ISC",
"dependencies": {
"@illepic/lit": "^1.0.0"
},
"scripts": {
"postinstall": "cp -R node_modules/@illepic/lit themes/awesome-theme/dist"
}
}
[DRUPAL_PROJECT]/package.json
design-system:
js:
dist/example-card.js:
attributes:
type: module
css:
base:
dist/global.css: {}
[DRUPAL_PROJECT]/themes/awesome-theme/awesome_theme.libraries.yml
Automatically release new versions of your design system packages.
Benefits from design systems tooling (i.e. knapsack.cloud!)
Release cycle out of bound from application
Much easier to iterate on the design system with a mechanism to lock Drupal to a specific version!
Simple "rollback" for frontend regressions.
{
"name": "drupal-project",
"version": "0.0.1",
"dependencies": {
"@illepic/lit": "1.12.0"
}
}
{
"name": "drupal-project",
"version": "0.0.1",
"dependencies": {
"@illepic/lit": "1.11.0"
}
}
"Oops, go back!"
// github.com/lit/lit/tree/main/packages/labs/react
import * as React from 'react';
import {createComponent} from '@lit-labs/react';
import {MyElement} from './my-element.js';
export const MyElementComponent = createComponent(
React,
'my-element',
MyElement,
{
onactivate: 'activate',
onchange: 'change',
}
);
... not Twig Pattern Lab
Encapsulation.
Distribute your components with confidence.
Does this mean we no longer need Twig?
Twig is essential as the theming layer to connect Drupal data to frontend design components. In fact, we highly recommend these twig projects:
Yes AND.
https://lit.dev/docs/releases/upgrade/#load-polyfill-support-when-using-web-components-polyfills
Bundling
FoUC
SEO
TTI
SSR
https://github.com/illepic/lit
By Christopher Bloom
Use web components instead of Twig for your design system. Drupal loves it!
Frontend developer, lover of design systems, CSS architecture, and all things javascript.