6M Technical Reference

6M version 2.2.0

This document describes the rules by which TUI 6M components are to be developed. If you don’t know what 6M is, read the 6M Intro. If you are developing a 6M component for the first time, you may find the 6M tutorial handy.

The 6M reference describes only the mandatory traits of a 6M component. You are strongly encouraged to have a look at the best practices as well in order to develop a high-quality component.

The frontend

Web component

From the frontend perspective, a 6M component is a web component, which produces a custom element.

The names of the custom element must be prefixed with tui- and must be unique. If you’re unsure if a name is already taken, look into the components catalog.

Tag and attribute naming

The initial configuration of the custom element must happen through HTML attributes. In order to allow the element to be used multiple times within the same context (i.e. on the same or in the same SPA), it must support a scope attribute. If the scope attribute is set, all event publishers and subscribers registered by the component must respect the scope.

The following naming rules for attributes apply:

  • Do not use global attributes/properties such as id, class, style, is, hidden or slot for element-specific features.
  • Do not use the data- prefix for attributes on custom elements. They are not needed and make code less readable.
  • The locale attribute must only be used for setting the component’s locale (see below).
  • The scope attribute must only be used for setting the component’s event scope (see below).
  • Attribute names must be lower case.

Inner/private web components

If your 6M component ships additional custom elements, e.g. to build its inner parts, remember to prefix the names of these inner elements with the main component’s tag name followed by two dashes. For example, if your component’s tag is tui-image-gallery, and you define a button as an inner element, this element’s tag name should be something like tui-image-gallery--button.

Attribute changes at runtime

At the moment, it is not mandatory that the component reacts to attribute changes which occur after their initialisation. But this may become a requirement in the future, therefore it is recommended to implement this feature, if possible.

Skeletons

The component must provide at least one skeleton placeholder for each of its custom elements. Skeletons are neccessary to show an early preview of the layout and reduce flickering on the screen during loading/rendering of a website.

In practice, the owner of a website will use a custom element together with one of its skeletons like this:

<tui-image-gallery hotel="ABC1234">
<!-- BEGIN SKELETON -->
<div style="background: #eee; padding: 30px; display: flex;">
<div style="background: #ddd; width: 110px; height: 110px; border-radius: 50%"></div>
<div style="flex: 1 0; margin-left: 30px">
<div style="background: #ddd; height: 30px; width: 100%; margin-bottom: 10px;"></div>
<div style="background: #ddd; height: 30px; width: 100%; margin-bottom: 10px;"></div>
<div style="background: #ddd; height: 30px;"></div>
</div>
</div>
<!-- END SKELETON -->
</tui-image-gallery>

The skeleton must be styled with inline CSS to have the exact size of the real element. It should also show the shapes of its primary content elements.

Each Skeleton template must be put into a separate HTML file.

Web components have a feature called Slots. Slots can be anonymous or named. If you are using slots in your 6M component, please keep in mind that a skeleton would be inserted into anonymous slots. Therefore it is mandatory to only use named slots in 6M components.

Styling and generic elements

Components should be usable in different branding contexts. For that purpose, TUI has developed a design system based on generic web components, called UI Elements. You can and should use them in your web component to reduce the resource footprint of your 6M component. To learn more about available elements and their usage, have a look at the Element section on this website.

The component must also support branding based on the color schemes of the different TUI brands and be aware of the brand attribute on the host element, i.e. styling via :host([brand=…]). Have a look at the design.tui documentation to learn more.

The JavaScript

It is entirely up to you how the JavaScript looks on the inside, just take care of the following:

  • The component must run in all common browsers, mobile and desktop (yes, this includes IE 11).
  • There are already polyfills for web components v1, provided through UI Elements, which are based on the Stencil project.
  • Your code must not pollute the global (window) namespace, or at least take measures to avoid collisions.
  • It must not have any external or cross-dependencies, unless they are shipped as a part of the component. (Exceptions are UI Elements and the Cotton Ball.)
  • Your code must not mess with JavaScript internals (i.e. no “monkey-patching”).
  • While you are free to use any framework/libraries you want, you should prefer lightweight solutions where possible.
  • Think about performance and memory footprint when writing your code.

Please have a look at the unified build process requirements, too. There are several rules regarding the built JavaScript.

Event Handler (the “Cotton Ball”)

6M components talk to each other through events managed by a Publish/Subscribe event handler which we call the Cotton Ball. The Cotton Ball is the only “hard” dependency of a 6M component, and as such will always be present in the context of a website, so you can expect it to be there.

Cotton Ball API

The Cotton Ball has the following methods:

