Skip to content

Using Components

Quiet includes a collection of Web Components , or custom HTML elements. They have attributes, properties, events, and methods, all of which are described in the documentation for each component.

To use the components, install them using the instructions found on the installation page.

Once installed, simply add the components you want to your HTML.

<quiet-button>
  I'm a button
</quiet-button>

You can obtain references using document.querySelector() and document.getElementById() just like a native HTML element.

const button = document.querySelector('quiet-button');

Some HTML elements, such as <img>, are void elements , meaning you can omit their closing tag. Custom elements cannot be void elements, so make sure you always close their tags!

Awaiting registration Jump to heading

Since custom elements are indifferent to frameworks, there's no initialization phase to ensure components have been registered before using them. Thus, before accessing properties or methods on custom elements, you need to ensure they're defined first. You can use customElements.whenDefined() for this.

await customElements.whenDefined('quiet-button');

// <quiet-button> is defined and ready to use!

However, this can be cumbersome when you have a lot of components on the page. For convenience, Quiet provides an allDefined() function that waits for all Quiet components that are currently on the page to finish registering.

CDN npm
import { allDefined } from 'https://cdn.jsdelivr.net/npm/@quietui/quiet-browser@1.0.0/dist/quiet.js';

await allDefined();

// All `<quiet-*>` elements defined and are ready to use!
import { allDefined } from '@quietui/quiet';

await allDefined();

// All `<quiet-*>` elements defined and are ready to use!

You can use this function in the beginning of your app to wait for components to load up front, or you can use it on demand whenever you need to access a component's properties or methods with JavaScript.

Advanced usage

You can provide options to change how this function works, allowing you to listen for other custom elements and/or custom elements that may not be on the page when the function is called. All available options are shown below.

await allDefined({
  // Wait for all `<quiet-*>` elements on the page
  match: tagName => tagName.startsWith('quiet-'),

  // Also wait for `<foo-element>` and `<bar-element>`
  additionalElements: ['foo-element', 'bar-element'],

  // Look in the current document (can also be a shadow root)
  root: document
});

This function will throw if a registration fails for any reason, so you'll probably want to wrap it in a try...catch block!

Attributes & properties Jump to heading

Most components have properties that can be set with attributes. For example, buttons have a variant attribute that let's you change the type of button that gets rendered.

Primary Destructive
<quiet-button variant="primary">Primary</quiet-button>
<quiet-button variant="destructive">Destructive</quiet-button>

Some properties are Boolean, meaning they only accept true or false values. Boolean properties are false when the attribute is absent and true when the attribute is present. For example, you can disable a button by adding the disabled attribute. To enable the button later on, remove the disabled attribute.

Disabled
<quiet-button disabled>
  Disabled
</quiet-button>

Some attributes will reflect, or be added/removed automatically, when setting the property via JavaScript. You can determine if an attribute reflects by looking at the properties table in the component's documentation.

The updateComplete property Jump to heading

Components batch DOM updates for performance, so you might run into scenarios where you update a property but don't see the changes in the DOM right away. In this case, you can await the updateComplete property, which is available on every component.

Here's an example where we update a progress bar's value and inspect the corresponding attribute. Note how the attribute isn't updated until awaiting updateComplete.

<quiet-progress value="0"></quiet-progress>

<script>
  const progress = document.querySelector('quiet-progress');
  progress.value = 100;

  // outputs "0"
  console.log(progress.getAttribute('value'));

  await progress.updateComplete;

  // outputs "100"
  console.log(progress.getAttribute('value'));
</script>

If you're updating multiple elements, you can safely use requestAnimationFrame() instead of awaiting every individual component.

Slots Jump to heading

Many components accept content through slots . Slots are a platform feature that work very similar to the slots you may have used in Vue. A custom element can have any number of slots.

The default slot is almost any content inside the component. In this example, we're slotting a text node into the button to serve as its label, but it could be an HTML element as well.

Like
<quiet-button>
  Like
</quiet-button>

However, some components also have named slots. To insert content into a named slot, add the slot attribute to any top-level child element of the component. The position of the slotted content doesn't matter, as long as it's not nested within another element. The component will automatically render the slotted content in the correct location.

Like Like
<quiet-button>
  <quiet-icon slot="start" name="thumb-up"></quiet-icon>
  Like
</quiet-button>

<quiet-button>
  Like
  <quiet-icon slot="start" name="thumb-up"></quiet-icon>
</quiet-button>

You can insert more than one item into a named slot. For example, this card has a footer slot that houses two buttons. The text node remains in the default slot.

The sleepy tabby cat stretched languidly in the sunbeam, her whiskers twitching as she dreamed of chasing mice and climbing curtains with boundless energy. Primary Default
<quiet-card style="max-width: 340px;">
  The sleepy tabby cat stretched languidly in the sunbeam, her whiskers twitching as she dreamed of chasing mice and climbing curtains with boundless energy.

  <quiet-button slot="footer" variant="primary">Primary</quiet-button>
  <quiet-button slot="footer">Default</quiet-button>
</quiet-card>

Events Jump to heading

Many components emit custom events when certain things happen. For example, a <quiet-tab-list> emits an event called quiet-tab-shown when a tab is shown. You can listen for custom events the same way you listen for native events.

