@invisibleloop/pulse 0.1.34 → 0.1.36

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.
Files changed (52) hide show
  1. package/.github/workflows/publish.yml +9 -2
  2. package/docs/src/lib/layout.js +1 -1
  3. package/docs/src/pages/components/accordion.js +1 -1
  4. package/docs/src/pages/components/app-badge.js +1 -1
  5. package/docs/src/pages/components/banner.js +1 -1
  6. package/docs/src/pages/components/breadcrumbs.js +1 -1
  7. package/docs/src/pages/components/button.js +1 -1
  8. package/docs/src/pages/components/card.js +1 -1
  9. package/docs/src/pages/components/carousel.js +2 -2
  10. package/docs/src/pages/components/charts.js +7 -7
  11. package/docs/src/pages/components/checkbox.js +1 -1
  12. package/docs/src/pages/components/cluster.js +1 -1
  13. package/docs/src/pages/components/code-window.js +1 -1
  14. package/docs/src/pages/components/container.js +1 -1
  15. package/docs/src/pages/components/cta.js +1 -1
  16. package/docs/src/pages/components/divider.js +1 -1
  17. package/docs/src/pages/components/feature.js +1 -1
  18. package/docs/src/pages/components/fieldset.js +1 -1
  19. package/docs/src/pages/components/fileupload.js +1 -1
  20. package/docs/src/pages/components/footer.js +1 -1
  21. package/docs/src/pages/components/grid.js +1 -1
  22. package/docs/src/pages/components/heading.js +1 -1
  23. package/docs/src/pages/components/hero.js +1 -1
  24. package/docs/src/pages/components/icons.js +1 -1
  25. package/docs/src/pages/components/image.js +1 -1
  26. package/docs/src/pages/components/input.js +1 -1
  27. package/docs/src/pages/components/list.js +1 -1
  28. package/docs/src/pages/components/media.js +1 -1
  29. package/docs/src/pages/components/modal.js +6 -6
  30. package/docs/src/pages/components/nav.js +1 -1
  31. package/docs/src/pages/components/pricing.js +1 -1
  32. package/docs/src/pages/components/progress.js +1 -1
  33. package/docs/src/pages/components/prose.js +1 -1
  34. package/docs/src/pages/components/pullquote.js +1 -1
  35. package/docs/src/pages/components/radio.js +2 -2
  36. package/docs/src/pages/components/rating.js +1 -1
  37. package/docs/src/pages/components/search.js +1 -1
  38. package/docs/src/pages/components/section.js +1 -1
  39. package/docs/src/pages/components/segmented.js +1 -1
  40. package/docs/src/pages/components/slider.js +1 -1
  41. package/docs/src/pages/components/spinner.js +1 -1
  42. package/docs/src/pages/components/stack.js +1 -1
  43. package/docs/src/pages/components/stat.js +1 -1
  44. package/docs/src/pages/components/stepper.js +1 -1
  45. package/docs/src/pages/components/testimonial.js +1 -1
  46. package/docs/src/pages/components/timeline.js +2 -2
  47. package/docs/src/pages/components/toggle.js +1 -1
  48. package/docs/src/pages/components/tooltip.js +1 -1
  49. package/package.json +1 -1
  50. package/public/.pulse-ui-version +1 -1
  51. package/src/ui/fileupload.js +1 -1
  52. package/src/ui/progress.js +3 -1
@@ -8,10 +8,12 @@ jobs:
8
8
  release:
9
9
  runs-on: ubuntu-latest
10
10
  permissions:
11
- contents: read
11
+ contents: write
12
12
  id-token: write
13
13
  steps:
14
14
  - uses: actions/checkout@v4
15
+ with:
16
+ token: ${{ secrets.RELEASE_PAT }}
15
17
 
16
18
  - uses: actions/setup-node@v4
17
19
  with:
@@ -31,6 +33,11 @@ jobs:
31
33
  while npm view @invisibleloop/pulse@$VERSION version 2>/dev/null | grep -q .; do
32
34
  VERSION=$(node -e "const [a,b,c]='$VERSION'.split('.').map(Number);console.log(a+'.'+b+'.'+(c+1))")
33
35
  done
34
- npm version $VERSION --no-git-tag-version
36
+ npm version $VERSION --no-git-tag-version --allow-same-version
35
37
  echo "$VERSION" > public/.pulse-ui-version
38
+ git config user.name "github-actions[bot]"
39
+ git config user.email "github-actions[bot]@users.noreply.github.com"
40
+ git add package.json package-lock.json public/.pulse-ui-version
41
+ git commit -m "chore: release $VERSION [skip ci]" || true
42
+ git push
36
43
  npm publish --provenance --access public
@@ -120,7 +120,7 @@ export function codeBlock(highlighted, filename = '') {
120
120
  }
121
121
 