/**
* Emits an event.
*
* @param string componentName The components own name.
* @param string scope The scope, as passed through the `scope` attribute.
* @param string eventName The name of the event.
* @param object payload The event data/payload.
*
* @return void
*/

tuiCottonBall.publish(componentName, scope, eventName, payload)

/**
* Emits an event.
*
* @param string componentName The name of the component from which an event is expected.
* @param string scope The scope in which to listen.
* @param string eventName The name of the event to listen on.
* @param function callback Callback
*
* @return function Callback to unregister the event
*/

tuiCottonBall.subscribe(componentName, scope, eventName, callback)
Event scoping

6M has the concept of scoping: A 6M component can be put into one or more scopes, thus grouping it together with other components. The components HTML tag must support the scope attribute to which the maintainer of a host site can assign the scope(s).

When a component publishes, or subscribes to, an event, it must always pass the value of the scope attribute. Usually the value will be a string, but it may also be undefined if the host site maintainer does not assign a scope. The Cotton Ball will take care of distributing events into the right scopes.

Component and event names

The publish and subscribe methods have two name-related parameters: componentName and eventName. The componentName is the root namespace, the eventName is the specific name. Names are written in lowercase and may contain lowercase letters, dashes, underscores and numbers. Event names may additionaly contain dots for separating sub-namespaces.

const currentScope = this.getAttribute("scope") || "*"
tuiCottonBall.publish("image-gallery", currentScope, "init", {/* … */})
tuiCottonBall.publish("image-gallery", currentScope, "imgage-gallery.slider.open", {/* … */})

Avoiding collisions

Remember that your component runs in the same website context as other components. Wherever you occupy resources or assign names, remember to avoid collisions. Some areas to look out for are:

  • Custom Element tags
  • CSS class names (outside of Shadow DOM)
  • Cookie names
  • Local/Session Storage keys

Use appropriate measures, such as namespacing, to avoid collisions.

Localisation

All components must be multilingual by design, supporting the en-US locale as fallback as well as at least one additional locale (usually de-DE). Note that in JavaScript and HTML, the language and country parts are separated by a dash, not an underscore.

Locales are set through the locale attribute on the element. If the component doesn’t ship localisation for the requested locale, it should try to use a similar one, or en-US as fallback.

For example, if the locale attribute requests the de-AT locale, but the component only supports de-DE, it should switch to that one. If the locale attribute requests the pt-BR locale, and the component doesn’t support Portuguese at all, it should switch to the en-US locale.

You are free in your choice of localisation implementation, though you might want to have a look at the l10n library maintained by the TUI.com team. It is lightweight, can extract localized messages from your code and works well with web components.

UI locale vs. content locale

If the component is able to display content in a different locale than the UI (for example legal documents), it may offer the configuration this feature though an element attribute. Consider the following example:

<tui-document-viewer content-locale="fr-BE"></tui-document-viewer>

The 6m.json file

A 6M component must have a 6m.json file with 6M-specific meta information.

File location

The 6m.json must be publicly available at runtime from your assets server. In fact, making this file available and submitting its URL to the 6M components registry is the official way to publish the component.

The files referenced within 6m.json are expected to be available relative to the location of the 6m.json file.

JSON properties

The file must contain a JSON object with the following properties:

name

A human readable name of the component, such as “Image Gallery”. To avoid redundancy, the name should not contain “TUI” or “6M”.

file

The location of the main JavaScript file for embedding the component. This can be either a path, in which case it is considered relative to the 6m.json file. Or it can be a full URL. The file field is optional. If it doesn’t exist, the file is assumed to be named main.js, and to be in the same directory as the 6m.json file.

icon

The location of an image file (PNG or JPG) with a resolution of 300x300 pixels. Expect the image to be scaled down to much lower dimensions.

screenshots

An optional array of objects, where every object has the keys location and description. The location is the location of an image file (PNG or JPEG) showing a screenshot of the component. It is recommended to show the component with a width of 1024 pixels, though other sizes are allowed as well. The description should contain a caption for the image.

maintainer

A string containing an e-mail address to contact the maintainer of this component.

NOTE: If the component is maintained by a team, or maintainers are changing, it is recommemded to set up an e-mail alias and forward it to the responsible person(s).

description

A short description of 30 to 50 words, e.g. “A component for displaying images of hotels or other services. Can be used as part of other components or on the root level of a host page.” The description could, for example, be displayed in a list of known 6M components.

documentation

The location of a Markdown document containing extended documentation of the component, relative to the 6m.json file, e.g. docs/about.md. Consider your target audience to be website owners who are interested in embedding your component into their site. In-depth technical documentation is not required here, however, you are welcome to give hints on the integration. Also, you don’t need to document your attributes or events here, because this documentation will be generated automatically.

