Skip to content
Ooidget

Ooidget Component Format 0.1Editor's Draft

Documents and components

The component file

A component file is a JSON text [RFC8259] encoded in UTF-8 whose top-level value is an object. Its media type is application/ooidget+json; its file name extension SHOULD be .json, and a bundle of several components SHOULD use .ooidget.json.

PropertyTypeRequiredDescription
specstringnoThe version of this specification the file was written for, as "major.minor". Absent: "0.1".
aboutstringnoA human-readable description of the component. Runtimes MUST NOT interpret it.
folderstringnoA grouping for authoring tools. Runtimes MUST NOT interpret it.
widgetWidgetnoThe component’s drawing, state, children and behaviour.
interfaceobjectnoIts props, events and methods for a host.
testsarraynoTests it carries, runnable by any runtime.
behaviourstringnoPresent instead of widget: the source of a shared behaviour.
extensionsUsedarray of stringnoThe extensions the file uses.
extensionsRequiredarray of stringnoThe extensions without which the file cannot be rendered.
extensionsobjectnoExtension-specific data, by extension name.

A file MUST contain widget or behaviour, and MUST NOT contain both.

Names and namespaces

Components, shared behaviours and code are referred to by name, never copied. A name is a path of segments separated by /, each segment ASCII letters, digits, _ and -, beginning with a letter; names are case-sensitive. The last segment names the thing; the segments before it are its namespace, which is the folder it lives in, relative to the project’s root:

NameRefers to
knobknob.json (or knob.behaviour.js) at the project’s root
controls/knobcontrols/knob.json
@ooidget/controls/knobknob.json in the package @ooidget/controls, as npm names packages

A function is referred to as { "fn": "module#export" }: the export export of the code module module.js, named as above. Without #, { "fn": "export" } is an export of the referring component’s own code module.

ReferenceFunction
{ "fn": "road" }road exported by this component’s name.js
{ "fn": "geometry#arc" }arc exported by geometry.js at the project’s root
{ "fn": "@ooidget/std/colour#mix" }mix exported by colour.js in the package @ooidget/std

Names resolve within the project first, then in its packages; a project MUST NOT define a name twice. Where a document refers to a component (a use layer, a child, a shared behaviour), the runtime resolves its name through the host’s component resolver. A name that does not resolve is not an error: the referring layer or child is skipped, and a runtime SHOULD report it to the host as a diagnostic.

Both forms are valid everywhere code is accepted: JavaScript inline in a string, for quick expressions and small behaviours (s.on && !ui.down, `${s.tempo} bpm`, ({ click(s) { return { on: !s.on } } })), or a reference to a function in a code module. Authoring tools SHOULD move code longer than a line or two into a code module, where it is written, read and checked as JavaScript.

Project folders

A component’s source is kept as a project folder, one file per reusable thing, each referred to by name and never copied into the files that use it:

FileHoldsReferred to by
name.jsona component file: data onlyuse layers, children, "behaviour": { "use": name }
name.jsa code module: the component’s own beside its name.json, or a shared one{ "fn": "export" } in its component, { "fn": "name#export" } anywhere
name.behaviour.jsa shared behaviour: an ECMAScript module whose default export is a function from settings to a handler object"behaviour": { "use": name, ...settings }
styles.jsonstyles by namea layer’s style
tokens.jsondesign tokens in the DTCG formattoken references
assets/images and fontsimage layers, fonts

Authoring tools SHOULD write projects this way. A name MUST NOT be defined by more than one file of a project. A bundle is the packed form of a project folder, for distribution: tools pack and unpack between the two without loss.

Code modules

Quick code may be written inline, as JavaScript in a string (expressions, a short behaviour). Longer code (a behaviour with several handlers, a computed drawing, a painter) belongs in the component’s code module: an ECMAScript module name.js beside name.json.

ExportIs
behaviourthe component’s handler object, used when its widget has no behaviour member
any other namea function a value of the component refers to

Wherever this specification accepts an Expression<T>, a document MAY instead give { "fn": "name" }: the code module’s export of that name, called with the expression’s parameters (s, ui, m, env) and returning the value. For example, a road drawn from state:

cluster.json
{ "svgExpr": { "fn": "road" } }
cluster.js
/** The road ahead, its dashes moving with the distance driven. */
export function road(s, ui) {
  let o = "";
  for (let i = 0; i < 12; i++) o += dash(i, s.trip);
  return o;
}

A code module MUST NOT have side effects when imported, and its exports MUST behave as expressions and behaviours do: no I/O, no time, no randomness. Runtimes load it in the same sandbox. Because a code module is already code, a runtime needs no eval for it.

A code module MAY import other code modules of the project, and only those, by relative path with the .js extension (import { detents } from "./detents.js"), resolved as names are: relative to the importing module’s folder. Imports of anything else (packages, URLs, the host’s modules) are not allowed.

In a bundle a component’s code module travels as the member module of its component file: the module’s source text.

Bundles

A bundle is a single JSON object that carries several components and the resources they share, for distribution and for the web:

PropertyTypeRequiredDescription
specstringnoAs for a component file.
componentsobjectyesComponent files by name.
stylesobjectnoStyles by name.
tokensobjectnoDesign tokens, in the DTCG format.
assetsobjectnoAsset contents by name, as data: URLs [RFC2397].
rootstringnoThe component a host shows by default.

A runtime given a bundle MUST resolve component names against its components before asking the host.

The widget object

PropertyTypeRequiredDescription
size[number, number]yesThe component’s design size, [width, height], in design units. Both MUST be positive.
stateobjectnoThe initial state. Absent: {}.
layersarray of LayernoDrawn in order, first at the back.
childrenarray of ChildnoComponents contained in this one.
layoutLayoutnoHow children are arranged. Absent: each child is placed by its own frame.
behaviourstring or objectnoThe behaviour: its source, or a reference to a shared one.
regionsobjectnoNamed regions for hit-testing.
tooltipExpression<string>noText the host MAY show while the pointer rests on the instance.
focusablebooleannoWhether the instance takes keyboard focus. Absent: false.
clipbooleannoWhether layers and children are clipped to the instance’s box. Absent: false.
fitstringnoHow a root instance fills a host area larger or smaller than its size: "contain" (default), "fill".
scrollobjectno{ "x": Expression<number>, "y": Expression<number> }: offsets subtracted from children’s positions.
defsstringnoAn SVG fragment of definitions (gradients, patterns, filters) shared by this widget’s SVG layers.
rolestringnoIts WAI-ARIA role.
labelExpression<string>noIts accessible name.
minSize, maxSize, aspectnoThe smallest and largest layout of a root instance, and the proportions it keeps; see Adapting to the container.
scalingobjectno{ min, max, base? }: how far it scales as a whole with its area.

Members not defined here or by an extension MUST be ignored by runtimes and SHOULD be reported by validators.