@marwes-ui/react 0.0.3 → 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 +1 -1
- package/README.md +417 -62
- package/dist/index.d.ts +1591 -263
- package/dist/index.js +4009 -204
- package/dist/index.js.map +1 -1
- package/package.json +16 -12
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Niklas Westman
|
|
3
|
+
Copyright (c) 2026 Niklas Westman, Martino Ognissanti and contributors
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,100 +1,455 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img alt="Marwes Design System" src="https://raw.githubusercontent.com/niklas-westman/marwes/main/.github/assets/banner-light.png" width="100%">
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- Resolve theme from context and call core recipes.
|
|
8
|
-
- Apply typed RenderKit output to React elements.
|
|
5
|
+
<br>
|
|
6
|
+
<br>
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
8
|
+
# Marwes Design System - React
|
|
9
|
+
|
|
10
|
+
**React components with first edition styling, typed theme tokens, accessibility contracts, and AI-readable semantics built in.**
|
|
11
|
+
|
|
12
|
+
React 18+ • 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 is built for teams and AI-assisted workflows that need a component system that is easy to install, easy to theme, and hard to misuse.
|
|
23
|
+
|
|
24
|
+
- **One package for React apps**: components, provider, default first edition CSS, theme helpers, and typed props.
|
|
25
|
+
- **Beautiful defaults**: install the adapter, wrap your app, and start building without a separate CSS setup.
|
|
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 React component is backed by the same framework-agnostic recipes, a11y mapping, and theme shape.
|
|
29
|
+
|
|
30
|
+
## Package Map
|
|
31
|
+
|
|
32
|
+
For a React app, install this package first. It includes the React 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/react` | You are building a React app. |
|
|
37
|
+
| `@marwes-ui/vue` | You are building a Vue 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.
|
|
13
42
|
|
|
14
43
|
## Install
|
|
44
|
+
|
|
15
45
|
```bash
|
|
16
|
-
pnpm add @marwes-ui/
|
|
46
|
+
pnpm add @marwes-ui/react react react-dom
|
|
17
47
|
```
|
|
18
48
|
|
|
49
|
+
No preset CSS import is needed. `@marwes-ui/react` depends on `@marwes-ui/presets` and loads the first edition CSS automatically.
|
|
50
|
+
|
|
19
51
|
## Quick Start
|
|
52
|
+
|
|
20
53
|
```tsx
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
54
|
+
import {
|
|
55
|
+
Button,
|
|
56
|
+
Checkbox,
|
|
57
|
+
Input,
|
|
58
|
+
MarwesProvider,
|
|
59
|
+
SubmitButton,
|
|
60
|
+
} from "@marwes-ui/react"
|
|
24
61
|
|
|
25
62
|
export function App() {
|
|
26
63
|
return (
|
|
27
|
-
<MarwesProvider
|
|
28
|
-
<
|
|
29
|
-
<Input placeholder="Email" onValueChange={(v) => console.log(v)} />
|
|
64
|
+
<MarwesProvider>
|
|
65
|
+
<Input placeholder="Email" ariaLabel="Email" />
|
|
30
66
|
<Checkbox ariaLabel="Subscribe" />
|
|
67
|
+
<Button variant="secondary">Preview</Button>
|
|
68
|
+
<SubmitButton>Save</SubmitButton>
|
|
69
|
+
</MarwesProvider>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Style App-Owned UI
|
|
75
|
+
|
|
76
|
+
Marwes components pick up provider tokens automatically. Your own React styling can use the same tokens by importing `mwThemeVars`. This example uses styled-components, but the same values work in Emotion, vanilla-extract, inline style objects, CSS Modules, and plain CSS.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { Button, MarwesProvider, mwThemeVars } from "@marwes-ui/react"
|
|
80
|
+
import styled from "styled-components"
|
|
81
|
+
|
|
82
|
+
const AppShell = styled.main`
|
|
83
|
+
min-height: 100dvh;
|
|
84
|
+
padding: ${mwThemeVars.spacing.sp24};
|
|
85
|
+
color: ${mwThemeVars.color.text};
|
|
86
|
+
background: ${mwThemeVars.color.background};
|
|
87
|
+
`
|
|
88
|
+
|
|
89
|
+
const FeaturePanel = styled.section`
|
|
90
|
+
padding: ${mwThemeVars.spacing.sp24};
|
|
91
|
+
background: ${mwThemeVars.color.surface};
|
|
92
|
+
border: 1px solid ${mwThemeVars.color.border};
|
|
93
|
+
border-radius: ${mwThemeVars.ui.radius};
|
|
94
|
+
`
|
|
95
|
+
|
|
96
|
+
const PrimaryCallout = styled.aside`
|
|
97
|
+
background: ${mwThemeVars.color.primary.base};
|
|
98
|
+
color: ${mwThemeVars.color.primary.label};
|
|
99
|
+
`
|
|
100
|
+
|
|
101
|
+
export function App() {
|
|
102
|
+
return (
|
|
103
|
+
<MarwesProvider>
|
|
104
|
+
<AppShell>
|
|
105
|
+
<PrimaryCallout>Launch workspace</PrimaryCallout>
|
|
106
|
+
<FeaturePanel>
|
|
107
|
+
<Button variant="primary">Save</Button>
|
|
108
|
+
</FeaturePanel>
|
|
109
|
+
</AppShell>
|
|
31
110
|
</MarwesProvider>
|
|
32
|
-
)
|
|
111
|
+
)
|
|
33
112
|
}
|
|
34
113
|
```
|
|
35
114
|
|
|
36
|
-
|
|
115
|
+
Plain CSS and CSS Modules can use the raw custom properties directly:
|
|
116
|
+
|
|
117
|
+
```css
|
|
118
|
+
.app-shell {
|
|
119
|
+
min-height: 100dvh;
|
|
120
|
+
padding: var(--mw-spacing-sp-24);
|
|
121
|
+
color: var(--mw-color-text);
|
|
122
|
+
background: var(--mw-color-background);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Use Typed Components
|
|
127
|
+
|
|
128
|
+
Import components, enums, and prop types from the same package. The values line up with the same core recipes and preset CSS.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import {
|
|
132
|
+
BadgeVariant,
|
|
133
|
+
Button,
|
|
134
|
+
ButtonSize,
|
|
135
|
+
ButtonVariant,
|
|
136
|
+
Card,
|
|
137
|
+
H1,
|
|
138
|
+
InputField,
|
|
139
|
+
Paragraph,
|
|
140
|
+
Spacer,
|
|
141
|
+
Spacings,
|
|
142
|
+
StatusBadge,
|
|
143
|
+
SubmitButton,
|
|
144
|
+
type ButtonProps,
|
|
145
|
+
type InputFieldProps,
|
|
146
|
+
} from "@marwes-ui/react"
|
|
147
|
+
|
|
148
|
+
const primaryAction: ButtonProps = {
|
|
149
|
+
variant: ButtonVariant.primary,
|
|
150
|
+
size: ButtonSize.md,
|
|
151
|
+
children: "Create project",
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const emailField: InputFieldProps = {
|
|
155
|
+
label: "Email",
|
|
156
|
+
helperText: "Used for project notifications.",
|
|
157
|
+
input: {
|
|
158
|
+
type: "email",
|
|
159
|
+
placeholder: "you@example.com",
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function ProjectPanel() {
|
|
164
|
+
return (
|
|
165
|
+
<Card title="Project setup">
|
|
166
|
+
<StatusBadge variant={BadgeVariant.success}>Ready</StatusBadge>
|
|
167
|
+
<Spacer spacing={Spacings.sp16} />
|
|
168
|
+
<H1 size="h2">Launch workspace</H1>
|
|
169
|
+
<Paragraph size="md">
|
|
170
|
+
Components share theme tokens, typed variants, spacing, and semantic metadata.
|
|
171
|
+
</Paragraph>
|
|
172
|
+
<InputField {...emailField} />
|
|
173
|
+
<Spacer spacing={Spacings.sp24} />
|
|
174
|
+
<Button {...primaryAction} />
|
|
175
|
+
<SubmitButton>Save changes</SubmitButton>
|
|
176
|
+
</Card>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Available Components
|
|
182
|
+
|
|
37
183
|
Provider and hooks:
|
|
38
184
|
- `MarwesProvider`
|
|
39
185
|
- `useTheme`
|
|
40
|
-
- `
|
|
186
|
+
- `useToast`
|
|
41
187
|
|
|
42
|
-
|
|
188
|
+
Actions and buttons:
|
|
43
189
|
- `Button`
|
|
44
|
-
- `
|
|
190
|
+
- `PrimaryButton`, `SecondaryButton`, `TextButton`, `SuccessButton`
|
|
191
|
+
- `SubmitButton`, `CancelButton`, `CreateButton`, `DestructiveButton`
|
|
192
|
+
- `LinkButton`, `SaveButton`, `ConfirmButton`, `VerifyButton`
|
|
193
|
+
- `EditButton`, `CloseButton`, `RefreshButton`
|
|
194
|
+
- `UploadButton`, `DownloadButton`, `CopyButton`
|
|
195
|
+
- `SearchButton`, `FilterButton`, `SortButton`, `DropdownButton`
|
|
196
|
+
|
|
197
|
+
Forms and inputs:
|
|
198
|
+
- `Input`, `Textarea`, `Select`, `RichText`, `InputOtp`
|
|
199
|
+
- `InputField`, `TextareaField`, `SelectField`, `RichTextField`
|
|
200
|
+
- `DropdownField`, `SearchField`, `PasswordField`, `EmailField`
|
|
201
|
+
- `DateOfBirthField`, `ZipCodeField`, `PhoneField`, `URLField`, `CurrencyField`
|
|
202
|
+
- `Checkbox`, `CheckboxField`, `CheckboxGroupField`
|
|
203
|
+
- `Radio`, `RadioGroupField`, `YesNoRadioGroup`, `RatingRadioGroup`, `OptionRadioGroup`
|
|
204
|
+
- `Switch`, `SwitchField`, `FeatureToggle`, `PreferenceSwitch`, `PermissionSwitch`
|
|
205
|
+
- `Slider`, `SliderField`, `VolumeSlider`, `BrightnessSlider`, `RadiusSlider`
|
|
206
|
+
|
|
207
|
+
Content and layout:
|
|
208
|
+
- `Card`, `ProductCard`, `ProfileCard`, `StatCard`
|
|
209
|
+
- `H1`, `H2`, `H3`, `Paragraph`
|
|
210
|
+
- `Spacer`, `Spacing`, `Divider`
|
|
45
211
|
- `Icon`
|
|
46
|
-
- `
|
|
47
|
-
- `H1`, `H2`, `H3`
|
|
48
|
-
- `Paragraph`
|
|
212
|
+
- `Avatar`, `AvatarBadge`, `AvatarGroup`, `ProfileAvatar`, `PresenceAvatar`, `TeamAvatarGroup`
|
|
49
213
|
|
|
50
|
-
|
|
51
|
-
- `
|
|
214
|
+
Feedback and overlays:
|
|
215
|
+
- `Badge`, `BadgeGroup`, `StatusBadge`, `PriorityBadge`, `NotificationBadge`
|
|
216
|
+
- `Spinner`, `ButtonSpinner`, `EmptyStateSpinner`
|
|
217
|
+
- `Toast`, `ToastContainer`, `ToastProvider`
|
|
218
|
+
- `SuccessToast`, `ErrorToast`, `WarningToast`, `InfoToast`
|
|
219
|
+
- `Tooltip`, `TooltipGroup`
|
|
220
|
+
- `Dialog`, `DialogModal`, `ConfirmDialog`, `DestructiveDialog`, `InfoDialog`
|
|
221
|
+
- `Accordion`, `AccordionField`, `FAQAccordion`, `SettingsAccordion`, `SectionsAccordion`
|
|
222
|
+
- `Tab`, `TabGroup`, `TabPanel`, `NavigationTabs`, `ContentTabs`, `SettingsTabs`
|
|
52
223
|
|
|
53
|
-
|
|
54
|
-
- `
|
|
224
|
+
Typed tokens and helpers:
|
|
225
|
+
- `ThemeInput`, `ThemeMode`, `Density`, `ToneName`
|
|
226
|
+
- `mwAvailableFonts`, `mwGoogleFontFamilies`, `mwFontFallbacks`, `createFontStack`
|
|
227
|
+
- `mwThemeVars`, `mwThemeVarNames`, `mwStyledTheme`, `mwVar`
|
|
228
|
+
- `ButtonVariant`, `ButtonSize`, `ButtonAction`
|
|
229
|
+
- `BadgeVariant`, `AvatarSize`, `AvatarType`, `SwitchSize`, `IconName`, `Spacings`
|
|
55
230
|
|
|
56
|
-
##
|
|
57
|
-
- Treat `@marwes-ui/core` as source of truth for behavior and a11y.
|
|
58
|
-
- Apply RenderKit fields explicitly.
|
|
59
|
-
- Keep component wrappers thin and predictable.
|
|
60
|
-
- Do not hardcode Figma design values in adapters.
|
|
231
|
+
## Theme In Seconds
|
|
61
232
|
|
|
62
|
-
|
|
63
|
-
- `src/provider/*` - provider + hooks
|
|
64
|
-
- `src/components/*` - React wrappers around core recipes
|
|
65
|
-
- `src/index.ts` - public exports
|
|
233
|
+
First edition is the default. Pass `theme` only when a brand or design file needs to change the baseline.
|
|
66
234
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- `../../docs/FIGMA_TO_MARWES.md`
|
|
235
|
+
```tsx
|
|
236
|
+
import { MarwesProvider, mwAvailableFonts } from "@marwes-ui/react"
|
|
70
237
|
|
|
71
|
-
|
|
238
|
+
const brandTheme = {
|
|
239
|
+
color: {
|
|
240
|
+
primary: "#2457FF",
|
|
241
|
+
danger: "#D90429",
|
|
242
|
+
success: "#15803D",
|
|
243
|
+
warning: "#D97706",
|
|
244
|
+
background: "#F8FAFC",
|
|
245
|
+
surface: "#FFFFFF",
|
|
246
|
+
surfaceElevated: "#FFFFFF",
|
|
247
|
+
text: "#111827",
|
|
248
|
+
textMuted: "#4B5563",
|
|
249
|
+
border: "#D1D5DB",
|
|
250
|
+
focus: "#2457FF",
|
|
251
|
+
},
|
|
252
|
+
font: {
|
|
253
|
+
primary: mwAvailableFonts.Poppins,
|
|
254
|
+
secondary: mwAvailableFonts.Lora,
|
|
255
|
+
},
|
|
256
|
+
ui: {
|
|
257
|
+
radius: 10,
|
|
258
|
+
density: "comfortable",
|
|
259
|
+
},
|
|
260
|
+
}
|
|
72
261
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
262
|
+
export function App() {
|
|
263
|
+
return (
|
|
264
|
+
<MarwesProvider theme={brandTheme}>
|
|
265
|
+
<AppShell />
|
|
266
|
+
</MarwesProvider>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
```
|
|
77
270
|
|
|
78
|
-
|
|
271
|
+
The provider resolves `ThemeInput` into `--mw-*` CSS variables. Preset CSS consumes those variables across the full component system.
|
|
79
272
|
|
|
273
|
+
## Custom Styling Tokens
|
|
274
|
+
|
|
275
|
+
`MarwesProvider` resolves `ThemeInput` into `--mw-*` CSS variables. Marwes components and preset CSS consume those variables automatically. The custom styling token helpers let app-owned styles use the same provider-scoped values instead of hard-coding colors, spacing, radius, typography, or duplicated `var(...)` strings.
|
|
276
|
+
|
|
277
|
+
Use the helpers by purpose:
|
|
278
|
+
|
|
279
|
+
- `mwThemeVars` is the default styling helper. It returns CSS `var(...)` references for styled-components, Emotion, vanilla-extract, inline style values, and config files.
|
|
280
|
+
- `mwThemeVarNames` returns raw custom property names for assigning provider-scoped overrides in style objects or tooling.
|
|
281
|
+
- `mwVar()` wraps custom or advanced `--mw-*` names when the named token object does not cover a specialized case.
|
|
282
|
+
- `mwStyledTheme` is a plain object that mirrors `mwThemeVars` for styled-components or Emotion `ThemeProvider` usage.
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
import type { ReactNode } from "react"
|
|
286
|
+
import { mwStyledTheme, mwThemeVarNames, mwThemeVars, mwVar } from "@marwes-ui/react"
|
|
287
|
+
import styled, { ThemeProvider } from "styled-components"
|
|
288
|
+
|
|
289
|
+
const Panel = styled.section`
|
|
290
|
+
padding: ${mwThemeVars.spacing.sp24};
|
|
291
|
+
color: ${mwThemeVars.color.text};
|
|
292
|
+
background: ${mwThemeVars.color.surface};
|
|
293
|
+
border-radius: ${mwThemeVars.ui.radius};
|
|
294
|
+
`
|
|
295
|
+
|
|
296
|
+
const InlinePanel = () => (
|
|
297
|
+
<div
|
|
298
|
+
style={{
|
|
299
|
+
[mwThemeVarNames.color.focus]: "#FF00AA",
|
|
300
|
+
color: mwThemeVars.color.text,
|
|
301
|
+
outlineColor: mwVar("--mw-color-focus", "#2457FF"),
|
|
302
|
+
}}
|
|
303
|
+
/>
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
const AppTheme = ({ children }: { children: ReactNode }) => (
|
|
307
|
+
<ThemeProvider theme={mwStyledTheme}>{children}</ThemeProvider>
|
|
308
|
+
)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Plain CSS and CSS Modules do not need a JavaScript bridge:
|
|
312
|
+
|
|
313
|
+
```css
|
|
314
|
+
.panel {
|
|
315
|
+
padding: var(--mw-spacing-sp-24);
|
|
316
|
+
color: var(--mw-color-text);
|
|
317
|
+
border-radius: var(--mw-ui-radius);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
This enables React apps to keep custom panels, local layouts, third-party CSS-in-JS components, 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.
|
|
322
|
+
|
|
323
|
+
Keep the APIs separate:
|
|
324
|
+
- Use `Spacings.sp24` for Marwes spacing props such as `<Spacer spacing={Spacings.sp24} />`.
|
|
325
|
+
- Use `mwThemeVars.spacing.sp24` for custom CSS values.
|
|
326
|
+
- Use `mwThemeVarNames.spacing.sp24` when assigning or inspecting a CSS custom property name.
|
|
327
|
+
- Use `useTheme()` when React logic needs resolved runtime values such as `"#2457FF"`.
|
|
328
|
+
|
|
329
|
+
## Google Fonts DX
|
|
330
|
+
|
|
331
|
+
Most Google Font use cases only need `mwAvailableFonts`; no `fontLoading` prop is needed.
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
import { MarwesProvider, mwAvailableFonts } from "@marwes-ui/react"
|
|
335
|
+
|
|
336
|
+
<MarwesProvider
|
|
337
|
+
theme={{
|
|
338
|
+
font: {
|
|
339
|
+
primary: mwAvailableFonts.Poppins,
|
|
340
|
+
secondary: mwAvailableFonts.Lora,
|
|
341
|
+
},
|
|
342
|
+
}}
|
|
343
|
+
>
|
|
344
|
+
<App />
|
|
345
|
+
</MarwesProvider>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
For self-hosted or licensed fonts, use `BrandSans`, `BrandSerif`, `BrandMono`, or `createFontStack()`.
|
|
349
|
+
|
|
350
|
+
## Semantic Buttons
|
|
351
|
+
|
|
352
|
+
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.
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
import {
|
|
356
|
+
CancelButton,
|
|
357
|
+
CreateButton,
|
|
358
|
+
DestructiveButton,
|
|
359
|
+
SubmitButton,
|
|
360
|
+
} from "@marwes-ui/react"
|
|
361
|
+
|
|
362
|
+
<CancelButton>Cancel</CancelButton>
|
|
363
|
+
<CreateButton>Create</CreateButton>
|
|
364
|
+
<SubmitButton>Save</SubmitButton>
|
|
365
|
+
<DestructiveButton>Delete</DestructiveButton>
|
|
80
366
|
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
367
|
+
|
|
368
|
+
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:
|
|
369
|
+
|
|
370
|
+
```html
|
|
371
|
+
<button
|
|
372
|
+
data-component="button"
|
|
373
|
+
data-purpose="destructive"
|
|
374
|
+
data-action="delete"
|
|
375
|
+
data-destructive="true"
|
|
376
|
+
data-confirmation-required="true"
|
|
377
|
+
>
|
|
378
|
+
Delete
|
|
379
|
+
</button>
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
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.
|
|
383
|
+
|
|
384
|
+
Use raw `Button` props when you intentionally need a custom combination.
|
|
385
|
+
|
|
386
|
+
```tsx
|
|
387
|
+
import { Button, ButtonAction, ButtonSize, ButtonVariant } from "@marwes-ui/react"
|
|
388
|
+
|
|
389
|
+
<Button
|
|
390
|
+
action={ButtonAction.submit}
|
|
391
|
+
size={ButtonSize.md}
|
|
392
|
+
variant={ButtonVariant.primary}
|
|
393
|
+
>
|
|
394
|
+
Submit
|
|
395
|
+
</Button>
|
|
93
396
|
```
|
|
94
397
|
|
|
95
|
-
|
|
398
|
+
## Why It Is Accessible
|
|
399
|
+
|
|
400
|
+
Marwes React components are accessible because the adapter renders a shared core contract, not because each component hand-rolls ARIA in isolation.
|
|
401
|
+
|
|
402
|
+
- Core recipes produce typed `a11y` output for roles, labels, described-by wiring, invalid state, disabled state, and semantic metadata.
|
|
403
|
+
- React components prefer native DOM controls first: `button`, `input`, `select`, `textarea`, `hr`, and standard form wiring.
|
|
404
|
+
- Field components connect visible labels, helper text, and errors through `id`, `htmlFor`, `aria-describedby`, `aria-invalid`, and polite error announcements.
|
|
405
|
+
- 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.
|
|
406
|
+
- Storybook accessibility smoke checks run through the Storybook a11y addon for the promoted React families, and shared contract tests keep React aligned with Vue.
|
|
407
|
+
|
|
408
|
+
Example:
|
|
409
|
+
|
|
410
|
+
```tsx
|
|
411
|
+
import { InputField } from "@marwes-ui/react"
|
|
412
|
+
|
|
413
|
+
<InputField
|
|
414
|
+
label="Email"
|
|
415
|
+
helperText="Used for receipts."
|
|
416
|
+
error="Enter a valid email."
|
|
417
|
+
input={{ type: "email", placeholder: "you@example.com" }}
|
|
418
|
+
/>
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
That contract resolves to DOM wiring like:
|
|
422
|
+
|
|
423
|
+
```html
|
|
424
|
+
<label for="email">Email</label>
|
|
425
|
+
<input
|
|
426
|
+
id="email"
|
|
427
|
+
type="email"
|
|
428
|
+
aria-describedby="email-helper email-error"
|
|
429
|
+
aria-invalid="true"
|
|
430
|
+
>
|
|
431
|
+
<p id="email-helper">Used for receipts.</p>
|
|
432
|
+
<p id="email-error" aria-live="polite">Enter a valid email.</p>
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
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 React adapter.
|
|
436
|
+
|
|
437
|
+
## Package Boundaries
|
|
438
|
+
|
|
439
|
+
- `@marwes-ui/core` owns recipes, theme resolution, a11y mapping, and semantic metadata.
|
|
440
|
+
- `@marwes-ui/presets` owns first edition CSS.
|
|
441
|
+
- `@marwes-ui/react` owns React rendering and provider behavior.
|
|
442
|
+
|
|
443
|
+
## Scripts
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
pnpm --filter @marwes-ui/react build
|
|
447
|
+
pnpm --filter @marwes-ui/react typecheck
|
|
448
|
+
pnpm --filter @marwes-ui/react test
|
|
449
|
+
```
|
|
96
450
|
|
|
97
451
|
## Related Docs
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
452
|
+
|
|
453
|
+
- [Docs index](https://github.com/niklas-westman/marwes/tree/main/docs)
|
|
454
|
+
- [Architecture](https://github.com/niklas-westman/marwes/blob/main/docs/reference/architecture.md)
|
|
455
|
+
- [Figma to Marwes](https://github.com/niklas-westman/marwes/blob/main/docs/guides/figma-to-marwes.md)
|