Introduction

CoralUI is a library of components and styles. CoralUI's components are built on web components, a collection of standards that enable encapsulated and reusable components on the web. While the collection of standards holds much promise for the future of the web, CoralUI specifically only takes advantage of the custom elements portion. Other parts of the collection of standards are insufficiently supported in targeted browsers and existing polyfills are too incomplete for feasible use.

Custom Elements

Custom elements allow CoralUI to define new types of DOM elements to be used in an HTML document. As a result, CoralUI can extend native elements like a button or text input to provide additional functionality or it can provide completely new elements like a progress indicator or accordion. Consumers can then use these elements using their custom tags (e.g., <coral-progress>) and directly interact with their custom properties.

Custom elements are not currently supported natively in all target browsers. To allow CoralUI to function properly in all target browsers, a custom elements polyfill has been included with CoralUI which supplies necessary support when native support is not available.

A strong advantage CoralUI derives from custom elements is the ability to hide many implementation details from the consumer. While a progress indicator may be composed of a variety of spans, divs, or images, these are underlying details that shouldn't concern consumers. Rather than consumers copying and maintaining large swaths of code containing all these elements with their various attributes, they are typically able to use a simple custom element tag and the underlying elements are seemlessly produced by CoralUI on their behalf. By keeping implementation details internal to components, the exposed public API is minimized and more explicit resulting in a lower risk of introducing breaking changes. This, in turn, speeds up the CoralUI development process.

CSS

CoralUI's CSS attempts to achieve the following goals:

  • Compatible: Does not conflict with styles in the consuming project.
  • Optimized: Fast!
  • Readable: Conveys context based on the CSS class name alone.
  • Extensible: Maintains flexibility by limiting assumptions concerning DOM hierarchy.

To help achieve these goals, CoralUI follows the SUIT CSS convention. SUIT was chosen over alternative conventions because it is opinionated and well-documented. It also provides a favorable naming style and outlines patterns for handling states, utilities, and JavaScript interaction.

Another tactic CoralUI uses to maintain compatibility with consumer projects is to make styles opt-in-only. In other words, if CoralUI's stylesheet is loaded into a consumer project with no other changes, the look and feel of the consumer project should not change. The only exception to this is that CoralUI comes bundled with Normalize.css which attempts to normalize default CSS styles across browsers.

CoralUI components should avoid requiring that consumers add CSS classes to components; CoralUI components have logic capable of adding CSS classes to their own elements as necessary. They may even add or remove CSS classes based on a property value. On the other hand, there are styles CoralUI provides that do not have accompanying components. For example, table styles are provided for consumer use but no accompanying table component. In these cases, consumers are expected to use the CSS classes directly.

Developer Fundamentals

Programmatically interacting with components

The Components Docs provide detailed information on each component in CoralUI, including the different attributes available, and built-in methods and events on the JavaScript components.

Here are some quick steps to get you started with JavaScript components:

Coral UI 3 widgets can be instantiated using a Coral class instance:

/* Alert - Coral class constructor*/
var alert = new Coral.Alert();
alert.header.innerHTML = "Hey!"
alert.content.innerHTML = "This is an alert.";
alert.variant = "info";

Or by creating a HTML Element directly:

/* Alert - DOM element */
document.createElement('coral-alert');
Hey! This is an alert.

Programmatically interacting with sub-components

Let's say we create an element and add some Coral components to it with markup:

var el = document.createElement('div');
el.innerHTML = '<coral-numberinput></coral-numberinput>';
document.body.appendChild(el);

One would expect we could now do something like this:

var numberInput = el.firstElementChild;
numberInput.value = 10;

This works great in Chrome (which has native Custom Elements support), but does nothing in both Firefox and IE 9 because the element hasn't been upgraded yet.

The workaround is to use the method Coral.commons.ready(el, cb):

var numberInput = el.firstElementChild;
  Coral.commons.ready(el, function()
  numberInput.value = 10;
});

And the full code:

var el = document.createElement('div');
el.innerHTML = '<coral-numberinput></coral-numberinput>';
var numberInput = el.firstElementChild;
document.body.appendChild(el);
// add callback
Coral.commons.ready(el, function() {
  numberInput.value = 10;
});

The above code works in all supported browsers, all of the time.