6m-version

The version of the specification the 6M component implements. The version number of this specification is found at the top of this document.

legacy

Some components have been developed in the pre-webcomponent era. They are not embedded as custom elements, but as div tags, where the component identifier is added as a class name and attributes have a data- prefix. If this is such a component, the value of legacy must be true. Otherwise it can be omitted or be set to false.

locales

Contains an array of supported locales, e.g. ["en-US", "de-DE"].

tag

Contains a string with the tag name of the custom element, e.g. tui-image-gallery.

attributes

An array of one or more objects, where every object has the following structure:

{
"name" : "hotel",
"description" : "An ID of a hotel.",
"documentation" : "docs/attributes/hotel.md",
"schema" : {
"type": "string",
"pattern": "^[A-Z]{3}[0-9]{5}$"
},
"required" : true
}
  • The name key contains the attribute’s name.
  • The description key contains a description of the attributes functionality.
  • The documentation field contains the location of a Markdown document with extended documentation. This field is OPTIONAL.
  • The schema key contains a JSON schema definition (draft-07 compatible).
  • The required key indicates if the attribute is mandatory.

NOTE: The locale, scope and brand attributes do not have to be documented.

events

The events key contains an object with two sub-keys, publish and subscribe. They each contain an array with one or more items, where each item is an object describing an event:

{
"name" : "image-gallery.slideshow.next",
"description" : "Triggers when the slideshow switches to the next element.",
"data" : [
{
"name" : "nextPos",
"description" : "The position of the next image in the set. Note that if the last image is reached, nextPos will be `0`, i.e. the first picture.",
"documentation" : "docs/events/image-gallery.slideshow.next.md",
"schema" : {
"type": "integer",
"minimum": 0,
"maximum": 999
}
}
]
}
  • The name key contains the event’s name.
  • The description key contains the event’s description.
  • The documentation field contains the location of a Markdown document with extended documentation. This field is OPTIONAL.
  • The data key describes the payload object of the event. It is an array, where each item is an object describing a property of the payload object:
    • The name key contains the property’s name.
    • The description key contains the field’s description.
    • The documentation field contains the location of a Markdown document with extended documentation. This field is OPTIONAL.
    • The type key contains the data type of the property’s value. Possible values are integer, float, string, array and object.
    • The schema key contains a JSON schema definition (draft-07 compatible).

You only need to document events which are defined by the component itself. In situations where your component acts as a “client” of a different component, i.e. it publishes, or subscribes to, an event defined elsewhere, you don’t need to document that particular event.

skeletons

The skeletons field is an array containing at least one object. Each object contains a description key with a brief description of the skeleton, and a location key with the relative location of the skeleton file.

[
{
"description": "A minimal, non-animated skeleton.",
"location" : "skeletons/minimal.html"
},
{
"description": "An animated skeleton with more detail.",
"location" : "skeletons/animated.html"
}
]
examples

The examples field is optional, if set it is an array containing objects. Each object contains a description key with a brief description of the example, and a attributes key with a map of attributes to set on the example element.

[
{
"description": "Showing the images in the most crazy way possible.",
"attributes" : {
"some-attribute" : "some-value",
"some-other-attribute" : "some-other-value"
}
}
]

Examples help editors and product owners to see usage scenarios of a component. It is especially advised to have examples if your component has required attributes and will fail if embedded without them.

The Middle Layer

General considerations

The middle layer of a 6M component is a translation service into the world of backends. Common use cases for middle layers are:

  • Backend access which requires secret credentials,
  • Complex or aggregated backend calls,
  • Complex business logic which is too heavy to be delivered to, or executed in, the browser,
  • Business logic which must stay confidential,
  • Lambda-style functions, e.g. image processing.

In any case, the middle layer must be stateless, i.e. it must not do things like persisting data or managing sessions. Caching and serving imported data are edge-cases; make sure not to run into concurrency issues when multiple middle layers run in parallel.

6M Components without a Middle Layer

It is possible that a 6M component doesn’t need to talk to any backends at all, and therefore doesn’t need a Middle Layer. However, if there is backend communication, a dedicated Middle Layer must be used rather than talking to fat backends directly.

Technology

Developers of 6M components are free to implement and operate their Middle Layers in any technology (e.g. NodeJS, Golang, Java, Python, PHP) and with any API paradigm (e.g. REST or GraphQL) they want. It is however recommended to choose languages and frameworks which are best suited for a lightweight, stateless service.

The 6M reference describes only the mandatory traits of a 6M component. You are strongly encouraged to have a look at the best practices as well in order to develop a high-quality component.