@velkymx/vibeui 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/{src/components → components}/VibeAlert.vue.d.ts +4 -4
  2. package/dist/{src/components → components}/VibeButton.vue.d.ts +2 -2
  3. package/dist/{src/components → components}/VibeDataTable.vue.d.ts +5 -1
  4. package/dist/components/VibeFormErrorSummary.vue.d.ts +17 -0
  5. package/dist/{src/components → components}/VibeFormInput.vue.d.ts +38 -2
  6. package/dist/{src/components → components}/VibeHero.vue.d.ts +9 -0
  7. package/dist/{src/components → components}/VibeModal.vue.d.ts +18 -0
  8. package/dist/{src/components → components}/VibeSortable.vue.d.ts +1 -1
  9. package/dist/{src/components → components}/VibeToast.vue.d.ts +3 -3
  10. package/dist/{src/components → components}/index.d.ts +2 -1
  11. package/dist/{src/injectionKeys.d.ts → injectionKeys.d.ts} +6 -0
  12. package/dist/purify.es-eYghtX9j.mjs +735 -0
  13. package/dist/{src/types.d.ts → types.d.ts} +2 -0
  14. package/dist/utils/routeKey.d.ts +11 -0
  15. package/dist/vibeui.css +1 -1
  16. package/dist/vibeui.es.js +1240 -982
  17. package/dist/vibeui.umd.js +3 -3
  18. package/docs/components/advanced/popover.md +1 -1
  19. package/docs/components/advanced/scrollspy.md +2 -2
  20. package/docs/components/advanced/tooltip.md +1 -1
  21. package/docs/components/core/button.md +19 -1
  22. package/docs/components/core/close-button.md +1 -1
  23. package/docs/components/data/datatable.md +3 -1
  24. package/docs/components/hero/hero.md +1 -0
  25. package/docs/components/interactive/modal.md +8 -0
  26. package/docs/components/navigation/navbar.md +6 -0
  27. package/docs/components/navigation/pagination.md +1 -0
  28. package/docs/forms/form-error-summary.md +96 -0
  29. package/docs/forms/form-group.md +4 -1
  30. package/docs/forms/form-input.md +28 -0
  31. package/llms.txt +24 -5
  32. package/package.json +1 -1
  33. package/dist/purify.es-1D-VntFD.mjs +0 -679
  34. package/dist/{src/App.vue.d.ts → App.vue.d.ts} +0 -0
  35. package/dist/{src/components → components}/HelloWorld.vue.d.ts +0 -0
  36. package/dist/{src/components → components}/VibeAccordion.vue.d.ts +0 -0
  37. package/dist/{src/components → components}/VibeAutocomplete.vue.d.ts +0 -0
  38. package/dist/{src/components → components}/VibeBadge.vue.d.ts +0 -0
  39. package/dist/{src/components → components}/VibeBreadcrumb.vue.d.ts +0 -0
  40. package/dist/{src/components → components}/VibeButtonGroup.vue.d.ts +1 -1
  41. package/dist/{src/components → components}/VibeCard.vue.d.ts +0 -0
  42. package/dist/{src/components → components}/VibeCarousel.vue.d.ts +1 -1
  43. package/dist/{src/components → components}/VibeChartBar.vue.d.ts +0 -0
  44. package/dist/{src/components → components}/VibeChartLine.vue.d.ts +0 -0
  45. package/dist/{src/components → components}/VibeChartPie.vue.d.ts +0 -0
  46. package/dist/{src/components → components}/VibeCloseButton.vue.d.ts +0 -0
  47. package/dist/{src/components → components}/VibeCol.vue.d.ts +0 -0
  48. package/dist/{src/components → components}/VibeCollapse.vue.d.ts +1 -1
  49. package/dist/{src/components → components}/VibeContainer.vue.d.ts +0 -0
  50. package/dist/{src/components → components}/VibeDatePicker.vue.d.ts +1 -1
  51. package/dist/{src/components → components}/VibeDraggable.vue.d.ts +0 -0
  52. package/dist/{src/components → components}/VibeDropdown.vue.d.ts +0 -0
  53. package/dist/{src/components → components}/VibeDroppable.vue.d.ts +0 -0
  54. package/dist/{src/components → components}/VibeFileInput.vue.d.ts +1 -1
  55. package/dist/{src/components → components}/VibeFormCheckbox.vue.d.ts +1 -1
  56. package/dist/{src/components → components}/VibeFormDatepicker.vue.d.ts +1 -1
  57. package/dist/{src/components → components}/VibeFormGroup.vue.d.ts +0 -0
  58. package/dist/{src/components → components}/VibeFormRadio.vue.d.ts +1 -1
  59. package/dist/{src/components → components}/VibeFormSelect.vue.d.ts +1 -1
  60. package/dist/{src/components → components}/VibeFormSpinbutton.vue.d.ts +1 -1
  61. package/dist/{src/components → components}/VibeFormSwitch.vue.d.ts +1 -1
  62. package/dist/{src/components → components}/VibeFormTextarea.vue.d.ts +1 -1
  63. package/dist/{src/components → components}/VibeFormWysiwyg.vue.d.ts +1 -1
  64. package/dist/{src/components → components}/VibeIcon.vue.d.ts +1 -1
  65. package/dist/{src/components → components}/VibeInputGroup.vue.d.ts +1 -1
  66. /package/dist/{src/components → components}/VibeLink.vue.d.ts +0 -0
  67. /package/dist/{src/components → components}/VibeListGroup.vue.d.ts +0 -0
  68. /package/dist/{src/components → components}/VibeNav.vue.d.ts +0 -0
  69. /package/dist/{src/components → components}/VibeNavbar.vue.d.ts +0 -0
  70. /package/dist/{src/components → components}/VibeNavbarBrand.vue.d.ts +0 -0
  71. /package/dist/{src/components → components}/VibeNavbarNav.vue.d.ts +0 -0
  72. /package/dist/{src/components → components}/VibeNavbarToggle.vue.d.ts +0 -0
  73. /package/dist/{src/components → components}/VibeOffcanvas.vue.d.ts +0 -0
  74. /package/dist/{src/components → components}/VibePagination.vue.d.ts +0 -0
  75. /package/dist/{src/components → components}/VibePlaceholder.vue.d.ts +0 -0
  76. /package/dist/{src/components → components}/VibePopover.vue.d.ts +0 -0
  77. /package/dist/{src/components → components}/VibeProgress.vue.d.ts +0 -0
  78. /package/dist/{src/components → components}/VibeResizable.vue.d.ts +0 -0
  79. /package/dist/{src/components → components}/VibeRow.vue.d.ts +0 -0
  80. /package/dist/{src/components → components}/VibeScrollspy.vue.d.ts +0 -0
  81. /package/dist/{src/components → components}/VibeSkeleton.vue.d.ts +0 -0
  82. /package/dist/{src/components → components}/VibeSlider.vue.d.ts +0 -0
  83. /package/dist/{src/components → components}/VibeSpinner.vue.d.ts +0 -0
  84. /package/dist/{src/components → components}/VibeStepper.vue.d.ts +0 -0
  85. /package/dist/{src/components → components}/VibeTab.vue.d.ts +0 -0
  86. /package/dist/{src/components → components}/VibeTabContent.vue.d.ts +0 -0
  87. /package/dist/{src/components → components}/VibeTabs.vue.d.ts +0 -0
  88. /package/dist/{src/components → components}/VibeToastHost.vue.d.ts +0 -0
  89. /package/dist/{src/components → components}/VibeTooltip.vue.d.ts +0 -0
  90. /package/dist/{src/components → components}/chart/chartColors.d.ts +0 -0
  91. /package/dist/{src/components → components}/chart/chartResize.d.ts +0 -0
  92. /package/dist/{src/components → components}/chart/chartTooltip.d.ts +0 -0
  93. /package/dist/{src/components → components}/chart/chartTypes.d.ts +0 -0
  94. /package/dist/{src/components → components}/chart/drawBar.d.ts +0 -0
  95. /package/dist/{src/components → components}/chart/drawLine.d.ts +0 -0
  96. /package/dist/{src/components → components}/chart/drawPie.d.ts +0 -0
  97. /package/dist/{src/components → components}/dndStore.d.ts +0 -0
  98. /package/dist/{src/composables → composables}/useBackButton.d.ts +0 -0
  99. /package/dist/{src/composables → composables}/useBreakpoints.d.ts +0 -0
  100. /package/dist/{src/composables → composables}/useColorMode.d.ts +0 -0
  101. /package/dist/{src/composables → composables}/useForm.d.ts +0 -0
  102. /package/dist/{src/composables → composables}/useFormValidation.d.ts +0 -0
  103. /package/dist/{src/composables → composables}/useId.d.ts +0 -0
  104. /package/dist/{src/composables → composables}/usePosition.d.ts +0 -0
  105. /package/dist/{src/composables → composables}/useToast.d.ts +0 -0
  106. /package/dist/{src/directives → directives}/vTooltip.d.ts +0 -0
  107. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  108. /package/dist/{src/main.d.ts → main.d.ts} +0 -0
  109. /package/dist/{src/utils → utils}/safeCss.d.ts +0 -0
  110. /package/dist/{src/utils → utils}/safeHref.d.ts +0 -0
  111. /package/dist/{src/utils → utils}/sanitizeHtml.d.ts +0 -0
