view {} is currently captured and parsed for the first SPA build subset. This
page is the contract for the supported markup subset; syntax not listed here is
unsupported unless another language reference explicitly says otherwise.
Contract Decisions
view {}markup expands only through GOWDK-owned AST nodes andg:directives. There is no implicit pass-through lane: foreign template syntax and unknowng:attributes are rejected with explicit diagnostics instead of being translated or silently ignored.- Rendered text and attributes are escaped by default. The one explicit raw
HTML opt-in is the
g:html={Expr}directive documented below; all other raw HTML syntax (including{@html ...}) is rejected. - Snippet/render blocks are not supported. Use GOWDK component slots for the supported reusable-markup model.
- Head management is page metadata, not
view {}markup. Use@title,@description,@canonical, and@image. - External template syntax is rejected instead of translated implicitly.
Deferred construct families each fail with a registered diagnostic (see diagnostics.md) rather than ad-hoc behavior:
- Async placeholders (
{#await}blocks,g:await/g:async) are deferred. The diagnostic points at build/load data, actions, APIs, and fragments for asynchronous data (unsupported_markup_syntax/unsupported_markup_directive). - Transitions and animations (
g:transition,g:animate) are deferred. The diagnostic points at CSS transitions or a future addon-specific contract (unsupported_markup_directive). - Document/window/body/head targets (
g:window,g:document,g:body,g:head) are deferred. The diagnostic points at page metadata and element-levelg:on:*(unsupported_markup_directive).g:targetvalues must be literal#idselectors, so DOM/document targets are also rejected there by value validation. - DOM actions/attachments (
g:use,g:action,g:attach) are deferred. The diagnostic points at componentclient {}blocks withg:ref(unsupported_markup_directive). - Raw HTML beyond the
g:htmlhatch —{@html ...}and any other foreign raw-HTML syntax — is rejected with guidance towardg:html={Expr}(unsupported_markup_syntax).
Implemented today:
- Lowercase HTML element tags.
- SPA quoted attributes.
- Boolean attributes.
- Expression attributes such as
data-title={post.Title}using the same interpolation scope as text. - Class shorthand such as
.text-4xland.font-bold, normalized into ordinaryclassattributes. - ID shorthand such as
#hero, normalized into an ordinaryidattribute. - Self-closing tags rendered as explicit open/close tags.
- SPA text and attribute values, escaped before output.
{name}and dotted-name interpolation such as{post.Title}in page text and quoted attributes when SPA build data is available, including route params from literalpaths {}and string values from literalbuild {}or imported Go build data functions.- Explicit route-param interpolation with
{param("slug")}in page text, quoted attributes, and component prop values. SPA builds validate that each referenced param is declared by the page route. Inside quoted attributes, escape the inner quotes as{param(\"slug\")}. - Self-closing component calls such as
<Hero title="GOWDK" />when the component file is passed togowdk build. - Wrapper component calls such as
<Panel>...</Panel>, with child markup rendered into<slot />in the component view. - Named component slots using caller-side
<template g:slot="name">...</template>and component-side<slot name="name">fallback</slot>. - Scalar scoped slot values using component-side
<slot name="row" value={Field} />and caller-side<template g:slot="row" let:value>...</template>. {prop}text and attribute interpolation inside component views.- Component prop values can interpolate page build data, such as
<Hero title="{slug}" />. g:post={action}on<form>, lowered tomethod="post"and the current concrete route when the action exists.g:target="#id"andg:swap="innerHTML|outerHTML"ong:postforms, lowered todata-gowdk-targetanddata-gowdk-swapfor future partial runtime enhancement.g:message:required,g:message:minlength,g:message:maxlength, andg:message:patternon literal controls insideg:postforms to attach request-shape validation messages to the generated action schema. Each message directive must match a literal HTML constraint on the same control.g:on:<event>={...}on elements inside stateful components. The first generated-JS expression subset supports field increment/decrement, assignment from typed scalar expressions, arithmetic, comparisons, boolean logic, parentheses, scalar field reads, and calls to component-local client functions such asg:on:click={Increment()}andg:on:click={Add(Count + 1)}.- Event directives can use
.prevent,.stop,.once,.capture,.debounce(duration), and.throttle(duration)modifiers, for exampleg:on:submit.prevent={Save()}andg:on:input.debounce(250ms)={Search()}. - DOM event expressions can read the compiler-owned event scope:
event.value,event.checked,event.key,event.code,event.clientX, andevent.clientY. - Component
client {}blocks can declareon mount,on destroy, andeffect when Fieldblocks. These blocks use the same state-mutation subset as client functions; effects rerun after the named state field changes and can return cleanup blocks withreturn { ... }. - Component
client {}blocks can declare DOM refs such asref searchInput HTMLInputElement; elements bind them withg:ref={searchInput}. Ref statements only supportFocus,Blur, andScrollIntoView. - Component
client {}blocks can declare computed values withreturn expror one Go-styleifreturn followed by a fallback return. Computed values are read-only, can depend on props, state, and earlier computed values, and update dependent bindings after state changes. g:if={boolExpr},g:else-if={boolExpr}, andg:elseon sibling elements inside stateful components. The static first render may mark inactive branches withhidden; after island mount, generated JavaScript mounts the active branch and unmounts inactive branches.g:bind:value={Field}on<input>,<textarea>, and<select>inside stateful components whenFieldis a string state field. Numeric state fields can bind to<input type="number">. The first slice emits the initial value, updates state on control events, and syncs the control after other state changes.- Radio groups can bind string state with
<input type="radio" value="..." g:bind:value={Field}>. g:bind:checked={Field}on checkbox<input>elements inside stateful components whenFieldis a bool state field. It emits the initialcheckedstate, updates state onchange, and syncs after other state changes.- Local form bindings can be used inside normal
g:postaction forms. Binding listeners do not add submit interception; the action form still posts through its loweredmethodandaction. - Reactive expression attributes on safe non-URL attributes inside stateful
components, such as
disabled={Open}andaria-expanded={Open}. Boolean HTML attributes are toggled as attributes; scalar and ARIA attributes are stringified. - Class toggles on elements inside stateful components, such as
class:active={Open}. The expression must be bool, literal classes are preserved, and the generated island runtime updatesclassList. - Style bindings on elements inside stateful components, such as
style:height.px={PanelHeight}andstyle:width.%={WidthPercent}. The expression must be string or numeric, literal style declarations are preserved, and the generated island runtime updates the CSS property. - Island expressions can read nested fields and indexed values from Go-typed
state, such as
User.Name,Items[0].Name, andFlags[Count]. - Island expressions can choose values with the Go-ish conditional expression
if Open { "open" } else { "closed" }. - Elements inside stateful components can render Go-typed slice state with
g:for={item in Items}org:for={item, i in Items}and a required scalarg:key={item.ID}. The first slice supports item field interpolation such as{item.Name}, index interpolation such as{i}, and keyed row reuse/reorder during island render passes. - Client handlers can mutate state arrays with compiler-owned built-ins:
append(Items, { Field: expr }),remove(Items, index), andmove(Items, from, to). - Client expressions support first-slice compiler-owned built-ins:
len(value),lower(value),upper(value),contains(value, query),string(value),int(value), andfloat(value). - Component-level
@wasmdeclarations make normal calls to that component use WASM island assets.g:island="wasm"remains a call-site override. Unknowng:islandvalues are compile/render errors. Without@wasmorg:island, stateful component calls use generated JavaScript by default. - The explicit raw HTML escape hatch
g:html={Expr}on a non-void element without markup children. See the "Raw HTML (g:html)" section below. - Familiar external-template block syntax such as
{#if},{#each},{#await},{#snippet},{@html},{@const}, and{@debug}is rejected with diagnostics that point to the current GOWDK-native alternatives —{@html body}now points at the explicitg:html={Expr}directive. These diagnostics are guidance only; they do not imply that GOWDK will implement those external constructs feature-for-feature. - Unknown
g:attributes are rejected at parse time with a diagnostic that lists where the supported directive set is documented. There is no silent pass-through for unrecognized directives.
Supported g: Directives
These are the supported g: directives in view {} markup:
g:post={Action}on<form>.g:target="#id"andg:swap="innerHTML|outerHTML"ong:postforms.g:message:required,g:message:minlength,g:message:maxlength, andg:message:patternon literal form controls insideg:postforms.g:on:<event>[.<modifier>...]={Expr}inside stateful components. Supported modifiers are.prevent,.stop,.once,.capture,.debounce(duration), and.throttle(duration).g:ref={name}inside stateful components.g:if={boolExpr},g:else-if={boolExpr}, andg:elseinside stateful components.g:for={item in Items}org:for={item, i in Items}with requiredg:key={scalarExpr}inside stateful components.g:bind:value={Field}on<input>,<textarea>, and<select>inside stateful components.g:bind:checked={Field}on checkbox<input>elements inside stateful components.g:slot="name"on caller-side<template>elements for named and scoped slots.g:island="wasm"on component calls when a call-site WASM override is needed.g:html={Expr}on non-void HTML elements without markup children, in pages and stateless component views. See "Raw HTML (g:html)" below.
All other g: directives are unsupported today and rejected at parse time
with the unsupported_markup_directive message. In particular, there is no
g:head, g:window, g:body, g:document, g:transition, g:animate, or
g:action directive in the compiler core.
Raw HTML (g:html)
g:html={Expr} is the single explicit, GOWDK-owned opt-in for raw HTML
output:
view {
<article class="prose" g:html={post.BodyHTML}></article>
}
Contract:
- The element renders its open and close tags normally; literal and interpolated attributes on the element are still escaped.
- The expression resolves through the same render-data lookup as
{Expr}text interpolation, including dotted names and component props. Unknown names fail the same way text interpolation fails. - The resolved string is written as the element content without escaping.
Security warning: content rendered through g:html bypasses GOWDK's
escape-by-default output. Only feed trusted or server-side sanitized HTML to
g:html. Never pass user-controlled input through it; route-param
interpolation ({param("...")}) is rejected inside g:html for this reason.
Restrictions (each is an explicit error):
- The element must have no children in markup; the expression provides the whole content.
g:htmlrequires an expression value (g:html={Body}), not a string literal or boolean attribute.g:htmlis not allowed on void elements such as<br>or<img>.g:htmlcannot combine withg:for/g:keyorg:bind:*on the same element.g:htmlis rejected inside stateful component views, insideg:forloops, and for island-bound reactive fields, because the island runtime re-renders bound content as escaped text and cannot honor raw HTML there.
Server-rendered fragment swaps (g:post + g:target/g:swap) inject
server-rendered HTML via innerHTML/outerHTML, so raw HTML rendered with
g:html flows through them unchanged.
Not implemented yet:
- Non-string component props in inline
props {}blocks. - Raw HTML escape hatches beyond the
g:htmlelement directive, including attribute-position or text-position raw output. - Snippet/render block syntax as a first-class reusable markup value.
- Template-level await blocks, local const tags, debug tags, transitions, animations, DOM actions, and document/window/body/head special targets.
- Full client-side expressions beyond the first safe island subset, including broader date/time built-ins and JavaScript-style ternaries.
- Other
g:directives beyond the supported directive list above. - Reactive URL and event-handler attributes, plus raw
style={expr}attributes. - Shorthand preservation in a full component AST.
- Comment preservation.
Examples may show components, attributes, interpolation, and g: directives.
Those examples are product direction unless they fit the implemented subset
above.
Future markup work must define:
- HTML tag parsing.
- Component invocation syntax.
- Text and interpolation.
- Attribute escaping.
- Boolean, string, and expression attributes.
g:directives.- Raw HTML escape hatches beyond the element-level
g:htmldirective, if any. - Source spans and diagnostics for malformed markup.