webtini

minimalistic view modeling for the web

similarities to knockout and vue but smaller and rawer

  • Minimalistic
  • Conformant
  • Declarative
  • Unobtrusive
  • Modular

Quick Start

the modern web doesn't need to be obfuscated and transpiled

<html>
  <head>
    <title>webtini quick-start</title>
    <script src="https://datadink.github.io/webtini/packages/standard-binder-package.min.js"></script>
    <!-- A style to hide the page until its ready -->
    <style>.loading { display: none; }</style>
  </head>
  <!-- Binds the "loading" class to the isLoading property on the Application -->
  <body class="loading" bind-class-loading="isLoading">
    <header>
      <!-- Binds to the "title" property on the application -->
      <h1>{title}</h1>
    </header>
    <nav>
      <!-- Binds this template to the `links` array on the Application -->
      <!-- Each item in the `links` array will generate an instance of the template content -->
      <template bind="links">
        <li><a bind-href="url">{text}</a></li>
      </template>
    </nav>
    <h1>Count: {count}</h1>
    <!-- Binds the button's "click" event to the "increment" function on the application -->
    <button bind-event-click="increment">Increment</button>
    <!-- Binds the input's "value" property to the application's "count" property -->
    <!-- Binds the input's "input" event to the application's "oninput" function -->
    <input type="number" bind-value="count" bind-event-input="oninput">
    <div>
      <!-- Binds this template to the "items" array on the Application -->
      <!-- Note: Each item is bound to Application.items[x]
        `~` binds the data root (Application)
        `^` binds the parent (Applications.items)
        `^.^` binds the parent-parent (Application)
      -->
      <template bind="items">
        <span>{{{value} of {^.length} or {~.count}/{^.^.count}}}</span>
      </template>
    </div>
    <script type="module">
      // The Application here acts as a viewmodel that the DOM view can be bound to using a `Binder`.
      // It exposes data and functionality for the view to bind to.
      class Application {
        // Binders are the engines for webtini applications.
        binder = new webtini.StandardBinder();

        // The body's "loading" class is bound to this value
        isLoading = false; 

        // The heading is bound to this value which pulls from the <title> in the header
        get title() { return document.querySelector('title').textContent; }

        // The page navigation template binds here and generates an instance of its content
        // for each item.
        links = [
          { text: 'source',  url: 'https://github.com/datadink/webtini' },
          { text: 'documentation',  url: 'https://datadink.github.io/webtini/documentation' },
        ];

        // A coordinated data value that multiple parts of the application use
        count = 0;

        // An event handler that triggers when the button in the view is clicked
        increment() { 
          this.count++; 
          this.render();
        }

        // An event handler that triggers when the input in the view is altered
        // Warning: Be aware of handlers that trigger events that retrigger handlers -> infinite recursion
        oninput(e) { 
          this.count = parseInt(e.target.value ?? 0); 
          this.render();
        }

        // (Advanced) Creates an array of items based on the "count" that a template in the view binds to.
        get items() { 
          var number = Math.abs(Math.max(-5000, Math.min(100, this.count)));
          return [...new Array(Number.isNaN(number) ? 0 : number)]
            .map((_, i) => ({ value: i + 1 })); 
        }

        // Triggers the binder on the body of this page to (re)render the page with the application's data
        render() { this.binder.bind(document.body, this); }
      }
      const app = new Application();
      app.render();
    </script>
  </body>
</html>

Packages

use the web as-is and as it's meant to be

Standard

Includes all standard modules

download

Common JS

<script src="standard-binder-package.min.js"></script>
const binder = new webtini.StandardBinder();

JS Module

import {StandardBinder} from './standard-binder-module.min.js';
const binder = new StandardBinder();

Includes:

  • AttributeBinder
  • ClassBinder
  • EventBinder
  • StyleBinder
  • TemplateBinder
  • TextBinder

Minimal

Provides basic model binding, content generation & interactivity

download

Common JS

<!-- to pull directly from github (not for production) -->
<script src="minimal-binder-package.min.js"></script>
const binder = webtini.MinimalBinder();

JS Module

// to import directly from github (also not for production)
import {MinimalBinder} from './minimal-binder-module.min.js';
const binder = new MinimalBinder();

Includes:

  • TemplateBinder
  • EventBinder

Basic Binders

webtini is modular & extensible - use as much or little as you like

AttributeBinder

Enables bind-attribute-name attributes to bind values to other attributes on an element

<img bind-attribute-alt="data.context.text" />

ClassBinder

Enables bind-class-name attributes to toggle classes on an element from a boolean(ish) value

<div bind-class-active="data.context.selected">...</div>

EventBinder

Enables bind-event-name attributes to assign functions to events on an element

<button bind-event-click="data.context.submit">Save</button>

StyleBinder

Enables bind-style-name attributes to assign values to an element's styles

<div bind-style-background="data.context.color">...</div>

TemplateBinder

Causes HTMLTemplateElements to generate content when bound to an array

<template bind="data.context.items"><span bind-textcontent="name"></span></template>
binder.bind(element, { data: { context: { items: [{name: 'a'}, {name: 'b'}] } } });

TextBinder

Enables familiar inline {data.property} binding from within text.

<h1>Page: {title}</h1>

FAQ

constomize webtini to work the way you want

  • When does it make sense to use this?
    • When you need a web app right now with little effort and no overhead.
  • What is the benefit?
    • This package helps to keep web development simple, clean and extensible.
    • Modern web technologies no longer need massive processing efforts such as transpiling from something else.
    • By working with & along-side modern web patterns & practices it is easier to prevent your code from becoming obsolete.
    • MVVM and similar patterns help keep concerns decoupled and modular.
  • Can this be used for large applications?
    • Yes.
  • Does this work on legacy browsers?
    • No: webtini targets modern browsers only
  • Is it performant?
    • Depends: webtini leaves performance up to you. You have full control over the render/update loop, etc.
    • Typically, web applications do not need extreme performance and there's no framework in the world that doesn't add overhead to your application.
  • Does it scale?
    • Depends: webtini only focuses on connecting your view with your data & functionality. The scalability of the app you write is up to you.
    • Because of webtini's extensible design, it can be easily customized, optimized and you can even contribute here if you've got something useful!
  • What do I have to learn?
    • Just good-ol', raw javascript, html, and css.
    • Understanding data-driven patterns like MVVM will help a lot.
  • Can I use typescript?
    • As far as I know.
  • How does this fit in with REACT?
    • It doesn't. React obfuscates and entangles web technologies. webtini embraces them.