• Stars
    star
    1,564
  • Rank 29,921 (Top 0.6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 7 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Absolutely minimal view layer for building web interfaces

Superfine

Superfine is a minimal view layer for building web interfaces. Think Hyperapp without the framework—no state machines, effects, or subscriptions—just the absolute bare minimum (1 kB minified+gzipped). Mix it with your favorite state management library or use it standalone for maximum flexibility.

Here's the first example to get you started. Try it here—no build step required!

<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module">
      import { h, text, patch } from "https://unpkg.com/superfine"

      const setState = (state) =>
        patch(
          document.getElementById("app"),
          h("main", {}, [
            h("h1", {}, text(state)),
            h("button", { onclick: () => setState(state - 1) }, text("-")),
            h("button", { onclick: () => setState(state + 1) }, text("+")),
          ])
        )

      setState(0)
    </script>
  </head>
  <body>
    <main id="app"></main>
  </body>
</html>

When describing how a page looks in Superfine, we don't write markup. Instead, we use the h() and text() functions to create a lightweight representation of the DOM (or virtual DOM for short), and patch() to actually render the DOM.

Superfine won't re-create the entire DOM every time we use patch(). By comparing the old and new virtual DOM we are able to change only the parts of the DOM that need to change instead of rendering everything from scratch.

Next up, let's take a look at a simple todo app. You can only add or cross off todos with it. Can you figure out what's happening just by poking around the code a bit? Have a go here.

<script type="module">
  import { h, text, patch } from "https://unpkg.com/superfine"

  const updateValue = (state, value) => ({ ...state, value })

  const addTodo = (state) => ({
    ...state,
    value: "",
    todos: state.todos.concat(state.value).filter(any => any),
  })

  const setState = (state) => {
    patch(
      document.getElementById("app"),
      h("main", {}, [
        h("h2", {}, text("To-do list")),
        h("ul", {},
          state.todos.map((todo) =>
            h("li", {}, [
              h("label", {}, [
                h("input", { type: "checkbox" }),
                h("span", {}, text(todo))
              ]),
            ])
          )
        ),
        h("section", {}, [
          h("input", {
            type: "text",
            value: state.value,
            oninput: ({ target }) =>
              setState(updateValue(state, target.value)),
          }),
          h("button",
            { onclick: () => setState(addTodo(state)) },
            text("Add todo")
          ),
        ]),
      ])
    )
  }

  setState({ todos: ["Learn Quantum Physics"], value: "" })
</script>

Check out more examples

Now it's your turn to take Superfine for a spin. Can you add a button to clear all todos? How about bulk-marking as done? If you get stuck or would like to ask a question, just file an issue and I'll try to help you out—have fun! ✨

Installation

npm install superfine

Top-Level API

h(type, props, [children])

Create them virtual DOM nodes! h() takes the node type; an object of HTML or SVG attributes, and an array of child nodes (or just one child node).

h("main", { class: "relative" }, [
  h("label", { for: "outatime" }, text("Destination time:")),
  h("input", { id: "outatime", type: "date", value: "2015-10-21" }),
])

text(string)

Create a virtual DOM text node.

h("h1", {}, text("1.21 Gigawatts!?!"))

patch(node, vdom)

Render a virtual DOM on the DOM efficiently. patch() takes an existing DOM node, a virtual DOM, and returns the freshly patched DOM.

const node = patch(
  document.getElementById("app"),
  h("main", {}, [
    // ...
  ])
)

Attributes API

Superfine nodes can use any of the HTML attributes, SVG attributes, DOM events, and also keys.

class:

To specify one or more CSS classes, use the class attribute. This applies to all regular DOM and SVG elements alike. The class attribute expects a string.

const mainView = h("main", { class: "relative flux" }, [
  // ...
])

style:

Use the style attribute to apply arbitrary CSS rules to your DOM nodes. The style attribute expects a string.

Important: We don't recommend using the style attribute as the primary means of styling elements. In most cases, class should be used to reference classes defined in an external CSS stylesheet.

const alertView = h("h1", { style: "color:red" }, text("Great Scott!"))

key:

Keys help identify nodes whenever we update the DOM. By setting the key property on a virtual DOM node, you declare that the node should correspond to a particular DOM element. This allows us to re-order the element into its new position, if the position changed, rather than risk destroying it.

Important: Keys must be unique among sibling nodes.

import { h } from "superfine"

export const imageGalleryView = (images) =>
  images.map(({ hash, url, description }) =>
    h("li", { key: hash }, [
      h("img", {
        src: url,
        alt: description,
      }),
    ])
  )

Recycling

Superfine will patch over server-side rendered HTML, recycling existing content instead of creating new elements. This technique enables better SEO, as search engine crawlers will see the fully rendered page more easily. And on slow internet or slow devices, users will enjoy faster time-to-content as HTML renders before your JavaScript is downloaded and executed.

<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module">
      import { h, text, patch } from "https://unpkg.com/superfine"

      const setState = (state) =>
        patch(
          document.getElementById("app"),
          h("main", {}, [
            h("h1", {}, text(state)),
            h("button", { onclick: () => setState(state - 1) }, text("-")),
            h("button", { onclick: () => setState(state + 1) }, text("+")),
          ])
        )

      setState(0)
    </script>
  </head>
  <body>
    <main id="app"><h1>0</h1><button>-</button><button>+</button></main>
  </body>
</html>

Notice that all the necessary HTML is already served with the document.

Superfine expects the markup to be identical between the server and the client. Treat mismatches as bugs and fix them! Now you just need a way to send content to browsers.

JSX

JSX is a language syntax extension that lets you write HTML tags interspersed with JavaScript. To compile JSX to JavaScript, install the JSX transform plugin, and create a .babelrc file in the root of your project like this:

{
  "plugins": [["transform-react-jsx", { "pragma": "h" }]]
}

Superfine doesn't support JSX out of the box, but adding it to your project is easy.

import { h, text } from "superfine"

const jsx = (type, props, ...children) =>
  typeof type === "function"
    ? type(props, children)
    : h(type, props || {}, children.flatMap((any) =>
        typeof any === "string" || typeof any === "number" ? text(any) : any
      )
    )

Import that everywhere you're using JSX and you'll be good to go. Here's a working example.

import jsx from "./jsx.js"
import { patch } from "superfine"

License

MIT

More Repositories

1

hyperapp

1kB-ish JavaScript framework for building hypertext applications
JavaScript
19,043
star
2

fisher

A plugin manager for Fish
Shell
7,560
star
3

awsm.fish

A curation of prompts, plugins & other Fish treasures 🐚💎
4,066
star
4

nvm.fish

The Node.js version manager you'll adore, crafted just for Fish
Shell
2,033
star
5

cookbook.fish

From Shell to Plate: Savor the Zest of Fish 🦞
2,002
star
6

colorette

🌈Easily set your terminal text color & styles
JavaScript
1,595
star
7

classcat

Build a class attribute string quickly
JavaScript
905
star
8

getopts

Node.js CLI options parser
JavaScript
633
star
9

hydro

Ultra-pure, lag-free prompt with async Git status—just for Fish
Shell
609
star
10

hyperawesome

A curated list of awesome projects built with Hyperapp + more
492
star
11

replay.fish

Run Bash commands, replay changes in Fish 🍤
Shell
392
star
12

twist

Declarative JavaScript Testing
JavaScript
378
star
13

autopair.fish

Auto-complete matching pairs in the Fish command line
Shell
369
star
14

fishtape

100% pure-Fish test runner
Shell
345
star
15

spark.fish

▁▂▄▆▇█▇▆▄▂▁
Shell
335
star
16

hyperapp-router

Declarative routing for Hyperapp V1 using the History API.
JavaScript
257
star
17

getopts.fish

Parse CLI options in Fish
Shell
220
star
18

gitio.fish

Create a custom git.io URL
Shell
88
star
19

hyperapp-html

Html helper functions for Hyperapp V1
JavaScript
81
star
20

pyenv

Pyenv support plugin for fish-shell
Shell
62
star
21

humantime.fish

Turn milliseconds into a human-readable string in Fish
Shell
21
star
22

.github

My health files
1
star
23

jorgebucaran.github.io

HTML
1
star