Extras
Beyond the basics — viewport & breakpoints, diagnostics, inline overrides, and treating JoyDom as a SwiftUI view.
JoyDom(spec:components:) renders a document on its own — none of the following is required to get pixels on screen. These are the optional surfaces you reach for as you go deeper. Each is a chainable modifier on the view (later calls win; .onEvent is last-wins).
Viewport and breakpoints
A document's breakpoints are pure data — conditions like width, orientation, and print mode. The renderer resolves which are active by comparing them against a Viewport. Without one, breakpoints stay inactive and only document-level styles apply — so the viewport is optional, needed only when a document uses @media/breakpoint overrides.
JoyDom(spec: spec, components: registry)
.viewport(Viewport(width: 1024, orientation: .landscape))Viewport carries the three signals joy-dom's media queries can ask about:
| Field | Type | Default | Drives |
|---|---|---|---|
width | CGFloat | — | width-based media conditions |
orientation | Orientation | .portrait | orientation: conditions |
isPrint | Bool | false | @media print style blocks |
Only width is required — Viewport(width: 1024) is valid. A bare (width) media feature with no operator or value always matches, since a viewport always has a width.
For breakpoints that track the live layout, derive the width from a GeometryReader:
GeometryReader { proxy in
JoyDom(spec: spec, components: registry)
.viewport(Viewport(width: proxy.size.width))
}You can also inject a viewport through the environment with .joyViewport(_:) higher up your view
tree, instead of passing it to each JoyDom.
Diagnostics
The renderer is silent by default: unsupported properties, unknown selectors, and invalid values are skipped, not thrown. Attach .onDiagnostic to observe them — handy while authoring documents or behind #if DEBUG:
JoyDom(spec: spec, components: registry)
.onDiagnostic { warning in
switch warning.kind {
case .unsupportedProperty(let name):
print("joy-dom: unsupported property \(name) — \(warning.detail)")
case .invalidValue(let property, let value):
print("joy-dom: invalid \(property) = \(value)")
default:
break
}
}Each JoyWarning has a kind and a detail string. The kinds: .unsupportedProperty, .unsupportedSelector, .unsupportedAtRule, .invalidValue(property:value:), .duplicateLocalID, .duplicateSchemaID, and .other.
Inline component overrides
Beyond the registry, you can override individual nodes per instance by their schema id, with a trailing builder — handy for one-off content (a hero image, a specific button) without registering a type:
JoyDom(spec: spec, components: registry) {
Component("hero") { Image("banner").resizable().scaledToFill() }
if showCTA {
Component("submit") { Button("Get started") { /* … */ } }
}
}Lookup for each node is locals → registry → placeholder, so a local wins over a registered factory. Overrides are render-only; events still reach the single .onEvent handler. The builder supports if/else (not loops).
JoyDom is a SwiftUI view
JoyDom is an ordinary View, so standard modifiers compose around it and you can embed it anywhere:
JoyDom(spec: spec, components: registry)
.padding()
.frame(maxWidth: 600)
.background(.background)Its configuration knobs are chainable methods, each returning a new JoyDom:
.onEvent { event in … }— the single event handler (Events)..viewport(_:)— breakpoint resolution (above)..onDiagnostic { warning in … }— renderer warnings (above)..defaultFontFamily(_:)— the CSS-inherited body font (nil= system font).
Where next
- Getting started — install and render your first document.
- Custom components — register your own node types.
- API reference (DocC) ↗ — every type the renderer exposes.