Skip to content

iwalton3/vdx-web

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

117 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VDX - Vanilla Developer Experience

vdx-web (core framework) + vdx-ui (component library)

A modern web framework with ZERO npm dependencies. Reactive state, components, routing, two-way binding - all running directly in the browser with no build step.

Key Features

  • Zero dependencies - No npm, no node_modules, no supply chain risk
  • No build step - ES6 modules run directly in the browser
  • TypeScript support - Optional .d.ts files for type checking (docs)
  • Reactive state - Vue-style proxy-based reactivity
  • Two-way binding - x-model with automatic type conversion
  • Component system - Web Components with fine-grained reactive rendering
  • Router - Hash and HTML5 routing with lazy loading
  • Static site friendly - Embed components in any HTML page

Quick Start

cd app
python3 test-server.py

Then open: http://localhost:9000/

That's it! No npm install, no build process, no dependencies to install.

New to VDX? Check out the Step-by-Step Tutorial for a comprehensive guide.

VS Code Setup (Optional)

For CSS and HTML syntax highlighting in components, install es6-string-html.

Ctrl+P, then:

ext install Tobermory.es6-string-html

Use a /*css*/ comment in your styles to get highlighting:

styles: /*css*/`
    .button {
        background: #007bff;
    }
`

Hello World

<!DOCTYPE html>
<html>
<body>
    <my-counter></my-counter>

    <script type="module">
        import { defineComponent, html } from './lib/framework.js';

        defineComponent('my-counter', {
            data() {
                return { count: 0 };
            },

            template() {
                return html`
                    <div>
                        <h1>Count: ${this.state.count}</h1>
                        <button on-click="${() => this.state.count++}">
                            Increment
                        </button>
                    </div>
                `;
            }
        });
    </script>
</body>
</html>

No compilation. No bundling. Just refresh your browser.

Two-Way Data Binding

A feature even React doesn't have - automatic two-way binding with type conversion:

defineComponent('user-form', {
    data() {
        return {
            username: '',
            age: 18,
            newsletter: false
        };
    },

    template() {
        return html`
            <form on-submit-prevent="handleSubmit">
                <input type="text" x-model="username" placeholder="Username">
                <input type="number" x-model="age" min="13" max="120">
                <label>
                    <input type="checkbox" x-model="newsletter">
                    Subscribe to newsletter
                </label>
                <button type="submit">Submit</button>
            </form>

            <pre>
                Username: ${this.state.username}
                Age: ${this.state.age} (type: ${typeof this.state.age})
                Newsletter: ${this.state.newsletter}
            </pre>
        `;
    },

    methods: {
        handleSubmit(e) {
            console.log({
                username: this.state.username,
                age: this.state.age,          // Already a number!
                newsletter: this.state.newsletter  // Already a boolean!
            });
        }
    }
});

The framework automatically uses the correct attribute (value or checked), sets up the right event, and converts types.

Live Demos

Demo Description
E-commerce Shop Full shopping experience with routing and state management
Component Library Interactive showcase of all vdx-ui components
Playground Core framework features demo
Static Integration Embedding components in static HTML
PWA Offline Service worker with versioned caching for offline support
Task Manager (TS Demo) Task management demo application written in typescript
Framework Tests Comprehensive test suite

Documentation

Using VDX in Your Project

Projects using VDX can reference the compact framework guide in their CLAUDE.md:

## Framework
This project uses VDX. See [FRAMEWORK.md](path/to/vdx/FRAMEWORK.md) for patterns.

Why VDX?

In the age of code rot, supply chain vulnerabilities, and dependency chains too big to review, this project explores another path.

Core Principles

  • Zero npm dependencies - No package.json, no node_modules, no supply chain risk
  • No build step - Runs directly in the browser using ES6 modules
  • Fine-grained reactivity - Direct DOM updates, no virtual DOM diffing overhead
  • Modern DX - Reactive state, components, routing, two-way binding
  • Production ready - 290+ passing tests, XSS protection, used in real apps