@@ -18,7 +18,7 @@ Provide either `text` or `content` (they are interchangeable). Content is always
18
18
 
19
19
  | Event | Payload | Description |
20
20
  |-------|---------|-------------|
21
- | `component-error` | `{ message, componentName, originalError }` | Emitted if Bootstrap JS is unavailable at mount |
21
+ | `component-error` | `ComponentError` | Emitted if Bootstrap JS is unavailable at mount |
22
22
 
23
23
  ## Slots
24
24
 
@@ -9,7 +9,7 @@ Automatically update navigation based on scroll position.
9
9
  | `target` | `String` | Required | CSS selector of navigation element to update |
10
10
  | `rootMargin` | `String` | `'0px 0px -25%'` | Intersection margin for triggering activation. Format: `'top right bottom left'` in CSS units |
11
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
+ | `method` | `String` | `'auto'` | Scroll detection method: `'auto'`, `'offset'`, or `'position'`. Note: `'offset'` is deprecated in Bootstrap 5.2+ (IntersectionObserver-based detection supersedes it); prefer `'auto'` or `'position'`. |
13
13
  | `smoothScroll` | `Boolean` | `false` | Enable smooth scrolling |
14
14
  | `tag` | `String` | `'div'` | HTML tag to render |
15
15
  | `height` | `String` | `'100%'` | CSS height of the scrollable container. Validated as a CSS length; invalid values fall back to `'100%'` |
