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-eval is required.

Comparison with full Vue

FeatureLuneStandard Vue
Target Use CaseProgressive EnhancementSingle Page Application (SPA)
Runtime Size~44kb minified, ~15kb gzipped~120kb minified, ~45kb gzipped
Virtual DOMNo (uses real DOM)Yes
Build RequiredNoRecommended
Scoped CSSNoYes (SFC)
SSR SupportNo (it is the client-side enhancement)Yes

Basic Example

html
<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.

html
<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

  1. Load the script: The <script> tag loads Lune from a CDN. The defer attribute ensures it runs after the HTML is parsed. The init attribute tells it to automatically find and mount components.

  2. Define Scope: lu-scope="{ count: 0 }" marks the <div> as a component and initializes its state with count set to 0. This state is reactive — if it changes, the UI updates automatically.

  3. Bind Events: @click="count--" and @click="count++" are event listeners that modify the state.

  4. Display Data: {{ count }} displays the current value of count.