@surgeui/ds-vue 2.3.0 → 3.0.1

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 (143) hide show
  1. package/README.md +172 -277
  2. package/dist/components/atoms/Avatar.vue.d.ts +0 -1
  3. package/dist/components/atoms/Avatar.vue.d.ts.map +1 -1
  4. package/dist/components/atoms/Badge.vue.d.ts.map +1 -1
  5. package/dist/components/atoms/Button.vue.d.ts +1 -2
  6. package/dist/components/atoms/Button.vue.d.ts.map +1 -1
  7. package/dist/components/atoms/CheckboxGroup.vue.d.ts +2 -2
  8. package/dist/components/atoms/CheckboxGroup.vue.d.ts.map +1 -1
  9. package/dist/components/atoms/FileUpload.vue.d.ts +4 -9
  10. package/dist/components/atoms/FileUpload.vue.d.ts.map +1 -1
  11. package/dist/components/atoms/FormField.vue.d.ts +4 -3
  12. package/dist/components/atoms/FormField.vue.d.ts.map +1 -1
  13. package/dist/components/atoms/Heading.vue.d.ts.map +1 -1
  14. package/dist/components/atoms/Image.vue.d.ts +0 -3
  15. package/dist/components/atoms/Image.vue.d.ts.map +1 -1
  16. package/dist/components/atoms/Input.vue.d.ts +3 -3
  17. package/dist/components/atoms/Input.vue.d.ts.map +1 -1
  18. package/dist/components/atoms/Link.vue.d.ts.map +1 -1
  19. package/dist/components/atoms/Panel.vue.d.ts.map +1 -1
  20. package/dist/components/atoms/Price.vue.d.ts.map +1 -1
  21. package/dist/components/atoms/Progress.vue.d.ts +1 -4
  22. package/dist/components/atoms/Progress.vue.d.ts.map +1 -1
  23. package/dist/components/atoms/RadioGroup.vue.d.ts +2 -2
  24. package/dist/components/atoms/RadioGroup.vue.d.ts.map +1 -1
  25. package/dist/components/atoms/SelectBox.vue.d.ts +5 -9
  26. package/dist/components/atoms/SelectBox.vue.d.ts.map +1 -1
  27. package/dist/components/atoms/Slider.vue.d.ts +2 -2
  28. package/dist/components/atoms/Slider.vue.d.ts.map +1 -1
  29. package/dist/components/atoms/Spinner.vue.d.ts +1 -2
  30. package/dist/components/atoms/Spinner.vue.d.ts.map +1 -1
  31. package/dist/components/atoms/Switch.vue.d.ts +5 -4
  32. package/dist/components/atoms/Switch.vue.d.ts.map +1 -1
  33. package/dist/components/atoms/Textarea.vue.d.ts +2 -2
  34. package/dist/components/atoms/Textarea.vue.d.ts.map +1 -1
  35. package/dist/components/atoms/Toggle.vue.d.ts +2 -3
  36. package/dist/components/atoms/Toggle.vue.d.ts.map +1 -1
  37. package/dist/components/molecules/AccordionItem.vue.d.ts +2 -6
  38. package/dist/components/molecules/AccordionItem.vue.d.ts.map +1 -1
  39. package/dist/components/molecules/AvatarGroup.vue.d.ts.map +1 -1
  40. package/dist/components/molecules/CheckboxGroupField.vue.d.ts +3 -3
  41. package/dist/components/molecules/CheckboxGroupField.vue.d.ts.map +1 -1
  42. package/dist/components/molecules/Dropdown.vue.d.ts +2 -3
  43. package/dist/components/molecules/Dropdown.vue.d.ts.map +1 -1
  44. package/dist/components/molecules/FileUploadField.vue.d.ts +5 -10
  45. package/dist/components/molecules/FileUploadField.vue.d.ts.map +1 -1
  46. package/dist/components/molecules/FloatButton.vue.d.ts +10 -10
  47. package/dist/components/molecules/FloatButton.vue.d.ts.map +1 -1
  48. package/dist/components/molecules/InputField.vue.d.ts +12 -12
  49. package/dist/components/molecules/InputField.vue.d.ts.map +1 -1
  50. package/dist/components/molecules/LinkGroup.vue.d.ts.map +1 -1
  51. package/dist/components/molecules/OtpInput.vue.d.ts +2 -2
  52. package/dist/components/molecules/OtpInput.vue.d.ts.map +1 -1
  53. package/dist/components/molecules/OtpInputField.vue.d.ts +8 -8
  54. package/dist/components/molecules/OtpInputField.vue.d.ts.map +1 -1
  55. package/dist/components/molecules/Password.vue.d.ts +2 -2
  56. package/dist/components/molecules/Password.vue.d.ts.map +1 -1
  57. package/dist/components/molecules/PasswordField.vue.d.ts +4 -4
  58. package/dist/components/molecules/PasswordField.vue.d.ts.map +1 -1
  59. package/dist/components/molecules/RadioGroupField.vue.d.ts +3 -3
  60. package/dist/components/molecules/RadioGroupField.vue.d.ts.map +1 -1
  61. package/dist/components/molecules/SelectBoxField.vue.d.ts +14 -26
  62. package/dist/components/molecules/SelectBoxField.vue.d.ts.map +1 -1
  63. package/dist/components/molecules/SliderField.vue.d.ts +4 -4
  64. package/dist/components/molecules/SliderField.vue.d.ts.map +1 -1
  65. package/dist/components/molecules/SwitchField.vue.d.ts +3 -3
  66. package/dist/components/molecules/SwitchField.vue.d.ts.map +1 -1
  67. package/dist/components/molecules/TextareaField.vue.d.ts +6 -6
  68. package/dist/components/molecules/TextareaField.vue.d.ts.map +1 -1
  69. package/dist/components/molecules/ToggleGroup.vue.d.ts +2 -2
  70. package/dist/components/organisms/Accordion.vue.d.ts +1 -0
  71. package/dist/components/organisms/Accordion.vue.d.ts.map +1 -1
  72. package/dist/components/organisms/Alert.vue.d.ts +2 -3
  73. package/dist/components/organisms/Alert.vue.d.ts.map +1 -1
  74. package/dist/components/organisms/Card.vue.d.ts +0 -1
  75. package/dist/components/organisms/Card.vue.d.ts.map +1 -1
  76. package/dist/components/organisms/Dialog.vue.d.ts +3 -3
  77. package/dist/components/organisms/Dialog.vue.d.ts.map +1 -1
  78. package/dist/components/organisms/Tabs.vue.d.ts +6 -2
  79. package/dist/components/organisms/Tabs.vue.d.ts.map +1 -1
  80. package/dist/components/templates/FullScreenLayout.vue.d.ts +27 -0
  81. package/dist/components/templates/FullScreenLayout.vue.d.ts.map +1 -0
  82. package/dist/components/templates/GridLayout.vue.d.ts +46 -0
  83. package/dist/components/templates/GridLayout.vue.d.ts.map +1 -0
  84. package/dist/components/templates/HeroLayout.vue.d.ts +49 -0
  85. package/dist/components/templates/HeroLayout.vue.d.ts.map +1 -0
  86. package/dist/components/templates/HolyGrailLayout.vue.d.ts +51 -0
  87. package/dist/components/templates/HolyGrailLayout.vue.d.ts.map +1 -0
  88. package/dist/components/templates/MagazineLayout.vue.d.ts +38 -0
  89. package/dist/components/templates/MagazineLayout.vue.d.ts.map +1 -0
  90. package/dist/components/templates/MasonryLayout.vue.d.ts +27 -0
  91. package/dist/components/templates/MasonryLayout.vue.d.ts.map +1 -0
  92. package/dist/components/templates/SidebarLayout.vue.d.ts +36 -0
  93. package/dist/components/templates/SidebarLayout.vue.d.ts.map +1 -0
  94. package/dist/components/templates/SingleColumnLayout.vue.d.ts +36 -0
  95. package/dist/components/templates/SingleColumnLayout.vue.d.ts.map +1 -0
  96. package/dist/components/templates/SplitLayout.vue.d.ts +34 -0
  97. package/dist/components/templates/SplitLayout.vue.d.ts.map +1 -0
  98. package/dist/components/templates/_stories/layoutDemo.d.ts +43 -0
  99. package/dist/components/templates/_stories/layoutDemo.d.ts.map +1 -0
  100. package/dist/composables/index.d.ts +16 -1
  101. package/dist/composables/index.d.ts.map +1 -1
  102. package/dist/composables/useAnnouncer.d.ts +32 -0
  103. package/dist/composables/useAnnouncer.d.ts.map +1 -0
  104. package/dist/composables/useDismiss.d.ts +32 -0
  105. package/dist/composables/useDismiss.d.ts.map +1 -0
  106. package/dist/composables/useFloatingPosition.d.ts +32 -0
  107. package/dist/composables/useFloatingPosition.d.ts.map +1 -0
  108. package/dist/composables/useFocusTrap.d.ts +42 -0
  109. package/dist/composables/useFocusTrap.d.ts.map +1 -0
  110. package/dist/composables/useFormField.d.ts +76 -0
  111. package/dist/composables/useFormField.d.ts.map +1 -0
  112. package/dist/composables/useListNavigation.d.ts +40 -0
  113. package/dist/composables/useListNavigation.d.ts.map +1 -0
  114. package/dist/composables/useLocale.d.ts +33 -0
  115. package/dist/composables/useLocale.d.ts.map +1 -0
  116. package/dist/composables/useTheme.d.ts +2 -2
  117. package/dist/composables/useTypeahead.d.ts +17 -0
  118. package/dist/composables/useTypeahead.d.ts.map +1 -0
  119. package/dist/composables/useUniqueId.d.ts +4 -0
  120. package/dist/composables/useUniqueId.d.ts.map +1 -1
  121. package/dist/{index-BAchYb_D.js → index-ShUpX8pH.js} +1 -1
  122. package/dist/index-ksHUqbRL.js +9062 -0
  123. package/dist/index.d.ts +18 -1
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.es.js +84 -52
  126. package/dist/index.umd.js +1 -1
  127. package/dist/locales/en.d.ts +4 -0
  128. package/dist/locales/en.d.ts.map +1 -0
  129. package/dist/locales/fr.d.ts +4 -0
  130. package/dist/locales/fr.d.ts.map +1 -0
  131. package/dist/locales/index.d.ts +10 -0
  132. package/dist/locales/index.d.ts.map +1 -0
  133. package/dist/locales/types.d.ts +155 -0
  134. package/dist/locales/types.d.ts.map +1 -0
  135. package/dist/style.css +1 -1
  136. package/dist/types/index.d.ts +140 -15
  137. package/dist/types/index.d.ts.map +1 -1
  138. package/dist/utils/accessibility.d.ts +4 -3
  139. package/dist/utils/accessibility.d.ts.map +1 -1
  140. package/dist/utils/layout.d.ts +9 -0
  141. package/dist/utils/layout.d.ts.map +1 -0
  142. package/package.json +83 -74
  143. package/dist/index-B8fo_rCP.js +0 -6835
