@c-technology/adaptive-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +332 -0
- package/dist/index.cjs +116 -0
- package/dist/index.d.cts +24 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# adaptive-ui
|
|
2
|
+
|
|
3
|
+
> **Human-aware UI adaptation for modern web apps**
|
|
4
|
+
|
|
5
|
+
adaptive-ui is a **TypeScript-first library** that helps your UI adapt to **users**, not just screen sizes.
|
|
6
|
+
|
|
7
|
+
Instead of only responding to `mobile`, `desktop`, or `tablet`, adaptive-ui reacts to **real conditions** like:
|
|
8
|
+
|
|
9
|
+
* slow network
|
|
10
|
+
* low battery
|
|
11
|
+
* user impatience
|
|
12
|
+
* first-time vs returning users
|
|
13
|
+
* accessibility stress
|
|
14
|
+
|
|
15
|
+
This README explains everything **from zero**, assuming you are a beginner.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🚨 The Problem (Why adaptive-ui Exists)
|
|
20
|
+
|
|
21
|
+
Most web apps today:
|
|
22
|
+
|
|
23
|
+
* look responsive
|
|
24
|
+
* but feel frustrating
|
|
25
|
+
|
|
26
|
+
Why?
|
|
27
|
+
Because they adapt to **screens**, not **people**.
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
|
|
31
|
+
* User has slow internet → app still loads heavy animations
|
|
32
|
+
* User is confused → UI gives no guidance
|
|
33
|
+
* User is impatient → UI hides important actions
|
|
34
|
+
* User has low battery → app keeps wasting power
|
|
35
|
+
|
|
36
|
+
Developers usually fix this by writing **random `if` statements everywhere**.
|
|
37
|
+
|
|
38
|
+
That approach:
|
|
39
|
+
|
|
40
|
+
* does not scale
|
|
41
|
+
* is hard to reason about
|
|
42
|
+
* creates messy code
|
|
43
|
+
|
|
44
|
+
**adaptive-ui solves this by acting as a decision layer for your UI.**
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 🧠 What adaptive-ui Is (And Is NOT)
|
|
49
|
+
|
|
50
|
+
### ✅ What it IS
|
|
51
|
+
|
|
52
|
+
* a **logic layer** for UI behavior
|
|
53
|
+
* a way to detect user context
|
|
54
|
+
* a strategy system for adapting interfaces
|
|
55
|
+
* framework-friendly (React, Next.js, etc.)
|
|
56
|
+
|
|
57
|
+
### ❌ What it is NOT
|
|
58
|
+
|
|
59
|
+
* ❌ a UI component library
|
|
60
|
+
* ❌ a design system
|
|
61
|
+
* ❌ an analytics tool
|
|
62
|
+
* ❌ an AI / ML system
|
|
63
|
+
* ❌ a tracking library
|
|
64
|
+
|
|
65
|
+
adaptive-ui **never renders UI**. It only helps you make better decisions.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🏗️ How adaptive-ui Works (Simple Explanation)
|
|
70
|
+
|
|
71
|
+
adaptive-ui works in **three steps**:
|
|
72
|
+
|
|
73
|
+
### 1️⃣ Detect Signals
|
|
74
|
+
|
|
75
|
+
Signals are things like:
|
|
76
|
+
|
|
77
|
+
* slow network
|
|
78
|
+
* low battery
|
|
79
|
+
* many rapid clicks (impatience)
|
|
80
|
+
|
|
81
|
+
### 2️⃣ Build Context
|
|
82
|
+
|
|
83
|
+
From signals, adaptive-ui builds a **context**, such as:
|
|
84
|
+
|
|
85
|
+
* `slow-network`
|
|
86
|
+
* `impatient`
|
|
87
|
+
* `new-user`
|
|
88
|
+
|
|
89
|
+
### 3️⃣ Run Strategies
|
|
90
|
+
|
|
91
|
+
Strategies say:
|
|
92
|
+
|
|
93
|
+
> "When these conditions are true → do this"
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
|
|
97
|
+
* If user is impatient → simplify the UI
|
|
98
|
+
* If network is slow → disable animations
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 📦 Installation
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm install adaptive-ui
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
or
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pnpm install adaptive-ui
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
or
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
bun add adaptive-ui
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
or
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
yarn add adaptive-ui
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## ⚛️ Basic Usage (React / Next.js)
|
|
129
|
+
|
|
130
|
+
### Step 1: Import the hook
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { useAdaptive } from "adaptive-ui";
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Step 2: Use it inside a component
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
export default function Dashboard() {
|
|
142
|
+
const ui = useAdaptive();
|
|
143
|
+
|
|
144
|
+
return <h1>Dashboard</h1>;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This gives you access to the **adaptive engine**.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 🧩 Context Detection (Beginner Friendly)
|
|
153
|
+
|
|
154
|
+
adaptive-ui automatically detects some conditions.
|
|
155
|
+
|
|
156
|
+
### Example: Check if user is impatient
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
if (ui.has("impatient")) {
|
|
160
|
+
console.log("User is impatient");
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
You don’t need to know *how* impatience is detected.
|
|
165
|
+
|
|
166
|
+
That logic is handled internally.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🧠 Strategies (The Core Feature)
|
|
171
|
+
|
|
172
|
+
Strategies define **how your UI should react**.
|
|
173
|
+
|
|
174
|
+
### Example: Lite UI on slow network
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
ui.strategy({
|
|
178
|
+
conditions: ["slow-network"],
|
|
179
|
+
actions: () => {
|
|
180
|
+
document.body.classList.add("ui-lite");
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
This means:
|
|
186
|
+
|
|
187
|
+
> If network is slow → switch to lite UI
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### Example: Focus mode for impatient users
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
ui.strategy({
|
|
195
|
+
conditions: ["impatient"],
|
|
196
|
+
actions: () => {
|
|
197
|
+
document.body.classList.add("ui-focus");
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### Run strategies
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
ui.run();
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
This applies all matching strategies.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 🎨 Example CSS
|
|
215
|
+
|
|
216
|
+
```css
|
|
217
|
+
.ui-lite * {
|
|
218
|
+
animation: none !important;
|
|
219
|
+
transition: none !important;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.ui-focus nav,
|
|
223
|
+
.ui-focus aside {
|
|
224
|
+
display: none;
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Your UI now adapts **without changing components**.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 🧪 Debugging (Very Important)
|
|
233
|
+
|
|
234
|
+
To understand *why* the UI changed:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
console.log(ui.explain());
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Output:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
{
|
|
244
|
+
context: ["slow-network", "impatient"],
|
|
245
|
+
strategies: 2
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
This helps avoid confusion during development.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## ♿ Accessibility-Friendly by Design
|
|
254
|
+
|
|
255
|
+
adaptive-ui helps you:
|
|
256
|
+
|
|
257
|
+
* reduce motion
|
|
258
|
+
* simplify layouts
|
|
259
|
+
* increase clarity
|
|
260
|
+
|
|
261
|
+
Without extra libraries.
|
|
262
|
+
|
|
263
|
+
It reacts to **behavior**, not user labels.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 🚀 Why Use adaptive-ui?
|
|
268
|
+
|
|
269
|
+
* cleaner UI logic
|
|
270
|
+
* fewer edge cases
|
|
271
|
+
* better UX automatically
|
|
272
|
+
* no heavy setup
|
|
273
|
+
* beginner-friendly
|
|
274
|
+
|
|
275
|
+
Once added, it quietly improves your app.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## 🧱 What adaptive-ui Will NEVER Do
|
|
280
|
+
|
|
281
|
+
To keep trust high:
|
|
282
|
+
|
|
283
|
+
* ❌ no analytics
|
|
284
|
+
* ❌ no tracking
|
|
285
|
+
* ❌ no AI
|
|
286
|
+
* ❌ no user profiling
|
|
287
|
+
* ❌ no remote configs
|
|
288
|
+
|
|
289
|
+
Your app stays **privacy-respecting**.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 🛣️ Roadmap
|
|
294
|
+
|
|
295
|
+
Planned features:
|
|
296
|
+
|
|
297
|
+
* debug overlay
|
|
298
|
+
* persistent context
|
|
299
|
+
* strategy priorities
|
|
300
|
+
* plugin system
|
|
301
|
+
|
|
302
|
+
Not planned:
|
|
303
|
+
|
|
304
|
+
* ML models
|
|
305
|
+
* server-side tracking
|
|
306
|
+
* UI rendering
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 🧠 Philosophy
|
|
311
|
+
|
|
312
|
+
> Good UI should feel invisible.
|
|
313
|
+
|
|
314
|
+
adaptive-ui helps you build interfaces that **respect users**, without extra complexity.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 📄 License
|
|
319
|
+
|
|
320
|
+
MIT License
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## 🙌 Final Note
|
|
325
|
+
|
|
326
|
+
If you are a beginner:
|
|
327
|
+
|
|
328
|
+
* start small
|
|
329
|
+
* add one strategy
|
|
330
|
+
* observe the effect
|
|
331
|
+
|
|
332
|
+
adaptive-ui grows **with your understanding**, not against it.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AdaptiveUI: () => AdaptiveUI,
|
|
24
|
+
focusMode: () => focusMode,
|
|
25
|
+
liteUI: () => liteUI,
|
|
26
|
+
useAdaptive: () => useAdaptive
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/adaptive.ts
|
|
31
|
+
var AdaptiveUI = class {
|
|
32
|
+
constructor() {
|
|
33
|
+
this.context = /* @__PURE__ */ new Set();
|
|
34
|
+
this.strategies = [];
|
|
35
|
+
}
|
|
36
|
+
addContext(ctx) {
|
|
37
|
+
this.context.add(ctx);
|
|
38
|
+
}
|
|
39
|
+
has(ctx) {
|
|
40
|
+
return this.context.has(ctx);
|
|
41
|
+
}
|
|
42
|
+
strategy(strategy) {
|
|
43
|
+
this.strategies.push(strategy);
|
|
44
|
+
}
|
|
45
|
+
run() {
|
|
46
|
+
for (const s of this.strategies) {
|
|
47
|
+
if (s.conditions.every((c) => this.context.has(c))) {
|
|
48
|
+
s.actions();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
explain() {
|
|
53
|
+
return {
|
|
54
|
+
context: Array.from(this.context),
|
|
55
|
+
strategies: this.strategies.length
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/react.ts
|
|
61
|
+
var import_react = require("react");
|
|
62
|
+
|
|
63
|
+
// src/signals/network.ts
|
|
64
|
+
function detectSlowNetwork(onDetect) {
|
|
65
|
+
const connection = navigator.connection;
|
|
66
|
+
if (!connection) return;
|
|
67
|
+
if (connection.effectiveType === "2g" || connection.effectiveType === "slow-2g") {
|
|
68
|
+
onDetect();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/signals/battery.ts
|
|
73
|
+
async function detectLowBattery(onDetect) {
|
|
74
|
+
if (!("getBattery" in navigator)) return;
|
|
75
|
+
const battery = await navigator.getBattery();
|
|
76
|
+
if (battery.level < 0.2) {
|
|
77
|
+
onDetect();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/signals/behavior.ts
|
|
82
|
+
function detectImpatience(onDetect) {
|
|
83
|
+
let clicks = 0;
|
|
84
|
+
window.addEventListener("click", () => {
|
|
85
|
+
clicks++;
|
|
86
|
+
if (clicks >= 6) {
|
|
87
|
+
onDetect();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/react.ts
|
|
93
|
+
function useAdaptive() {
|
|
94
|
+
const [adaptive] = (0, import_react.useState)(() => new AdaptiveUI());
|
|
95
|
+
(0, import_react.useEffect)(() => {
|
|
96
|
+
detectSlowNetwork(() => adaptive.addContext("slow-network"));
|
|
97
|
+
detectLowBattery(() => adaptive.addContext("low-battery"));
|
|
98
|
+
detectImpatience(() => adaptive.addContext("impatient"));
|
|
99
|
+
}, []);
|
|
100
|
+
return adaptive;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/strategies.ts
|
|
104
|
+
function liteUI() {
|
|
105
|
+
document.body.classList.add("ui-lite");
|
|
106
|
+
}
|
|
107
|
+
function focusMode() {
|
|
108
|
+
document.body.classList.add("ui-focus");
|
|
109
|
+
}
|
|
110
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
111
|
+
0 && (module.exports = {
|
|
112
|
+
AdaptiveUI,
|
|
113
|
+
focusMode,
|
|
114
|
+
liteUI,
|
|
115
|
+
useAdaptive
|
|
116
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type Context = "slow-network" | "low-battery" | "impatient" | "new-user";
|
|
2
|
+
type Strategy = {
|
|
3
|
+
conditions: Context[];
|
|
4
|
+
actions: () => void;
|
|
5
|
+
};
|
|
6
|
+
declare class AdaptiveUI {
|
|
7
|
+
private context;
|
|
8
|
+
private strategies;
|
|
9
|
+
addContext(ctx: Context): void;
|
|
10
|
+
has(ctx: Context): boolean;
|
|
11
|
+
strategy(strategy: Strategy): void;
|
|
12
|
+
run(): void;
|
|
13
|
+
explain(): {
|
|
14
|
+
context: Context[];
|
|
15
|
+
strategies: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare function useAdaptive(): AdaptiveUI;
|
|
20
|
+
|
|
21
|
+
declare function liteUI(): void;
|
|
22
|
+
declare function focusMode(): void;
|
|
23
|
+
|
|
24
|
+
export { AdaptiveUI, focusMode, liteUI, useAdaptive };
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@c-technology/adaptive-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Human-aware UI adaptation layer for web apps",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --dts",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"lint": "tsc --noEmit",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/react": "^19.2.7",
|
|
31
|
+
"react": "^19.2.3",
|
|
32
|
+
"tsup": "^8.5.1",
|
|
33
|
+
"typescript": "^5.3.0"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"adaptive-ui",
|
|
37
|
+
"ux",
|
|
38
|
+
"accessibility",
|
|
39
|
+
"frontend",
|
|
40
|
+
"react",
|
|
41
|
+
"typescript"
|
|
42
|
+
],
|
|
43
|
+
"author": "Your Name",
|
|
44
|
+
"license": "MIT"
|
|
45
|
+
}
|