Stability & support¶
What you'll find
The stability contract on the road to 1.0 (S10) and the Mode C subset contract (S11): what is public and stable, what may change, which browsers are supported, and where the accessibility baseline is.
Versioning (road to 1.0)¶
tempestweb is pre-1.0 (0.x). Until then:
- Public surface = what you import from
tempestweband its documented subpackages (tempestweb.server,tempestweb.native,tempestweb.transpile,tempestweb.html,tempestweb.pwa,tempestweb.cli) + the wire contract (docs/contract.md). Underscored names are private. - Compatibility: a
0.xminor may carry documented behavior changes (see the CHANGELOG). Pin the version in production. - Deprecation (from 1.0): a feature slated for removal gets a warning for at least one minor before it goes; removals only in a major.
Browser matrix¶
| Browser | Mode A (WASM) | Mode B (server) | Mode C (transpile) |
|---|---|---|---|
| Chrome/Edge ≥ 111 | ✅ | ✅ | ✅ |
| Firefox ≥ 110 | ✅ | ✅ | ✅ |
| Safari ≥ 16.4 | ✅¹ | ✅ | ✅ |
Requirements: ES modules + fetch + WebSocket/EventSource. Installable PWA needs
HTTPS; iOS push requires the app to be installed (Safari ≥ 16.4). ¹Pyodide
boot (Mode A) is heavier on Safari/mobile — prefer B or C for first-paint/SEO.
Accessibility¶
The client emits semantic HTML with roles/aria from Widget.semantics
(aria-label/role/aria-description), tabindex from focus_order, and uses
native controls (<input>/<button>) wherever possible.
The baseline is measured, not declared. The CI a11y job runs axe-core
over the DOM the real renderer builds, for scenes generated from the apps this
repo ships (tests/conformance/_a11y_scenes.py → scripts/a11y-gate.mjs), and it
blocks the merge on a serious or critical violation:
| What the gate catches | What it does not |
|---|---|
a control with no accessible name, an image with no alt, an invalid role, a nested interactive, a label with no field, a duplicate id |
colour contrast and installability — they need real layout, and live in the Lighthouse layer (pwa.yml) |
The scenes are generated, not hand-written: the Mode C component gallery, the control panel, a list with a text field, a form, a nav shell with a drawer and an image screen. Auditing hand-written markup would prove the test's snippet is accessible, not that the renderer is.
Coverage is measured by widget type and by component, and the second axis had a
hole: nine scenes and not one used the fields this repo owns
(TextField/EmailField/PasswordField and the two forms). The login-form
scene looks like it does and does not — it builds the core's
EmailInput/PasswordInput inside a FormField, which the renderer names. The
result: PasswordField shipped an anonymous control (label, critical) with a
green gate until 0.113.0. login_demo closes that axis.
One rule may only be loosened in writing: an axe rule that cannot apply to a scene
goes into KNOWN_EXCEPTIONS with its reason (today: the three whole-document
rules — landmark-one-main, page-has-heading-one, region — plus
color-contrast, which belongs to the Lighthouse layer). Silencing without a
written reason is what turned "accessibility baseline" into an empty claim before.
The wire contract is frozen¶
The wire contract (docs/contract.md)
is part of the stable surface, so it carries its own version — independent of
the package version — in tempestweb.contract:
The golden fixtures already caught accidental drift, but they are regenerable
from the core: they cannot tell "I regenerated because the core moved" from "I
changed the contract". WIRE_SHAPE_DIGEST can — it hashes the wire's shape
(every key and its type, never its value), so:
| Change | Digest | Version | What else |
|---|---|---|---|
| a fixture regenerated with new values | same | same | nothing |
a new optional key, a new envelope kind, a new event type |
moves | same | a CHANGELOG entry |
| a key renamed/removed/retyped, patch semantics changed | moves | bump | a migration note |
tests/unit/test_wire_contract_freeze.py fails on a shape change and names, in
its message, which of the two choices the author owes. A third-party client pins
WIRE_CONTRACT_VERSION and knows what it is talking to.
Mode C subset contract (S11)¶
The transpiler accepts a typed subset of Python — stable and fail-loud
(file:line for anything outside it). See the full list in the
Mode C guide.
In (stable): dataclasses (inheritance/methods/kwargs), view() + handler
closures, full arithmetic, chained comparison, comprehensions (list/dict, with
tuple targets), literals, slices, formatted f-strings, common builtins, stdlib
string/list/dict methods, if/for/while/break/continue/try-except-finally/with/
raise/assert, unpacking, chained assignment, navigation/i18n/theme/animation/
validators, and all native/ capabilities.
Out (by decision): global, yield/generators, del, the walrus (:=),
raise ... from, starred unpacking, arbitrary decorators (only @dataclass),
and most of tempest_core.components (Python composition that expands at
build() time — use Modes A/B, or primitives/HStack/VStack in C). Porting the
components (a JS resolver layer) remains on the roadmap — S11.
A/B/C portability
A view() within the subset runs identically in all three modes.
build --mode transpile proves it by rendering through the real core — a
Mode-C-only API would break the build.
Recap¶
- Pre-1.0: documented public surface + wire contract; pin the version.
- Modern browsers (recent Chrome/Edge/Firefox/Safari) in all three modes.
- a11y via semantics/roles; an axe gate is a follow-up.
- The Mode C subset is a stable, fail-loud contract; components stay in A/B.