Build web apps like the hipster you are

Paint Something New

Palette is a front-end web component and app library which works in tendem with browser APIs to bring modern development ergonomics closer to the platform.

import { define, html, css } from "@rusticarcade/palette";

const CountingButton = define("counting-button", {
  template: html`<button>${"$count"}</button>`,

  styles: css`
    :host {
      --border-color: black;
    }

    button {
      font-size: 1.5em;
      border: 3px solid var(--border-color);
    }
  `,

  initialState: { count: 0 },

  script() {
    this.listen("button", "click", () => {
      this.liveState.count += 1;
    });
  }
});

Template with HTML

Palette's templating language is standard HTML. Internally, Templates use standard <template> elements.

The expressive Notation system allows for binding data to attributes or utility directives like ::each and ::swap

// index.html
<!-- Using a normal <template> tag! -->
<template id="user-avatar">
  <img :src="@avatar" :title="@username" :alt="*description" />
</template>

<div id="root"></div>

// script.ts
const $root = document.getElementById("root");
const $raw = document.getElementById("user-avatar");
const template = new Template($raw);

template.render($root, {
  attr: {
  	avatar: "...",
    username: "...",
  },
  data: {
    description: "...",
  },
});

Handle State However

Powerful State management with deep reactivity, array mutation support, and even transactions with rollbacks.

const state = new State({ count: 0 });

state.addListener((current) => {
  console.log(current);
});

// Prints "1"
state.set("count", 1);

// Prints "2"
state.patch({
  count: 2,
});

// Prints "3"
state.live.count = 3;

// Prints "4"
state.mutate((data) => {
  return { count: data.count + 1 }
});

// Prints "5"
state.transaction((s) => {
  s.set("count", 5);
});

// Does nothing, rolls back error
state.transaction((s) => {
  s.set("count", 6);
  throw new Error("Oop!");
});

For the Browser, Not Against

There's a lot of frameworks out there, and a lot of them are very good. However, most try to replace the DOM and browser standards, which made a lot more sense not long ago when browser APIs were much less mature and far quirkier.

Palette was designed inside and out to feel like an extension of the browser rather than an opaque layer atop it. Palette maintains a strict contract with the DOM, giving you the peace of mind to access, modify, and manage elements as you need.

const Demo = define("x-example", {
  template: html`<button id="btn"></button>`,

  script() {
    const btn = this.requireElementById("btn");

    // You wanna manually set the text value?
    // Go for it!
    btn.textContent = "Click me!";
  }
});

Take Full Control

The define() helper is great for 90% of Components, but if you want total control over your E-Destiny, you can directly extend the Component class for more flexibility:

import { Component, html, css } from "@rusticarcade/palette";

class CountingButton extends Component {
  static tagName = "counting-button";
  static template = html`<button>${"$count"}</button>`;
  static styles = css`
    :host {
      --border-color: black;
    }

    button {
      font-size: 1.5em;
      border: 3px solid var(--border-color);
    }
  `;

  initialState = { count: 0 };

  script() {
    this.listen("button", "click", () => {
      this.liveState.count += 1;
    });
  }
};

Component.register(CountingButton);