Skip to content

This project is in the development stage. npm version

Thanks for support and feedback! npm downloads

Knott JS


Web Component, Virtual CSS and Virtual DOM.

Introduction

Knott is a tiny 1.5 kilobytes (minified + gzipped) virtual DOM and virtual CSS library for building and styling fast, lightweight web components that everything runs on the memory, it's fast!

Netlify Status

Getting Started

bash
$ npm i knott --save-dev
1

Create an empty template to learn how to use Knott.js with CLI (command-line interface). Without installing anything to setup a new project. After the installation, follow the on-screen instructions and you are done setup.

bash
$ npx knott-cli@latest my-new-knott-project
1

Playground

The Playground is an online code editor, you can use it for testing or just playing around with Knott.js on the go. If you don't find the CLI is more convenient.

Launch Playground now!

Usage

  • Press F1 to open Palette menu to access advanced options.
  • Press Reset button to delete data stored in the browser.

image

Create Element

Use craft() to create virtual nodes commonly everywhere in the project. It takes a selector as a new element, props as attributes, text as string to put on the document, html to add custom non-virtual-node element, optional actions as an event listener, tasks as custom function calls, and expands the array of children elements in the same node with expand: [...]. Read more details below.

js
// app.js
import { craft } from "knott";
//import { craft } from "https://unpkg.com/knott@0.2.0/knott.js";

const newComponent = (props) =>
  craft(
    selector, {
      // attributes
      props {
        id: "",
        class: "",
        style: "",
        // any...
      },
      // options
      text: "TEXT",
      html: `<p></p>`,
      data: [ { a: "1" }, { a: "2" } ],
      keys: ["A","B"],
      actions: [
        [action, event, ()=>{ f() }],
      ],
      tasks: [ f() ],
      expand: [
        slotComponent,
        customFunction,
        craft(),
        // and so on...
      ],
    }
  );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
KeysParamsDescriptions
selectorStringcreate new element (e.g. <div>, <p>), or semantically elements (e.g. <profile-image>, <profile-avatar/>).
propsListcreate any attributes (e.g. alt="", src="") as properties to an element.
expandList [...]create child elements with craft() in a parent element (e.g. <div><p><a></a></p></div>) or includes other slotComponent created with craft() or customFunction calls when component is loaded.
textStringcreate string content to an element (e.g. <p>Text</p>).
htmlStringcreate first-render HTML element to an element, but text content will be disabled.
keysList [...]iterate objects in a JSON array append on an element from data objects.
dataObject [...]add an array JSON object to an element. Use keys to select which object to iterate.
actionsList [...]create event listener to an element to call function(s) when clicked or onload.
tasksList [...]add on-demand function(s) call when the component is loaded.
vdomBooleanset true to display virtual node objects in console.
slotComponentfunctionimport component file by using import {...} from "...";.
customFunctionfunctionimport custom function calls.

Create basic component with inline CSS properties.

js
// Example #1
craft("div", props: { style:"color:red" }, text: "Welcome!");
1
2
js
// Example #2
craft("top-header-container", html: `<div class="...">Welcome!</div>`);
1
2

Create a component with children element within a parent element with expand:.

js
craft("div", // parent
  text: "Parent",
  expand: [ // children
    craft("div", {
      text: "Children",
    }),
  ]
);
1
2
3
4
5
6
7
8

Virtual Node

An virtual node with element is rendered into actual document to display on browser is using render() and mounted with mount() as component taken one element ID.

Example 1: Recommended writting.

js
// Example #1
// app.js
import { craft, mount, render } from "knott";

const vNode = () =>
  craft("div", {
    props { style: "color:white" },
    text: "This is Text!",
  });

// render as...
// <div class="..." style="...">...</div>
mount("app", render(vNode()));
1
2
3
4
5
6
7
8
9
10
11
12
13

Example 2: Shorthand writing.

js
// Example #2
// app.js
import { craft, mount, render } from "knott";

// render as...
// <div class="..." style="...">...</div>
mount("app", render(
  craft("div", {
    props { class: "color:white" },
    text: "This is Text!",
  });
));
1
2
3
4
5
6
7
8
9
10
11
12

Example 3: The html: is treated as a single object acting as an injection for the parent element.

js
// Example #3
// app.js
import { craft, mount, render } from "knott";

// render as <div><p>Text</p></div>
mount("app", render(
  craft("div", {
    html: `<p>Text</p>`,
  });
));
1
2
3
4
5
6
7
8
9
10

Create Component

Create a new component named largeCard and export with the same example largeCard name.

js
// component-a.js
import { craft } from "knott";

const largeCard = () =>
   craft("div", {
    text: "Large Card",
  });

