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');
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 theCoral
JavaScript namespace. Here we've given the valueWeather
meaning if consumers wished to instantiate the weather component they would do so usingnew Coral.Weather();
.tagName
defines the element tag name used within markup to instantiate the component. Here we've given the valuecoral-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 aname
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 of70
meaning if a consumer doesn't specify atemperature
value then we would like CoralUI to automatically set thetemperature
property to70
.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 thatthis.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 purposessync
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.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 byCoral.Dialog
CUI.TabPanel
is replaced byCoral.TabView
, which is used in conjunction with aCoral.TabList
and aCoral.PanelStack
CUI.FlexWizard
is replaced byCoral.WizardView
, which is used in conjunction with aCoral.StepList
and aCoral.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 colorcoral-MinimalButton
has been incorporated as a variant ofCoral.Button
Coral.DatePicker
: the options 'dayNames' and 'monthNames' have been removed. In the future they will be taken automatically from i18nCoral.NumberInput
: the option 'defaultValue' has been removedCoral.PanelStack
does not have padding;
u-coral-padding
needs to be usedCoral.Select
dropped support for groupsCoral.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