npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@sidiousvic/phantom

v2.1.5

Published

A state—reactive DOM rendering engine for building UIs. 👻

Downloads

172

Readme

Phantom

npm version install size

A state—reactive DOM rendering engine for building UIs. 👻

npm i @sidiousvic/phantom

Phantom lets you build state—reactive UIs using raw HTML strings ejected from functions.

export default function Pizza(slices) {
  return `
    <div id="pizza-box">
      <h1 data-phantom="${slices}" id="slices-h1">${slices}</h1>
    </div>
  `;
}

You update state via actions, and Phantom swaps DOM nodes for you.

phantomStore.fire({ type: "EAT_PIZZA" });

🚀 Get launched

🍕 Data management

FAQ

🔧 Developers

👻 Examples

💥 Get launched

1. Write an entry Phantom component

In Phantom, components are functions that return HTML template literals.

Template strings allow you to inject dynamic data (including other components) via template literal placeholders ${}.

export default function phantomComponent() {
  const slices
  return `
    <div id="pizza-box">
      <h1 id="slices-h1">🍕</h1>
    </div>
  `;
}

| 👻   We recommend the leet-html VSCode extension for HTML string highlighting. | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

2. Create a Phantom store

Use createPhantomStore to produce your store.

import { createPhantomStore } from "@sidiousvic/phantom";

const data = { slices: ["🍕", "🍕", "🍕"] };

function reducer(state = data, action) {
  switch (action.type) {
    case "EAT_SLICE": // remove a slice from array
      return { ...state, slices: state.slices.slice(0, -1) };
    default:
      return state;
  }
}

const phantomStore = createPhantomStore(reducer);

export default phantomStore;

3. Initialize Phantom and appear()

Start the Phantom engine by feeding it a component and a store.

import phantom from "@sidiousvic/phantom";
import phantomComponent from "./phantomComponent.js";
import phantomStore from "./phantomStore.js";

const { appear } = phantom(phantomComponent, phantomStore);

appear(); // 3, 2, 1... 💥 initial render!

Phantom will expose the appear method. 💥

appear will perform the initial DOM render on call, your UI's first apparition. 👻

🍕 Data management

Components are cool and all, but what if we want to use dynamic data?

What if we want our UI to react to data changes?

Phantom integrates with a Redux—like store to subscribe the DOM to state updates.

1. Initialize Phantom.

import phantom from "@sidiousvic/phantom";
import phantomComponent from "./phantomComponent.js";
import phantomStore from "./phantomStore.js";

const { appear, data, fire } = phantom(phantomComponent, phantomStore);

appear(); // 3, 2, 1... 🚀 initial render!

data and fire are pointers to the Phantom store.

2. Use data to read state from the Phantom store.

data returns the current in—store data.

const { slices } = data();

Data can be passed as arguments to child components.

function phantomComponent() {
  const { slices } = data();
  return `
    ${Pizza(slices)}
  `;
}

You can use template literal placeholders to inject pieces of state into a component.

export default function Pizza(slices) {
  return `
    <div id="pizza-box">
      <h1 data-phantom="${slices}" id="slices-h1">${slices}</h1>
    </div>
  `;
}

3. Use fire to dispatch an action and trigger a state update and rerender.

An action is an object with a type key and optional data payload.

fire takes an action and dispatches it to the phantomStore, triggering a state change.

Phantom will update the DOM on every fire(action).

document.addEventListener("click", eatPizza);

function eatPizza(e) {
  if (e.target.id === "slices-h1") {
    fire({ type: "EAT_PIZZA" }); // DOM will update
  }
}

4. Subscribe a component to data changes

Phantom can perform DOM differentiation and swap only the nodes whose state has updated. To activate this behavior,

return `<element data-phantom="${yourData}">${yourData}</element>`;
  • [x] Include a data-phantom attribute with the piece(s) of state that you want to subscribe to.
  • [x] An id attribute.

Phantom will look at at both the data-phantom and id attributes in order to compute if a DOM node has to be repainted.

| ⚠️   If you don't do this, Phantom will repaint the entire DOM on data updates. | | :----------------------------------------------------------------------------------- |

FAQ

Why use Phantom ?

A baby panda dies every time you choose a 1MB+* industrial—level frontend framework to code a pomodoro clock or a personal portfolio page. 🐼

You don't drive to the corner store, but walking is overrated. Phantom is the bike you need.

🖍 Declarative

With Phantom, you can write markup in a declarative way ala JSX using raw HTML strings, and inject dynamic data using template literals—staying fully JS native.

No JSX, no complex API, no syntactic hyperglycemia.

🍕 Component—based

Phantom lets you divide your UI into components, abstracting markup into composable functions.

🧪 Reactive

The Phantom engine integrates with a store and subscribes to state updates. It swaps nodes when their data changes.

👩🏾‍🏭 Closer to the DOM metal

Frameworks often abstract too much architecture and functionality out of the DOM. They make you yield too much to their way of doing things—events, effects, styling, routing—you have to find the solutions withing their ecosystem.

Phantom only helps with DOM rendering. It's convenient, but close enough to the DOM that you can integrate it with other solutions without using fibers, combiners or adapters of any kind.

* unpacked size of ReactDOM is 3MB. Vue is 2.98MB. Phantom is < 99 kB.

Does Phantom use a virtual DOM?

When a component's data changes, Phantom will re—render that node in the DOM by diffing an internal PhantomDOM, a map representation of the DOM.

Is Phantom XSS secure?

Yes. Phantom uses the internal PhantomExorciser to sanitize HTML strings before injecting them into the DOM.

🔧 Developers

Phantom is written and built using Typescript.

⌨️ Scripts

  • [x] npm run build
    generates a static build in dist/ .

  • [x] npm run test
    runs the tests located in spec.

  • [x] npm run example/<example-name>
    runs an example app from examples/

If you find a 🐞, please file an issue.

👩🏽‍🔧 Contributing

We are always psyched to welcome contributors to Phantom.

Feel free to raise issues, ask questions or fork the project and play with it.

If you want to submit a contribution, please read our

👷🏽‍♂️ Contribution Guidelines

Phantom is maintained regularly by @sidiousvic and @nayelyrodarte.

👻 Examples

There are several examples you can run, each furnished with their own devServer configuration.

Phantom in CodeSandbox

Click on one of the images above to be taken to an online sandbox.

Devs who have cloned Phantom may use npm run example/[example name] and navigate to the url that appears in their terminal.

Phantom is built with love and pepperoni by @sidiousvic

ko-fi