Introduction
Lune is a lightweight, progressive-first JavaScript framework that enables developers to add client-side interactivity to server-rendered applications without the overhead of a full Single Page Application (SPA) framework.
What is Lune?
Lune is a fork of petite-vue, optimized for Progressive Enhancement. It provides the same template syntax and reactivity mental model as standard Vue, but is specifically optimized for "sprinkling" small amounts of interactions on existing HTML pages.
Key Benefits
- Lightweight: Only ~44kb minified, ~15kb gzipped.
- Progressive: Designed to layer on top of your existing HTML.
- No Build Step: Works directly in the browser with a
<script>tag. - Vue-Compatible: Uses familiar Vue syntax (
lu-if,lu-for,@click, etc.). - CSP-Friendly: Template expressions are compiled by Lune's own engine, so no
unsafe-evalis required.
Comparison with full Vue
| Feature | Lune | Standard Vue |
|---|---|---|
| Target Use Case | Progressive Enhancement | Single Page Application (SPA) |
| Runtime Size | ~44kb minified, ~15kb gzipped | ~120kb minified, ~45kb gzipped |
| Virtual DOM | No (uses real DOM) | Yes |
| Build Required | No | Recommended |
| Scoped CSS | No | Yes (SFC) |
| SSR Support | No (it is the client-side enhancement) | Yes |
Basic Example
<script src="https://cdn.jsdelivr.net/npm/lune-js" defer init></script>
<div lu-scope="{ count: 0 }">
<button @click="count--">-</button>
<span>{{ count }}</span>
<button @click="count++">+</button>
</div>Interactive Example: Toggle
Here is a common pattern: toggling visibility of an element.
<div lu-scope="{ open: false }">
<button @click="open = !open">{{ open ? 'Close' : 'Open' }} Content</button>
<div lu-show="open" class="content">
<p>This content is toggled!</p>
</div>
</div>How it works
Load the script: The
<script>tag loads Lune from a CDN. Thedeferattribute ensures it runs after the HTML is parsed. Theinitattribute tells it to automatically find and mount components.Define Scope:
lu-scope="{ count: 0 }"marks the<div>as a component and initializes its state withcountset to 0. This state is reactive — if it changes, the UI updates automatically.Bind Events:
@click="count--"and@click="count++"are event listeners that modify the state.Display Data:
{{ count }}displays the current value ofcount.