Get started
Install one package, load one stylesheet, then use whichever layer fits your stack: CSS classes, custom elements or React.
Install
The umbrella package @kyralabs/m3exp-web re-exports every layer. Add
@kyralabs/m3e-tokens as well so your package manager lets you import its icon
stylesheets by path (pnpm only resolves direct dependencies).
npm install @kyralabs/m3exp-web @kyralabs/m3e-tokens
Pick a layer
The three component layers render the same DOM with the same classes, so they share one theme and mix on one page: a static card from CSS classes, a menu element inside it, a React date picker next to it.
| Layer | Package | Use it for |
|---|---|---|
| CSS classes | @kyralabs/m3e-css |
Static components: buttons, cards, lists, chips, text fields, app bars. No JavaScript. |
| Custom elements | @kyralabs/m3e-elements |
Behavior without a framework: menus, dialogs, sheets, pickers, springs and shape morphs. Zero dependencies, one entry per element. |
| React | @kyralabs/m3e-react |
React apps: React Aria for behavior and accessibility, Motion for springs. |
| Tokens | @kyralabs/m3e-tokens |
Always: the CSS custom properties every layer reads, the fonts, Material Symbols and seed themes. |
Load the CSS
One import gives you the fonts, the tokens, the baseline theme and every component class. Icons are a separate stylesheet because the Material Symbols font is large.
@import '@kyralabs/m3exp-web/css/index.css'; /* fonts, tokens, baseline theme, every class */
@import '@kyralabs/m3e-tokens/material-symbols.css'; /* icons */
To ship less CSS, import the token stylesheets and only the components a page shows; each component file also works on its own:
@import '@kyralabs/m3e-tokens/fonts.css';
@import '@kyralabs/m3e-tokens/tokens.css';
@import '@kyralabs/m3e-tokens/theme.css';
@import '@kyralabs/m3e-css/icon.css';
@import '@kyralabs/m3e-css/button.css';
@import '@kyralabs/m3e-css/card.css';
Your first component
The same button in each layer:
<!-- CSS classes: no JavaScript -->
<button type="button" class="m3e-button m3e-button--filled m3e-button--size-m">
<span class="m3e-icon" aria-hidden="true">save</span>
Save
</button>
import { Button, Icon } from '@kyralabs/m3exp-web/react';
<Button variant="filled" size="m" icon={<Icon name="save" />} onPress={save}>
Save
</Button>
The button has no custom element: CSS draws it completely, and React adds the press morph on a spring. Components whose behavior CSS cannot provide, such as menus, have an element:
<button id="more" type="button" class="m3e-icon-button" aria-label="More options">
<span class="m3e-icon" aria-hidden="true">more_vert</span>
</button>
<m3e-menu anchor="more">
<m3e-menu-item value="edit" icon="edit">Edit</m3e-menu-item>
<m3e-menu-item value="share" icon="share">Share</m3e-menu-item>
</m3e-menu>
<script type="module">
import '@kyralabs/m3exp-web/elements'; // registers every element
</script>
@kyralabs/m3exp-web/elements registers every element. To load only the ones a page
uses, install @kyralabs/m3e-elements and import its per-element entries, such as
@kyralabs/m3e-elements/menu.js; the Astro guide
shows how.
Next steps
- Set up a project: React with Vite, Astro or plain HTML.
- Choose a font profile and ship only the icons you use: fonts and icons.
- Generate a theme from your brand color: theming.