kempo-ui 0.4.2 → 0.4.4
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/.claude/skills/get-icon/SKILL.md +144 -0
- package/.claude/skills/highlight-code/SKILL.md +62 -0
- package/.claude/skills/new-component/SKILL.md +362 -0
- package/.github/skills/get-icon/SKILL.md +4 -4
- package/.github/skills/highlight-code/SKILL.md +51 -20
- package/.github/skills/new-component/SKILL.md +183 -152
- package/AGENTS.md +2 -2
- package/dist/components/Calendar.js +170 -0
- package/dist/components/SpeechToText.js +60 -0
- package/dist/components/TextToSpeech.js +56 -0
- package/dist/components/Time.js +37 -0
- package/docs/components/accordion.html +16 -0
- package/docs/components/aside.html +16 -0
- package/docs/components/calendar.html +720 -0
- package/docs/components/card.html +16 -0
- package/docs/components/code-editor.html +16 -0
- package/docs/components/color-picker.html +16 -0
- package/docs/components/combobox.html +16 -0
- package/docs/components/content-slider.html +16 -0
- package/docs/components/context.html +16 -0
- package/docs/components/dialog.html +16 -0
- package/docs/components/dropdown.html +16 -0
- package/docs/components/filter-list.html +16 -0
- package/docs/components/focus-capture.html +16 -0
- package/docs/components/html-editor.html +16 -0
- package/docs/components/hybrid-component.html +16 -0
- package/docs/components/icon.html +16 -0
- package/docs/components/import.html +16 -0
- package/docs/components/light-component.html +16 -0
- package/docs/components/nav-spacer.html +16 -0
- package/docs/components/nav.html +16 -0
- package/docs/components/photo-viewer.html +16 -0
- package/docs/components/progress.html +16 -0
- package/docs/components/resize.html +16 -0
- package/docs/components/shadow-component.html +16 -0
- package/docs/components/show-more.html +16 -0
- package/docs/components/slider.html +16 -0
- package/docs/components/sortable.html +16 -0
- package/docs/components/speech-to-text.html +717 -0
- package/docs/components/spinner.html +16 -0
- package/docs/components/split.html +16 -0
- package/docs/components/table.html +16 -0
- package/docs/components/tableControls.html +16 -0
- package/docs/components/tableCustomFields.html +16 -0
- package/docs/components/tableFetchRecords.html +16 -0
- package/docs/components/tableFieldSortHide.html +16 -0
- package/docs/components/tablePagination.html +16 -0
- package/docs/components/tablePlaceholder.html +16 -0
- package/docs/components/tableRecordEditing.html +16 -0
- package/docs/components/tableRecordFiltering.html +16 -0
- package/docs/components/tableRecordHiding.html +16 -0
- package/docs/components/tableRecordSearching.html +16 -0
- package/docs/components/tableRecordSelection.html +16 -0
- package/docs/components/tableRowControls.html +16 -0
- package/docs/components/tableServerSync.html +16 -0
- package/docs/components/tableSorting.html +16 -0
- package/docs/components/tabs.html +16 -0
- package/docs/components/tags.html +16 -0
- package/docs/components/text-to-speech.html +668 -0
- package/docs/components/theme-select.html +16 -0
- package/docs/components/theme-switcher.html +16 -0
- package/docs/components/time.html +670 -0
- package/docs/components/timestamp.html +16 -0
- package/docs/components/toast.html +16 -0
- package/docs/components/toggle.html +16 -0
- package/docs/components/tree.html +16 -0
- package/docs/icons/mic.svg +1 -0
- package/docs/icons/record_voice_over.svg +1 -0
- package/docs/icons/stop.svg +1 -0
- package/docs/index.html +40 -0
- package/docs/src/components/Calendar.js +170 -0
- package/docs/src/components/SpeechToText.js +60 -0
- package/docs/src/components/TextToSpeech.js +56 -0
- package/docs/src/components/Time.js +37 -0
- package/docs/utils/context.html +16 -0
- package/docs/utils/cookie.html +16 -0
- package/docs/utils/debounce.html +16 -0
- package/docs/utils/drag.html +16 -0
- package/docs/utils/elevation.html +16 -0
- package/docs/utils/formatTimestamp.html +16 -0
- package/docs/utils/object.html +16 -0
- package/docs/utils/propConverters.html +16 -0
- package/docs/utils/string.html +16 -0
- package/docs/utils/theme.html +16 -0
- package/docs/utils/toTitleCase.html +16 -0
- package/docs/utils/type.html +16 -0
- package/docs/utils/wait.html +16 -0
- package/docs-src/components/calendar.page.html +254 -0
- package/docs-src/components/speech-to-text.page.html +251 -0
- package/docs-src/components/text-to-speech.page.html +202 -0
- package/docs-src/components/time.page.html +204 -0
- package/docs-src/index.page.html +24 -0
- package/docs-src/nav.fragment.html +16 -0
- package/icons/mic.svg +1 -0
- package/icons/record_voice_over.svg +1 -0
- package/icons/stop.svg +1 -0
- package/llms.txt +4 -0
- package/package.json +1 -1
- package/src/components/Calendar.js +473 -0
- package/src/components/SpeechToText.js +252 -0
- package/src/components/TextToSpeech.js +194 -0
- package/src/components/Time.js +137 -0
- package/tests/components/SpeechToText.browser-test.js +530 -0
- package/tests/components/TextToSpeech.browser-test.js +432 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: get-icon
|
|
3
|
+
description: Fetches icons from the Google Material Design Icon set, saves them to this project, and formats them. Use any time an icon is needed (explicit or implicit) — buttons, controls, actions, etc. ALWAYS check the local icons/ directory first before downloading.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Get Icon
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
Use this skill any time an icon is needed — whether the user explicitly asks for one or you determine that a UI element (button, control, action, etc.) would benefit from one. For example, if a user asks you to build a "bold" button for a WYSIWYG toolbar, you should proactively source an appropriate icon without being explicitly told to.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
Icons live in the `icons/` directory as SVG files. The project uses the **Material Symbols Outlined** style from Google's CDN. The workflow has three stages:
|
|
15
|
+
|
|
16
|
+
1. **Check local icons first** — the icon may already exist
|
|
17
|
+
2. **Find** the correct Material Symbols name if it doesn't
|
|
18
|
+
3. **Fetch, format, and save** using the script (directional handling is automatic)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Stage 1: Check Local Icons First
|
|
23
|
+
|
|
24
|
+
Before going to the internet, list the contents of `icons/` and look for an existing icon that fits the need (use `ls icons/` via Bash, or search with Glob).
|
|
25
|
+
|
|
26
|
+
- If a **clear match** exists (e.g. `icons/format_bold.svg` for a bold button), use it — no download needed.
|
|
27
|
+
- If a **reasonable match** exists for a directional icon (e.g. `icons/arrow.svg` for a left-arrow), use it with the appropriate `direction` attribute — no download needed.
|
|
28
|
+
- If **nothing fits**, proceed to Stage 2.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Stage 2: Find the Icon Name
|
|
33
|
+
|
|
34
|
+
If the user describes an icon but doesn't supply an exact name, search Google Material Symbols to find the right one.
|
|
35
|
+
|
|
36
|
+
Run the search script, which fetches icon names and tags from GitHub and caches them locally in `node_modules/.cache/kempo-ui/`. On subsequent runs it only re-downloads if the icon list has changed (SHA check — ~1KB request vs ~500KB full list). Search matches both icon names **and tags**, so searching `chevron` will also surface `keyboard_arrow_right` because it has a `chevron` tag:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm run listicons -- <search_term>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
```bash
|
|
44
|
+
npm run listicons -- bold
|
|
45
|
+
# → format_bold
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm run listicons -- chevron
|
|
50
|
+
# → chevron_right
|
|
51
|
+
# chevron_left
|
|
52
|
+
# keyboard_arrow_right
|
|
53
|
+
# keyboard_arrow_left
|
|
54
|
+
# ...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The results show **full icon names including direction suffixes** so you can see what variants are available. Pick the best match for the user's intent. If no results are returned, try different search terms (e.g. `type` instead of `font`, `text` instead of `word`).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Stage 3: Fetch, Format, and Save
|
|
62
|
+
|
|
63
|
+
Run the fetch script with the chosen icon name. The script automatically handles directional icons — if the name you pass is directional (has a `_left`, `_up`, `_down`, `_backward` suffix, or has no exact match but a `_right`/`_forward` variant exists), it will prompt:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
"chevron_left" is a directional icon. kempo-ui uses right direction only and applies CSS rotation.
|
|
67
|
+
Save right-facing variant as "chevron.svg"? (Y/n)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm run geticon -- <icon_name> [custom_name] [-y]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- `icon_name` — the Material Symbols name as returned by the search script
|
|
75
|
+
- `custom_name` — optional rename (e.g. `keyboard_double_arrow_right double_chevron` saves as `double_chevron.svg`)
|
|
76
|
+
- `-y` — auto-accept the directional prompt without user interaction
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
```bash
|
|
80
|
+
# Non-directional — downloads silently
|
|
81
|
+
npm run geticon -- format_bold
|
|
82
|
+
|
|
83
|
+
# Directional — prompts to confirm saving right-facing variant as "chevron"
|
|
84
|
+
npm run geticon -- chevron_left
|
|
85
|
+
|
|
86
|
+
# Directional with rename and auto-accept
|
|
87
|
+
npm run geticon -- keyboard_double_arrow_up double_chevron -y
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
If the script exits with a non-zero code mentioning a 404, the icon name is wrong — return to Stage 2 and search for the correct name.
|
|
91
|
+
|
|
92
|
+
The script produces minified output with only `xmlns` and `viewBox` on `<svg>`, and only `fill="currentColor"` and `d` on each `<path>`:
|
|
93
|
+
```svg
|
|
94
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="..."/></svg>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Directional Icons and `<k-icon>`
|
|
100
|
+
|
|
101
|
+
When the script saves a right-facing icon (after a directional prompt), the `<k-icon>` component handles rotation via its `direction` attribute:
|
|
102
|
+
|
|
103
|
+
| `direction` value | Rotation applied |
|
|
104
|
+
|---|---|
|
|
105
|
+
| *(omitted)* | 0° — points right |
|
|
106
|
+
| `down` | 90° |
|
|
107
|
+
| `left` | 180° |
|
|
108
|
+
| `up` | 270° |
|
|
109
|
+
|
|
110
|
+
**Example:** A user asking for a "left arrow" after downloading `arrow.svg`:
|
|
111
|
+
```html
|
|
112
|
+
<k-icon name="arrow" direction="left"></k-icon>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Complete Examples
|
|
118
|
+
|
|
119
|
+
**User asks to "add a bold button" (implicit icon need)**
|
|
120
|
+
|
|
121
|
+
1. **Local check:** `icons/format_bold.svg` exists → use it directly, no download needed
|
|
122
|
+
2. **Use:** `<k-icon name="format_bold"></k-icon>`
|
|
123
|
+
|
|
124
|
+
**User asks to "add a thumbs up icon" (explicit, not in local icons)**
|
|
125
|
+
|
|
126
|
+
1. **Local check:** No match in `icons/`
|
|
127
|
+
2. **Find:** `npm run listicons -- thumbs+up` → `thumb_up`
|
|
128
|
+
3. **Directional check:** Not directional, proceed normally
|
|
129
|
+
4. **Run:** `npm run geticon -- thumb_up`
|
|
130
|
+
5. **Result:** `icons/thumb_up.svg` saved
|
|
131
|
+
|
|
132
|
+
**User asks for a "left arrow icon"**
|
|
133
|
+
|
|
134
|
+
1. **Local check:** `icons/arrow.svg` exists and is right-facing
|
|
135
|
+
2. **Directional check:** Directional — right-facing variant already present
|
|
136
|
+
3. **No download needed:** use `<k-icon name="arrow" direction="left"></k-icon>`
|
|
137
|
+
|
|
138
|
+
**User asks for a "left arrow icon" (no arrow in local icons)**
|
|
139
|
+
|
|
140
|
+
1. **Local check:** No arrow icon found
|
|
141
|
+
2. **Find:** `npm run listicons -- arrow` → consider `arrow_forward` or `arrow_right`
|
|
142
|
+
3. **Directional check:** Directional — download the right-facing variant with a generic name
|
|
143
|
+
4. **Run:** `npm run geticon -- arrow_forward arrow`
|
|
144
|
+
5. **Use:** `<k-icon name="arrow" direction="left"></k-icon>`
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: highlight-code
|
|
3
|
+
description: Generates syntax-highlighted HTML for documentation code samples. USE THIS instead of hand-writing <pre><code class="hljs ..."> blocks or creating temporary scripts. Run via `npx kempo-highlightcode <lang>` with code piped on stdin.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Highlight Code
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
Use this skill whenever you need syntax-highlighted HTML to embed in a documentation page — for example, when adding or updating a `<pre><code class="hljs ...">` block in a `docs-src/components/*.page.html` file. **Do not hand-write the highlighted markup, and do not create temporary scripts to do this.**
|
|
11
|
+
|
|
12
|
+
> Source files live in `docs-src/` and are pre-rendered to `docs/` by the kempo-server templating system. Always edit the `*.page.html` source — never the generated `docs/*.html`.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
Pipe code via stdin (supports multi-line):
|
|
17
|
+
```bash
|
|
18
|
+
cat <<'EOF' | npx kempo-highlightcode <lang>
|
|
19
|
+
<div class='container'>
|
|
20
|
+
<h1>Hello</h1>
|
|
21
|
+
</div>
|
|
22
|
+
EOF
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or pass minified code as an argument:
|
|
26
|
+
```bash
|
|
27
|
+
npx kempo-highlightcode <lang> "<div class='container'><h1>Hello</h1></div>"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Supported languages:** `html`, `css`, `js` / `javascript`, `ts` / `typescript`, `json`, `md` / `markdown`, `sh` / `bash`, `xml`
|
|
31
|
+
|
|
32
|
+
## Output
|
|
33
|
+
|
|
34
|
+
Prints a complete `<pre><code class="hljs {lang}">…</code></pre>` block to stdout, with `<br>` tags instead of newlines — a single line suitable for embedding directly in HTML.
|
|
35
|
+
|
|
36
|
+
Input is automatically beautified before highlighting, so you can pass minified code.
|
|
37
|
+
|
|
38
|
+
## Workflow for Updating Doc Code Samples
|
|
39
|
+
|
|
40
|
+
1. Capture the highlighted block to a file or shell variable:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
HL=$(cat <<'EOF' | npx kempo-highlightcode xml
|
|
44
|
+
<k-table id=myExample></k-table>
|
|
45
|
+
<script type=module>
|
|
46
|
+
doSomething('foo');
|
|
47
|
+
</script>
|
|
48
|
+
EOF
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. Use the **Edit tool** to replace the existing `<pre><code class="hljs xml">…</code></pre>` block in the target page with `$HL`. Do not use `sed` or shell-based file editing — Edit handles escaping correctly.
|
|
53
|
+
|
|
54
|
+
- Read the file first to find the exact `old_string` to replace.
|
|
55
|
+
- If the page has multiple `<pre><code>` blocks, include enough surrounding context in `old_string` to make it unique.
|
|
56
|
+
|
|
57
|
+
## Important Notes
|
|
58
|
+
|
|
59
|
+
- Use **single-quotes** for JS string literals inside the code. This preserves all quoting correctly through to node.
|
|
60
|
+
- HTML attribute values can be **unquoted** in the argument (`id=foo` not `id="foo"`) — the beautifier adds consistent formatting anyway.
|
|
61
|
+
- When piping multi-line code, use a **quoted heredoc** (`<<'EOF'`) so the shell does not interpolate `$`, backticks, or backslashes inside the code.
|
|
62
|
+
- Output uses `<br>` instead of `\n`, keeping the block on one line in the HTML source.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: new-component
|
|
3
|
+
description: Creates a new Kempo UI web component end-to-end — choosing the right base class, writing the source file, registering the custom element, adding the docs page, wiring up the nav/index/llms.txt, AND writing tests. Use any time you are asked to create a new component or add a new custom element.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# New Component
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
Use this skill any time you are asked to create a new component or add a new custom element to the project.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Creating a component involves six steps. Do not skip steps — especially the tests.
|
|
17
|
+
|
|
18
|
+
1. **Choose the base component** — pick the right rendering strategy
|
|
19
|
+
2. **Write the source file** in `src/components/` (also registers the custom element)
|
|
20
|
+
3. **Read the templating primer** so the docs page is structured correctly
|
|
21
|
+
4. **Add the documentation page** in `docs-src/components/` and wire up nav/index/llms.txt
|
|
22
|
+
5. **Write and run unit tests** in `tests/components/`
|
|
23
|
+
6. **Verify in the browser** at `http://localhost:8083`
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Step 1: Choose the Base Component
|
|
28
|
+
|
|
29
|
+
Three base classes are available. Pick based on rendering needs:
|
|
30
|
+
|
|
31
|
+
### `ShadowComponent`
|
|
32
|
+
Use when the component needs shadow DOM encapsulation. The base class automatically injects the kempo-css stylesheet into the shadow root. **This is the default for most components.**
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
import { html, css } from '../lit-all.min.js';
|
|
36
|
+
import ShadowComponent from './ShadowComponent.js';
|
|
37
|
+
|
|
38
|
+
export default class MyComponent extends ShadowComponent {
|
|
39
|
+
render() {
|
|
40
|
+
return html`<p>Shadow DOM content with scoped styles</p>`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `LightComponent`
|
|
46
|
+
Use when the component renders to the light DOM (no encapsulation, inherits page styles). Override `renderLightDom()`.
|
|
47
|
+
|
|
48
|
+
### `HybridComponent`
|
|
49
|
+
Use when the component needs both a shadow DOM portion and a light DOM portion (e.g. slotted children that also need managed light DOM output). Override both `render()` and `renderLightDom()`.
|
|
50
|
+
|
|
51
|
+
**Important:** Always call `super.updated(...)` when overriding `updated()` in any base class.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Step 2: Write the Source File
|
|
56
|
+
|
|
57
|
+
Create `src/components/MyComponent.js`. Follow these conventions (also see [AGENTS.md](../../AGENTS.md)):
|
|
58
|
+
|
|
59
|
+
- Use multi-line comments to separate logical sections: `Lifecycle Callbacks`, `Event Handlers`, `Public Methods`, `Utility`, `Rendering`, `Styles`.
|
|
60
|
+
- Declare reactive properties with `static properties = { ... }` and initialize defaults in the constructor.
|
|
61
|
+
- Use `static styles = css\`...\`` for component-scoped CSS (shadow DOM only).
|
|
62
|
+
- Use **arrow functions** for class methods to avoid `.bind(this)`.
|
|
63
|
+
- For private fields use native JS private fields (`#field`) — never underscore-prefixed.
|
|
64
|
+
- For form-associated components: set `static formAssociated = true`, call `this.attachInternals()` in the constructor, and use `internals.setFormValue(...)` / `internals.setValidity(...)`.
|
|
65
|
+
- **Buttons inside the shadow DOM should have `class="no-btn"`** to opt out of kempo-css's default button styling. Add explicit `display: inline-flex; align-items: center; justify-content: center;` in the component CSS to keep content centered after stripping. The same applies to `class="no-style"` on selects/inputs if you need to fully opt out, but most form controls render fine with kempo-css defaults.
|
|
66
|
+
|
|
67
|
+
Example skeleton:
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { html, css } from '../lit-all.min.js';
|
|
71
|
+
import ShadowComponent from './ShadowComponent.js';
|
|
72
|
+
|
|
73
|
+
export default class MyComponent extends ShadowComponent {
|
|
74
|
+
static properties = {
|
|
75
|
+
value: { type: String, reflect: true },
|
|
76
|
+
disabled: { type: Boolean, reflect: true }
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
Lifecycle Callbacks
|
|
81
|
+
*/
|
|
82
|
+
constructor() {
|
|
83
|
+
super();
|
|
84
|
+
this.value = '';
|
|
85
|
+
this.disabled = false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
Event Handlers
|
|
90
|
+
*/
|
|
91
|
+
handleClick = () => {
|
|
92
|
+
if(this.disabled) return;
|
|
93
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
94
|
+
detail: { value: this.value },
|
|
95
|
+
bubbles: true
|
|
96
|
+
}));
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
Rendering
|
|
101
|
+
*/
|
|
102
|
+
render() {
|
|
103
|
+
return html`
|
|
104
|
+
<button class="no-btn btn" ?disabled=${this.disabled} @click=${this.handleClick}>
|
|
105
|
+
${this.value}
|
|
106
|
+
</button>
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
Styles
|
|
112
|
+
*/
|
|
113
|
+
static styles = css`
|
|
114
|
+
:host { display: inline-block; }
|
|
115
|
+
.btn {
|
|
116
|
+
display: inline-flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
justify-content: center;
|
|
119
|
+
padding: var(--spacer_h) var(--spacer);
|
|
120
|
+
border: 1px solid var(--c_border);
|
|
121
|
+
border-radius: var(--radius);
|
|
122
|
+
background: var(--c_bg);
|
|
123
|
+
color: var(--tc);
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
customElements.define('k-my-component', MyComponent);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Custom element naming
|
|
133
|
+
- All elements use the `k-` prefix (e.g. `k-spinner`, `k-color-picker`).
|
|
134
|
+
- Use kebab-case for multi-word names.
|
|
135
|
+
- The `customElements.define(...)` call goes at the **bottom** of the file, after the class.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Step 3: kempo-server Templating Primer
|
|
140
|
+
|
|
141
|
+
The docs site is built with the **kempo-server v3 templating system**. Source files live in `docs-src/`; production pages are pre-rendered into `docs/` at build time. **Do not edit anything inside `docs/components/` directly — those files are generated.**
|
|
142
|
+
|
|
143
|
+
### File types
|
|
144
|
+
|
|
145
|
+
| Suffix | Purpose | Example |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `*.page.html` | An individual page. Wraps content in `<page>` and fills template slots with `<content>` blocks. | `docs-src/components/slider.page.html` |
|
|
148
|
+
| `*.template.html` | Shared layout. Defines named slots with `<location>` and pulls in fragments with `<fragment name="..." />`. | `docs-src/default.template.html` |
|
|
149
|
+
| `*.fragment.html` | Reusable HTML partial. Included via `<fragment name="..." />` from templates or pages. | `docs-src/nav.fragment.html` |
|
|
150
|
+
| `*.global.html` | Site-wide content auto-injected into matching `<location>` tags across every page. | (none currently) |
|
|
151
|
+
|
|
152
|
+
### How a page renders
|
|
153
|
+
|
|
154
|
+
1. The `<page>` tag chooses a template (defaults to `default`, looked up upward from the page directory). For component docs that's `docs-src/default.template.html`.
|
|
155
|
+
2. Each `<content>` block in the page fills a `<location>` slot in the template:
|
|
156
|
+
- `<content>` (no `location`) fills `<location />` (the unnamed default slot — body content).
|
|
157
|
+
- `<content location="scripts">` fills `<location name="scripts" />` (`<script type="module">` tags at the end of `<body>`).
|
|
158
|
+
- `<content location="header">` fills `<location name="header">` (overrides the default `<h1>` heading).
|
|
159
|
+
3. Page tag attributes become template variables: `<page pageName="Slider" title="...">` makes `{{pageName}}` and `{{title}}` available throughout the template.
|
|
160
|
+
4. The `<fragment name="nav" />` call in the template pulls in `docs-src/nav.fragment.html`, which renders the navbar and side menu.
|
|
161
|
+
5. Path variables: **`{{pathToRoot}}`** is the relative path back to `docs-src/` — use it for every asset reference. The renderer substitutes the correct number of `../` segments based on the page's depth. Other built-ins include `{{year}}`, `{{date}}`, `{{datetime}}`, `{{timestamp}}`, `{{version}}`, `{{env}}`.
|
|
162
|
+
6. Templates and fragments resolve **upward** from the referencing page directory (nearest match wins). Pages and globals scan **downward** (recursively).
|
|
163
|
+
|
|
164
|
+
### Conditionals and loops
|
|
165
|
+
|
|
166
|
+
The templating engine supports:
|
|
167
|
+
- `<if condition="..."> … </if>` with `===`, `!==`, `>`, `<`, `>=`, `<=`, `&&`, `||`, `!`
|
|
168
|
+
- `<foreach in="arrayName" as="item"> {{item.name}} </foreach>` with dot-path access
|
|
169
|
+
|
|
170
|
+
### Dev server
|
|
171
|
+
|
|
172
|
+
A dev server is already running on `http://localhost:8083` (started via `npm run dev`). It uses SSR for `docs-src/`, so your changes appear on refresh without a build. Static assets in `docs/` (CSS, media, manifest) are served via customRoutes. **Do not start another server.**
|
|
173
|
+
|
|
174
|
+
For production, `npm run build` minifies the JS, copies icons, and pre-renders all pages from `docs-src/` into `docs/`.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Step 4: Add the Documentation Page
|
|
179
|
+
|
|
180
|
+
Create `docs-src/components/my-component.page.html`. Use an existing page (e.g. [`slider.page.html`](../../docs-src/components/slider.page.html), [`time.page.html`](../../docs-src/components/time.page.html)) as a structural reference. Recommended sections:
|
|
181
|
+
|
|
182
|
+
- A Table of Contents accordion at the top
|
|
183
|
+
- Examples (Basic Usage, Default Value, mode-specific examples, etc.)
|
|
184
|
+
- A `<h2 id="jsRef">JavaScript Reference</h2>` section covering: Constructor, Requirements, Properties, Methods, CSS Variables, Events
|
|
185
|
+
- Module script tags inside `<content location="scripts">`
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<page pageName="My Component" title="My Component - Components - Kempo Docs - A Web Components Solution">
|
|
189
|
+
<content>
|
|
190
|
+
<k-accordion persistent-id="toc" class="b r mb">
|
|
191
|
+
<k-accordion-header for-panel="toc-panel">Table of Contents</k-accordion-header>
|
|
192
|
+
<k-accordion-panel name="toc-panel">
|
|
193
|
+
<!-- TOC links -->
|
|
194
|
+
</k-accordion-panel>
|
|
195
|
+
</k-accordion>
|
|
196
|
+
|
|
197
|
+
<h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
|
|
198
|
+
<!-- examples -->
|
|
199
|
+
|
|
200
|
+
<h2 id="jsRef"><a href="#jsRef" class="no-link">JavaScript Reference</a></h2>
|
|
201
|
+
<!-- properties / methods / events -->
|
|
202
|
+
</content>
|
|
203
|
+
<content location="scripts">
|
|
204
|
+
<script type="module" src="{{pathToRoot}}src/components/MyComponent.js"></script>
|
|
205
|
+
<script type="module" src="{{pathToRoot}}src/components/Accordion.js"></script>
|
|
206
|
+
<script type="module" src="{{pathToRoot}}src/components/Card.js"></script>
|
|
207
|
+
</content>
|
|
208
|
+
</page>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use `{{pathToRoot}}` for any relative path (the templating system substitutes the correct `../` count per page depth).
|
|
212
|
+
|
|
213
|
+
### Code Samples in the Docs
|
|
214
|
+
|
|
215
|
+
For every `<pre><code class="hljs ...">` block, **use the highlight-code skill** to generate the markup. Do not hand-write the highlighted HTML — it is fragile and error-prone.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
cat <<'EOF' | npx kempo-highlightcode xml
|
|
219
|
+
<k-my-component value="hello"></k-my-component>
|
|
220
|
+
EOF
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Then paste the result into the page (Edit tool).
|
|
224
|
+
|
|
225
|
+
### Wire Up Nav, Index, and llms.txt
|
|
226
|
+
|
|
227
|
+
After creating the page, add the component in **four** places, all in alphabetical order:
|
|
228
|
+
|
|
229
|
+
#### 1. Search filter dropdown — [`docs-src/nav.fragment.html`](../../docs-src/nav.fragment.html)
|
|
230
|
+
|
|
231
|
+
Inside `<k-filter-list id="navSearchList">`, add a `<k-filter-item>`:
|
|
232
|
+
|
|
233
|
+
```html
|
|
234
|
+
<k-filter-item filter-keywords="my component mycomponent keywords here components"><a
|
|
235
|
+
href="{{pathToRoot}}components/my-component.html"
|
|
236
|
+
>My Component<br><small>Component</small></a></k-filter-item>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**WATCH OUT:** When using the Edit tool to insert a new filter-item, do **not** truncate the `<a` of the surrounding filter-item. The Edit tool's `old_string`/`new_string` boundaries must end on whole lines. Verify with Read after each edit.
|
|
240
|
+
|
|
241
|
+
#### 2. Sidebar menu link — same `nav.fragment.html`
|
|
242
|
+
|
|
243
|
+
Inside the `<menu>` block under `<h3>Components</h3>`, add an `<a>` tag in alphabetical order:
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<a href="{{pathToRoot}}components/my-component.html">My Component</a>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### 3. Homepage card — [`docs-src/index.page.html`](../../docs-src/index.page.html)
|
|
250
|
+
|
|
251
|
+
Inside the `<div class="row -mx">` under `<h2>Components</h2>`, add a card in alphabetical order:
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<div class="span-12 t-span-6 d-span-4 px">
|
|
255
|
+
<a href="{{pathToRoot}}components/my-component.html" class="card mb no-link d-b">
|
|
256
|
+
<h3 class="tc-primary">My Component</h3>
|
|
257
|
+
<p class="tc-muted">One-sentence description of what the component does.</p>
|
|
258
|
+
</a>
|
|
259
|
+
</div>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### 4. LLM reference — [`llms.txt`](../../llms.txt)
|
|
263
|
+
|
|
264
|
+
Add a row to the **Components** table (alphabetical by element name):
|
|
265
|
+
|
|
266
|
+
```markdown
|
|
267
|
+
| `<k-my-component>` | `MyComponent.js` | One-sentence description with key attributes and events | [my-component.html](https://dustinpoissant.github.io/kempo-ui/components/my-component.html) |
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
If the component registers multiple elements (e.g. parent + child), list all element names in the first column separated by spaces.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Step 5: Write and Run Unit Tests
|
|
275
|
+
|
|
276
|
+
**This step is required.** Skipping tests is a common mistake — the AGENTS.md says "ALL tests must pass — ZERO failures are acceptable."
|
|
277
|
+
|
|
278
|
+
Create `tests/components/MyComponent.browser-test.js`. Use [Toggle.browser-test.js](../../tests/components/Toggle.browser-test.js) or [Slider.browser-test.js](../../tests/components/Slider.browser-test.js) as a reference.
|
|
279
|
+
|
|
280
|
+
Conventions:
|
|
281
|
+
- Import the component class at the top.
|
|
282
|
+
- Define an async `createMyComponent(attrs = {})` helper that builds the DOM, appends to `document.body`, awaits `el.updateComplete`, and returns `{ container, el }`.
|
|
283
|
+
- Define a `cleanup(container)` helper that removes the container from the DOM.
|
|
284
|
+
- Export a default plain object where each key is a test description and each value is an `async ({pass, fail}) => {}` function.
|
|
285
|
+
- Always call `cleanup(container)` before every `pass()` or `fail()` call.
|
|
286
|
+
- Use multi-line comments to group related tests (`/* Element Creation */`, `/* Properties */`, etc.).
|
|
287
|
+
|
|
288
|
+
Tests to include at minimum:
|
|
289
|
+
|
|
290
|
+
- Element is created and is an instance of the component class
|
|
291
|
+
- Element has a shadow root (when applicable)
|
|
292
|
+
- Default property values are correct
|
|
293
|
+
- Attribute reflection works (when `reflect: true`)
|
|
294
|
+
- Public methods behave correctly
|
|
295
|
+
- Events are dispatched correctly with the right `detail` shape
|
|
296
|
+
- For form-associated components: form submission produces the expected value
|
|
297
|
+
|
|
298
|
+
Run the tests after writing them:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
npm run test -- MyComponent
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The partial string `MyComponent` matches any test file path containing it. Fix any failures before considering the component complete. Zero failures.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Step 6: Verify in the Browser
|
|
309
|
+
|
|
310
|
+
A dev server is already running on `http://localhost:8083` — **do not start another**. Source pages render via SSR so changes appear on refresh without rebuilding.
|
|
311
|
+
|
|
312
|
+
- Navigate to `http://localhost:8083/components/my-component.html`
|
|
313
|
+
- Use chrome-devtools-mcp to interact with the rendered output
|
|
314
|
+
- Test the golden path AND edge cases
|
|
315
|
+
- Confirm form-associated behavior by submitting an actual `<form>`
|
|
316
|
+
- Watch for console errors that aren't pre-existing
|
|
317
|
+
|
|
318
|
+
If you cannot test the UI for some reason, say so explicitly rather than claiming success.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Component Architecture and Communication
|
|
323
|
+
|
|
324
|
+
- Use **methods** to trigger actions. Events notify that something already happened; they should not be used to trigger logic.
|
|
325
|
+
- Prefer `el.closest('k-parent')?.doSomething()` over dispatching an event and listening for it.
|
|
326
|
+
- Child components should locate their parent via `closest('k-parent-element')` and call its methods directly.
|
|
327
|
+
- Avoid `window` globals and global custom events for coordination. Scope events to the relevant element; reserve `window` events for global, non-visual concerns (e.g. settings changes).
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Elevation (Z-Index)
|
|
332
|
+
|
|
333
|
+
Any component using `position: fixed` must follow the kempo-css elevation system where `z-index = level × 10`.
|
|
334
|
+
|
|
335
|
+
| Level | z-index | Kempo UI Components | Notes |
|
|
336
|
+
|-------|---------|---------------------|-------|
|
|
337
|
+
| 2 | 20 | (page default) | No z-index needed for flow-position components |
|
|
338
|
+
| 3 | 30 | Aside (push) | Fixed panels that sit **below** a fixed navbar |
|
|
339
|
+
| 5 | 50 | *(navbar — user-defined)* | Reserved buffer for user-defined navbars |
|
|
340
|
+
| 6 | 60 | Aside (overlay) | Overlay drawers that sit **above** a fixed navbar |
|
|
341
|
+
| 7 | 70 | Dropdown | Floating menus; above navbar and overlay |
|
|
342
|
+
| 8 | 80 | Dialog, PhotoViewer | Full-screen modals and lightboxes |
|
|
343
|
+
| 9 | 90 | Toast | Notification toasts; always topmost |
|
|
344
|
+
|
|
345
|
+
Levels 1, 4 are intentional buffer zones for user customization.
|
|
346
|
+
|
|
347
|
+
When deciding a new component's elevation:
|
|
348
|
+
- If it is a panel or drawer in `push` mode (shifts page content), use level 3.
|
|
349
|
+
- If it is a panel or drawer in `overlay` mode (floats over content), use level 6.
|
|
350
|
+
- If it covers the entire viewport (modal/dialog), use level 8.
|
|
351
|
+
- If it is a temporary notification, use level 9.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Common Mistakes To Avoid
|
|
356
|
+
|
|
357
|
+
- **Hand-writing the syntax-highlighted HTML** in code samples — use the `highlight-code` skill instead.
|
|
358
|
+
- **Skipping tests** — every component needs a `*.browser-test.js` file with the minimum coverage above.
|
|
359
|
+
- **Editing `docs/`** — that directory is generated by the build. Edit `docs-src/`.
|
|
360
|
+
- **Forgetting `class="no-btn"` on shadow-DOM `<button>` elements** — kempo-css aggressively styles native buttons; opt out and re-center with flex.
|
|
361
|
+
- **Editing only the search filter and forgetting the sidebar menu, index page, or llms.txt** — all four locations must be updated for a new component.
|
|
362
|
+
- **Truncating adjacent elements when editing `nav.fragment.html`** — Edit tool boundaries must include whole lines; always Read after editing to verify nothing was clipped.
|
|
@@ -124,9 +124,9 @@ When the script saves a right-facing icon (after a directional prompt), the `<k-
|
|
|
124
124
|
**User asks to "add a thumbs up icon" (explicit, not in local icons)**
|
|
125
125
|
|
|
126
126
|
1. **Local check:** No match in `icons/`
|
|
127
|
-
2. **Find:**
|
|
127
|
+
2. **Find:** `npm run listicons -- thumbs+up` → `thumb_up`
|
|
128
128
|
3. **Arrow check:** Not directional, proceed normally
|
|
129
|
-
4. **Run:** `
|
|
129
|
+
4. **Run:** `npm run geticon -- thumb_up`
|
|
130
130
|
5. **Result:** `icons/thumb_up.svg` saved and build triggered
|
|
131
131
|
|
|
132
132
|
**User asks for a "left arrow icon"**
|
|
@@ -138,7 +138,7 @@ When the script saves a right-facing icon (after a directional prompt), the `<k-
|
|
|
138
138
|
**User asks for a "left arrow icon" (no arrow in local icons)**
|
|
139
139
|
|
|
140
140
|
1. **Local check:** No arrow icon found
|
|
141
|
-
2. **Find:**
|
|
141
|
+
2. **Find:** `npm run listicons -- arrow` → consider `arrow_forward` or `arrow_right`
|
|
142
142
|
3. **Arrow check:** Directional — download the right-facing variant with a generic name
|
|
143
|
-
4. **Run:** `
|
|
143
|
+
4. **Run:** `npm run geticon -- arrow_forward arrow`
|
|
144
144
|
5. **Use:** `<k-icon name="arrow" direction="left"></k-icon>`
|