# Declarative Canvas UI for the browser, inspired by Swift

> Source: <https://romanuk.org/declarative-canvas-ui-for-the-browser-inspired-by-swift/>
> Published: 2026-08-11 18:27:42+00:00

# Declarative Canvas UI for the browser, inspired by Swift

I read [this post](https://hivekit.io/blog/why-you-might-want-to-build-your-webapp-in-canvas-instead-of-html/) and immediately started wondering how it would look. I didn’t know that Google Spreadsheets use canvas to render their UI, or that Figma does too.
Now it seems pretty obvious. A few years ago I would have stopped right there, but now we have LLM agents at our disposal.

The article mentions these 4 reasons to use it for UIs:

- Fast
- Control
- Consistency
- Portability

All of this sounds like a canvas — pun very much intended — to build multiplatform apps, for web, desktop, or mobile.

Basically, I followed the post’s advice and built [WeaveKit](https://github.com/pixdeo/weavekit), a declarative Canvas UI toolkit.

If you’re interested, you can check out [WeaveKit](https://github.com/pixdeo/weavekit). The syntax is heavily inspired by SwiftUI.
It’s just syntactic sugar on top, using `dslToJs()`

.

```
VStack {
  Text('Hello, world')
    .font({ size: 28, weight: 700 })
}
.padding(20)
```

running on this very same page:

It’s possible to wire up a button and state.

# Reactive example

``` js
const clicks = signal(0)

VStack {
  spacing: 10
  align: 'leading'
  Text(() => 'clicks: ' + clicks())
    .font({ size: 26, weight: 700 })
    .foreground('#fafafa')
  Button('+1', () => clicks.set(n => n + 1))
}
.padding(20)
```

This part wires and mount everything:

```
// dslToJs rewrites the blocks to plain JS; new Function puts the
// toolkit in scope. Runs once — the signal is created here, not
// per frame — and the trailing expression is returned implicitly.
const buildView = new Function(
  'Button', 'Text', 'VStack', 'signal',
  dslToJs(source).code,
)
const view = buildView(Button, Text, VStack, signal)

mount(document.getElementById('app'), createCanvasBackend(), () => view)
```

Give it a spin — clone [WeaveKit](https://github.com/pixdeo/weavekit) and run the examples in your own page.
