Skip to content

Before you build

Dave Lockhart edited this page Feb 19, 2019 · 24 revisions

This article describes the different steps you should go through before you start building your web component.

checklist

Topics:

Shared vs. One-Off

First, what do we mean by a "shared" (or reusable) web component?

A shared web component:

  • Has its code decoupled from any particular project
  • Has its own repo in the BrightspaceUI or BrightspaceHypermediaComponents organizations
  • Has its own tests, documentation with code demos and samples, and CI system
  • Is versioned independently using semver
  • Has a corresponding design on design.d2l
  • Is architected to meet a variety of use cases
  • Is localized, accessible and performant

Conversely, a one-off web component might live inside your project's repository alongside other project front-end code, making it less discoverable to others. It's probably more unique to your particular application, or isn't quite ready to become a defined design pattern yet.

Should I build a shared component?

Building and publishing a shared web component for everyone to use can be exciting! It's also tempting to get ahead of ourselves and build something before the design has been solidified, or without considering use cases beyond our own.

Before building anything, ask yourself...

Does it already exist?

The most important question to answer before building a new web component is to find out whether someone has already done the work for you. Unfortunately, we don't currently have a catalogue of all web components in existence or a component "wish list".

For some places to look, see: Where to find existing Web Components

Is a shared component necessary?

This is a tougher question, and is often best answered with development and design in the conversation.

Remember, shared components aren't free! They come with additional architectural and design effort, extra considerations for accessibility and performance, and overhead for testing, documentation and ongoing support. Once a shared component is built and widely adopted, it's more difficult to make changes.

Here are some questions you can ask to decide if a shared component is a worthwhile endeavour:

  • Is the component already needed in multiple other applications?
  • Do you foresee this component being used in many other applications in the future?
  • Is the design organization on board with using this component or pattern everywhere and are they ready to invest in formalizing the component's design on design.d2l?

Ultimately, you want to weigh the cost of making the investment now against the eventual payoffs.

If you decide not to build a shared web component, you should still keep it separate in your project and treat it as though it might live elsewhere in the future. That way it can easily be factored out down the road.

Working with Design

Let's recognize that the Daylight design system and our web components are a collaboration between development and design.

daylight design system

It's important to involve both a designer and a developer -- we want to be developing both the component itself as well as the necessary design pattern on design.d2l in tandem. Yey, teamwork!

Questions

For a great example of how design and development can work together to define a web component library, check out "Designing a Design System for Modular Modules, and Building a Team to Build It" from the 2017 Polymer Summit (10 minutes).

screenshot from designing a design system session

These are great questions for developers and designers to go through together, to ensure our design documentation is in line with our implementation:

What is it called?

It may sound trivial, but simply deciding what a particular component or pattern does, what it's for and what it's called is a critical first step.

For more about choosing a component name, see: Component Architecture: element name

What's it made of?

From there, break the component down. What underlying native HTML elements are brought together to make this component unique? Example: "a heading, a label and a button"

Beyond native HTML elements, is it composed of other web components? Is it meant to be part of a larger component or workflow?

What are its variations?

Can it be configured using attributes? What are those attributes called and what are their possible values? For more about component attributes, see: Component Architecture: attributes.

Does the component have extendable spaces (i.e. <slot>s) where arbitrary content can be inserted? More: Component Architecture: slots.

At what point have you reached a threshold where a single component should be split into multiple?

How does it scale with screen size?

How does the component behave responsively for smaller devices? Do things get hidden, shrink, become stacked?

What are its styles?

Will it use CSS properties or mixins for colour, padding, or other aspects of its appearance?

Which style variables does it reference? (e.g. “primary” color, “alert-error” color, “body” font size)

Start Early!

If a design pattern does not exist yet, please keep in mind that it usually takes a designer a few weeks to do the necessary investigation, testing and iteration to make sure that a design will work well in all the various places that it might be used across the product, as well as to build consensus with the rest of the team. Please involve them early enough, and plan for the time that's required!

Web Component Architecture

