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

Lit Web Components

High Performance HTML Tables with Lit and CSS Contain

Learn how to easily create HTML tables in Lit with high performance rendering using CSS contain.

Read Article
Lit Web Components

High Performance HTML Tables with Lit and Virtual Scrolling

Learn how to easily create HTML tables in Lit from dynamic data sources.

Read Article
Lit Web Components

Creating Dynamic Tables in Lit

Learn how to easily create HTML tables in Lit from dynamic data sources.

Read Article