The template language
Attributes and values
Writing attributes, and what each becomes in the page.
An element's or component's attributes are written in parentheses after its name.
Writing attributes
input(type: "email", value: email, required, placeholder: "you@example.com")
button(class: "rounded" disabled: busy) "Save"name: valuesets an attribute. The value is any expression.- A string first is the classes:
div("p-4 flex", id: "main")isdiv(class: "p-4 flex", id: "main"). It's checked and explained like anyclass. Text content still goes after the parentheses:Button("w-full") "Save". - A bare
namemeansname: true. - A variant,
.name, is one of a prop's named choices:Button(variant: .ghost, size: .sm). It's the text"ghost", so it works wherever that text does (in comparisons too:if size == .large), but the language server checks it against the choices the prop takes and suggests the nearest:.gost→ did you mean.ghost? - A list of plain values is an element's space-separated words:
class: [.p-4, "flex", if on then "ring" else null],position: [0, 1.5, -2]. Anullis left out. Components receive the list itself. - Attributes are separated by commas, spaces or line breaks, freely mixed. A trailing comma is allowed.
@event: handlerbinds an event; see Components and events.
Values
| Value | Example |
|---|---|
| Text, with interpolation | "Hello {user.name}" |
| Numbers | 3, -1.5 |
| Booleans and null | true, false, null |
| A path | post.title |
| Any expression | if done then "line-through" else "", price * qty, posts |> take(3) |
Values are evaluated each time the element renders, and an attribute that reads state changes when the state does.
What becomes of an attribute
On an HTML element, an attribute is written only if Weft knows it. The lists are in Built-in elements.
- HTML attributes such as
href,src,alt,type,valueandtabindexare written as text. An attribute whose value isnullis left out. - Flags such as
disabled,required,checkedandhiddenare written with no value when their value is truthy, and left out otherwise. labelbecomesaria-label, the element's accessible name. On an image withoutalt, it becomes thealtinstead.aria-*anddata-*pass through as they are, and so doesrole.classis a list of Tailwind classes, and the CSS for each is generated as the page renders. Any class with variants works:hover:bg-sky-500/50,md:grid-cols-3,dark:text-white.styleis inline CSS.gap,paddingandmargin(a number is pixels),colorandbackgroundare added to it.focus: truefocuses the element when it's rendered.motionandmotion-*animate it; see the README's animation section.- Anything else is left out.
div(foo: 1)writes nofoo; usedata-foofor your own. - Inside
svg, every attribute passes through, in the SVG namespace.
On a component, every attribute is a prop: a value the component reads, which updates it when it changes. The built-in components also take:
class, merged with their own classes, with yours winning where they conflict;as, the element they render as;id,aria-*,data-*and DOM events such as@click, which go to their outer element.
Interpolation
{expression} in a string is replaced by the expression's value as text, in attribute values and text nodes alike:
img(src: "/photos/{photo.id}.jpg", alt: "{photo.title}, by {photo.author}")How each kind of value becomes text is in Expressions. {{ and }} are literal braces.