WARNING!

Chocola 2 in development

2.0.0 is now in development, adding a lot of breaking changes like SFCs and CSR. It's recommended to wait for Chocola 2 or use available pre-release version in NPM.
* This website was built using chocola@2.0.0-next.7. To install the latest pre-release version of Chocola, use npm i chocola@next

Every { } bakes into HTML.

Chocola compiles enhanced HTML, CSS, and JS straight to the browser. Write logic as HTML, not template strings — no bundler, no virtual DOM, three dependencies total.

status-badge.html
<h1>{title}</h1>
<p>Status: {status}</p>
<div if="{status === 'live'}">
  <span>● Online</span>
</div>
<h1>Chocola</h1>
<p>
Status: <span>live</span>
</p>

<div>
  <span>● Online</span>
</div>
// title, status: baked once at build time
3
dependencies
jsdom, chalk and js-beautify. Nothing else.
0kb
shipped by default
no boilerplate to bloat your code
1
config file
all the config you need for Chocola
0
bundlers
no Webpack, no Vite; one config
Logic, baked into HTML

if's and logic — as elements, not templates

Chocola reads control flow the way it reads everything else: as HTML. No template language to learn, no curly-brace soup to escape — just attributes, and a tag that disappears when it's done its job.

if / elif / else
Chained across sibling elements, resolved like a real conditional.
        

                    <div if="{user.role
                        ===
                        'admin'}">Admin Panel</div>
                    
<div elif="{user.role === 'editor'}">Editor Dashboard</div>
<div else>View Only</div>
<void> wrapper
Renders only its children — the wrapper itself never reaches the DOM.
        

                    <void if={isLoaded}>
                    
  <h1>Data Loaded</h1>
  <p>Here is your content.</p>
</void>
Components

A recipe, and the thing it made from it

A component file is a blueprint, not an instance. Chocola clones and type-checks its state for every place the component is used, then hands you a single mount hook to wire up the rest.

my-button.js
                    import body from "./html/my-button.html";
                    
import styles from "./css/my-button.css";

// runs once, when this instance mounts
function RUNTIME(self, ctx) {
const btn = self.querySelector("btn");

btn.addEventListener("click", () => {
    ctx.cta = true;
  });
}

export default function MyButton() {
  return {
    body,
    styles,
    script: RUNTIME
  }
}
body

The enhanced HTML this component renders — logic blocks, static { }, and live ${ } included.

ctx

The state shape, declared once with types (Boolean, String…). Every instance gets its own typed, cloned copy.

self

The component's root element, passed into RUNTIME — query it directly, no refs system needed.

script

A mount hook, not a render loop. It runs once per instance; the template reacts to ctx on its own.

api

The public surface for this component — methods and properties any outside script can call on a live instance.

Roadmap

Four versions, shipped one at a time

Chocola grows the same way it compiles: deliberately, with nothing added until the layer underneath is solid.

V1
Available

Foundations

  • Single-page SSG
  • Basic logic blocks
  • Components
V2
In progress

Consolidation

  • CSR SPA router
  • Single file components
  • Declarative imports for components
  • ESM modules and dependencies
V3
Planned

Reactivity

  • Reactivity engine
  • Live ${foo} bindings
  • Reactive state and lifecycle hooks
  • Fine-tuning optimization options
V4
Planned

Shipping

  • SSR SPA
Chocola Logo
Get started

Install once. Compile forever.

No boilerplate. No plugins to reconcile. Point Chocola at a folder.

$ npm install chocola { Prompt copied! }