Background
In browsers, that have native support for custom elements, components are upgraded and ready to go immediately. However, in polyfilled environments, MutationObservers are used to determine when elements are added and need to be upgraded. MutationObservers queue "microtasks", which fire event listeners before the next animation frame. As such, accessing the component API of an element created from markup has to happen on the next animation frame. In browsers, that implement requestAnimationFrame, we can simply insert dynamic markup, then wait for the next animation frame and execute the callback, while being certain that all custom elements have been upgraded.

Coral.Component triggers a coral-component:ready event after the createdCallback runs. Coral.commons.ready(el, cb) will wait for that event (and all ready events of possible sub-components) before calling the provided callback.

Read more about Custom Elements.

Creating components

The magic of creating components begins at Coral.register(). This is used to define the JavaScript "class" that will be instantiated for each component. Let's take a look at creating a simple component to display the weather:

Coral.register({
  name: 'Weather',
  tagName: 'coral-weather',
  className: 'coral-Weather',
  
  properties: {
    temperature: {
      default: 70,
      transform: Coral.transform.number,
      sync: function() {
        this.innerHTML = 'It\'s ' + this.temperature + '° outside today!';
      }
    }
  }
});
  • name defines the class name of the component witihin the Coral JavaScript namespace. Here we've given the value Weather meaning if consumers wished to instantiate the weather component they would do so using new Coral.Weather();.
  • tagName defines the element tag name used within markup to instantiate the component. Here we've given the value coral-weather meaning if consumers wished to instantiate the greeting component using markup they would do so using <coral-weather></coral-weather>.
  • properties defines an object of property names with their accompanying descriptors. These are properties that consumers can use to the interact with the component. Here we've defined a name property.

Property descriptors support a plethora of configuration. For the temperature property we've defined the following:

  • default defines the default value of the property. Here we've defined a default of 70 meaning if a consumer doesn't specify a temperature value then we would like CoralUI to automatically set the temperature property to 70.
  • transform defines how a value should be transformed when being set. For example, if a consumer specified a temperature via markup (<coral-weather temperature="35"></coral-weather>) the value would by default be treated as a string. In our case, however, we want to deal with the value as a number since we may want to eventually compare it to other temperatures to determine if it's hot or cold, etc. CoralUI provides multiple built-in transforms to leverage or you can provide your own. This example leverages the built-in number transform. This helps guarantee that this.temperature will always be a number.
  • sync defines what action should be taken when the property changes. This example updates the content of the component to display the temperature. Note that for optimization purposes sync is called a maximum of once per animation frame regardless of how many times the property is set between animation frames. This minimizes the amount of DOM churn.

Architecture

Coming soon

Base Classes

Coral

Coral is the main namespace for Coral.

See Documentation for Coral

Coral.Collection

Coral.Collection provides a standardized way to manipulate items in a component.

See Documentation for Coral.Collection

Coral.commons

Coral.commons is Coral's utility belt. It contains shared functions that are used throughout Coral.

See Documentation for Coral.commons

Coral.Component

Coral.Component is the base class for every Coral component. It handles the custom element life cycle, eventing, property value DOM synchronization, and visibility.

See Documentation for Coral.Component

Coral.Keys

Coral.Keys provides key combination functionality Coral components.

See Documentation for Coral.Keys

Coral.mixin.formField

FormField provides the basic properties that every component used in a form should have.

See Documentation for Coral.mixin.formField

Coral.mixin.overlay

Coral.mixin.overlay is mixed into components that should be displayed as a floating overlay.

See Documentation for Coral.mixin.overlay

Coral.mixin.selectionList

Coral.mixin.selectionList is a mixin that adds selectable item support to a Coral component.

See Documentation for Coral.mixin.selectionList

Coral.property

Coral.property is a set of property descriptor factories used by Coral components.

See Documentation for Coral.property

Coral.transform

Coral.transform is a set of property value transformation functions for use in Coral components.

See Documentation for Coral.transform

Coral.validate

Coral.validate is a set of property value validation functions for use in Coral components.

See Documentation for Coral.validate

Interfaces

CoralUI provides some interfaces, which are documented in each component's JSDocs. Additional detail on those interfaces is provide below.

Collection Interface

Some components like Select or TabList include multiple items. As an effort to standardize manipulation of these items, we've added a Collection interface, which provides a consistent API for handling multiple items within a component. The Collection Interface is exposed via an items property that gives access to different methods to manipulate items.

