Introduction to Master UI
Understand how native markup, component contracts, themes and optional behaviors fit together before installing Master UI.
On this page
Master UI gives HTML interfaces a shared set of component classes, design tokens and documented states. You write native markup, such as <button class="btn">, and use the same classes in a server template, React, Svelte or Vue. The package supplies CSS, contracts for the markup and scripts for patterns that need them.
Use it when you want to keep control of your markup while sharing component appearance and behavior across a project. Start with the default moonstone and obsidian theme, retune its tokens, or take a component's source into your project. Master UI is experimental: pin the installed version and review upgrades.
#A contract and an implementation
A contract describes the markup a stylesheet understands: classes, elements, state attributes and public custom properties. It gives people and coding agents the same vocabulary to use and a way to check common mistakes. The implementation supplies the actual appearance and behavior.
Markup, contract and implementation
The contract joins markup to its implementation. The button's btn class names the same interface in HTML and CSS. A contract also describes elements, state attributes and public properties. Theme changes replace material values while those names stay usable. For patterns that need a script, input changes attributes and CSS draws the resulting state.
| Part | What it provides | How you use it |
|---|---|---|
| Contract | A JSON description beside each stylesheet, including a correct example | Read the installed contract before writing markup |
| Implementation | The stylesheet that draws the contract | Adjust tokens, a theme or your own copy |
| Behavior | A reference script for patterns that need keyboard, value or focus handling | Attach it directly or through a supplied framework component |
| Check | Static diagnostics for markup the checker can read | Run it after edits and in CI, then verify the rendered interface |
The contract is closed: there is no btn-outline unless a contract says so, and the checker reports one that is written anyway. The implementation is open: retune it with tokens, replace its material with a theme, or take a component's source into your project. Token and theme changes keep the contract intact. If you own a component and change its structure or states, update its local contract and consumers together. Working with AI covers what an agent reads and runs.
#How it fits together
| Layer | Owner | Example |
|---|---|---|
| Design tokens | Master CSS preset and @master/ui/theme.css | --color-surface-raised, --shadow-raised |
| Component styles | @master/ui | .btn, .btn-primary, .input |
| Layout classes | @master/ui | .page, .shell, .auto-grid, .form |
| Local adjustments | Your markup | w-full, mt-md, px-lg |
Component classes live in the components layer and utilities in the later utilities layer, so a utility on one instance always wins without !important or selector tricks:
<button type="button" class="btn btn-primary w-full">Save changes</button>#Components, layouts, blocks and pages
The library comes in four tiers, from a part to a whole screen. What separates them is not size. It is whether there is knowledge worth putting behind a class.
| Tier | What it is | How it is published |
|---|---|---|
| Components | A part with a material, a state, a behavior of the platform, or a geometry its pieces share | Semantic classes in @master/ui |
| Layouts | How the room inside a region is arranged: a stack, an auto grid, a bento, masonry, a column a figure breaks out of | Utilities, and a layout class where utilities cannot say it. They are listed with the components |
| Blocks | One stretch of a page with a purpose: a hero, a pricing table, a footer | Markup to copy: components and utilities, composed |
| Pages | A whole screen: where its regions go and what scrolls | Markup to copy, on one of two frames, page and shell |
A thing earns a class when it has at least one of four kinds of knowledge:
- A material. A face, a contour, a shadow or a corner that must sit on the material ladder.
- A state. Hover, focus, chosen, open, disabled or invalid, read from a native attribute or from ARIA.
- A platform behavior. Anchor positioning, a scroll-state query, an entrance in the top layer: what a utility cannot write.
- A geometry its pieces share. A corner that nests by an inset, a child whose size follows its holder's.
A button combines material, states and geometry. A footer composed of links in columns needs no new component class: it is a block built with utilities. Three rules follow.
- A component does not own the page. It never sets where it stands, unless standing there is its behavior: a navbar sticks, a dialog enters the top layer.
- A layout does not own a material. It decides regions and scrolling; the faces in those regions are sheets, trays and cards.
- The bar for a class is knowledge, not repetition. Markup that repeats is copied, not named.
A few layouts hold something utilities cannot say, such as a height in dvh, a phone's safe area, a container threshold or a list of grid tracks. Those are the layout classes: the two frames, page and shell, and auto-grid, bento, masonry, breakout, sticky-stack and form.
#Principles
- Native elements first. Components use the element that already has the right semantics and keep its native behavior where possible. Where a pattern has no native element, the stylesheet draws its states from ARIA and a small script moves them.
- The platform before script. What a browser can now do by itself, a component does not reimplement. See the platform below.
- Light, not decoration. Raised faces catch a line of light and settle on short shadows that fade; wells sink below their surroundings. State is depth, not color, and edges are soft. See Default theme.
- Tokens, not hard-coded values. Surfaces, lines, elevation, radii and motion come from theme tokens, and a test fails a component that writes a color of its own; override a token once to retune every component.
- Small public vocabulary. A base class, a few modifier classes such as
btn-primaryandbtn-sm, and documented custom properties such as--btn-height. - Light and dark from one stylesheet. Semantic tokens resolve with
light-dark(), following the page's color scheme.
#The platform
Native elements handle the work they already support: a checkbox stores a checked value, a select participates in a form, and a modal dialog makes the page behind it inert. Master UI styles their attributes and states.
The stylesheet still has a browser baseline. Features such as cascade layers, nesting and light-dark() are required; optional features such as anchor positioning and continuous corners enhance a working baseline. Some fallbacks need application code: a browser without invoker commands needs a click handler that calls dialog.showModal().
Read Browser support for the baseline, enhancement fallbacks and how to test the features your application uses. Component pages name the relevant fallback beside their examples.
#Patterns that need a script
A few patterns have no native element yet: a combobox, a command palette, a calendar, a tree walked with the arrow keys, a context menu, an OTP field that moves on by itself, a splitter dragged between two panes, tabs that switch panels in place. Master UI still gives them one stylesheet each. The division of work is the same every time:
- The stylesheet draws every state from ARIA:
aria-selectedfor the active option,aria-checkedfor a chosen one,aria-expandedfor an open branch,aria-current="date"for today,aria-orientationfor the way a splitter runs. It also places and animates the surface, and derives what it can, such as an empty state, with:has(). - A script handles input and updates state. The package ships reference behaviors, tested against the keys their contracts list. They update native and ARIA attributes; a pointer position or another value CSS cannot derive may be passed through a custom property. CSS owns the material and placement rules.
- The container is native wherever one exists. A command palette is a
<dialog>opened bycommand="show-modal"; a date picker's calendar sits in apopover; a tree without script is nested<details>.
Markup written against these attributes is the contract, so it keeps working whatever renders it: a template, a framework component, or an agent.
#In React, Svelte and Vue
Most of the library is the same in a framework as on a plain page: a class on a native element, written in the framework's own syntax. A <Button> that only renames btn btn-primary adds nothing, and hides the class from the checker.
A framework component exists only where the framework owns something: a value the application reads or sets, rows rendered from data, ids that join one part to another, or a lifetime. Examples include the patterns above, a dialog opened from state, a toast queue and a field with its label and error. Each renders the contract's markup and attaches the behavior, which stays in the package; the component itself is source you copy into the project with npx master-ui add combobox. React, Svelte and Vue lists them.
#What this alpha publishes
The contracts, the stylesheets, the behaviors, a second theme, the checker, a registry of source to copy, and the React, Svelte and Vue components for the patterns that hold state. Framework components ship as source in @master/ui, rather than separate bindings packages. Shared interaction logic stays in the behaviors; each framework component handles its own rendering and lifetime. Stability says what is public and what may change.
Continue with Installation, then choose a component or browse the components.