Copy the kit, then copy a page.

Drop the files into a static site. nginx, or anything that serves files, is enough. Each page then uses the same owners: DataProvider for HTTP, Interceptor for fetch, and bind for the paint.

<link rel="stylesheet" href="./kit/css/tokens.css">
<link rel="stylesheet" href="./kit/css/base.css">
<script type="module" src="./page.js"></script>
  1. 1

    Copy the kit

    Copy src/ and css/ into the site static tree. There is no package install and no build step for the kit itself.

  2. 2

    Link two stylesheets

    tokens.css holds the --pk-* variables. base.css imports layout, forms, and components. A live room also links live.css.

  3. 3

    Write one page script

    The script constructs a DataProvider, subscribes, and calls load, create, or save. Interceptor is already on that path. English stays in the HTML.

  4. 4

    Steal a recipe

    demo/recipes/ is the catalog: sign in, items, table, settings, activity, room. Copy the page that matches the job. Full page loads stay full page loads.

Where the work goes

An agent that follows this split stays on one page. It copies a recipe, points a DataProvider at a URL, and puts shared HTTP behavior on the interceptor. The provider is the only copy of that resource.

DataProvider

One provider holds one resource. load is GET, create is POST, save is PUT, remove is DELETE. subscribe reports data, status, and error. The app owns the JSON. The kit does not ship a schema.

Interceptor

DataProvider calls defaultInterceptor.fetch, and that is the only fetch. Register a request hook for a header, or an error hook with redirectOnStatus when 401 or 403 should replace the page. Do that once on the page, not inside each control.

Bind

bindText, bindField, and bindList write the DOM from the provider event. bindField is two-way on a top-level key. It is not a form framework. Submit still calls create or save.

LiveProvider

Use it when the page is a socket, as in the room recipe. connect, send, and close are the verbs. A send while the socket is down is dropped. The page still does not construct a WebSocket.

One page script

Headers and error redirects sit on the interceptor. The provider only knows its URL. bind paints status into an element that already exists in the HTML.

import {
  DataProvider,
  bindText,
  defaultInterceptor,
  redirectOnStatus,
} from './kit/index.js';

// The page already holds the token. This runs for every DataProvider call.
defaultInterceptor.use('request', ({ url, opts }) => {
  opts.headers.set('Authorization', 'Bearer ' + token);
  return { url, opts };
});

defaultInterceptor.use(
  'error',
  redirectOnStatus({ 401: '/login.html', 403: '/403.html' }),
);

const items = new DataProvider({ name: 'items', url: '/api/items' });
bindText('#status', items, (event) => event.status);
items.load();

Preview the recipes

In the plainkit repo, make preview serves the demo at http://localhost:8091/demo/. make check is the architecture gate. The kit license is MIT.