Code components
A code component (kind: code) is one you write yourself. When creating one,
you choose between two formats:
- Tibi component (recommended) — a Tibi single-file component: one
.tibisource with a<template>, an optional scoped<style>, and an optional<script tibi>for reactivity. On save, Bliss compiles the source into a self-contained custom element that runs sandboxed in an iframe on published pages (system components markedinlinerender bare). The authored source is the artefact you edit; the compiled output is regenerated on every save. - Web component — a raw JavaScript custom element plus a CSS file, saved and run exactly as written. Pick this when you want full manual control or are porting an existing custom element.
Both start from a working example, so a freshly created component renders immediately. The format is fixed at creation: a component cannot be converted between the two later. To move an existing web component to Tibi, create a new Tibi component and port the code across (with the format guide open beside you), then replace the old component on your pages.
The long-term plan is for published sites to run on the full Tibi runtime.
Authoring in the .tibi format now means components carry over unchanged when
that lands: only the compile target swaps.
The editor
The code component editor (reached from any component library via Edit) gives you:
- A Monaco editor (the VS Code editor) with syntax highlighting, tabbed files, and per-file undo history.
- A
component.tibitab for the source and apropstab for the component's public prop API. Legacy components (raw JavaScript and CSS) keepscript.jsandstyles.csstabs instead. - A sandboxed live preview: press Run preview to compile and re-render.
- Ctrl/Cmd+S saves. Saving compiles the source first; compile errors are shown inline in the editor and under it, and block the save until fixed.
- This guide, in the editor: the Docs button in the header (or Format guide above the source) opens this page in a side panel, so the directive reference stays at hand while you write.
The .tibi format
<template>
<section class="widget">
<h2>{title}</h2>
<button @click="count.value += 1">Clicked {{ count }} times</button>
</section>
</template>
<style>
.widget { display: flex; flex-direction: column; gap: 0.5rem; }
</style>
<script tibi>
const count = state(0);
</script>
- Exactly one
<template>block is required; at most one<style>and one<script>. <style>is scoped to the component (shadow DOM), so selectors cannot leak in or out.<script tibi>opts into the reactive surface. A plain<script>is passthrough: it runs once, unmodified, before the element is defined.
Reactive surface
Inside <script tibi> these helpers are in scope:
| Helper | Purpose |
|---|---|
state(v) | A reactive signal; read and write via .value. |
computed(fn) | A derived, read-only signal. |
effect(fn) | Re-runs fn whenever the signals it reads change. |
onMount(fn) / onUnmount(fn) | Lifecycle hooks. |
emit(name, detail) | Dispatches a bubbling CustomEvent from the element. |
props | The component's prop values, reactive to attribute changes. |
Template directives:
{{ expr }}— reactive text interpolation.{propName}— interpolates a declared prop (in text and attribute values).@event="handler"— event listener; the value can be a function reference or an inline statement.:attr="expr"— reactive attribute; removed when the value isfalseor nullish.t-show="expr"— togglesdisplay.t-bind="ref"— two-way binding between an input and astatesignal.t-if/t-else-if/t-else— conditional sibling chain.t-for="item of list"— list rendering (an optional index:t-for="(item, i) of list").quid="name"— surfaces the element as$nameinside the script.<slot/>— native shadow-DOM slot.
Props
Declare props on the props tab. Each prop becomes an observed attribute on
the custom element, a {name} token in the template, and a key on the reactive
props object in the script. Page authors set prop values wherever the
component is placed.
Interim limitations
Bliss currently compiles .tibi sources itself rather than running the full
Tibi runtime, so a few Tibi features are not available yet (each produces a
compile warning, not an error):
- Frontmatter (compile-time data fences) is ignored.
- Server channels (
__channels,Tibi.ws) are stubbed;subscribe()is a no-op. - Component composition (uppercase tags such as
<Greeting/>) does not resolve; each code component stands alone. t-forre-renders its list wholesale on change; replace the array (items.value = [...]) rather than mutating it in place.- Avoid
quidinsidet-if/t-forbranches; refs are collected once at mount.
Web components
Web-component code (raw custom-element JavaScript plus CSS, including
components written before the .tibi format existed) is saved and run exactly
as written, with Monaco editing on the script.js and styles.css tabs. The
script must register a custom element whose tag matches the component's tag
field.