Attributes

What every attribute declares about the values it accepts, how a flag is named, what an attribute holds when a document does not write it, and the order a schema lists its attributes in.

On this page

#Types

Every attribute declares a type — there is no implicit string. An untyped attribute would silently receive the most permissive type, and a value nobody considered would be indistinguishable from a deliberately textual one; library validation is fail-fast instead.

The base set is sealed. A type is a base plus language-defined refinements; a schema cannot invent a base, and an unknown refinement is a load error. union is the only combinator: at least two branches, provably disjoint, never nested.

Optionality is required and nothing else. A type never means “this, or nothing” — an optional attribute is one that leaves required unset, not one with a looser type.

Prefer a named type over a loose string. series, duration, paint and their peers each carry a value grammar and the diagnostics that come with it; an attribute that accepts “numbers or a dataset column” typed as plain string re-invents series without either. A colour is paint: the language’s colour base holds only the literals rgb(r, g, b) and hex(RRGGBB), and paint is the union of that base and the hue names. A schema may not declare a type named after a base.

An attribute whose value may be finished only at render — one that reads a slider’s letter, a derived member, a column applied with ->, or the node’s own readings through #self — is an expr position (D68): { base: 'expr', type: 'number' }. Every other position takes only a value known while the document compiles, and refuses a bound one.

#Booleans

A flag is named for what it enables, so asserting it reads as plain statement: editable, interactive, runnable, show-grid. In source a boolean is a bare word and its negation is !show-grid — each spelling carries one idea.

A bool is a literal: true, false, yes, no. An attribute that takes a condition is an { base: 'expr', type: 'bool' } position. An animatable bool such as a mark’s hidden reaches the compiler as text, and the SDK types its base and each keyframe through that position with the document’s control letters bound, so hidden: k > 3 follows a slider.

A negated name breaks that arithmetic: hidden: false stacks a negation on a negative and has to be decoded rather than read. Name the affirmative state and let the default carry the common case — chrome that is visible by default is show-ticks, show-labels, switched off with !, never a family of hide- flags switched on.

Choose the default so a typical document never mentions the flag: show-ticks defaults true because most graphs want ticks; runnable defaults false because most code environments are read, not run.

#Defaults

A default is written the way an author would write the value. Booleans and numbers are literal (default: false, default: 20); every custom-parsed type takes its default as a source string — '500ms', '[-10, 10]', 'teal' — parsed through the type exactly as an authored value would be.

An absent default is a statement, not an omission. It means the attribute is required, or that the value derives at runtime — a graph domain comes from the data it shows; a highlight takes the colour of what it highlights. A filled-in placeholder would foreclose that behaviour, so omit deliberately.

#The implicit attribute and order

A construct names its implicit attribute with implicit — the attribute the name: value head form assigns. A construct that omits the tag has no head form: line:0, 1, 2 is a parse error against it, and the named spelling is the only one. Tag the attribute the construct is most naturally of.

Order carries no meaning, but the convention still reads: required payload first, then styling, with bookkeeping — label, hidden, lock — last. Abridged from the line mark:

line: {
    attributes: {
        x: { type: 'series', animatable: 'number-list' },
        y: { type: 'series', animatable: 'number-list' },
        colour: { type: 'paint', aliases: ['color'], default: 'pink', animatable: 'colour' },
        width: { type: 'parametised-number', default: '2', animatable: 'number' },
        dots: { type: 'bool', default: true },
        label: { type: { base: 'inline', content: 'all' } },
        hidden: { type: 'bool', default: false, animatable: 'discrete' },
        lock: { type: 'bool', aliases: ['locked'], default: false },
    },
    implicit: 'x',
},

The tag may name a canonical attribute or an alias, and it is stored canonical. It may not name a derived attribute, and it may not name one a dynamic case withdraws. The implicit attribute’s name is reserved inside the construct’s own attribute group.

Last updated