Skip to content
PixelPerfect

HTML + CSS · Optional JS · v2.1.0

Tiny UI. Big adventure.

A four-color interface kit for indie makers building playful websites, tools, and game screens. No framework. No runtime dependencies. Just class names that feel like cheat codes.

MIT licensed 2.5 KB JS gzip Keyboard ready

Trailhead

Pack the release, link one stylesheet, and begin the Mossmere trail with optional JavaScript only when the path needs to move.

Two files. Zero setup.

Ship a screen before the kettle boils.

PixelPerfect is framework-neutral. Copy the release assets into your project, use semantic HTML, and sprinkle in the pp- classes you need.

Download v2.1.0 ↗
<link rel="stylesheet" href="pixelperfect.css">

<button class="pp-button">Start quest</button>

<script src="pixelperfect.iife.js"></script>
<script>PixelPerfect.init();</script>

Field Guide

Read Mossmere through one handheld-green palette, stepped spacing, sharp corners, and shadows you can count.

SCREEN#E6F4A8
MID#A9C46C
SHADOW#526B46
INK#16231D
4 PX

Everything starts on the pixel.

Spacing, borders, focus rings, and elevation all grow from --pp-pixel.

Aa

Pixelify Sans, hosted by you.

The variable font ships in the release with its SIL Open Font License.

Tab

Focus should be obvious.

Every interactive control inherits a chunky visible keyboard focus treatment.

:root {
  --pp-color-screen: #e6f4a8;
  --pp-color-mid: #a9c46c;
  --pp-color-shadow: #526b46;
  --pp-color-ink: #16231d;
  --pp-pixel: 4px;
}

Component finder

Find the right building block

Search all 34 components by name, category, or familiar UI term.