@@ -19,7 +19,7 @@ Automatically update navigation based on scroll position.
19
19
  | Event | Payload | Description |
20
20
  |-------|---------|-------------|
21
21
  | `activate` | `event` | Emitted when a new nav item is activated |
22
- | `component-error` | `{ message, componentName, originalError }` | Emitted if Bootstrap JS is unavailable |
22
+ | `component-error` | `ComponentError` | Emitted if Bootstrap JS is unavailable |
23
23
 
24
24
  ## Slots
25
25
 
@@ -17,7 +17,7 @@ Content is always rendered as plain text — HTML is not supported (this is inte
17
17
 
18
18
  | Event | Payload | Description |
19
19
  |-------|---------|-------------|
20
- | `component-error` | `{ message, componentName, originalError }` | Emitted if Bootstrap JS is unavailable at mount |
20
+ | `component-error` | `ComponentError` | Emitted if Bootstrap JS is unavailable at mount |
21
21
 
22
22
  ## Slots
23
23
 
@@ -16,12 +16,30 @@ Button component with variants, sizes, and support for links and router-links.
16
16
  | `active` | `Boolean` | `false` | Apply active state styling |
17
17
  | `focusRing` | `Boolean` | `false` | Enable the Bootstrap 5.3 focus-ring helper |
18
18
 
19
+ ## Accessibility
20
+
21
+ **Disabled contrast (WCAG 1.4.3):** The disabled state overrides Bootstrap's default `opacity: 0.65` fade with full-opacity body tokens (`--bs-body-color` / `--bs-tertiary-bg`), keeping label contrast ≥ 4.5:1 in both light and dark mode.
22
+
23
+ **Icon-only buttons (WCAG 4.1.2):** A button whose slot contains only an icon (no visible text) must have an `aria-label` or `aria-labelledby` attribute so screen readers can announce its purpose. In development mode, VibeButton logs a `console.warn` when this is missing.
24
+
25
+ ```vue
26
+ <!-- correct -->
27
+ <VibeButton aria-label="Delete item">
28
+ <i class="bi bi-trash" aria-hidden="true" />
29
+ </VibeButton>
30
+
31
+ <!-- incorrect — no accessible name -->
32
+ <VibeButton>
33
+ <i class="bi bi-trash" />
34
+ </VibeButton>
35
+ ```
36
+
19
37
  ## Events
20
38
 
21
39
  | Event | Payload | Description |
22
40
  |-------|---------|-------------|
23
41
  | `click` | `MouseEvent` | Emitted when button is clicked (unless disabled) |
24
- | `component-error` | `Object` | Emitted when an error occurs |
42
+ | `component-error` | `ComponentError` | Emitted when an error occurs |
25
43
 
26
44
  ## Slots
27
45
 
@@ -15,7 +15,7 @@ Generic close button for dismissing content like modals and alerts.
15
15
  | Event | Payload | Description |
16
16
  |-------|---------|-------------|
17
17
  | `click` | `MouseEvent` | Emitted when button is clicked (unless disabled) |
18
- | `component-error` | `Object` | Emitted when an error occurs |
18
+ | `component-error` | `ComponentError` | Emitted when an error occurs |
19
19
 
20
20
  ## Usage
21
21
 
@@ -20,7 +20,7 @@ Powerful data table component with search, sorting, and pagination - similar to
20
20
  | Prop | Type | Default | Description |
21
21
  |------|------|---------|-------------|
22
22
  | `items` | `T[]` | `[]` | Array of data objects to display |
23
- | `columns` | `DataTableColumn<T>[]` | Required | Column definitions |
23
+ | `columns` | `DataTableColumn<T>[]` | `[]` | Column definitions. Defaults to an empty array, so an unset/loading state renders an empty table rather than erroring. |
24
24
  | `rowKey` | `String` | `'id'` | Property name used as the unique key for each row. **Recommended** — set it to a unique field in your data (e.g. `'id'`, `'uuid'`) so Vue tracks rows correctly across sorting, filtering, and pagination. Falls back to a positional key (with a DEV warning) when missing. |
25
25
 
26
26
  > **Typing tip**: `DataTableColumn` is generic over your row type. For full slot-prop / formatter typing, annotate the column array:
@@ -56,6 +56,7 @@ Powerful data table component with search, sorting, and pagination - similar to
56
56
  | `searchable` | `Boolean` | `true` | Enable search functionality |
57
57
  | `sortable` | `Boolean` | `true` | Enable column sorting |
58
58
  | `paginated` | `Boolean` | `true` | Enable pagination |
59
+ | `clickable` | `Boolean` | `false` | Show a pointer cursor on rows to signal they are interactive (pair with a `@row-clicked` listener) |
59
60
 
60
61
  ### Search Props
61
62
 
@@ -119,6 +120,7 @@ interface DataTableColumn {
119
120
  | `update:sortBy` | `String` | Emitted when sort column changes |
120
121
  | `update:sortDesc` | `Boolean` | Emitted when sort direction changes |
121
122
  | `row-clicked` | `(item, globalIndex)` | Emitted when a row is clicked. `globalIndex` is the index within the full filtered/sorted dataset, not the current page. Only emitted when a `@row-clicked` listener is attached (rows show a pointer cursor in that case). |
123
+ | `component-error` | `ComponentError` | Emitted if an internal error occurs |
122
124
 
123
125
  ## Slots
124
126
 
@@ -22,6 +22,7 @@ A hero / banner section. Renders a `<section>` with consistent padding, an inner
22
22
  | `minHeight` | `String` | `undefined` | Sanitized via `safeLength`; when set, content is vertically centered |
23
23
  | `bgImage` | `String` | `undefined` | Full-bleed background image URL; sanitized via `safeHref` (blocks `javascript:`/`data:`), rendered `cover`/`center` |
24
24
  | `overlay` | `Boolean \| String` | `false` | Darkening layer over `bgImage` for legibility — `true` for a default dark scrim, or a CSS color (sanitized via `safeColor`) |
25
+ | `overlayOpacity` | `Number` | `0.5` | Opacity of the default dark scrim when `overlay` is `true`; clamped to `[0, 1]`. Ignored when `overlay` is a CSS color string |
25
26
  | `gradient` | `String` | `undefined` | CSS gradient background (e.g. `linear-gradient(135deg, #667eea, #764ba2)`); validated to `*-gradient(...)` only — no `url()`/`expression()` |
26
27
  | `tag` | `Tag` | `'section'` | Root element tag |
27
28
 
@@ -17,6 +17,8 @@ Modal dialogs for lightboxes, user notifications, or custom content.
17
17
  | `hideHeader` | `Boolean` | `false` | Hide header section |
18
18
  | `hideFooter` | `Boolean` | `false` | Hide footer section |
19
19
  | `teleport` | `Boolean\|String` | `'body'` | Destination for Vue Teleport |
20
+ | `autoFocus` | `Boolean` | `true` | Focus the first focusable descendant when the modal opens (WCAG 2.4.3). |
21
+ | `submitOnMetaEnter` | `Boolean` | `true` | Submit the first `<form>` inside the modal on `Cmd+Enter` / `Ctrl+Enter` (WCAG 2.1.1). |
20
22
 
21
23
  ## Events
22
24
 
@@ -116,8 +118,14 @@ function save() {
116
118
 
117
119
  **Teleportation:** By default, this component teleports its DOM elements to the `<body>` to avoid stacking context issues. You can customize this with the `teleport` prop.
118
120
 
121
+ **Auto-focus (WCAG 2.4.3):** On open, focus moves to the first focusable descendant (input, select, button, etc.). Repeated opens re-focus. Disable with `:auto-focus="false"` when you need to control focus yourself.
122
+
123
+ **Focus trap (WCAG 2.1.2):** While the modal is open, `Tab` and `Shift+Tab` cycle within the modal only. All sibling elements receive the `inert` attribute so screen readers cannot reach the page behind the modal. `inert` is cleared on close and on unmount.
124
+
119
125
  **Focus Return (WCAG 2.4.3):** When the modal closes, focus is automatically returned to the element that opened it, including for programmatically opened modals.
120
126
 
127
+ **Cmd/Ctrl+Enter submit (WCAG 2.1.1):** Pressing `Cmd+Enter` (macOS) or `Ctrl+Enter` (Windows/Linux) inside the modal submits the first `<form>` descendant via `requestSubmit()`. Disable with `:submit-on-meta-enter="false"` for non-form modals.
128
+
121
129
  **Escape Hatch:** The exposed `_unsafe_bsInstance` template ref gives raw access to the underlying Bootstrap Modal instance. It is **not** part of the stable API — calling `dispose()` or other lifecycle methods on it directly **will** break the component. Prefer `v-model` and the exposed `show()` / `hide()` methods.
122
130
 
123
131
  ## Mobile Optimization
@@ -39,6 +39,12 @@ Mobile collapse toggle button.
39
39
  | `target` | `String` | Required | The `id` of the `VibeCollapse` to toggle |
40
40
  | `ariaLabel` | `String` | `'Toggle navigation'` | Accessible label for the button |
41
41
 
42
+ #### Events
43
+
44
+ | Event | Payload | Description |
45
+ |-------|---------|-------------|
46
+ | `component-error` | `ComponentError` | Emitted if the Bootstrap collapse toggle fails (e.g. Bootstrap JS unavailable) |
47
+
42
48
  ### VibeNavbarNav
43
49
  Navigation links container. Supports regular links and dropdown items.
44
50
 
@@ -21,6 +21,7 @@ Data-driven pagination component with v-model support.
21
21
  |-------|---------|-------------|
22
22
  | `update:currentPage` | `page: number` | Emitted when page changes (v-model) |
23
23
  | `page-click` | `page: number` | Emitted when a page is clicked |
24
+ | `component-error` | `ComponentError` | Emitted if an internal error occurs |
24
25
 
25
26
  ## Slots
26
27
 
@@ -0,0 +1,96 @@
1
+ # VibeFormErrorSummary
2
+
3
+ Top-of-form error summary that lists all field errors in one place with links to the offending fields. Satisfies WCAG 3.3.1 (Error Identification) for forms with multiple validation errors.
4
+
5
+ ## Props
6
+
7
+ | Prop | Type | Required | Description |
8
+ |------|------|----------|-------------|
9
+ | `errors` | `Record<string, string>` | yes | Map of field name → error message. Entries with an empty string are ignored. |
10
+
11
+ ## Events
12
+
13
+ | Event | Payload | Description |
14
+ |-------|---------|-------------|
15
+ | `focus` | `string` | Emitted when the user clicks an error link. Payload is the field key. Use it to programmatically focus the field. |
16
+
17
+ ## Slots
18
+
19
+ None.
20
+
21
+ ## Usage
22
+
23
+ ### Basic
24
+
25
+ ```vue
26
+ <script setup lang="ts">
27
+ import { reactive } from 'vue'
28
+
29
+ const errors = reactive<Record<string, string>>({
30
+ email: '',
31
+ password: '',
32
+ })
33
+
34
+ function handleSubmit() {
35
+ errors.email = ''
36
+ errors.password = ''
37
+ if (!form.email) errors.email = 'Email is required.'
38
+ if (!form.password) errors.password = 'Password must be at least 8 characters.'
39
+ }
40
+
41
+ function focusField(key: string) {
42
+ document.getElementById(`field-${key}`)?.focus()
43
+ }
44
+ </script>
45
+
46
+ <template>
47
+ <form @submit.prevent="handleSubmit">
48
+ <VibeFormErrorSummary :errors="errors" @focus="focusField" />
49
+
50
+ <VibeFormGroup label="Email" required :validation-state="errors.email ? 'invalid' : null" :validation-message="errors.email">
51
+ <VibeFormInput id="field-email" v-model="form.email" type="email" autocomplete="email" />
52
+ </VibeFormGroup>
53
+
54
+ <VibeFormGroup label="Password" required :validation-state="errors.password ? 'invalid' : null" :validation-message="errors.password">
55
+ <VibeFormInput id="field-password" v-model="form.password" type="password" autocomplete="current-password" />
56
+ </VibeFormGroup>
57
+
58
+ <VibeButton type="submit" variant="primary">Sign in</VibeButton>
59
+ </form>
60
+ </template>
61
+ ```
62
+
63
+ ### With useForm
64
+
65
+ ```vue
66
+ <script setup lang="ts">
67
+ import { useForm, validators } from '@velkymx/vibeui'
68
+
69
+ const { fields, errors, handleSubmit } = useForm({
70
+ email: { value: '', rules: [validators.required(), validators.email()] },
71
+ password: { value: '', rules: [validators.required(), validators.minLength(8)] },
72
+ })
73
+
74
+ function focusField(key: string) {
75
+ document.getElementById(`field-${key}`)?.focus()
76
+ }
77
+ </script>
78
+
79
+ <template>
80
+ <form @submit.prevent="handleSubmit">
81
+ <VibeFormErrorSummary :errors="errors" @focus="focusField" />
82
+ <!-- fields … -->
83
+ </form>
84
+ </template>
85
+ ```
86
+
87
+ ## Important Notes
88
+
89
+ - **Anchor convention:** error links use `href="#field-{key}"`. Set `:id="\`field-${key}\`"` on each input so the links resolve (and so `focusField` can look them up). The component itself does not set these ids — you control the field ids.
90
+ - **`role="alert"` (WCAG 4.1.3):** the container carries `role="alert" aria-live="polite"` so screen readers announce the error list automatically when errors appear, without requiring focus.
91
+ - **Auto-hides when clean:** when all `errors` values are empty strings the summary renders nothing. No need to conditionally mount it.
92
+ - **`@focus` vs native anchor:** the `@click.prevent` on each link suppresses the default scroll-to-anchor jump and lets you implement the focus strategy (e.g. scroll + focus, or open a collapsed section first).
93
+
94
+ ## Bootstrap CSS Classes
95
+
96
+ - `.alert`, `.alert-danger`
@@ -12,7 +12,7 @@ Wrapper that owns a form control's label, help text, validation feedback, and id
12
12
  |------|------|---------|-------------|
13
13
  | `label` | `string` | `undefined` | Label text rendered for the first child control. |
14
14
  | `labelFor` | `string` | `undefined` | Explicit id to use instead of the auto-generated one. |
15
- | `required` | `boolean` | `false` | Append a `*` to the label. |
15
+ | `required` | `boolean` | `false` | `true` appends a red `*` (aria-hidden) and a visually-hidden "required" span to the label. `false` (default) appends a gray "(optional)" label (aria-hidden). Omit on the child control — the group renders the indicator. |
16
16
  | `validationState` | `'valid' \| 'invalid' \| null` | `null` | Render valid/invalid feedback. |
17
17
  | `validationMessage` | `string` | `undefined` | Feedback message text. |
18
18
  | `helpText` | `string` | `undefined` | Help text rendered below the control. |
@@ -105,6 +105,9 @@ const city = ref('')
105
105
  - **Single-consumer rule:** only the first child control consumes the group id. With multiple controls (e.g. a checkbox or radio group), subsequent controls generate their own ids — the group's label still describes the set.
106
106
  - **Don't double up:** when the group provides `label`, `helpText`, or validation, omit those same props on the child control; the child suppresses its own copies to avoid duplicates.
107
107
  - **Floating labels** require the label to render after the control, which the group handles automatically; pair it with a `placeholder` on the input.
108
+ - **Required/optional indicator (WCAG 3.3.2):** the `required` prop renders both a visual indicator (`*` / "(optional)") and a visually-hidden text node for screen readers. The visual spans carry `aria-hidden="true"` so the screen-reader announcement comes from the hidden span, not the symbol.
109
+ - **`aria-describedby` (WCAG 1.3.1):** `helpText` and `validationMessage` elements are assigned stable ids; the child control's `aria-describedby` is wired automatically — no manual id plumbing needed.
110
+ - **Live error region (WCAG 4.1.3):** the invalid-feedback div carries `role="alert"` so new errors are announced by screen readers without requiring the user to re-focus the field.
108
111
 
109
112
  ## Bootstrap CSS Classes
110
113
 
@@ -23,6 +23,10 @@ Single-line text input supporting all standard HTML input types, with built-in v
23
23
  | `plaintext` | `boolean` | `false` | Render as read-only plain text (`.form-control-plaintext`). |
24
24
  | `noWrapper` | `boolean` | `false` | Render only the `<input>` (no wrapper/label/feedback). Use inside `VibeInputGroup`. |
25
25
  | `focusRing` | `boolean` | `false` | Add the Bootstrap 5.3 `.focus-ring` helper. |
26
+ | `showToggle` | `boolean` | `false` | Show a show/hide password toggle button. Only applies when `type="password"`. |
27
+ | `showPasswordStrength` | `boolean` | `false` | Show a 4-segment strength meter and `aria-live` strength label below the input. Only applies when `type="password"`. |
28
+ | `autocomplete` | `AutocompleteType` | auto-detected | HTML `autocomplete` attribute. Auto-detected: `type="email"` → `"email"`. Set `"off"` to disable. |
29
+ | `inputmode` | `InputMode` | auto-detected | Mobile keyboard hint. Auto-detected: `number` → `decimal`, `email` → `email`, `tel` → `tel`, `url` → `url`, `search` → `search`. Override explicitly when needed. |
26
30
 
27
31
  ## Events
28
32
 
@@ -97,11 +101,35 @@ const search = ref('')
97
101
  </template>
98
102
  ```
99
103
 
104
+ ### Password field with toggle and strength meter
105
+
106
+ ```vue
107
+ <script setup lang="ts">
108
+ import { ref } from 'vue'
109
+ const password = ref('')
110
+ </script>
111
+
112
+ <template>
113
+ <VibeFormGroup label="Password" required>
114
+ <VibeFormInput
115
+ v-model="password"
116
+ type="password"
117
+ autocomplete="new-password"
118
+ show-toggle
119
+ show-password-strength
120
+ />
121
+ </VibeFormGroup>
122
+ </template>
123
+ ```
124
+
100
125
  ## Important Notes
101
126
 
102
127
  - **Automatic ID & label linking:** when wrapped in a `VibeFormGroup`, the first child control consumes the group id, so the group's `<label>`, help text, and feedback are linked automatically. Do not also set `label`/`helpText` on the input in that case — the group renders them.
103
128
  - **Number coercion:** with `type="number"`, an empty field emits `''`; otherwise the emitted value is a `number`.
104
129
  - **Plain text:** `plaintext` forces the control read-only.
130
+ - **Placeholder contrast (WCAG 1.4.3):** placeholder text is rendered at full opacity using `--bs-secondary-color`, overriding the browser-default ~0.6 alpha that drops contrast below 3:1.
131
+ - **`autocomplete` auto-detection:** the component sets `autocomplete="email"` automatically for `type="email"` inputs. For password fields, set `autocomplete="current-password"` (login) or `autocomplete="new-password"` (registration) explicitly — the component cannot infer intent from type alone.
132
+ - **`inputmode` auto-detection:** overrides only apply when the prop is not set. Pass an explicit `inputmode` to override (e.g. `inputmode="numeric"` for a 4-digit PIN field that uses `type="text"`).
105
133
 
106
134
  ## Bootstrap CSS Classes
107
135
 
package/llms.txt CHANGED
@@ -402,17 +402,36 @@ Components like `VibeBreadcrumb`, `VibeListGroup`, `VibeDropdown`, `VibeAccordio
402
402
  ]" />
403
403
  ```
404
404
 
405
- ### VibeFormGroup — automatic label linking
405
+ ### VibeFormGroup — automatic label linking and accessibility
406
406
 
407
- `VibeFormGroup` uses Vue's `provide`/`inject` to share a generated ID with child inputs. This means `id` and `label-for` are optionalthe group handles accessibility automatically:
407
+ `VibeFormGroup` uses Vue's `provide`/`inject` to share a generated ID with child inputs. `id`, `for`, and `aria-describedby` are all wired automatically no manual ids needed:
408
408
 
409
409
  ```vue
410
- <VibeFormGroup label="Email Address">
410
+ <!-- required prop renders red * + visually-hidden "required" for screen readers -->
411
+ <!-- invalid-feedback has role="alert" — errors announced without focus -->
412
+ <VibeFormGroup label="Email Address" required :validation-state="err ? 'invalid' : null" :validation-message="err">
411
413
  <VibeFormInput v-model="email" type="email" />
412
- <!-- label is automatically linked to input, no id needed -->
413
414
  </VibeFormGroup>
414
415
  ```
415
416
 
417
+ For multi-field forms, place `<VibeFormErrorSummary :errors="errors" @focus="focusField" />` at the top of the form. It renders a `role="alert"` block listing all non-empty errors with focus links (WCAG 3.3.1).
418
+
419
+ ### VibeFormInput — password, autocomplete, and mobile keyboard
420
+
421
+ ```vue
422
+ <!-- show-toggle: eye button to reveal password -->
423
+ <!-- show-password-strength: 4-segment strength bar with aria-live -->
424
+ <!-- autocomplete: typed enum — "current-password", "new-password", "email", etc. -->
425
+ <!-- inputmode: auto-detected (number→decimal, email→email, tel→tel, url→url, search→search) -->
426
+ <VibeFormInput
427
+ v-model="password"
428
+ type="password"
429
+ autocomplete="new-password"
430
+ show-toggle
431
+ show-password-strength
432
+ />
433
+ ```
434
+
416
435
  ### Programmatic control (template ref)
417
436
 
418
437
  Visibility is normally driven by `v-model` — prefer that. When you must trigger an action imperatively (e.g. open a modal from a non-template handler), interactive components expose **safe methods** via a template ref. Use these, not the underlying Bootstrap object:
@@ -773,7 +792,7 @@ Per-component and per-API reference. **Read the relevant page before building wi
773
792
 
774
793
  **Progress:** [VibeProgress](docs/components/progress/progress.md)
775
794
 
776
- **Forms:** [overview](docs/forms/README.md) · [VibeAutocomplete](docs/forms/autocomplete.md) · [VibeFileInput](docs/forms/file-input.md) · [VibeFormCheckbox](docs/forms/form-checkbox.md) · [VibeFormDatepicker](docs/forms/form-datepicker.md) · [VibeFormGroup](docs/forms/form-group.md) · [VibeFormInput](docs/forms/form-input.md) · [VibeFormRadio](docs/forms/form-radio.md) · [VibeFormSelect](docs/forms/form-select.md) · [VibeFormSpinbutton](docs/forms/form-spinbutton.md) · [VibeFormSwitch](docs/forms/form-switch.md) · [VibeFormTextarea](docs/forms/form-textarea.md) · [VibeFormWysiwyg](docs/forms/form-wysiwyg.md) · [VibeInputGroup](docs/forms/input-group.md) · [Validation & useForm](docs/forms/validation.md)
795
+ **Forms:** [overview](docs/forms/README.md) · [VibeAutocomplete](docs/forms/autocomplete.md) · [VibeFileInput](docs/forms/file-input.md) · [VibeFormCheckbox](docs/forms/form-checkbox.md) · [VibeFormDatepicker](docs/forms/form-datepicker.md) · [VibeFormErrorSummary](docs/forms/form-error-summary.md) · [VibeFormGroup](docs/forms/form-group.md) · [VibeFormInput](docs/forms/form-input.md) · [VibeFormRadio](docs/forms/form-radio.md) · [VibeFormSelect](docs/forms/form-select.md) · [VibeFormSpinbutton](docs/forms/form-spinbutton.md) · [VibeFormSwitch](docs/forms/form-switch.md) · [VibeFormTextarea](docs/forms/form-textarea.md) · [VibeFormWysiwyg](docs/forms/form-wysiwyg.md) · [VibeInputGroup](docs/forms/input-group.md) · [Validation & useForm](docs/forms/validation.md)
777
796
 
778
797
  **Composables:** [useBackButton](docs/composables/back-button.md) · [useBreakpoints](docs/composables/breakpoints.md) · [useColorMode](docs/composables/color-mode.md) · [useForm](docs/composables/use-form.md) · [usePosition](docs/composables/use-position.md) · [useToast](docs/composables/use-toast.md)
779
798
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velkymx/vibeui",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "A lightweight Vue 3 component library for Bootstrap 5.3 with dual-mode support (shorthand props and composable slots)",
5
5
  "main": "./dist/vibeui.umd.js",
6
6
  "module": "./dist/vibeui.es.js",