RPG dialogue
A JRPG dialogue box: a pal talks, the words type out, you pick an answer.
<piix-rpg-box>DOM + canvasOh! A visitor. Nobody comes down here anymore.
Want to see what I've been building?
Alright. I'll be here, typing.
Follow me! It's 124 pals deep.
<piix-rpg-box pal="kitty" name="Kit">
<p>Oh! A visitor. Nobody comes down here anymore.</p>
<p>Want to see what I've been <b>building</b>?</p>
<menu>
<button data-goto="tour">Show me!</button>
<button>Maybe later</button>
</menu>
<p data-end>Alright. I'll be here, typing.</p>
<p id="tour" data-clip="hop">Follow me! It's 124 pals deep.</p>
</piix-rpg-box>
Every child is one page of dialogue. The words type out letter by letter, keeping your markup (bold, links, line breaks), while the pal in the portrait talks, with a short beat after every full stop. Click, Enter or Space finishes the page or turns to the next. A <menu> of buttons becomes a choice with a blinking cursor, and data-goto="an id" jumps to that page, so a conversation can branch. It starts typing when it scrolls into view.
Installation
<!-- once, anywhere on the page: every component --> <script src="https://cdn.jsdelivr.net/npm/piixpal@0.6/piixpal.min.js"></script>
<!-- just rpg dialogue (the shared engine loads itself, once) --> <script src="https://cdn.jsdelivr.net/npm/piixpal@0.6/dist/c/kitty.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/piixpal@0.6/dist/c/rpg-box.min.js"></script>
npm install piixpal // once, in your entry file import "piixpal";
// npm install piixpal
import "piixpal";
export default function Page() {
return (
<piix-rpg-box pal="kitty" name="Kit">
<p>Oh! A visitor. Nobody comes down here anymore.</p>
<p>Want to see what I've been <b>building</b>?</p>
<menu>
<button data-goto="tour">Show me!</button>
<button>Maybe later</button>
</menu>
<p data-end>Alright. I'll be here, typing.</p>
<p id="tour" data-clip="hop">Follow me! It's 124 pals deep.</p>
</piix-rpg-box>
);
}
Usage
<piix-rpg-box pal="kitty" name="Kit">
<p>Oh! A visitor. Nobody comes down here anymore.</p>
<p>Want to see what I've been <b>building</b>?</p>
<menu>
<button data-goto="tour">Show me!</button>
<button>Maybe later</button>
</menu>
<p data-end>Alright. I'll be here, typing.</p>
<p id="tour" data-clip="hop">Follow me! It's 124 pals deep.</p>
</piix-rpg-box>
Examples
Two speakers
data-pal and data-name on a page change who says it.
I crawl on headings for a living.
And I nap on paragraphs.
Wait, someone's reading this?!
<piix-rpg-box theme="paper" pal="bitbug" name="Bitbug" loop auto="1800"> <p>I crawl on headings for a living.</p> <p data-name="Kit" data-pal="kitty">And I nap on paragraphs.</p> <p data-clip="alarm">Wait, someone's reading this?!</p> </piix-rpg-box>
Onboarding that branches
Listen for choice to act on the answer.
First time here?
Great! Start with Install in the sidebar.
Welcome back. New this week: games.
<piix-rpg-box id="guide" theme="dark" pal="frog" name="Guide">
<p>First time here?</p>
<menu>
<button value="new">Yes, show me around</button>
<button value="back" data-goto="welcome-back">No, I've been before</button>
</menu>
<p data-end>Great! Start with <b>Install</b> in the sidebar.</p>
<p id="welcome-back">Welcome back. New this week: <b>games</b>.</p>
</piix-rpg-box>
<script>
guide.addEventListener('choice', e => console.log(e.detail.value));
</script>
Say it from code
Click the button.
<piix-rpg-box id="npc" theme="lime" pal="kitty" name="Kit"><p>Click the button.</p></piix-rpg-box>
<piix-button tone="ink" size="sm" onclick="npc.say('Saved! Your <b>progress</b> is safe.', { clip: 'hop' })">Save</piix-button>
API reference
| Attribute | What it does | Default |
|---|---|---|
pal | the pal in the portrait (any pal name, or none) | none |
name | the speaker's name, shown on the box | – |
theme | classic dark paper lime | classic |
speed | letters per second | 40 |
voice | how high the typing blips are, 0.5 to 2 | 1 |
lines | the text area is at least this many lines tall | 3 |
auto | turn the page by itself after this many ms | – |
loop | start again after the last page | – |
font | pixel or inherit (your page font) | pixel |
sound | make sounds even without <piix-chiptune>; off keeps it silent | – |
data-pal, data-name | on a page: who says this one | – |
data-clip | on a page: the portrait's mood, any of that pal's clips | – |
data-speed | on a page: letters per second for it | – |
data-end | on a page: the conversation ends here | – |
data-goto | on a choice: the id of the page to jump to | – |
Events
| Event | When |
|---|---|
page | a page starts: detail.index, detail.id |
choice | an answer is picked: detail.value, detail.label, detail.index |
done | the conversation ends |
Methods
| Method | What it does |
|---|---|
next() | finish the page, or turn to the next |
restart() | from the first page |
say(html, { pal, name, clip }) | say something new, right now |
Accessibility
- Screen readers get each whole page at once through a live region, not letter by letter.
- Choices are real buttons: arrow keys move, Enter picks, focus stays inside the box.
- With reduced motion the words appear whole and the portrait stays still.