In this guide, we'll make a Palette Component from scratch using two
different approaches. First, we'll use the define() helper function. Then,
we'll look at how to make a class-defined Component.
A Component is just a JavaScript class that extends the HTMLElement class. That is the same base class for all standard HTML tags in JavaScript.
We'll define our component with the define() helper. We'll call it
<counting-button>,
import { define, html } from "@rusticarcade/palette";
define("counting-button", {
template: html`
<button>
I've been clicked 0 times
</button>
`
});
What've we got here? Not a lot:
define and html helpersdefine() to define a Palette component with the tag name counting-buttonhtml helper to create an inline HTML template for our componentIf you load that script in an HTML file with
<counting-button></counting-button> in the markup, your component will render
right there! It should be a blank, boring, awful, terrible button.
Before we go any further, our button needs to look fabulous. We'll add some css
using the css helper.
import { define, html, css } from "@rusticarcade/palette";
const CountingButton = define("counting-button", {
template: html`
<button>
I've been clicked 0 times
</button>
`
styles: css`
button {
font-size: 2em;
border: 3px dotted black;
background-color: #ff33cc;
}
`,
});
Perfect.
Now that our button is brilliant, we can take a look at what we've got.
The css helper wraps a string of CSS into a CSSStyleSheet instance which the
Component in turn uses for the template.
Internally, the Component creates a ShadowRoot which it renders the template
into. The stylesheet is adopted to the shadow root, so it only affects the
contents of your Component.
Put simply: Styles for Palette components are scoped to the component
Now, about that counting button...
Our button doesn't do anything because it is terrible but that is okay because we are going to make it wonderful by adding some stateful data.
import { define, html, css } from "@rusticarcade/palette";
const CountingButton = define("counting-button", {
template: html`
<button>
I've been clicked ${"$count"} times
</button>
`,
styles: css`
button {
font-size: 2em;
border: 3px dotted black;
background-color: #ff33cc;
}
`,
initialState: { count: 0 },
});
A few changes here. Be sure to notice the change to the template!
We're now using one of the features of the html helper: interpolating strings
as placeholder notations. We'll dive further into templating later, but for now,
all that matters is that "$count" string will be replaced with count from
the state data we just added.
The component will render the template, pull in the count from initialState,
and BOOM! The button is exactly as it was and still doesn't do anything.
But that's okay, we're so close! We just gotta make the number go up. Love that.
The last piece of the puzzle is to add a handler when clicking on the button.
Palette Components separate Events from Templates. You can define managed event
listeners in your Component's script() hook:
import { define, html, css } from "@rusticarcade/palette";
const CountingButton = define("counting-button", {
template: html`
<button>
I've been clicked ${"$count"} times
</button>
`,
styles: css`
button {
font-size: 2em;
border: 3px dotted black;
background-color: #ff33cc;
}
`,
initialState: { count: 0 },
script() {
this.listen("button", "click", () => {
this.liveState.count += 1;
});
}
});
Bam. That button's destiny has been fulfilled, and you've created your first Palette Component.
You can entirely forego the define() helper if you would prefer more verbose
but finer-grained control over your component definition.
Instead, just extend the Component class:
import { Component, html, css } from "@rusticarcade/palette";
class CountingButton extends Component {
static tagName = "counting-button";
static template = html`
<button>
I've been clicked ${"$count"} times
</button>
`;
styles = css`
button {
font-size: 2em;
border: 3px dotted black;
background-color: #ff33cc;
}
`;
initialState = { count: 0 };
script() {
this.listen("button", "click", () => {
this.liveState.count += 1;
});
}
};
// You can also just use customElements.define()
Component.register(CountingButton);