Technical Innovation

  • Template compilation system - Compiles `html`` templates once, applies values on re-render
  • Smart two-way binding - x-model with automatic type conversion
  • Chainable event handlers - Combine x-model with on-input/on-change for custom logic
  • Auto-bound methods - No manual .bind(this) needed
  • Computed properties - Memoized values with dependency tracking
  • Virtual scrolling - Efficiently render massive lists

Static Site Integration

Components work like native HTML elements, making them perfect for enhancing static sites:

  • Vanilla JS event listeners - Use addEventListener() on components like any HTML element
  • DOM attribute propagation - setAttribute() changes automatically flow into components
  • Direct prop setting - Pass arrays/objects/functions directly: element.items = [...]
  • Children props - Pass HTML content inside component tags, access via this.props.children
  • Nested component hydration - VDX components in light DOM children hydrate automatically, enabling SSG patterns
  • No framework lock-in - Components integrate with jQuery, vanilla JS, or any other code

Important: Components are a boundary between vanilla JS and VDX. The framework manages everything inside a component's template - don't use DOM manipulation (appendChild, innerHTML, etc.) on elements inside components, as the reactive system will overwrite changes on the next render.

See the Static Integration Demo for live examples.

Hydration Support

VDX supports automatic hydration of component trees from static HTML - perfect for static site generators (Hugo, Jekyll, Eleventy):

<!-- Server-rendered or static HTML -->
<collapsible-panel title="Settings">
    <theme-switcher mode="dark"></theme-switcher>
    <volume-control level="80"></volume-control>
</collapsible-panel>

When the page loads, VDX automatically captures children, parses them as VNodes, and hydrates nested components recursively. No special hydration API needed.

Architecture

How It Works

  1. Template Compilation - `html`` templates compiled once to AST structure
  2. Template Instantiation - DOM nodes created with reactive bindings for each dynamic value
  3. Fine-Grained Updates - Only the specific DOM nodes that depend on changed state are updated
// Template compiled once when first rendered
const template = html`<div>${this.state.count}</div>`;

// On state change: only the text node for ${count} is updated, not the whole tree

Fine-Grained Reactivity

VDX uses fine-grained reactive rendering inspired by SolidJS:

  • Each template binding (${value}) creates its own reactive effect
  • State changes trigger O(1) updates to specific DOM nodes
  • No full-tree diffing - direct DOM manipulation for maximum performance
  • Scales to thousands of items with consistent performance

Project Structure

app/
├── lib/                     # vdx-web: Core framework
│   ├── framework.js         # Main barrel export
│   ├── framework.d.ts       # TypeScript definitions
│   ├── router.js            # Router system
│   ├── router.d.ts          # TypeScript definitions
│   ├── utils.js             # Utilities (notify, darkTheme, etc.)
│   ├── utils.d.ts           # TypeScript definitions
│   └── core/                # Framework internals (~3000 lines)
├── dist/                    # Pre-bundled versions for embedding
├── componentlib/            # vdx-ui: Professional UI component library
├── components/              # Shared UI components
├── tests/                   # Test suite (293 tests)
├── ts-demo/                 # TypeScript demo application
└── index.html               # Entry point

Two Ways to Use

1. Library imports (development):

import { defineComponent, html, reactive } from './lib/framework.js';
import { Router } from './lib/router.js';
import { notify, darkTheme } from './lib/utils.js';

2. Pre-bundled (embedding/simple projects):

import { defineComponent, html, reactive } from './dist/framework.js';
// Everything in one ~74KB file

Security Features

Defense-in-Depth XSS Protection

  1. Symbol-based trust markers - HTML_MARKER and RAW_MARKER are non-exported Symbols
  2. Context-aware escaping - URLs, attributes, text content each handled correctly
  3. toString() attack prevention - Uses Object.prototype.toString.call() for objects
  4. No dangerous fallbacks - Removed all dangerouslySetInnerHTML except in raw()
// SAFE - Auto-escaped
html`<div>${userInput}</div>`

// SAFE - URL sanitized
html`<a href="${userUrl}">Link</a>`

// USE ONLY FOR TRUSTED CONTENT
html`<div>${raw(trustedApiHtml)}</div>`

Running Tests

cd app
python3 test-server.py

Then open: http://localhost:9000/tests/

Browser Compatibility

Requires modern browsers with ES6+ support:

  • Chrome/Edge 61+
  • Firefox 63+
  • Safari 10.1+

No IE11 support - By design (IE11 EOL 2022)

Deployment

Since there's no build step required, deployment is simple:

  1. Copy the app/ directory to your web server
  2. Configure server routing if using HTML5 mode (see docs/routing.md)

That's it!

Optional Build Step

While VDX runs directly in the browser with no build step, there's an optional optimizer and bundler for production deployments. Unlike typical JavaScript tooling, these tools have zero npm dependencies - they're pure Node.js scripts.

