A real layout engine
Flex and grid with CSS's own semantics, typed lengths, and text that wraps, clamps and sits on a baseline. Every case is checked against Chrome, so a box is the size you expect.
Layout in fullThe whole interface, off the main thread
Components, layout, paint, text and input all run in a render worker. Your application logic runs in another. Whatever you compute, the frame still lands.
Every pixel above is drawn by Gesso into a single canvas element.
Try it
Two copies of the same component. Only one of them notices.
What you get
Flex and grid with CSS's own semantics, typed lengths, and text that wraps, clamps and sits on a baseline. Every case is checked against Chrome, so a box is the size you expect.
Layout in fullLines break where Chrome breaks them in eight scripts, with colour emoji, right to left paragraphs and web fonts loaded inside the worker. Both renderers, same result.
How text worksInputs, overlays, tables, trees and media. Every one themed, keyboard operable, and announced to assistive technology without you doing anything.
Browse the libraryThe model
A state change writes one property on one node. There is nothing to memoise, no dependency array to keep honest, no view identity to reason about. Your component function runs once, so every closure in it is stable.
import { percent } from 'gesso-core';
import { Button } from 'gesso-components';
import { type ComponentContext, type Inputs, computed, input, internalState } from 'gesso-framework';
export function Counter(inputs: Inputs<{ label?: string }>, _context: ComponentContext) {
const label = input(inputs.label, 'Count'); // inputs are cells; this one has a default
const count = internalState(0);
const caption = computed(() => `${label.value}: ${count.value}`);
return (
<row gap={12} x="center" y="center" width={percent(100)} height={percent(100)}>
<text text={caption} textStyle="title" />
<Button label="Add one" onClick={() => count.value++} />
</row>
);
} Neither version names a colour. primary is a theme token, resolved when it is painted, which is why the counter follows this page's light and dark toggle.
The scaffold writes the worker, the shell and the first component, then gets out of the way.
$pnpm create:app my-app