122
122
  export function table(headers, rows) {
123
- const ths = headers.map(h => `<th>${h}</th>`).join('')
123
+ const ths = headers.map(h => `<th scope="col">${h}</th>`).join('')
124
124
  const trs = rows.map(row =>
125
125
  `<tr>${row.map(cell => `<td>${cell}</td>`).join('')}</tr>`
126
126
  ).join('')
@@ -38,7 +38,7 @@ export default {
38
38
  )}
39
39
 
40
40
  ${table(
41
- ['Prop', 'Type', 'Default', ''],
41
+ ['Prop', 'Type', 'Default', 'Description'],
42
42
  [
43
43
  ['<code>items</code>', 'array', '[]', '<code>{ question: string, answer: string }[]</code>'],
44
44
  ]
@@ -30,7 +30,7 @@ appBadge({ store: 'google', href: playStoreUrl })`
30
30
  )}
31
31
 
32
32
  ${table(
33
- ['Prop', 'Type', 'Default', ''],
33
+ ['Prop', 'Type', 'Default', 'Description'],
34
34
  [
35
35
  ['<code>store</code>', 'string', "'apple'", "'apple' or 'google'"],
36
36
  ['<code>href</code>', 'string', "'#'", 'Link to the app store listing'],
@@ -34,7 +34,7 @@ export default {
34
34
  )}
35
35
 
36
36
  ${table(
37
- ['Prop', 'Type', 'Default', ''],
37
+ ['Prop', 'Type', 'Default', 'Description'],
38
38
  [
39
39
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
40
40
  ['<code>variant</code>', 'string', "'info'", "'info' · 'promo' · 'warning'"],
@@ -82,7 +82,7 @@ export default {
82
82
  )}
83
83
 
84
84
  ${table(
85
- ['Prop', 'Type', 'Default', ''],
85
+ ['Prop', 'Type', 'Default', 'Description'],
86
86
  [
87
87
  ['<code>items</code>', 'array', '[]', 'Array of <code>{ label, href }</code>. The last item should have no <code>href</code> — it becomes the current page.'],
88
88
  ['<code>separator</code>', 'string', "'/'", 'Character rendered between items'],
@@ -64,7 +64,7 @@ button({ label: 'Submit', type: 'submit', variant: 'primary' })`
64
64
  )}
65
65
 
