kaplay-ui 0.20.8 → 1.0.0-alpha.10

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 CHANGED
@@ -2,38 +2,49 @@
2
2
  <img src="assets/kaplay-ui-logo.png" alt="kaplay-ui logo" width="400" >
3
3
  </p>
4
4
 
5
- # KAPLAY UI
5
+ # KAPLAY UI Plugin
6
6
 
7
- _A simple and customizable UI plugin library for building interfaces in https://kaplayjs.com/._
7
+ A lightweight UI helper plugin for the [KAPLAY](https://kaplayjs.com/) game library, providing ready‑to‑use UI Game Objects such as text‑based buttons **and labels**.
8
8
 
9
- Kaplay UI provides a game‑oriented **UI plugin** designed specifically for KAPLAY.
9
+ This plugin simplifies the creation of UI Game Object elements using familiar KAPLAY primitives, offering sensible defaults while remaining flexible and composable.
10
10
 
11
- For now it helps you build Game Objects like text buttons and labels — without reinventing the wheel.
11
+ ---
12
+
13
+ ## ✨ Features
12
14
 
13
- > ⚠️ **Note**
14
- > The currently published stable version (`0.20.8`) is being replaced by a complete redesign.
15
- >
16
- > A new **v1** version is under active development and can be installed in prerelease form (see below).
15
+ - 🎛️ **Text Button** — easily create a button with centered text
16
+ - 🏷️ **Label** a lightweight text-based UI element for HUDs, counters, tooltips, and more
17
+ - 🧩 Designed to feel like native KAPLAY components
18
+ - 🎨 Comes with built‑in sizing, positioning, and outline defaults
19
+ - 🔧 Fully customizable through the normal KAPLAY component system
17
20
 
18
21
  ---
19
22
 
20
23
  ## 📦 Installation
21
24
 
22
- ### Prerelease (new v1 work)
25
+ Install the plugin using your preferred package manager:
26
+
27
+ ```sh
28
+ npm install kaplay-ui
29
+ ```
30
+
31
+ or:
23
32
 
24
- ```bash
25
- npm install kaplay-ui@next
33
+ ```sh
34
+ pnpm add kaplay-ui
26
35
  ```
27
36
 
28
- This gives you the latest `1.0.0‑alpha.*` builds.
37
+ or:
38
+
39
+ ```sh
40
+ yarn add kaplay-ui
41
+ ```
29
42
 
30
43
  ---
31
44
 
32
45
  ## 🚀 Usage
33
46
 
34
- **Kaplay UI** exports a plugin for adding UI Game Objects.
35
-
36
- The `kaplayUI` plugin is exported from the package root:
47
+ Import and register the plugin when initializing your KAPLAY game:
37
48
 
38
49
  ```ts
39
50
  import kaplay from "kaplay";
@@ -46,20 +57,18 @@ const k = kaplay({
46
57
 
47
58
  You now have access to the UI helpers via your `k` instance:
48
59
 
49
- ```ts
60
+ ```js
50
61
  const btn = k.addTextButton("Play", 200, 100);
51
62
  const label = k.addLabel("Score: 0");
52
63
  ```
53
64
 
54
65
  ---
55
66
 
56
- ## 🧩 Game Objects (_**1.0.0‑alpha.\* version**_)
57
-
58
- ### 🔤 **Text Button** (`addTextButton()`)
67
+ ## 🔘 `addTextButton()`
59
68
 
60
69
  Creates a button-like GameObj with centered text and some convenient defaults.
61
70
 
62
- #### _**Signature**_
71
+ ### **Signature**
63
72
 
64
73
  ```ts
65
74
  addTextButton(
@@ -69,7 +78,7 @@ addTextButton(
69
78
  ): GameObj
70
79
  ```
71
80
 
72
- #### _**Default values**_
81
+ ### **Default values**
73
82
 
74
83
  | Parameter | Default |
75
84
  | --------- | ---------- |
@@ -77,18 +86,18 @@ addTextButton(
77
86
  | `width` | `200` |
78
87
  | `height` | `100` |
79
88
 
80
- #### _**Default styling**_
89
+ ### **Default styling**
81
90
 
82
91
  When created, the button includes:
83
92
 
84
- - k.outline(3)
85
- - k.pos(0, 0)
86
- - k.anchor("topleft")
87
- - k.area() — for click/hover detection
93
+ - `k.outline(3)`
94
+ - `k.pos(0, 0)`
95
+ - `k.anchor("topleft")`
96
+ - `k.area()` — for click/hover detection
88
97
 
89
- #### _**Examples**_
98
+ ### **Example**
90
99
 
91
- ```ts
100
+ ```js
92
101
  // Default button
93
102
  const btn1 = k.addTextButton();
94
103
 
@@ -106,11 +115,11 @@ btn2.onClick(() => {
106
115
 
107
116
  ---
108
117
 
109
- ### 🏷️ **Label** (`addLabel()`)
118
+ ## 🏷️ `addLabel()`
110
119
 
111
120
  A lightweight text-based UI element — ideal for HUD counters, tooltips, status text, or titles.
112
121
 
113
- #### _**Signature**_
122
+ ### **Signature**
114
123
 
115
124
  ```ts
116
125
  addLabel(
@@ -120,7 +129,7 @@ addLabel(
120
129
  )
121
130
  ```
122
131
 
123
- #### _**Default values**_
132
+ ### **Default values**
124
133
 
125
134
  | Parameter | Default |
126
135
  | --------- | ------- |
@@ -128,7 +137,7 @@ addLabel(
128
137
  | `width` | `160` |
129
138
  | `height` | `96` |
130
139
 
131
- #### _**Examples**_
140
+ ### **Example**
132
141
 
133
142
  ```js
134
143
  // Default button
@@ -146,11 +155,11 @@ const scoreLabel = k.addLabel(`Score: ${score}`);
146
155
 
147
156
  k.wait(2, () => {
148
157
  score++;
149
- scoreLabel.children[0].text = `Score: ${score}`;
150
- });
158
+ scoreLabel.children[0].text = `Score: ${score}`
159
+ })
151
160
  ```
152
161
 
153
- #### _**Common use cases**_
162
+ ### Common use cases
154
163
 
155
164
  - HUD overlays
156
165
  - Score counters
@@ -160,22 +169,108 @@ k.wait(2, () => {
160
169
 
161
170
  ---
162
171
 
163
- ## 🛣️ Roadmap
172
+ ## 📱 Mobile‑Friendly UI Demo
164
173
 
165
- _See evolving roadmap at: https://github.com/jbakchr/kaplay-ui/blob/v1/ROADMAP.md_
174
+ This example shows how to build a touch‑friendly UI using `addTextButton()` on mobile devices. It uses responsive scaling and KAPLAY’s `stretch` and `letterbox` features to adapt to different screen sizes.
166
175
 
167
- ---
176
+ ### **Example**
168
177
 
169
- ## 📚 License
178
+ ```js
179
+ import kaplay from "kaplay";
180
+ import kaplayUI from "kaplay-ui";
170
181
 
171
- This project is licensed under the **MIT License**.
172
- See the `LICENSE` file for details.
182
+ const k = kaplay({
183
+ width: 480, // required when using letterbox/stretch
184
+ height: 270,
185
+ background: [25, 25, 25],
186
+ plugins: [kaplayUI],
187
+
188
+ stretch: true, // resize canvas on mobile
189
+ letterbox: true, // preserve aspect ratio
190
+ });
191
+
192
+ function uiScale() {
193
+ return Math.min(k.width() / 400, 1.4);
194
+ }
195
+
196
+ function centerX(width: number) {
197
+ return k.center().x - width / 2;
198
+ }
199
+
200
+ k.scene("mobile-menu", () => {
201
+ const scale = uiScale();
202
+
203
+ k.add([
204
+ k.text("Mobile Menu", { size: 36 * scale }),
205
+ k.anchor("center"),
206
+ k.pos(k.center().x, 70 * scale),
207
+ ]);
208
+
209
+ const btnWidth = 260 * scale;
210
+ const btnHeight = 90 * scale;
211
+
212
+ const startBtn = k.addTextButton("Start", btnWidth, btnHeight);
213
+ startBtn.pos = k.vec2(centerX(btnWidth), 150 * scale);
214
+ startBtn.onClick(() => k.go("mobile-game"));
215
+ startBtn.onHover(() => (startBtn.color = k.rgb(140, 220, 140)));
216
+ startBtn.onHoverEnd(() => (startBtn.color = k.WHITE));
217
+
218
+ const settingsBtn = k.addTextButton("Settings", btnWidth, btnHeight);
219
+ settingsBtn.pos = k.vec2(centerX(btnWidth), 280 * scale);
220
+ settingsBtn.onClick(() => k.go("settings"));
221
+ settingsBtn.onHover(() => (settingsBtn.color = k.rgb(140, 160, 240)));
222
+ settingsBtn.onHoverEnd(() => (settingsBtn.color = k.WHITE));
223
+ });
224
+
225
+ k.scene("mobile-game", () => {
226
+ const scale = uiScale();
227
+
228
+ k.add([
229
+ k.text("Game Scene", { size: 28 * scale }),
230
+ k.anchor("center"),
231
+ k.pos(k.center().x, 90 * scale),
232
+ ]);
233
+
234
+ const backBtn = k.addTextButton("Back", 200 * scale, 80 * scale);
235
+ backBtn.pos = k.vec2(centerX(200 * scale), k.height() - 120 * scale);
236
+ backBtn.onClick(() => k.go("mobile-menu"));
237
+ backBtn.onHover(() => (backBtn.scale = 1.1 * scale));
238
+ backBtn.onHoverEnd(() => (backBtn.scale = scale));
239
+ });
240
+
241
+ k.scene("settings", () => {
242
+ const scale = uiScale();
243
+
244
+ k.add([
245
+ k.text("Settings", { size: 32 * scale }),
246
+ k.anchor("center"),
247
+ k.pos(k.center().x, 70 * scale),
248
+ ]);
249
+
250
+ const backBtn = k.addTextButton("Back", 200 * scale, 80 * scale);
251
+ backBtn.pos = k.vec2(centerX(200 * scale), k.height() - 140 * scale);
252
+ backBtn.onClick(() => k.go("mobile-menu"));
253
+ });
254
+
255
+ k.go("mobile-menu");
256
+ ```
257
+
258
+ ### ✔ Features demonstrated
259
+
260
+ - Responsive UI scaling
261
+ - Touch‑friendly hit areas
262
+ - Hover animations for mobile
263
+ - Auto‑centered vertical layout
264
+ - Scene navigation
173
265
 
174
266
  ---
175
267
 
176
- ## 💬 Contact
268
+ ## 📚 Documentation
269
+
270
+ _**Evolving**_ documentation is available in the [docs](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/index.md) folder.
271
+
272
+ ---
177
273
 
178
- Have questions or suggestions?
179
- Open an issue on GitHub:
274
+ ## 📄 License
180
275
 
181
- 👉 <https://github.com/jbakchr/kaplay-ui>
276
+ MIT — free for personal and commercial use.
@@ -0,0 +1,113 @@
1
+ //#region src/components/button/index.ts
2
+ function e(e, t, n, r) {
3
+ return e.make([
4
+ e.rect(t, n, { radius: 15 }),
5
+ e.color(200, 200, 200),
6
+ e.outline(3, e.rgb(92, 91, 91)),
7
+ e.pos(0, 0),
8
+ e.anchor(r),
9
+ e.area()
10
+ ]);
11
+ }
12
+ //#endregion
13
+ //#region src/components/label/index.ts
14
+ function t(e, t, n) {
15
+ return e.make([
16
+ e.rect(t, n),
17
+ e.pos(0, 0),
18
+ e.color(0, 0, 0),
19
+ e.opacity(.7),
20
+ e.anchor("topleft")
21
+ ]);
22
+ }
23
+ //#endregion
24
+ //#region src/components/text/index.ts
25
+ function n(e, t, n, r, i, a) {
26
+ return e.make([
27
+ e.text(t, { size: n }),
28
+ e.color(0, 0, 0),
29
+ e.pos(r, i),
30
+ e.anchor(a)
31
+ ]);
32
+ }
33
+ //#endregion
34
+ //#region src/helpers/pos-utils.ts
35
+ function r(e) {
36
+ return {
37
+ cX: e.width / 2,
38
+ cY: e.height / 2
39
+ };
40
+ }
41
+ function i(e, t, n, r, i) {
42
+ switch (t.anchor) {
43
+ case "bot":
44
+ n.use(e.pos(0 * r, -1 * i));
45
+ break;
46
+ case "botleft":
47
+ n.use(e.pos(1 * r, -1 * i));
48
+ break;
49
+ case "botright":
50
+ n.use(e.pos(-1 * r, -1 * i));
51
+ break;
52
+ case "center":
53
+ n.use(e.pos(0 * r, 0 * i));
54
+ break;
55
+ case "left":
56
+ n.use(e.pos(1 * r, 0 * i));
57
+ break;
58
+ case "right":
59
+ n.use(e.pos(-1 * r, 0 * i));
60
+ break;
61
+ case "top":
62
+ n.use(e.pos(0 * r, 1 * i));
63
+ break;
64
+ case "topleft":
65
+ n.use(e.pos(1 * r, 1 * i));
66
+ break;
67
+ case "topright":
68
+ n.use(e.pos(-1 * r, 1 * i));
69
+ break;
70
+ default: break;
71
+ }
72
+ }
73
+ //#endregion
74
+ //#region src/helpers/layout-utils.ts
75
+ var a = new Set([
76
+ "anchor",
77
+ "pos",
78
+ "area"
79
+ ]);
80
+ function o(e) {
81
+ return a.has(e);
82
+ }
83
+ //#endregion
84
+ //#region src/elements/text-button/index.ts
85
+ function s(t, a, s, c) {
86
+ let l = e(t, s, c, "topleft"), { cX: u, cY: d } = r(l), f = n(t, a, 22, u, d, "center");
87
+ return l.add(f), t.add(l), l.onUse((e) => {
88
+ if (o(e)) {
89
+ let { cX: e, cY: n } = r(l);
90
+ i(t, l, f, e, n);
91
+ }
92
+ }), l.onHover(() => {
93
+ t.setCursor("pointer");
94
+ }), l.onHoverEnd(() => {
95
+ t.setCursor("default");
96
+ }), l;
97
+ }
98
+ //#endregion
99
+ //#region src/elements/label/index.ts
100
+ function c(e, i, a, o) {
101
+ let s = t(e, a, o), { cX: c, cY: l } = r(s), u = n(e, i, 20, c, l, "center");
102
+ return u.use(e.color(255, 255, 255)), s.add(u), e.add(s), s;
103
+ }
104
+ //#endregion
105
+ //#region src/index.ts
106
+ function l(e) {
107
+ return {
108
+ addTextButton: (t = "Settings", n = 150, r = 60) => s(e, t, n, r),
109
+ addLabel: (t = "", n = 160, r = 96) => c(e, t, n, r)
110
+ };
111
+ }
112
+ //#endregion
113
+ export { l as default };
@@ -0,0 +1 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.uiPlugin=t())})(this,function(){function e(e,t,n,r){return e.make([e.rect(t,n,{radius:15}),e.color(200,200,200),e.outline(3,e.rgb(92,91,91)),e.pos(0,0),e.anchor(r),e.area()])}function t(e,t,n){return e.make([e.rect(t,n),e.pos(0,0),e.color(0,0,0),e.opacity(.7),e.anchor(`topleft`)])}function n(e,t,n,r,i,a){return e.make([e.text(t,{size:n}),e.color(0,0,0),e.pos(r,i),e.anchor(a)])}function r(e){return{cX:e.width/2,cY:e.height/2}}function i(e,t,n,r,i){switch(t.anchor){case`bot`:n.use(e.pos(0*r,-1*i));break;case`botleft`:n.use(e.pos(1*r,-1*i));break;case`botright`:n.use(e.pos(-1*r,-1*i));break;case`center`:n.use(e.pos(0*r,0*i));break;case`left`:n.use(e.pos(1*r,0*i));break;case`right`:n.use(e.pos(-1*r,0*i));break;case`top`:n.use(e.pos(0*r,1*i));break;case`topleft`:n.use(e.pos(1*r,1*i));break;case`topright`:n.use(e.pos(-1*r,1*i));break;default:break}}var a=new Set([`anchor`,`pos`,`area`]);function o(e){return a.has(e)}function s(t,a,s,c){let l=e(t,s,c,`topleft`),{cX:u,cY:d}=r(l),f=n(t,a,22,u,d,`center`);return l.add(f),t.add(l),l.onUse(e=>{if(o(e)){let{cX:e,cY:n}=r(l);i(t,l,f,e,n)}}),l.onHover(()=>{t.setCursor(`pointer`)}),l.onHoverEnd(()=>{t.setCursor(`default`)}),l}function c(e,i,a,o){let s=t(e,a,o),{cX:c,cY:l}=r(s),u=n(e,i,20,c,l,`center`);return u.use(e.color(255,255,255)),s.add(u),e.add(s),s}function l(e){return{addTextButton:(t=`Settings`,n=150,r=60)=>s(e,t,n,r),addLabel:(t=``,n=160,r=96)=>c(e,t,n,r)}}return l});
@@ -0,0 +1,4 @@
1
+ theme: jekyll-theme-minimal
2
+ title: "KAPLAY UI"
3
+ description: "UI Plugin for KAPLAY"
4
+ markdown: kramdown
@@ -0,0 +1,23 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+
8
+ <title>{{ page.title }} - {{ site.title }}</title>
9
+
10
+ <!-- Load custom stylesheet -->
11
+ <link rel="stylesheet" href="{{ '/assets/style.css' | relative_url }}" />
12
+ </head>
13
+
14
+ <body>
15
+ <!-- KAPLAY-style header -->
16
+ <header class="kaplay-header">
17
+ <img src="{{ '/assets/logo.png' | relative_url }}" class="kaplay-logo" />
18
+ </header>
19
+
20
+ <!-- Main content -->
21
+ <main class="page-content">{{ content }}</main>
22
+ </body>
23
+ </html>
@@ -0,0 +1,23 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+
8
+ <title>{{ page.title }} - {{ site.title }}</title>
9
+
10
+ <!-- Load custom stylesheet -->
11
+ <link rel="stylesheet" href="{{ '/assets/style.css' | relative_url }}" />
12
+ </head>
13
+
14
+ <body>
15
+ <!-- KAPLAY-style header -->
16
+ <header class="kaplay-header">
17
+ <img src="{{ '/assets/logo.png' | relative_url }}" class="kaplay-logo" />
18
+ </header>
19
+
20
+ <!-- Main content -->
21
+ <main class="page-content">{{ content }}</main>
22
+ </body>
23
+ </html>
@@ -0,0 +1,6 @@
1
+ # Design Decisions
2
+
3
+ This md and the subfolders below it documents to design decision defaults of:
4
+
5
+ - [Label](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/design-decisions/label/label-defaults.md)
6
+ - [Text Button](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/design-decisions/text-button/text-button-defaults.md)
@@ -0,0 +1,219 @@
1
+ # Label Design Decisions
2
+
3
+ This document provides why the current defaults for the `addLabel()` Game Objects are what they are for:
4
+
5
+ 1. [Background color](#-most-common-background-colors-for-labels-in-games)
6
+ 2. [Width and Height](#-the-most-common-label-sizes-in-games)
7
+
8
+ ## 🎨 Most Common Background Colors for Labels in Games
9
+
10
+ ### **1. Dark, Semi-Transparent Background (Most Common Overall)**
11
+
12
+ Examples:
13
+
14
+ - `rgba(0, 0, 0, 0.5)`
15
+ - `rgba(0, 0, 0, 0.7)`
16
+
17
+ Why it's common:
18
+
19
+ - Works on almost every scene background (bright, dark, colorful).
20
+ - Makes white or light text extremely readable.
21
+ - Gives a “HUD” feel found in many genres.
22
+
23
+ Where it's used:
24
+
25
+ - Health bars
26
+ - Tooltips
27
+ - HUD counters
28
+ - Inventory labels
29
+
30
+ This is the single most widely used choice in modern 2D games.
31
+
32
+ ---
33
+
34
+ ### **2. Solid Black or Near‑Black Background**
35
+
36
+ Examples:
37
+
38
+ - `rgb(0, 0, 0)`
39
+ - `rgb(20, 20, 20)`
40
+
41
+ Why:
42
+
43
+ - Maximum contrast
44
+ - Minimalistic
45
+ - Fits sci-fi, retro, and clean UI styles
46
+
47
+ Often used for:
48
+
49
+ - Menu labels
50
+ - Title bars
51
+ - Status text
52
+
53
+ Good when opacity effects aren’t needed.
54
+
55
+ ---
56
+
57
+ ### **3. Light Background With Dark Text**
58
+
59
+ Examples:
60
+
61
+ - `rgb(255, 255, 255)` with black text
62
+ - `rgb(240, 240, 240)` for softer look
63
+
64
+ Why:
65
+
66
+ - Works well for “window”-style UI
67
+ - Mimics desktop/UI patterns
68
+ - Very readable for high-contrast HUDs
69
+
70
+ Used in:
71
+
72
+ - Casual games
73
+ - Puzzle games
74
+ - Mobile UX-inspired UI
75
+
76
+ This is less common than dark backgrounds but still widely used.
77
+
78
+ ---
79
+
80
+ ### **4. Colored Background Matching the Game’s Theme**
81
+
82
+ Examples:
83
+
84
+ - Blue, red, or green surfaces
85
+ - Faded color fills like `rgba(50, 100, 200, 0.8)`
86
+
87
+ Why:
88
+
89
+ - Looks more “stylized” or thematic
90
+ - Helps differentiate label purposes (e.g., warning = red)
91
+ - Used in highly themed menus and fantasy games
92
+
93
+ Common in:
94
+
95
+ - JRPG-style menus
96
+ - Crafting systems
97
+ - Quest logs
98
+
99
+ ---
100
+
101
+ ### **5. No Background (Text Only)**
102
+
103
+ While not literally a “background color,” this is a very common choice:
104
+
105
+ - Pure text HUD
106
+ - Floating damage numbers
107
+ - Dialogue text overlays
108
+
109
+ Games often use this when:
110
+
111
+ - Text doesn’t compete with backgrounds
112
+ - The look should be minimal
113
+
114
+ You can easily support this in `addLabel()` by letting `background: null`.
115
+
116
+ ---
117
+
118
+ ## ⭐ If you want the safest, most universal choice…
119
+
120
+ #### → **Use a dark semi-transparent background with light text.**
121
+
122
+ Something like:
123
+
124
+ ```ts
125
+ background: rgba(0, 0, 0, 0.5),
126
+ color: rgb(255, 255, 255)
127
+ ```
128
+
129
+ This works cleanly on:
130
+
131
+ - Dark levels
132
+ - Bright levels
133
+ - High-motion action scenes
134
+ - Stylized or pixel art environments
135
+
136
+ And it's easy for players to read instantly.
137
+
138
+ ---
139
+
140
+ ## 🎮 The Most Common Label Sizes in Games
141
+
142
+ Even though no source gives explicit “standard label dimensions,” we _can_ derive the common ranges from:
143
+
144
+ - Typical **pixel-art game resolutions** such as **320×180**, **640×360**, **1280×720** (all frequently used) [\[screwloose....github.io\]](https://screwloose-games.github.io/documentation/content/guides/art/pixel_art/pixel_art_guide.html)
145
+ - Common **sprite and tile sizes** (16×16, 32×32, 64×64) which UI assets usually align to [\[aipixelart.net\]](https://aipixelart.net/node/267)
146
+ - Observed dimensions in **pixel-art UI packs** on itch.io (buttons, panels, labels etc. typically in the 32–128px height range) [\[itch.io\]](https://itch.io/game-assets/tag-gui/tag-pixel-art)
147
+
148
+ From these, we can determine practical label sizes used in real games.
149
+
150
+ ---
151
+
152
+ ### 📐 **Typical Label Widths**
153
+
154
+ #### **Small labels (HUD counters, score, ammo):**
155
+
156
+ - **96px – 160px** wide
157
+ - Fits single short text like “SCORE: 0”, “HP”, “XP”, “TIME”
158
+
159
+ #### **Medium labels (section titles, longer stat text):**
160
+
161
+ - **160px – 256px** wide
162
+ - Fits text strings like “OBJECTIVE COMPLETE”, “CURRENT QUEST”
163
+
164
+ #### **Large labels (UI titles, menu headings):**
165
+
166
+ - **256px – 384px** wide
167
+ - Used for things like “INVENTORY”, “SETTINGS”, “PAUSED”
168
+
169
+ These sizes map cleanly into 16px/32px multiples recommended for pixel-art alignment.
170
+
171
+ ---
172
+
173
+ ### 📏 **Typical Label Heights**
174
+
175
+ Based on common UI asset proportions:
176
+
177
+ - **Text-only HUD label:**
178
+ **24–32px** tall (for 8–16px font sizes)
179
+
180
+ - **Label with padding + background panel:**
181
+ **32–48px** tall is most standard
182
+
183
+ - **Chunkier RPG or fantasy labels:**
184
+ **48–64px** tall in many itch.io GUI packs [\[itch.io\]](https://itch.io/game-assets/tag-gui/tag-pixel-art)
185
+
186
+ These heights align with the commonly used sprite grids of **16×16**, **32×32**, and **64×64** pixels from pixel-art standards. [\[aipixelart.net\]](https://aipixelart.net/node/267)
187
+
188
+ ---
189
+
190
+ ### 🧩 **Putting It All Together (Practical Defaults)**
191
+
192
+ For a game using a common base pixel-art resolution like **640×360** (widely recommended), typical label sizes look like this: [\[screwloose....github.io\]](https://screwloose-games.github.io/documentation/content/guides/art/pixel_art/pixel_art_guide.html)
193
+
194
+ | Label Type | Width | Height |
195
+ | ------------------- | ------------- | ----------- |
196
+ | Small HUD label | **120–160px** | **24–32px** |
197
+ | Medium UI label | **160–256px** | **32–48px** |
198
+ | Large heading label | **256–384px** | **48–64px** |
199
+
200
+ These fit cleanly into standard pixel-art grids and look correct across the common 320×180, 640×360, and 1280×720 style pixel resolutions.
201
+
202
+ ---
203
+
204
+ ### 🎯 **Recommended Defaults for kaplay-ui**
205
+
206
+ If you want to provide sensible defaults for your Label component:
207
+
208
+ - **Width:** Automatically sized based on text
209
+ - **Height:**
210
+ - **32px** for simple HUD labels
211
+ - **48px** for background panels
212
+
213
+ This keeps things aligned with the pixel-art norms and avoids awkward scaling.
214
+
215
+ ---
216
+
217
+ _**See next decisions about `Text Button` here:**_
218
+
219
+ - [Text Button](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/design-decisions/text-button/text-button-defaults.md)
package/docs/faq.md ADDED
@@ -0,0 +1,72 @@
1
+ # ❓ KAPLAY UI — FAQ
2
+
3
+ _Frequently asked questions about the KAPLAY UI plugin._
4
+
5
+ ---
6
+
7
+ ## 📚 Table of Contents
8
+
9
+ - [General](#-general)
10
+ - [Installation](#-installation)
11
+ - [Usage](#-usage)
12
+ - [Licensing](#-licensing)
13
+
14
+ ---
15
+
16
+ ## 🧠 General
17
+
18
+ **Q:** _**What is this plugin for?**_
19
+
20
+ **A:** KAPLAY UI provides lightweight, composable UI helpers — such as text‑based buttons and labels — built using familiar **KAPLAY primitives**.
21
+
22
+ <div class="block">
23
+ It reduces boilerplate, keeps UI consistent, and helps you build menus, HUDs, overlays, and interactive UI faster.
24
+ </div>
25
+
26
+ ---
27
+
28
+ ## 📦 Installation
29
+
30
+ ### **Q:** _**How do I install KAPLAY UI?**_
31
+
32
+ **A:** Install from your project root using your preferred package manager:
33
+
34
+ ```sh
35
+ npm install kaplay-ui@next
36
+ ```
37
+
38
+ ---
39
+
40
+ ## 🚀 Usage
41
+
42
+ ### **Q:** _**How do I use the `kaplay-ui` plugin?**_
43
+
44
+ **A:** Import and enable the plugin when creating your KAPLAY instance:
45
+
46
+ ```ts
47
+ import kaplay from "kaplay";
48
+ import kaplayUI from "kaplay-ui";
49
+
50
+ const k = kaplay({
51
+ plugins: [kaplayUI],
52
+ });
53
+ ```
54
+
55
+ Now you can create UI components such as:
56
+
57
+ ```ts
58
+ k.addTextButton("Play");
59
+ k.addLabel("Score: 0");
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 📄 Licensing
65
+
66
+ ### **Q:** _**How may I use `kaplay-ui` ?**_
67
+
68
+ The **MIT License** allows full personal and commercial use with virtually no restrictions.
69
+
70
+ <div class="block">
71
+ 🔓 **In short:** You can use, modify, distribute, and even sell projects using KAPLAY UI.
72
+ </div>
package/docs/index.md ADDED
@@ -0,0 +1,126 @@
1
+ # 🦖 KAPLAY-UI 🧩
2
+
3
+ _A lightweight and flexible **UI plugin for KAPLAY**, built to feel like a natural extension of the engine._
4
+
5
+ Create UI elements quickly using familiar KAPLAY primitives — buttons, labels, HUD elements, menus, and more.
6
+
7
+ <div class="block">
8
+
9
+ ✨ KAPLAY UI helps you build game-ready user interfaces with almost no setup.
10
+
11
+ Perfect for menus, HUDs, mobile UI, prototypes, and small-to-medium games.
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ ## 🚀 Why KAPLAY UI?
18
+
19
+ - 🧩 Works exactly like native KAPLAY
20
+ - 🎛️ Easy, declarative API
21
+ - ✨ Sensible styling defaults
22
+ - 🎨 Fully customizable
23
+
24
+ ---
25
+
26
+ ## 📦 Installation _(v1.0.0-alpha\*)_
27
+
28
+ ```sh
29
+ npm install kaplay-ui@next
30
+ ```
31
+
32
+ ---
33
+
34
+ ## 🧰 Getting Started
35
+
36
+ Add the plugin when creating your KAPLAY instance:
37
+
38
+ ```ts
39
+ import kaplay from "kaplay";
40
+ import kaplayUI from "kaplay-ui";
41
+
42
+ const k = kaplay({
43
+ plugins: [kaplayUI],
44
+ });
45
+ ```
46
+
47
+ Now create a UI element:
48
+
49
+ ```ts
50
+ const tb = k.addTextButton("Play");
51
+ const sl = k.addLabel("Score: 0");
52
+ ```
53
+
54
+ _**That’s it** — you’re ready to build UI!_
55
+
56
+ ---
57
+
58
+ ## 🧱 Core Components
59
+
60
+ ### 🔤 Text Button
61
+
62
+ Create an interactive button with centered text and built‑in outline & area detection.
63
+
64
+ ```ts
65
+ const tb = k.addTextButton("Play");
66
+
67
+ tb.onClick(() => {
68
+ console.log("Let's play!");
69
+ });
70
+ ```
71
+
72
+ ### 🏷️ Label
73
+
74
+ A small, flexible text element — perfect for HUDs.
75
+
76
+ ```ts
77
+ const lbl = k.addLabel("Score: 0");
78
+ ```
79
+
80
+ _More components are being added soon as the plugin grows._ 🎉
81
+
82
+ ---
83
+
84
+ ## 📚 Documentation
85
+
86
+ _Soon_ .. you can find more details here:
87
+
88
+ - 📄 **faq.md** — _Questions and answers_
89
+ - 🧠 **design-decisions** — _The Whys?_
90
+ - 🔘 **API Pages** — _The What & How_
91
+ - 🧪 **Examples** — _Learn by doing_
92
+ - 🪧 **Demos** — _See it in action_
93
+
94
+ ## 🗺️ Roadmap (WIP)
95
+
96
+ - **⏳ More component types**
97
+ _(sliders, toggles, panels)_
98
+
99
+ - **🎨 Theme presets**
100
+ _(built-in light/dark UI packs)_
101
+
102
+ - **🎛️ Complex layout helpers**
103
+ _(stacks, grids)_
104
+
105
+ - **📘 Full documentation site**
106
+ _(getting started, faq and .. more)_
107
+
108
+ ---
109
+
110
+ ## 🤝 Contributing
111
+
112
+ Contributions and feature ideas are warmly welcome!
113
+
114
+ Feel free to submit issues or PRs on GitHub — or share ideas in the discussions tab.
115
+
116
+ ---
117
+
118
+ ## 📜 License
119
+
120
+ MIT © 2026 [Jonas Bak Phillipson](https://github.com/jbakchr)
121
+
122
+ ---
123
+
124
+ <div class="block" style="text-align:center;font-weight:bold;">
125
+ <p>Made with 💚 + 🦖 by:<p>
126
+ <p style="font-style:italic;"><span style="color:#ffde86;">The</span> <span style="color:#f2ae99;">KAPLAY </span><span style="color:#CF9FFF;">Community</span></p>
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "kaplay-ui",
3
- "version": "0.20.8",
3
+ "version": "1.0.0-alpha.10",
4
4
  "description": "UI components for KAPLAY",
5
- "main": "index.js",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "scripts": {
8
+ "dev": "vite dev",
9
+ "build": "vite build"
10
+ },
6
11
  "repository": {
7
12
  "type": "git",
8
13
  "url": "git+https://github.com/jbakchr/kaplay-ui.git"
@@ -19,7 +24,11 @@
19
24
  },
20
25
  "homepage": "https://github.com/jbakchr/kaplay-ui#readme",
21
26
  "dependencies": {
22
- "kaplay": "^3001.0.10"
27
+ "kaplay": "^3001.0.19"
23
28
  },
24
- "type": "module"
29
+ "devDependencies": {
30
+ "@types/node": "^25.5.0",
31
+ "vite": "^8.0.2",
32
+ "vite-plugin-dts": "^4.5.4"
33
+ }
25
34
  }
package/src/index.ts ADDED
@@ -0,0 +1,133 @@
1
+ import type { KAPLAYCtx } from "kaplay";
2
+
3
+ // Types
4
+ import { LabelComponent, TextButtonElement } from "./types";
5
+
6
+ // Text Button
7
+ import { createLabel, createTextButton } from "./elements";
8
+
9
+ /**
10
+ * # KAPLAY UI Plugin
11
+ *
12
+ * A small UI helper plugin that adds convenience functions for creating
13
+ * common UI components such as buttons and labels within a KAPLAY game.
14
+ *
15
+ * The returned object extends your KAPLAY context with UI creation helpers.
16
+ *
17
+ * ## Available Helpers
18
+ * - `addTextButton(txt?, width?, height?)`
19
+ * - `addLabel(txt?, width?, height?)`
20
+ *
21
+ * @returns {{
22
+ * addTextButton: (txt?: string, width?: number, height?: number) => TextButtonElement,
23
+ * addLabel: (txt?: string, width?: number, height?: number) => LabelElement
24
+ * }}
25
+ * An object exposing helper functions for creating UI elements.
26
+ *
27
+ * @example
28
+ * import kaplay from "kaplay";
29
+ * import kaplayUI from "kaplay-ui"
30
+ *
31
+ * const k = kaplay({
32
+ * plugins: [kaplayUI]
33
+ * })
34
+ *
35
+ * const label = k.addLabel("Hello!");
36
+ * const button = k.addTextButton("Start");
37
+ */
38
+
39
+ export default function kaplayUI(k: KAPLAYCtx): {
40
+ addTextButton: (
41
+ txt?: string,
42
+ width?: number,
43
+ height?: number,
44
+ ) => TextButtonElement;
45
+ addLabel: (txt?: string, width?: number, height?: number) => LabelComponent;
46
+ } {
47
+ return {
48
+ /**
49
+ * # Text button
50
+ * Creates a clickable game object containing a button with centered text.
51
+ *
52
+ * This helper provides sensible defaults for size, layout, and styling so you can
53
+ * quickly add UI buttons to your KAPLAY game.
54
+ *
55
+ * ## Default Parameter Values
56
+ * - `txt`: `"Button"`
57
+ * - `width`: `200`
58
+ * - `height`: `100`
59
+ *
60
+ * ## Default Styling
61
+ * The button object is created with:
62
+ * - `k.outline(3)`
63
+ * - `k.pos(0, 0)`
64
+ * - `k.anchor("topleft")`
65
+ * - `k.area()`
66
+ *
67
+ * @param {string} [txt="Button"]
68
+ * The text label displayed at the center of the button.
69
+ *
70
+ * @param {number} [width=200]
71
+ * Width of the button in pixels.
72
+ *
73
+ * @param {number} [height=100]
74
+ * Height of the button in pixels.
75
+ *
76
+ * @returns {TextButton}
77
+ * A KAPLAY Game Object representing the button with centered text.
78
+ *
79
+ * @example
80
+ * const playBtn = addTextButton("Play");
81
+ * playBtn.onClick(() => console.log("Play clicked!"));
82
+ */
83
+ addTextButton: (
84
+ txt: string = "Settings",
85
+ width: number = 150,
86
+ height: number = 60,
87
+ ): TextButtonElement => createTextButton(k, txt, width, height),
88
+ /**
89
+ * # Label
90
+ * Creates a simple text container with a background and layout box.
91
+ *
92
+ * Labels are lightweight UI elements used to display non-interactive text
93
+ * such as titles, descriptions, or dynamic info (scores, status, etc.).
94
+ *
95
+ * ## Default Parameter Values
96
+ * - `txt`: `""`
97
+ * - `width`: `160`
98
+ * - `height`: `96`
99
+ *
100
+ * ## Default Styling
101
+ * A label includes:
102
+ * - `k.color(0, 0, 0)`
103
+ * - `k.opacity(0.7)`
104
+ * - `k.anchor("topleft")`
105
+ *
106
+ * @param {string} [txt=""]
107
+ * The text content shown inside the label.
108
+ *
109
+ * @param {number} [width=160]
110
+ * Width of the label container in pixels.
111
+ *
112
+ * @param {number} [height=96]
113
+ * Height of the label container in pixels.
114
+ *
115
+ * @returns {LabelElement}
116
+ * A KAPLAY game object representing a text label.
117
+ *
118
+ * @example
119
+ * let score = 0
120
+ * const scoreLabel = addLabel(`Score: ${score}`);
121
+ *
122
+ * k.wait(2, () => {
123
+ * score++:
124
+ * scoreLabel.children[0].text = `Score: ${score}`
125
+ * })
126
+ */
127
+ addLabel: (
128
+ txt: string = "",
129
+ width: number = 160,
130
+ height: number = 96,
131
+ ): LabelComponent => createLabel(k, txt, width, height),
132
+ };
133
+ }
Binary file
Binary file
package/inputs/index.js DELETED
@@ -1,226 +0,0 @@
1
- import "kaplay/global";
2
-
3
- import cbIcon from "../assets/cb-icon.png";
4
-
5
- /**
6
- * Makes button with centered text
7
- * @param {string} txt - Button text to display
8
- * @param {number} x - The x postion to set (default is 0).
9
- * @param {number} y - The x postion to set (default is 0)
10
- * @param {number} width - Width of button (default is 100)
11
- * @param {number} height - Height of button (default is 50)
12
- * @param {number} btnRadius - Border radius of button (default is 8)
13
- * @param {number} btnOutline - Button outline (default is 1)
14
- * @param {number} txtSize - Text size of button (default is 15)
15
- * @returns {GameObj}
16
- */
17
- export const makeTextButton = (
18
- txt,
19
- x = 0,
20
- y = 0,
21
- width = 100,
22
- height = 50,
23
- btnRadius = 8,
24
- btnOutline = 1,
25
- txtSize = 15
26
- ) => {
27
- // Make button
28
- const btn = make([
29
- rect(width, height, { radius: btnRadius }),
30
- pos(x, y),
31
- area(),
32
- scale(1),
33
- outline(btnOutline),
34
- color(255, 255, 255),
35
- ]);
36
-
37
- // Make button text
38
- const btnText = make([
39
- text(txt, { size: txtSize }),
40
- pos(width / 2, height / 2),
41
- anchor("center"),
42
- color(0, 0, 0),
43
- ]);
44
-
45
- // Add text to button
46
- btn.add(btnText);
47
-
48
- return btn;
49
- };
50
-
51
- /**
52
- * Makes toggle
53
- * @param {number} x - The x postion to set (default is 0)
54
- * @param {number} y - The x postion to set (default is 0)
55
- * @param {number} width - Toggle width (default is 50)
56
- * @param {number} height - Toggle height (default is 25)
57
- * @returns {GameObj}
58
- */
59
- export const makeToggle = (x = 0, y = 0, width = 50, height = 25) => {
60
- // Toggle base
61
- const toggle = make([
62
- rect(width, height, { radius: height / 2 }),
63
- pos(x, y),
64
- color(169, 169, 169),
65
- area(),
66
- {
67
- toggled: false,
68
- },
69
- ]);
70
-
71
- // Toggle button
72
- const toggleBtn = make([
73
- circle(height * 0.4),
74
- color(WHITE),
75
- pos(height / 2, height / 2),
76
- ]);
77
-
78
- // Add button to toggleBase
79
- toggle.add(toggleBtn);
80
-
81
- toggle.onClick(() => {
82
- if (toggle.toggled) {
83
- toggle.use(color(169, 169, 169));
84
- toggleBtn.use(pos(height / 2, height / 2));
85
- toggle.toggled = false;
86
- } else {
87
- toggle.use(color(148, 188, 236));
88
- toggleBtn.use(pos(width - height / 2, height / 2));
89
- toggle.toggled = true;
90
- }
91
- });
92
-
93
- return toggle;
94
- };
95
-
96
- /**
97
- * Makes checkbox
98
- * @param {number} x - The x postion to set (default is 0)
99
- * @param {number} y - The x postion to set (default is 0)
100
- * @param {number} width - Checkbox width (default is 25)
101
- * @param {number} height - Checkbox height (default is 25)
102
- * @returns {GameObj}
103
- */
104
- export const makeCheckbox = (x = 0, y = 0, width = 25, height = 25) => {
105
- loadSprite("cb", cbIcon);
106
-
107
- const checkbox = make([
108
- rect(width, height, { radius: 4 }),
109
- pos(x, y),
110
- color(255, 255, 255),
111
- outline(1),
112
- area(),
113
- {
114
- checked: false,
115
- },
116
- ]);
117
-
118
- const checkedIcon = make([sprite("cb", { width, height }), area()]);
119
-
120
- checkbox.onClick(() => {
121
- if (checkbox.checked) {
122
- checkbox.use(color(255, 255, 255));
123
- checkbox.remove(checkedIcon);
124
- checkbox.checked = false;
125
- } else {
126
- checkbox.use(color(148, 188, 236));
127
- checkbox.add(checkedIcon);
128
- checkbox.checked = true;
129
- }
130
- });
131
-
132
- return checkbox;
133
- };
134
-
135
- /**
136
- * Makes switch
137
- * @param {number} x - Switch x postion (default is 0)
138
- * @param {number} y - Switch y postion (default is 0)
139
- * @param {number} width - Switch width (default is 50)
140
- * @param {number} height - Switch height (default is 25)
141
- * @returns {GameObj}
142
- */
143
- export const makeSwitch = (x = 0, y = 0, width = 50, height = 25) => {
144
- // Make switch base
145
- const switchBase = make([
146
- rect(width, height),
147
- pos(x, y),
148
- opacity(0),
149
- area(),
150
- {
151
- switched: false,
152
- },
153
- ]);
154
-
155
- // Make switch line
156
- const switchLine = make([
157
- rect(width, height / 2, { radius: height / 4 }),
158
- color(169, 169, 169),
159
- pos(0, height / 2 - height / 4),
160
- area(),
161
- ]);
162
-
163
- // Make switch circle
164
- const switchCircle = make([circle(height / 2), pos(height / 2, height / 2)]);
165
-
166
- // On click
167
- switchBase.onClick(() => {
168
- if (switchBase.switched) {
169
- switchLine.use(color(169, 169, 169));
170
- switchCircle.use(color(255, 255, 255));
171
- switchCircle.use(pos(height / 2, height / 2));
172
- switchBase.switched = false;
173
- } else {
174
- switchLine.use(color(148, 188, 236));
175
- switchCircle.use(color(24, 118, 210));
176
- switchCircle.use(pos(width - switchCircle.radius, height / 2));
177
- switchBase.switched = true;
178
- }
179
- });
180
-
181
- // Add line and circle
182
- switchBase.add(switchLine);
183
- switchBase.add(switchCircle);
184
-
185
- return switchBase;
186
- };
187
-
188
- /**
189
- * Makes text input
190
- * @param {number} x - Text input x postion (default is 0)
191
- * @param {number} y - Text input y postion (default is 0)
192
- * @param {number} width - Text input width (default is 400)
193
- * @param {number} txtSize - Text input text size (default is 15)
194
- * @param {number} pad - Text input padding (default is 10)
195
- * @param {boolean} hasFocus - Text input focus (default is true)
196
- * @returns {GameObj}
197
- */
198
- export const makeTextInput = (
199
- x = 0,
200
- y = 0,
201
- width = 400,
202
- txtSize = 15,
203
- pad = 10,
204
- hasFocus = true
205
- ) => {
206
- // Text input container
207
- const inputBase = make([
208
- rect(width, txtSize + pad * 2),
209
- pos(x, y),
210
- area(),
211
- outline(1),
212
- ]);
213
-
214
- // Text input
215
- const input = make([
216
- text("", { width: width - pad * 2, size: txtSize }),
217
- textInput(hasFocus),
218
- color(BLACK),
219
- pos(pad, pad),
220
- area(),
221
- ]);
222
-
223
- inputBase.add(input);
224
-
225
- return inputBase;
226
- };