Skip to content
ErickgiberPublic

About

A lightweight JavaScript library designed for developing high-performance apps with WebGL and 2D Canvas.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

137 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

CanvApps Logo

CanvApps 🎨

The First Compiled UI Framework That Renders at 120 FPS by Eliminating the DOM.
Native 120 FPS hardware rasterization β€’ Zero DOM layout thrashing β€’ Ghost DOM accessibility

npm version core npm downloads License: MIT TypeScript Vite Capacitor 120 FPS Ghost DOM


⚑ Why CanvApps?

CanvApps is not a canvas drawing library β€” it's a full compiled UI framework.

It compiles declarative .cvs Single-File Components into an HTML5 2D GPU-accelerated Canvas render tree at 120 FPS, backed by Ghost DOM for native text editing, mobile keyboards, and accessibility.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       🎨 2D GPU CANVAS LAYER          β”‚         πŸ‘» GHOST DOM LAYER            β”‚
β”‚  (120 FPS Hardware Rasterization)     β”‚  (Zero-Cost Semantic HTML Overlay)    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ Pure Mathematical Flexbox           β”‚ β€’ Native iOS/Android Virtual Keyboardsβ”‚
β”‚ β€’ Fine-Grained Signals (No VDOM)      β”‚ β€’ Real Browser Text Selection & Copy  β”‚
β”‚ β€’ Hardware-timed Kinetic & Motion FX  β”‚ β€’ Full A11y (VoiceOver / TalkBack)    β”‚
β”‚ β€’ Pixel-Perfect DPR Retina Scale      β”‚ β€’ OS Context Menus & IME Compositions β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Features

  • πŸš€ 120 FPS β€” Direct Canvas 2D rendering with Retina/HiDPI scaling
  • 🚫 Zero DOM β€” Pure TypeScript Flexbox solver, no reflows
  • πŸ‘» Ghost DOM β€” Native keyboards, text selection, clipboard, screen readers
  • ⚑ Signals β€” signal, computed, effect, batch β€” no Virtual DOM diffing
  • 🎨 .cvs SFC - components compiled at build time
  • 🎞️ Motion & KineticFX β€” Declarative animations, hero morphs, particle bursts
  • πŸ“¦ Multi-Target β€” SPA, PWA, or native mobile (Capacitor) from one config

πŸš€ Quick Start

Create a new project (Recommended)

npm create canvapps@latest my-app
cd my-app
npm install
npm run dev

Also works with other package managers:

pnpm create canvapps my-app
yarn create canvapps my-app
bun create canvapps my-app

Or install manually

npm install @canvapps/core

CDN (No build tools)

<!DOCTYPE html>
<html>
<head>
  <style>html, body, #app { width: 100%; height: 100%; margin: 0; overflow: hidden; }</style>
</head>
<body>
  <div id="app"></div>
  <script src="https://cdn.jsdelivr.net/npm/@canvapps/core/dist/canvapps.umd.cjs"></script>
  <script>
    const { Engine, UIView, UIText, UIButton, signal, effect } = window.canvapps;

    const count = signal(0);
    const engine = new Engine({ container: '#app', autoResize: true, backgroundColor: '#0f172a' });

    const root = new UIView({ width: '100%', height: '100%', alignItems: 'center', justifyContent: 'center', gap: 14 });
    const text = new UIText('Clicks: 0', { fontSize: 22, color: '#ffffff' });
    const btn = new UIButton('+ Click Me', { padding: [10, 20], backgroundColor: '#2563eb', labelColor: '#ffffff' });

    btn.on('click', () => count.update(n => n + 1));
    effect(() => text.setText(`Clicks: ${count.value}`));

    root.addChild(text).addChild(btn);
    engine.setRoot(root).start();
  </script>
</body>
</html>

🎨 .cvs Single-File Components

Compiled at build time with zero runtime overhead. syntax:

<script lang="ts">
  const count = signal(0);
  const taskInput = signal('');

  function onInput(e: any) {
    taskInput.value = e?.target?.value ?? e?.value ?? '';
  }

  function addTask() {
    const text = taskInput.value.trim();
    if (!text) return;
    tasks.update((list) => [{ id: Date.now(), title: text }, ...list]);
    taskInput.value = '';
  }

  const tasks = signal([
    { id: 1, title: 'Eliminate DOM layout thrashing' },
    { id: 2, title: 'Test Ghost DOM on iOS & Android' },
  ]);
