Rendering elements
alacris.Element implements templ.Component, so it goes anywhere a component
goes and picks up children from the block around it.
@alacris.E("user-card").Prop("name", "Ada") { <h3 slot="title">Ada Lovelace</h3>}Builder methods return the element, so they chain. They collect errors rather
than panicking, and Render reports the first one. A bad tag name or an
unencodable prop fails the request instead of producing quietly broken HTML.
Prop takes the JavaScript prop name from define(). The library converts
it to the attribute the element observes.
return alacris.E("user-card"). Prop("name", "Ada"). Prop("age", 36). Prop("tags", []string{"math", "code"})<user-card name="Ada" age="36" tags="["math","code"]"></user-card>Props are kebab-cased and encoded by their Go type.
A nil value leaves the attribute off, so the component keeps the default it
declared:
// A nil value leaves the attribute off entirely, so the component keeps// the default it declared in define().return alacris.E("x-demo"). Prop("name", nil). Prop("tags", []string(nil)). Prop("kept", "written")<x-demo kept="written"></x-demo>A nil prop leaves the component's own default alone.
PropRaw sets an already-encoded value when you need to bypass the encoding
rules, and Props takes a map, applied in sorted key order for stable output.
Ordinary attributes
Section titled “Ordinary attributes”Attr follows HTML rules rather than alacris’: a true bool renders bare
and a false one disappears. That is the opposite of how props behave, on
purpose: hidden and disabled are HTML, not component API.
return alacris.E("user-card"). ID("ada"). Class("card", "card--wide"). Attr("hidden", false). Attr("aria-label", "A person"). Attr("data-testid", "user-card")<user-card class="card card--wide" id="ada" aria-label="A person" data-testid="user-card"></user-card>Ordinary attributes follow HTML rules: a false bool disappears.
ID, Class and ClassIf are shorthands. Attrs spreads a
templ.Attributes map.
alacris.E("user-card"). ID("ada"). ClassIf(selected, "is-selected"). Attrs(templ.Attributes{"data-index": 3})Attr("class", …) appends to the class list rather than replacing it.
Attr("style", …) is refused, because Style and Var sanitize their input
and a raw string cannot be checked usefully.
Styles and custom properties
Section titled “Styles and custom properties”Var sets a CSS custom property, which is how a themed component is configured
from outside its shadow root:
chip := alacris.Vars("--chip-bg", "--chip-fg", "--chip-radius")return alacris.E("ala-chip"). Apply(chip, map[string]string{ "--chip-bg": "#ffe9a8", "--chip-radius": "999px", }). Text("new")<ala-chip style="--chip-bg:#ffe9a8;--chip-radius:999px">new</ala-chip>Custom properties reach through the shadow boundary.
Both the property and the value are checked. Anything that could end the declaration, end the attribute, or open a comment is an error rather than a silent substitution:
alacris.E("x-y").Var("bg", "red;position:fixed")// render fails: alacris: character ';' is not allowed in an inline CSS valueSee Theming for VarSet, which turns a
component’s set of custom properties into something Go can check.
Slots and children
Section titled “Slots and children”Slot content is light DOM. It is in the document, in the first paint, and visible to a crawler. Put content that matters when JavaScript has not arrived yet here.
Three ways to fill a slot, in order of preference:
{/* 1. On your own markup. No wrapper element, full control. */}@components.UserCard(props) { <h3 slot={ components.UserCardSlotTitle }>Ada Lovelace</h3>}// 2. Slot, when the content comes from Go. Wraps it in a <div slot="...">.alacris.E("info-card").Slot("title", heading())
// 3. SlotAs, when the wrapper element matters to layout.alacris.E("info-card").SlotAs("h3", "title", heading())return alacris.E("info-card"). Prop("tone", "warn"). Slot("title", text("Ada Lovelace")). SlotAs("p", "body", text("Wrote the first algorithm."))<info-card tone="warn"> <div slot="title">Ada Lovelace</div> <p slot="body">Wrote the first algorithm.</p></info-card>Wrote the first algorithm.
Slot content is light DOM, so it is in the first paint.
Children sets children programmatically instead of taking them from the
enclosing block, and Text sets a single escaped text child.
Hiding elements until they are defined
Section titled “Hiding elements until they are defined”A custom element that is in the document but not yet upgraded is empty and has no size. The page paints, then reflows when the module lands.
<head> @alacris.Pending{Tags: ui.Tags} @alacris.Scripts(cfg)</head>return alacris.Pending{Tags: []string{"user-card", "ala-chip"}}<style> user-card:not(:defined),ala-chip:not(:defined){visibility:hidden}</style>Hides elements until their module has defined them.
ui.Tags is generated, so the list cannot fall behind the components. For
Alacris UI, ui.Pending() (from github.com/bmartel/alacris-go/ui) is the
same list for every design-system tag. The declaration defaults to
visibility: hidden; pass Style to choose another.
alacris.Pending{ Tags: ui.Tags, Style: map[string]string{"opacity": "0", "transition": "opacity .15s"},}Errors
Section titled “Errors”Every builder method records problems instead of panicking:
el := alacris.E("div") // no hyphen: not a custom elementel.Prop("id", int64(1)<<60) // beyond JavaScript's safe rangeif err := el.Err(); err != nil { // ... or just let Render return it}This is why a typo surfaces as a failed request rather than an element the browser silently ignores.