A .lt file has up to four blocks. Order is free; only
<template> is required.
Anatomy
<meta>
name: Card
props:
title: string
body: string = ""
provide: [dict]
uses: [Header]
</meta>
<style>
.card { padding: 1rem }
</style>
<template>
<article class="card">
<h1>{title}</h1>
<p>{body || dict.fallback}</p>
<slot/>
</article>
</template>
<script>
/* inlined as-is at end of body */
document.querySelector('.card')?.addEventListener('click', e => console.log('hi'));
</script>
No imports
Notice: there are no import statements anywhere. The framework
resolves uses declaratively — the compiler reads the list and
produces the ES module imports for you.
Expression sub-language
Inside { } you have a strict subset of JS:
- Property access:
{dict.welcome},{posts[0].title} - Optional chaining:
{cfg?.site?.name} - Logic:
{a || b},{a && b},{a ?? b} - Ternary:
{user ? user.name : "guest"}
Why so strict?
Because expressions are compiled to plain JS source code at build time —
no eval() or new Function() ever runs,
not even at build time. CSP-safe, Workers-safe, fast.