export { largeCard };
1
2
3
4
5
6
7
8
9

Import Component

Import component files as a module and reuse them anywhere in the project.

js
// app.js
import { craft, mount, render } from "knott";
import { largeCard } from "component-a"; // as module.

mount("root", render(
  craft("body", {
    props: { id: "root" },
    expand: [
      largeCard, // imported component as child element in <body id="root">
    ],
  })
));
1
2
3
4
5
6
7
8
9
10
11
12

Component Properties

Extend more options (or props) of a reusable component.

js
// component
const newComponent = (
  // props
  newId,
  newStyle,
  newContent
) =>
  // element
  craft("div", {
    props: { // attributes
      id: newId,
      class: newStyle,
    },
    // options
    text: newContent,
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Event Listener

KeysModesEventsCalls
actionsadd, remove, addWindow, removeWindowhttps://developer.mozilla.org/en-US/docs/Web/EventsFunction
js
// component.js
import { craft } from "knott";

const alertButton = () =>
  craft("div", {
    text: "Click Me!",
    actions: [
      ["add", "click", () => { ... }],
      ["addWindow", "click", () => { ... }],
      ["remove", "click", () => { f() }],
      ["removeWindow", "click", () => { ... }],
    ],
  });

export { alertButton };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Mount Component

FunctionsDescriptions
mount("id", render(vNode));deliver real (DOM) elements on the page visible on browser viewport. Note: Virtual node has to be mounted with a target element Id (e.g <div id="root"></div> or <body id="root"></body>).

An example to display additional component by using mount() when a text clicked with click handler actions:[...].

js
// component.js
import { craft, mount, render } from "knott";

const panelA = () =>
  craft("div", {
    props: { id: "idPanelA" },
    text: "This is Panel A. Click Me!",
    actions: [
      ["add", "click", () => { // or
        mount("idPanelA", render(panelB));
      }],
    ],
    tasks: [ // or
      mount("idPanelA", render(panelB))
    ],
  });

// to be added
const panelB = () =>
  craft("div", {
    text: "Panel B is appeared!",
  });

export { panelA };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

UnMount Component

FunctionsDescriptions
unmount("id");remove (DOM) element from the page invisible on browser viewport.

An example is to remove the component or element node from DOM with unmount().Extend more options of a reusable component.

js
// component.js
import { craft, mount, unmount, render } from "knott";

const panelA = () =>
   craft("div", {
    text: "Click Me to remove Panel B",
    actions: [ // or
      ["add", "click", () => {
        unmount("idPanelB");
      }],
    ],
    tasks: [ // or
      unmount("idPanelB");
    ],
    expand: [
      // to be removed
      craft(
        "div", {
          props: { id: "idPanelB", }, // <= target
          text: "Panel B!",
        }
      );
    ],
  });

export { panelA };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Data Binding

js
// Example #1
// component.js
const css = "font-weight:bold";
const text = "Welcome to Knott JS!";

const newCard = (css, text) =>
  craft("div", {
    props: { style: css },
    text: text,
  });
1
2
3
4
5
6
7
8
9
10
js
// Example #2
// component.js
const css = "font-weight:bold";
const text = "Knott JS!";

const newCard = (css, text) =>
  craft("div", {
    props: { style: `classA classB classC ${css}` },
    text: `Welcome to ${text}`,
  });
1
2
3
4
5
6
7
8
9
10
js
// Example #3
// component.js
const css = "font-weight:bold";
const text = "Knott JS!";

const newCard = (css, text) =>
  craft("div", {
    html: `
      <div style=${css}>
        ${text}
      </div>
    `,
  });
1
2
3
4
5
6
7
8
9
10
11
12
13

Iteration

KeysParamsDescriptions
actions[[mode, event, function]]create an event listener to an element to call function(s) when clicked or onload.
js
// component.js
const images = [
  { url: "https://example.com/example-one.png" },
  { url: "https://example.com/example-two.png" },
];

const logos = () =>
  craft("partner-logos", {
    actions: [
      ["addWindow", "load", () => {
        images.forEach((item) => {
          const l = document.createElement("div");
          l.innerHTML = `<img src="${item.url}" alt="" loading="lazy" />`;
          document.querySelector("partner-logos").appendChild(l);
        });
        //...
      }],
    ]
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Routing

Knott.js keeps the routing as simple as possible, just a basic alias in between the path (page url), page title (or page id), and template component. Use router() without refreshing pages, page loading almost instantly.

FunctionParamsDescriptions
route()pathurl (e.g. "/", "/about", "/food/breads")
templatepage id or page title
controllertemplate page function call (e.g. "/#/about")

Example #1: Basic how to use the router.

js
// Example #1
// app.js
import { route, router } from "knott";

const homePage = () => craft("div", { text: "Home Page" });

route("/", "Home", homePage);

router(); // init
1
2
3
4
5
6
7
8
9

Example #2: Write more elegantly, organize pages with objects.

js
// Example #2
// app.js
import { route, router } from "knott";

import { homePage } from "./home";
import { fruitsPage, breadsPage } from "./foods";

const pages = [ // example.com/#/fruits
  { path: "/", title: "Home", template: homePage },
  { path: "/fruits", title: "Fruits", template: fruitsPage },
  { path: "/breads", title: "Breads", template: breadsPage },
];

pages.forEach((alias) => {
  route(
    alias.path,
    alias.title,
    alias.template,
  );
});

// init
router();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Show / Hide Component

Use dedicated helper toggleById(), toggleBySelector(), toggleByIdSelector to create basic show/hide element with Event Listener.

functionsparamsdescription
toggleById()id, classlist[...]target effect element by id
toggleBySelector()selector, classlist[...]target effect element by selector
toggleByIdSelector()id, selector, classlist[...]target effect element by id and selector
js
// Example #1
// component.js
import { craft, design } from "knott";
import { toggleById } from "knott";

const newButton = () =>
  craft("button", {
    text: "Click Me!",
    actions: [
      ["add", "click", () => {
        toggleById("modal", [
          "display:block",
        ]);
      }]
    ],
  });

const newModal = () =>
  craft("div", {
    props: { id: "modal", class: "display:none" },
    text: "This is a Modal",
  });

design(true);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
js
// Example #2
// component.js
import { craft, design } from "knott";
import { toggleBySelector } from "knott";

const newButton = () =>
  craft("button", {
    text: "Click Me!",
    actions: [
      ["add", "click", () => {
        toggleById("new-modal", [
          "display:block",
        ]);
      }]
    ],
  });

const newModal = () =>
  craft("new-modal", {
    props: { id: "modal", class: "display:none" },
    text: "This is a Modal",
  });

design(true);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Hover Effect

Use dedicated helper toggleById(), toggleBySelector(), toggleByIdSelector to create basic hover effect with Event Listener.

functionsparamsdescription
toggleById()id, classlist[...]target effect element by id
toggleBySelector()selector, classlist[...]target effect element by selector
toggleByIdSelector()id, selector, classlist[...]target effect element by id and selector
js
// Example #1
// component.js
import { toggleById, design } from "knott";

const newButton = () =>
  craft("button", {
    props: { id: "thisButton" },
    text: "Hover Me!",
    actions: [
      ["add", "mouseover", () => {
        toggleById("thisButton", [
          "opacity:0.5"
        ]);
      }],
      ["add", "mouseout", () => {
        toggleById("thisButton", [
          "opacity:1"
        ]);
      }]
    ],
  });

design(true);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Styling

Introducing the Virtual CSS with Artis.

image

Note: style() has removed from Knott since v0.1.8 and refactored to a separated new project called the Artis, the Virtual CSS library. Artis.js syntax is very different from it's predecessor style().

Artis also has bundled into Knott as external module to use as internal module.

js
// Example #1
// app.js
import { design } from "knott";

window.onload = () => design(true); // init artis.js
1
2
3
4
5

To install Artis separately,

js
// Example #1
$ npm install artis@latest --save-dev
1
2
js
// Example #2
// app.js
import { design } from "artis";
//import { design } from "https://unpkg.com/artis@1.0.9/artis.js";

window.onload = () => design(true); // init artis.js
1
2
3
4
5
6

Please read the Artis Documentation. Or get your hand dirty and try out at Artis Playground for more virtual goodness.

Or, if you need a more robust design tool to create responsible user interface, introducing the low-level CSS framework, Weavv CSS.

image

To install Weavv CSS seperately,

js
// Example #1
$ npm install weavvcss@latest --save-dev
1
2
js
// Example #2
// app.js
import "weavvcss";
1
2
3

If you curious about project Weavv CSS, try at the Playground without setup anything.

Service Worker

Enable Persistent Caching service worker to store app assets in browser for offline access.

Import cache() module from knott and set parameter to true.

js
// Example #1
// app.js
import { cache } from "knott";

// init
cache(true); // should execute after the `mount()`
1
2
3
4
5
6

Create a new separate file named sw.js at the root of the project directory and, add below lines. Edit CacheName and CacheAssets to suit your need.

js
// {root_directory}/sw.js
const cacheName = "knott-app-cache-version";

const cacheAssets = [
  "/",
  "/index.html",
  "/assets/app.js",
  "/assets/app.css",
];

1
2
3
4
5
6
7
8
9
10

Thanks for reading.

License: MIT