Components that support the Collection interface expose the details in their JSDocs.

Form Fields

Components that are typically used inside a form follow a contract that exposes a set of required properties. We call this the FormField interface. Every component in a form is expected to have the following properties: disabled, invalid, labelledBy, name, eadOnly, required, and value. FormField components also trigger a change event without a namespace, which allows the components to be integrated transparently into any form.

CoralUI 3 Migration

The purpose of this section is to ease the migration process, mostly by providing an overview of where there is a difference in the components between older versions and CoralUI 3.

Custom Elements

As described elsewhere in the architecture documentation, CoralUI 3 replaces the traditional markup of CoralUI 2 with Web Components'sCustom Elements. Custom Elements allows the developer to register new tags that serve as components that can be easily reused. This abstracts and simplifies the markup required to initialize each component and eases the process of passing options to each component.

Empty Element

For <coral-icon>, <coral-numberinput>, etc, even though they don't have any body, the end tag is required. i.e. You have to use <coral-icon></coral-icon> instead of <coral-icon />. Otherwise the browser will consider it unclosed element and will close it like a block element.

Object References

The CoralUI 2 implementation stored the JavaScript object as a data attribute. This required using var numberinput = $("#elementId").data("numberinput") to allow access to the API. Because CoralUI 3 components are now Custom Elements, there is not a separate data object. The element contains itself the API, just like a native HTLM Element.

This means that CoralUI 3 JavaScript construction can be done without pre-existing DOM markup, and initialization parameters are now passed as normal element attributes. This also has an impact on how properties are set. In CoralUI 2, we had to interact with this data object, and set the options that it provided. Now, every property is exposed with a JavaScript setter and getter.

Data Attributes

In CoralUI 2, data attributes were used to pass options to the component constructor. For example, CUI.NumberInput required data-min, data-max, and data-init to initialize the markup as a NumberInput. You were also forced to write the complete internal markup of the component and then reference it in the DOM when initializing the JavaScript. Since components are now treated as normal DOM element, initialization parameters are passed as normal element attributes, not data attributes.

Platform Changes

  • The event cui-contentloaded does not exist anymore. Since we are using custom elements, they get upgraded automatically by the browser.
  • No more data-* attributes. As indicated previously, now they are full fledged attributes.
  • No more need to retrieve the underlaying JavaScript object using $("#myNumberInput").data("numberInput");. As mentioned above, object references are the same as any other DOM element.
  • The new Collection interface that standardizes the item manipulation (mentioned above).
  • A FormField interface that exposes the expected properties for every form component (mentioned above).

Component Changes

With CoralUI 3, some components have been renamed or deprecated. This section provides a quick overview.

New Components

  • Coral.ActionBar
  • Coral.Calendar
  • Coral.Card
  • Coral.Clock
  • Coral.Masonry
  • Coral.PanelStack
  • Coral.Table
  • Coral.TabList

Renamed Components

  • CUI.Modal is replaced by Coral.Dialog
  • CUI.TabPanel is replaced by Coral.TabView, which is used in conjunction with a Coral.TabList and a Coral.PanelStack
  • CUI.FlexWizard is replaced by Coral.WizardView, which is used in conjunction with a Coral.StepList and a Coral.PanelStack

Former Core Components

In CoralUI 3, many of the former 'core' components have been promoted to become a custom element. This includes some addition of JavaScript APIs for those components.

    Those components are:
  • Coral.AnchorButton
  • Coral.Button
  • Coral.ButtonGroup
  • Coral.Checkbox
  • Coral.Icon
  • Coral.Progress
  • Coral.Radio
  • Coral.RadioGroup
  • Coral.Table
  • Coral.TextField
  • Coral.TextArea
  • Coral.Wait

Other Notes

  • coral-Button–secondary has been removed; light grey is now the default button color
  • coral-MinimalButton has been incorporated as a variant of Coral.Button
  • Coral.DatePicker: the options 'dayNames' and 'monthNames' have been removed. In the future they will be taken automatically from i18n
  • Coral.NumberInput: the option 'defaultValue' has been removed
  • Coral.PanelStack does not have padding; u-coral-padding needs to be used
  • Coral.Select dropped support for groups
  • Coral.Switch does not support labels. A switch should only be used to express on/off or enable/disabled, thus does not need additional label support