<quiet-text-field></quiet-text-field>

<script>
  const textField = document.querySelector('quiet-text-field');

  textField.addEventListener('quiet-input', event => {
    // The text field has received input
    console.log(event);
  });
</script>

You can also listen to native events on custom elements. However, it's important to understand that native events occur inside the component's shadow DOM and are retargeted to the host , so they may not always work the way you expect — particularly in complex components. If a custom event is available, it's always better to use it over a native one.

Event bubbling is a common pitfall. In the same way native HTML elements all dispatch a click event, Quiet components may dispatch custom events that aren't unique to the component. Learn more about custom event bubbling.

Some custom events contain a detail property with additional information. You can access this information through the detail property of the event.

document.addEventListener('quiet-discovery-complete', event => {
  console.log(event.detail);
  // result { registered: [], unknown: [] }
});

The information contained in an event's detail property is described in the respective component's documentation.

Methods Jump to heading

Some components have methods you can call. To call a method, you'll first need to obtain a reference to the element. For example, you can programmatically set focus to a button by calling its focus() method.

const button = document.querySelector('quiet-button');

button.focus();

Not all components have methods. Those that do will be listed in the respective component's documentation.

Styling custom elements Jump to heading

Custom elements commonly use a platform feature called shadow DOM to encapsulate behaviors and styles. Their internals are rendered in a separate document fragment, which prevents most styles from leaking in from and out to the main document.

Speaking of internals…it's important to look at the documentation for each component to understand its anatomy before applying any CSS. A component often requires styles to target a specific part inside of it rather than the element itself.

By design, some styles, such as font size and family, will inherit through the shadow DOM. However, the majority of them do not. Thus, you cannot use standard CSS selectors to target a custom element's internals. Instead, you need to use one of the following APIs.

Please read this section thoroughly if you've never styled custom elements before. It's a common frustration because it involves CSS features you may not have used before.

CSS custom properties Jump to heading

CSS custom properties , often referred to as CSS variables, are unique in that their values will inherit through the shadow root. Custom properties are typically used when a value needs to be reused more than once within the custom element, or when they map to a property that isn't necessarily intuitive.

For example, <quiet-spinner> renders an animated SVG in its shadow root. The color of the indicator is applied to a <circle> element within the SVG, but the implementation detail shouldn't matter to a user who just wants to make the spinner pink.

For this reason, this spinner exposes a custom property called --indicator-color that you can set to change the color. You can set a custom property just like any other CSS property.

<quiet-spinner style="--indicator-color: deeppink;"></quiet-spinner>

You can also set custom properties inside a stylesheet or a <style> element.

quiet-spinner {
  --indicator-color: deeppink;
}

Abstractions like this allow custom element authors to change or even replace a custom element's internals without modifying its public API. This means less breaking changes for you later on!

Not all components expose custom properties. Refer to the documentation to see which custom properties a component has.

CSS parts Jump to heading

Many components expose parts inside the shadow DOM that you can target with CSS. Unlike custom properties, which only modify individual properties, a part gives you complete control over the exposed element's styles.

Use the ::part() selector to target a specific part in your CSS. This example applies a striped background image to the progress bar's indicator.

<quiet-progress id="custom-progress-bg" label="Progress" value="50"></quiet-progress>

<style>
  #custom-progress-bg::part(indicator) {
    background-image: linear-gradient(-45deg, #fff3 25%, transparent 25%, transparent 50%, #fff3 50%, #fff3 75%, transparent 75%, transparent);
  }
</style>

One caveat of parts is you can't select children, siblings, or structural pseudo elements of a part such as :first-child, :last-child, :first-of-type, etc. This would break the principal of encapsulation and it tends to be a source of frustration for users.

You can, however, select non-structural pseudo elements such as ::before and ::after, as well as pseudo classes like :hover, :active, :focus. If you're on a device that support hovering, hover over the indicator and observe the background color change.

<quiet-progress id="custom-progress-hv" label="Progress" value="50"></quiet-progress>

<style>
  #custom-progress-hv::part(indicator):hover {
    background-color: deeppink;
  }
</style>

Another caveat of parts involves animation. When you target an element with ::part(), you're changing styles inside the shadow DOM. However, animations require keyframes to exist in the same document, and it's not currently possible to define keyframes from outside the shadow DOM. You can, however, apply animations to the host element itself.

Not all components expose parts. Refer to the documentation to see which parts a component has.

Custom states Jump to heading

Some components have custom states, a newer API that let's you target a custom element when it's in a particular state, such as active or disabled.

Toggle buttons, for example, have a toggled state you can use to target buttons when they're activated. Try clicking the button below and observe its styles.

Toggle me
<quiet-button id="toggle-button" toggle="off">Toggle me</quiet-button>

<style>
  #toggle-button:state(toggled) {
    outline: dashed 4px deeppink;
    outline-offset: 4px;
  }
</style>

Not all components have custom states. Refer to the documentation to see which custom states a component has.

Search this website Toggle dark mode Get the code on GitHub Follow @quietui.org on Bluesky Follow @quiet_ui on X

    No results found