@invisibleloop/pulse 0.1.21
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/commands/build-page.md +59 -0
- package/.claude/commands/new-doc-page.md +45 -0
- package/.claude/commands/verify.md +52 -0
- package/.claude/pulse-checklist.md +111 -0
- package/.claude/settings.local.json +102 -0
- package/.github/workflows/ci.yml +22 -0
- package/.github/workflows/publish.yml +41 -0
- package/.pulse/load-reports/home/1773432711417.json +22 -0
- package/CLAUDE.md +383 -0
- package/README.md +95 -0
- package/docs/.claude/pulse-checklist.md +111 -0
- package/docs/public/.pulse-ui-version +1 -0
- package/docs/public/dist/accessibility.boot-5DVTARJU.js +115 -0
- package/docs/public/dist/actions.boot-P66HKQEM.js +164 -0
- package/docs/public/dist/auth.boot-IMAJAUPH.js +140 -0
- package/docs/public/dist/caching.boot-DVR6KDE7.js +53 -0
- package/docs/public/dist/components--accordion.boot-3HVKMNWC.js +11 -0
- package/docs/public/dist/components--alert.boot-GCEXOZAC.js +6 -0
- package/docs/public/dist/components--app-badge.boot-DVT3GCHJ.js +6 -0
- package/docs/public/dist/components--avatar.boot-PSW24EVA.js +5 -0
- package/docs/public/dist/components--badge.boot-TYDY2RMK.js +7 -0
- package/docs/public/dist/components--banner.boot-EI5PZSZK.js +7 -0
- package/docs/public/dist/components--breadcrumbs.boot-SMA2E2GO.js +34 -0
- package/docs/public/dist/components--button.boot-J54BQM2E.js +23 -0
- package/docs/public/dist/components--card.boot-PZGNDIB6.js +138 -0
- package/docs/public/dist/components--carousel.boot-TP6LPFZZ.js +12 -0
- package/docs/public/dist/components--charts.boot-2EOYQWKL.js +108 -0
- package/docs/public/dist/components--checkbox.boot-DS5BSL6T.js +54 -0
- package/docs/public/dist/components--cluster.boot-HHVIBBJG.js +9 -0
- package/docs/public/dist/components--code-window.boot-2GR2DV33.js +20 -0
- package/docs/public/dist/components--container.boot-7LOOGK2K.js +5 -0
- package/docs/public/dist/components--cta.boot-FSNZ5YRT.js +11 -0
- package/docs/public/dist/components--divider.boot-3NI2C3QG.js +6 -0
- package/docs/public/dist/components--empty.boot-YX2UR3PV.js +7 -0
- package/docs/public/dist/components--feature.boot-MUD7NSUO.js +13 -0
- package/docs/public/dist/components--fieldset.boot-J7BYHMKF.js +19 -0
- package/docs/public/dist/components--fileupload.boot-NIKVTTPD.js +52 -0
- package/docs/public/dist/components--footer.boot-EYUK5FRG.js +14 -0
- package/docs/public/dist/components--grid.boot-URDQVDDR.js +59 -0
- package/docs/public/dist/components--heading.boot-BPQKU43E.js +44 -0
- package/docs/public/dist/components--hero.boot-4RAPRGAB.js +17 -0
- package/docs/public/dist/components--icons.boot-ZITNU5JP.js +68 -0
- package/docs/public/dist/components--image.boot-XEEGHQZF.js +19 -0
- package/docs/public/dist/components--input.boot-SGASZG5K.js +7 -0
- package/docs/public/dist/components--list.boot-W3XC5MHD.js +55 -0
- package/docs/public/dist/components--media.boot-5VFIETZO.js +13 -0
- package/docs/public/dist/components--modal.boot-RZUYXBN2.js +47 -0
- package/docs/public/dist/components--nav.boot-ODBOHU7O.js +33 -0
- package/docs/public/dist/components--pricing.boot-4AQ4ZVBY.js +21 -0
- package/docs/public/dist/components--progress.boot-GHAGYZOK.js +30 -0
- package/docs/public/dist/components--prose.boot-QANJL6JI.js +67 -0
- package/docs/public/dist/components--pullquote.boot-Q2WMNAZU.js +22 -0
- package/docs/public/dist/components--radio.boot-TJRDQ2OL.js +75 -0
- package/docs/public/dist/components--rating.boot-QBAN6DEL.js +38 -0
- package/docs/public/dist/components--search.boot-PXH5O5AG.js +17 -0
- package/docs/public/dist/components--section.boot-AQGIYHWW.js +12 -0
- package/docs/public/dist/components--segmented.boot-BEVTKEJO.js +33 -0
- package/docs/public/dist/components--select.boot-47X5RHOC.js +10 -0
- package/docs/public/dist/components--slider.boot-PSRRX7XL.js +47 -0
- package/docs/public/dist/components--spinner.boot-MZ5MO2OH.js +22 -0
- package/docs/public/dist/components--stack.boot-DI4NJXBF.js +9 -0
- package/docs/public/dist/components--stat.boot-QMFUWBQT.js +9 -0
- package/docs/public/dist/components--stepper.boot-34PP2NEV.js +22 -0
- package/docs/public/dist/components--table.boot-FCQGSFIQ.js +11 -0
- package/docs/public/dist/components--testimonial.boot-DWQPDKYG.js +11 -0
- package/docs/public/dist/components--textarea.boot-QVXLBOJ5.js +4 -0
- package/docs/public/dist/components--timeline.boot-26LN52P2.js +95 -0
- package/docs/public/dist/components--toggle.boot-IQQEI76S.js +29 -0
- package/docs/public/dist/components--tooltip.boot-LGHCO6NN.js +9 -0
- package/docs/public/dist/components.boot-SE6PQ4P7.js +103 -0
- package/docs/public/dist/config.boot-DTRRWUE6.js +126 -0
- package/docs/public/dist/constraints.boot-DUHDZBMC.js +71 -0
- package/docs/public/dist/deploy.boot-SLAD3NI2.js +163 -0
- package/docs/public/dist/docs-8e3d4b5c.css +1 -0
- package/docs/public/dist/extending.boot-UA3CN243.js +159 -0
- package/docs/public/dist/faq.boot-6EQAWLQR.js +43 -0
- package/docs/public/dist/getting-started.boot-TDKIFL5U.js +86 -0
- package/docs/public/dist/guard.boot-AUHAWTG4.js +80 -0
- package/docs/public/dist/home.boot-BVQXRH32.js +383 -0
- package/docs/public/dist/how-it-works.boot-LTWAKWKW.js +104 -0
- package/docs/public/dist/hydration.boot-JRM6IPJL.js +78 -0
- package/docs/public/dist/images.boot-M6ZVKTZS.js +80 -0
- package/docs/public/dist/manifest.json +94 -0
- package/docs/public/dist/meta.boot-7NXGPHR4.js +79 -0
- package/docs/public/dist/mutations.boot-F6F43UDX.js +79 -0
- package/docs/public/dist/navigation.boot-AOXWS3ZF.js +57 -0
- package/docs/public/dist/performance.boot-C3UPCOBK.js +98 -0
- package/docs/public/dist/persist.boot-WT32PQOQ.js +61 -0
- package/docs/public/dist/project-structure.boot-FB3LRVJ4.js +63 -0
- package/docs/public/dist/prompt-examples.boot-YKR4VDK4.js +31 -0
- package/docs/public/dist/pulse-ui-81a85c03.css +1 -0
- package/docs/public/dist/raw-responses.boot-M4KA5YXL.js +104 -0
- package/docs/public/dist/routing.boot-FNX5FDGH.js +70 -0
- package/docs/public/dist/runtime-B73WLANC.js +1 -0
- package/docs/public/dist/runtime-KO4BHUQ3.js +49 -0
- package/docs/public/dist/runtime-L2HNXIHW.js +59 -0
- package/docs/public/dist/runtime-QFURDKA2.js +5 -0
- package/docs/public/dist/runtime-UVPXO4IR.js +375 -0
- package/docs/public/dist/runtime-VMJA3Z4N.js +10 -0
- package/docs/public/dist/runtime-ZJ4FXT5O.js +11 -0
- package/docs/public/dist/server-api.boot-K7X3LCFB.js +219 -0
- package/docs/public/dist/server-data.boot-Y7HQYC4R.js +157 -0
- package/docs/public/dist/slash-commands.boot-V2UV7OW2.js +26 -0
- package/docs/public/dist/spec.boot-2WU7ZHCV.js +159 -0
- package/docs/public/dist/state.boot-B24GUE3R.js +73 -0
- package/docs/public/dist/store.boot-TLIB4XHH.js +150 -0
- package/docs/public/dist/streaming.boot-W2DZSMW4.js +80 -0
- package/docs/public/dist/stripe.boot-QN3C2GEL.js +164 -0
- package/docs/public/dist/supabase.boot-BG4XXLZE.js +303 -0
- package/docs/public/dist/testing.boot-6U4WKMTE.js +130 -0
- package/docs/public/dist/validation.boot-PQHYGW5B.js +100 -0
- package/docs/public/docs.css +2020 -0
- package/docs/public/menu.js +83 -0
- package/docs/public/pulse-ui.css +2739 -0
- package/docs/public/pulse-ui.js +236 -0
- package/docs/server.js +192 -0
- package/docs/src/lib/component-page.js +47 -0
- package/docs/src/lib/highlight.js +255 -0
- package/docs/src/lib/layout.js +131 -0
- package/docs/src/lib/metrics-store.js +6 -0
- package/docs/src/lib/nav.js +159 -0
- package/docs/src/lib/stats.js +81 -0
- package/docs/src/pages/accessibility.js +157 -0
- package/docs/src/pages/actions.js +191 -0
- package/docs/src/pages/auth.js +177 -0
- package/docs/src/pages/caching.js +95 -0
- package/docs/src/pages/components/accordion.js +48 -0
- package/docs/src/pages/components/alert.js +35 -0
- package/docs/src/pages/components/app-badge.js +41 -0
- package/docs/src/pages/components/avatar.js +35 -0
- package/docs/src/pages/components/badge.js +36 -0
- package/docs/src/pages/components/banner.js +45 -0
- package/docs/src/pages/components/breadcrumbs.js +94 -0
- package/docs/src/pages/components/button.js +84 -0
- package/docs/src/pages/components/card.js +225 -0
- package/docs/src/pages/components/carousel.js +72 -0
- package/docs/src/pages/components/charts.js +278 -0
- package/docs/src/pages/components/checkbox.js +129 -0
- package/docs/src/pages/components/cluster.js +47 -0
- package/docs/src/pages/components/code-window.js +57 -0
- package/docs/src/pages/components/container.js +40 -0
- package/docs/src/pages/components/cta.js +53 -0
- package/docs/src/pages/components/divider.js +37 -0
- package/docs/src/pages/components/empty.js +36 -0
- package/docs/src/pages/components/feature.js +60 -0
- package/docs/src/pages/components/fieldset.js +65 -0
- package/docs/src/pages/components/fileupload.js +127 -0
- package/docs/src/pages/components/footer.js +58 -0
- package/docs/src/pages/components/grid.js +165 -0
- package/docs/src/pages/components/heading.js +107 -0
- package/docs/src/pages/components/hero.js +65 -0
- package/docs/src/pages/components/icons.js +285 -0
- package/docs/src/pages/components/image.js +71 -0
- package/docs/src/pages/components/input.js +51 -0
- package/docs/src/pages/components/list.js +112 -0
- package/docs/src/pages/components/media.js +51 -0
- package/docs/src/pages/components/modal.js +111 -0
- package/docs/src/pages/components/nav.js +86 -0
- package/docs/src/pages/components/pricing.js +68 -0
- package/docs/src/pages/components/progress.js +102 -0
- package/docs/src/pages/components/prose.js +111 -0
- package/docs/src/pages/components/pullquote.js +71 -0
- package/docs/src/pages/components/radio.js +194 -0
- package/docs/src/pages/components/rating.js +106 -0
- package/docs/src/pages/components/search.js +61 -0
- package/docs/src/pages/components/section.js +59 -0
- package/docs/src/pages/components/segmented.js +121 -0
- package/docs/src/pages/components/select.js +45 -0
- package/docs/src/pages/components/slider.js +114 -0
- package/docs/src/pages/components/spinner.js +73 -0
- package/docs/src/pages/components/stack.js +48 -0
- package/docs/src/pages/components/stat.js +55 -0
- package/docs/src/pages/components/stepper.js +66 -0
- package/docs/src/pages/components/table.js +45 -0
- package/docs/src/pages/components/testimonial.js +49 -0
- package/docs/src/pages/components/textarea.js +31 -0
- package/docs/src/pages/components/timeline.js +227 -0
- package/docs/src/pages/components/toggle.js +84 -0
- package/docs/src/pages/components/tooltip.js +48 -0
- package/docs/src/pages/components.js +204 -0
- package/docs/src/pages/config.js +193 -0
- package/docs/src/pages/constraints.js +99 -0
- package/docs/src/pages/deploy.js +233 -0
- package/docs/src/pages/extending.js +198 -0
- package/docs/src/pages/faq.js +96 -0
- package/docs/src/pages/getting-started.js +106 -0
- package/docs/src/pages/guard.js +121 -0
- package/docs/src/pages/home.js +401 -0
- package/docs/src/pages/how-it-works.js +183 -0
- package/docs/src/pages/hydration.js +98 -0
- package/docs/src/pages/images.js +121 -0
- package/docs/src/pages/meta.js +120 -0
- package/docs/src/pages/mutations.js +106 -0
- package/docs/src/pages/navigation.js +85 -0
- package/docs/src/pages/performance.js +157 -0
- package/docs/src/pages/persist.js +88 -0
- package/docs/src/pages/project-structure.js +90 -0
- package/docs/src/pages/prompt-examples.js +186 -0
- package/docs/src/pages/raw-responses.js +124 -0
- package/docs/src/pages/routing.js +99 -0
- package/docs/src/pages/server-api.js +281 -0
- package/docs/src/pages/server-data.js +185 -0
- package/docs/src/pages/slash-commands.js +55 -0
- package/docs/src/pages/spec.js +207 -0
- package/docs/src/pages/state.js +101 -0
- package/docs/src/pages/store.js +181 -0
- package/docs/src/pages/streaming.js +108 -0
- package/docs/src/pages/stripe.js +193 -0
- package/docs/src/pages/supabase.js +323 -0
- package/docs/src/pages/testing.js +198 -0
- package/docs/src/pages/validation.js +138 -0
- package/examples/contact.js +166 -0
- package/examples/counter.js +94 -0
- package/examples/dev.server.js +91 -0
- package/examples/examples.test.js +394 -0
- package/examples/pricing.js +244 -0
- package/examples/products.js +191 -0
- package/examples/quiz.js +208 -0
- package/examples/shared.js +78 -0
- package/examples/todos.js +162 -0
- package/package.json +75 -0
- package/public/.pulse-ui-version +1 -0
- package/public/chippy-bird.css +246 -0
- package/public/examples/contact.css +119 -0
- package/public/examples/counter.css +79 -0
- package/public/examples/pricing.css +132 -0
- package/public/examples/products.css +100 -0
- package/public/examples/quiz.css +200 -0
- package/public/examples/todos.css +137 -0
- package/public/favicon.ico +0 -0
- package/public/log-dashboard.css +383 -0
- package/public/pulse-ui.css +2740 -0
- package/public/pulse-ui.js +236 -0
- package/public/pulse.css +149 -0
- package/scripts/build.js +411 -0
- package/src/agent/checklist.md +111 -0
- package/src/agent/coverage-check.js +66 -0
- package/src/agent/guide-components.md +274 -0
- package/src/agent/guide-examples.md +54 -0
- package/src/agent/guide-routing.md +36 -0
- package/src/agent/guide-server.md +258 -0
- package/src/agent/guide-spec.md +103 -0
- package/src/agent/guide-styles.md +191 -0
- package/src/agent/guide.md +979 -0
- package/src/agent/identity.md +106 -0
- package/src/agent/workflow.md +108 -0
- package/src/cli/cli.test.js +82 -0
- package/src/cli/dev.js +195 -0
- package/src/cli/discover.js +113 -0
- package/src/cli/index.js +361 -0
- package/src/cli/load-report.js +91 -0
- package/src/cli/load-runner.js +121 -0
- package/src/cli/report-server.js +723 -0
- package/src/cli/report.js +116 -0
- package/src/cli/scaffold.archive.js +1371 -0
- package/src/cli/scaffold.js +349 -0
- package/src/cli/start.js +74 -0
- package/src/html.js +19 -0
- package/src/mcp/server.js +884 -0
- package/src/mcp/validate-worker.js +110 -0
- package/src/runtime/image.js +74 -0
- package/src/runtime/image.test.js +111 -0
- package/src/runtime/index.js +621 -0
- package/src/runtime/navigate.js +146 -0
- package/src/runtime/runtime.test.js +773 -0
- package/src/runtime/ssr.js +464 -0
- package/src/runtime/ssr.test.js +421 -0
- package/src/runtime/store.js +92 -0
- package/src/runtime/toast.js +163 -0
- package/src/server/index.js +1386 -0
- package/src/server/server.test.js +1248 -0
- package/src/spec/schema.js +428 -0
- package/src/spec/schema.test.js +291 -0
- package/src/store/index.js +102 -0
- package/src/store/store.test.js +210 -0
- package/src/testing/html.js +283 -0
- package/src/testing/index.js +249 -0
- package/src/testing/testing.test.js +450 -0
- package/src/ui/accordion.js +28 -0
- package/src/ui/alert.js +43 -0
- package/src/ui/app-badge.js +48 -0
- package/src/ui/avatar.js +47 -0
- package/src/ui/badge.js +24 -0
- package/src/ui/banner.js +26 -0
- package/src/ui/breadcrumbs.js +38 -0
- package/src/ui/button.js +66 -0
- package/src/ui/card.js +34 -0
- package/src/ui/carousel.js +59 -0
- package/src/ui/charts.js +321 -0
- package/src/ui/checkbox.js +65 -0
- package/src/ui/cluster.js +44 -0
- package/src/ui/code-window.js +39 -0
- package/src/ui/container.js +24 -0
- package/src/ui/cta.js +37 -0
- package/src/ui/divider.js +29 -0
- package/src/ui/empty.js +33 -0
- package/src/ui/feature.js +33 -0
- package/src/ui/fieldset.js +37 -0
- package/src/ui/fileupload.js +89 -0
- package/src/ui/footer.js +38 -0
- package/src/ui/grid.js +36 -0
- package/src/ui/heading.js +45 -0
- package/src/ui/hero.js +37 -0
- package/src/ui/icons.js +161 -0
- package/src/ui/index.js +89 -0
- package/src/ui/input.js +74 -0
- package/src/ui/list.js +36 -0
- package/src/ui/media.js +44 -0
- package/src/ui/modal.js +80 -0
- package/src/ui/nav.js +61 -0
- package/src/ui/pricing.js +56 -0
- package/src/ui/progress.js +62 -0
- package/src/ui/prose.js +29 -0
- package/src/ui/pullquote.js +34 -0
- package/src/ui/radio.js +102 -0
- package/src/ui/rating.js +93 -0
- package/src/ui/search.js +77 -0
- package/src/ui/section.js +69 -0
- package/src/ui/segmented.js +50 -0
- package/src/ui/select.js +77 -0
- package/src/ui/slider.js +84 -0
- package/src/ui/spinner.js +34 -0
- package/src/ui/stack.js +36 -0
- package/src/ui/stat.js +52 -0
- package/src/ui/stepper.js +46 -0
- package/src/ui/switch.js +57 -0
- package/src/ui/table.js +45 -0
- package/src/ui/testimonial.js +48 -0
- package/src/ui/textarea.js +72 -0
- package/src/ui/timeline.js +72 -0
- package/src/ui/tooltip.js +28 -0
- package/src/ui/ui.test.js +1241 -0
- package/src/ui/uiimage.js +65 -0
- package/tsconfig.json +13 -0
- package/types/html.d.ts +17 -0
- package/types/image.d.ts +70 -0
- package/types/index.d.ts +7 -0
- package/types/navigate.d.ts +38 -0
- package/types/runtime.d.ts +63 -0
- package/types/schema.d.ts +243 -0
- package/types/server.d.ts +145 -0
- package/types/ssr.d.ts +110 -0
- package/types/testing.d.ts +154 -0
- package/types/ui.d.ts +704 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { accordion } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/accordion')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/accordion',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Accordion — Pulse Docs',
|
|
12
|
+
description: 'Accordion component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/accordion',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'accordion',
|
|
21
|
+
description: 'Collapsible FAQ items built on native <code><details></code>/<code><summary></code> — no JavaScript required. The open/close animation is handled entirely by the browser.',
|
|
22
|
+
content: `
|
|
23
|
+
${demo(
|
|
24
|
+
accordion({
|
|
25
|
+
items: [
|
|
26
|
+
{ question: 'Is there a free plan?', answer: 'Yes — the free plan includes up to 3 pages and community support, with no credit card required.' },
|
|
27
|
+
{ question: 'Can I cancel anytime?', answer: 'Absolutely. There are no contracts or lock-in periods. Cancel from your account settings at any time.' },
|
|
28
|
+
{ question: 'Does it work on older devices?', answer: 'The app supports iOS 14+ and Android 8+, covering over 97% of active devices.' },
|
|
29
|
+
],
|
|
30
|
+
}),
|
|
31
|
+
`accordion({
|
|
32
|
+
items: [
|
|
33
|
+
{ question: 'Is there a free plan?', answer: 'Yes — up to 3 pages, no card required.' },
|
|
34
|
+
{ question: 'Can I cancel anytime?', answer: 'No contracts. Cancel from your account.' },
|
|
35
|
+
{ question: 'Works on older devices?', answer: 'iOS 14+ and Android 8+.' },
|
|
36
|
+
],
|
|
37
|
+
})`
|
|
38
|
+
)}
|
|
39
|
+
|
|
40
|
+
${table(
|
|
41
|
+
['Prop', 'Type', 'Default', ''],
|
|
42
|
+
[
|
|
43
|
+
['<code>items</code>', 'array', '[]', '<code>{ question: string, answer: string }[]</code>'],
|
|
44
|
+
]
|
|
45
|
+
)}
|
|
46
|
+
`,
|
|
47
|
+
}),
|
|
48
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { alert } from '../../../../src/ui/index.js'
|
|
4
|
+
|
|
5
|
+
const { prev, next } = prevNext('/components/alert')
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
route: '/components/alert',
|
|
9
|
+
meta: {
|
|
10
|
+
title: 'Alert — Pulse Docs',
|
|
11
|
+
description: 'Alert component for Pulse UI.',
|
|
12
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
13
|
+
},
|
|
14
|
+
state: {},
|
|
15
|
+
view: () => renderComponentPage({
|
|
16
|
+
currentHref: '/components/alert',
|
|
17
|
+
prev,
|
|
18
|
+
next,
|
|
19
|
+
name: 'alert',
|
|
20
|
+
description: '<code>error</code> and <code>warning</code> use <code>role="alert"</code> — screen readers announce them immediately. <code>info</code> and <code>success</code> use <code>role="status"</code> for a polite announcement.',
|
|
21
|
+
content: `
|
|
22
|
+
${demo(
|
|
23
|
+
alert({ variant: 'info', content: 'Your account is pending email verification.' }) +
|
|
24
|
+
alert({ variant: 'success', title: 'Saved', content: 'Your changes have been saved.' }) +
|
|
25
|
+
alert({ variant: 'warning', title: 'Heads up', content: 'This action cannot be undone.' }) +
|
|
26
|
+
alert({ variant: 'error', title: 'Failed', content: 'Something went wrong. Please try again.' }),
|
|
27
|
+
`alert({ variant: 'info', content: 'Your account is pending email verification.' })
|
|
28
|
+
alert({ variant: 'success', title: 'Saved', content: 'Your changes have been saved.' })
|
|
29
|
+
alert({ variant: 'warning', title: 'Heads up', content: 'This action cannot be undone.' })
|
|
30
|
+
alert({ variant: 'error', title: 'Failed', content: escHtml(server.error) })`,
|
|
31
|
+
{ col: true }
|
|
32
|
+
)}
|
|
33
|
+
`,
|
|
34
|
+
}),
|
|
35
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { appBadge } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/app-badge')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/app-badge',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'App Badge — Pulse Docs',
|
|
12
|
+
description: 'App Badge component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/app-badge',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'appBadge',
|
|
21
|
+
description: 'App Store and Google Play download buttons. Designed to sit in a <code>hero()</code> actions slot, or anywhere a download CTA is needed.',
|
|
22
|
+
content: `
|
|
23
|
+
${demo(
|
|
24
|
+
`<div style="display:flex;gap:.75rem;flex-wrap:wrap">` +
|
|
25
|
+
appBadge({ store: 'apple', href: '#' }) +
|
|
26
|
+
appBadge({ store: 'google', href: '#' }) +
|
|
27
|
+
`</div>`,
|
|
28
|
+
`appBadge({ store: 'apple', href: appStoreUrl })
|
|
29
|
+
appBadge({ store: 'google', href: playStoreUrl })`
|
|
30
|
+
)}
|
|
31
|
+
|
|
32
|
+
${table(
|
|
33
|
+
['Prop', 'Type', 'Default', ''],
|
|
34
|
+
[
|
|
35
|
+
['<code>store</code>', 'string', "'apple'", "'apple' or 'google'"],
|
|
36
|
+
['<code>href</code>', 'string', "'#'", 'Link to the app store listing'],
|
|
37
|
+
]
|
|
38
|
+
)}
|
|
39
|
+
`,
|
|
40
|
+
}),
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { avatar } from '../../../../src/ui/index.js'
|
|
4
|
+
|
|
5
|
+
const { prev, next } = prevNext('/components/avatar')
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
route: '/components/avatar',
|
|
9
|
+
meta: {
|
|
10
|
+
title: 'Avatar — Pulse Docs',
|
|
11
|
+
description: 'Avatar component for Pulse UI.',
|
|
12
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
13
|
+
},
|
|
14
|
+
state: {},
|
|
15
|
+
view: () => renderComponentPage({
|
|
16
|
+
currentHref: '/components/avatar',
|
|
17
|
+
prev,
|
|
18
|
+
next,
|
|
19
|
+
name: 'avatar',
|
|
20
|
+
description: 'When <code>src</code> is set, renders an <code><img></code> with <code>loading="lazy"</code>. Without <code>src</code>, shows initials derived from <code>alt</code>.',
|
|
21
|
+
content: `
|
|
22
|
+
${demo(
|
|
23
|
+
`<div style="display:flex;gap:1rem;align-items:center">` +
|
|
24
|
+
avatar({ alt: 'Alice Smith', size: 'sm' }) +
|
|
25
|
+
avatar({ alt: 'Bob Jones', size: 'md' }) +
|
|
26
|
+
avatar({ alt: 'Carol White', size: 'lg' }) +
|
|
27
|
+
avatar({ alt: 'Dan Brown', size: 'xl' }) +
|
|
28
|
+
`</div>`,
|
|
29
|
+
`avatar({ src: user.avatarUrl, alt: user.name, size: 'md' })
|
|
30
|
+
avatar({ alt: 'Alice Smith' }) // renders initials "AS"
|
|
31
|
+
avatar({ alt: 'Alice', initials: 'A' }) // explicit initials`
|
|
32
|
+
)}
|
|
33
|
+
`,
|
|
34
|
+
}),
|
|
35
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { badge } from '../../../../src/ui/index.js'
|
|
4
|
+
|
|
5
|
+
const { prev, next } = prevNext('/components/badge')
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
route: '/components/badge',
|
|
9
|
+
meta: {
|
|
10
|
+
title: 'Badge — Pulse Docs',
|
|
11
|
+
description: 'Badge component for Pulse UI.',
|
|
12
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
13
|
+
},
|
|
14
|
+
state: {},
|
|
15
|
+
view: () => renderComponentPage({
|
|
16
|
+
currentHref: '/components/badge',
|
|
17
|
+
prev,
|
|
18
|
+
next,
|
|
19
|
+
name: 'badge',
|
|
20
|
+
description: 'Inline status and label indicator. Five semantic variants cover the most common states.',
|
|
21
|
+
content: `
|
|
22
|
+
${demo(
|
|
23
|
+
badge({ label: 'Default', variant: 'default' }) + ' ' +
|
|
24
|
+
badge({ label: 'Info', variant: 'info' }) + ' ' +
|
|
25
|
+
badge({ label: 'Success', variant: 'success' }) + ' ' +
|
|
26
|
+
badge({ label: 'Warning', variant: 'warning' }) + ' ' +
|
|
27
|
+
badge({ label: 'Error', variant: 'error' }),
|
|
28
|
+
`badge({ label: 'Default', variant: 'default' })
|
|
29
|
+
badge({ label: 'Info', variant: 'info' })
|
|
30
|
+
badge({ label: 'Success', variant: 'success' })
|
|
31
|
+
badge({ label: 'Warning', variant: 'warning' })
|
|
32
|
+
badge({ label: 'Error', variant: 'error' })`
|
|
33
|
+
)}
|
|
34
|
+
`,
|
|
35
|
+
}),
|
|
36
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { banner, stack } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/banner')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/banner',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Banner — Pulse Docs',
|
|
12
|
+
description: 'Banner component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/banner',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'banner',
|
|
21
|
+
description: 'Full-width announcement bar. Sits above the nav for promotions and launch announcements. The content slot accepts links and formatted text.',
|
|
22
|
+
content: `
|
|
23
|
+
${demo(
|
|
24
|
+
stack({
|
|
25
|
+
gap: 'sm',
|
|
26
|
+
content:
|
|
27
|
+
banner({ variant: 'promo', content: '🎉 Chippy Bird v2.0 is out — <a href="#" style="color:inherit;text-decoration:underline">see what\'s new</a>' }) +
|
|
28
|
+
banner({ variant: 'info', content: 'Android support is coming soon. <a href="#" style="color:inherit;text-decoration:underline">Join the waitlist</a>' }) +
|
|
29
|
+
banner({ variant: 'warning', content: 'Scheduled maintenance Sunday 2–4am UTC.' }),
|
|
30
|
+
}),
|
|
31
|
+
`banner({ variant: 'promo', content:
|
|
32
|
+
'🎉 v2.0 is out — ' + button({ label: "See what's new", variant: 'ghost', size: 'sm', href: '/changelog' })
|
|
33
|
+
})`
|
|
34
|
+
)}
|
|
35
|
+
|
|
36
|
+
${table(
|
|
37
|
+
['Prop', 'Type', 'Default', ''],
|
|
38
|
+
[
|
|
39
|
+
['<code>content</code>', 'string (HTML)', '—', 'Raw HTML slot'],
|
|
40
|
+
['<code>variant</code>', 'string', "'info'", "'info' · 'promo' · 'warning'"],
|
|
41
|
+
]
|
|
42
|
+
)}
|
|
43
|
+
`,
|
|
44
|
+
}),
|
|
45
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { breadcrumbs } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/breadcrumbs')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/breadcrumbs',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Breadcrumbs — Pulse Docs',
|
|
12
|
+
description: 'Accessible breadcrumb navigation component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/breadcrumbs',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'breadcrumbs',
|
|
21
|
+
description: 'Accessible breadcrumb navigation. The current page item renders as a <code><span></code> with <code>aria-current="page"</code>; all preceding items render as links.',
|
|
22
|
+
content: `
|
|
23
|
+
|
|
24
|
+
<h2 class="doc-h2" id="basic">Basic</h2>
|
|
25
|
+
${demo(
|
|
26
|
+
breadcrumbs({
|
|
27
|
+
items: [
|
|
28
|
+
{ label: 'Home', href: '/' },
|
|
29
|
+
{ label: 'Products', href: '/products' },
|
|
30
|
+
{ label: 'Sneakers' },
|
|
31
|
+
],
|
|
32
|
+
}),
|
|
33
|
+
`breadcrumbs({
|
|
34
|
+
items: [
|
|
35
|
+
{ label: 'Home', href: '/' },
|
|
36
|
+
{ label: 'Products', href: '/products' },
|
|
37
|
+
{ label: 'Sneakers' },
|
|
38
|
+
],
|
|
39
|
+
})`
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
<h2 class="doc-h2" id="longer">Longer trail</h2>
|
|
43
|
+
${demo(
|
|
44
|
+
breadcrumbs({
|
|
45
|
+
items: [
|
|
46
|
+
{ label: 'Home', href: '/' },
|
|
47
|
+
{ label: 'Shop', href: '/shop' },
|
|
48
|
+
{ label: 'Footwear', href: '/shop/footwear' },
|
|
49
|
+
{ label: 'Sneakers', href: '/shop/footwear/sneakers' },
|
|
50
|
+
{ label: 'Air Max 90' },
|
|
51
|
+
],
|
|
52
|
+
}),
|
|
53
|
+
`breadcrumbs({
|
|
54
|
+
items: [
|
|
55
|
+
{ label: 'Home', href: '/' },
|
|
56
|
+
{ label: 'Shop', href: '/shop' },
|
|
57
|
+
{ label: 'Footwear', href: '/shop/footwear' },
|
|
58
|
+
{ label: 'Sneakers', href: '/shop/footwear/sneakers' },
|
|
59
|
+
{ label: 'Air Max 90' },
|
|
60
|
+
],
|
|
61
|
+
})`
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
<h2 class="doc-h2" id="separator">Custom separator</h2>
|
|
65
|
+
${demo(
|
|
66
|
+
breadcrumbs({
|
|
67
|
+
separator: '›',
|
|
68
|
+
items: [
|
|
69
|
+
{ label: 'Docs', href: '/docs' },
|
|
70
|
+
{ label: 'Components', href: '/docs/components' },
|
|
71
|
+
{ label: 'Breadcrumbs' },
|
|
72
|
+
],
|
|
73
|
+
}),
|
|
74
|
+
`breadcrumbs({
|
|
75
|
+
separator: '›',
|
|
76
|
+
items: [
|
|
77
|
+
{ label: 'Docs', href: '/docs' },
|
|
78
|
+
{ label: 'Components', href: '/docs/components' },
|
|
79
|
+
{ label: 'Breadcrumbs' },
|
|
80
|
+
],
|
|
81
|
+
})`
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
${table(
|
|
85
|
+
['Prop', 'Type', 'Default', ''],
|
|
86
|
+
[
|
|
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
|
+
['<code>separator</code>', 'string', "'/'", 'Character rendered between items'],
|
|
89
|
+
['<code>class</code>', 'string', '—', ''],
|
|
90
|
+
]
|
|
91
|
+
)}
|
|
92
|
+
`,
|
|
93
|
+
}),
|
|
94
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { button } from '../../../../src/ui/index.js'
|
|
5
|
+
import { iconArrowRight, iconDownload, iconPlus, iconSend } from '../../../../src/ui/icons.js'
|
|
6
|
+
|
|
7
|
+
const { prev, next } = prevNext('/components/button')
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
route: '/components/button',
|
|
11
|
+
meta: {
|
|
12
|
+
title: 'Button — Pulse Docs',
|
|
13
|
+
description: 'Button component for Pulse UI.',
|
|
14
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
15
|
+
},
|
|
16
|
+
state: {},
|
|
17
|
+
view: () => renderComponentPage({
|
|
18
|
+
currentHref: '/components/button',
|
|
19
|
+
prev,
|
|
20
|
+
next,
|
|
21
|
+
name: 'button',
|
|
22
|
+
description: 'Renders as <code><button></code> by default. Pass <code>href</code> to get an <code><a></code> that looks identical. All four variants are shown below.',
|
|
23
|
+
content: `
|
|
24
|
+
${demo(
|
|
25
|
+
button({ label: 'Primary', variant: 'primary' }) +
|
|
26
|
+
button({ label: 'Secondary', variant: 'secondary' }) +
|
|
27
|
+
button({ label: 'Ghost', variant: 'ghost' }) +
|
|
28
|
+
button({ label: 'Danger', variant: 'danger' }),
|
|
29
|
+
`button({ label: 'Primary', variant: 'primary' })
|
|
30
|
+
button({ label: 'Secondary', variant: 'secondary' })
|
|
31
|
+
button({ label: 'Ghost', variant: 'ghost' })
|
|
32
|
+
button({ label: 'Danger', variant: 'danger' })`
|
|
33
|
+
)}
|
|
34
|
+
|
|
35
|
+
${demo(
|
|
36
|
+
button({ label: 'Small', size: 'sm' }) +
|
|
37
|
+
button({ label: 'Medium', size: 'md' }) +
|
|
38
|
+
button({ label: 'Large', size: 'lg' }),
|
|
39
|
+
`button({ label: 'Small', size: 'sm' })
|
|
40
|
+
button({ label: 'Medium', size: 'md' })
|
|
41
|
+
button({ label: 'Large', size: 'lg' })`
|
|
42
|
+
)}
|
|
43
|
+
|
|
44
|
+
${demo(
|
|
45
|
+
button({ label: 'Download', icon: iconDownload({ size: 14 }) }) +
|
|
46
|
+
button({ label: 'New item', icon: iconPlus({ size: 14 }), variant: 'secondary' }) +
|
|
47
|
+
button({ label: 'Continue', iconAfter: iconArrowRight({ size: 14 }) }) +
|
|
48
|
+
button({ label: 'Send', iconAfter: iconSend({ size: 14 }), variant: 'ghost' }),
|
|
49
|
+
`import { iconDownload, iconPlus, iconArrowRight, iconSend } from '@invisibleloop/pulse/ui'
|
|
50
|
+
|
|
51
|
+
button({ label: 'Download', icon: iconDownload({ size: 14 }) })
|
|
52
|
+
button({ label: 'New item', icon: iconPlus({ size: 14 }), variant: 'secondary' })
|
|
53
|
+
button({ label: 'Continue', iconAfter: iconArrowRight({ size: 14 }) })
|
|
54
|
+
button({ label: 'Send', iconAfter: iconSend({ size: 14 }), variant: 'ghost' })`
|
|
55
|
+
)}
|
|
56
|
+
|
|
57
|
+
${demo(
|
|
58
|
+
button({ label: 'Disabled', disabled: true }) +
|
|
59
|
+
button({ label: 'Link', href: '/docs' }) +
|
|
60
|
+
button({ label: 'Submit', type: 'submit', variant: 'primary' }),
|
|
61
|
+
`button({ label: 'Disabled', disabled: true })
|
|
62
|
+
button({ label: 'Link', href: '/docs' })
|
|
63
|
+
button({ label: 'Submit', type: 'submit', variant: 'primary' })`
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
${table(
|
|
67
|
+
['Prop', 'Type', 'Default', ''],
|
|
68
|
+
[
|
|
69
|
+
['<code>label</code>', 'string', '—', 'Visible text'],
|
|
70
|
+
['<code>variant</code>', 'string', 'primary', 'primary · secondary · ghost · danger'],
|
|
71
|
+
['<code>size</code>', 'string', 'md', 'sm · md · lg'],
|
|
72
|
+
['<code>href</code>', 'string', '—', 'Renders <code><a></code> instead of <code><button></code>'],
|
|
73
|
+
['<code>disabled</code>', 'boolean', 'false', ''],
|
|
74
|
+
['<code>type</code>', 'string', 'button', 'button · submit · reset'],
|
|
75
|
+
['<code>icon</code>', 'string', '—', 'SVG HTML prepended inside'],
|
|
76
|
+
['<code>iconAfter</code>','string', '—', 'SVG HTML appended inside'],
|
|
77
|
+
['<code>fullWidth</code>','boolean', 'false', ''],
|
|
78
|
+
['<code>class</code>', 'string', '—', 'Extra classes appended to the element'],
|
|
79
|
+
['<code>attrs</code>', 'object', '{}', 'Extra HTML attributes (button only)'],
|
|
80
|
+
]
|
|
81
|
+
)}
|
|
82
|
+
`,
|
|
83
|
+
}),
|
|
84
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { renderComponentPage, demo } from '../../lib/component-page.js'
|
|
2
|
+
import { prevNext } from '../../lib/nav.js'
|
|
3
|
+
import { table } from '../../lib/layout.js'
|
|
4
|
+
import { button, card, badge, grid } from '../../../../src/ui/index.js'
|
|
5
|
+
|
|
6
|
+
const { prev, next } = prevNext('/components/card')
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
route: '/components/card',
|
|
10
|
+
meta: {
|
|
11
|
+
title: 'Card — Pulse Docs',
|
|
12
|
+
description: 'Card component for Pulse UI.',
|
|
13
|
+
styles: ['/pulse-ui.css', '/docs.css'],
|
|
14
|
+
},
|
|
15
|
+
state: {},
|
|
16
|
+
view: () => renderComponentPage({
|
|
17
|
+
currentHref: '/components/card',
|
|
18
|
+
prev,
|
|
19
|
+
next,
|
|
20
|
+
name: 'card',
|
|
21
|
+
description: '<code>content</code> and <code>footer</code> are raw HTML slots — they pass through as-is. Wrap user-supplied values in <code>escHtml()</code> before composing them into the string.',
|
|
22
|
+
content: `
|
|
23
|
+
${demo(
|
|
24
|
+
card({
|
|
25
|
+
title: 'Recent activity',
|
|
26
|
+
content: '<p class="u-text-muted u-text-sm" style="margin:0">No activity in the last 7 days.</p>',
|
|
27
|
+
footer: button({ label: 'View all', href: '#', variant: 'ghost', size: 'sm' }),
|
|
28
|
+
}),
|
|
29
|
+
`card({
|
|
30
|
+
title: 'Recent activity',
|
|
31
|
+
content: \`<p class="u-text-muted u-text-sm">No activity in the last 7 days.</p>\`,
|
|
32
|
+
footer: button({ label: 'View all', href: '/activity', variant: 'ghost', size: 'sm' }),
|
|
33
|
+
})`
|
|
34
|
+
)}
|
|
35
|
+
|
|
36
|
+
<h2 class="doc-h2" id="image">With image</h2>
|
|
37
|
+
<p>Use <code>flush</code> to remove body padding, then add an image at the top and restore padding on the text content below it.</p>
|
|
38
|
+
|
|
39
|
+
${demo(
|
|
40
|
+
card({
|
|
41
|
+
flush: true,
|
|
42
|
+
content: `
|
|
43
|
+
<div class="u-rounded-md u-overflow-hidden" style="height:180px;background:linear-gradient(135deg,#667eea,#764ba2);display:flex;align-items:center;justify-content:center;">
|
|
44
|
+
<span style="font-size:3rem">🌄</span>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="u-p-5">
|
|
47
|
+
<p class="u-font-semibold u-mb-2" style="margin-top:0">Mountain retreat</p>
|
|
48
|
+
<p class="u-text-muted u-text-sm" style="margin:0">A peaceful escape in the highlands. Three nights, all-inclusive.</p>
|
|
49
|
+
</div>
|
|
50
|
+
`,
|
|
51
|
+
footer: button({ label: 'Book now', href: '#', variant: 'primary', size: 'sm' }),
|
|
52
|
+
}),
|
|
53
|
+
`card({
|
|
54
|
+
flush: true,
|
|
55
|
+
content: \`
|
|
56
|
+
<div class="u-rounded-md u-overflow-hidden" style="height:180px;background:...">
|
|
57
|
+
<img src="/img/retreat.jpg" alt="Mountain retreat" style="width:100%;height:100%;object-fit:cover">
|
|
58
|
+
</div>
|
|
59
|
+
<div class="u-p-5">
|
|
60
|
+
<p class="u-font-semibold u-mb-2">Mountain retreat</p>
|
|
61
|
+
<p class="u-text-muted u-text-sm">A peaceful escape in the highlands.</p>
|
|
62
|
+
</div>
|
|
63
|
+
\`,
|
|
64
|
+
footer: button({ label: 'Book now', href: '/book', variant: 'primary', size: 'sm' }),
|
|
65
|
+
})`
|
|
66
|
+
)}
|
|
67
|
+
|
|
68
|
+
<h2 class="doc-h2" id="badges">With badges</h2>
|
|
69
|
+
<p>Compose <code>badge()</code> inside <code>content</code> to show status labels or category tags.</p>
|
|
70
|
+
|
|
71
|
+
${demo(
|
|
72
|
+
card({
|
|
73
|
+
title: 'API rate limits',
|
|
74
|
+
content: `
|
|
75
|
+
<div class="u-flex u-gap-2 u-mb-4">
|
|
76
|
+
${badge({ label: 'Production', variant: 'success' })}
|
|
77
|
+
${badge({ label: 'v2.1', variant: 'info' })}
|
|
78
|
+
</div>
|
|
79
|
+
<p class="u-text-muted u-text-sm" style="margin:0">Requests are capped at 1,000/min per API key. Contact support to increase your limit.</p>
|
|
80
|
+
`,
|
|
81
|
+
footer: `
|
|
82
|
+
<div class="u-flex u-gap-2">
|
|
83
|
+
${button({ label: 'View docs', href: '#', variant: 'ghost', size: 'sm' })}
|
|
84
|
+
${button({ label: 'Request increase', href: '#', variant: 'secondary', size: 'sm' })}
|
|
85
|
+
</div>
|
|
86
|
+
`,
|
|
87
|
+
}),
|
|
88
|
+
`card({
|
|
89
|
+
title: 'API rate limits',
|
|
90
|
+
content: \`
|
|
91
|
+
<div class="u-flex u-gap-2 u-mb-4">
|
|
92
|
+
\${badge({ label: 'Production', variant: 'success' })}
|
|
93
|
+
\${badge({ label: 'v2.1', variant: 'info' })}
|
|
94
|
+
</div>
|
|
95
|
+
<p class="u-text-muted u-text-sm">Requests are capped at 1,000/min per API key.</p>
|
|
96
|
+
\`,
|
|
97
|
+
footer: \`
|
|
98
|
+
\${button({ label: 'View docs', href: '/docs', variant: 'ghost', size: 'sm' })}
|
|
99
|
+
\${button({ label: 'Request increase', href: '/contact', variant: 'secondary', size: 'sm' })}
|
|
100
|
+
\`,
|
|
101
|
+
})`
|
|
102
|
+
)}
|
|
103
|
+
|
|
104
|
+
<h2 class="doc-h2" id="metadata">With metadata row</h2>
|
|
105
|
+
<p>A flex row inside <code>content</code> works well for author, date, read-time and similar metadata.</p>
|
|
106
|
+
|
|
107
|
+
${demo(
|
|
108
|
+
card({
|
|
109
|
+
title: 'Building with Pulse',
|
|
110
|
+
content: `
|
|
111
|
+
<p class="u-text-muted u-text-sm u-mb-4">Learn how to scaffold a new project and ship your first spec-driven page in under ten minutes.</p>
|
|
112
|
+
<div class="u-flex u-gap-3" style="align-items:center;flex-wrap:wrap">
|
|
113
|
+
<div style="width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#667eea,#764ba2);flex-shrink:0"></div>
|
|
114
|
+
<span class="u-text-sm u-text-muted">Jane Smith</span>
|
|
115
|
+
<span class="u-text-sm u-text-muted">·</span>
|
|
116
|
+
<span class="u-text-sm u-text-muted">Mar 15, 2024</span>
|
|
117
|
+
<span class="u-text-sm u-text-muted u-ml-auto">5 min read</span>
|
|
118
|
+
</div>
|
|
119
|
+
`,
|
|
120
|
+
footer: button({ label: 'Read article →', href: '#', variant: 'ghost', size: 'sm' }),
|
|
121
|
+
}),
|
|
122
|
+
`card({
|
|
123
|
+
title: 'Building with Pulse',
|
|
124
|
+
content: \`
|
|
125
|
+
<p class="u-text-muted u-text-sm u-mb-4">Learn how to scaffold a new project...</p>
|
|
126
|
+
<div class="u-flex u-gap-3" style="align-items:center;flex-wrap:wrap">
|
|
127
|
+
<img src="/img/avatar.jpg" class="u-rounded-full" style="width:28px;height:28px" alt="">
|
|
128
|
+
<span class="u-text-sm u-text-muted">Jane Smith</span>
|
|
129
|
+
<span class="u-text-sm u-text-muted">·</span>
|
|
130
|
+
<span class="u-text-sm u-text-muted">Mar 15, 2024</span>
|
|
131
|
+
<span class="u-text-sm u-text-muted u-ml-auto">5 min read</span>
|
|
132
|
+
</div>
|
|
133
|
+
\`,
|
|
134
|
+
footer: button({ label: 'Read article →', href: '/blog/pulse', variant: 'ghost', size: 'sm' }),
|
|
135
|
+
})`
|
|
136
|
+
)}
|
|
137
|
+
|
|
138
|
+
<h2 class="doc-h2" id="stat">Stat / metric card</h2>
|
|
139
|
+
<p>Cards without a title work well as simple metric tiles. Use <code>grid()</code> to lay multiple cards out side by side.</p>
|
|
140
|
+
|
|
141
|
+
${demo(
|
|
142
|
+
grid({
|
|
143
|
+
cols: 3,
|
|
144
|
+
gap: 'sm',
|
|
145
|
+
content: [
|
|
146
|
+
card({ content: `<p class="u-text-muted u-text-sm u-mb-1" style="margin-top:0">Total revenue</p><p class="u-text-3xl u-font-bold" style="margin:0">£48,295</p><p class="u-text-sm u-text-green u-mb-0" style="margin-top:.25rem">↑ 12% this month</p>` }),
|
|
147
|
+
card({ content: `<p class="u-text-muted u-text-sm u-mb-1" style="margin-top:0">Active users</p><p class="u-text-3xl u-font-bold" style="margin:0">3,842</p><p class="u-text-sm u-text-muted u-mb-0" style="margin-top:.25rem">Across all plans</p>` }),
|
|
148
|
+
card({ content: `<p class="u-text-muted u-text-sm u-mb-1" style="margin-top:0">Churn rate</p><p class="u-text-3xl u-font-bold" style="margin:0">1.4%</p><p class="u-text-sm u-text-red u-mb-0" style="margin-top:.25rem">↑ 0.2% vs last month</p>` }),
|
|
149
|
+
].join(''),
|
|
150
|
+
}),
|
|
151
|
+
`grid({
|
|
152
|
+
cols: 3,
|
|
153
|
+
gap: 'sm',
|
|
154
|
+
content: [
|
|
155
|
+
card({
|
|
156
|
+
content: \`
|
|
157
|
+
<p class="u-text-muted u-text-sm u-mb-1">Total revenue</p>
|
|
158
|
+
<p class="u-text-3xl u-font-bold">£48,295</p>
|
|
159
|
+
<p class="u-text-sm u-text-green">↑ 12% this month</p>
|
|
160
|
+
\`,
|
|
161
|
+
}),
|
|
162
|
+
card({ ... }),
|
|
163
|
+
card({ ... }),
|
|
164
|
+
].join(''),
|
|
165
|
+
})`
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
<h2 class="doc-h2" id="grid-inside">Grid inside a card</h2>
|
|
169
|
+
<p>Components compose freely — pass a <code>grid()</code> into a card's <code>content</code> slot for structured layouts inside a surface.</p>
|
|
170
|
+
|
|
171
|
+
${demo(
|
|
172
|
+
card({
|
|
173
|
+
title: 'Plan comparison',
|
|
174
|
+
content: grid({
|
|
175
|
+
cols: 3,
|
|
176
|
+
gap: 'sm',
|
|
177
|
+
content: [
|
|
178
|
+
`<div class="u-text-center u-p-2">
|
|
179
|
+
<p class="u-font-semibold u-mb-1" style="margin-top:0">Starter</p>
|
|
180
|
+
<p class="u-text-2xl u-font-bold u-mb-1" style="margin:0">£0</p>
|
|
181
|
+
<p class="u-text-muted u-text-sm" style="margin:0">Up to 3 projects</p>
|
|
182
|
+
</div>`,
|
|
183
|
+
`<div class="u-text-center u-p-2" style="border-left:1px solid var(--ui-border);border-right:1px solid var(--ui-border)">
|
|
184
|
+
<p class="u-font-semibold u-mb-1" style="margin-top:0;color:var(--ui-accent)">Pro</p>
|
|
185
|
+
<p class="u-text-2xl u-font-bold u-mb-1" style="margin:0">£12</p>
|
|
186
|
+
<p class="u-text-muted u-text-sm" style="margin:0">Unlimited projects</p>
|
|
187
|
+
</div>`,
|
|
188
|
+
`<div class="u-text-center u-p-2">
|
|
189
|
+
<p class="u-font-semibold u-mb-1" style="margin-top:0">Enterprise</p>
|
|
190
|
+
<p class="u-text-2xl u-font-bold u-mb-1" style="margin:0">Custom</p>
|
|
191
|
+
<p class="u-text-muted u-text-sm" style="margin:0">SLA + support</p>
|
|
192
|
+
</div>`,
|
|
193
|
+
].join(''),
|
|
194
|
+
}),
|
|
195
|
+
footer: button({ label: 'View full pricing', href: '#', variant: 'ghost', size: 'sm' }),
|
|
196
|
+
}),
|
|
197
|
+
`card({
|
|
198
|
+
title: 'Plan comparison',
|
|
199
|
+
content: grid({
|
|
200
|
+
cols: 3,
|
|
201
|
+
gap: 'sm',
|
|
202
|
+
content: [
|
|
203
|
+
\`<div class="u-text-center u-p-2">...</div>\`,
|
|
204
|
+
\`<div class="u-text-center u-p-2">...</div>\`,
|
|
205
|
+
\`<div class="u-text-center u-p-2">...</div>\`,
|
|
206
|
+
].join(''),
|
|
207
|
+
}),
|
|
208
|
+
footer: button({ label: 'View full pricing', href: '/pricing', variant: 'ghost', size: 'sm' }),
|
|
209
|
+
})`
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
${table(
|
|
213
|
+
['Prop', 'Type', 'Default', ''],
|
|
214
|
+
[
|
|
215
|
+
['<code>title</code>', 'string', '—', 'Escaped automatically'],
|
|
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.'],
|
|
217
|
+
['<code>content</code>', 'string', '—', 'HTML string — not escaped'],
|
|
218
|
+
['<code>footer</code>', 'string', '—', 'HTML string — not escaped'],
|
|
219
|
+
['<code>flush</code>', 'boolean', 'false', 'Removes body padding — useful for full-bleed images or tables'],
|
|
220
|
+
['<code>class</code>', 'string', '—', ''],
|
|
221
|
+
]
|
|
222
|
+
)}
|
|
223
|
+
`,
|
|
224
|
+
}),
|
|
225
|
+
}
|