Core concepts

Calcite Components is a library of reusable web components built using Stencil.js. With Calcite Components, you can quickly build on-brand, lightweight, and accessible web applications.

Web components are a native browser standard, and many of the technical concepts necessary to develop with Calcite Components are not specific to the library. This page provides an introduction to key web concepts, which are necessary for effective development. For further reading, all of the concepts on this page can be found on MDN Web Docs and other web standard documentation sources.

Custom elements

Custom elements are part of the Web Components standards, which work across modern browsers using any JavaScript library or web framework with HTML. Custom elements encapsulate functionality, which prevents conflicts with the rest of your code.

Calcite Components are custom elements and can be used similar to native HTML elements. For instance:

Use dark colors for code blocksCopy
1
<calcite-tip heading="Platypus"></calcite-tip>

Slots

Slots are placeholder elements that allow you to add your own content by referencing the slot's name. Slots are a common web components concept, and chances are you already use them. For example, take the following HTML:

Use dark colors for code blocksCopy
1
2
3
4
5
<select>
  <option value="platypus">Platypus</option>
  <option value="sloth">Sloth</option>
  <option value="armadillo">Nine-banded armadillo</option>
</select>

In web component terminology, the option elements are placed in select's default slot. Additionally, the "Platypus", "Sloth", and "Nine-banded armadillo" text is placed in option's respective default slots.

Many Calcite Components also utilize default slots. For instance, in the calcite-tip below, the p content is added to the default slot:

Use dark colors for code blocksCopy
1
2
3
4
<calcite-tip heading="Platypus">
  <img slot="thumbnail" src="platypus.jpg" alt="A platypus sensing its prey using electrical fields." />
  <p>A platypus is a mammal with a bill, similar to a duck. They use their bill to sense prey via electrolocation.</p>
</calcite-tip>

In many cases, a default slot is all that is needed. However, as components become more complicated, the need arises to position and style child elements differently. This is where named slots come into play. In the example above, an image is placed into the tip's thumbnail slot. This informs the component that the image should be handled differently than the elements in the default slot.

If a component has slots, they will be listed in the documentation, such as the slots for calcite-card. You can also learn more about slots on MDN.

Shadow DOM

Custom elements are encapsulated, which keeps their markup structure, style, and behavior hidden and separate from other code on the page. The Shadow DOM is the mechanism that encapsulates custom elements. As a result, Shadow DOM hides and separates a web component's DOM elements, so they are rendered in the browser, but do not clash with the rest of your code.

The Shadow DOM encapsulation creates persistent styling and functionality across your applications, enabling your users to have a consistent user experience.

CSS variables

Calcite Components provides CSS variables to override styles. Due to web components' shadow DOM, the styles can not be easily altered without CSS variables. There are CSS variables for tokens that are used throughout the design system, including color and typography.

Additionally, some Calcite Components have their own CSS variables to change component-specific styles. These CSS variables can be found in a component's documentation, such as the CSS variables for calcite-loader.

An example use case is swapping the foreground and text colors in calcite-notice using CSS variables.

Use dark colors for code blocksCopy
1
2
3
4
calcite-notice {
  --calcite-color-foreground-1: #151515;
  --calcite-color-text-1: #ffffff;
}

The CSS variable MDN documentation provides a detailed explanation of the functionality.

Loading components

Web components start as plain HTML elements, and are upgraded as soon as their implementations are defined in the browser. Calcite Components are automatically defined when they are imported and used in an application. However, sometimes it is necessary to wait until a component is defined before executing specific code.

Hydration

Stencil.js provides the option to add a flag when a component and all of its child components have finished hydrating. This helps prevent any flash of unstyled content (FOUC) as various components are asynchronously loaded and rendered. In Calcite Components, the calcite-hydrated attribute is added to components once they are hydrated, and is useful when debugging your application.

When defined

The whenDefined() method of the customElementRegistry interface returns a promise, which is fulfilled when the specified element is defined.

Once the promise is fulfilled, you can run code that requires the component to be defined, like so:

Use dark colors for code blocksCopy
1
customElements.whenDefined("calcite-button").then(() => document.querySelector("calcite-button").setFocus());

Component on ready

To determine when a component is rendered, you can use Stencil's componentOnReady() method. The method returns a Promise that resolves after the componentDidLoad() lifecycle method fires. It is useful for making sure a component is loaded before using its methods, or when one component is dependent on another.

For example, you may want to display calcite-loader until other component(s) finish rendering:

Use dark colors for code blocksCopy
1
2
3
4
5
(async () => {
  await customElements.whenDefined("calcite-alert");
  await document.querySelector("calcite-alert").componentOnReady();
  document.querySelector("calcite-loader").hidden = true;
})();

Calling a component's method as the callback of requestAnimationFrame ensures the user interface updates with the component's state. For example, if you want to set the current step for calcite-stepper based on the user's browsing history, you can use the goToStep() method:

Use dark colors for code blocksCopy
1
2
3
4
5
(async () => {
  await customElements.whenDefined("calcite-stepper");
  const el = await document.querySelector("calcite-stepper").componentOnReady();
  requestAnimationFrame(() => el.goToStep(3));
})();

Events

Calcite Components creates and triggers events using the CustomEvent() constructor.

CustomEvent behaves similarly to Event, which is emitted by HTML elements, such as when clicking a button. For example, you can still access the element in the event payload's target property.

You can view a component's documentation page to determine whether it has any events, such as the calcite-pagination event. For example:

Use dark colors for code blocksCopy
1
2
3
document.addEventListener("calcitePaginationChange", event => {
  console.log(`Starting item number on the page: ${event.target.startItem}`);
});

Global configuration

Version

Since 2.10, developers can use the calciteConfig global variable to detect the Calcite version at runtime.

Use dark colors for code blocksCopy
1
window.addEventListener("load", () => console.log(window.calciteConfig.version));

Log messages

Since 2.11, key messages are logged to the console, such as component deprecations. Developers can opt to remove messaging from production environments and builds using calciteConfig:

Use dark colors for code blocksCopy
1
2
3
var calciteConfig = {
  logLevel: "off"
};

Forms

Each component within a form must be populated with a name attribute in order to pass the value properly on form submission. For instance, adding name attributes to Input Date Picker and Text Area:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
<form>
  <calcite-label>
    Observation date:
    <calcite-input-date-picker name="observation-date"></calcite-input-date-picker>
  </calcite-label>
  <calcite-label>
    Observation notes:
    <calcite-text-area name="observation-notes" placeholder="Observation notes" max-length="250"></calcite-text-area>
  </calcite-label>
  <calcite-button type="submit">Submit</calcite-button>
</form>

For additional considerations with forms, explore accessibility with forms.

Form validation

Form validation includes the use of the status, validationMessage, and validationIcon properties. The properties support default and custom validation messages and icons when a component's status property is "invalid".

Validation constraints

To set up custom constraints:

  • Create a validation constraints array to define custom constraints, messages, and icons for specific fields using their id.

  • Utilize a function like setCustomValidity to set the validationMessage, validationIcon, and status on the respective fields.

  • Use an event listener to check the user input against the predefined validation constraints. If the user input does not meet the specified constraints, a custom validation message is set using the setCustomValidity function.

Patterns

The use of the pattern attribute can support constraints used for form validation. When used, it defines a regular expression that the input value must match in order for the form to be considered valid. For instance, defining validationMessages and validationIcons that will be displayed when a specified part of the pattern is matched.

Modes

Calcite Components provides light and dark modes, which can be changed using their corresponding CSS classes: calcite-mode-light and calcite-mode-dark. There is also a calcite-mode-auto class which defers to the browser's prefers-color-scheme CSS media query to decide whether the light or dark mode will be used.

Setting the mode class on an element changes all of its child nodes as well. Therefore, to switch the entire application from light to dark, you can do the following:

Use dark colors for code blocksCopy
1
2
3
<body class="calcite-mode-dark">
  <!-- Your application content -->
</body>

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.