Variants and versions
A component changes in two independent ways: its format (a version, for the engine and stored content) and its presentation (variants, for the student). The rules come from decision 0010.
Versions
Section titled “Versions”manifest.version is the element format version: the attributes and children a source may have and what they mean.
- The compiler writes it as
vin every view:<astl-gap id="kbz4hfyfwsrba2cht" v="1" size="8">. - Sources may write
v; without it a source element counts as version 1. - A breaking change bumps
versionand adds a migration:ComponentDefinition.migrations[n]upgrades a source element written for version n to n + 1. The engine runs the steps in order (1 to 2, 2 to 3, ...) before it validates, so definitions only ever see the current format, and the view carries the currentv. - A source with a
vnewer than the registered manifest, or an older one without every migration step, gets the errorVERSION_UNSUPPORTED({ tag, version }). - Stored views are immutable: a player keeps rendering every older view version of a registered element. An element
whose
vis newer than the player's manifest is replaced by the "unsupported" notice instead of being guessed at.
import type { ComponentDefinition } from '@astrolabe/core';
export const blankDefinition: ComponentDefinition = { manifest: { ...blankManifest, version: 2 }, // ...validate, slots, toView migrations: { // Version 1 called the answer `value`. 1: (el) => { const { value, ...rest } = el.attrs; return { ...el, attrs: value === undefined ? rest : { ...rest, answer: value } }; }, },};What is breaking, and so bumps version:
- removing or renaming an attribute or a child, or changing what a value means;
- changing a default (of an attribute, a scoring mode or the variant);
- removing a variant.
Not breaking: adding an optional attribute, a child type, a scoring mode or a variant.
Variants
Section titled “Variants”manifest.variants is { default, values } (or null for one presentation). It gives the element a public variant
attribute, an enum of values (variantAttribute in @astrolabe/core), that authors pick per element:
<p>An apple a day <astl-gap id="apple" answer="keeps the doctor away" variant="resizable"></astl-gap>.</p><astl-choice id="pet" variant="cards">...</astl-choice>- Presentation only. A variant changes how the element looks and behaves for the student, never what it grades or which answers exist: every variant of a source compiles to the same answer key and grades the same. The built-in components prove it with a test that compiles every fixture with every variant.
- The engine validates it like any enum.
variant="fancy"givesATTRIBUTE_INVALIDwith the allowed values in its message ("Use one of: list, cards."). A valid value stays in the view. - Elements render it. A value the element does not know (from a newer view) renders the default.
- The authoring kit offers it after the element's own inspector fields, labelled by
editor.variant(the kit's "Variant" when the component has none) andeditor.value.variant.<value>in the component's catalogs.
| Element | Variant | What changes |
|---|---|---|
<astl-gap> |
inline (default) |
The field is size characters wide and grows with the answer up to 40 characters. |
<astl-gap> |
resizable |
It grows with the answer up to the whole line, for phrases and sentences. Neither variant gets narrower than size. |
<astl-choice> |
list (default) |
One option per row. |
<astl-choice> |
cards |
The options as a grid of tiles, as many columns as fit, with the same radios or checkboxes, keyboard and verdict cues. In one column (a phone) they keep the list's height. Best for short options: long texts wrap in narrow tiles. |
Each component page lists its variants with their labels: <astl-gap>,
<astl-choice>.
Add a variant to your component
Section titled “Add a variant to your component”- In the manifest, list it:
variants: { default: 'list', values: ['list', 'grid'] }. The default is what elements show without the attribute, so it should be today's presentation. - In the element, render it. CSS often does it alone:
:host([variant='grid']) .options { display: grid; }. In code,this.stringAttribute('variant')(onAstlElement) returns the default for a missing or unknown value. - In every catalog, add
editor.value.variant.<value>(andeditor.variantfor the field label). - Add a fixture per variant (
<type>-variant-<value>), so the matrix, the conformance goldens and the end-to-end tests cover it, and keep the variant out ofvalidate,slotsand anything else that decides grading.
Variants belong to the component and its author: how it works for the student. A look for a whole exercise or for one element, made by the host, is a look (a style template) instead; it can restyle any variant.