Git Product home page Git Product logo

deepdoge / purified.js Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 0.0 1.34 MB

A lightweight JavaScript UI building library that prioritizes transparency and direct access to native browser features.

License: MIT License

TypeScript 67.26% HTML 1.54% JavaScript 24.02% CSS 7.17%
library reactive spa webcomponents frontend data-binding dom dom-manipulation lightweight reactive-ui vanilla-dom-manipulation pwa

purified.js's Introduction

purified.js

purified.js logo

Don't limit your potential

What is purified.js

purified.js is a 1.0kB (minified, gzipped) JavaScript UI building library that encourages the usage of pure JavaScript and DOM, while providing a thin layer of abstraction for the annoying parts for better DX (developer experience).


Size โšก

purified.js stands out with its minimal size:

Library .min.js .min.js.gz
purified.js 2.3kB 1.0kB
Preact 10.19.3 11.2kB 4.5kB
Solid 1.8.12 23kB 8.1kB
jQuery 3.7.1 85.1kB 29.7kB
Vue 3.4.15 110.4kB 40kB
ReactDOM 18.2.0 130.2kB 42kB
Angular 17.1.0 310kB 104kB

Compare Syntax


Installation ๐Ÿ™

To install purified.js, follow the installation instructions.

Key Features ๐Ÿš

  • purified.js uses signals.
  • purified.js provides built-in signals and utilities such as:
    • ref() state signal.
    • computed() computed signal.
    • awaited() converts a promise into a signal.
    • effect() follows and reacts to multiple signals.
  • purified.js allows direct DOM manipulation, because it can.
  • purified.js is small because it's pure and simple.
  • purified.js is simple because it's small and pure.

Example: purified.js + ShadowRoot ๐Ÿค

import { computed, css, fragment, ref, sheet, tags } from "purified-js"

const { div, button } = tags

function App() {
    return div({ id: "app" }).children(Counter())
}

function Counter() {
    const host = div()
    const shadow = host.element.attachShadow({ mode: "open" })
    shadow.adoptedStyleSheets.push(counterStyle)

    const count = ref(0)
    const double = computed(() => count.val * 2)

    shadow.append(
        fragment(
            button({ class: "my-button", "data-count": count })
                .onclick(() => count.val++)
                .children("Count:", count),
            ["Double:", double],
        ),
    )
    return host
}

const counterStyle = sheet(css`
    :host {
        display: grid;
        place-content: center;
    }
`)

document.adoptedStyleSheets.push(
    sheet(css`
        :root {
            color-scheme: dark;
        }
    `),
)

document.body.append(App().element)

Play on JSFiddle


Guide ๐Ÿฅก

Coming soon.

Documentation ๐Ÿฑ

Coming soon.


Motivation ๐Ÿฃ

JavaScript frameworks are often large and complex, force you into their specific ecosystems, restrict your use of native browser APIs, and prevent direct DOM manipulation. Additionally, their reliance on custom file extensions and build steps can complicate the use of regular JavaScript or TypeScript files, leading to type-related issues.

purified.js aims to enhance the developer experience while keeping you as close to pure JavaScript as possible. By keeping it pure, purified.js adds necessary functionality while avoiding the limitations and intricate bugs of modern JavaScript frameworks.

Personal

  • I'm tired of frameworks over-engineering and hiding reactivity from the developers. I'm tired of guessing what will re-render or update while writing the code. That's why I used signals.
  • I'm tired of frameworks sacrificing SPA related ergonomics, so they can also support SSR too.
  • I'm tired of writing code for a specific framework instead of just writing TS/JS.
  • I'm tired of being away from the DOM.
  • I'm tired of SSR and how complex it can be.
  • I'm tired of writing everything around SSR.

Current Limitations ๐Ÿฆ€

  • Lifecycle and Reactivity: Currently, I use Custom Elements to detect if an element is connected to the DOM. This means:

    • Every element created by the tags proxy, are Custom Elements. But they look like normal <div>(s) and <span>(s) and etc on the DevTools, because they extend the original element and use the original tag name. This way we can follow the life cycle of every element. And it works amazingly.

    • But we also have signals, which might not return an HTMLElement. So we gotta wrap signals with something in the DOM. So we can follow its lifecycle and know where it starts and ends. Traditionally this is done via Comment Node(s). But there is no feasible and sync way to follow a Comment Node on the DOM while also allowing direct DOM manipulation (DOM#533). So instead of Comment Node(s), I used Custom Elements to wrap signal renders. This way, I can follow the lifecycle of the signal render in the DOM, and decide to follow or unfollow the signal. Since signal render itself is an Element this approach has limitations, such as .parent > * selector wouldn't select all children if some are inside a signal.

      As another solution to this, a HTMLElement or Element attribute similar to inert that hides the element from the query selector both in JS and CSS would also be useful.

    But as long as the developer is aware of this limitation or difference, it shouldn't cause any issues.


Why Not JSX Templating? ๐Ÿ•

  • Lack of Type Safety: An <img> element created with JSX cannot have the HTMLImageElement type because all JSX elements must return the same type. This causes issues if you expect a HTMLImageElement some where in the code but all JSX returns is HTMLElement or something like JSX.Element. Also, it has some other issues related to the generics, discriminated unions and more.

  • Build Step Required: JSX necessitates a build step, adding complexity to the development workflow. In contrast, purified.js avoids this, enabling a simpler and more streamlined development process by working directly with native JavaScript and TypeScript.

  • Attributes vs. Properties: In purified.js, I can differentiate between attributes and properties of an element while building it, which is not currently possible with JSX. This distinction enhances clarity and control when defining element characteristics. Additionally, if I were to use JSX, I would prefer a syntax like this:

    <MyComponent("Hello", { World: "!" }) class="my-component" aria-busy="true" />

    This format clearly separates props and attributes, making it easier to understand and maintain.


purified.js's People

Contributors

deepdoge avatar

Stargazers

Dietrich Stein avatar  avatar

Watchers

 avatar  avatar Jayman Matthews avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.