Once you've decided to build a component, and have collaborated with design to define a pattern, it's time to think about the technical design of your component. "Architect" is a word we don't often apply to front-end development, but since these web components will be consumed by lots of developers other than yourself, and seen by millions of users, it's important to get this stuff right.

Element Name

As noted above, choosing the name of your web component should be done in collaboration with design.

Some things to consider when naming your element:

  • Is there an already agreed upon industry name for a particular UI component (e.g. tabs, carousel)? Did this component exist in a previous D2L UI framework generation whose name is still applicable?
  • Our elements live in a global namespace, so all elements must begin with <d2l-*
  • Make your element name as unique as possible, within reason. For example, don't name it something generic like <d2l-item>, instead call it <d2l-menu-item> or <d2l-selectbox-item>.
  • Child elements should share a prefix, for example: <d2l-menu>, <d2l-menu-item>, <d2l-menu-item-separator>
  • Similarly, related elements that live in the same repo should share a prefix, for example: <d2l-button>, <d2l-button-subtle>, <d2l-button-icon>
  • Look to see if your element belongs to a particular category of components that all share a particular prefix or postfix. Example, "inputs": <d2l-text-input>, <d2l-file-input>, etc.

Attributes

Attributes are the primary way developers will configure your component.

When choosing your component's attributes:

  • Follow the patterns already established with native HTML elements. For example, the disabled boolean attribute for inputs.
  • Try to strike a balance between making your component flexible for future use cases, but avoid over-engineering. Start with a thin slice of functionality, but leave the door open to future enhancements.
  • Boolean attributes do not require a value, e.g. <my-element disabled>, not <my-element disabled="totally">
  • Take special care in choosing the default value for your attributes. Aim for having 80% of consumers being happy with the default, even if it doesn't correspond to your initial use case.

Events and cross-component communication

When your component communicates and interacts with other elements, the pattern we want to use is:

"Attributes down, events up".

attributes down, events up

This pattern simplifies communication, makes debugging easier, and reduces the likelihood of triggering infinite change cascades/loops. In practice, it means that your component will set attributes on its children to modify state, and child components will communicate up to your component using standard browser events.

When naming your events, prefix them with your component's name. For example: d2l-menu-item-select.

JavaScript API

A good general rule is to completely avoid having a JavaScript API on your component, especially for manipulating state. Instead, use attributes whenever possible.

By requiring consumers of your component to call a method in JavaScript, they need to actually write JavaScript. Attributes on the other hand, can be set right in the HTML markup or through a data-binding framework. When attributes can completely define a component's state, any state can be re-created without any JavaScript.

Compare these two examples of opening or closing a dialog component:

Using attributes:

<d2l-dialog open>Dialog is open initially</d2l-dialog>

Using JavaScript methods:

<d2l-dialog id="my-dialog">I want this to be open initially... why do I have to write JavaScript?</d2l-dialog>
<script type="module">
WebComponents.waitFor(() => {
    document.getElementById('my-dialog').open();
});
</script>

Attributes also open the door to using CSS attribute selectors to target styles:

d2l-dialog {
    display: none;
}
d2l-dialog[open] {
    display: block;
}

Slots

Slots are a shadow DOM concept that provide a powerful extension and customization mechanism. They let you define certain placeholders within your element where consumers can place their own markup.

#shadow-root
  <slot name="icon">No icon specified.</slot>
  <span id="something">
    <slot>Default slot</slot>
  </span>

Components can have a default slot (<slot>) and named slots (<slot name="foo">). Components can also define a slot's fallback content if a consumer doesn't specify anything. More: Google Web Fundamentals: Web Components - Composition and Slots.

Recommendations:

  • Similar to attributes, strike a balance between flexibility and complexity when choosing which slots to expose
  • To limit what a consumer can put inside a slot, an attribute may be more appropriate. For example, <my-button text="click me"></my-button> may be preferable to <my-button>click me</my-button> if you want to ensure that nothing other than text can go inside the button.

Get an Architecture Review

Once you've spent some time thinking about your web component's architecture, reach out on the #web-platform Slack channel and get someone to review it. The developers in that channel have a wealth of experience and knowledge to share, so take advantage of it!

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.