34 components

    Camp Command Post

    Issue trail orders through controls that feel clicked, pressed, disabled, and busy.

    Button

    Quest board · dusk watch

    Awaiting a command.

    <button class="pp-button" type="button">New game</button>
    <button class="pp-button pp-button--secondary" type="button">Continue</button>
    <button class="pp-button" type="button" disabled>Locked</button>

    Icon button

    Ranger's pocket tools

    No tool selected.

    <button class="pp-icon-button" type="button" aria-label="Favorite">
      <svg class="pp-icon" aria-hidden="true"><use href="icons.svg#heart"></use></svg>
    </button>

    Button group

    Moonwell · 100% map scale.

    <div class="pp-button-group" role="group" aria-label="Map zoom">
      <button class="pp-button pp-button--secondary" type="button">−</button>
      <button class="pp-button pp-button--secondary" type="button" aria-pressed="true">100%</button>
      <button class="pp-button pp-button--secondary" type="button">+</button>
    </div>

    Link

    Forked trail sign

    Choose a marked path.

    Adventurer Registry

    Record every traveler with native inputs underneath and cartridge-era tactility on top.

    Field

    2–20 characters. Swords optional.

    Registry card is ready.

    <div class="pp-field">
      <label class="pp-label" for="hero">Hero name</label>
      <input id="hero" class="pp-input" type="text" aria-describedby="hero-help">
      <p id="hero-help" class="pp-help">2–20 characters.</p>
    </div>

    Input

    That rune is not in this realm.

    Rune rejected. Try MOSS-21.

    <input class="pp-input" type="text" aria-invalid="true" aria-describedby="save-error">
    <p id="save-error" class="pp-error">That rune is not in this realm.</p>

    Textarea

    0 runes recorded.

    <label class="pp-label" for="notes">Quest notes</label>
    <textarea id="notes" class="pp-textarea" placeholder="The old bridge…"></textarea>

    Select

    Heroic hike selected.

    <select class="pp-select" aria-label="Difficulty">
      <option>Story stroll</option>
      <option selected>Heroic hike</option>
    </select>

    Checkbox

    1 registry option enabled.

    <label class="pp-check">
      <input type="checkbox" checked> Show map markers
    </label>

    Radio

    Choose a familiar

    Moth familiar is waiting.

    <label class="pp-radio">
      <input type="radio" name="familiar" checked> Moth
    </label>

    Switch

    Camp music on · shake off.

    <label class="pp-switch">
      <input type="checkbox" role="switch" checked>
      <span class="pp-switch__track" aria-hidden="true"></span>
      <span>Music</span>
    </label>

    Range

    Lantern hum set to 72%.

    <label class="pp-label" for="volume">Sound volume</label>
    <input id="volume" class="pp-range" type="range" min="0" max="100" value="72">

    File input

    PNG only. Keep it tiny.

    No sprite parcel attached.

    <label class="pp-label" for="sprite">Upload character sprite</label>
    <input id="sprite" class="pp-file-input" type="file" accept="image/png">

    Quest Log & Inventory

    Carry loot, lore, records, party members, and every useful moonspore found between them.

    Card

    Rare

    Moonspore blade

    Glows faintly around soup.

    Compact slot · 12 gems

    No item equipped.

    <article class="pp-card pp-stack">
      <span class="pp-badge">Rare</span>
      <h3>Moonspore blade</h3>
      <p>Glows faintly around soup.</p>
    </article>

    Panel / window

    Quest log
    The lantern thief

    Follow the glow past the old mill.

    Quest is unpinned.

    <section class="pp-panel">
      <header class="pp-panel__titlebar">Quest log</header>
      <div class="pp-panel__body">Follow the glow…</div>
    </section>

    List

    • Empty stone

    Deep cave save selected.

    <ul class="pp-list" aria-label="Save files">
      <li>Meadow · 02:14</li>
      <li aria-selected="true">Deep cave · 06:42</li>
    </ul>

    Table

    Party statistics
    HeroHPLV
    Moss8/812
    Moth3/39
    <div class="pp-table-wrap">
      <table class="pp-table">
        <thead><tr><th scope="col">Hero</th><th scope="col">HP</th></tr></thead>
        <tbody><tr><td>Moss</td><td>8/8</td></tr></tbody>
      </table>
    </div>

    Avatar

    MKMO
    Moss Knight
    Online
    <span class="pp-avatar" aria-label="Moss Knight">MK</span>
    <span class="pp-avatar pp-avatar--small" aria-label="Moth">MO</span>

    Badge

    NEW99+LV.12
    <span class="pp-badge">NEW</span>

    Tag

    Showing cozy quests.

    <span class="pp-tag">cozy</span>
    <span class="pp-tag">tiny dragons</span>

    Checkpoint Shrine

    Make every save, warning, wait, and recovery visible without breaking the spell.

    Alert

    Checkpoint reached.
    Your progress is safe here.

    Checkpoint glow ready.

    <div class="pp-alert" role="status">
      <div><strong>Checkpoint reached.</strong> Your progress is safe.</div>
      <button data-pp-dismiss aria-label="Dismiss alert">×</button>
    </div>

    Progress

    62%

    Moonwell route is 62% complete.

    <progress class="pp-progress" value="62" max="100">62%</progress>

    Meter

    4 of 10
    <meter class="pp-meter" min="0" max="10" low="3" high="7" optimum="10" value="4">4 of 10</meter>

    Spinner

    Entering moonwell…

    Arrival signal ready.

    <div role="status">
      <span class="pp-spinner" aria-hidden="true"></span>
      <span>Entering dungeon…</span>
    </div>

    Skeleton

    <div aria-busy="true" aria-label="Loading item details">
      <span class="pp-skeleton"></span>
      <span class="pp-skeleton"></span>
    </div>

    Tooltip

    <span class="pp-tooltip" data-pp-tooltip="Restores 3 hearts">
      <button class="pp-icon-button" aria-label="Health potion details">…</button>
    </span>

    Toast

    Generated through PixelPerfect.toast().

    No shrine messages replayed.

    const notice = PixelPerfect.toast('Quest saved to slot B.', {
      tone: 'success',
      duration: 4000
    });
    
    // Optional early cleanup
    notice.dismiss();

    Camp

    Rest, reorganize, and return through native dialogs and focus-friendly drawers.

    Dialog

    Moonlit campfire

    No rest taken.

    <button class="pp-button" data-pp-dialog-open="#rest-dialog">Rest at camp</button>
    <dialog id="rest-dialog" class="pp-dialog">
      <div class="pp-dialog__body">…</div>
      <button data-pp-dismiss>Keep walking</button>
    </dialog>

    Drawer

    Pack beside the tent

    Pack closed · Moonspore blade equipped.

    <button data-pp-drawer-open="#inventory">Open inventory</button>
    <aside id="inventory" class="pp-drawer" aria-hidden="true" aria-label="Inventory">
      <button data-pp-dismiss>Close</button>
    </aside>

    Journey's End

    Every small primitive now travels together through one complete Mossmere interface.

    ♥ ♥ ♥ ♥ 112 gems
    New path!
    The brambles moved.
    Rest at camp?

    The fire is warm. Resting restores every heart and advances the moon.