package/README.md CHANGED
@@ -1,373 +1,268 @@
1
1
  # SurgeUI Vue Design System
2
2
 
3
- A modern **Vue.js 3** component library with Composition API, designed to create beautiful and consistent user interfaces.
3
+ An accessible **Vue 3** component library: components, layouts and composables built with the Composition API and TypeScript, themable through CSS custom properties.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@surgeui/ds-vue)](https://www.npmjs.com/package/@surgeui/ds-vue)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![Vue 3](https://img.shields.io/badge/Vue-3.x-4FC08D.svg)](https://vuejs.org/)
7
+ [![Vue 3](https://img.shields.io/badge/Vue-3.5+-4FC08D.svg)](https://vuejs.org/)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-3178C6.svg)](https://www.typescriptlang.org/)
9
9
 
10
-
11
10
  ## ✨ Features
12
11
 
13
- - 🎨 **Modern Design** - Polished and professional components
14
- - 🎯 **Composition API** - Built with Vue 3 Composition API
15
- - 🔧 **TypeScript** - Full TypeScript support for better developer experience
16
- - 🎨 **Customizable** - Global configuration and theming support
17
- - 📱 **Responsive** - Components adapted to all screen sizes
18
- - ♿ **Accessible** - WCAG 2.1 AA compliant with complete accessibility support
19
- - 🌙 **Dark Mode** - Native dark mode support
20
- - ⚡ **Performance** - Optimized for best performance
21
-
12
+ - 🧩 **50+ components** - Display, actions, forms and 9 mobile-first layouts
13
+ - ♿ **Accessible** - WAI-ARIA patterns (combobox, menu button, tabs, accordion, dialog), full keyboard support, screen reader announcements
14
+ - 🎨 **Themable** - 4 built-in themes × light / dark mode, CSS custom properties (`--su-*`)
15
+ - 🌍 **Translatable** - Default texts in French and English, customizable messages
16
+ - 🔧 **TypeScript** - Typed props, emits and composables
17
+ - 📱 **Responsive** - Mobile first; layouts adapt to their **container** width (container queries)
18
+ - 🔄 **RTL & LTR** - Right-to-left support, including keyboard navigation
22
19
 
23
20
  ## Quick Start
24
21
 
22
+ ### Requirements
23
+
24
+ - Vue **3.5+**
25
+
25
26
  ### Installation
26
27
 
27
28
  ```bash
28
29
  npm install @surgeui/ds-vue
29
30
  ```
30
31
 
31
- ### Basic Usage
32
-
33
- ```vue
34
- <script setup lang="ts" >
35
- import '@surgeui/ds-vue/style.css'
36
- import { SuButton } from '@surgeui/ds-vue'
37
- import type { ButtonProps } from '@surgeui/ds-vue/types'
38
- </script>
39
-
40
- <template>
41
- <SuButton variant="primary">
42
- My first button
43
- </SuButton>
44
- </template>
45
- ```
32
+ ### Global installation (recommended)
46
33
 
47
- ### Global Installation
34
+ Registers every component with the `Su` prefix (`SuButton`, `SuInputField`, …):
48
35
 
49
36
  ```ts
50
- // main.js
37
+ // main.ts
51
38
  import { createApp } from 'vue'
52
- import SurgeUpDS from '@surgeui/ds-vue'
39
+ import SurgeUI from '@surgeui/ds-vue'
53
40
  import '@surgeui/ds-vue/style.css'
54
41
  import App from './App.vue'
55
42
 
56
- const app = createApp(App)
57
- app.use(SurgeUpDS)
58
- app.mount('#app')
43
+ createApp(App)
44
+ .use(SurgeUI)
45
+ .mount('#app')
59
46
  ```
60
47
 
61
- ## 🎨 Customization & Theming
48
+ ```vue
49
+ <template>
50
+ <SuButton variant="primary">My first button</SuButton>
51
+ </template>
52
+ ```
62
53
 
63
- SurgeUI provides a **declarative, CSS-based theming system** that allows you to customize colors and styles by defining custom CSS variables.
54
+ ### Selective import
64
55
 
65
- ### How It Works
56
+ Components are exported **without** the prefix:
66
57
 
67
- The customization system automatically converts theme properties into CSS variables with the `--su-custom-` prefix. These variables can override the default theme tokens when defined.
58
+ ```vue
59
+ <script setup lang="ts">
60
+ import '@surgeui/ds-vue/style.css'
61
+ import { Button as SuButton, InputField as SuInputField } from '@surgeui/ds-vue'
62
+ import type { ButtonProps } from '@surgeui/ds-vue'
63
+ </script>
68
64
 
69
- ### Global Configuration
65
+ <template>
66
+ <SuInputField label="Email" type="email" />
67
+ <SuButton variant="primary">Subscribe</SuButton>
68
+ </template>
69
+ ```
70
70
 
71
- Configure the design system when installing:
71
+ ### Plugin options
72
72
 
73
73
  ```ts
74
- import { createApp } from 'vue'
75
- import SurgeUpDS from '@surgeui/ds-vue'
76
- import '@surgeui/ds-vue/style.css'
77
- import App from './App.vue'
78
-
79
- const app = createApp(App)
80
-
81
- const themeConfig = {
82
- // Component prefix (default: 'Su')
83
- prefix: 'My', // Components become MyButton, MyInput, etc.
74
+ import SurgeUI, { locales } from '@surgeui/ds-vue'
84
75
 
85
- // Theme customization via CSS variables
86
- theme: {
87
- // Text colors
88
- textPrimary: '#1f2937',
89
- textSecondary: '#6b7280',
90
- textTertiary: '#9ca3af',
91
- textDisabled: '#d1d5db',
92
-
93
- // Background colors
94
- bgCanvas: '#ffffff',
95
- bgSurface: '#f9fafb',
96
- bgOverlay: 'rgba(0, 0, 0, 0.5)',
97
-
98
- // Border colors
99
- borderDefault: '#e5e7eb',
100
- borderFocus: '#3b82f6',
101
-
102
- // Action colors
103
- primaryDefault: '#3b82f6',
104
- primaryHover: '#2563eb',
105
- primaryText: '#ffffff',
106
-
107
- // State colors
108
- stateSuccess: '#10b981',
109
- stateWarning: '#f59e0b',
110
- stateError: '#ef4444',
76
+ app.use(SurgeUI, {
77
+ prefix: 'Su', // Component prefix: SuButton, SuDialog…
78
+ locale: locales.en, // Default texts language (French by default)
79
+ theme: { // Theme overrides (see Customization)
80
+ primaryDefault: '#2563eb'
111
81
  }
112
- }
113
-
114
- app.use(SurgeUpDS, themeConfig)
115
- app.mount('#app')
82
+ })
116
83
  ```
117
84
 
118
- ### CSS Variable Generation
85
+ ## 🧩 What's inside
119
86
 
120
- All theme properties are automatically converted to CSS custom properties with the `--su-custom-` prefix:
87
+ | Category | Components |
88
+ |----------|------------|
89
+ | Display | Heading, Panel, Icon, Image, Avatar, AvatarGroup, Badge, Card, Alert, Tabs, Accordion, Dialog, Popover, Progress, Spinner, Price |
90
+ | Actions | Button, ButtonGroup, Link, LinkGroup, Toggle, ToggleGroup, Dropdown, FloatButton |
91
+ | Forms | Input, Textarea, Password, SelectBox, RadioGroup, CheckboxGroup, Switch, Slider, FileUpload, OtpInput, FormField, FormFieldGroup, and a `*Field` version of each control (label + message) |
92
+ | Layouts | SingleColumnLayout, SidebarLayout, SplitLayout, HolyGrailLayout, MagazineLayout, GridLayout, MasonryLayout, HeroLayout, FullScreenLayout |
121
93
 
122
- ```ts
123
- theme: {
124
- textPrimary: '#1f2937' // → --su-custom-text-primary
125
- bgSurface: '#f9fafb' // → --su-custom-bg-surface
126
- primaryDefault: '#3b82f6' // → --su-custom-primary-default
127
- stateSuccessBg: '#d1fae5' // → --su-custom-state-success-bg
128
- }
129
- ```
94
+ ### Composables
130
95
 
131
- ### Using Custom Variables in CSS
96
+ | Composable | Purpose |
97
+ |------------|---------|
98
+ | `useTheme` | Theme and light / dark mode, system preference, persistence |
99
+ | `useCustomTheme` | Runtime CSS variable overrides |
100
+ | `useBreakpoint` | Reactive breakpoints |
101
+ | `usePrice` | Localized price formatting |
102
+ | `useLocale` | Default component texts (`provideLocale`, `defineLocale`) |
103
+ | `useFormField` | Links a custom control to `SuFormField` (id, label, message, states) |
104
+ | `useAnnouncer` | Screen reader announcements through persistent live regions |
105
+ | `useFocusTrap` | Stacked focus traps for panels and modal popovers |
106
+ | `useDismiss` | Layered dismissal: Escape, pointer down outside, focus loss |
107
+ | `useListNavigation` / `useTypeahead` | Arrow keys, Home / End and typeahead for lists, menus and tabs |
108
+ | `useFloatingPosition` | Positions a floating element against its trigger (flip, shift, arrow) |
132
109
 
133
- Define custom variables in your CSS and they will automatically take precedence over default theme tokens:
110
+ ## 📝 Forms
134
111
 
135
- ```css
136
- /* In your global CSS file */
137
- :root {
138
- /* Text customization */
139
- --su-custom-text-primary: #1f2937;
140
- --su-custom-text-secondary: #6b7280;
141
-
142
- /* Background customization */
143
- --su-custom-bg-canvas: #ffffff;
144
- --su-custom-bg-surface: #f9fafb;
145
-
146
- /* Button customization */
147
- --su-custom-primary-default: #3b82f6;
148
- --su-custom-primary-hover: #2563eb;
149
- --su-custom-primary-text: #ffffff;
150
-
151
- /* State customization */
152
- --su-custom-state-success: #10b981;
153
- --su-custom-state-error: #ef4444;
154
- }
155
-
156
- /* Dark mode example */
157
- @media (prefers-color-scheme: dark) {
158
- :root {
159
- --su-custom-text-primary: #f3f4f6;
160
- --su-custom-bg-canvas: #111827;
161
- --su-custom-bg-surface: #1f2937;
162
- }
163
- }
164
- ```
165
-
166
- ### Available Theme Tokens
167
-
168
- #### Text Tokens
169
- - `textPrimary` - Primary text color
170
- - `textSecondary` - Secondary text color
171
- - `textTertiary` - Tertiary text color
172
- - `textDisabled` - Disabled text color
173
- - `textInverse` - Inverse text color (for contrast backgrounds)
174
-
175
- #### Background Tokens
176
- - `bgCanvas` - Canvas/page background
177
- - `bgSurface` - Card and modal backgrounds
178
- - `bgSurfaceElevated` - Elevated surface backgrounds
179
- - `bgHover` - Hover state background
180
- - `bgActive` - Active state background
181
- - `bgSelected` - Selected state background
182
- - `bgOverlay` - Overlay/backdrop background
183
-
184
- #### Border Tokens
185
- - `borderDefault` - Default border color
186
- - `borderSubtle` - Subtle border color
187
- - `borderStrong` - Strong border color
188
- - `borderFocus` - Focus state border
189
- - `borderDisabled` - Disabled border
190
-
191
- #### Action Tokens
192
- - `primaryDefault` - Primary action default
193
- - `primaryHover` - Primary action hover
194
- - `primaryActive` - Primary action active
195
- - `primaryDisabled` - Primary action disabled
196
- - `primaryText` - Primary action text
197
- - `secondaryDefault` - Secondary action default
198
- - `secondaryHover` - Secondary action hover
199
- - `secondaryText` - Secondary action text
200
-
201
- #### State Tokens
202
- - `stateSuccess` - Success state color
203
- - `stateSuccessBg` - Success background
204
- - `stateWarning` - Warning state color
205
- - `stateWarningBg` - Warning background
206
- - `stateError` - Error state color
207
- - `stateErrorBg` - Error background
208
- - `stateInfo` - Info state color
209
- - `stateInfoBg` - Info background
210
-
211
- #### Link Tokens (legacy configuration)
212
- - `linkVariant` - Default link variant
213
- - `linkSize` - Default link size
214
- - `linkUnderline` - Default underline style
215
- - `dialogDisplay` - Default dialog display mode
216
-
217
- ### Variable Priority & Fallback
218
-
219
- Variables follow this priority order:
220
-
221
- 1. **Custom CSS variables** (`--su-custom-*`) - Highest priority
222
- 2. **Theme configuration** - Applied during plugin initialization
223
- 3. **Default theme tokens** - Fallbacks from theme definitions
224
-
225
- This means:
226
- - CSS variables defined in `:root` or element styles always take precedence
227
- - Variables not defined in the theme config fall back to default tokens
228
- - You can override specific tokens without defining the entire theme
229
-
230
- ### Scoped Customization
231
-
232
- Apply different themes to different parts of your application:
112
+ A control placed in `SuFormField` links itself to the field: label, help message, `aria-describedby`, error state, `required` and `disabled`. No slot props needed:
233
113
 
234
114
  ```vue
235
- <template>
236
- <!-- Light theme section -->
237
- <section class="light-section">
238
- <SuButton>Light Button</SuButton>
239
- </section>
240
-
241
- <!-- Dark theme section -->
242
- <section class="dark-section">
243
- <SuButton>Dark Button</SuButton>
244
- </section>
245
- </template>
246
-
247
- <style scoped>
248
- .light-section {
249
- --su-custom-text-primary: #1f2937;
250
- --su-custom-bg-surface: #ffffff;
251
- }
252
-
253
- .dark-section {
254
- --su-custom-text-primary: #f3f4f6;
255
- --su-custom-bg-surface: #1f2937;
256
- }
257
- </style>
115
+ <SuFormField label="Country" message="Country of residence" state="error" required>
116
+ <SuSelectBox :options="countries" v-model="country" />
117
+ </SuFormField>
258
118
  ```
259
119
 
260
- ### With Icons
120
+ The `*Field` components (`SuInputField`, `SuSelectBoxField`…) bundle the field and the control in one component.
121
+
122
+ ## 🎨 Themes and dark mode
261
123
 
262
- SurgeUI use **Heroicons** for add and display icons
124
+ Four themes (`default`, `ocean`, `forest`, `sunset`), each in light and dark mode. They are applied through the `data-theme` and `data-theme-mode` attributes on the root element; `useTheme` sets them for you, follows the system preference and remembers the user's choice:
263
125
 
264
126
  ```vue
265
127
  <script setup>
266
- import { informationCircleIcon, HeartIcon } from '@heroicons/vue/24/outline'
128
+ import { useTheme } from '@surgeui/ds-vue'
129
+
130
+ const { setTheme, setThemeMode, toggleMode, isDarkMode } = useTheme()
267
131
  </script>
268
132
 
269
133
  <template>
270
- <SuHeading>
271
- <informationCircleIcon /> User informations
272
- </SuHeading>
273
- <SuButton variant="primary" :icon="HeartIcon" iconDisplay="left">
274
- I like it
275
- </SuButton>
134
+ <SuButton @click="toggleMode">{{ isDarkMode ? 'Light' : 'Dark' }} mode</SuButton>
135
+ <SuButton @click="setTheme('ocean')">Ocean theme</SuButton>
276
136
  </template>
277
137
  ```
278
138
 
279
- ## ♿ Accessibility
139
+ `setThemeMode` accepts `'light'`, `'dark'` or `'system'`. Without `useTheme`, set the attributes yourself:
280
140
 
281
- All components follow WCAG 2.1 AA standards:
141
+ ```html
142
+ <html data-theme="default" data-theme-mode="dark">
143
+ ```
282
144
 
283
- - **Focus Management** - Clear focus indicators and logical tab order
284
- - **Keyboard Navigation** - Full keyboard support
285
- - **Screen Readers** - Proper ARIA attributes and announcements
286
- - **Color Contrast** - WCAG AA compliant contrast ratios (4.5:1 minimum)
287
- - **Touch Targets** - Minimum 44px touch targets
288
- - **Reduced Motion** - Respects `prefers-reduced-motion`
289
- - **High Contrast** - Supports `prefers-contrast: high`
290
- - **RTL Support** - Right-to-left language support
145
+ ## 🎨 Customization
291
146
 
292
- ## 🌙 Dark Mode
147
+ ### Theme configuration
293
148
 
294
- Dark mode is automatically supported through CSS media queries:
149
+ Theme properties passed to the plugin become CSS variables with the `--su-custom-` prefix, which override the default tokens:
150
+
151
+ ```ts
152
+ app.use(SurgeUI, {
153
+ theme: {
154
+ textPrimary: '#1f2937', // → --su-custom-text-primary
155
+ bgSurface: '#f9fafb', // → --su-custom-bg-surface
156
+ primaryDefault: '#3b82f6', // → --su-custom-primary-default
157
+ primaryHover: '#2563eb',
158
+ stateError: '#ef4444'
159
+ }
160
+ })
161
+ ```
162
+
163
+ ### CSS variables
164
+
165
+ You can also define the variables directly, globally or on a section of the page:
295
166
 
296
167
  ```css
297
- @media (prefers-color-scheme: dark) {
298
- /* Dark mode styles are automatically applied */
168
+ :root {
169
+ --su-custom-primary-default: #3b82f6;
170
+ --su-custom-primary-hover: #2563eb;
171
+ --su-custom-primary-text: #ffffff;
172
+ }
173
+
174
+ /* Values for the dark mode */
175
+ [data-theme-mode='dark'] {
176
+ --su-custom-bg-canvas: #111827;
177
+ --su-custom-bg-surface: #1f2937;
299
178
  }
300
179
 
301
- .dark, [theme="dark"] * {
302
- /* Dark mode styles are automatically applied */
180
+ /* A section with its own colors */
181
+ .promo {
182
+ --su-custom-primary-default: #db2777;
303
183
  }
304
184
  ```
305
185
 
306
- ## 📱 Responsive Design
186
+ Priority: `--su-custom-*` variables (CSS or plugin `theme` option) → theme tokens.
307
187
 
308
- All components are responsive by default:
188
+ ### Available theme keys
309
189
 
310
- - **Mobile-first** approach
311
- - **Flexible layouts** that adapt to screen size
312
- - **Touch-friendly** interactions
313
- - **Automatic responsive behavior** (e.g., FormFields switches to vertical on mobile)
190
+ - **Text** - `textPrimary`, `textSecondary`, `textTertiary`, `textDisabled`, `textInverse`
191
+ - **Links** - `linkDefault`, `linkHover`, `linkVisited`, `linkMuted`
192
+ - **Backgrounds** - `bgCanvas`, `bgSurface`, `bgSurfaceElevated`, `bgOverlay`, `bgDisabled`, `bgHover`, `bgActive`, `bgSelected`
193
+ - **Borders** - `borderDefault`, `borderSubtle`, `borderStrong`, `borderFocus`, `borderDisabled`
194
+ - **Primary actions** - `primaryDefault`, `primaryHover`, `primaryActive`, `primaryDisabled`, `primaryText`
195
+ - **Secondary actions** - `secondaryDefault`, `secondaryHover`, `secondaryActive`, `secondaryDisabled`, `secondaryText`
196
+ - **States** - `stateSuccess`, `stateWarning`, `stateError`, `stateInfo` and their `…Bg` variants
197
+ - **Shadow** - `shadowColor`
314
198
 
315
- ## 🎭 Storybook
199
+ ## 🌍 Languages
316
200
 
317
- Explore all components interactively in Storybook:
201
+ Default component texts (accessible labels, placeholders, error messages, announcements) are in French; English is provided. An explicit prop always wins.
318
202
 
319
- ```bash
320
- npm run storybook
203
+ ```ts
204
+ import SurgeUI, { defineLocale, locales } from '@surgeui/ds-vue'
205
+
206
+ app.use(SurgeUI, {
207
+ locale: defineLocale(locales.en, {
208
+ messages: { selectBox: { placeholder: 'Pick one…' } }
209
+ })
210
+ })
321
211
  ```
322
212
 
323
- ## 🛠️ Development
213
+ The `locale` option also accepts a ref, and `provideLocale()` switches the language of a subtree at runtime.
324
214
 
325
- ### Prerequisites
215
+ ## 🖼️ Icons
326
216
 
327
- - Node.js 16+
328
- - Vue.js 3.3+
217
+ Components accept any Vue component as an icon; the examples use [Heroicons](https://heroicons.com/):
329
218
 
330
- ### Build the library
219
+ ```vue
220
+ <script setup>
221
+ import { HeartIcon } from '@heroicons/vue/24/outline'
222
+ </script>
331
223
 
332
- ```bash
333
- npm run build
224
+ <template>
225
+ <SuButton variant="primary" :icon="HeartIcon" icon-display="left">I like it</SuButton>
226
+ </template>
334
227
  ```
335
228
 
336
- ### Development mode
229
+ ## ♿ Accessibility
337
230
 
338
- ```bash
339
- npm run dev
340
- ```
231
+ Components are built to meet **WCAG 2.1 AA**:
341
232
 
342
- ### Run Storybook
233
+ - **WAI-ARIA patterns** - combobox + listbox (SelectBox), menu button (Dropdown), tabs, accordion, modal dialog (native `<dialog>`)
234
+ - **Keyboard** - arrow keys, Home / End, typeahead, Escape; RTL-aware arrows
235
+ - **Focus** - visible focus rings, focus returned to the trigger, focus trap for modal popovers
236
+ - **Forms** - labels, messages and states linked automatically to the control
237
+ - **Screen readers** - persistent live regions for announcements, named landmarks (HolyGrailLayout, with skip link)
238
+ - **Preferences** - `prefers-reduced-motion`, `prefers-contrast: more`, minimum touch targets
343
239
 
344
- ```bash
345
- npm run storybook
346
- ```
240
+ ## ⬆️ Upgrading to 3.0
347
241
 
348
- ## 📖 Documentation
242
+ Version 3.0 contains breaking changes:
349
243
 
350
- Visit our [complete documentation](https://surgeui.com/) for:
244
+ - `SuButton` has `type="button"` by default: pass `type="submit"` to submit a form.
245
+ - `SuSwitch` renders a `<button>` instead of a `<div>`.
246
+ - `SuDialog` uses the native `<dialog>` element: the `zIndex` prop has no effect and the backdrop is `::backdrop`.
247
+ - Vue **3.5+** is required.
248
+ - `accessibility.announceToScreenReader()` and `accessibility.trapFocus()` are deprecated in favor of `announce()` / `useAnnouncer()` and `useFocusTrap()`.
351
249
 
352
- - **Component API** - Detailed props, events, and slots
353
- - **Usage examples** - Real-world implementation examples
354
- - **Accessibility guide** - Best practices for inclusive design
355
- - **Customization** - Theming and configuration options
250
+ See the [CHANGELOG](https://github.com/mahmoud-nb/surge.ui/blob/main/CHANGELOG.md) for the full list.
356
251
 
357
- ## 🤝 Contributing
252
+ ## 📖 Documentation
358
253
 
359
- We welcome contributions! Please see our [Contributing Guide](https://github.com/mahmoud-nb/surge.ui/blob/main/package/CONTRIBUTING.md) for details.
254
+ - [Documentation](https://surgeui.com/) - Guides, component API, live examples (French and English)
255
+ - [Storybook](https://surgeui.com/storybook/) - Interactive examples and accessibility checks
256
+ - [GitHub Repository](https://github.com/mahmoud-nb/surge.ui)
360
257
 
361
- ## 📄 License
258
+ ## 🤝 Contributing
362
259
 
363
- MIT License - see the [LICENSE](https://github.com/mahmoud-nb/surge.ui/blob/main/package/LICENSE.md) file for details.
260
+ Contributions are welcome! See the [Contributing Guide](https://github.com/mahmoud-nb/surge.ui/blob/main/package/CONTRIBUTING.md).
364
261
 
365
- ## 🔗 Links
262
+ ## 📄 License
366
263
 
367
- - [Documentation](https://surgeui.com/)
368
- - [Storybook](https://surgeui.com/storybook/)
369
- - [GitHub Repository](https://github.com/mahmoud-nb/surge.ui)
264
+ MIT - see the [LICENSE](https://github.com/mahmoud-nb/surge.ui/blob/main/package/LICENSE.md) file.
370
265
 
371
266
  ---
372
267
 
373
- Made with ❤️ by the SurgeUp team
268
+ Made with ❤️ by the SurgeUI team
@@ -14,7 +14,6 @@ declare const _default: import('vue').DefineComponent<AvatarProps, {
14
14
  size: import('../../types').AvatarSize;
15
15
  variant: import('../../types').AvatarVariant;
16
16
  loading: boolean;
17
- loadingLabel: string;
18
17
  clickable: boolean;
19
18
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
20
19
  avatarRef: HTMLDivElement;
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/Avatar.vue"],"names":[],"mappings":"AA+cA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;;;;;;;;;;;;;;;;;;;;;AA6S1C,wBAWG"}
1
+ {"version":3,"file":"Avatar.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/Avatar.vue"],"names":[],"mappings":"AA2YA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;;;;;;;;;;;;;;;;;;;;AA+T1C,wBAWG"}
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/Badge.vue"],"names":[],"mappings":"AAoRA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAwFzC,iBAAS,cAAc;WA0CT,OAAO,IAA6B;;oBAvHpC,GAAG;;oBAAH,GAAG;;;;EA4HhB;AAaD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;yFAQnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"Badge.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/Badge.vue"],"names":[],"mappings":"AAyPA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAwFzC,iBAAS,cAAc;WA0CT,OAAO,IAA6B;;oBAvHpC,GAAG;;oBAAH,GAAG;;;;EA4HhB;AAaD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;yFAQnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -22,9 +22,8 @@ declare const __VLS_component: import('vue').DefineComponent<ButtonProps, {}, {}
22
22
  size: import('../../types').ButtonSize;
23
23
  variant: import('../../types').ButtonVariant;
24
24
  loading: boolean;
25
- loadingLabel: string;
26
- radius: import('../../types').ButtonRadius;
27
25
  iconDisplay: "left" | "right" | "only";
26
+ type: import('../../types').ButtonType;
28
27
  disabled: boolean;
29
28
  block: boolean;
30
29
  tabIndex: number;
@@ -1 +1 @@
1
- {"version":3,"file":"Button.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/Button.vue"],"names":[],"mappings":"AA0VA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA8G1C,iBAAS,cAAc;WA+DT,OAAO,IAA6B;;yBAVpB,GAAG;;;;EAehC;AAkBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;2FASnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"Button.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/Button.vue"],"names":[],"mappings":"AAwNA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA0G1C,iBAAS,cAAc;WAoET,OAAO,IAA6B;;yBAVrB,GAAG;;;;EAe/B;AAmBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;2FASnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -16,13 +16,13 @@ type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
16
16
  declare const __VLS_component: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
17
17
  focus: (event: FocusEvent) => any;
18
18
  blur: (event: FocusEvent) => any;
19
- change: (value: (string | number)[]) => any;
20
19
  "update:modelValue": (value: (string | number)[]) => any;
20
+ change: (value: (string | number)[]) => any;
21
21
  }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
22
22
  onFocus?: ((event: FocusEvent) => any) | undefined;
23
23
  onBlur?: ((event: FocusEvent) => any) | undefined;
24
- onChange?: ((value: (string | number)[]) => any) | undefined;
25
24
  "onUpdate:modelValue"?: ((value: (string | number)[]) => any) | undefined;
25
+ onChange?: ((value: (string | number)[]) => any) | undefined;
26
26
  }>, {
27
27
  size: import('../../types').Size;
28
28
  disabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"CheckboxGroup.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/CheckboxGroup.vue"],"names":[],"mappings":"AAqpBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAIjD,KAAK,WAAW,GAAG,kBAAkB,CAAC;AAqItC,KAAK,iBAAiB,GAAG;IACzB,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CAChC,GAAG,WAAW,CAAC;AAKhB,iBAAS,cAAc;WA2GT,OAAO,IAA6B;;wBAXtB,GAAG;uBACH,GAAG;;;;EAe9B;AAqBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;6EAQnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"CheckboxGroup.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/CheckboxGroup.vue"],"names":[],"mappings":"AA0TA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAOjD,KAAK,WAAW,GAAG,kBAAkB,CAAC;AA6ItC,KAAK,iBAAiB,GAAG;IACzB,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CAChC,GAAG,WAAW,CAAC;AAKhB,iBAAS,cAAc;WAkHT,OAAO,IAA6B;;wBAXtB,GAAG;uBACH,GAAG;;;;EAe9B;AAsBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;6EAQnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -11,19 +11,19 @@ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {
11
11
  }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
12
12
  error: (error: string, file?: File | undefined) => any;
13
13
  focus: (event: FocusEvent) => any;
14
+ remove: (file: UploadedFile) => any;
14
15
  blur: (event: FocusEvent) => any;
15
- change: (files: UploadedFile[]) => any;
16
16
  "update:modelValue": (value: UploadedFile[]) => any;
17
+ change: (files: UploadedFile[]) => any;
17
18
  upload: (file: UploadedFile) => any;
18
- remove: (file: UploadedFile) => any;
19
19
  }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
20
20
  onError?: ((error: string, file?: File | undefined) => any) | undefined;
21
21
  onFocus?: ((event: FocusEvent) => any) | undefined;
22
+ onRemove?: ((file: UploadedFile) => any) | undefined;
22
23
  onBlur?: ((event: FocusEvent) => any) | undefined;
23
- onChange?: ((files: UploadedFile[]) => any) | undefined;
24
24
  "onUpdate:modelValue"?: ((value: UploadedFile[]) => any) | undefined;
25
+ onChange?: ((files: UploadedFile[]) => any) | undefined;
25
26
  onUpload?: ((file: UploadedFile) => any) | undefined;
26
- onRemove?: ((file: UploadedFile) => any) | undefined;
27
27
  }>, {
28
28
  size: import('../../types').FileUploadSize;
29
29
  variant: import('../../types').FileUploadVariant;
@@ -35,11 +35,6 @@ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {
35
35
  maxSize: number;
36
36
  maxFiles: number;
37
37
  readonly: boolean;
38
- placeholder: string;
39
- dragText: string;
40
- browseText: string;
41
- loadingText: string;
42
- loadingSubText: string;
43
38
  allowPreview: boolean;
44
39
  showFileList: boolean;
45
40
  showProgress: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"FileUpload.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/FileUpload.vue"],"names":[],"mappings":"AAkgCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGhE,KAAK,WAAW,GAAG,mBAAmB,CAAC;AAwWvC,KAAK,iBAAiB,GAAG;IACzB,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B,GAAG,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+ThB,wBAWG"}
1
+ {"version":3,"file":"FileUpload.vue.d.ts","sourceRoot":"","sources":["../../../src/components/atoms/FileUpload.vue"],"names":[],"mappings":"AAi3BA,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAKhE,KAAK,WAAW,GAAG,mBAAmB,CAAC;AA8VvC,KAAK,iBAAiB,GAAG;IACzB,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B,GAAG,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqUhB,wBAWG"}