Skip to content

Commit 8e44d7f

Browse files
authored
Merge pull request #17 from sathvikc/main
feat: add Lume.js framework implementation
2 parents a5a4613 + 3423e47 commit 8e44d7f

16 files changed

Lines changed: 1921 additions & 0 deletions

.github/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ So, without further ado, let's see how every framework weathers the storm! ⛈
5454
<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>
5555
<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>
5656
<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>
57+
<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>
5758
<br><sub>Click a framework to view info, test/lint/build/etc statuses, and to preview the demo app</sub></p>
5859
<!-- end_framework_list -->
5960

@@ -194,6 +195,7 @@ and also view a stats on a per-framework basis.
194195
| <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 |
195196
| <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 |
196197
| <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 |
198+
| <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 |
197199
<!-- end_framework_stats -->
198200

199201
---
@@ -251,6 +253,7 @@ Each app gets built and tested to ensure that it is functional, compliant with t
251253
| <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) |
252254
| <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) |
253255
| <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) |
256+
| <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) |
254257
<!-- end_all_status -->
255258

256259
---

apps/lume-js/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Weather App — Lume.js
2+
3+
A minimal reactive weather application built with [Lume.js](https://github.com/sathvikc/lume-js) (~5.58 KB gzipped, all-in-one).
4+
5+
## Usage
6+
7+
```bash
8+
# Dev server (no build step required)
9+
npm run dev # python3 -m http.server 3000
10+
11+
# Run tests
12+
npm run test # Playwright E2E
13+
14+
# Lint
15+
npm run lint
16+
```
17+
18+
## Implementation
19+
20+
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:
21+
22+
- **`state()`** — flat reactive store holding all UI state
23+
- **`bindDom()`** — binds `data-bind`, `data-show`, `data-disabled`, and `data-class` (via `stringAttr('class')`) to DOM elements
24+
- **`repeat()`** — keyed list rendering for the 7-day forecast (from `lume-js/addons`)
25+
- **`effect()`** — derives `showWeather` from `hasData`, `isLoading`, `hasError` via auto-tracked reactive effect
26+
- **`show`** handler — built-in handler for `data-show` visibility toggling
27+
- **`stringAttr('class')`** handler — sets full `className` from state via `data-class` attribute
28+
29+
This benchmark app uses the **CDN Global (IIFE)** build — a single `<script defer>` tag, no ES module syntax required.
30+
31+
```html
32+
<script defer src="https://cdn.jsdelivr.net/npm/lume-js/dist/lume.global.js"></script>
33+
<script>
34+
const { state, bindDom, computed, repeat, show } = window.Lume;
35+
</script>
36+
```
37+
38+
## All Supported Import Patterns
39+
40+
**npm (tree-shakeable ESM):**
41+
```js
42+
import { state, bindDom, effect, isReactive } from 'lume-js';
43+
import { computed, watch, repeat, debug } from 'lume-js/addons';
44+
import { show, classToggle, boolAttr } from 'lume-js/handlers';
45+
```
46+
47+
**CDN ESM (module script, tree-shakeable):**
48+
```html
49+
<script type="module">
50+
import { state, bindDom } from 'https://cdn.jsdelivr.net/npm/lume-js/dist/index.min.mjs';
51+
import { computed, repeat } from 'https://cdn.jsdelivr.net/npm/lume-js/dist/addons.min.mjs';
52+
import { show } from 'https://cdn.jsdelivr.net/npm/lume-js/dist/handlers.min.mjs';
53+
</script>
54+
```
55+
56+
**CDN Global (IIFE, all-in-one, what this benchmark uses):**
57+
```html
58+
<script defer src="https://cdn.jsdelivr.net/npm/lume-js/dist/lume.global.js"></script>
59+
<script>
60+
const { state, bindDom, computed, repeat, show } = window.Lume;
61+
</script>
62+
```
63+
64+
## Real-world example
65+
66+
[Lume.js](https://sathvikc.github.io/lume-js) — library documentation site built with Lume.js itself.

apps/lume-js/eslint.config.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import globals from 'globals';
2+
3+
export default [
4+
{
5+
files: ['js/**/*.js'],
6+
languageOptions: {
7+
ecmaVersion: 2022,
8+
sourceType: 'script',
9+
globals: {
10+
...globals.browser,
11+
WeatherUtils: 'readonly',
12+
WeatherService: 'readonly'
13+
}
14+
},
15+
rules: {
16+
'no-unused-vars': ['warn', { varsIgnorePattern: '^[A-Z]' }],
17+
'no-undef': 'error',
18+
'eqeqeq': 'error',
19+
'no-var': 'error',
20+
'prefer-const': 'warn'
21+
}
22+
}
23+
];

apps/lume-js/index.html

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta name="description" content="Real-time weather forecasts built with Lume.js — a lightweight reactive library with no build step.">
7+
<meta property="og:title" content="Weather App - Lume.js">
8+
<meta property="og:description" content="Real-time weather forecasts built with Lume.js — a lightweight reactive library with no build step.">
9+
<meta property="og:type" content="website">
10+
<meta property="og:url" content="https://sathvikc.github.io/lume-js/">
11+
<title>Weather App - Lume.js</title>
12+
<link rel="icon" href="./public/favicon.ico" type="image/x-icon">
13+
<link rel="stylesheet" href="public/styles/design-system.css">
14+
<link rel="stylesheet" href="public/styles/variables.css">
15+
<link rel="stylesheet" href="public/styles/base.css">
16+
<link rel="stylesheet" href="public/styles/components.css">
17+
<link rel="stylesheet" href="styles.css">
18+
<script defer src="https://cdn.jsdelivr.net/npm/lume-js@2.2.1/dist/lume.global.js"></script>
19+
<script defer src="./js/weather-utils.js"></script>
20+
<script defer src="./js/weather-service.js"></script>
21+
</head>
22+
<body>
23+
<header class="header">
24+
<div class="container">
25+
<h1 class="header__title">Weather Front</h1>
26+
</div>
27+
</header>
28+
29+
<main class="main">
30+
<div class="container">
31+
<section class="search-section">
32+
<form
33+
class="search-form"
34+
data-testid="search-form"
35+
id="search-form"
36+
>
37+
<div class="search-form__group">
38+
<label for="location-input" class="sr-only">Enter city name</label>
39+
<input
40+
type="text"
41+
id="location-input"
42+
class="search-input"
43+
placeholder="Enter city name..."
44+
data-testid="search-input"
45+
autocomplete="off"
46+
data-bind="searchQuery"
47+
>
48+
<button
49+
type="submit"
50+
class="search-button"
51+
data-testid="search-button"
52+
data-disabled="isLoading"
53+
>
54+
<span class="search-button__text" data-bind="buttonText"></span>
55+
<span class="search-button__icon">🌦️</span>
56+
</button>
57+
</div>
58+
</form>
59+
</section>
60+
61+
<div class="weather-container" data-testid="weather-container">
62+
<!-- Loading State -->
63+
<div
64+
class="loading"
65+
data-testid="loading"
66+
data-show="isLoading"
67+
hidden
68+
>
69+
<div class="loading__spinner"></div>
70+
<p>Loading weather data...</p>
71+
</div>
72+
73+
<!-- Error State -->
74+
<div
75+
class="error"
76+
data-testid="error"
77+
data-show="hasError"
78+
hidden
79+
>
80+
<h2 class="error__title">Unable to load weather data</h2>
81+
<p class="error__message" data-bind="errorMessage">Please check the city name and try again.</p>
82+
</div>
83+
84+
<!-- Weather Content -->
85+
<div
86+
class="weather-content"
87+
data-testid="weather-content"
88+
data-show="showWeather"
89+
hidden
90+
>
91+
<div class="weather-layout">
92+
<!-- Current Weather -->
93+
<section class="current-section">
94+
<h2 class="section-title">Current Weather</h2>
95+
<div class="weather-card" data-testid="current-weather">
96+
<div class="current-weather">
97+
<h3
98+
class="current-weather__location"
99+
data-testid="current-location"
100+
data-bind="locationDisplay"
101+
></h3>
102+
<div class="current-weather__main">
103+
<div
104+
class="current-weather__icon"
105+
data-testid="current-icon"
106+
data-bind="icon"
107+
>🌤️</div>
108+
<div class="current-weather__temp-group">
109+
<div
110+
class="current-weather__temp"
111+
data-testid="current-temperature"
112+
data-bind="temperature"
113+
></div>
114+
<div
115+
class="current-weather__condition"
116+
data-testid="current-condition"
117+
data-bind="condition"
118+
data-class="conditionClass"
119+
></div>
120+
</div>
121+
</div>
122+
123+
<div class="current-weather__details">
124+
<div class="weather-detail">
125+
<div class="weather-detail__label">Feels like</div>
126+
<div
127+
class="weather-detail__value"
128+
data-testid="feels-like"
129+
data-bind="feelsLike"
130+
></div>
131+
</div>
132+
<div class="weather-detail">
133+
<div class="weather-detail__label">Humidity</div>
134+
<div
135+
class="weather-detail__value"
136+
data-testid="humidity"
137+
data-bind="humidity"
138+
></div>
139+
</div>
140+
<div class="weather-detail">
141+
<div class="weather-detail__label">Wind Speed</div>
142+
<div
143+
class="weather-detail__value"
144+
data-testid="wind-speed"
145+
data-bind="windSpeed"
146+
></div>
147+
</div>
148+
<div class="weather-detail">
149+
<div class="weather-detail__label">Pressure</div>
150+
<div
151+
class="weather-detail__value"
152+
data-testid="pressure"
153+
data-bind="pressure"
154+
></div>
155+
</div>
156+
<div class="weather-detail">
157+
<div class="weather-detail__label">Cloud Cover</div>
158+
<div
159+
class="weather-detail__value"
160+
data-testid="cloud-cover"
161+
data-bind="cloudCover"
162+
></div>
163+
</div>
164+
<div class="weather-detail">
165+
<div class="weather-detail__label">Wind Direction</div>
166+
<div
167+
class="weather-detail__value"
168+
data-testid="wind-direction"
169+
data-bind="windDirection"
170+
></div>
171+
</div>
172+
</div>
173+
</div>
174+
</div>
175+
</section>
176+
177+
<!-- Forecast (populated by repeat() in weather-app.js) -->
178+
<section class="forecast-section">
179+
<h2 class="section-title">7-Day Forecast</h2>
180+
<div class="forecast">
181+
<div class="forecast__list" data-testid="forecast-list" id="forecast-list"></div>
182+
</div>
183+
</section>
184+
</div>
185+
</div>
186+
</div>
187+
</div>
188+
</main>
189+
190+
<footer class="footer">
191+
<div class="container">
192+
<p class="footer__text">
193+
Built with Lume.js • MIT License •
194+
<a href="https://github.com/Lissy93" class="footer__link" target="_blank" rel="noopener">Alicia Sykes</a>
195+
</p>
196+
</div>
197+
</footer>
198+
199+
<script defer src="js/app.js"></script>
200+
</body>
201+
</html>

0 commit comments

Comments
 (0)