Benefits

Feature Benefit
Linting Catch reactivity issues before deployment
Optimization Automatic fine-grained reactivity (wrap expressions in contain())
Minification Smaller file sizes with accurate source maps
Bundling Combine framework files with tree-shaking

Quick Start

# Lint for reactivity issues (catches bugs that break fine-grained updates)
node optimize.js -i ./src -l

# Lint in strict mode (for CI - fail on unfixable issues)
node optimize.js -i ./src -l --strict

# Build with minification and source maps
node optimize.js -i ./src -o ./dist -m -s

# Bundle framework files
node bundler-esm.js

What the Optimizer Does

  1. Wraps reactive expressions - Transforms ${this.state.x} in templates to use fine-grained reactivity automatically
  2. Fixes early dereferences - Inlines const { x } = this.state patterns that would break reactivity
  3. Strips runtime wrappers - Removes eval(opt()) calls (they become redundant after build-time optimization)
  4. Minifies with source maps - Full JavaScript minification with accurate debugging support

Why No Dependencies?

The optimizer and bundler are ~3000 lines of vanilla JavaScript each. They:

  • Parse and transform code using custom parsers
  • Generate source maps with VLQ encoding
  • Perform tree-shaking and dead code elimination

No Babel, no Terser, no webpack, no esbuild. Just Node.js.

Completely Optional

You can:

  • Never use the build tools - Everything works directly in the browser
  • Use only the linter - Catch bugs without changing your workflow
  • Use full optimization - For production deployments with smaller files

See docs/optimization.md for the complete guide.

PWA Offline Support

You can use a service worker with vdx-web, which retains the caching and update benefits of a typical application bundler with less complexity and the added benefit of offline support. All you need to do is generate a new manifest before each build and the service worker handles the rest.

How It Works

  1. spider-deps.js discovers all JS/CSS dependencies from your entry point
  2. Generates cache-manifest.json with content hash version
  3. Service worker caches all files atomically with version isolation
  4. App works offline with cache-first strategy
# Before deploying, generate the manifest
node spider-deps.js

See the PWA Offline Demo for a working example, or check out the source code.

Use Cases

  • Sustainable projects - No dependency rot or supply chain issues
  • Embedded environments - Jellyfin Media Player, Electron apps, etc.
  • Quick prototypes - Just drop in one file and start building
  • Legacy modernization - Add reactive components without a complete rewrite
  • Security-conscious projects - Audit the entire codebase (~3000 lines)
  • Learning - Understand how modern frameworks work under the hood

Philosophy

In a world where a simple "hello world" requires hundreds of megabytes of dependencies, we asked: What if we didn't?

Zero dependencies doesn't mean zero features.

Inspiration

  • lit-html - Tagged template literals for HTML
  • Vue 3 - Proxy-based reactivity system
  • SolidJS - Fine-grained reactive rendering
  • Svelte - Component-first architecture
  • Web Components - Native browser APIs

License

VDX Framework and all components are MIT licensed.

Quality Assurance

VDX consists mainly of code created using Claude. Quality assurance is achieved through extensive unit and end to end testing, as well as manual tests, performance evaluation, and through usage in other personal projects such as mrepo-web and my personal website.

This project is a quality framework with a high degree of optimization and iteration based on actual development experience and testing, but don't expect it to be perfect. There will be changes in the future but it's mostly feature complete and the core APIs are established and unlikely to change significantly.

There's no warranty if you build a business around my side project and get burned, but the framework in general avoids a lot of other risks such as code rot and supply chain breaches associated with extremely large NPM dependency graphs. (There are no external dependencies.) Since there is no build step, you could theoretically use this framework long into the future, as long as there are no major breaking changes in web browsers (less likely than your framework/build system/bundler/etc going out of style or deciding to leave you in the cold with a mountain of breaking changes).

Contributing

PRs are welcome! Please ensure:

  • No npm dependencies are added
  • Tests pass (see /app/tests/ and /componentlib-e2e/)
  • Code follows existing patterns
  • Security best practices maintained

Acknowledgments

  • SolidJS - For inspiration on fine-grained reactivity
  • Vue.js - For inspiration on reactivity system
  • lit-html - For tagged template literal ideas
  • developit/htm - For optimization ideas for template engine

About

Build modern component-based webapps that run directly in the browser as written. Batteries included, easy to use, no `npm install`!

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors