@marwes-ui/vue 1.0.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.
- package/LICENSE +21 -0
- package/README.md +452 -0
- package/dist/index.d.ts +2856 -0
- package/dist/index.js +6525 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Niklas Westman, Martino Ognissanti and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img alt="Marwes Design System" src="https://raw.githubusercontent.com/niklas-westman/marwes/main/.github/assets/banner-light.png" width="100%">
|
|
4
|
+
|
|
5
|
+
<br>
|
|
6
|
+
<br>
|
|
7
|
+
|
|
8
|
+
# Marwes Design System - Vue
|
|
9
|
+
|
|
10
|
+
**Vue 3 components with first edition styling, typed theme tokens, accessibility contracts, and AI-readable semantics built in.**
|
|
11
|
+
|
|
12
|
+
Vue 3.4+ • TypeScript-first • Default CSS included • ThemeInput • Google Fonts DX • Purpose components
|
|
13
|
+
|
|
14
|
+
[Documentation](https://github.com/niklas-westman/marwes/tree/main/docs) • [Storybook](https://d3hobet9plpuvm.cloudfront.net/storybook-react/latest/) • [GitHub](https://github.com/niklas-westman/marwes)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Why Use It
|
|
21
|
+
|
|
22
|
+
Marwes gives Vue apps a ready design-system base without requiring custom CSS setup or local component forks.
|
|
23
|
+
|
|
24
|
+
- **One package for Vue apps**: components, provider, default first edition CSS, theme helpers, and typed props.
|
|
25
|
+
- **Vue-native binding**: common controls support `v-model` while preserving Marwes semantic contracts.
|
|
26
|
+
- **Consequential theming**: a `ThemeInput` object changes colors, fonts, radius, density, typography, and component visuals through shared CSS variables.
|
|
27
|
+
- **Purpose components**: `SubmitButton`, `CancelButton`, and `DestructiveButton` make intent machine-readable so tests, audits, and AI agents can handle actions safely.
|
|
28
|
+
- **Shared core contracts**: every Vue component is backed by the same framework-agnostic recipes, a11y mapping, and theme shape.
|
|
29
|
+
|
|
30
|
+
## Package Map
|
|
31
|
+
|
|
32
|
+
For a Vue app, install this package first. It includes the Vue adapter, loads the default preset CSS, and re-exports the core theme helpers you normally need.
|
|
33
|
+
|
|
34
|
+
| Package | Use it when |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `@marwes-ui/vue` | You are building a Vue app. |
|
|
37
|
+
| `@marwes-ui/react` | You are building a React app instead. |
|
|
38
|
+
| `@marwes-ui/core` | You are building adapters, tests, tooling, or framework-agnostic integrations. |
|
|
39
|
+
| `@marwes-ui/presets` | You need standalone first edition CSS or preset theme exports. |
|
|
40
|
+
|
|
41
|
+
This split keeps installation simple for app teams while giving humans and AI agents clear package boundaries: adapters render, core defines contracts, presets style.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pnpm add @marwes-ui/vue vue
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
No preset CSS import is needed. `@marwes-ui/vue` depends on `@marwes-ui/presets` and loads the first edition CSS automatically.
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```vue
|
|
54
|
+
<script setup lang="ts">
|
|
55
|
+
import { ref } from "vue"
|
|
56
|
+
import { Button, Checkbox, Input, MarwesProvider, SubmitButton } from "@marwes-ui/vue"
|
|
57
|
+
|
|
58
|
+
const email = ref("")
|
|
59
|
+
const subscribed = ref(false)
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<MarwesProvider>
|
|
64
|
+
<Input v-model="email" placeholder="Email" ariaLabel="Email" />
|
|
65
|
+
<Checkbox v-model="subscribed" ariaLabel="Subscribe" />
|
|
66
|
+
<Button variant="secondary">Preview</Button>
|
|
67
|
+
<SubmitButton>Save</SubmitButton>
|
|
68
|
+
</MarwesProvider>
|
|
69
|
+
</template>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Style App-Owned UI
|
|
73
|
+
|
|
74
|
+
Marwes components pick up provider tokens automatically. Your own Vue styling can use the same tokens through scoped CSS, CSS Modules, or `mwThemeVars` in script-driven style objects.
|
|
75
|
+
|
|
76
|
+
```vue
|
|
77
|
+
<script setup lang="ts">
|
|
78
|
+
import { Button, MarwesProvider, mwThemeVars } from "@marwes-ui/vue"
|
|
79
|
+
|
|
80
|
+
const panelStyle = {
|
|
81
|
+
padding: mwThemeVars.spacing.sp24,
|
|
82
|
+
background: mwThemeVars.color.surface,
|
|
83
|
+
color: mwThemeVars.color.text,
|
|
84
|
+
borderColor: mwThemeVars.color.border,
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<MarwesProvider>
|
|
90
|
+
<main class="app-shell">
|
|
91
|
+
<aside class="primary-callout">Launch workspace</aside>
|
|
92
|
+
<section class="feature-panel" :style="panelStyle">
|
|
93
|
+
<Button variant="primary">Save</Button>
|
|
94
|
+
</section>
|
|
95
|
+
</main>
|
|
96
|
+
</MarwesProvider>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<style scoped>
|
|
100
|
+
.app-shell {
|
|
101
|
+
min-height: 100dvh;
|
|
102
|
+
padding: var(--mw-spacing-sp-24);
|
|
103
|
+
color: var(--mw-color-text);
|
|
104
|
+
background: var(--mw-color-background);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.feature-panel {
|
|
108
|
+
border: 1px solid var(--mw-color-border);
|
|
109
|
+
border-radius: var(--mw-ui-radius);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.primary-callout {
|
|
113
|
+
background: var(--mw-color-primary-base);
|
|
114
|
+
color: var(--mw-color-primary-label);
|
|
115
|
+
}
|
|
116
|
+
</style>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Use Typed Components
|
|
120
|
+
|
|
121
|
+
Import components, enum objects, and prop types from the same package. The values line up with the same core recipes and preset CSS.
|
|
122
|
+
|
|
123
|
+
```vue
|
|
124
|
+
<script setup lang="ts">
|
|
125
|
+
import {
|
|
126
|
+
BadgeVariant,
|
|
127
|
+
Button,
|
|
128
|
+
ButtonSize,
|
|
129
|
+
ButtonVariant,
|
|
130
|
+
Card,
|
|
131
|
+
H1,
|
|
132
|
+
InputField,
|
|
133
|
+
Paragraph,
|
|
134
|
+
Spacer,
|
|
135
|
+
Spacings,
|
|
136
|
+
StatusBadge,
|
|
137
|
+
SubmitButton,
|
|
138
|
+
type ButtonProps,
|
|
139
|
+
type InputFieldProps,
|
|
140
|
+
} from "@marwes-ui/vue"
|
|
141
|
+
|
|
142
|
+
const primaryAction: ButtonProps = {
|
|
143
|
+
variant: ButtonVariant.primary,
|
|
144
|
+
size: ButtonSize.md,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const emailField: InputFieldProps = {
|
|
148
|
+
label: "Email",
|
|
149
|
+
helperText: "Used for project notifications.",
|
|
150
|
+
input: {
|
|
151
|
+
type: "email",
|
|
152
|
+
placeholder: "you@example.com",
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
</script>
|
|
156
|
+
|
|
157
|
+
<template>
|
|
158
|
+
<Card>
|
|
159
|
+
<template #title>Project setup</template>
|
|
160
|
+
<StatusBadge :variant="BadgeVariant.success">Ready</StatusBadge>
|
|
161
|
+
<Spacer :spacing="Spacings.sp16" />
|
|
162
|
+
<H1 size="h2">Launch workspace</H1>
|
|
163
|
+
<Paragraph size="md">
|
|
164
|
+
Components share theme tokens, typed variants, spacing, and semantic metadata.
|
|
165
|
+
</Paragraph>
|
|
166
|
+
<InputField v-bind="emailField" />
|
|
167
|
+
<Spacer :spacing="Spacings.sp24" />
|
|
168
|
+
<Button v-bind="primaryAction">Create project</Button>
|
|
169
|
+
<SubmitButton>Save changes</SubmitButton>
|
|
170
|
+
</Card>
|
|
171
|
+
</template>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Available Components
|
|
175
|
+
|
|
176
|
+
Provider and hooks:
|
|
177
|
+
- `MarwesProvider`
|
|
178
|
+
- `useTheme`
|
|
179
|
+
- `useToast`
|
|
180
|
+
|
|
181
|
+
Actions and buttons:
|
|
182
|
+
- `Button`
|
|
183
|
+
- `PrimaryButton`, `SecondaryButton`, `TextButton`, `SuccessButton`
|
|
184
|
+
- `SubmitButton`, `CancelButton`, `CreateButton`, `DestructiveButton`
|
|
185
|
+
- `LinkButton`, `SaveButton`, `ConfirmButton`, `VerifyButton`
|
|
186
|
+
- `EditButton`, `CloseButton`, `RefreshButton`
|
|
187
|
+
- `UploadButton`, `DownloadButton`, `CopyButton`
|
|
188
|
+
- `SearchButton`, `FilterButton`, `SortButton`, `DropdownButton`
|
|
189
|
+
|
|
190
|
+
Forms and inputs:
|
|
191
|
+
- `Input`, `Textarea`, `Select`, `RichText`, `InputOtp`
|
|
192
|
+
- `InputField`, `TextareaField`, `SelectField`, `RichTextField`
|
|
193
|
+
- `DropdownField`, `SearchField`, `PasswordField`, `EmailField`
|
|
194
|
+
- `DateOfBirthField`, `ZipCodeField`, `PhoneField`, `URLField`, `CurrencyField`
|
|
195
|
+
- `Checkbox`, `CheckboxField`, `CheckboxGroupField`
|
|
196
|
+
- `Radio`, `RadioGroupField`, `YesNoRadioGroup`, `RatingRadioGroup`, `OptionRadioGroup`
|
|
197
|
+
- `Switch`, `SwitchField`, `FeatureToggle`, `PreferenceSwitch`, `PermissionSwitch`
|
|
198
|
+
- `Slider`, `SliderField`, `VolumeSlider`, `BrightnessSlider`, `RadiusSlider`
|
|
199
|
+
|
|
200
|
+
Content and layout:
|
|
201
|
+
- `Card`, `ProductCard`, `ProfileCard`, `StatCard`
|
|
202
|
+
- `H1`, `H2`, `H3`, `Paragraph`
|
|
203
|
+
- `Spacer`, `Spacing`, `Divider`
|
|
204
|
+
- `Icon`
|
|
205
|
+
- `Avatar`, `AvatarBadge`, `AvatarGroup`, `ProfileAvatar`, `PresenceAvatar`, `TeamAvatarGroup`
|
|
206
|
+
|
|
207
|
+
Feedback and overlays:
|
|
208
|
+
- `Badge`, `BadgeGroup`, `StatusBadge`, `PriorityBadge`, `NotificationBadge`
|
|
209
|
+
- `Spinner`, `ButtonSpinner`, `EmptyStateSpinner`
|
|
210
|
+
- `Toast`, `ToastContainer`, `ToastProvider`
|
|
211
|
+
- `SuccessToast`, `ErrorToast`, `WarningToast`, `InfoToast`
|
|
212
|
+
- `Tooltip`, `TooltipGroup`
|
|
213
|
+
- `Dialog`, `DialogModal`, `ConfirmDialog`, `DestructiveDialog`, `InfoDialog`
|
|
214
|
+
- `Accordion`, `AccordionField`, `FAQAccordion`, `SettingsAccordion`, `SectionsAccordion`
|
|
215
|
+
- `Tab`, `TabGroup`, `TabPanel`, `NavigationTabs`, `ContentTabs`, `SettingsTabs`
|
|
216
|
+
|
|
217
|
+
Typed tokens and helpers:
|
|
218
|
+
- `ThemeInput`, `ThemeMode`, `Density`, `ToneName`
|
|
219
|
+
- `mwAvailableFonts`, `mwGoogleFontFamilies`, `mwFontFallbacks`, `createFontStack`
|
|
220
|
+
- `mwThemeVars`, `mwThemeVarNames`, `mwStyledTheme`, `mwVar`
|
|
221
|
+
- `ButtonVariant`, `ButtonSize`, `ButtonAction`
|
|
222
|
+
- `BadgeVariant`, `AvatarSize`, `AvatarType`, `SwitchSize`, `IconName`, `Spacings`
|
|
223
|
+
|
|
224
|
+
## Theme In Seconds
|
|
225
|
+
|
|
226
|
+
First edition is the default. Pass `theme` only when a brand or design file needs to change the baseline.
|
|
227
|
+
|
|
228
|
+
```vue
|
|
229
|
+
<script setup lang="ts">
|
|
230
|
+
import { MarwesProvider, mwAvailableFonts } from "@marwes-ui/vue"
|
|
231
|
+
|
|
232
|
+
const brandTheme = {
|
|
233
|
+
color: {
|
|
234
|
+
primary: "#2457FF",
|
|
235
|
+
danger: "#D90429",
|
|
236
|
+
success: "#15803D",
|
|
237
|
+
warning: "#D97706",
|
|
238
|
+
background: "#F8FAFC",
|
|
239
|
+
surface: "#FFFFFF",
|
|
240
|
+
surfaceElevated: "#FFFFFF",
|
|
241
|
+
text: "#111827",
|
|
242
|
+
textMuted: "#4B5563",
|
|
243
|
+
border: "#D1D5DB",
|
|
244
|
+
focus: "#2457FF",
|
|
245
|
+
},
|
|
246
|
+
font: {
|
|
247
|
+
primary: mwAvailableFonts.Poppins,
|
|
248
|
+
secondary: mwAvailableFonts.Lora,
|
|
249
|
+
},
|
|
250
|
+
ui: {
|
|
251
|
+
radius: 10,
|
|
252
|
+
density: "comfortable",
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
</script>
|
|
256
|
+
|
|
257
|
+
<template>
|
|
258
|
+
<MarwesProvider :theme="brandTheme">
|
|
259
|
+
<AppShell />
|
|
260
|
+
</MarwesProvider>
|
|
261
|
+
</template>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The provider resolves `ThemeInput` into `--mw-*` CSS variables. Preset CSS consumes those variables across the full component system.
|
|
265
|
+
|
|
266
|
+
## Custom Styling Tokens
|
|
267
|
+
|
|
268
|
+
`MarwesProvider` resolves `ThemeInput` into `--mw-*` CSS variables. Marwes components and preset CSS consume those variables automatically. The custom styling token helpers let app-owned Vue styles, script-driven style objects, and build-time config use the same provider-scoped values instead of hard-coding colors, spacing, radius, typography, or duplicated `var(...)` strings.
|
|
269
|
+
|
|
270
|
+
Vue style blocks and CSS Modules can consume provider variables directly:
|
|
271
|
+
|
|
272
|
+
```vue
|
|
273
|
+
<template>
|
|
274
|
+
<section class="panel">
|
|
275
|
+
<slot />
|
|
276
|
+
</section>
|
|
277
|
+
</template>
|
|
278
|
+
|
|
279
|
+
<style scoped>
|
|
280
|
+
.panel {
|
|
281
|
+
padding: var(--mw-spacing-sp-24);
|
|
282
|
+
color: var(--mw-color-text);
|
|
283
|
+
background: var(--mw-color-surface);
|
|
284
|
+
border-radius: var(--mw-ui-radius);
|
|
285
|
+
}
|
|
286
|
+
</style>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
For script-driven style objects, composables, renderer bridges, or config files, import the same core helpers through the Vue package:
|
|
290
|
+
|
|
291
|
+
- `mwThemeVars` is the default styling helper. It returns CSS `var(...)` references for style objects, CSS-in-JS integrations, vanilla-extract, and config files.
|
|
292
|
+
- `mwThemeVarNames` returns raw custom property names for assigning provider-scoped overrides in style objects or tooling.
|
|
293
|
+
- `mwVar()` wraps custom or advanced `--mw-*` names when the named token object does not cover a specialized case.
|
|
294
|
+
- `mwStyledTheme` mirrors `mwThemeVars` as a plain object for shared CSS-in-JS integrations that expect a theme object.
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { mwStyledTheme, mwThemeVarNames, mwThemeVars, mwVar } from "@marwes-ui/vue"
|
|
298
|
+
|
|
299
|
+
const panelStyle = {
|
|
300
|
+
padding: mwThemeVars.spacing.sp24,
|
|
301
|
+
color: mwThemeVars.color.text,
|
|
302
|
+
outlineColor: mwVar("--mw-color-focus", "#2457FF"),
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const focusOverride = {
|
|
306
|
+
[mwThemeVarNames.color.focus]: "#FF00AA",
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const cssInJsTheme = mwStyledTheme
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
This enables Vue apps to keep scoped CSS, dynamic `:style` bindings, custom layout wrappers, and design-tool integrations visually tied to the same active theme as Marwes components. Changing `MarwesProvider :theme="..."` updates both preset components and app-owned styles.
|
|
313
|
+
|
|
314
|
+
Keep the APIs separate:
|
|
315
|
+
- Use `Spacings.sp24` for Marwes spacing props such as `<Spacer :spacing="Spacings.sp24" />`.
|
|
316
|
+
- Use `mwThemeVars.spacing.sp24` for custom CSS values.
|
|
317
|
+
- Use `mwThemeVarNames.spacing.sp24` when assigning or inspecting a CSS custom property name.
|
|
318
|
+
- Use `useTheme()` when Vue logic needs resolved runtime values such as `"#2457FF"`.
|
|
319
|
+
|
|
320
|
+
## Vue Binding Conventions
|
|
321
|
+
|
|
322
|
+
- Text-like controls support `v-model` through `modelValue` and `update:modelValue`.
|
|
323
|
+
- Inputs also expose `onValueChange` when callback-style wiring is useful.
|
|
324
|
+
- Checkboxes support `v-model` and `onCheckedChange`.
|
|
325
|
+
|
|
326
|
+
Prefer `v-model` in normal Vue app code unless you specifically need callback-style wiring.
|
|
327
|
+
|
|
328
|
+
## Google Fonts DX
|
|
329
|
+
|
|
330
|
+
Most Google Font use cases only need `mwAvailableFonts`; no `fontLoading` prop is needed.
|
|
331
|
+
|
|
332
|
+
```vue
|
|
333
|
+
<script setup lang="ts">
|
|
334
|
+
import { mwAvailableFonts } from "@marwes-ui/vue"
|
|
335
|
+
|
|
336
|
+
const theme = {
|
|
337
|
+
font: {
|
|
338
|
+
primary: mwAvailableFonts.Poppins,
|
|
339
|
+
secondary: mwAvailableFonts.Lora,
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
</script>
|
|
343
|
+
|
|
344
|
+
<template>
|
|
345
|
+
<MarwesProvider :theme="theme">
|
|
346
|
+
<App />
|
|
347
|
+
</MarwesProvider>
|
|
348
|
+
</template>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
For self-hosted or licensed fonts, use `BrandSans`, `BrandSerif`, `BrandMono`, or `createFontStack()`.
|
|
352
|
+
|
|
353
|
+
## Purpose Components
|
|
354
|
+
|
|
355
|
+
Prefer purpose components for common actions. They lock UX intent and emit AI-readable metadata, so the component is not just "a button with red styling" but a known action with known risk.
|
|
356
|
+
|
|
357
|
+
```vue
|
|
358
|
+
<script setup lang="ts">
|
|
359
|
+
import {
|
|
360
|
+
CancelButton,
|
|
361
|
+
CreateButton,
|
|
362
|
+
DestructiveButton,
|
|
363
|
+
SubmitButton,
|
|
364
|
+
} from "@marwes-ui/vue"
|
|
365
|
+
</script>
|
|
366
|
+
|
|
367
|
+
<template>
|
|
368
|
+
<CancelButton>Cancel</CancelButton>
|
|
369
|
+
<CreateButton>Create</CreateButton>
|
|
370
|
+
<SubmitButton>Save</SubmitButton>
|
|
371
|
+
<DestructiveButton>Delete</DestructiveButton>
|
|
372
|
+
</template>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
That matters for agentic workflows. A human, test, or AI agent can inspect the DOM and see that a destructive button requires confirmation before activation:
|
|
376
|
+
|
|
377
|
+
```html
|
|
378
|
+
<button
|
|
379
|
+
data-component="button"
|
|
380
|
+
data-purpose="destructive"
|
|
381
|
+
data-action="delete"
|
|
382
|
+
data-destructive="true"
|
|
383
|
+
data-confirmation-required="true"
|
|
384
|
+
>
|
|
385
|
+
Delete
|
|
386
|
+
</button>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
An agent can then follow a safer rule: if `data-confirmation-required="true"`, ask the user before clicking. A test can assert the same behavior without guessing from the label "Delete" or from a red color.
|
|
390
|
+
|
|
391
|
+
## Why It Is Accessible
|
|
392
|
+
|
|
393
|
+
Marwes Vue components are accessible because the adapter renders a shared core contract, not because each component hand-rolls ARIA in isolation.
|
|
394
|
+
|
|
395
|
+
- Core recipes produce typed `a11y` output for roles, labels, described-by wiring, invalid state, disabled state, and semantic metadata.
|
|
396
|
+
- Vue components prefer native DOM controls first: `button`, `input`, `select`, `textarea`, `hr`, and standard form wiring.
|
|
397
|
+
- Field components connect visible labels, helper text, and errors through `id`, `for`, `aria-describedby`, `aria-invalid`, and polite error announcements.
|
|
398
|
+
- Coordinated widgets carry explicit contracts: tabs wire `tablist`/`tab`/`tabpanel`, dialogs own dialog semantics, toasts expose live-region behavior, and purpose buttons expose risk metadata.
|
|
399
|
+
- Storybook accessibility smoke checks run through the Storybook a11y addon for the promoted Vue families, and shared contract tests keep Vue aligned with React.
|
|
400
|
+
|
|
401
|
+
Example:
|
|
402
|
+
|
|
403
|
+
```vue
|
|
404
|
+
<script setup lang="ts">
|
|
405
|
+
import { InputField } from "@marwes-ui/vue"
|
|
406
|
+
</script>
|
|
407
|
+
|
|
408
|
+
<template>
|
|
409
|
+
<InputField
|
|
410
|
+
label="Email"
|
|
411
|
+
helperText="Used for receipts."
|
|
412
|
+
error="Enter a valid email."
|
|
413
|
+
:input="{ type: 'email', placeholder: 'you@example.com' }"
|
|
414
|
+
/>
|
|
415
|
+
</template>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
That contract resolves to DOM wiring like:
|
|
419
|
+
|
|
420
|
+
```html
|
|
421
|
+
<label for="email">Email</label>
|
|
422
|
+
<input
|
|
423
|
+
id="email"
|
|
424
|
+
type="email"
|
|
425
|
+
aria-describedby="email-helper email-error"
|
|
426
|
+
aria-invalid="true"
|
|
427
|
+
>
|
|
428
|
+
<p id="email-helper">Used for receipts.</p>
|
|
429
|
+
<p id="email-error" aria-live="polite">Enter a valid email.</p>
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
The important part is that the same label, helper, error, and invalid contract is tested at the shared contract layer and then applied by the Vue adapter.
|
|
433
|
+
|
|
434
|
+
## Package Boundaries
|
|
435
|
+
|
|
436
|
+
- `@marwes-ui/core` owns recipes, theme resolution, a11y mapping, and semantic metadata.
|
|
437
|
+
- `@marwes-ui/presets` owns first edition CSS.
|
|
438
|
+
- `@marwes-ui/vue` owns Vue rendering and provider behavior.
|
|
439
|
+
|
|
440
|
+
## Scripts
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
pnpm --filter @marwes-ui/vue build
|
|
444
|
+
pnpm --filter @marwes-ui/vue typecheck
|
|
445
|
+
pnpm --filter @marwes-ui/vue test
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Related Docs
|
|
449
|
+
|
|
450
|
+
- [Docs index](https://github.com/niklas-westman/marwes/tree/main/docs)
|
|
451
|
+
- [Architecture](https://github.com/niklas-westman/marwes/blob/main/docs/reference/architecture.md)
|
|
452
|
+
- [Figma to Marwes](https://github.com/niklas-westman/marwes/blob/main/docs/guides/figma-to-marwes.md)
|