Getting Started

Welcome to the Axiom Design System.

Axiom is a set of web components that keeps what we loved in the first Material Design and the first Fluent Design: the light that follows the pointer, real shadows, acrylic glass, a press that tilts what you touch, live tiles you can drag, an indicator that stretches as it moves. They were fun, rich and still easy to read, and later systems traded much of that away.

A good design system raises the floor of what a page looks like without lowering the ceiling. The effects share one infrastructure. Surface draws every effect on one shape with one frame scheduler, Shape gives every effect the same outline, and Typed OM reads the design tokens you set as CSS custom properties. The Architecture page shows how it fits together.

The effects stay accessible: the reveal light shows borders only where the pointer is, shadows are only ever black and set by one depth, and reduced motion and forced colours are respected. The story of the project is in this post.

Install the components your app uses.

An app uses the components: install the ones you use. Each of them brings Surface, Shape and Typed OM along as its own dependencies, so you do not install those.

Install

shell
# with npm
npm install @ax-design/tile-group @ax-design/tile @ax-design/navigation @ax-design/scroll @ax-design/scrollbar

# with bun
bun add @ax-design/tile-group @ax-design/tile @ax-design/navigation @ax-design/scroll @ax-design/scrollbar

Register

Every package has a register() that defines its elements. Call it once, before the markup is used:

js
import { register as registerTile } from '@ax-design/tile';
import { register as registerTileGroup } from '@ax-design/tile-group';
import { register as registerNavigation } from '@ax-design/navigation';
import { register as registerScroll } from '@ax-design/scroll';
import { register as registerScrollbar } from '@ax-design/scrollbar';

registerTile();       // also defines <ax-surface>
registerTileGroup();
registerNavigation();
registerScroll();
registerScrollbar();  // <ax-scroll-view> and <ax-scrollbar>

<ax-tile> is not in the tile group package. It comes from @ax-design/tile, so register both.

Markup

A tile has one action, the link or button with slot="action". A tile group lays its children out in cells, like the Start screen, and ax-scroll turns the wheel sideways:

html
<body>
  <ax-scrollbar></ax-scrollbar>

  <ax-navigation>
    <ax-navigation-item>Home</ax-navigation-item>
    <ax-navigation-item>Apps</ax-navigation-item>
    <ax-navigation-item>Games</ax-navigation-item>
  </ax-navigation>

  <ax-scroll style="height: 330px">
    <div style="display: flex; width: max-content">
      <ax-tile-group style="--tile-rows: 4">
        <ax-tile columns="4" rows="2" layers="fill" style="--ax-fill-color: #2b5797">
          <a slot="action" href="/mail">Mail</a>
        </ax-tile>
        <ax-tile columns="2" rows="2" layers="fill" style="--ax-fill-color: #da532c">
          <a slot="action" href="/photos">Photos</a>
        </ax-tile>
        <ax-tile columns="2" rows="2" layers="fill" style="--ax-fill-color: #00aba9">
          <a slot="action" href="/music">Music</a>
        </ax-tile>
      </ax-tile-group>
    </div>
  </ax-scroll>

  <ax-scroll-view style="height: 240px">
    <article>...</article>
  </ax-scroll-view>
</body>

The reveal light on a tile is its own package, Reveal Highlight. The tiles above use layers="fill" only. To add the light, install it and register its layer:

js
import { registerRevealLayer } from '@ax-design/reveal-highlight';

registerRevealLayer();  // before the tiles are used, then layers="fill reveal"

Foundations

Install the foundations when you build a component of your own, or when you want an effect on a Surface: elevation, acrylic, reveal or the tilt of pressure. A Surface is the box every effect draws on. It owns the shape, the tilt and one frame scheduler for the page.

Install

shell
# with npm
npm install @ax-design/surface @ax-design/elevation @ax-design/reveal-highlight

# with bun
bun add @ax-design/surface @ax-design/elevation @ax-design/reveal-highlight

Register

The effects register their layers by name, and the Surface then uses them:

js
import { register } from '@ax-design/surface';
import { registerElevationLayer } from '@ax-design/elevation';
import { registerRevealLayer } from '@ax-design/reveal-highlight';

// The layers first, then the element that uses them.
registerElevationLayer();
registerRevealLayer();
register();

Example

The layers are drawn in the order you list them, and your content is always on top. fill comes with Surface; put elevation first, so the other layers paint over it:

html
<ax-surface layers="elevation fill reveal">
  <button class="my-button">Hello</button>
</ax-surface>
css
ax-surface {
  --ax-radius: 12px;
  --ax-fill-color: white;
  --ax-border-color: #3b4252;
  --ax-tilt-mode: absolute;  /* the surface tilts toward the point you press */
}

Pressure's own element is <ax-pressure> (register() from @ax-design/pressure); on a Surface you get the same tilt with --ax-tilt-mode. To write a layer of your own, see Surface.

Using a CDN

The components, the effects and Pressure ship a script, build/main.js, that exposes a global with a register(). Each script carries its own copy of Surface, so load packages of the same version together. Surface, Shape and Typed OM have no script of their own: use them as modules.

html
<script src="https://unpkg.com/@ax-design/tile/build/main.js"></script>
<script src="https://unpkg.com/@ax-design/tile-group/build/main.js"></script>
<script src="https://unpkg.com/@ax-design/navigation/build/main.js"></script>
<script src="https://unpkg.com/@ax-design/scroll/build/main.js"></script>
<script src="https://unpkg.com/@ax-design/scrollbar/build/main.js"></script>
<script>
  AxTile.register();
  AxTileGroup.register();
  AxNavigation.register();
  AxScroll.register();
  AxScrollbar.register();
</script>

Packages

How the packages depend on each other, layer by layer, is on the Architecture page.