</script>

<view width="100%" height="100%" flexDirection="column" backgroundColor="#f8fafc" padding="[20, 32]" gap="16">

  <!-- Dynamic Bindings -->
  <text fontSize="18" :color={count.value > 5 ? '#dc2626' : '#0f172a'}>
    Clicks: {{ count.value }}
  </text>

  <!-- Events -->
  <button label="+1" @click="() => count.update(n => n + 1)" />

  <!-- Inputs (Ghost DOM: native keyboards & clipboard) -->
  <input placeholder="New task..." :value="taskInput.value" @input="onInput" @submit="addTask" />

  <!-- List Iteration -->
  @each tasks.value as item, index {
    <view width="100%" padding="12" flexDirection="row" justifyContent="space-between">
      <text fontSize="13" selectable="true">β€’ {{ item.title }}</text>
    </view>
  }

  <!-- Conditionals -->
  @if (count.value > 10) {
    <text color="#059669">πŸŽ‰ You passed 10 clicks!</text>
  }

</view>

🌈 Template Syntax

Dynamic Bindings (:prop={expr})

<view :gap={isMobile.value ? 6 : 12} :flexDirection={isMobile.value ? 'column' : 'row'}>
  <button :backgroundColor={isActive.value ? '#2563eb' : '#64748b'} label="Toggle" />
</view>

Conditionals (@if / @else)

@if (isMobile.value) {
  <text>πŸ“± Mobile Layout</text>
} else {
  <text>πŸ’» Desktop Layout</text>
}

List Iteration (@each)

@each tasks.value as item, index {
  <view width="100%" padding="12">
    <text selectable="true">β€’ {{ item.title }}</text>
    <button label="βœ•" @click="() => removeTask(item.id)" />
  </view>
}

Component Composition

<script lang="ts">
  import HomeView from './views/HomeView.cvs';
  import SettingsView from './views/SettingsView.cvs';
  const showSettings = signal(false);
</script>

<view width="100%" height="100%">
  @if (showSettings.value) {
    <SettingsView @close="() => showSettings.value = false" />
  } else {
    <HomeView />
  }
</view>

Events (@event)

<button label="Save" @click="handleSave" />
<button label="+1" @click="() => count.update(n => n + 1)" />
<input @input="onInput" @submit="onSubmit" />
<select :options="options" @change="onChange" />
<view @pointerenter="() => hovered.value = true" @pointerleave="() => hovered.value = false">
  <text>Hover me</text>
</view>

Supported events: @click, @dblclick, @pointerdown, @pointerup, @pointermove, @pointerenter, @pointerleave, @input, @change, @submit, @keydown, @keyup, @scroll, @wheel, @finish, @close.


🎞️ Motion & Animations

Declarative <motion> Transitions

<motion animation="cinematic-splash" :duration="1100" :hold="800" @finish="onSplashFinish">
  <view width="100%" height="100%" backgroundColor="#090d16" alignItems="center" justifyContent="center">
    <text fontSize="56" fontWeight="bold" color="#ffffff">CanvApps</text>
  </view>
</motion>

<motion enter="elastic" exit="slide-left" :duration="450">
  <GalleryView />
</motion>

KineticFX (Particle Bursts & Flying Tokens)

import { KineticFX } from '@canvapps/core';

KineticFX.flyToken({
  from: { x: e.clientX, y: e.clientY },
  to: '#counter-badge',
  text: '+100 XP',
  duration: 480,
  onHit: () => score.value += 100,
});

KineticFX.burst({
  x: e.clientX, y: e.clientY,
  colors: ['#38bdf8', '#34d399', '#818cf8'],
  count: 24,
});

Modal with Hero Morph

<modal :open="isOpen.value" :originRect="rect.value" animation="hero" :blur="true" @close="closeModal">
  <view width="760" backgroundColor="#ffffff" borderRadius="20" padding="28">
    <text fontSize="20" fontWeight="bold">Hero Expanded Dialog</text>
  </view>
</modal>

Smart Animate (Shared Element Transitions)

Assign matching :layoutId props to elements across views for fluid morph transitions:

<!-- Source -->
<a href="/docs" label="πŸ“– Docs" :layoutId="'nav-docs'" />

<!-- Destination -->
<text :layoutId="'nav-docs'" fontSize="13" fontWeight="bold">πŸ“– Docs</text>

πŸ’Ύ Reactive Stores

