@velkymx/vibeui 0.8.2 → 1.0.2
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/AGENTS.md +44 -0
- package/README.md +245 -58
- package/dist/purify.es-1D-VntFD.mjs +679 -0
- package/dist/src/App.vue.d.ts +3 -0
- package/dist/src/components/HelloWorld.vue.d.ts +6 -0
- package/dist/src/components/VibeAccordion.vue.d.ts +89 -0
- package/dist/{components → src/components}/VibeAlert.vue.d.ts +24 -30
- package/dist/src/components/VibeAutocomplete.vue.d.ts +74 -0
- package/dist/{components → src/components}/VibeBadge.vue.d.ts +10 -18
- package/dist/src/components/VibeBreadcrumb.vue.d.ts +51 -0
- package/dist/{components → src/components}/VibeButton.vue.d.ts +16 -20
- package/dist/{components → src/components}/VibeButtonGroup.vue.d.ts +10 -18
- package/dist/{components → src/components}/VibeCard.vue.d.ts +51 -24
- package/dist/{components → src/components}/VibeCarousel.vue.d.ts +27 -49
- package/dist/src/components/VibeChartBar.vue.d.ts +61 -0
- package/dist/src/components/VibeChartLine.vue.d.ts +70 -0
- package/dist/src/components/VibeChartPie.vue.d.ts +34 -0
- package/dist/{components → src/components}/VibeCloseButton.vue.d.ts +9 -7
- package/dist/{components → src/components}/VibeCol.vue.d.ts +9 -17
- package/dist/src/components/VibeCollapse.vue.d.ts +76 -0
- package/dist/src/components/VibeContainer.vue.d.ts +36 -0
- package/dist/src/components/VibeDataTable.vue.d.ts +131 -0
- package/dist/src/components/VibeDatePicker.vue.d.ts +120 -0
- package/dist/src/components/VibeDraggable.vue.d.ts +77 -0
- package/dist/{components → src/components}/VibeDropdown.vue.d.ts +46 -43
- package/dist/src/components/VibeDroppable.vue.d.ts +70 -0
- package/dist/src/components/VibeFileInput.vue.d.ts +127 -0
- package/dist/{components → src/components}/VibeFormCheckbox.vue.d.ts +30 -22
- package/dist/{components → src/components}/VibeFormDatepicker.vue.d.ts +27 -30
- package/dist/{components → src/components}/VibeFormGroup.vue.d.ts +11 -14
- package/dist/{components → src/components}/VibeFormInput.vue.d.ts +22 -23
- package/dist/{components → src/components}/VibeFormRadio.vue.d.ts +23 -24
- package/dist/{components → src/components}/VibeFormSelect.vue.d.ts +28 -34
- package/dist/{components → src/components}/VibeFormSpinbutton.vue.d.ts +31 -34
- package/dist/{components → src/components}/VibeFormSwitch.vue.d.ts +22 -23
- package/dist/{components → src/components}/VibeFormTextarea.vue.d.ts +24 -25
- package/dist/{components → src/components}/VibeFormWysiwyg.vue.d.ts +24 -23
- package/dist/src/components/VibeHero.vue.d.ts +108 -0
- package/dist/{components → src/components}/VibeIcon.vue.d.ts +27 -7
- package/dist/{components → src/components}/VibeInputGroup.vue.d.ts +11 -13
- package/dist/{components → src/components}/VibeLink.vue.d.ts +14 -18
- package/dist/{components → src/components}/VibeListGroup.vue.d.ts +26 -22
- package/dist/{components → src/components}/VibeModal.vue.d.ts +29 -45
- package/dist/{components → src/components}/VibeNav.vue.d.ts +40 -20
- package/dist/{components → src/components}/VibeNavbar.vue.d.ts +12 -19
- package/dist/src/components/VibeNavbarBrand.vue.d.ts +34 -0
- package/dist/src/components/VibeNavbarNav.vue.d.ts +80 -0
- package/dist/{components → src/components}/VibeNavbarToggle.vue.d.ts +7 -5
- package/dist/{components → src/components}/VibeOffcanvas.vue.d.ts +28 -43
- package/dist/{components → src/components}/VibePagination.vue.d.ts +38 -29
- package/dist/{components → src/components}/VibePlaceholder.vue.d.ts +9 -17
- package/dist/src/components/VibePopover.vue.d.ts +72 -0
- package/dist/src/components/VibeProgress.vue.d.ts +37 -0
- package/dist/src/components/VibeResizable.vue.d.ts +140 -0
- package/dist/{components → src/components}/VibeRow.vue.d.ts +9 -17
- package/dist/src/components/VibeScrollspy.vue.d.ts +89 -0
- package/dist/src/components/VibeSkeleton.vue.d.ts +54 -0
- package/dist/src/components/VibeSlider.vue.d.ts +77 -0
- package/dist/src/components/VibeSortable.vue.d.ts +59 -0
- package/dist/{components → src/components}/VibeSpinner.vue.d.ts +4 -7
- package/dist/src/components/VibeStepper.vue.d.ts +133 -0
- package/dist/src/components/VibeTab.vue.d.ts +41 -0
- package/dist/src/components/VibeTabContent.vue.d.ts +56 -0
- package/dist/src/components/VibeTabs.vue.d.ts +78 -0
- package/dist/{components → src/components}/VibeToast.vue.d.ts +40 -42
- package/dist/src/components/VibeToastHost.vue.d.ts +16 -0
- package/dist/src/components/VibeTooltip.vue.d.ts +63 -0
- package/dist/src/components/chart/chartColors.d.ts +3 -0
- package/dist/src/components/chart/chartResize.d.ts +2 -0
- package/dist/src/components/chart/chartTooltip.d.ts +2 -0
- package/dist/src/components/chart/chartTypes.d.ts +6 -0
- package/dist/src/components/chart/drawBar.d.ts +5 -0
- package/dist/src/components/chart/drawLine.d.ts +18 -0
- package/dist/src/components/chart/drawPie.d.ts +4 -0
- package/dist/src/components/dndStore.d.ts +7 -0
- package/dist/{components → src/components}/index.d.ts +18 -1
- package/dist/{composables → src/composables}/useBreakpoints.d.ts +1 -0
- package/dist/{composables → src/composables}/useColorMode.d.ts +1 -1
- package/dist/src/composables/useForm.d.ts +22 -0
- package/dist/src/composables/useId.d.ts +2 -0
- package/dist/src/composables/usePosition.d.ts +20 -0
- package/dist/src/composables/useToast.d.ts +52 -0
- package/dist/src/directives/vTooltip.d.ts +24 -0
- package/dist/{index.d.ts → src/index.d.ts} +6 -0
- package/dist/src/injectionKeys.d.ts +28 -0
- package/dist/{types.d.ts → src/types.d.ts} +32 -6
- package/dist/src/utils/safeCss.d.ts +16 -0
- package/dist/src/utils/safeHref.d.ts +7 -0
- package/dist/src/utils/sanitizeHtml.d.ts +13 -0
- package/dist/vibeui.css +2 -1
- package/dist/vibeui.es.js +8351 -3508
- package/dist/vibeui.umd.js +3 -1
- package/docs/README.md +85 -13
- package/docs/components/advanced/popover.md +27 -52
- package/docs/components/advanced/scrollspy.md +57 -15
- package/docs/components/advanced/tooltip.md +26 -47
- package/docs/components/card/card.md +9 -1
- package/docs/components/charts/chart-bar.md +129 -0
- package/docs/components/charts/chart-line.md +136 -0
- package/docs/components/charts/chart-pie.md +102 -0
- package/docs/components/core/alert.md +27 -5
- package/docs/components/core/badge.md +0 -6
- package/docs/components/core/button-group.md +0 -6
- package/docs/components/core/button.md +1 -1
- package/docs/components/core/close-button.md +5 -5
- package/docs/components/core/link.md +60 -17
- package/docs/components/core/placeholder.md +0 -6
- package/docs/components/core/skeleton.md +40 -0
- package/docs/components/core/spinner.md +0 -6
- package/docs/components/data/datatable.md +29 -10
- package/docs/components/hero/hero.md +64 -0
- package/docs/components/interactive/accordion.md +29 -0
- package/docs/components/interactive/carousel.md +19 -4
- package/docs/components/interactive/collapse.md +3 -2
- package/docs/components/interactive/date-picker.md +78 -0
- package/docs/components/interactive/draggable.md +91 -0
- package/docs/components/interactive/dropdown.md +54 -18
- package/docs/components/interactive/modal.md +15 -20
- package/docs/components/interactive/offcanvas.md +4 -1
- package/docs/components/interactive/resizable.md +73 -0
- package/docs/components/interactive/slider.md +57 -0
- package/docs/components/interactive/sortable.md +52 -0
- package/docs/components/interactive/stepper.md +83 -0
- package/docs/components/interactive/tabs.md +66 -0
- package/docs/components/interactive/toast.md +93 -30
- package/docs/components/layout/col.md +0 -6
- package/docs/components/layout/container.md +0 -6
- package/docs/components/layout/row.md +0 -6
- package/docs/components/list/list-group.md +4 -0
- package/docs/components/navigation/breadcrumb.md +4 -0
- package/docs/components/navigation/nav.md +64 -1
- package/docs/components/navigation/navbar.md +2 -0
- package/docs/components/navigation/pagination.md +16 -0
- package/docs/components/progress/progress.md +7 -1
- package/docs/composables/color-mode.md +44 -6
- package/docs/composables/use-form.md +78 -0
- package/docs/composables/use-position.md +68 -0
- package/docs/composables/use-toast.md +91 -0
- package/docs/directives/v-tooltip.md +58 -0
- package/docs/forms/README.md +60 -61
- package/docs/forms/autocomplete.md +96 -0
- package/docs/forms/file-input.md +97 -0
- package/docs/forms/form-checkbox.md +75 -25
- package/docs/forms/form-datepicker.md +58 -28
- package/docs/forms/form-group.md +79 -44
- package/docs/forms/form-input.md +88 -31
- package/docs/forms/form-radio.md +60 -36
- package/docs/forms/form-select.md +75 -31
- package/docs/forms/form-spinbutton.md +72 -33
- package/docs/forms/form-switch.md +53 -25
- package/docs/forms/form-textarea.md +60 -29
- package/docs/forms/form-wysiwyg.md +73 -38
- package/docs/forms/input-group.md +53 -34
- package/docs/forms/validation.md +135 -521
- package/docs/getting-started/starter-template.md +136 -0
- package/docs/superpowers/plans/2026-05-12-vibe-charts.md +1999 -0
- package/docs/superpowers/plans/2026-05-29-e2e-browser-testing.md +615 -0
- package/docs/superpowers/specs/2026-05-12-charting-design.md +185 -0
- package/docs/superpowers/specs/2026-05-29-e2e-browser-testing-design.md +168 -0
- package/docs/utilities/position.md +82 -0
- package/docs/versioning.md +57 -0
- package/examples/README.md +201 -0
- package/examples/album.html +262 -0
- package/examples/blog.html +166 -0
- package/examples/carousel.html +114 -0
- package/examples/checkout.html +275 -0
- package/examples/cover.html +89 -0
- package/examples/dashboard.html +156 -0
- package/examples/index.html +129 -0
- package/examples/jumbotron.html +56 -0
- package/examples/mobile-dashboard.html +138 -0
- package/examples/pricing.html +172 -0
- package/examples/product.html +145 -0
- package/examples/sidebars.html +166 -0
- package/examples/sign-in.html +119 -0
- package/examples/starter.html +314 -0
- package/examples/sticky-footer-navbar.html +70 -0
- package/examples/sticky-footer.html +60 -0
- package/examples/test-simple.html +91 -0
- package/llms.txt +407 -47
- package/package.json +42 -13
- package/dist/App.vue.d.ts +0 -2
- package/dist/components/HelloWorld.vue.d.ts +0 -5
- package/dist/components/VibeAccordion.vue.d.ts +0 -90
- package/dist/components/VibeBreadcrumb.vue.d.ts +0 -47
- package/dist/components/VibeCollapse.vue.d.ts +0 -84
- package/dist/components/VibeContainer.vue.d.ts +0 -44
- package/dist/components/VibeDataTable.vue.d.ts +0 -76
- package/dist/components/VibeNavbarBrand.vue.d.ts +0 -42
- package/dist/components/VibeNavbarNav.vue.d.ts +0 -61
- package/dist/components/VibePopover.vue.d.ts +0 -95
- package/dist/components/VibeProgress.vue.d.ts +0 -45
- package/dist/components/VibeScrollspy.vue.d.ts +0 -77
- package/dist/components/VibeTabContent.vue.d.ts +0 -74
- package/dist/components/VibeTooltip.vue.d.ts +0 -86
- package/dist/composables/useId.d.ts +0 -5
- package/dist/types/index.d.ts +0 -6
- /package/dist/{composables → src/composables}/useBackButton.d.ts +0 -0
- /package/dist/{composables → src/composables}/useFormValidation.d.ts +0 -0
- /package/dist/{main.d.ts → src/main.d.ts} +0 -0
package/docs/README.md
CHANGED
|
@@ -6,12 +6,26 @@ Complete documentation for VibeUI - A modern Vue 3 UI component library built wi
|
|
|
6
6
|
|
|
7
7
|
VibeUI is designed to be simple and lightweight, providing Vue 3 components that wrap Bootstrap 5.3 functionality with a clean, intuitive API.
|
|
8
8
|
|
|
9
|
+
> **New here?** See the [Starter Template](./getting-started/starter-template.md) for a complete, copy-pasteable minimal app.
|
|
10
|
+
|
|
9
11
|
### Installation
|
|
10
12
|
|
|
11
13
|
```bash
|
|
12
14
|
npm install @velkymx/vibeui bootstrap
|
|
13
15
|
```
|
|
14
16
|
|
|
17
|
+
#### Optional peer dependencies
|
|
18
|
+
|
|
19
|
+
Install these only if you use the components that need them:
|
|
20
|
+
|
|
21
|
+
- `bootstrap-icons` — required by `VibeIcon`.
|
|
22
|
+
- `quill` + `dompurify` — required by `VibeFormWysiwyg`.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install bootstrap-icons # for VibeIcon
|
|
26
|
+
npm install quill dompurify # for VibeFormWysiwyg
|
|
27
|
+
```
|
|
28
|
+
|
|
15
29
|
### Setup
|
|
16
30
|
|
|
17
31
|
```javascript
|
|
@@ -29,7 +43,7 @@ createApp(App).use(VibeUI).mount('#app')
|
|
|
29
43
|
|
|
30
44
|
### Bootstrap JavaScript
|
|
31
45
|
|
|
32
|
-
**VibeUI
|
|
46
|
+
**Do NOT import Bootstrap JS — VibeUI manages it.** VibeUI is mobile-optimized and fully abstracts Bootstrap's JavaScript. You do not need to manually import or initialize Bootstrap JS for VibeUI components; importing it yourself can cause duplicate instances and event conflicts.
|
|
33
47
|
|
|
34
48
|
The library handles:
|
|
35
49
|
- **Dynamic Initialization:** Components initialize their own JS logic on mount.
|
|
@@ -49,42 +63,97 @@ The library handles:
|
|
|
49
63
|
- [VibeBadge](./components/core/badge.md)
|
|
50
64
|
- [VibeButton](./components/core/button.md)
|
|
51
65
|
- [VibeButtonGroup](./components/core/button-group.md)
|
|
52
|
-
- [VibeLink](./components/core/link.md)
|
|
53
66
|
- [VibeCloseButton](./components/core/close-button.md)
|
|
54
|
-
- [
|
|
67
|
+
- [VibeLink](./components/core/link.md)
|
|
55
68
|
- [VibePlaceholder](./components/core/placeholder.md)
|
|
69
|
+
- [VibeSkeleton](./components/core/skeleton.md)
|
|
70
|
+
- [VibeSpinner](./components/core/spinner.md)
|
|
56
71
|
|
|
57
72
|
### [Interactive Components](./components/interactive/)
|
|
58
73
|
- [VibeAccordion](./components/interactive/accordion.md)
|
|
59
74
|
- [VibeCarousel](./components/interactive/carousel.md)
|
|
60
75
|
- [VibeCollapse](./components/interactive/collapse.md)
|
|
76
|
+
- [VibeDatePicker](./components/interactive/date-picker.md)
|
|
77
|
+
- [VibeDraggable / VibeDroppable](./components/interactive/draggable.md)
|
|
61
78
|
- [VibeDropdown](./components/interactive/dropdown.md)
|
|
62
79
|
- [VibeModal](./components/interactive/modal.md)
|
|
63
80
|
- [VibeOffcanvas](./components/interactive/offcanvas.md)
|
|
81
|
+
- [VibeResizable](./components/interactive/resizable.md)
|
|
82
|
+
- [VibeSlider](./components/interactive/slider.md)
|
|
83
|
+
- [VibeSortable](./components/interactive/sortable.md)
|
|
84
|
+
- [VibeStepper](./components/interactive/stepper.md)
|
|
85
|
+
- [VibeTabs / VibeTab](./components/interactive/tabs.md)
|
|
64
86
|
- [VibeToast](./components/interactive/toast.md)
|
|
65
87
|
|
|
66
88
|
### [Advanced Components](./components/advanced/)
|
|
67
|
-
- [VibeTooltip](./components/advanced/tooltip.md)
|
|
68
89
|
- [VibePopover](./components/advanced/popover.md)
|
|
69
90
|
- [VibeScrollspy](./components/advanced/scrollspy.md)
|
|
91
|
+
- [VibeTooltip](./components/advanced/tooltip.md)
|
|
92
|
+
|
|
93
|
+
### [Card](./components/card/)
|
|
94
|
+
- [VibeCard](./components/card/card.md)
|
|
95
|
+
|
|
96
|
+
### [Hero](./components/hero/)
|
|
97
|
+
- [VibeHero](./components/hero/hero.md)
|
|
98
|
+
|
|
99
|
+
### [List](./components/list/)
|
|
100
|
+
- [VibeListGroup](./components/list/list-group.md)
|
|
101
|
+
|
|
102
|
+
### [Navigation](./components/navigation/)
|
|
103
|
+
- [VibeBreadcrumb](./components/navigation/breadcrumb.md)
|
|
104
|
+
- [VibeNav](./components/navigation/nav.md)
|
|
105
|
+
- [VibeNavbar](./components/navigation/navbar.md)
|
|
106
|
+
- [VibePagination](./components/navigation/pagination.md)
|
|
70
107
|
|
|
71
108
|
### [Data Components](./components/data/)
|
|
72
109
|
- [VibeDataTable](./components/data/datatable.md)
|
|
73
110
|
|
|
111
|
+
### [Charts](./components/charts/)
|
|
112
|
+
- [VibeChartBar](./components/charts/chart-bar.md)
|
|
113
|
+
- [VibeChartLine](./components/charts/chart-line.md)
|
|
114
|
+
- [VibeChartPie](./components/charts/chart-pie.md)
|
|
115
|
+
|
|
116
|
+
### [Progress](./components/progress/)
|
|
117
|
+
- [VibeProgress](./components/progress/progress.md)
|
|
118
|
+
|
|
74
119
|
### [Form Components](./forms/)
|
|
120
|
+
- [VibeAutocomplete](./forms/autocomplete.md)
|
|
121
|
+
- [VibeFileInput](./forms/file-input.md)
|
|
122
|
+
- [VibeFormCheckbox](./forms/form-checkbox.md)
|
|
123
|
+
- [VibeFormDatepicker](./forms/form-datepicker.md) (native input)
|
|
75
124
|
- [VibeFormGroup](./forms/form-group.md) - Automated layout & accessibility
|
|
76
125
|
- [VibeFormInput](./forms/form-input.md)
|
|
77
|
-
- [
|
|
126
|
+
- [VibeFormRadio](./forms/form-radio.md)
|
|
78
127
|
- [VibeFormSelect](./forms/form-select.md)
|
|
128
|
+
- [VibeFormSpinbutton](./forms/form-spinbutton.md)
|
|
129
|
+
- [VibeFormSwitch](./forms/form-switch.md)
|
|
130
|
+
- [VibeFormTextarea](./forms/form-textarea.md)
|
|
79
131
|
- [VibeFormWysiwyg](./forms/form-wysiwyg.md)
|
|
132
|
+
- [VibeInputGroup](./forms/input-group.md)
|
|
133
|
+
- [Validation rules](./forms/validation.md)
|
|
80
134
|
|
|
81
135
|
## Composables
|
|
82
136
|
|
|
83
137
|
Standalone utilities that can be used independently of any component.
|
|
84
138
|
|
|
85
|
-
- [
|
|
86
|
-
-
|
|
87
|
-
-
|
|
139
|
+
- [useBackButton](./composables/back-button.md) - Android hardware back button handling
|
|
140
|
+
- [useBreakpoints](./composables/breakpoints.md) - Programmatic breakpoint detection
|
|
141
|
+
- [useColorMode](./composables/color-mode.md) - Bootstrap light/dark/auto color modes
|
|
142
|
+
- [useForm](./composables/use-form.md) - Multi-field form state with validation
|
|
143
|
+
- [usePosition](./composables/use-position.md) - Floating-UI based anchor positioning
|
|
144
|
+
- [useToast](./composables/use-toast.md) - Global toast service
|
|
145
|
+
|
|
146
|
+
## Directives
|
|
147
|
+
|
|
148
|
+
- [v-vibe-tooltip](./directives/v-tooltip.md) - Inline tooltip directive
|
|
149
|
+
|
|
150
|
+
## Utilities
|
|
151
|
+
|
|
152
|
+
- [Position utility classes](./utilities/position.md) - Bootstrap position-* / top-* / translate-middle reference
|
|
153
|
+
|
|
154
|
+
## Versioning & Stability
|
|
155
|
+
|
|
156
|
+
- [Versioning & Stability Policy](./versioning.md) - SemVer commitment, what's covered by the public API, deprecation and peer-dependency policy
|
|
88
157
|
|
|
89
158
|
## Design Philosophy
|
|
90
159
|
|
|
@@ -102,21 +171,24 @@ Most interactive and form components support `v-model` for two-way state synchro
|
|
|
102
171
|
<VibeModal v-model="show" title="Hello" />
|
|
103
172
|
```
|
|
104
173
|
|
|
105
|
-
### Instance Exposure
|
|
106
|
-
|
|
174
|
+
### Instance Exposure (escape hatch)
|
|
175
|
+
|
|
176
|
+
The underlying Bootstrap instance is exposed as `_unsafe_bsInstance` for rare cases the props/`v-model` API doesn't cover.
|
|
177
|
+
|
|
178
|
+
> **This is an escape hatch, not part of the stable API.** Calling Bootstrap lifecycle methods directly will desync VibeUI's internal state and can break the component. The `_unsafe_` prefix is intentional — prefer props and `v-model` whenever possible.
|
|
107
179
|
|
|
108
180
|
```vue
|
|
109
181
|
<template>
|
|
110
182
|
<VibeModal ref="myModal" />
|
|
111
183
|
</template>
|
|
112
184
|
|
|
113
|
-
<script setup>
|
|
185
|
+
<script setup lang="ts">
|
|
114
186
|
import { useTemplateRef, onMounted } from 'vue'
|
|
115
187
|
const modal = useTemplateRef('myModal')
|
|
116
188
|
|
|
117
189
|
onMounted(() => {
|
|
118
|
-
//
|
|
119
|
-
modal.value
|
|
190
|
+
// Escape hatch — only when no prop/v-model covers your case
|
|
191
|
+
modal.value?._unsafe_bsInstance?.handleUpdate()
|
|
120
192
|
})
|
|
121
193
|
</script>
|
|
122
194
|
```
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# VibePopover
|
|
2
2
|
|
|
3
|
-
Popovers for displaying
|
|
3
|
+
Popovers for displaying contextual content overlays. Bootstrap's popover JS is initialized automatically when the component mounts.
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
7
|
| Prop | Type | Default | Description |
|
|
8
8
|
|------|------|---------|-------------|
|
|
9
|
+
| `text` | `String` | `undefined` | Popover body content (preferred) |
|
|
10
|
+
| `content` | `String` | `undefined` | Alias for `text` |
|
|
9
11
|
| `title` | `String` | `undefined` | Popover title |
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
| `placement` | `TooltipPlacement` | `'top'` | Popover position: `'top'`, `'bottom'`, `'start'`, `'end'` |
|
|
13
|
+
| `trigger` | `String` | `'click'` | Trigger events (space-separated): `'click'`, `'hover'`, `'focus'` |
|
|
14
|
+
|
|
15
|
+
Provide either `text` or `content` (they are interchangeable). Content is always rendered as plain text — HTML is not supported (this is intentional, to avoid XSS).
|
|
16
|
+
|
|
17
|
+
## Events
|
|
18
|
+
|
|
19
|
+
| Event | Payload | Description |
|
|
20
|
+
|-------|---------|-------------|
|
|
21
|
+
| `component-error` | `{ message, componentName, originalError }` | Emitted if Bootstrap JS is unavailable at mount |
|
|
14
22
|
|
|
15
23
|
## Slots
|
|
16
24
|
|
|
@@ -18,6 +26,10 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
18
26
|
|------|-------------|
|
|
19
27
|
| `default` | Element that triggers the popover |
|
|
20
28
|
|
|
29
|
+
## Exposed
|
|
30
|
+
|
|
31
|
+
> **Escape hatch:** `_unsafe_bsInstance` (the underlying Bootstrap `Popover` instance) is exposed via a template ref. It is **not** part of the stable API — calling `dispose()` or other lifecycle methods on it directly WILL break the component.
|
|
32
|
+
|
|
21
33
|
## Usage
|
|
22
34
|
|
|
23
35
|
### Basic Popover
|
|
@@ -26,7 +38,7 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
26
38
|
<template>
|
|
27
39
|
<VibePopover
|
|
28
40
|
title="Popover Title"
|
|
29
|
-
|
|
41
|
+
text="This is the popover content"
|
|
30
42
|
>
|
|
31
43
|
<VibeButton variant="danger">Click me</VibeButton>
|
|
32
44
|
</VibePopover>
|
|
@@ -40,7 +52,7 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
40
52
|
<div>
|
|
41
53
|
<VibePopover
|
|
42
54
|
title="Top Popover"
|
|
43
|
-
|
|
55
|
+
text="Popover on top"
|
|
44
56
|
placement="top"
|
|
45
57
|
>
|
|
46
58
|
<VibeButton variant="secondary">Top</VibeButton>
|
|
@@ -48,7 +60,7 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
48
60
|
|
|
49
61
|
<VibePopover
|
|
50
62
|
title="End Popover"
|
|
51
|
-
|
|
63
|
+
text="Popover on right"
|
|
52
64
|
placement="end"
|
|
53
65
|
>
|
|
54
66
|
<VibeButton variant="secondary">End</VibeButton>
|
|
@@ -56,7 +68,7 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
56
68
|
|
|
57
69
|
<VibePopover
|
|
58
70
|
title="Bottom Popover"
|
|
59
|
-
|
|
71
|
+
text="Popover on bottom"
|
|
60
72
|
placement="bottom"
|
|
61
73
|
>
|
|
62
74
|
<VibeButton variant="secondary">Bottom</VibeButton>
|
|
@@ -64,7 +76,7 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
64
76
|
|
|
65
77
|
<VibePopover
|
|
66
78
|
title="Start Popover"
|
|
67
|
-
|
|
79
|
+
text="Popover on left"
|
|
68
80
|
placement="start"
|
|
69
81
|
>
|
|
70
82
|
<VibeButton variant="secondary">Start</VibeButton>
|
|
@@ -79,7 +91,7 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
79
91
|
<template>
|
|
80
92
|
<VibePopover
|
|
81
93
|
title="Hover Popover"
|
|
82
|
-
|
|
94
|
+
text="This appears on hover"
|
|
83
95
|
trigger="hover focus"
|
|
84
96
|
>
|
|
85
97
|
<VibeButton variant="info">Hover me</VibeButton>
|
|
@@ -87,25 +99,11 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
87
99
|
</template>
|
|
88
100
|
```
|
|
89
101
|
|
|
90
|
-
### HTML Content
|
|
91
|
-
|
|
92
|
-
```vue
|
|
93
|
-
<template>
|
|
94
|
-
<VibePopover
|
|
95
|
-
title="<strong>HTML Title</strong>"
|
|
96
|
-
content="<em>HTML</em> content with <a href='#'>link</a>"
|
|
97
|
-
html
|
|
98
|
-
>
|
|
99
|
-
<VibeButton variant="warning">HTML Popover</VibeButton>
|
|
100
|
-
</VibePopover>
|
|
101
|
-
</template>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
102
|
### No Title
|
|
105
103
|
|
|
106
104
|
```vue
|
|
107
105
|
<template>
|
|
108
|
-
<VibePopover
|
|
106
|
+
<VibePopover text="Just content, no title">
|
|
109
107
|
<VibeButton variant="success">No Title</VibeButton>
|
|
110
108
|
</VibePopover>
|
|
111
109
|
</template>
|
|
@@ -113,37 +111,14 @@ Popovers for displaying rich content overlays. Requires Bootstrap JS and initial
|
|
|
113
111
|
|
|
114
112
|
## Important Notes
|
|
115
113
|
|
|
116
|
-
**Automatic Initialization:** This component
|
|
117
|
-
|
|
118
|
-
**Touch Optimization:** On touch devices, if the trigger is set to `hover focus`, it automatically switches to `click` to ensure reliable behavior on mobile screens.
|
|
114
|
+
**Automatic Initialization:** This component initializes Bootstrap's Popover for you when it mounts. Bootstrap's JS is loaded internally — do not import or initialize Bootstrap JS yourself.
|
|
119
115
|
|
|
120
|
-
**
|
|
116
|
+
**Touch Optimization:** On touch devices, if the trigger is `hover focus`, it automatically switches to `click` for reliable behavior on mobile screens. Initialization is SSR-safe.
|
|
121
117
|
|
|
122
|
-
|
|
123
|
-
// Initialize all popovers
|
|
124
|
-
import { Popover } from 'bootstrap'
|
|
125
|
-
...
|
|
126
|
-
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
|
|
127
|
-
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new Popover(popoverTriggerEl))
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Or in Vue:
|
|
131
|
-
|
|
132
|
-
```vue
|
|
133
|
-
<script setup>
|
|
134
|
-
import { onMounted } from 'vue'
|
|
135
|
-
import { Popover } from 'bootstrap'
|
|
136
|
-
|
|
137
|
-
onMounted(() => {
|
|
138
|
-
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
|
|
139
|
-
const popoverList = [...popoverTriggerList].map(el => new Popover(el))
|
|
140
|
-
})
|
|
141
|
-
</script>
|
|
142
|
-
```
|
|
118
|
+
**Reactive content:** Updating `text`/`content`/`title` updates the live popover without a re-init.
|
|
143
119
|
|
|
144
120
|
## Bootstrap CSS Classes
|
|
145
121
|
|
|
146
|
-
- Uses Bootstrap's `data-bs-toggle="popover"` attributes
|
|
147
122
|
- `.popover`
|
|
148
123
|
- `.popover-arrow`
|
|
149
124
|
- `.popover-header`
|
|
@@ -7,16 +7,19 @@ Automatically update navigation based on scroll position.
|
|
|
7
7
|
| Prop | Type | Default | Description |
|
|
8
8
|
|------|------|---------|-------------|
|
|
9
9
|
| `target` | `String` | Required | CSS selector of navigation element to update |
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
10
|
+
| `rootMargin` | `String` | `'0px 0px -25%'` | Intersection margin for triggering activation. Format: `'top right bottom left'` in CSS units |
|
|
11
|
+
| `offset` | `Number` | `undefined` | **Deprecated.** Use `rootMargin` instead. Ignored in Bootstrap 5.2+. |
|
|
12
|
+
| `method` | `String` | `'auto'` | Scroll detection method: `'auto'`, `'offset'`, or `'position'` |
|
|
12
13
|
| `smoothScroll` | `Boolean` | `false` | Enable smooth scrolling |
|
|
13
14
|
| `tag` | `String` | `'div'` | HTML tag to render |
|
|
15
|
+
| `height` | `String` | `'100%'` | CSS height of the scrollable container. Validated as a CSS length; invalid values fall back to `'100%'` |
|
|
14
16
|
|
|
15
17
|
## Events
|
|
16
18
|
|
|
17
19
|
| Event | Payload | Description |
|
|
18
20
|
|-------|---------|-------------|
|
|
19
|
-
| `activate` |
|
|
21
|
+
| `activate` | `event` | Emitted when a new nav item is activated |
|
|
22
|
+
| `component-error` | `{ message, componentName, originalError }` | Emitted if Bootstrap JS is unavailable |
|
|
20
23
|
|
|
21
24
|
## Slots
|
|
22
25
|
|
|
@@ -24,6 +27,12 @@ Automatically update navigation based on scroll position.
|
|
|
24
27
|
|------|-------------|
|
|
25
28
|
| `default` | Scrollable content with target sections |
|
|
26
29
|
|
|
30
|
+
## Exposed Methods
|
|
31
|
+
|
|
32
|
+
| Method | Description |
|
|
33
|
+
|--------|-------------|
|
|
34
|
+
| `refresh()` | Recalculates section positions. Call after dynamic content changes. |
|
|
35
|
+
|
|
27
36
|
## Usage
|
|
28
37
|
|
|
29
38
|
### Basic Scrollspy
|
|
@@ -35,11 +44,7 @@ Automatically update navigation based on scroll position.
|
|
|
35
44
|
<VibeNav id="navbar-example" vertical pills :items="navItems" />
|
|
36
45
|
</div>
|
|
37
46
|
<div class="col-8">
|
|
38
|
-
<VibeScrollspy
|
|
39
|
-
target="#navbar-example"
|
|
40
|
-
smooth-scroll
|
|
41
|
-
style="height: 300px; overflow-y: auto"
|
|
42
|
-
>
|
|
47
|
+
<VibeScrollspy target="#navbar-example" :height="'300px'" smooth-scroll>
|
|
43
48
|
<h4 id="item-1">Item 1</h4>
|
|
44
49
|
<p>Content for item 1...</p>
|
|
45
50
|
<h4 id="item-2">Item 2</h4>
|
|
@@ -48,17 +53,54 @@ Automatically update navigation based on scroll position.
|
|
|
48
53
|
</div>
|
|
49
54
|
</div>
|
|
50
55
|
</template>
|
|
56
|
+
|
|
57
|
+
<script setup>
|
|
58
|
+
const navItems = [
|
|
59
|
+
{ text: 'Item 1', href: '#item-1' },
|
|
60
|
+
{ text: 'Item 2', href: '#item-2' }
|
|
61
|
+
]
|
|
62
|
+
</script>
|
|
51
63
|
```
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
### Adjusting the Activation Margin
|
|
66
|
+
|
|
67
|
+
`rootMargin` controls how early a section is activated relative to the scrollable viewport. The default `'0px 0px -25%'` activates a section when it enters the top 75% of the container.
|
|
68
|
+
|
|
69
|
+
```vue
|
|
70
|
+
<!-- Activate earlier — section activates when it crosses the midpoint -->
|
|
71
|
+
<VibeScrollspy target="#nav" root-margin="0px 0px -50%">
|
|
72
|
+
...
|
|
73
|
+
</VibeScrollspy>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Refreshing After Dynamic Content
|
|
54
77
|
|
|
55
|
-
|
|
78
|
+
```vue
|
|
79
|
+
<script setup>
|
|
80
|
+
import { ref } from 'vue'
|
|
81
|
+
|
|
82
|
+
const scrollspy = ref(null)
|
|
83
|
+
|
|
84
|
+
async function loadMore() {
|
|
85
|
+
// ... add content
|
|
86
|
+
await nextTick()
|
|
87
|
+
scrollspy.value.refresh()
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<VibeScrollspy ref="scrollspy" target="#nav">
|
|
93
|
+
...
|
|
94
|
+
</VibeScrollspy>
|
|
95
|
+
</template>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Important Notes
|
|
56
99
|
|
|
57
|
-
**
|
|
100
|
+
**Automatic Initialization:** This component automatically initializes Bootstrap's ScrollSpy functionality when mounted, provided Bootstrap JavaScript is available.
|
|
58
101
|
|
|
59
|
-
|
|
102
|
+
**`rootMargin` replaces `offset`:** Bootstrap 5.2 replaced the `offset` integer with `rootMargin` (an IntersectionObserver-compatible margin string). The `offset` prop is deprecated and logs a console warning. Use `rootMargin` for all new usage.
|
|
60
103
|
|
|
61
|
-
|
|
104
|
+
**KeepAlive support:** Positions are recalculated when the component is reactivated inside `<KeepAlive>`.
|
|
62
105
|
|
|
63
|
-
|
|
64
|
-
- Targets navigation items automatically
|
|
106
|
+
**Validated height:** The `height` prop is validated as a CSS length string before being applied. Invalid values silently fall back to `'100%'` (CSS injection defense).
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
# VibeTooltip
|
|
2
2
|
|
|
3
|
-
Tooltips for displaying contextual information.
|
|
3
|
+
Tooltips for displaying contextual information. Bootstrap's tooltip JS is initialized automatically when the component mounts.
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
7
|
| Prop | Type | Default | Description |
|
|
8
8
|
|------|------|---------|-------------|
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
9
|
+
| `text` | `String` | `undefined` | Tooltip text content (preferred) |
|
|
10
|
+
| `content` | `String` | `undefined` | **Deprecated.** Use `text` instead |
|
|
11
|
+
| `placement` | `TooltipPlacement` | `'top'` | Tooltip position: `'top'`, `'bottom'`, `'start'`, `'end'` |
|
|
12
|
+
| `trigger` | `String` | `'hover focus'` | Trigger events (space-separated): `'hover'`, `'focus'`, `'click'` |
|
|
13
|
+
|
|
14
|
+
Content is always rendered as plain text — HTML is not supported (this is intentional, to avoid XSS).
|
|
15
|
+
|
|
16
|
+
## Events
|
|
17
|
+
|
|
18
|
+
| Event | Payload | Description |
|
|
19
|
+
|-------|---------|-------------|
|
|
20
|
+
| `component-error` | `{ message, componentName, originalError }` | Emitted if Bootstrap JS is unavailable at mount |
|
|
13
21
|
|
|
14
22
|
## Slots
|
|
15
23
|
|
|
@@ -17,13 +25,17 @@ Tooltips for displaying contextual information. Requires Bootstrap JS and initia
|
|
|
17
25
|
|------|-------------|
|
|
18
26
|
| `default` | Element that triggers the tooltip |
|
|
19
27
|
|
|
28
|
+
## Exposed
|
|
29
|
+
|
|
30
|
+
> **Escape hatch:** `_unsafe_bsInstance` (the underlying Bootstrap `Tooltip` instance) is exposed via a template ref. It is **not** part of the stable API — calling `dispose()` or other lifecycle methods on it directly WILL break the component.
|
|
31
|
+
|
|
20
32
|
## Usage
|
|
21
33
|
|
|
22
34
|
### Basic Tooltip
|
|
23
35
|
|
|
24
36
|
```vue
|
|
25
37
|
<template>
|
|
26
|
-
<VibeTooltip
|
|
38
|
+
<VibeTooltip text="Tooltip text">
|
|
27
39
|
<VibeButton variant="secondary">Hover me</VibeButton>
|
|
28
40
|
</VibeTooltip>
|
|
29
41
|
</template>
|
|
@@ -34,19 +46,19 @@ Tooltips for displaying contextual information. Requires Bootstrap JS and initia
|
|
|
34
46
|
```vue
|
|
35
47
|
<template>
|
|
36
48
|
<div>
|
|
37
|
-
<VibeTooltip
|
|
49
|
+
<VibeTooltip text="Tooltip on top" placement="top">
|
|
38
50
|
<VibeButton variant="secondary">Top</VibeButton>
|
|
39
51
|
</VibeTooltip>
|
|
40
52
|
|
|
41
|
-
<VibeTooltip
|
|
53
|
+
<VibeTooltip text="Tooltip on right" placement="end">
|
|
42
54
|
<VibeButton variant="secondary">End</VibeButton>
|
|
43
55
|
</VibeTooltip>
|
|
44
56
|
|
|
45
|
-
<VibeTooltip
|
|
57
|
+
<VibeTooltip text="Tooltip on bottom" placement="bottom">
|
|
46
58
|
<VibeButton variant="secondary">Bottom</VibeButton>
|
|
47
59
|
</VibeTooltip>
|
|
48
60
|
|
|
49
|
-
<VibeTooltip
|
|
61
|
+
<VibeTooltip text="Tooltip on left" placement="start">
|
|
50
62
|
<VibeButton variant="secondary">Start</VibeButton>
|
|
51
63
|
</VibeTooltip>
|
|
52
64
|
</div>
|
|
@@ -57,55 +69,22 @@ Tooltips for displaying contextual information. Requires Bootstrap JS and initia
|
|
|
57
69
|
|
|
58
70
|
```vue
|
|
59
71
|
<template>
|
|
60
|
-
<VibeTooltip
|
|
72
|
+
<VibeTooltip text="Click to see tooltip" trigger="click">
|
|
61
73
|
<VibeButton variant="primary">Click me</VibeButton>
|
|
62
74
|
</VibeTooltip>
|
|
63
75
|
</template>
|
|
64
76
|
```
|
|
65
77
|
|
|
66
|
-
### HTML Content
|
|
67
|
-
|
|
68
|
-
```vue
|
|
69
|
-
<template>
|
|
70
|
-
<VibeTooltip content="<em>Tooltip</em> with <strong>HTML</strong>" html>
|
|
71
|
-
<VibeButton variant="info">HTML Tooltip</VibeButton>
|
|
72
|
-
</VibeTooltip>
|
|
73
|
-
</template>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
78
|
## Important Notes
|
|
77
79
|
|
|
78
|
-
**Automatic Initialization:** This component
|
|
80
|
+
**Automatic Initialization:** This component initializes Bootstrap's Tooltip for you when it mounts. Bootstrap's JS is loaded internally — do not import or initialize Bootstrap JS yourself.
|
|
79
81
|
|
|
80
|
-
**Touch Optimization:** On touch devices, the
|
|
82
|
+
**Touch Optimization:** On touch devices, when the trigger is `hover focus` it automatically switches to `click` for reliable behavior on mobile screens. Initialization is SSR-safe.
|
|
81
83
|
|
|
82
|
-
**
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
// Initialize all tooltips
|
|
86
|
-
import { Tooltip } from 'bootstrap'
|
|
87
|
-
...
|
|
88
|
-
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
|
89
|
-
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl))
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Or in Vue:
|
|
93
|
-
|
|
94
|
-
```vue
|
|
95
|
-
<script setup>
|
|
96
|
-
import { onMounted } from 'vue'
|
|
97
|
-
import { Tooltip } from 'bootstrap'
|
|
98
|
-
|
|
99
|
-
onMounted(() => {
|
|
100
|
-
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
|
101
|
-
const tooltipList = [...tooltipTriggerList].map(el => new Tooltip(el))
|
|
102
|
-
})
|
|
103
|
-
</script>
|
|
104
|
-
```
|
|
84
|
+
**Reactive content:** Updating `text` (or the deprecated `content`) updates the live tooltip without a re-init.
|
|
105
85
|
|
|
106
86
|
## Bootstrap CSS Classes
|
|
107
87
|
|
|
108
|
-
- Uses Bootstrap's `data-bs-toggle="tooltip"` attributes
|
|
109
88
|
- `.tooltip`
|
|
110
89
|
- `.tooltip-arrow`
|
|
111
90
|
- `.tooltip-inner`
|
|
@@ -14,9 +14,12 @@ Flexible content container component for displaying headers, bodies, footers, an
|
|
|
14
14
|
| `body` | `String` | `undefined` | Card body text |
|
|
15
15
|
| `header` | `String` | `undefined` | Card header text |
|
|
16
16
|
| `footer` | `String` | `undefined` | Card footer text |
|
|
17
|
+
| `headerClass` | `String \| Array \| Object` | `undefined` | Extra class(es) for the `.card-header` wrapper (e.g. a variant-colored header) |
|
|
18
|
+
| `bodyClass` | `String \| Array \| Object` | `undefined` | Extra class(es) for the `.card-body` wrapper |
|
|
19
|
+
| `footerClass` | `String \| Array \| Object` | `undefined` | Extra class(es) for the `.card-footer` wrapper |
|
|
17
20
|
| `imgSrc` | `String` | `undefined` | Image URL |
|
|
18
21
|
| `imgAlt` | `String` | `''` | Image alt text |
|
|
19
|
-
| `imgTop` | `Boolean` | `
|
|
22
|
+
| `imgTop` | `Boolean` | `false` | Display image at top |
|
|
20
23
|
| `imgBottom` | `Boolean` | `false` | Display image at bottom |
|
|
21
24
|
|
|
22
25
|
## Slots
|
|
@@ -24,6 +27,7 @@ Flexible content container component for displaying headers, bodies, footers, an
|
|
|
24
27
|
| Slot | Description |
|
|
25
28
|
|------|-------------|
|
|
26
29
|
| `default` | Additional body content |
|
|
30
|
+
| `image` | A direct card child rendered **outside** `.card-body` (before the header) — for a card image with custom composition, e.g. a `card-img-top` with an absolutely-positioned overlay/badge |
|
|
27
31
|
| `header` | Custom header content |
|
|
28
32
|
| `title` | Custom title content |
|
|
29
33
|
| `body` | Custom body content |
|
|
@@ -62,6 +66,7 @@ Flexible content container component for displaying headers, bodies, footers, an
|
|
|
62
66
|
<VibeCard
|
|
63
67
|
img-src="/path/to/image.jpg"
|
|
64
68
|
img-alt="Card image"
|
|
69
|
+
img-top
|
|
65
70
|
title="Card with Image"
|
|
66
71
|
body="This card has an image at the top."
|
|
67
72
|
/>
|
|
@@ -175,6 +180,7 @@ Use the default slot with Bootstrap grid:
|
|
|
175
180
|
<VibeCard
|
|
176
181
|
img-src="/path/to/image1.jpg"
|
|
177
182
|
img-alt="Card 1"
|
|
183
|
+
img-top
|
|
178
184
|
title="Card 1"
|
|
179
185
|
body="Card content here."
|
|
180
186
|
/>
|
|
@@ -183,6 +189,7 @@ Use the default slot with Bootstrap grid:
|
|
|
183
189
|
<VibeCard
|
|
184
190
|
img-src="/path/to/image2.jpg"
|
|
185
191
|
img-alt="Card 2"
|
|
192
|
+
img-top
|
|
186
193
|
title="Card 2"
|
|
187
194
|
body="Card content here."
|
|
188
195
|
/>
|
|
@@ -191,6 +198,7 @@ Use the default slot with Bootstrap grid:
|
|
|
191
198
|
<VibeCard
|
|
192
199
|
img-src="/path/to/image3.jpg"
|
|
193
200
|
img-alt="Card 3"
|
|
201
|
+
img-top
|
|
194
202
|
title="Card 3"
|
|
195
203
|
body="Card content here."
|
|
196
204
|
/>
|