@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 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
- # @marwes-ui/react
1
+ <div align="center">
2
2
 
3
- React adapter for Marwes core recipes.
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
- ## Responsibilities
6
- - Provide React components and provider APIs.
7
- - Resolve theme from context and call core recipes.
8
- - Apply typed RenderKit output to React elements.
5
+ <br>
6
+ <br>
9
7
 
10
- ## Non-Responsibilities
11
- - No component logic duplication from core.
12
- - No preset CSS ownership.
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/core @marwes-ui/react @marwes-ui/presets
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 { MarwesProvider, Button, Input, Checkbox } from "@marwes-ui/react";
22
- import { firstEdition } from "@marwes-ui/presets";
23
- import "@marwes-ui/presets/firstEdition/styles.css";
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 preset={firstEdition} theme={{ color: { primary: "#5B8CFF" } }}>
28
- <Button tone="primary">Save</Button>
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
- ## Current Exports
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
- - `useSystem`
186
+ - `useToast`
41
187
 
42
- Atoms:
188
+ Actions and buttons:
43
189
  - `Button`
44
- - `Input`
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
- - `Checkbox`
47
- - `H1`, `H2`, `H3`
48
- - `Paragraph`
212
+ - `Avatar`, `AvatarBadge`, `AvatarGroup`, `ProfileAvatar`, `PresenceAvatar`, `TeamAvatarGroup`
49
213
 
50
- Molecules:
51
- - `CheckboxField`
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
- Utilities:
54
- - `useRenderKitDebug`
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
- ## Adapter Rules
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
- ## Package Structure
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
- ## Figma Mapping
68
- If design changes originate in Figma, align token/state mapping with:
69
- - `../../docs/FIGMA_TO_MARWES.md`
235
+ ```tsx
236
+ import { MarwesProvider, mwAvailableFonts } from "@marwes-ui/react"
70
237
 
71
- Most Figma changes should land in core theme/recipes and presets CSS first, then flow into React automatically.
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
- ## Scripts
74
- - `pnpm --filter @marwes-ui/react dev`
75
- - `pnpm --filter @marwes-ui/react build`
76
- - `pnpm --filter @marwes-ui/react typecheck`
262
+ export function App() {
263
+ return (
264
+ <MarwesProvider theme={brandTheme}>
265
+ <AppShell />
266
+ </MarwesProvider>
267
+ )
268
+ }
269
+ ```
77
270
 
78
- ## Package Structure
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
- dist/ # Build output (npm published files)
82
- ├── index.js # ESM bundle
83
- ├── index.d.ts # TypeScript declarations
84
- └── index.js.map # Source maps
85
-
86
- src/ # Source files (not published)
87
- ├── components/ # React components
88
- ├── hooks/ # React hooks
89
- └── provider/ # Context provider
90
-
91
- tsconfig.json # TypeScript config (noEmit: true)
92
- tsup.config.ts # Build configuration
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
- **Important**: The `dist/` folder is generated by `pnpm build` and should never be edited directly. Only the `dist/` folder is published to npm (configured in `package.json` `files` field).
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
- - `../../docs/PROJECT.md`
99
- - `../../docs/ARCHITECTURE.md`
100
- - `../../docs/ENGINEERING.md`
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)