For when plain HTML needs a little help.

Small by design. Surprisingly useful.

Crappie lets you share styles, load external templates, and reuse small components across plain HTML projects without a build step, bundler, or full frontend framework.

0
build steps required
1
shared base file
ways to make this harder
crappie.dev
status-card.html
external template
<template id="status-card">
  <section class="panel">
    <h2 data-prop="title"></h2>
    <p data-prop="body"></p>
  </section>
</template>
page.html
<cr-use template="status-card"
  title="Plain HTML lives"
  body="Shared files. Reusable markup.">
</cr-use>
Origin story

The answer to one accidental question.

Crappie started with a simple problem: a growing set of plain HTML design demos all needed the same Tailwind setup, theme tokens, utilities, and repeated markup.

Copy-paste worked for a while. Iframes solved part of it. A shared base file got closer. Eventually, the answer was obvious: let the page fetch the shared pieces it already needed.

Crappie is for the point where plain HTML is still the right tool, but repetition is getting in the way. It gives you simple files, reusable templates, and shared components without adding a build step, router, state management layer, or hydration debate.

The name is intentional. Crappie is a nod to the fish, and a small wink at the nature of the approach: simple, practical, and maybe a little unconventional.

It is a small, practical tool for keeping HTML projects organized while keeping them HTML.

How it works

Keep the HTML. Lose the repetition.

The API

Three simple tags.

No lifecycle methods to memorize. No build step to configure.

To prove it works: the nav and footer use <cr-include>. The How it works cards are <cr-use> templates with props and slotted icons. Same pattern across the docs and demos.

<cr-include>

The simplest behavior. Fetches an external HTML file and pastes it exactly where the tag is. You're looking at it right now.

<cr-include src="/nav.html"></cr-include>
<cr-use>

Clones a template and fills matching data-prop targets with attribute values.

<cr-use template="card" title="Hi"></cr-use>
data-slot

Pass inner content. Crappie moves the children of <cr-use> into the slot.

<div data-slot></div>
Probably not / sure why not

Be honest about the fish.

Crappie is not trying to be your enterprise frontend platform. It is the little tool you reach for when the alternative is copy-pasting the same card into fourteen HTML files.

Probably not

  • Mission-critical SaaS apps
  • Deep reactive state
  • Complex forms and data mutation
  • Anything needing a real app architecture

Sure, why not

  • Static prototypes and demos
  • Dashboards and design labs
  • Docs pages and internal tools
  • Side projects, mockups, and one-off tools
Tiny example

A framework-shaped shortcut.

Keep the component as a file. Use it from a page. Let Crappie do the boring fetch-and-clone dance.

Open live demo
crappie sketch
/components/note.html
<template id="note">
  <article class="card">
    <strong data-prop="title"></strong>
    <p data-prop="body"></p>
    <div data-slot></div>
  </article>
</template>
/pages/index.html
<cr-use template="note"
  title="Almost enough"
  body="Plain HTML just needed a nudge.">
  <a href="/docs">Read docs</a>
</cr-use>