Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ So, without further ado, let's see how every framework weathers the storm! ⛈
<a href="https://framework-benchmarks.as93.net/lit/"><img width="48" src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/lit.png" /></a>
<a href="https://framework-benchmarks.as93.net/vanjs/"><img width="48" src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/vanjs.png" /></a>
<a href="https://framework-benchmarks.as93.net/vanilla/"><img width="48" src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/javascript.png" /></a>
<a href="https://framework-benchmarks.as93.net/lume-js/"><img width="48" src="https://raw.githubusercontent.com/sathvikc/lume-js/refs/heads/main/lume-logo.png" /></a>
<br><sub>Click a framework to view info, test/lint/build/etc statuses, and to preview the demo app</sub></p>
<!-- end_framework_list -->

Expand Down Expand Up @@ -194,6 +195,7 @@ and also view a stats on a per-framework basis.
| <a href="https://github.com/alpinejs/alpine"><img src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/alpine.png" alt="🏔️" width="16"></a> [**Alpine.js**](https://github.com/alpinejs/alpine) | 31.6k | 2.1M | 8.8 MB | 316 | 6.5y | 1 week ago | MIT |
| <a href="https://github.com/lit/lit"><img src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/lit.png" alt="🔥" width="16"></a> [**Lit**](https://github.com/lit/lit) | 21.6k | 23.3M | 60.8 MB | 210 | 8.9y | 4 days ago | BSD-3-Clause |
| <a href="https://github.com/vanjs-org/van"><img src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/vanjs.png" alt="🚐" width="16"></a> [**VanJS**](https://github.com/vanjs-org/van) | 4.4k | 7.1k | 3.8 MB | 24 | 3.0y | 3 months ago | MIT |
| <a href="https://github.com/sathvikc/lume-js"><img src="https://raw.githubusercontent.com/sathvikc/lume-js/refs/heads/main/lume-logo.png" alt="💡" width="16"></a> [**Lume.js**](https://github.com/sathvikc/lume-js) | 39 | 524 | 0.4 MB | 1 | 0.6y | today | MIT |
<!-- end_framework_stats -->

---
Expand Down Expand Up @@ -251,6 +253,7 @@ Each app gets built and tested to ensure that it is functional, compliant with t
| <a href="https://github.com/lissy93/framework-benchmarks/tree/main/apps/lit"><img src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/lit.png" width="16" /> Lit</a> | ![Lit Build Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/build-lit.svg) | ![Lit Test Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/test-lit.svg) | ![Lit Lint Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/lint-lit.svg) |
| <a href="https://github.com/lissy93/framework-benchmarks/tree/main/apps/vanjs"><img src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/vanjs.png" width="16" /> VanJS</a> | ![VanJS Build Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/build-vanjs.svg) | ![VanJS Test Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/test-vanjs.svg) | ![VanJS Lint Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/lint-vanjs.svg) |
| <a href="https://github.com/lissy93/framework-benchmarks/tree/main/apps/vanilla"><img src="https://storage.googleapis.com/as93-screenshots/frontend-benchmarks/framework-logos/javascript.png" width="16" /> Vanilla JavaScript</a> | ![Vanilla JavaScript Build Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/build-vanilla.svg) | ![Vanilla JavaScript Test Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/test-vanilla.svg) | ![Vanilla JavaScript Lint Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/lint-vanilla.svg) |
| <a href="https://github.com/lissy93/framework-benchmarks/tree/main/apps/lume-js"><img src="https://raw.githubusercontent.com/sathvikc/lume-js/refs/heads/main/lume-logo.png" width="16" /> Lume.js</a> | ![Lume.js Build Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/build-lume-js.svg) | ![Lume.js Test Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/test-lume-js.svg) | ![Lume.js Lint Status](https://raw.githubusercontent.com/lissy93/framework-benchmarks/badges/lint-lume-js.svg) |
<!-- end_all_status -->

---
Expand Down
66 changes: 66 additions & 0 deletions apps/lume-js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Weather App — Lume.js

A minimal reactive weather application built with [Lume.js](https://github.com/sathvikc/lume-js) (~5.58 KB gzipped, all-in-one).

## Usage

```bash
# Dev server (no build step required)
npm run dev # python3 -m http.server 3000

# Run tests
npm run test # Playwright E2E

# Lint
npm run lint
```

## Implementation

Lume.js is a modern take on Knockout.js — state is defined in JavaScript and the HTML is a plain view bound to it. The key difference from Alpine is that state lives in JS (`state()`), not inline in HTML attributes. Even the binding attribute is the same: `data-bind`. What makes it modern: flat Proxy-based store instead of `ko.observable()` wrappers, microtask batching, and ESM/CDN distribution with no build step required. The implementation uses:

- **`state()`** — flat reactive store holding all UI state
- **`bindDom()`** — binds `data-bind`, `data-show`, `data-disabled`, and `data-class` (via `stringAttr('class')`) to DOM elements
- **`repeat()`** — keyed list rendering for the 7-day forecast (from `lume-js/addons`)
- **`effect()`** — derives `showWeather` from `hasData`, `isLoading`, `hasError` via auto-tracked reactive effect
- **`show`** handler — built-in handler for `data-show` visibility toggling
- **`stringAttr('class')`** handler — sets full `className` from state via `data-class` attribute

This benchmark app uses the **CDN Global (IIFE)** build — a single `<script defer>` tag, no ES module syntax required.

```html
<script defer src="https://cdn.jsdelivr.net/npm/lume-js/dist/lume.global.js"></script>
<script>
const { state, bindDom, computed, repeat, show } = window.Lume;
</script>
```

## All Supported Import Patterns

**npm (tree-shakeable ESM):**
```js
import { state, bindDom, effect, isReactive } from 'lume-js';
import { computed, watch, repeat, debug } from 'lume-js/addons';
import { show, classToggle, boolAttr } from 'lume-js/handlers';
```

**CDN ESM (module script, tree-shakeable):**
```html
<script type="module">
import { state, bindDom } from 'https://cdn.jsdelivr.net/npm/lume-js/dist/index.min.mjs';
import { computed, repeat } from 'https://cdn.jsdelivr.net/npm/lume-js/dist/addons.min.mjs';
import { show } from 'https://cdn.jsdelivr.net/npm/lume-js/dist/handlers.min.mjs';
</script>
```

**CDN Global (IIFE, all-in-one, what this benchmark uses):**
```html
<script defer src="https://cdn.jsdelivr.net/npm/lume-js/dist/lume.global.js"></script>
<script>
const { state, bindDom, computed, repeat, show } = window.Lume;
</script>
```

## Real-world example

[Lume.js](https://sathvikc.github.io/lume-js) — library documentation site built with Lume.js itself.
23 changes: 23 additions & 0 deletions apps/lume-js/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import globals from 'globals';

export default [
{
files: ['js/**/*.js'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'script',
globals: {
...globals.browser,
WeatherUtils: 'readonly',
WeatherService: 'readonly'
}
},
rules: {
'no-unused-vars': ['warn', { varsIgnorePattern: '^[A-Z]' }],
'no-undef': 'error',
'eqeqeq': 'error',
'no-var': 'error',
'prefer-const': 'warn'
}
}
];
201 changes: 201 additions & 0 deletions apps/lume-js/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Real-time weather forecasts built with Lume.js — a lightweight reactive library with no build step.">
<meta property="og:title" content="Weather App - Lume.js">
<meta property="og:description" content="Real-time weather forecasts built with Lume.js — a lightweight reactive library with no build step.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://sathvikc.github.io/lume-js/">
<title>Weather App - Lume.js</title>
<link rel="icon" href="./public/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="public/styles/design-system.css">
<link rel="stylesheet" href="public/styles/variables.css">
<link rel="stylesheet" href="public/styles/base.css">
<link rel="stylesheet" href="public/styles/components.css">
<link rel="stylesheet" href="styles.css">
<script defer src="https://cdn.jsdelivr.net/npm/lume-js@2.2.1/dist/lume.global.js"></script>
<script defer src="./js/weather-utils.js"></script>
<script defer src="./js/weather-service.js"></script>
</head>
<body>
<header class="header">
<div class="container">
<h1 class="header__title">Weather Front</h1>
</div>
</header>

<main class="main">
<div class="container">
<section class="search-section">
<form
class="search-form"
data-testid="search-form"
id="search-form"
>
<div class="search-form__group">
<label for="location-input" class="sr-only">Enter city name</label>
<input
type="text"
id="location-input"
class="search-input"
placeholder="Enter city name..."
data-testid="search-input"
autocomplete="off"
data-bind="searchQuery"
>
<button
type="submit"
class="search-button"
data-testid="search-button"
data-disabled="isLoading"
>
<span class="search-button__text" data-bind="buttonText"></span>
<span class="search-button__icon">🌦️</span>
</button>
</div>
</form>
</section>

<div class="weather-container" data-testid="weather-container">
<!-- Loading State -->
<div
class="loading"
data-testid="loading"
data-show="isLoading"
hidden
>
<div class="loading__spinner"></div>
<p>Loading weather data...</p>
</div>

<!-- Error State -->
<div
class="error"
data-testid="error"
data-show="hasError"
hidden
>
<h2 class="error__title">Unable to load weather data</h2>
<p class="error__message" data-bind="errorMessage">Please check the city name and try again.</p>
</div>

<!-- Weather Content -->
<div
class="weather-content"
data-testid="weather-content"
data-show="showWeather"
hidden
>
<div class="weather-layout">
<!-- Current Weather -->
<section class="current-section">
<h2 class="section-title">Current Weather</h2>
<div class="weather-card" data-testid="current-weather">
<div class="current-weather">
<h3
class="current-weather__location"
data-testid="current-location"
data-bind="locationDisplay"
></h3>
<div class="current-weather__main">
<div
class="current-weather__icon"
data-testid="current-icon"
data-bind="icon"
>🌤️</div>
<div class="current-weather__temp-group">
<div
class="current-weather__temp"
data-testid="current-temperature"
data-bind="temperature"
></div>
<div
class="current-weather__condition"
data-testid="current-condition"
data-bind="condition"
data-class="conditionClass"
></div>
</div>
</div>

<div class="current-weather__details">
<div class="weather-detail">
<div class="weather-detail__label">Feels like</div>
<div
class="weather-detail__value"
data-testid="feels-like"
data-bind="feelsLike"
></div>
</div>
<div class="weather-detail">
<div class="weather-detail__label">Humidity</div>
<div
class="weather-detail__value"
data-testid="humidity"
data-bind="humidity"
></div>
</div>
<div class="weather-detail">
<div class="weather-detail__label">Wind Speed</div>
<div
class="weather-detail__value"
data-testid="wind-speed"
data-bind="windSpeed"
></div>
</div>
<div class="weather-detail">
<div class="weather-detail__label">Pressure</div>
<div
class="weather-detail__value"
data-testid="pressure"
data-bind="pressure"
></div>
</div>
<div class="weather-detail">
<div class="weather-detail__label">Cloud Cover</div>
<div
class="weather-detail__value"
data-testid="cloud-cover"
data-bind="cloudCover"
></div>
</div>
<div class="weather-detail">
<div class="weather-detail__label">Wind Direction</div>
<div
class="weather-detail__value"
data-testid="wind-direction"
data-bind="windDirection"
></div>
</div>
</div>
</div>
</div>
</section>

<!-- Forecast (populated by repeat() in weather-app.js) -->
<section class="forecast-section">
<h2 class="section-title">7-Day Forecast</h2>
<div class="forecast">
<div class="forecast__list" data-testid="forecast-list" id="forecast-list"></div>
</div>
</section>
</div>
</div>
</div>
</div>
</main>

<footer class="footer">
<div class="container">
<p class="footer__text">
Built with Lume.js • MIT License •
<a href="https://github.com/Lissy93" class="footer__link" target="_blank" rel="noopener">Alicia Sykes</a>
</p>
</div>
</footer>

<script defer src="js/app.js"></script>
</body>
</html>
Loading
Loading