MON · 20 JUL 2026Vol. 26 · 30

Code components

Authoring code components as Tibi single-file components, the Monaco editor, and the interim compiler.

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 .tibi source 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 marked inline render 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.tibi tab for the source and a props tab for the component's public prop API. Legacy components (raw JavaScript and CSS) keep script.js and styles.css tabs 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:

HelperPurpose
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.
propsThe 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 is false or nullish.
  • t-show="expr" — toggles display.
  • t-bind="ref" — two-way binding between an input and a state signal.
  • 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 $name inside 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-for re-renders its list wholesale on change; replace the array (items.value = [...]) rather than mutating it in place.
  • Avoid quid inside t-if/t-for branches; 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.