@marwes-ui/core 0.0.4 → 1.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.
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,68 +1,277 @@
1
- # @marwes-ui/core
2
-
3
- Framework-agnostic Marwes core: theme system, typed component recipes, and a11y mapping.
4
-
5
- ## Responsibilities
6
- - Define and normalize the `Theme` contract.
7
- - Build component render kits (`tag`, `className`, `vars`, `a11y`).
8
- - Centralize a11y behavior so adapters stay consistent.
9
- - Stay independent from DOM and framework runtime.
10
-
11
- ## Non-Responsibilities
12
- - No React/Vue rendering.
13
- - No preset CSS authoring.
14
- - No runtime styling engine.
15
-
16
- ## Public API
17
- Theme and system:
18
- - `defaultTheme`
19
- - `mergeTheme`
20
- - `normalizeTheme`
21
- - `createSystem`
22
- - Types: `Theme`, `ThemeOverrides`, `Preset`, `System`, `CssVars`
23
-
24
- Atoms (recipes + types):
25
- - `createButtonRecipe`
26
- - `createInputRecipe`
27
- - `checkboxRecipe`
28
- - `createIconRecipe`
29
- - `createHeadingRecipe`
30
- - `createParagraphRecipe`
31
-
32
- Molecules:
33
- - `checkboxFieldRecipe`
34
-
35
- ## RenderKit Contract
36
- Core recipes return typed render data consumed by adapters:
37
- - `tag`: target HTML tag
38
- - `className`: stable `.mw-*` classes
39
- - `vars`: `Record<--mw-*, string>` CSS variables
40
- - `a11y`: explicit typed accessibility fields
41
- - optional control fields per component (for example `blockClick`, `indeterminate`)
42
-
43
- ## File Layout
44
- - `src/theme/*` - theme types/defaults/merge/normalize/system
45
- - `src/components/atoms/*` - atomic component contracts + recipes
46
- - `src/components/molecules/*` - composed component contracts + recipes
47
- - `src/shared/css-vars.ts` - CSS variable type contract
48
-
49
- ## Engineering Rules
50
- - Keep core framework-agnostic.
51
- - Do not use `any`.
52
- - Put accessibility mapping in `*.a11y.ts`.
53
- - Put visual decisions in presets, not in adapters.
54
- - Only emit CSS variables and stable classnames from recipes.
55
-
56
- ## Figma Mapping
57
- When implementing design changes from Figma, map tokens into theme keys and recipe vars as documented in:
58
- - `../../docs/FIGMA_TO_MARWES.md`
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%" style="border-radius: 40px;">
4
+
5
+ <br>
6
+ <br>
7
+
8
+ # Marwes Design System - Core
9
+
10
+ **The framework-agnostic engine behind Marwes themes, recipes, accessibility, and semantic metadata.**
11
+
12
+ Pure TypeScript • No React/Vue dependency • Typed RenderKit • Shared ThemeInput • AI-readable contracts
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
+ Most app teams should install `@marwes-ui/react` or `@marwes-ui/vue`. Install core directly when you are building an adapter, validating design-system contracts, or using Marwes theme and recipe utilities without a framework.
23
+
24
+ Core gives every adapter the same:
25
+
26
+ - `ThemeInput` contract
27
+ - resolved theme model
28
+ - CSS variable generation
29
+ - component recipes
30
+ - accessibility mapping
31
+ - semantic `data-*` metadata
32
+ - typed enums and token helpers
33
+
34
+ ## Package Map
35
+
36
+ Core is the contract layer, not the normal app entry point.
37
+
38
+ | Package | Use it when |
39
+ | --- | --- |
40
+ | `@marwes-ui/react` | You are building a React app. |
41
+ | `@marwes-ui/vue` | You are building a Vue app. |
42
+ | `@marwes-ui/core` | You need framework-agnostic recipes, theme utilities, accessibility contracts, or adapter/tooling APIs. |
43
+ | `@marwes-ui/presets` | You need standalone preset CSS or preset theme exports. |
44
+
45
+ This package is useful for humans and AI agents that need stable contracts without framework rendering: component recipes, theme variable names, semantic attributes, and accessibility mapping all live here.
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pnpm add @marwes-ui/core
51
+ ```
52
+
53
+ ## Theme Engine
54
+
55
+ ```ts
56
+ import { mwAvailableFonts, resolveThemeInput, themeToCSSVars } from "@marwes-ui/core"
57
+
58
+ const theme = resolveThemeInput({
59
+ color: {
60
+ primary: "#2457FF",
61
+ background: "#F8FAFC",
62
+ surface: "#FFFFFF",
63
+ text: "#111827",
64
+ border: "#D1D5DB",
65
+ focus: "#2457FF",
66
+ },
67
+ font: {
68
+ primary: mwAvailableFonts.Poppins,
69
+ secondary: mwAvailableFonts.Lora,
70
+ },
71
+ ui: {
72
+ radius: 10,
73
+ density: "comfortable",
74
+ },
75
+ })
76
+
77
+ const cssVars = themeToCSSVars(theme)
78
+ ```
79
+
80
+ React and Vue providers apply these variables to the provider root. Preset CSS consumes them across buttons, inputs, typography, cards, toasts, overlays, and layout primitives.
81
+
82
+ Marwes is designed to look great from the beginning. `ThemeInput` is intentionally partial: start from the polished defaults, map an existing design library into the tokens you own, and override only those product decisions.
83
+
84
+ ```ts
85
+ import { resolveThemeInput, ThemeMode, type ThemeInput } from "@marwes-ui/core"
86
+
87
+ const themeByMode = {
88
+ [ThemeMode.light]: { color: { primary: "#2457FF" } },
89
+ [ThemeMode.dark]: { color: { primary: "#8BA2FF", background: "#0B1020", text: "#F8FAFC" } },
90
+ } satisfies Record<ThemeMode, ThemeInput>
91
+
92
+ const darkBrandTheme = resolveThemeInput({
93
+ mode: ThemeMode.dark,
94
+ ...themeByMode[ThemeMode.dark],
95
+ })
96
+ ```
97
+
98
+ Every omitted token is filled from the selected light or dark default, so adapters can expose simple light/dark toggles without requiring a full theme object.
99
+
100
+ ### Light And Dark Mode Contract
101
+
102
+ Core owns the runtime `ThemeMode` contract that the React and Vue providers use for `useThemeMode()`. Use `ThemeMode.light` and `ThemeMode.dark` instead of string literals. A mode change resolves a normal theme, swaps the provider-scoped `--mw-*` variables, and keeps the active class aligned as `mw-theme--light` or `mw-theme--dark`.
103
+
104
+ ```ts
105
+ import { resolveThemeInput, themeToCSSVars, ThemeMode } from "@marwes-ui/core"
106
+
107
+ function resolveMode(mode: ThemeMode) {
108
+ const theme = resolveThemeInput({ mode })
109
+
110
+ return {
111
+ className: `mw-theme--${theme.mode}`,
112
+ cssVars: themeToCSSVars(theme),
113
+ }
114
+ }
115
+
116
+ resolveMode(ThemeMode.dark)
117
+ // {
118
+ // className: "mw-theme--dark",
119
+ // cssVars: { "--mw-color-background": "#141414", ... }
120
+ // }
121
+ ```
122
+
123
+ Most apps should use `useThemeMode()` from `@marwes-ui/react` or `@marwes-ui/vue`. Core is the framework-agnostic piece that makes the resolved variables and mode classes consistent across adapters.
124
+
125
+ ## Theme Variables
126
+
127
+ Marwes components are styled by CSS custom properties on the `MarwesProvider` root. The theme variable helpers expose that same token surface to application code, adapters, and tooling without creating a second runtime theme system.
128
+
129
+ Use these helpers when custom styling needs to stay connected to the active provider theme:
130
+
131
+ - `mwThemeVars` is the default custom styling API. It returns CSS `var(...)` references such as `"var(--mw-spacing-sp-24)"`.
132
+ - `mwThemeVarNames` returns raw custom property names such as `"--mw-spacing-sp-24"` for assignment, inspection, tests, and bridge packages.
133
+ - `mwVar()` wraps a custom `--mw-*` property name in `var(...)`, with optional fallback support.
134
+ - `mwStyledTheme` mirrors `mwThemeVars` as a plain object for styled-components and Emotion theme providers.
135
+
136
+ ```ts
137
+ import { mwStyledTheme, mwThemeVarNames, mwThemeVars, mwVar } from "@marwes-ui/core"
138
+
139
+ mwThemeVars.spacing.sp24 // "var(--mw-spacing-sp-24)"
140
+ mwThemeVars.color.text // "var(--mw-color-text)"
141
+ mwThemeVars.color.primary.base // "var(--mw-color-primary-base)"
142
+ mwThemeVars.ui.radius // "var(--mw-ui-radius)"
143
+
144
+ mwThemeVarNames.spacing.sp24 // "--mw-spacing-sp-24"
145
+ mwVar("--mw-color-text", "#141414") // "var(--mw-color-text, #141414)"
146
+ mwStyledTheme.spacing.sp24 // "var(--mw-spacing-sp-24)"
147
+ ```
148
+
149
+ This enables one theme contract across plain CSS, CSS Modules, CSS-in-JS, vanilla-extract, Tailwind-style config files, inline style objects, React, Vue, and future adapters. Because the helpers only expose CSS variable references and names, they remain framework-agnostic and follow any `ThemeInput` resolved by the provider.
150
+
151
+ Keep the APIs separate:
152
+ - `Spacings.sp24` returns `"sp-24"` for Marwes component props.
153
+ - `mwThemeVars.spacing.sp24` returns `"var(--mw-spacing-sp-24)"` for custom styling.
154
+ - `mwThemeVarNames.spacing.sp24` returns `"--mw-spacing-sp-24"` when code needs the property name itself.
155
+ - `themeToCSSVars()` maps resolved theme values into custom property declarations.
156
+ - Adapter `useTheme()` hooks return resolved runtime values for logic and inspection.
157
+
158
+ ## Recipe Engine
159
+
160
+ Core recipes return a typed RenderKit object instead of framework elements. Adapters map that object to React, Vue, or future renderers.
161
+
162
+ RenderKit includes:
163
+ - `tag`
164
+ - `className`
165
+ - `vars`
166
+ - `a11y`
167
+ - optional data attributes and component-specific control fields
168
+
169
+ ## Available Component Contracts
170
+
171
+ Core currently powers these component families:
172
+
173
+ - Buttons and semantic button purposes
174
+ - Inputs, textareas, selects, rich text, OTP, and field wrappers
175
+ - Checkbox and radio families
176
+ - Switches and sliders
177
+ - Cards, typography, icons, avatars, dividers, and spacing
178
+ - Badges and contextual badge variants
179
+ - Toasts, tooltips, dialogs, tabs, accordions, and spinners
180
+
181
+ React and Vue expose the public components. Core exposes the shared recipes, types, enum objects, semantic utilities, and theme helpers that keep those adapters consistent.
182
+
183
+ ## Accessibility Contract
184
+
185
+ Core is where Marwes accessibility is made reusable. Recipes return typed `a11y` output alongside classes, vars, and semantic metadata, so every adapter receives the same source of truth.
186
+
187
+ That contract covers:
188
+
189
+ - native-first semantics for controls that should stay native
190
+ - label and description wiring for fields and grouped controls
191
+ - invalid, disabled, selected, expanded, checked, and busy state mapping
192
+ - coordinated widget roles such as dialog, tablist, tab, tabpanel, tooltip, status, and alert
193
+ - stable `data-*` metadata for purpose components and agent-readable intent
194
+
195
+ Example: field helpers generate the ids that adapters use to connect helper text and errors to the control:
196
+
197
+ ```ts
198
+ import { buildInputFieldA11yIds } from "@marwes-ui/core"
199
+
200
+ buildInputFieldA11yIds({
201
+ id: "email",
202
+ hasHelperText: true,
203
+ hasError: true,
204
+ })
205
+ // {
206
+ // helperTextId: "email-helper",
207
+ // errorId: "email-error",
208
+ // describedBy: "email-helper email-error"
209
+ // }
210
+ ```
211
+
212
+ React and Vue tests run the same shared contracts against their DOM output. Storybook a11y smoke checks then add an axe-powered browser-level signal for the promoted families.
213
+
214
+ ## Semantic Metadata
215
+
216
+ Purpose components use the core semantic registry to emit stable metadata. That makes components easier for tests, audits, and AI agents to reason about.
217
+
218
+ ```ts
219
+ import { createPurposeSemanticAttributes } from "@marwes-ui/core"
220
+
221
+ createPurposeSemanticAttributes("destructive")
222
+ // {
223
+ // "data-purpose": "destructive",
224
+ // "data-action": "delete",
225
+ // "data-destructive": "true",
226
+ // "data-confirmation-required": "true"
227
+ // }
228
+ ```
229
+
230
+ This is intentionally practical. A destructive action can carry `data-destructive="true"` and `data-confirmation-required="true"`, which gives an AI agent or browser automation rule a clear signal to ask before clicking. It also gives tests a stable assertion target that does not depend on button text, color, or visual styling.
231
+
232
+ ## Public API Highlights
233
+
234
+ Theme:
235
+ - `resolveThemeInput`
236
+ - `themeToCSSVars`
237
+ - `mwThemeVars`
238
+ - `mwThemeVarNames`
239
+ - `mwStyledTheme`
240
+ - `mwVar`
241
+ - `lightThemeDefaults`
242
+ - `darkThemeDefaults`
243
+ - `mwAvailableFonts`
244
+ - `mwFontFallbacks`
245
+ - `mwGoogleFontFamilies`
246
+ - `createFontStack`
247
+
248
+ Recipe and semantic helpers:
249
+ - component recipe functions such as `createButtonRecipe`, `createInputRecipe`, `checkboxRecipe`, `radioRecipe`, `createDialogRecipe`, `createToastRecipe`
250
+ - accessibility helpers for supported families
251
+ - semantic builders and validators
252
+
253
+ Types and tokens:
254
+ - `Theme`, `ThemeInput`, `ThemeMode`, `ResolvedTheme`
255
+ - `ColorRole`, `SecondaryColorRole`, `ColorInput`
256
+ - `ButtonVariant`, `ButtonSize`, `ButtonAction`
257
+ - `BadgeVariant`, `AvatarSize`, `AvatarType`, `SwitchSize`, `IconName`, `Spacings`
258
+
259
+ ## Package Boundaries
260
+
261
+ - Core has no React, Vue, DOM, or CSS runtime dependency.
262
+ - Presets own static CSS and the default visual layer.
263
+ - React and Vue own rendering, provider lifecycle, and framework APIs.
59
264
 
60
265
  ## Scripts
61
- - `pnpm --filter @marwes-ui/core dev`
62
- - `pnpm --filter @marwes-ui/core build`
63
- - `pnpm --filter @marwes-ui/core typecheck`
266
+
267
+ ```bash
268
+ pnpm --filter @marwes-ui/core build
269
+ pnpm --filter @marwes-ui/core typecheck
270
+ pnpm --filter @marwes-ui/core test
271
+ ```
64
272
 
65
273
  ## Related Docs
66
- - `../../docs/PROJECT.md`
67
- - `../../docs/ARCHITECTURE.md`
68
- - `../../docs/ENGINEERING.md`
274
+
275
+ - [Docs index](https://github.com/niklas-westman/marwes/tree/main/docs)
276
+ - [Architecture](https://github.com/niklas-westman/marwes/blob/main/docs/reference/architecture.md)
277
+ - [Figma to Marwes](https://github.com/niklas-westman/marwes/blob/main/docs/guides/figma-to-marwes.md)