@winkintel/bootstrap-svelte 1.0.8 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Bootstrap Svelte
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@winkintel/bootstrap-svelte.svg)](https://www.npmjs.com/package/@winkintel/bootstrap-svelte)
4
+ [![weekly downloads](https://img.shields.io/npm/dw/@winkintel/bootstrap-svelte.svg)](https://www.npmjs.com/package/@winkintel/bootstrap-svelte)
5
+ [![CI](https://github.com/WinkIntel/bootstrap-svelte/actions/workflows/ci.yml/badge.svg)](https://github.com/WinkIntel/bootstrap-svelte/actions/workflows/ci.yml)
6
+ [![license](https://img.shields.io/npm/l/@winkintel/bootstrap-svelte.svg)](https://github.com/WinkIntel/bootstrap-svelte/blob/main/LICENSE.md)
7
+ [![documentation](https://img.shields.io/badge/docs-bootstrap--svelte.vercel.app-7952b3.svg)](https://bootstrap-svelte.vercel.app/)
8
+
3
9
  > Bootstrap 5 components for Svelte 5 with TypeScript support.
4
10
 
5
11
  Bootstrap Svelte provides ready-to-use, type-safe Bootstrap components for Svelte 5 applications. It follows Bootstrap's design language and class conventions while exposing Svelte-native component APIs.
@@ -8,11 +14,21 @@ Use it when you want Bootstrap's familiar grid, utilities, and UI patterns in a
8
14
 
9
15
  ## Links
10
16
 
17
+ - Documentation: <https://bootstrap-svelte.vercel.app/>
11
18
  - npm: [`@winkintel/bootstrap-svelte`](https://www.npmjs.com/package/@winkintel/bootstrap-svelte)
12
19
  - Repository: [`WinkIntel/bootstrap-svelte`](https://github.com/WinkIntel/bootstrap-svelte)
13
20
  - Bootstrap: <https://getbootstrap.com/>
14
21
  - Svelte: <https://svelte.dev/>
15
22
 
23
+ ## Machine-readable documentation
24
+
25
+ The showcase at <https://bootstrap-svelte.vercel.app> is published for AI agents and tools as well as people:
26
+
27
+ - `/llms.txt` indexes every page with one-line summaries ([llms.txt format](https://llmstxt.org)); `/llms-full.txt` bundles every page into one Markdown file.
28
+ - Every page is available as Markdown from its own URL with `Accept: text/markdown` (quality values are honored; the response carries `Vary: Accept` and a `Link` alternate header), or by appending `.md` to the path, for example `/components/button.md`.
29
+ - `/agents.md` explains when to use the library and how to integrate it; `/sitemap.xml` and `/robots.txt` serve crawlers, and `pnpm indexnow` pushes changed URLs to Bing and the other [IndexNow](https://www.indexnow.org/) engines after a deploy.
30
+ - Paths that do not exist return a real HTTP 404 with a Markdown or HTML body that links back to the documentation index.
31
+
16
32
  ## Why this exists
17
33
 
18
34
  Bootstrap is still a practical choice for many product, enterprise, and internal applications: it is familiar, well documented, stable, and easy for mixed frontend/backend teams to work with.
@@ -51,11 +67,7 @@ yarn add @winkintel/bootstrap-svelte bootstrap
51
67
 
52
68
  ### Peer dependency
53
69
 
54
- This package requires Svelte 5:
55
-
56
- ```bash
57
- pnpm add svelte@^5.29.0
58
- ```
70
+ This package requires Svelte `^5.29.0` as a peer dependency. Keep Svelte installed in your application; do not replace the package/bootstrap install command with a Svelte install command.
59
71
 
60
72
  ## Bootstrap CSS
61
73
 
@@ -82,23 +94,19 @@ Or use Bootstrap SCSS from your app stylesheet/build pipeline:
82
94
  let showModal = $state(false);
83
95
  </script>
84
96
 
85
- <Alert colorVariant="primary" isDismissible>
86
- Welcome to Bootstrap Svelte!
87
- </Alert>
97
+ <Alert colorVariant="primary" isDismissible>Welcome to Bootstrap Svelte!</Alert>
88
98
 
89
- <Card>
99
+ <Card.Root>
90
100
  <Card.Header>
91
101
  <Card.Title>Getting Started</Card.Title>
92
102
  </Card.Header>
93
103
  <Card.Body>
94
104
  <Card.Text>This is a card built with Bootstrap Svelte components.</Card.Text>
95
- <Button colorVariant="primary" onclick={() => (showModal = true)}>
96
- Open Modal
97
- </Button>
105
+ <Button colorVariant="primary" onclick={() => (showModal = true)}>Open Modal</Button>
98
106
  </Card.Body>
99
- </Card>
107
+ </Card.Root>
100
108
 
101
- <Modal isShown={showModal}>
109
+ <Modal.Root isShown={showModal}>
102
110
  <Modal.Dialog>
103
111
  <Modal.Content>
104
112
  <Modal.Header>
@@ -106,13 +114,11 @@ Or use Bootstrap SCSS from your app stylesheet/build pipeline:
106
114
  </Modal.Header>
107
115
  <Modal.Body>Modal content goes here.</Modal.Body>
108
116
  <Modal.Footer>
109
- <Button colorVariant="secondary" onclick={() => (showModal = false)}>
110
- Close
111
- </Button>
117
+ <Button colorVariant="secondary" onclick={() => (showModal = false)}>Close</Button>
112
118
  </Modal.Footer>
113
119
  </Modal.Content>
114
120
  </Modal.Dialog>
115
- </Modal>
121
+ </Modal.Root>
116
122
  ```
117
123
 
118
124
  ## Available components
@@ -174,7 +180,7 @@ Interactive components are implemented as Svelte components rather than requirin
174
180
 
175
181
  ## Status and feedback
176
182
 
177
- This package is published as `1.0.8`, but feedback from Svelte developers is still very welcome. Useful feedback includes:
183
+ This package is published as `1.0.11`, but feedback from Svelte developers is still very welcome. Useful feedback includes:
178
184
 
179
185
  - Component API ergonomics
180
186
  - Svelte 5 idioms and runes compatibility
@@ -13,12 +13,12 @@ Bootstrap-styled helper text component for providing additional information abou
13
13
  <Form.TextInput id="emailInput" placeholder="Enter your email" aria-describedby="email-help" />
14
14
  <Form.HelperText id="email-help">We'll never share your email with anyone else.</Form.HelperText>
15
15
 
16
- // Inside a form group
17
- <Form.Group>
16
+ // Inside an input group
17
+ <Form.InputGroup>
18
18
  <Form.InputLabel for="passwordInput">Password</Form.InputLabel>
19
19
  <Form.PasswordInput id="passwordInput" />
20
20
  <Form.HelperText>Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces or emoji.</Form.HelperText>
21
- </Form.Group>
21
+ </Form.InputGroup>
22
22
  ```
23
23
  ### Props
24
24
  - `class` (string): Optional. Additional CSS classes to apply to the helper text.
@@ -12,12 +12,12 @@
12
12
  * <Form.TextInput id="emailInput" placeholder="Enter your email" aria-describedby="email-help" />
13
13
  * <Form.HelperText id="email-help">We'll never share your email with anyone else.</Form.HelperText>
14
14
  *
15
- * // Inside a form group
16
- * <Form.Group>
15
+ * // Inside an input group
16
+ * <Form.InputGroup>
17
17
  * <Form.InputLabel for="passwordInput">Password</Form.InputLabel>
18
18
  * <Form.PasswordInput id="passwordInput" />
19
19
  * <Form.HelperText>Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces or emoji.</Form.HelperText>
20
- * </Form.Group>
20
+ * </Form.InputGroup>
21
21
  * ```
22
22
  * ### Props
23
23
  * - `class` (string): Optional. Additional CSS classes to apply to the helper text.
@@ -6,11 +6,11 @@ Bootstrap-styled label component for form input elements.
6
6
  @example
7
7
  ```svelte
8
8
  // Standard label for input
9
- <Form.Group>
9
+ <Form.InputGroup>
10
10
  <Form.InputLabel for="exampleInput">Email address</Form.InputLabel>
11
11
  <Form.EmailInput id="exampleInput" placeholder="name@example.com" />
12
12
  <Form.HelperText>We'll never share your email with anyone else.</Form.HelperText>
13
- </Form.Group>
13
+ </Form.InputGroup>
14
14
 
15
15
  // Floating label (used with Form.Floating)
16
16
  <Form.Floating>
@@ -6,11 +6,11 @@ import type { Form } from './index.js';
6
6
  * @example
7
7
  * ```svelte
8
8
  * // Standard label for input
9
- * <Form.Group>
9
+ * <Form.InputGroup>
10
10
  * <Form.InputLabel for="exampleInput">Email address</Form.InputLabel>
11
11
  * <Form.EmailInput id="exampleInput" placeholder="name@example.com" />
12
12
  * <Form.HelperText>We'll never share your email with anyone else.</Form.HelperText>
13
- * </Form.Group>
13
+ * </Form.InputGroup>
14
14
  *
15
15
  * // Floating label (used with Form.Floating)
16
16
  * <Form.Floating>
@@ -16,19 +16,19 @@ Bootstrap-styled range slider input component for selecting a value within a ran
16
16
  <Form.RangeInput disabled max="5" min="0" step="0.5" title="Set a number" value="1" />
17
17
 
18
18
  // With label and current value display
19
- <Form.Group>
19
+ <Form.InputGroup>
20
20
  <Form.InputLabel for="customRange">Example range</Form.InputLabel>
21
21
  <Form.RangeInput id="customRange" max="10" min="0" />
22
- </Form.Group>
22
+ </Form.InputGroup>
23
23
 
24
24
  // With steps and ticks
25
- <Form.Group>
25
+ <Form.InputGroup>
26
26
  <Form.InputLabel for="stepRange">Range with steps</Form.InputLabel>
27
27
  <Form.RangeInput id="stepRange" max="5" min="0" step="1" />
28
28
  <div class="d-flex justify-content-between">
29
29
  <span>0</span><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
30
30
  </div>
31
- </Form.Group>
31
+ </Form.InputGroup>
32
32
  ```
33
33
  ### Props
34
34
  - `class` (string): Optional. Additional CSS classes to apply to the range input.
@@ -16,19 +16,19 @@ import type { Form } from './index.js';
16
16
  * <Form.RangeInput disabled max="5" min="0" step="0.5" title="Set a number" value="1" />
17
17
  *
18
18
  * // With label and current value display
19
- * <Form.Group>
19
+ * <Form.InputGroup>
20
20
  * <Form.InputLabel for="customRange">Example range</Form.InputLabel>
21
21
  * <Form.RangeInput id="customRange" max="10" min="0" />
22
- * </Form.Group>
22
+ * </Form.InputGroup>
23
23
  *
24
24
  * // With steps and ticks
25
- * <Form.Group>
25
+ * <Form.InputGroup>
26
26
  * <Form.InputLabel for="stepRange">Range with steps</Form.InputLabel>
27
27
  * <Form.RangeInput id="stepRange" max="5" min="0" step="1" />
28
28
  * <div class="d-flex justify-content-between">
29
29
  * <span>0</span><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span>
30
30
  * </div>
31
- * </Form.Group>
31
+ * </Form.InputGroup>
32
32
  * ```
33
33
  * ### Props
34
34
  * - `class` (string): Optional. Additional CSS classes to apply to the range input.
@@ -22,6 +22,7 @@ Title component for the Modal.Header.
22
22
  - `level` (number): Optional. Heading level (1-6) for the title element
23
23
  -->
24
24
  <script lang="ts">import { uniqueClsx } from '../common/css.js';
25
+ import { onDestroy } from 'svelte';
25
26
  import { getOptionalModalRootState } from './modal.svelte.js';
26
27
  // Generate a unique ID for the modal title element, in case one is not provided...
27
28
  const uid = $props.id();
@@ -29,14 +30,19 @@ let { children, class: classValues, elementRef = $bindable(null), id = `modal-ti
29
30
  let elementType = $derived(`h${level}`);
30
31
  let classes = $derived(uniqueClsx('modal-title', classValues));
31
32
  const rootState = getOptionalModalRootState();
33
+ // Stable identity for this title's registry entry, so an id change updates
34
+ // the entry in place and keeps its position among sibling titles...
35
+ const titleKey = Symbol('modal-title');
32
36
  $effect(() => {
33
- const registeredId = id;
34
- if (!registeredId)
35
- return;
36
- rootState?.registerTitleId(registeredId);
37
- return () => {
38
- rootState?.unregisterTitleId(registeredId);
39
- };
37
+ if (id) {
38
+ rootState?.registerTitleId(titleKey, id);
39
+ }
40
+ else {
41
+ rootState?.unregisterTitleId(titleKey);
42
+ }
43
+ });
44
+ onDestroy(() => {
45
+ rootState?.unregisterTitleId(titleKey);
40
46
  });
41
47
  </script>
42
48
 
@@ -5,7 +5,7 @@ Add dialogs to your site for lightboxes, user notifications, or completely custo
5
5
 
6
6
  @example
7
7
  ```svelte
8
- <Modal isShown={true}>
8
+ <Modal.Root isShown={true}>
9
9
  <Modal.Dialog>
10
10
  <Modal.Content>
11
11
  <Modal.Header>
@@ -19,17 +19,17 @@ Add dialogs to your site for lightboxes, user notifications, or completely custo
19
19
  </Modal.Footer>
20
20
  </Modal.Content>
21
21
  </Modal.Dialog>
22
- </Modal>
22
+ </Modal.Root>
23
23
  ```
24
24
 
25
25
  #### Static backdrop
26
26
  ```svelte
27
- <Modal useBackdrop="static">...</Modal>
27
+ <Modal.Root useBackdrop="static">...</Modal.Root>
28
28
  ```
29
29
 
30
30
  #### Keyboard dismissible
31
31
  ```svelte
32
- <Modal isKeyboardDismissible={true}>...</Modal>
32
+ <Modal.Root isKeyboardDismissible={true}>...</Modal.Root>
33
33
  ```
34
34
 
35
35
  ### Props
@@ -5,7 +5,7 @@ import type { Modal } from './index.js';
5
5
  *
6
6
  * @example
7
7
  * ```svelte
8
- * <Modal isShown={true}>
8
+ * <Modal.Root isShown={true}>
9
9
  * <Modal.Dialog>
10
10
  * <Modal.Content>
11
11
  * <Modal.Header>
@@ -19,17 +19,17 @@ import type { Modal } from './index.js';
19
19
  * </Modal.Footer>
20
20
  * </Modal.Content>
21
21
  * </Modal.Dialog>
22
- * </Modal>
22
+ * </Modal.Root>
23
23
  * ```
24
24
  *
25
25
  * #### Static backdrop
26
26
  * ```svelte
27
- * <Modal useBackdrop="static">...</Modal>
27
+ * <Modal.Root useBackdrop="static">...</Modal.Root>
28
28
  * ```
29
29
  *
30
30
  * #### Keyboard dismissible
31
31
  * ```svelte
32
- * <Modal isKeyboardDismissible={true}>...</Modal>
32
+ * <Modal.Root isKeyboardDismissible={true}>...</Modal.Root>
33
33
  * ```
34
34
  *
35
35
  * ### Props
@@ -8,6 +8,10 @@ export class ModalRootState {
8
8
  // Private
9
9
  #isShown = $state(false);
10
10
  #useBackdrop = $state(true);
11
+ // Plain (non-reactive) ordered registry of mounted Modal.Title components,
12
+ // keyed by a stable per-instance token so an id change updates the entry in
13
+ // place instead of moving it. The first registered title labels the dialog...
14
+ #titles = [];
11
15
  titleId = $state(undefined);
12
16
  constructor(props) {
13
17
  this.props = props;
@@ -30,13 +34,23 @@ export class ModalRootState {
30
34
  toggleIsShown() {
31
35
  this.#isShown = !this.#isShown;
32
36
  }
33
- registerTitleId(id) {
34
- this.titleId ??= id;
35
- }
36
- unregisterTitleId(id) {
37
- if (this.titleId === id) {
38
- this.titleId = undefined;
37
+ // Both methods are called from Modal.Title. They must never read `titleId`
38
+ // (reactive state) there, or the title's `$effect` would depend on the state
39
+ // it writes and Svelte >= 5.56.5 loops with "Maximum update depth exceeded".
40
+ // The registry is a plain array, and `titleId` is only written...
41
+ registerTitleId(key, id) {
42
+ const entry = this.#titles.find((title) => title.key === key);
43
+ if (entry) {
44
+ entry.id = id;
45
+ }
46
+ else {
47
+ this.#titles.push({ key, id });
39
48
  }
49
+ this.titleId = this.#titles[0]?.id;
50
+ }
51
+ unregisterTitleId(key) {
52
+ this.#titles = this.#titles.filter((title) => title.key !== key);
53
+ this.titleId = this.#titles[0]?.id;
40
54
  }
41
55
  }
42
56
  /**
@@ -5,37 +5,37 @@ Build hidden sidebars into your project for navigation, shopping carts, and more
5
5
 
6
6
  @example
7
7
  ```svelte
8
- <Offcanvas placement="start" isVisible={true}>
8
+ <Offcanvas.Root placement="start" isShown={true}>
9
9
  <Offcanvas.Header>
10
10
  <Offcanvas.Title>Offcanvas Title</Offcanvas.Title>
11
11
  </Offcanvas.Header>
12
12
  <Offcanvas.Body>
13
13
  Content for the offcanvas goes here. You can place just about any Bootstrap component or custom elements here.
14
14
  </Offcanvas.Body>
15
- </Offcanvas>
15
+ </Offcanvas.Root>
16
16
  ```
17
17
 
18
18
  #### Placement options
19
19
  ```svelte
20
- <Offcanvas placement="start">...</Offcanvas>
21
- <Offcanvas placement="end">...</Offcanvas>
22
- <Offcanvas placement="top">...</Offcanvas>
23
- <Offcanvas placement="bottom">...</Offcanvas>
20
+ <Offcanvas.Root placement="start">...</Offcanvas.Root>
21
+ <Offcanvas.Root placement="end">...</Offcanvas.Root>
22
+ <Offcanvas.Root placement="top">...</Offcanvas.Root>
23
+ <Offcanvas.Root placement="bottom">...</Offcanvas.Root>
24
24
  ```
25
25
 
26
26
  #### Body scrolling
27
27
  ```svelte
28
- <Offcanvas bodyScrolling={true}>...</Offcanvas>
28
+ <Offcanvas.Root isBodyScrollable={true}>...</Offcanvas.Root>
29
29
  ```
30
30
 
31
31
  #### Static backdrop
32
32
  ```svelte
33
- <Offcanvas isStatic={true}>...</Offcanvas>
33
+ <Offcanvas.Root useBackdrop="static">...</Offcanvas.Root>
34
34
  ```
35
35
 
36
36
  #### Responsive offcanvas
37
37
  ```svelte
38
- <Offcanvas breakpoint="lg">...</Offcanvas>
38
+ <Offcanvas.Root showOnBreakpoint="lg">...</Offcanvas.Root>
39
39
  ```
40
40
 
41
41
  ### Props
@@ -5,37 +5,37 @@ import type { Offcanvas } from './index.js';
5
5
  *
6
6
  * @example
7
7
  * ```svelte
8
- * <Offcanvas placement="start" isVisible={true}>
8
+ * <Offcanvas.Root placement="start" isShown={true}>
9
9
  * <Offcanvas.Header>
10
10
  * <Offcanvas.Title>Offcanvas Title</Offcanvas.Title>
11
11
  * </Offcanvas.Header>
12
12
  * <Offcanvas.Body>
13
13
  * Content for the offcanvas goes here. You can place just about any Bootstrap component or custom elements here.
14
14
  * </Offcanvas.Body>
15
- * </Offcanvas>
15
+ * </Offcanvas.Root>
16
16
  * ```
17
17
  *
18
18
  * #### Placement options
19
19
  * ```svelte
20
- * <Offcanvas placement="start">...</Offcanvas>
21
- * <Offcanvas placement="end">...</Offcanvas>
22
- * <Offcanvas placement="top">...</Offcanvas>
23
- * <Offcanvas placement="bottom">...</Offcanvas>
20
+ * <Offcanvas.Root placement="start">...</Offcanvas.Root>
21
+ * <Offcanvas.Root placement="end">...</Offcanvas.Root>
22
+ * <Offcanvas.Root placement="top">...</Offcanvas.Root>
23
+ * <Offcanvas.Root placement="bottom">...</Offcanvas.Root>
24
24
  * ```
25
25
  *
26
26
  * #### Body scrolling
27
27
  * ```svelte
28
- * <Offcanvas bodyScrolling={true}>...</Offcanvas>
28
+ * <Offcanvas.Root isBodyScrollable={true}>...</Offcanvas.Root>
29
29
  * ```
30
30
  *
31
31
  * #### Static backdrop
32
32
  * ```svelte
33
- * <Offcanvas isStatic={true}>...</Offcanvas>
33
+ * <Offcanvas.Root useBackdrop="static">...</Offcanvas.Root>
34
34
  * ```
35
35
  *
36
36
  * #### Responsive offcanvas
37
37
  * ```svelte
38
- * <Offcanvas breakpoint="lg">...</Offcanvas>
38
+ * <Offcanvas.Root showOnBreakpoint="lg">...</Offcanvas.Root>
39
39
  * ```
40
40
  *
41
41
  * ### Props
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "author": "Wink, Inc.",
3
3
  "name": "@winkintel/bootstrap-svelte",
4
- "version": "1.0.8",
4
+ "version": "1.0.11",
5
5
  "description": "Bootstrap components for Svelte 5 with TypeScript support.",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/WinkIntel/bootstrap-svelte.git"
10
10
  },
11
- "homepage": "https://github.com/WinkIntel/bootstrap-svelte#readme",
11
+ "homepage": "https://bootstrap-svelte.vercel.app/",
12
12
  "bugs": {
13
13
  "url": "https://github.com/WinkIntel/bootstrap-svelte/issues"
14
14
  },
@@ -26,6 +26,7 @@
26
26
  "doctor": "svelte-doctor check",
27
27
  "format": "prettier --write --ignore-path=.prettierignore $(git diff --name-only --diff-filter d --relative . && git ls-files --others --exclude-standard | sed \"s|^$(git rev-parse --show-prefix)||\")",
28
28
  "format-all": "prettier --write --ignore-path=.prettierignore ./src",
29
+ "indexnow": "node scripts/indexnow.mjs",
29
30
  "lint": "prettier --check ./src && eslint ./src",
30
31
  "ncu:check": "ncu --target minor && ncu --target minor --cooldown 0d --filter \"@sveltejs/adapter-static,@sveltejs/kit,@sveltejs/vite-plugin-svelte,svelte,svelte-check,svelte-preprocess\"",
31
32
  "ncu:upgrade": "ncu --target minor -u && ncu --target minor --cooldown 0d -u --filter \"@sveltejs/adapter-static,@sveltejs/kit,@sveltejs/vite-plugin-svelte,svelte,svelte-check,svelte-preprocess\" && pnpm install",
@@ -87,7 +88,7 @@
87
88
  "publint": "~0.3.21",
88
89
  "rimraf": "~6.1.3",
89
90
  "sass": "1.77.6",
90
- "svelte": "~5.56.4",
91
+ "svelte": "~5.57.0",
91
92
  "svelte-check": "~4.7.2",
92
93
  "svelte-doctor": "~0.3.1",
93
94
  "svelte-highlight": "~7.14.1",
@@ -99,10 +100,15 @@
99
100
  },
100
101
  "keywords": [
101
102
  "bootstrap",
103
+ "bootstrap5",
104
+ "bootstrap-5",
102
105
  "components",
106
+ "component-library",
103
107
  "svelte",
104
108
  "svelte5",
109
+ "sveltekit",
105
110
  "typescript",
106
- "ui"
111
+ "ui",
112
+ "ui-library"
107
113
  ]
108
114
  }