import { createStore, computed, persistentSignal } from '@canvapps/core';

export const sessionStore = createStore({
  user: null,
  isAuthenticated: false,
  theme: 'light' as 'light' | 'dark',
}, { name: 'session', persist: true }); // Auto localStorage sync

export const isLoggedIn = computed(() => sessionStore.state.isAuthenticated);
export const authToken = persistentSignal('jwt_token', '');

βš™οΈ Configuration (canvapps.config.ts)

import { defineConfig } from '@canvapps/core';

export default defineConfig({
  target: 'PWA',        // 'SPA' | 'PWA' | 'CAPACITOR'
  title: 'My App',
  outDir: 'dist-app',
  banner: true,         // Open-source watermark in output
  safeArea: true,       // Auto notch/status bar insets
  themeColor: { light: '#f8fafc', dark: '#090d16' },
  pwa: {
    name: 'My App',
    shortName: 'App',
    themeColor: '#2563eb',
    display: 'standalone',
  },
  capacitor: {
    appId: 'com.myapp.app',
    appName: 'My App',
  },
});

πŸ“± Safe Area & Theme Color

<!-- Auto notch/status bar spacing -->
<view width="100%" safeArea="top" padding={[12, 20]}>
  <text fontSize="18" fontWeight="bold">🎨 My App</text>
</view>
import { useSafeArea, setThemeColor } from '@canvapps/core';

const { top, bottom } = useSafeArea();
// top.value => 47 on iPhone 15, 0 on desktop

setThemeColor('#101010'); // Updates status bar, titlebar, overscroll

πŸ› οΈ Vite Plugin Setup

// vite.config.ts
import { defineConfig } from 'vite';
import canvappsPlugin from '@canvapps/core/vite';

export default defineConfig({
  plugins: [canvappsPlugin()],
  resolve: {
    alias: [
      { find: /^@canvapps$/, replacement: '@canvapps/core' },
      { find: /^canvapps$/, replacement: '@canvapps/core' },
    ],
  },
  optimizeDeps: {
    include: ['@canvapps/core'],
  },
});

πŸ—οΈ CLI Commands

# Create a new project
npm create canvapps@latest my-app

# Development server (Vite + HMR)
npm run dev

# Production build
npx canvapps build

# Format .cvs files
npx canvapps format

πŸ’» IDE Extension

Official extension for VS Code, Cursor, Windsurf, and Antigravity IDE:

πŸ‘‰ Download canvapps-vscode-0.1.0.vsix

Features: Syntax highlighting, Go to Definition, autocompletion, snippets (cvs-component, cvs-view, cvs-button, etc.), and tag auto-closing for .cvs files.

# Install via CLI
code --install-extension canvapps-vscode-0.1.0.vsix

πŸ“š API Reference

API Description
createApp(Component, options?) Create and configure an app instance
app.mount('#container') Mount app to DOM element
new Engine(options) Canvas renderer with 120 FPS loop
UIView Flexbox layout container
UIText Multiline text with optional selection
UIButton Interactive button with hover/active states
UIInput Text input with Ghost DOM keyboard sync
UISelect Canvas dropdown selector
UIModal Modal with hero morph & blur
UIMotion Declarative scene transitions
KineticFX Flying tokens & particle bursts
signal(val) Reactive state primitive
computed(fn) Derived reactive value
effect(fn) Reactive side effect
batch(fn) Batch multiple signal updates
createStore(state, opts?) Global reactive store with persistence
persistentSignal(key, val) Signal with localStorage sync
createRouter(config) Client-side canvas router
useSafeArea() Reactive device inset signals
setThemeColor(color) Dynamic status bar/titlebar sync
defineConfig(config) Typed canvapps.config.ts helper

πŸ“Š Comparison

DOM Frameworks Flutter Web CanvApps
Rendering Browser DOM WebGL/Skia (Wasm) Canvas 2D (GPU)
FPS 30–60 45–60 60–120
Layout Browser reflows Dart layout Math Flexbox (TS)
Reactivity VDOM diffing Widget rebuild Fine-grained signals
Keyboards Native Emulated Native (Ghost DOM)
A11y Native Emulated Native (Ghost DOM)
Bundle Medium–Heavy >2MB Wasm <30KB

πŸ“„ License

MIT Β© Erickgiber

About

A lightweight JavaScript library designed for developing high-performance apps with WebGL and 2D Canvas.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages