Rendering
FormScript does not describe a page. It describes a form, and a renderer turns that form into whatever the platform wants: a web page, a phone screen, a kiosk, a printable document.
The rule that makes this work: FormScript emits intents, never markup.
The intents
| Intent | What it is | What a web renderer does with it |
|---|---|---|
RenderTextInput |
free text | <input type=text> |
RenderNumberInput |
a number, formatted for the locale | <input type=number> |
RenderChoice |
a constrained selection | radio buttons or a select |
RenderMultiSelect |
several of many | a checkbox group |
RenderDate |
a date | a date input or a picker |
RenderToggle |
a boolean | a switch or a checkbox |
RenderFile |
an attachment | a file input |
RenderSignature |
a signature | a canvas |
RenderLocation |
a place | a map picker |
RenderGroup |
a labelled composite | a fieldset |
RenderRepeat |
a list that can grow | repeated fieldsets |
RenderProgress |
how far through | a progress element |
RenderErrorSummary |
why the submit failed | an alert region |
RenderMessage |
an inline message | text with aria-live |
A renderer must map every intent it receives. An unmapped intent is a conformance failure, and the runtime surfaces it as a warning rather than silently dropping the field — a form that quietly loses a question is worse than one that complains.
A renderer may map an intent to a different widget, as long as the
mapping preserves the intent's accessibility, validation and keyboard
semantics. Mapping RenderChoice to a control that cannot be operated by the
keyboard alone is not allowed.
Why intents and not markup
Because markup is a platform decision, and the questions in a form are not.
A document that says "the applicant is asked for a guarantor when the loan is over ten thousand" has said everything about the form. Whether that is a radio group, a dropdown, a field revealed by JavaScript or a printed line with a box beside it is a decision for the renderer — and a decision that has to be made again, differently, on every platform.
The intents keep the first kind of statement in the language and the second kind of statement in the renderer.
What a renderer must do
The specification's obligations for a renderer (Level 2 and above) are about behaving like the form says:
- maintain a labelled control for every active field;
- expose the field's state to assistive technology;
- keep the keyboard order the same as the document's order;
- make an inapplicable field disappear, rather than hide it in a way that leaves it in the tab order;
- show the validation messages where the constraint says they belong.
And the whole of §13, on accessibility, applies to the renderer rather than to the document: the document says what the question is, and the renderer makes sure a person can answer it.
Seeing it for yourself
The playground renders a form with the reference implementation's own renderer, and the language page shows the intents the real code produces for a real document.
The packages are:
| Package | What it does |
|---|---|
@formscript/render |
the intents, and the contract |
@formscript/render-dom |
renders intents into a DOM |
@formscript/render-html |
renders intents into HTML |
The conformance levels
The levels say how much of this a runtime has implemented:
- Level 0 — the document is read and analysed: it can be linted, and the diagnostics are right.
- Level 1 — the document is evaluated: the output document is right.
- Level 2 — the intents are produced, and a renderer exists.
- Level 3 — the whole of the language, including the parts this site documents at a glance: the remote data model, the action model, the accessibility contract.
The reference implementation declares Level 3, and the conformance page lists the evidence for each part of the claim.
The level is checked, not claimed: npm run formscript -- level prints the
declaration and the evidence behind it.