66
66
  ${table(
67
- ['Prop', 'Type', 'Default', ''],
67
+ ['Prop', 'Type', 'Default', 'Description'],
68
68
  [
69
69
  ['<code>label</code>', 'string', '—', 'Visible text'],
70
70
  ['<code>variant</code>', 'string', 'primary', 'primary · secondary · ghost · danger'],
@@ -210,7 +210,7 @@ export default {
210
210
  )}
211
211
 
212
212
  ${table(
213
- ['Prop', 'Type', 'Default', ''],
213
+ ['Prop', 'Type', 'Default', 'Description'],
214
214
  [
215
215
  ['<code>title</code>', 'string', '—', 'Escaped automatically'],
216
216
  ['<code>level</code>', 'number', '3', 'Heading tag for the title (1–6). Visual style is unchanged — use this to keep the document outline correct when the surrounding context already has an h2 or h3.'],
@@ -41,7 +41,7 @@ export default {
41
41
  })`
42
42
  )}
43
43
 
44
- <h3 class="doc-h3" id="arrows-dots"><a href="#arrows-dots" class="heading-anchor">Arrows and dots</a></h3>
44
+ <h2 class="doc-h2" id="arrows-dots"><a href="#arrows-dots" class="heading-anchor">Arrows and dots</a></h2>
45
45
  ${demo(
46
46
  carousel({
47
47
  arrows: true,
@@ -59,7 +59,7 @@ export default {
59
59
  )}
60
60
 
61
61
  ${table(
62
- ['Prop', 'Type', 'Default', ''],
62
+ ['Prop', 'Type', 'Default', 'Description'],
63
63
  [
64
64
  ['<code>slides</code>', 'string[] (HTML)', '[]', 'Array of raw HTML strings — one per slide'],
65
65
  ['<code>arrows</code>', 'boolean', '<code>true</code>', 'Show prev/next arrow buttons'],
@@ -212,9 +212,9 @@ sparkline({ data: [31,24,28,19,22,14,18,12], color: 'error', area: true })`
212
212
  cols: 3,
213
213
  gap: 'md',
214
214
  content:
215
- card({ content: stat({ label: 'Revenue', value: '$18.2k', change: '+12%', trend: 'up' }) + `<div style="margin-top:.75rem">${sparkline({ data: [8,11,9,14,12,16,15,18], width: '100%', color: 'success', area: true })}</div>` }) +
216
- card({ content: stat({ label: 'Users', value: '4,821', change: '+8.4%', trend: 'up' }) + `<div style="margin-top:.75rem">${sparkline({ data: [22,28,24,31,27,34,30,38], width: '100%', color: 'accent', area: true })}</div>` }) +
217
- card({ content: stat({ label: 'Churn', value: '2.1%', change: '−0.3%', trend: 'down' }) + `<div style="margin-top:.75rem">${sparkline({ data: [8,6,7,5,6,4,5,3], width: '100%', color: 'error', area: true })}</div>` }),
215
+ card({ content: stat({ label: 'Revenue', value: '$18.2k', change: '+12%', trend: 'up' }) + `<div style="margin-top:.75rem">${sparkline({ data: [8,11,9,14,12,16,15,18], color: 'success', area: true })}</div>` }) +
216
+ card({ content: stat({ label: 'Users', value: '4,821', change: '+8.4%', trend: 'up' }) + `<div style="margin-top:.75rem">${sparkline({ data: [22,28,24,31,27,34,30,38], color: 'accent', area: true })}</div>` }) +
217
+ card({ content: stat({ label: 'Churn', value: '2.1%', change: '−0.3%', trend: 'down' }) + `<div style="margin-top:.75rem">${sparkline({ data: [8,6,7,5,6,4,5,3], color: 'error', area: true })}</div>` }),
218
218
  }),
219
219
  `card({
220
220
  content: stat({ label: 'Revenue', value: '$18.2k', change: '+12%', trend: 'up' }) +
@@ -226,7 +226,7 @@ sparkline({ data: [31,24,28,19,22,14,18,12], color: 'error', area: true })`
226
226
 
227
227
  <h3 class="doc-h3">barChart()</h3>
228
228
  ${table(
229
- ['Prop', 'Type', 'Default', ''],
229
+ ['Prop', 'Type', 'Default', 'Description'],
230
230
  [
231
231
  ['<code>data</code>', 'array', '—', '<code>{ label, value }[]</code>'],
232
232
  ['<code>height</code>', 'number', '220', 'SVG height in px'],
@@ -239,7 +239,7 @@ sparkline({ data: [31,24,28,19,22,14,18,12], color: 'error', area: true })`
239
239
 
240
240
  <h3 class="doc-h3" style="margin-top:2rem">lineChart()</h3>
241
241
  ${table(
242
- ['Prop', 'Type', 'Default', ''],
242
+ ['Prop', 'Type', 'Default', 'Description'],
243
243
  [
244
244
  ['<code>data</code>', 'array', '—', '<code>{ label, value }[]</code>'],
245
245
  ['<code>height</code>', 'number', '220', 'SVG height in px'],
@@ -252,7 +252,7 @@ sparkline({ data: [31,24,28,19,22,14,18,12], color: 'error', area: true })`
252
252
 
253
253
  <h3 class="doc-h3" style="margin-top:2rem">donutChart()</h3>
254
254
  ${table(
255
- ['Prop', 'Type', 'Default', ''],
255
+ ['Prop', 'Type', 'Default', 'Description'],
256
256
  [
257
257
  ['<code>data</code>', 'array', '—', '<code>{ label, value, color? }[]</code> — color per segment'],
258
258
  ['<code>size</code>', 'number', '200', 'Diameter in px'],
@@ -264,7 +264,7 @@ sparkline({ data: [31,24,28,19,22,14,18,12], color: 'error', area: true })`
264
264
 
265
265
  <h3 class="doc-h3" style="margin-top:2rem">sparkline()</h3>
266
266
  ${table(
267
- ['Prop', 'Type', 'Default', ''],
267
+ ['Prop', 'Type', 'Default', 'Description'],
268
268
  [
269
269
  ['<code>data</code>', 'number[]', '—', 'Plain array of numbers'],
270
270
  ['<code>width</code>', 'number', '80', 'SVG width in px'],
@@ -109,7 +109,7 @@ export default {
109
109
 
110
110
  <h2 class="doc-h2" id="props">Props</h2>
111
111
  ${table(
112
- ['Prop', 'Type', 'Default', ''],
112
+ ['Prop', 'Type', 'Default', 'Description'],
113
113
  [
114
114
  ['<code>name</code>', 'string', '—', 'Field name'],
115
115
  ['<code>value</code>', 'string', '—', 'Submitted value when checked (defaults to browser default <code>"on"</code>)'],
@@ -33,7 +33,7 @@ export default {
33
33
  )}
34
34
 
35
35
  ${table(
36
- ['Prop', 'Type', 'Default', ''],
36
+ ['Prop', 'Type', 'Default', 'Description'],
37
37
  [
38
38
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
39
39
  ['<code>gap</code>', 'string', "'md'", "'xs' · 'sm' · 'md' · 'lg'"],
@@ -45,7 +45,7 @@ export default {
45
45
  )}
46
46
 
47
47
  ${table(
48
- ['Prop', 'Type', 'Default', ''],
48
+ ['Prop', 'Type', 'Default', 'Description'],
49
49
  [
50
50
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot — pre-highlighted code HTML or plain text'],
51
51
  ['<code>filename</code>', 'string', '—', "Filename shown in the chrome bar (e.g. 'home.js')"],
@@ -29,7 +29,7 @@ export default {
29
29
  )}
30
30
 
31
31
  ${table(
32
- ['Prop', 'Type', 'Default', ''],
32
+ ['Prop', 'Type', 'Default', 'Description'],
33
33
  [
34
34
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
35
35
  ['<code>size</code>', 'string', "'lg'", "'sm' (640px) · 'md' (768px) · 'lg' (1100px) · 'xl' (1280px)"],
@@ -38,7 +38,7 @@ export default {
38
38
  )}
39
39
 
40
40
  ${table(
41
- ['Prop', 'Type', 'Default', ''],
41
+ ['Prop', 'Type', 'Default', 'Description'],
42
42
  [
43
43
  ['<code>eyebrow</code>', 'string', '—', 'Small label above the heading'],
44
44
  ['<code>title</code>', 'string', '—', 'Main heading'],
@@ -27,7 +27,7 @@ divider({ label: 'or continue with' })`
27
27
  )}
28
28
 
29
29
  ${table(
30
- ['Prop', 'Type', 'Default', ''],
30
+ ['Prop', 'Type', 'Default', 'Description'],
31
31
  [
32
32
  ['<code>label</code>', 'string', '—', 'Optional centred text — renders as <code>&lt;div&gt;</code> with role="separator" when provided, <code>&lt;hr&gt;</code> otherwise'],
33
33
  ]
@@ -46,7 +46,7 @@ feature({ icon: iconPhone({ size: 20 }), title: 'Works offline', descriptio
46
46
  )}
47
47
 
48
48
  ${table(
49
- ['Prop', 'Type', 'Default', ''],
49
+ ['Prop', 'Type', 'Default', 'Description'],
50
50
  [
51
51
  ['<code>icon</code>', 'string (HTML)', '—', 'Raw HTML slot — SVG or emoji; displayed in an accent-tinted box'],
52
52
  ['<code>title</code>', 'string', '—', ''],
@@ -52,7 +52,7 @@ export default {
52
52
  )}
53
53
 
54
54
  ${table(
55
- ['Prop', 'Type', 'Default', ''],
55
+ ['Prop', 'Type', 'Default', 'Description'],
56
56
  [
57
57
  ['<code>legend</code>', 'string', '—', 'Group label — rendered as <code>&lt;legend&gt;</code>, announced by screen readers on focus'],
58
58
  ['<code>content</code>', 'string', '—', 'Raw HTML slot — input(), select(), textarea(), grid(), etc.'],
@@ -107,7 +107,7 @@ fileUpload({ name: 'photo', label: 'Photo', accept: 'image/*', event: 'change:se
107
107
  ${callout('note', 'The file object itself cannot be stored in Pulse state — state must be serialisable. Store the filename or a preview URL (via <code>URL.createObjectURL</code>) instead, and upload the file in an action via FormData.')}
108
108
 
109
109
  ${table(
110
- ['Prop', 'Type', 'Default', ''],
110
+ ['Prop', 'Type', 'Default', 'Description'],
111
111
  [
112
112
  ['<code>name</code>', 'string', '—', 'Field name — file available in FormData under this key'],
113
113
  ['<code>label</code>', 'string', '—', 'Visible label text'],
@@ -45,7 +45,7 @@ export default {
45
45
  )}
46
46
 
47
47
  ${table(
48
- ['Prop', 'Type', 'Default', ''],
48
+ ['Prop', 'Type', 'Default', 'Description'],
49
49
  [
50
50
  ['<code>logo</code>', 'string (HTML)', '—', 'Raw HTML slot — SVG, img, or text'],
51
51
  ['<code>logoHref</code>', 'string', "'/'", 'Logo link destination'],
@@ -153,7 +153,7 @@ export default {
153
153
  )}
154
154
 
155
155
  ${table(
156
- ['Prop', 'Type', 'Default', ''],
156
+ ['Prop', 'Type', 'Default', 'Description'],
157
157
  [
158
158
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot — direct children are grid items'],
159
159
  ['<code>cols</code>', 'number', '3', '1 · 2 · 3 · 4'],
@@ -92,7 +92,7 @@ heading({ level: 2, text: 'The quick brown fox jumps over the lazy dog tonight',
92
92
  )}
93
93
 
94
94
  ${table(
95
- ['Prop', 'Type', 'Default', ''],
95
+ ['Prop', 'Type', 'Default', 'Description'],
96
96
  [
97
97
  ['<code>level</code>', 'number (1–6)', '2', 'Controls both the HTML tag and the default visual size'],
98
98
  ['<code>text</code>', 'string', '—', 'Heading text — escaped automatically'],
@@ -50,7 +50,7 @@ export default {
50
50
  )}
51
51
 
52
52
  ${table(
53
- ['Prop', 'Type', 'Default', ''],
53
+ ['Prop', 'Type', 'Default', 'Description'],
54
54
  [
55
55
  ['<code>eyebrow</code>', 'string', '—', 'Small label above the title'],
56
56
  ['<code>title</code>', 'string', '—', ''],
@@ -272,7 +272,7 @@ feature({ icon: iconCode({ size: 22, bg: 'square', bgColor: 'muted' }), titl
272
272
  ${iconGrid_()}
273
273
 
274
274
  ${table(
275
- ['Prop', 'Type', 'Default', ''],
275
+ ['Prop', 'Type', 'Default', 'Description'],
276
276
  [
277
277
  ['<code>size</code>', 'number', '16', 'Width and height in px'],
278
278
  ['<code>class</code>', 'string', '—', 'Extra CSS classes (on wrapper when <code>bg</code> is set, otherwise on the SVG)'],
@@ -54,7 +54,7 @@ export default {
54
54
  )}
55
55
 
56
56
  ${table(
57
- ['Prop', 'Type', 'Default', ''],
57
+ ['Prop', 'Type', 'Default', 'Description'],
58
58
  [
59
59
  ['<code>src</code>', 'string', '—', 'Image source URL'],
60
60
  ['<code>alt</code>', 'string', '—', 'Alt text — required for accessibility'],
@@ -31,7 +31,7 @@ input({ name: 'search', label: 'Search', placeholder: 'Filter by name…', hint:
31
31
  )}
32
32
 
33
33
  ${table(
34
- ['Prop', 'Type', 'Default', ''],
34
+ ['Prop', 'Type', 'Default', 'Description'],
35
35
  [
36
36
  ['<code>name</code>', 'string', '—', 'Also used as id base: <code>field-{name}</code>'],
37
37
  ['<code>label</code>', 'string', '—', ''],
@@ -99,7 +99,7 @@ list({ gap: 'md', items: [...] })`,
99
99
  )}
100
100
 
101
101
  ${table(
102
- ['Prop', 'Type', 'Default', ''],
102
+ ['Prop', 'Type', 'Default', 'Description'],
103
103
  [
104
104
  ['<code>items</code>', 'string[]', '—', 'Array of HTML strings for each list item — escape user data before passing'],
105
105
  ['<code>ordered</code>', 'boolean', '<code>false</code>', '<code>false</code> renders <code>&lt;ul&gt;</code>, <code>true</code> renders <code>&lt;ol&gt;</code>'],
@@ -37,7 +37,7 @@ export default {
37
37
  )}
38
38
 
39
39
  ${table(
40
- ['Prop', 'Type', 'Default', ''],
40
+ ['Prop', 'Type', 'Default', 'Description'],
41
41
  [
42
42
  ['<code>image</code>', 'string (HTML)', '—', 'Raw HTML slot — img, figure, SVG, or styled div'],
43
43
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
@@ -46,7 +46,7 @@ modal({
46
46
  modalTrigger({ target: 'confirm-delete', label: 'Delete item', variant: 'danger' })`
47
47
  )}
48
48
 
49
- <h3 class="doc-h3" id="sizes"><a href="#sizes" class="heading-anchor">Sizes</a></h3>
49
+ <h2 class="doc-h2" id="sizes"><a href="#sizes" class="heading-anchor">Sizes</a></h2>
50
50
  ${demo(
51
51
  cluster({ gap: 'sm', justify: 'center', content:
52
52
  modalTrigger({ target: 'demo-sm', label: 'Small', variant: 'secondary', size: 'sm' }) +
@@ -58,7 +58,7 @@ modalTrigger({ target: 'confirm-delete', label: 'Delete item', variant: 'danger'
58
58
  )}
59
59
 
60
60
  ${table(
61
- ['Prop', 'Type', 'Default', ''],
61
+ ['Prop', 'Type', 'Default', 'Description'],
62
62
  [
63
63
  ['<code>id</code>', 'string', '—', 'Unique ID — required for triggers to target this dialog'],
64
64
  ['<code>title</code>', 'string', '—', ''],
@@ -70,10 +70,10 @@ modalTrigger({ target: 'confirm-delete', label: 'Delete item', variant: 'danger'
70
70
  ]
71
71
  )}
72
72
 
73
- <h3 class="doc-h3" id="trigger-props"><a href="#trigger-props" class="heading-anchor">modalTrigger props</a></h3>
73
+ <h2 class="doc-h2" id="trigger-props"><a href="#trigger-props" class="heading-anchor">modalTrigger props</a></h2>
74
74
 
75
75
  ${table(
76
- ['Prop', 'Type', 'Default', ''],
76
+ ['Prop', 'Type', 'Default', 'Description'],
77
77
  [
78
78
  ['<code>target</code>', 'string', '—', "The modal's <code>id</code>"],
79
79
  ['<code>label</code>', 'string', '<code>Open</code>', ''],
@@ -83,13 +83,13 @@ modalTrigger({ target: 'confirm-delete', label: 'Delete item', variant: 'danger'
83
83
  ]
84
84
  )}
85
85
 
86
- <h3 class="doc-h3" id="custom-trigger"><a href="#custom-trigger" class="heading-anchor">Custom triggers</a></h3>
86
+ <h2 class="doc-h2" id="custom-trigger"><a href="#custom-trigger" class="heading-anchor">Custom triggers</a></h2>
87
87
  <p class="doc-body">Any element with <code>data-dialog-open="&lt;id&gt;"</code> opens the dialog when clicked. Use <code>data-dialog-close</code> on any element inside or outside the dialog to close it programmatically:</p>
88
88
  <pre class="code-block"><code>&lt;button data-dialog-open="my-modal"&gt;Open&lt;/button&gt;
89
89
  &lt;button data-dialog-close&gt;Cancel&lt;/button&gt;</code></pre>
90
90
  ${callout('tip', 'The dialog also closes on ESC, backdrop click, and <code>&lt;form method="dialog"&gt;</code> submit — all native browser behaviour, no JavaScript needed.')}
91
91
 
92
- <h3 class="doc-h3" id="forms-inside"><a href="#forms-inside" class="heading-anchor">Forms inside a modal</a></h3>
92
+ <h2 class="doc-h2" id="forms-inside"><a href="#forms-inside" class="heading-anchor">Forms inside a modal</a></h2>
93
93
  <p class="doc-body"><code>modal()</code> wraps all content in <code>&lt;form method="dialog"&gt;</code> for native close behaviour. You <strong>cannot nest a <code>&lt;form data-action="..."&gt;</code> inside it</strong> — browsers silently discard nested forms, so the action will never fire.</p>
94
94
  <p class="doc-body">When a modal button needs to trigger a Pulse action, place the form <em>outside</em> the modal and use the HTML <code>form</code> attribute to associate the button with it:</p>
95
95
  <pre class="code-block"><code>// The action form lives outside the modal — hidden, no visible fields needed
@@ -71,7 +71,7 @@ export default {
71
71
  </div>
72
72
 
73
73
  ${table(
74
- ['Prop', 'Type', 'Default', ''],
74
+ ['Prop', 'Type', 'Default', 'Description'],
75
75
  [
76
76
  ['<code>logo</code>', 'string (HTML)', '—', 'Raw HTML slot — SVG, img, or text'],
77
77
  ['<code>logoHref</code>', 'string', "'/'", ''],
@@ -50,7 +50,7 @@ export default {
50
50
  )}
51
51
 
52
52
  ${table(
53
- ['Prop', 'Type', 'Default', ''],
53
+ ['Prop', 'Type', 'Default', 'Description'],
54
54
  [
55
55
  ['<code>name</code>', 'string', '—', ''],
56
56
  ['<code>level</code>', 'number', '3', 'Heading tag for the plan name (1–6). Visual style is unchanged.'],
@@ -85,7 +85,7 @@ progress({ value: 60, size: 'lg' })`,
85
85
  )}
86
86
 
87
87
  ${table(
88
- ['Prop', 'Type', 'Default', ''],
88
+ ['Prop', 'Type', 'Default', 'Description'],
89
89
  [
90
90
  ['<code>value</code>', 'number', '—', 'Current value. Omit for indeterminate.'],
91
91
  ['<code>max</code>', 'number', '100', ''],
@@ -99,7 +99,7 @@ const x = 1 + 2</code></pre>
99
99
  ${callout('warning', '<strong>Do not escape the content prop.</strong> <code>prose()</code> renders raw HTML — it is designed for trusted server-side content only. Never pass unescaped user input directly. Sanitise CMS output before rendering if your CMS allows arbitrary HTML.')}
100
100
 
101
101
  ${table(
102
- ['Prop', 'Type', 'Default', ''],
102
+ ['Prop', 'Type', 'Default', 'Description'],
103
103
  [
104
104
  ['<code>content</code>', 'string', '—', 'Raw HTML string — rendered as-is, not escaped. Use for server-side content only.'],
105
105
  ['<code>size</code>', '<code>sm | base | lg</code>', '<code>base</code>', 'Base font size scale. <code>sm</code>=0.875rem, <code>base</code>=1rem, <code>lg</code>=1.125rem'],
@@ -58,7 +58,7 @@ export default {
58
58
  )}
59
59
 
60
60
  ${table(
61
- ['Prop', 'Type', 'Default', ''],
61
+ ['Prop', 'Type', 'Default', 'Description'],
62
62
  [
63
63
  ['<code>quote</code>', 'string', '—', 'The quote text — escaped automatically'],
64
64
  ['<code>cite</code>', 'string', '—', 'Attribution text — rendered in a <code>&lt;figcaption&gt;</code>'],
@@ -163,7 +163,7 @@ export default {
163
163
 
164
164
  <h2 class="doc-h2" id="props-group">radioGroup() props</h2>
165
165
  ${table(
166
- ['Prop', 'Type', 'Default', ''],
166
+ ['Prop', 'Type', 'Default', 'Description'],
167
167
  [
168
168
  ['<code>name</code>', 'string', '—', 'Shared <code>name</code> attribute for all inputs in the group'],
169
169
  ['<code>legend</code>', 'string', '—', 'Group label — renders as a <code>&lt;legend&gt;</code>'],
@@ -178,7 +178,7 @@ export default {
178
178
 
179
179
  <h2 class="doc-h2" id="props-radio">radio() props</h2>
180
180
  ${table(
181
- ['Prop', 'Type', 'Default', ''],
181
+ ['Prop', 'Type', 'Default', 'Description'],
182
182
  [
183
183
  ['<code>name</code>', 'string', '—', 'Field name'],
184
184
  ['<code>value</code>', 'string', '—', 'Submitted value when this option is selected'],
@@ -90,7 +90,7 @@ rating({ value: 4, size: 'lg' })`
90
90
  ${callout('note', 'The interactive rating submits the selected star count as a number string under <code>name</code> in FormData. Read it with <code>Number(formData.get(\'score\'))</code>. If nothing is selected, the field is absent from FormData.')}
91
91
 
92
92
  ${table(
93
- ['Prop', 'Type', 'Default', ''],
93
+ ['Prop', 'Type', 'Default', 'Description'],
94
94
  [
95
95
  ['<code>value</code>', 'number', '0', 'Current rating. Supports 0.5 steps in display mode.'],
96
96
  ['<code>max</code>', 'number', '5', 'Total number of stars'],
@@ -40,7 +40,7 @@ search({
40
40
  )}
41
41
 
42
42
  ${table(
43
- ['Prop', 'Type', 'Default', ''],
43
+ ['Prop', 'Type', 'Default', 'Description'],
44
44
  [
45
45
  ['<code>name</code>', 'string', '—', 'Field name and id base'],
46
46
  ['<code>label</code>', 'string', '—', 'Label text — always provide for accessibility'],
@@ -41,7 +41,7 @@ export default {
41
41
  )}
42
42
 
43
43
  ${table(
44
- ['Prop', 'Type', 'Default', ''],
44
+ ['Prop', 'Type', 'Default', 'Description'],
45
45
  [
46
46
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
47
47
  ['<code>variant</code>', 'string', "'default'", "'default' · 'alt' · 'dark'"],
@@ -106,7 +106,7 @@ segmented({ name: 'view', value: 'b', size: 'lg', options: [...] })`
106
106
  ${callout('note', 'The segmented control submits the selected <code>value</code> string under <code>name</code> in FormData. Read it via <code>formData.get(\'period\')</code> in <code>action.onStart</code> or <code>action.run</code>.')}
107
107
 
108
108
  ${table(
109
- ['Prop', 'Type', 'Default', ''],
109
+ ['Prop', 'Type', 'Default', 'Description'],
110
110
  [
111
111
  ['<code>name</code>', 'string', '—', 'Field name — submitted in FormData'],
112
112
  ['<code>options</code>', 'array', '[]', 'Array of <code>{ value, label }</code>'],
@@ -93,7 +93,7 @@ slider({
93
93
  ${callout('warning', 'Do not use <code>data-event="input:mutationName"</code> on a slider. Pulse replaces <code>innerHTML</code> on every mutation, which interrupts the drag mid-gesture. Use <code>change</code> instead — it fires once when the user releases the handle.')}
94
94
 
95
95
  ${table(
96
- ['Prop', 'Type', 'Default', ''],
96
+ ['Prop', 'Type', 'Default', 'Description'],
97
97
  [
98
98
  ['<code>name</code>', 'string', '—', 'Field name — submitted in FormData'],
99
99
  ['<code>label</code>', 'string', '—', 'Visible label text'],
@@ -60,7 +60,7 @@ spinner({ color: 'white' })`
60
60
  )}
61
61
 
62
62
  ${table(
63
- ['Prop', 'Type', 'Default', ''],
63
+ ['Prop', 'Type', 'Default', 'Description'],
64
64
  [
65
65
  ['<code>size</code>', '<code>sm | md | lg</code>', '<code>md</code>', '1rem / 1.5rem / 2.5rem'],
66
66
  ['<code>color</code>', '<code>accent | muted | white</code>', '<code>accent</code>', ''],
@@ -36,7 +36,7 @@ export default {
36
36
  )}
37
37
 
38
38
  ${table(
39
- ['Prop', 'Type', 'Default', ''],
39
+ ['Prop', 'Type', 'Default', 'Description'],
40
40
  [
41
41
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
42
42
  ['<code>gap</code>', 'string', "'md'", "'xs' · 'sm' · 'md' · 'lg' · 'xl'"],
@@ -41,7 +41,7 @@ stat({ label: 'Avg. session', value: '4m 12s' })`
41
41
  )}
42
42
 
43
43
  ${table(
44
- ['Prop', 'Type', 'Default', ''],
44
+ ['Prop', 'Type', 'Default', 'Description'],
45
45
  [
46
46
  ['<code>label</code>', 'string', '—', ''],
47
47
  ['<code>value</code>', 'string', '—', 'Formatted value string — e.g. "2.4k", "98%"'],
@@ -54,7 +54,7 @@ export default {
54
54
  )}
55
55
 
56
56
  ${table(
57
- ['Prop', 'Type', 'Default', ''],
57
+ ['Prop', 'Type', 'Default', 'Description'],
58
58
  [
59
59
  ['<code>steps</code>', 'string[]', '[]', 'Array of step label strings'],
60
60
  ['<code>current</code>', 'number', '0', '0-based index of the active step. Pass <code>steps.length</code> to mark all steps complete.'],
@@ -35,7 +35,7 @@ export default {
35
35
  )}
36
36
 
37
37
  ${table(
38
- ['Prop', 'Type', 'Default', ''],
38
+ ['Prop', 'Type', 'Default', 'Description'],
39
39
  [
40
40
  ['<code>quote</code>', 'string', '—', ''],
41
41
  ['<code>name</code>', 'string', '—', 'Author name — also used for avatar initials'],
@@ -204,7 +204,7 @@ export default {
204
204
  )}
205
205
 
206
206
  ${table(
207
- ['Prop', 'Type', 'Default', ''],
207
+ ['Prop', 'Type', 'Default', 'Description'],
208
208
  [
209
209
  ['<code>direction</code>', 'string', "'vertical'", "'vertical' · 'horizontal'"],
210
210
  ['<code>items</code>', 'array', '[]', 'Array of <code>timelineItem</code> option objects'],
@@ -214,7 +214,7 @@ export default {
214
214
 
215
215
  <h3 class="doc-h3" style="margin-top:2rem">timelineItem() props</h3>
216
216
  ${table(
217
- ['Prop', 'Type', 'Default', ''],
217
+ ['Prop', 'Type', 'Default', 'Description'],
218
218
  [
219
219
  ['<code>content</code>', 'string (HTML)', '—', 'Raw HTML body — accepts any component output'],
220
220
  ['<code>label</code>', 'string', '—', 'Timestamp or step label (escaped)'],
@@ -68,7 +68,7 @@ toggle({ name: 'b', label: 'On and disabled', disabled: true, checked: true })`
68
68
  ${callout('note', 'The switch submits as <code>\'on\'</code> under its <code>name</code> when checked. When unchecked, the field is absent from FormData entirely — the same behaviour as a native checkbox. Read it with <code>formData.get(\'name\') === \'on\'</code>.')}
69
69
 
70
70
  ${table(
71
- ['Prop', 'Type', 'Default', ''],
71
+ ['Prop', 'Type', 'Default', 'Description'],
72
72
  [
73
73
  ['<code>name</code>', 'string', '—', 'Field name — submitted in FormData'],
74
74
  ['<code>label</code>', 'string', '—', 'Visible label text'],
@@ -35,7 +35,7 @@ export default {
35
35
  )}
36
36
 
37
37
  ${table(
38
- ['Prop', 'Type', 'Default', ''],
38
+ ['Prop', 'Type', 'Default', 'Description'],
39
39
  [
40
40
  ['<code>content</code>', 'string', '—', 'Tooltip text (plain text only)'],
41
41
  ['<code>trigger</code>', 'string (HTML)', '—', 'Raw HTML slot — the element the tooltip wraps'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invisibleloop/pulse",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "type": "module",
5
5
  "description": "AI-first frontend framework. The spec is the source of truth.",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- 0.1.34
1
+ 0.1.36
@@ -63,7 +63,7 @@ export function fileUpload({
63
63
 
64
64
  return `<div class="${e(wrapClasses)}">
65
65
  ${labelHtml}
66
- <div class="${e(zoneClasses)}" role="button" tabindex="${disabled ? '-1' : '0'}" aria-label="Upload file"
66
+ <div class="${e(zoneClasses)}" role="button" tabindex="${disabled ? '-1' : '0'}" aria-label="Drag and drop or browse"${disabled ? ' aria-disabled="true"' : ''}
67
67
  >
68
68
  <div class="ui-upload-body">
69
69
  ${icon}
@@ -48,11 +48,13 @@ export function progress({
48
48
  ${showValue && !indeterminate ? `<span class="ui-progress-value">${pct}%</span>` : ''}
49
49
  </div>` : ''
50
50
 
51
+ const ariaLabel = label ? e(label) : indeterminate ? 'Loading' : `${pct}%`
52
+
51
53
  return `<div
52
54
  class="${e(classes)}"
53
55
  role="progressbar"
56
+ aria-label="${ariaLabel}"
54
57
  ${!indeterminate ? `aria-valuenow="${clamped}" aria-valuemin="0" aria-valuemax="${max}"` : ''}
55
- ${label ? `aria-label="${e(label)}"` : ''}
56
58
  style="--progress-height:${h}"
57
59
  >${header}
58
60
  <div class="ui-progress-track">