@ktjs/core 0.18.10 → 0.19.1

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
@@ -1,378 +1,453 @@
1
- # @ktjs/core
2
-
3
1
  <img src="https://raw.githubusercontent.com/baendlorel/kt.js/dev/.assets/ktjs-0.0.1.svg" alt="KT.js Logo" width="150"/>
4
2
 
5
- [![npm version](https://img.shields.io/npm/v/@ktjs/core.svg)](https://www.npmjs.com/package/@ktjs/core)
3
+ [![npm version](https://img.shields.io/npm/v/kt.js.svg)](https://www.npmjs.com/package/kt.js) [![license](https://img.shields.io/github/license/baendlorel/kt.js.svg)](https://github.com/baendlorel/kt.js/blob/main/LICENSE)
6
4
 
7
- > 📦 Part of [KT.js](https://github.com/baendlorel/kt.js) - A simple and easy-to-use web framework that never re-renders.
5
+ [CHANGLOG✨](CHANGELOG.md)
8
6
 
9
- Core DOM manipulation utilities for KT.js framework with built-in JSX/TSX support.
7
+ ## What's New (v0.19.x)
10
8
 
11
- ## Overview
9
+ - Release `0.19.0` (2026-01-31): build & packaging fixes, TypeScript and lint cleanups, MUI-focused fixes (JSX handling, `jsxImportSource` set to `@ktjs/core`, radio/checkbox/TextField fixes), and repo cleanup (removed alias detection, moved shared utilities into `shared`). See the full details in the CHANGELOG.
12
10
 
13
- `@ktjs/core` is the foundation of KT.js, providing the essential `h` function and DOM utilities for building web applications with direct DOM manipulation. It emphasizes performance, type safety, and minimal abstraction over native DOM APIs.
11
+ > Note: This framework is still under development. APIs, type declarations, and other parts **may change frequently**. If you use it, please watch for updates in the near future. Feel free to mail me if you have any questions!
14
12
 
15
- **Current Version:** 0.18.1
13
+ KT.js is a tiny DOM utility focused on direct DOM manipulation. It favors not forcing re-renders and aims to keep DOM updates to the absolute minimum for maximum performance.
16
14
 
17
- ## Features
15
+ For more awesome packages, check out [my homepage💛](https://baendlorel.github.io/?repoType=npm)
18
16
 
19
- - **`h` Function**: Create HTMLElements with a simple, flexible API
20
- - Support for attributes, content, and event handlers
21
- - `on:<eventName>` syntax for event handlers (e.g., `on:click`)
22
- - Function attributes automatically treated as event listeners
23
- - Full TypeScript support with intelligent type inference
24
- - **JSX/TSX Support**: Built-in JSX runtime (no separate package needed)
25
- - Zero virtual DOM - JSX compiles directly to `h()` function calls
26
- - Full HTML element type inference (`<button>` returns `HTMLButtonElement`)
27
- - Support for function components
28
- - `redraw()` method for controlled re-rendering
29
- - **k-if directive**: Conditional element creation with `k-if` attribute
30
- - Array children support for seamless list rendering
31
- - **KTFor Component**: Efficient list rendering with key-based optimization (v0.16.0)
32
- - Returns Comment anchor with `__kt_for_list__` array storing rendered elements
33
- - Auto-appends list items when anchor is added via `applyContent`
34
- - Key-based DOM reuse with customizable key function (default: identity)
35
- - Type-safe with full TypeScript generics support
36
- - **KTAsync Component**: Handle async components with ease
37
- - Automatic handling of Promise-based components
38
- - Seamless integration with JSX/TSX
39
- - Fallback placeholder during async loading
40
- - Type-safe async component support
41
- - **Redraw Mechanism**: Fine-grained control over component updates
42
- - Update props and children selectively
43
- - Efficient replacement strategy
44
- - Works with both native elements and function components
45
- - **Ref Enhancement**: Change event binding support for `ref` (v0.18.1)
46
- - New methods `addOnChange` and `removeOnChange` for listening to value changes
47
- - Automatically calls registered callbacks when `ref.value` is updated
48
- - When both old and new values are DOM nodes, automatically replaces old node with new one in the DOM
49
- - **DOM Utilities**: Helper functions for common DOM operations
50
- - Native method caching for performance
51
- - Symbol-based private properties for internal state
52
- - **Type-Safe**: Complete TypeScript definitions
53
- - Accurate HTMLElement type inference
54
- - Event handler type hints with proper event types
55
- - Support for custom attributes and properties
56
- - **ES5 Compatible**: Transpiled to ES5 for maximum browser compatibility
57
- - **Zero Dependencies**: Fully self-contained implementation
58
-
59
- ## Installation
17
+ ## Architecture
18
+
19
+ KT.js is now a **monorepo** containing multiple packages:
20
+
21
+ - **[kt.js](./packages/kt.js)**: Main entry package that re-exports all functionality
22
+ - **[@ktjs/core](./packages/core)**: Core DOM manipulation utilities and the `h` function. SX/TSX support with full TypeScript integration (included in kt.js package)
23
+ - **[@ktjs/router](./packages/router)**: Client-side routing with navigation guards (not included in kt.js package)
24
+ - **[@ktjs/shortcuts](./packages/shortcuts)**: Convenient shortcut functions for common operations
25
+
26
+ You can install the full package or individual packages as needed:
60
27
 
61
28
  ```bash
62
- pnpm add @ktjs/core
63
- ```
29
+ # Install the main package (includes core + jsx + shortcuts)
30
+ pnpm add kt.js
64
31
 
65
- ## Usage
32
+ # Or install individual packages
33
+ pnpm add @ktjs/core # Core DOM utilities (independent)
34
+ pnpm add @ktjs/router # Client-side router (independent)
35
+ pnpm add @ktjs/shortcuts # Shortcuts (requires @ktjs/core)
36
+ ```
66
37
 
67
- ### Basic Element Creation
38
+ ## Philosophy
68
39
 
69
- ```typescript
70
- import { h } from '@ktjs/core';
40
+ As a web framework, repeatedly creating a large number of variables and objects is unacceptable. So I created KT.js.
71
41
 
72
- // Simple element
73
- const div = h('div', { class: 'container' }, 'Hello World');
42
+ KT.js follows one rule: **full control of DOM and avoid unnecessary repainting**.
74
43
 
75
- // With multiple attributes
76
- const input = h('input', {
77
- type: 'text',
78
- placeholder: 'Enter text',
79
- value: 'initial',
80
- });
44
+ ## Key Features
81
45
 
82
- // Nested elements
83
- const card = h('div', { class: 'card' }, [
84
- h('h2', {}, 'Title'),
85
- h('p', {}, 'Description'),
86
- h('button', {}, 'Click me'),
87
- ]);
46
+ - **Monorepo Architecture**: Modular packages that can be installed independently or together
47
+ - **Tiny Bundle Size**: Minimal runtime overhead with aggressive tree-shaking
48
+ - **`h` function**: Create DOM elements with a simple, flexible API
49
+ - Shortcut functions for all HTML elements (`div`, `span`, `button`, etc.)
50
+ - Event handlers with `on:<eventName>` syntax or function attributes
51
+ - Full TypeScript support with intelligent type inference
52
+ - **JSX/TSX Support**: Full JSX syntax support with TypeScript integration
53
+ - Zero virtual DOM - JSX compiles directly to `h()` function calls
54
+ - Full HTML element type inference (`<button>` returns `HTMLButtonElement`)
55
+ - Support for `on:click` event handler syntax
56
+ - `redraw()` method for controlled component updates (v0.11+)
57
+ - `k-if` directive for conditional rendering (v0.14.6+)
58
+ - Array children support for seamless `.map()` integration (v0.14.1+)
59
+ - **List Rendering**: Efficient list rendering with `KTFor` component (v0.16.0+)
60
+ - Comment anchor with `__kt_for_list__` array property
61
+ - Key-based DOM reuse for minimal updates
62
+ - Auto-appends list items when anchor added to parent
63
+ - **Async Components**: Built-in support for Promise-based components
64
+ - `KTAsync` component for handling async operations
65
+ - Automatic placeholder management during loading
66
+ - Seamless integration with JSX/TSX syntax
67
+ - **Client-Side Router** (separate package):
68
+ - Hash-based routing only (simplified from v0.14.7+)
69
+ - Async navigation guards with Promise support
70
+ - Dynamic route parameters and query string parsing
71
+ - RouterView component for declarative routing
72
+ - Pure routing logic - no rendering, no dependencies
73
+ - **Shortcuts & Utilities**:
74
+ - `withDefaults`: Wrap element creation functions with default properties
75
+ - Convenient shorthand functions for common operations
76
+ - Form helpers and layout utilities
77
+ - **Full ES5 Compatibility**: Works in IE9+ and all modern browsers
78
+ - Transpiled to ES5 with no modern syntax
79
+ - Optional minimal Promise polyfill for older environments
80
+ - **Shared Runtime**: Efficient code sharing across packages with zero overhead
81
+
82
+ ## Getting started
83
+
84
+ Install via package managers:
88
85
 
89
- // Note: attr parameter must be an object
90
- // String className shorthand is NOT supported:
91
- // h('div', 'my-class') ❌ - This will throw an error
86
+ ```bash
87
+ npm install kt.js
88
+ # or
89
+ pnpm add kt.js
92
90
  ```
93
91
 
94
- ### Event Handlers
92
+ ```ts
93
+ import { h, div } from 'kt.js';
95
94
 
96
- ```typescript
97
- import { h } from '@ktjs/core';
98
-
99
- // on: prefixed attribute (event handler)
100
- const button1 = h(
101
- 'button',
102
- {
103
- 'on:click': () => alert('Clicked!'),
104
- },
105
- 'Button 1',
106
- );
95
+ const container = div('container', [div('header'), div('body', 'something'), div('footer')]);
96
+ const app = h('section', { id: 'app' }, container);
97
+ ```
107
98
 
108
- // Function attribute (also treated as event listener)
109
- const button2 = h(
110
- 'button',
111
- {
112
- click: (e) => console.log('Event:', e),
113
- 'data-id': '123', // Regular attribute
114
- },
115
- 'Button 2',
116
- );
99
+ This will create the following DOM structure:
117
100
 
118
- // Both regular and event handler for same name
119
- const input = h('input', {
120
- value: 'initial', // Regular attribute
121
- 'on:change': (e) => console.log('Changed'), // Event listener
122
- });
101
+ ```html
102
+ <section id="app">
103
+ <div class="container">
104
+ <div class="header"></div>
105
+ <div class="body">something</div>
106
+ <div class="footer"></div>
107
+ </div>
108
+ </section>
123
109
  ```
124
110
 
125
- ### JSX/TSX Support
111
+ ### Using JSX/TSX
126
112
 
127
- ```tsx
128
- import { h } from '@ktjs/core';
113
+ KT.js now has full JSX support! With the `@ktjs/jsx` package (included in the main `kt.js` package), you can write components using familiar JSX syntax:
129
114
 
130
- // Configure tsconfig.json
115
+ **TypeScript Configuration** (`tsconfig.json`):
116
+
117
+ ```json
131
118
  {
132
119
  "compilerOptions": {
133
120
  "jsx": "react-jsx",
134
- "jsxImportSource": "@ktjs/core"
121
+ "jsxImportSource": "kt.js"
135
122
  }
136
123
  }
137
-
138
- // Use JSX syntax
139
- const App = () => (
140
- <div class="app">
141
- <h1>Hello KT.js</h1>
142
- <button on:click={() => alert('Hi')}>Click me</button>
143
- </div>
144
- );
145
-
146
- // Function components
147
- const Greeting = ({ name }: { name: string }) => (
148
- <div class="greeting">Hello, {name}!</div>
149
- );
150
-
151
- const app = <Greeting name="World" />;
152
124
  ```
153
125
 
154
- ### Conditional Rendering with k-if (v0.14.6+)
155
-
156
- The `k-if` directive allows conditional element creation:
126
+ **Basic JSX Example**:
157
127
 
158
128
  ```tsx
159
- import { h } from '@ktjs/core';
160
-
161
- // Element will only be created if condition is true
162
- const isVisible = true;
163
- const element = <div k-if={isVisible}>This will be rendered</div>;
129
+ import { jsx } from 'kt.js';
164
130
 
165
- // Element will not be created if condition is false
166
- const isHidden = false;
167
- const hidden = <div k-if={isHidden}>This will NOT be rendered</div>;
168
- // hidden will be undefined/null
131
+ function Counter() {
132
+ const count = 0;
169
133
 
170
- // Practical example
171
- const UserProfile = ({ user, isLoggedIn }: { user: any; isLoggedIn: boolean }) => (
172
- <div>
173
- <h1>User Profile</h1>
174
- <div k-if={isLoggedIn}>
175
- <p>Welcome, {user.name}!</p>
176
- <button>Logout</button>
134
+ return (
135
+ <div class="counter">
136
+ <h1>Counter: {count}</h1>
137
+ <button on:click={() => console.log('Clicked!')}>Increment</button>
177
138
  </div>
178
- <div k-if={!isLoggedIn}>
179
- <p>Please log in to continue</p>
180
- <button>Login</button>
181
- </div>
182
- </div>
183
- );
184
- ```
185
-
186
- **Note**: `k-if` is evaluated **once** at element creation time. It's not reactive - if you need dynamic visibility, use CSS or manually recreate the element.
139
+ );
140
+ }
187
141
 
188
- ### Redraw Mechanism (v0.11+)
142
+ // JSX compiles to direct h() function calls - no virtual DOM!
143
+ const counterElement = <Counter />;
144
+ ```
189
145
 
190
- The `redraw()` method allows you to update components efficiently:
146
+ **Event Handling with @ Syntax**:
191
147
 
192
148
  ```tsx
193
- import { h, KTHTMLElement } from '@ktjs/core';
149
+ function App() {
150
+ const handleClick = () => alert('Button clicked!');
194
151
 
195
- // With JSX - get element with redraw method
196
- const counter = (<button on:click={() => counter.redraw({ count: count + 1 })}>Count: {0}</button>) as KTHTMLElement;
152
+ return (
153
+ <div>
154
+ <button on:click={handleClick}>Click me</button>
155
+ </div>
156
+ );
157
+ }
158
+ ```
197
159
 
198
- // Function component with redraw
199
- const Counter = ({ count = 0 }: { count?: number }) => (
200
- <div>
201
- <div>Count: {count}</div>
202
- <button on:click={() => element.redraw({ count: count + 1 })}>Increment</button>
203
- </div>
204
- );
160
+ **Type Safety**:
205
161
 
206
- const element = (<Counter />) as KTHTMLElement;
162
+ ```tsx
163
+ // TypeScript knows this is an HTMLButtonElement
164
+ const button: HTMLButtonElement = <button>Click</button>;
207
165
 
208
- // Update props manually
209
- element.redraw({ count: 10 });
166
+ // TypeScript knows this is an HTMLInputElement
167
+ const input: HTMLInputElement = <input type="text" value="hello" />;
210
168
 
211
- // Update children (for native elements)
212
- const div = (<div>Old content</div>) as KTHTMLElement;
213
- div.redraw(undefined, 'New content');
169
+ // TypeScript provides autocomplete for HTML attributes
170
+ const div: HTMLDivElement = <div className="container" id="main" />;
214
171
  ```
215
172
 
216
- ### List Rendering with KTFor (v0.16.0)
173
+ **Important Notes**:
217
174
 
218
- The `KTFor` component provides efficient list rendering with key-based DOM reuse:
175
+ - KT.js JSX has **no Fragment support** - we don't have a Fragment concept
176
+ - JSX compiles directly to `h()` function calls - **zero virtual DOM overhead**
177
+ - Use `on:click` syntax for event handlers to avoid conflicts with existing attributes
178
+ - All JSX elements have proper HTML element type inference in TypeScript
179
+ - Use `k-if` attribute for conditional rendering (v0.14.6+)
180
+ - Children can be arrays for easy `.map()` integration (v0.14.1+)
219
181
 
220
- ```tsx
221
- import { KTFor } from '@ktjs/core';
182
+ **Conditional Rendering with k-if** (v0.14.6+):
222
183
 
223
- interface Todo {
224
- id: number;
225
- text: string;
226
- done: boolean;
184
+ ```tsx
185
+ import { jsx } from 'kt.js';
186
+
187
+ function UserProfile({ user, isLoggedIn }: { user: any; isLoggedIn: boolean }) {
188
+ return (
189
+ <div>
190
+ <h1>Profile</h1>
191
+ {/* Element only created if condition is true */}
192
+ <div k-if={isLoggedIn}>
193
+ <p>Welcome, {user.name}!</p>
194
+ <button>Logout</button>
195
+ </div>
196
+ {/* Element only created if condition is true */}
197
+ <div k-if={!isLoggedIn}>
198
+ <p>Please log in</p>
199
+ <button>Login</button>
200
+ </div>
201
+ </div>
202
+ );
227
203
  }
204
+ ```
228
205
 
229
- let todos: Todo[] = [
230
- { id: 1, text: 'Buy milk', done: false },
231
- { id: 2, text: 'Write code', done: true },
232
- ];
233
-
234
- // Create optimized list with key-based reuse
235
- const todoList = (
236
- <KTFor
237
- list={todos}
238
- key={(item) => item.id} // Optional, defaults to identity function
239
- map={(item, index) => (
240
- <div class={`todo ${item.done ? 'done' : ''}`}>
241
- <input type="checkbox" checked={item.done} />
242
- <span>{item.text}</span>
243
- <button on:click={() => deleteTodo(item.id)}>Delete</button>
244
- </div>
245
- )}
246
- />
247
- );
206
+ **Array Children Support** (v0.14.1+):
248
207
 
249
- // Add to DOM - anchor comment + __kt_for_list__ items are appended
250
- document.body.appendChild(todoList);
208
+ ```tsx
209
+ import { jsx } from 'kt.js';
210
+
211
+ function TodoList({ todos }: { todos: string[] }) {
212
+ return (
213
+ <div>
214
+ <h2>Todo List</h2>
215
+ <ul>
216
+ {/* Map arrays directly as children */}
217
+ {todos.map((todo) => (
218
+ <li>{todo}</li>
219
+ ))}
220
+ </ul>
221
+ </div>
222
+ );
223
+ }
251
224
 
252
- // Update the list - only changed items are updated
253
- todos = [...todos, { id: 3, text: 'New task', done: false }];
254
- todoList.redraw({ list: todos });
225
+ // Mix mapped elements with other elements
226
+ function MixedList({ items }: { items: string[] }) {
227
+ return (
228
+ <ul>
229
+ <li>Header Item</li>
230
+ {items.map((item) => (
231
+ <li>{item}</li>
232
+ ))}
233
+ <li>Footer Item</li>
234
+ </ul>
235
+ );
236
+ }
255
237
  ```
256
238
 
257
- **How it works:**
239
+ ### Async Components with KTAsync
258
240
 
259
- - Returns Comment anchor (`<!-- kt-for -->`) with `__kt_for_list__` array property
260
- - When appended via `applyContent`, anchor and all list items are added to DOM
261
- - Uses key-based diff to reuse DOM nodes on `redraw()`
262
- - Only adds/removes/moves nodes that changed
241
+ KT.js provides built-in support for async components through the `KTAsync` component:
263
242
 
264
- ````
243
+ ```tsx
244
+ import { KTAsync, ref } from 'kt.js';
245
+
246
+ // Define an async component that returns a Promise<HTMLElement>
247
+ const AsyncUserCard = function () {
248
+ return fetch('/api/user')
249
+ .then((res) => res.json())
250
+ .then((user) => (
251
+ <div class="user-card">
252
+ <h2>{user.name}</h2>
253
+ <p>{user.email}</p>
254
+ </div>
255
+ ));
256
+ };
265
257
 
266
- ### Array Children Support (v0.14.1+)
258
+ // Use KTAsync to handle the async component
259
+ function App() {
260
+ return (
261
+ <div class="app">
262
+ <h1>User Profile</h1>
263
+ <KTAsync component={AsyncUserCard} />
264
+ </div>
265
+ );
266
+ }
267
267
 
268
- Children can now be arrays for easier list rendering:
268
+ // The component starts with a placeholder comment node
269
+ // When the Promise resolves, it automatically replaces with the actual element
270
+ ```
269
271
 
270
- ```tsx
271
- import { h } from '@ktjs/core';
272
-
273
- // Map arrays directly as children
274
- const items = ['Apple', 'Banana', 'Orange'];
275
- const list = (
276
- <ul>
277
- {items.map((item) => (
278
- <li>{item}</li>
279
- ))}
280
- </ul>
281
- );
272
+ **How KTAsync works:**
282
273
 
283
- // Mix mapped elements with other elements
284
- const TodoList = ({ todos }: { todos: string[] }) => (
285
- <div>
286
- <h2>Todo List</h2>
287
- <ul>
288
- {todos.map((todo) => (
289
- <li>{todo}</li>
290
- ))}
291
- <li>
292
- <button>Add More</button>
293
- </li>
294
- </ul>
295
- </div>
296
- );
297
- ````
274
+ 1. Creates a placeholder comment node (`ktjs-suspense-placeholder`) immediately
275
+ 2. Calls your component function (which should return a `Promise<HTMLElement>` or `HTMLElement`)
276
+ 3. When the Promise resolves, automatically replaces the placeholder with the resolved element
277
+ 4. If your component returns a non-Promise value, it's used directly without async handling
298
278
 
299
- ### Async Components
279
+ **Example with dynamic updates:**
300
280
 
301
- ```typescript
302
- import { KTAsync, h } from '@ktjs/core';
281
+ ```tsx
282
+ const DynamicContent = function () {
283
+ const count = ref(0);
284
+ const container = (
285
+ <div>
286
+ <p>Count: {count}</p>
287
+ <button on:click={() => count.value++}>Increment</button>
288
+ </div>
289
+ );
303
290
 
304
- // Define an async component that returns a Promise
305
- const AsyncComponent = () => {
291
+ // Simulate async data loading
306
292
  return new Promise<HTMLElement>((resolve) => {
307
- setTimeout(() => {
308
- const element = h('div', { class: 'loaded' }, 'Content loaded!');
309
- resolve(element);
310
- }, 1000);
293
+ setTimeout(() => resolve(container), 500);
311
294
  });
312
295
  };
313
296
 
314
- // Use KTAsync to handle the async component
315
- const container = h('div', {}, [
316
- h('h1', {}, 'Loading...'),
317
- KTAsync({ component: AsyncComponent }),
318
- ]);
319
-
320
- // With JSX/TSX
321
- const App = () => (
297
+ // Usage
298
+ const app = (
322
299
  <div>
323
- <h1>Loading...</h1>
324
- <KTAsync component={AsyncComponent} />
300
+ <h1>Loading async content...</h1>
301
+ <KTAsync component={DynamicContent} />
325
302
  </div>
326
303
  );
304
+ ```
327
305
 
328
- // With custom placeholder
329
- const AppWithSkeleton = () => (
330
- <div>
331
- <KTAsync
332
- component={AsyncComponent}
333
- skeleton={<div class="skeleton">Loading...</div>}
334
- />
335
- </div>
306
+ If you give a function in attributes, it will be treated as an event listener, and the key will be considered as the event name. `@<eventName>` will also be considered as the handler to avoid conflicts with existing attributes:
307
+
308
+ ```ts
309
+ const button = btn(
310
+ {
311
+ dblclick: '22',
312
+ 'on:dblclick': function trueHandler() {
313
+ /* ... */
314
+ },
315
+ },
316
+ 'Click me',
336
317
  );
318
+
319
+ // This is equivalent to:
320
+ const button = btn(undefined, 'Click me');
321
+ button.setAttribute('dblclick', '22');
322
+ button.addEventListener('click', () => alert('Clicked!'));
323
+ button.addEventListener('dblclick', function trueHandler() {
324
+ /* ... */
325
+ });
337
326
  ```
338
327
 
339
- **How it works:**
328
+ ### Working with CSS-in-JS Libraries
340
329
 
341
- - `KTAsync` creates a placeholder (comment node or custom skeleton) immediately
342
- - When the Promise resolves, it automatically replaces the placeholder with the actual element
343
- - If the component returns a non-Promise value, it's used directly
344
- - No manual DOM manipulation needed - just return a Promise from your component
330
+ KT.js works seamlessly with CSS-in-JS libraries like `@emotion/css`:
345
331
 
346
- ## API Reference
332
+ ```ts
333
+ import { css } from '@emotion/css';
334
+ import { h, div } from 'kt.js';
347
335
 
348
- ### `h(tag, attributes?, content?)`
336
+ const className = css`
337
+ color: red;
338
+ font-size: 20px;
339
+ `;
349
340
 
350
- Creates an HTMLElement with the specified tag, attributes, and content.
341
+ // Pass class name as attribute
342
+ h('div', { class: className }, 'Styled text');
351
343
 
352
- **Parameters:**
344
+ // Or as the first string argument
345
+ div(className, 'Styled text');
346
+ ```
353
347
 
354
- - `tag` (string): HTML tag name (e.g., 'div', 'span', 'button')
355
- - `attributes` (object, optional): Element attributes and event handlers. **Must be an object** - string className shorthand is not supported.
356
- - `content` (string | HTMLElement | Array, optional): Element content
348
+ ### Using Shortcuts with Default Values
357
349
 
358
- **Returns:** HTMLElement
350
+ The `withDefaults` function allows you to create element factories with predefined properties:
359
351
 
360
- ## Type System
352
+ ```ts
353
+ import { withDefaults, div, button } from 'kt.js';
361
354
 
362
- The package includes comprehensive TypeScript definitions:
355
+ // Create a styled div factory
356
+ const card = withDefaults(div, { class: 'card' });
357
+ const blueCard = withDefaults(card, { style: 'background: blue' });
363
358
 
364
- - `HTMLTag`: Union type of all valid HTML tag names
365
- - `KTAttribute`: Attribute object type for element attributes and event handlers
366
- - `KTRawAttr`: Union type for raw attribute parameter (`KTAttribute | null | undefined | '' | false`)
367
- - `KTRawContent`: Valid content types (string, Element, Array, Promise, etc.)
368
- - Event handler types with proper event object types
359
+ // Use them
360
+ const myCard = card('card-body', 'Content'); // <div class="card"><div class="card-body">Content</div></div>
361
+ const myBlueCard = blueCard('title', 'Blue!'); // <div class="card" style="background: blue"><div class="title">Blue!</div></div>
362
+ ```
369
363
 
370
- ## Performance Considerations
364
+ ## Router
365
+
366
+ The router is available as a separate package `@ktjs/router`:
367
+
368
+ ```ts
369
+ import { createRouter } from '@ktjs/router';
370
+ import { div, h1 } from 'kt.js';
371
+
372
+ const router = createRouter({
373
+ routes: [
374
+ {
375
+ path: '/',
376
+ name: 'home',
377
+ beforeEnter: (to) => {
378
+ // Render your page here
379
+ document.getElementById('app')!.innerHTML = '';
380
+ document.getElementById('app')!.appendChild(div({}, [h1({}, 'Home Page')]));
381
+ },
382
+ },
383
+ {
384
+ path: '/user/:id',
385
+ name: 'user',
386
+ beforeEnter: (to) => {
387
+ // Route-specific guard and rendering
388
+ console.log('Entering user page');
389
+ document.getElementById('app')!.innerHTML = '';
390
+ document.getElementById('app')!.appendChild(div({}, [h1({}, `User ${to.params.id}`)]));
391
+ return true;
392
+ },
393
+ },
394
+ ],
395
+ beforeEach: async (to, from) => {
396
+ // Global navigation guard - return false to block navigation
397
+ console.log('Navigating to:', to.path);
398
+ return true;
399
+ },
400
+ afterEach: (to) => {
401
+ // Called after successful navigation
402
+ document.title = to.name || to.path;
403
+ },
404
+ onError: (error) => {
405
+ console.error('Router error:', error);
406
+ },
407
+ });
408
+
409
+ // Navigate programmatically
410
+ router.push('/user/123');
411
+ router.push('/user/456?page=2');
412
+
413
+ // Navigate by route name
414
+ router.push({ name: 'user', params: { id: '789' } });
415
+
416
+ // Get current route
417
+ console.log(router.current?.path, router.current?.params, router.current?.query);
418
+ ```
419
+
420
+ ### Router Features
371
421
 
372
- - Native DOM methods are cached for repeated use
373
- - Symbol-based properties avoid prototype pollution
374
- - Minimal object creation in hot paths
375
- - Tree-shakeable - only import what you use
422
+ - **Hash-based Routing Only** (v0.14.7+): Uses URL hash for client-side navigation (`#/path`)
423
+ - **Dynamic Parameters**: Support for dynamic route segments (`/user/:id`)
424
+ - **Query Strings**: Automatic parsing of query parameters (`?key=value`)
425
+ - **Named Routes**: Navigate using route names instead of paths
426
+ - **Async Navigation Guards**:
427
+ - `beforeEach`: Global guard before navigation (async)
428
+ - `beforeEnter`: Per-route guard (can also be used for rendering, async)
429
+ - `afterEach`: Global hook after navigation
430
+ - All guards support Promise-based async operations
431
+ - Guards can return `false` to cancel, string/object to redirect
432
+ - `GuardLevel` for fine-grained control over guard execution
433
+ - **Error Handling**: `onError` and `onNotFound` callbacks
434
+ - **Optimized Performance**: Pre-flattened routes and efficient matching algorithm
435
+ - **Zero Dependencies**: Fully self-contained router implementation (does not require `@ktjs/core` for runtime, only for TypeScript types)
436
+ - **Pure Routing**: No rendering logic - you control the DOM
437
+ - **Automatic Initialization**: Router auto-initializes on creation (v0.14.7+)
438
+
439
+ ## Browser Compatibility
440
+
441
+ KT.js is transpiled to ES5 and works in all modern browsers as well as legacy browsers including IE9+.
442
+
443
+ ### Promise Polyfill
444
+
445
+ For environments without native `Promise` support (like IE).
446
+
447
+ ```js
448
+ import 'some promise polyfill'; // Will fallback to sync version if Promise is not available
449
+ import { h, div, createRouter } from 'kt.js';
450
+ ```
376
451
 
377
452
  ## License
378
453