Cory Rylan

My name is , Google Developer Expert, Speaker, Software Developer. Building Design Systems and Web Components.

Follow @coryrylan
Lit Web Components

Reusable a11y with Web Components and Lit Controllers

Cory Rylan

- 2 minutes

Lit is a lightweight library for authoring Web Components. One of the features of Lit is its Reactive Controller API. This API allows you to hook into the lifecycle of the component in a composable way. In this post, we will create an AraiModalController to make it easy for any of our components to get all the proper attributes for an accessible Modal.

First, let's take a look at our basic work in progress modal component.

import { html, css, LitElement } from 'lit';
import { customElement } from 'lit/decorators/custom-element.js';
import { AriaModalController } from './modal.controller';

@customElement('test-modal')
export class Modal extends LitElement {
static styles = [css`
:host {
display: block;
padding: 12px;
border: 1px solid #ccc;
max-width: 400px;
margin: 24px auto;
}
`
];

render() {
return html`<slot></slot>`;
}
}

So far, it's pretty basic. We have a slot to project content into our modal and some basic CSS. There are a few different types of components that can be a modal type. Things like popovers, dropdowns, and modal can all have similar accessibility (a11y) behavior.

Leveraging Lit Controllers, we can encapsulate best practices for a11y in a way that makes it reusable for different components. First, let's take a look at the Lit Controller API.

import { ReactiveControllerHost } from 'lit';

/**
* Provides necessary attributes to create a valid aria modal
*/

export class Controller {
constructor(host: ReactiveControllerHost) {
host.addController(this);
}

hostConnected() {
// when component is added to DOM
}

hostUpdated() {
// when component property is updated
}

hostDisconnected() {
// when component is removed to DOM
}
}

Controllers allow us to hook into the component lifecycle without requiring inheritance. For example, in our AriaModalController, we can add the appropriate attributes to make our modal-like components accessible.

import { ReactiveControllerHost } from 'lit';

export class AriaModalController {
constructor(private host: ReactiveControllerHost & HTMLElement) {
this.host.addController(this);
}

hostConnected() {
this.host.setAttribute('aria-modal', 'true');
this.host.setAttribute('role', 'dialog');
}
}

To apply our controller to the modal, we instantiate it as a property of our component.

import { html, css, LitElement } from 'lit';
import { customElement } from 'lit/decorators/custom-element.js';
import { AriaModalController } from './modal.controller';

@customElement('test-modal')
export class Modal extends LitElement {
protected ariaModalController = new AriaModalController(this);

static styles = [css`
:host {
display: block;
padding: 12px;
border: 1px solid #ccc;
max-width: 400px;
margin: 24px auto;
}
`
];

render() {
return html`<slot></slot>`;
}
}

Now when our modal renders the output HTML is the following.

<test-modal aria-modal="true" role="dialog">
modal content
</test-modal>

Now, while this is not everything needed for an accessible modal, it demonstrates that Lit Controllers provide an excellent way to create composable behavior that we can share between components. You can check out the full working demo below!

View Demo Code   
Twitter Facebook LinkedIn Email
 

No spam. Short occasional updates on Web Development articles, videos, and new courses in your inbox.

Related Posts

RxJS

Using RxJS in Lit Web Components

Learn how to leverage RxJS in Lit based Web Components for reactive data streams.

Read Article
Lit Web Components

Build your first Web Component with Lit

Learn how to build your own Web Component to use in any JavaScript framework or frontend environment with Lit.

Read Article
Web Components

Using Modern Web Components

Web Components enable UI components that work in any JavaScript framework. Learn how Web Components work in JavaScript frameworks today.

Read Article