@marwes-ui/core
v1.3.0
Published
Framework-agnostic Marwes design system recipes, theme utilities, accessibility contracts, and typed RenderKit metadata.
Readme
Framework-agnostic Marwes design system recipes, theme utilities, accessibility contracts, and typed RenderKit metadata.
Marwes Design System - Core
The framework-agnostic engine behind Marwes themes, recipes, accessibility, and semantic metadata.
Pure TypeScript • No React/Vue dependency • Typed RenderKit • Shared ThemeInput • AI-readable contracts
Documentation • React Storybook • Vue Storybook • GitHub
Why Use It
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.
Core gives every adapter the same:
ThemeInputcontract- resolved theme model
- CSS variable generation
- component recipes
- accessibility mapping
- semantic
data-*metadata - typed enums and token helpers
Package Map
Core is the contract layer, not the normal app entry point.
| Package | Use it when |
| --- | --- |
| @marwes-ui/react | You are building a React app. |
| @marwes-ui/vue | You are building a Vue app. |
| @marwes-ui/core | You need framework-agnostic recipes, theme utilities, accessibility contracts, or adapter/tooling APIs. |
| @marwes-ui/presets | You need standalone preset CSS or preset theme exports. |
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.
Install
pnpm add @marwes-ui/coreTheme Engine
import { mwAvailableFonts, resolveThemeInput, themeToCSSVars } from "@marwes-ui/core"
const theme = resolveThemeInput({
color: {
primary: "#2457FF",
background: "#F8FAFC",
surface: "#FFFFFF",
text: "#111827",
border: "#D1D5DB",
focus: "#2457FF",
},
font: {
primary: mwAvailableFonts.Poppins,
secondary: mwAvailableFonts.Lora,
},
ui: {
radius: 10,
density: "comfortable",
},
})
const cssVars = themeToCSSVars(theme)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.
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.
import { resolveThemeInput, ThemeMode, type ThemeInput } from "@marwes-ui/core"
const themeByMode = {
[ThemeMode.light]: { color: { primary: "#2457FF" } },
[ThemeMode.dark]: { color: { primary: "#8BA2FF", background: "#0B1020", text: "#F8FAFC" } },
} satisfies Record<ThemeMode, ThemeInput>
const darkBrandTheme = resolveThemeInput({
mode: ThemeMode.dark,
...themeByMode[ThemeMode.dark],
})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.
Light And Dark Mode Contract
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.
import { resolveThemeInput, themeToCSSVars, ThemeMode } from "@marwes-ui/core"
function resolveMode(mode: ThemeMode) {
const theme = resolveThemeInput({ mode })
return {
className: `mw-theme--${theme.mode}`,
cssVars: themeToCSSVars(theme),
}
}
resolveMode(ThemeMode.dark)
// {
// className: "mw-theme--dark",
// cssVars: { "--mw-color-background": "#141414", ... }
// }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.
SSR Theme Rules
SSR adapters can use the same core theme engine to emit light and dark CSS variable rules before hydration. These helpers are pure string utilities; React and Vue expose framework-specific SSR helpers on top.
import { ThemeMode, resolveThemeInput, themeModesToCSSRules } from "@marwes-ui/core"
const css = themeModesToCSSRules({
light: resolveThemeInput({ mode: ThemeMode.light }),
dark: resolveThemeInput({ mode: ThemeMode.dark }),
rootTarget: "html",
rootAttribute: "class",
})The generated rules target [data-marwes-theme][data-marwes-mode="light"] and [data-marwes-theme][data-marwes-mode="dark"]. When rootTarget is provided, matching html.light / html.dark or data-attribute selectors are included so a pre-hydration script can switch the resolved mode before provider roots hydrate.
Theme Variables
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.
Use these helpers when custom styling needs to stay connected to the active provider theme:
mwThemeVarsis the default custom styling API. It returns CSSvar(...)references such as"var(--mw-spacing-sp-24)".mwThemeVarNamesreturns raw custom property names such as"--mw-spacing-sp-24"for assignment, inspection, tests, and bridge packages.mwVar()wraps a custom--mw-*property name invar(...), with optional fallback support.mwStyledThememirrorsmwThemeVarsas a plain object for styled-components and Emotion theme providers.
import { mwStyledTheme, mwThemeVarNames, mwThemeVars, mwVar } from "@marwes-ui/core"
mwThemeVars.spacing.sp24 // "var(--mw-spacing-sp-24)"
mwThemeVars.color.text // "var(--mw-color-text)"
mwThemeVars.color.primary.base // "var(--mw-color-primary-base)"
mwThemeVars.ui.radius // "var(--mw-ui-radius)"
mwThemeVarNames.spacing.sp24 // "--mw-spacing-sp-24"
mwVar("--mw-color-text", "#141414") // "var(--mw-color-text, #141414)"
mwStyledTheme.spacing.sp24 // "var(--mw-spacing-sp-24)"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.
Keep the APIs separate:
Spacings.sp24returns"sp-24"for Marwes component props.mwThemeVars.spacing.sp24returns"var(--mw-spacing-sp-24)"for custom styling.mwThemeVarNames.spacing.sp24returns"--mw-spacing-sp-24"when code needs the property name itself.themeToCSSVars()maps resolved theme values into custom property declarations.- Adapter
useTheme()hooks return resolved runtime values for logic and inspection.
Recipe Engine
Core recipes return a typed RenderKit object instead of framework elements. Adapters map that object to React, Vue, or future renderers.
RenderKit includes:
tagclassNamevarsa11y- optional data attributes and component-specific control fields
Available Component Contracts
Core currently powers these component families:
- Buttons and semantic button purposes
- Inputs, textareas, selects, rich text, OTP, and field wrappers
- Checkbox and radio families
- Switches and sliders
- Cards, typography, icons, avatars, dividers, and spacing
- Badges and contextual badge variants
- Toasts, tooltips, dialogs, tabs, accordions, and spinners
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.
Accessibility Contract
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.
That contract covers:
- native-first semantics for controls that should stay native
- label and description wiring for fields and grouped controls
- invalid, disabled, selected, expanded, checked, and busy state mapping
- coordinated widget roles such as dialog, tablist, tab, tabpanel, tooltip, status, and alert
- stable
data-*metadata for purpose components and agent-readable intent
Example: field helpers generate the ids that adapters use to connect helper text and errors to the control:
import { buildInputFieldA11yIds } from "@marwes-ui/core"
buildInputFieldA11yIds({
id: "email",
hasHelperText: true,
hasError: true,
})
// {
// helperTextId: "email-helper",
// errorId: "email-error",
// describedBy: "email-helper email-error"
// }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.
Semantic Metadata
Purpose components use the core semantic registry to emit stable metadata. That makes components easier for tests, audits, and AI agents to reason about.
import { createPurposeSemanticAttributes } from "@marwes-ui/core"
createPurposeSemanticAttributes("destructive")
// {
// "data-purpose": "destructive",
// "data-action": "delete",
// "data-destructive": "true",
// "data-confirmation-required": "true"
// }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.
Public API Highlights
Theme:
resolveThemeInputthemeToCSSVarsthemeToCSSRulethemeModesToCSSRulescreateMarwesThemeScriptcreateMarwesThemeStylemwThemeVarsmwThemeVarNamesmwStyledThememwVarlightThemeDefaultsdarkThemeDefaultsmwAvailableFontsmwFontFallbacksmwGoogleFontFamiliescreateFontStack
Recipe and semantic helpers:
- component recipe functions such as
createButtonRecipe,createInputRecipe,checkboxRecipe,radioRecipe,createDialogRecipe,createToastRecipe - accessibility helpers for supported families
- semantic builders and validators
Types and tokens:
Theme,ThemeInput,ThemeMode,ResolvedThemeColorRole,SecondaryColorRole,ColorInputButtonVariant,ButtonSize,ButtonActionBadgeVariant,AvatarSize,AvatarType,SwitchSize,IconName,Spacings
Package Boundaries
- Core has no React, Vue, DOM, or CSS runtime dependency.
- Presets own static CSS and the default visual layer.
- React and Vue own rendering, provider lifecycle, and framework APIs.
Scripts
pnpm --filter @marwes-ui/core build
pnpm --filter @marwes-ui/core typecheck
pnpm --filter @marwes-ui/core test