A tile with one action: it shows the press before the action runs, and it can launch its app with the flip of Windows 8.
A tile is the box of the Start screen of Windows 8. It has one action, a link or a button with slot="action". The action covers the whole tile and its text is the name of the tile. Anything else in the tile tilts with it.
A tile with an action is pressed: it tilts and lights up, and the action runs once that press has been seen. A tile with --ax-tile-launch set to flip launches its app instead, with the splash of the app, as Windows 8 does. The Start screen of the lab (/lab/start/) puts tiles and groups together.
ax-tile is an ax-surface from @ax-design/surface, so the layers, shapes and tilt of Surface work on it. Put the tiles in an ax-tile-group from @ax-design/tile-group to lay them out and drag them.
Installation
npm install @ax-design/tileand import it:
import { register } from '@ax-design/tile';
register();If you don't use a bundler, load the script instead:
<script src="https://unpkg.com/@ax-design/tile/build/main.js"></script>
<script>AxTile.register();</script>Press and activation
Press the tile. It tilts and lights up, and the button runs its action after the press has been seen. A link works the same way: it navigates once the press is over, so it does not leave under the finger.
The longest wait is capped by --ax-tile-activation-delay-max. It is none by default, which means no cap; here it is set to a value you can change.
Try it
<ax-tile layers="fill reveal">
<a slot="action" href="/mail">Mail</a>
</ax-tile>.tile {
--ax-tile-activation-delay-max: 400ms;
}Launch with the flip
With --ax-tile-launch set to flip, a click launches the app: the tile turns edge-on and grows to the whole page, and the splash of the app turns to face the viewer. The splash is the content of the splash template, on the color of the tile or the color you set.
This example waits 1.2 seconds, as an app does while it loads, and then the splash goes. Click again once it is gone. The action is a button here, so nothing navigates away.
Try it
<ax-tile class="app" layers="fill reveal">
<a slot="action" href="/mail">Mail</a>
<template slot="splash"><img src="mail-logo.svg" alt="" width="96"></template>
</ax-tile>.app {
--ax-tile-launch: flip;
--ax-tile-splash-color: #0078d7;
--ax-tile-splash-hold: 500ms;
--ax-tile-splash-fade: 167ms;
--ax-tile-splash-timeout: 5000ms;
}tile.addEventListener('tilelaunchactivate', (event) => {
// A promise of your app, settled when its first screen is ready.
event.detail.hold(appReady);
});Routers and the splash
A launch sends the click of the link as a real click, so the router of the page handles it as it handles any link. The splash can wait for the page the link leads to: the tile holds it with hold(promise) from the tilelaunchstart or tilelaunchactivate event, and the promise settles when the page is ready.
tile.launch(ready) runs the same launch from code, whatever --ax-tile-launch says. The first block is that call. The second is a sketch for SvelteKit: it is not tested with the framework, so check it against the version you use. The README has the same sketches for Next.js, React Router and Vue Router.
const ready = fetch('/api/mail/summary').then((r) => r.json());
tile.launch(ready);import { afterNavigate, preloadData } from '$app/navigation';
let arrived = null;
afterNavigate(() => arrived && arrived());
document.addEventListener('tilelaunchstart', (event) => {
const { url, hold } = event.detail;
if (!url) return;
hold(new Promise((resolve) => (arrived = resolve)));
preloadData(url);
});

Accessibility
Keyboard: Tab to the action. Enter presses a link or a button, and Space presses a button; Space does not activate a link, it scrolls the page. The press comes from the center of the tile, and the action runs after the ripple.
Screen readers: The tile sets no role. The link or the button of the action gives the role and the name. With label="hidden" the text is not shown, and the name stays.
High contrast (forced colors): The tile is a surface, so its fill and border follow the forced colors rules of Surface.
Reduced motion: The tilt takes its end state at once. A reveal ripple still plays, and the click waits for it. A flip launch is not played.
Reduced transparency: With the fallback the reveal light is not drawn, and the click runs at once, as it does without any feedback. Choose Fallback in the switch to see it live.
More contrast: Nothing changes by itself. The tile follows the prefers-contrast rules of Surface.
Try it: in Chrome or Edge, open DevTools, then Rendering, and emulate the CSS media features prefers-reduced-motion, forced-colors or prefers-reduced-transparency. Or turn on the setting of your system: Show animations, Contrast themes and Transparency effects on Windows; Reduce motion, Increase contrast and Reduce transparency on macOS.
Try it
import { setAlwaysUseFallback } from '@ax-design/surface';
// Draw every effect with its solid fallback, as when transparency effects are off.
setAlwaysUseFallback(true);Reference
slot="action" attribute
a | button
The link or the button of the tile. It covers the tile, and its text is the name of the tile at the bottom left. A tile without an action is static: it has no press and no keyboard handling.
slot="splash" attribute
template
The content of the splash of an app, shown centered on the splash color while the tile launches.
label attribute
hidden
The name is not shown, but it stays the accessible name of the action.
focus-ring attribute
native, default the focus ring of Surface
The browser draws the keyboard focus outline of the action, instead of the focus ring that follows the shape.
activation attribute
immediate, default delayed
The action runs as soon as it is clicked, with no wait for the press feedback.
--ax-border-width property
<length>, default 2px
On a tile, so that the reveal light has a border. Any style of yours wins.
--ax-tile-activation-delay-max property
<time> | none, default none
The longest wait of a delayed activation, in ms or s. none is no cap.
--ax-tile-launch property
none | flip, default none
flip turns the launch on: the tile turns, grows to the page and shows its splash. Read at each launch.
--ax-tile-launch-duration property
<time>, default 620ms
The length of the launch, from the tile to the whole page. The tile turns in its first 30%; the splash takes the rest.
--ax-tile-launch-easing property
<timing function>, default cubic-bezier(0.17, 0.55, 0.3, 0.95)
The timing function of the growth from the tile to the page. The growth starts after the first 16% of the duration.
--ax-tile-splash-color property
<color>, default the fill color of the tile
The color of the splash. A transparent color gives a black splash.
--ax-tile-splash-hold property
<time>, default 500ms
How long the splash stays once the launch has nothing left to wait for. Without it a fast router would take the splash away the moment the page arrives.
--ax-tile-splash-fade property
<time>, default 167ms
The fade of the splash once the app is ready. Linear.
--ax-tile-splash-timeout property
<time> | none, default 5s
The longest the splash waits for the promises held, or for a full-page navigation. none waits for as long as it takes.
tilelaunchstart event
CustomEvent<{ tile, action, url, hold(promise), preventPrefetch() }>
The click starts a launch. It bubbles and is cancelable: a canceled launch falls back to the delayed activation. url is the href of a link, null for a button.
tilelaunchactivate event
CustomEvent<{ tile, action, url, hold(promise) }>
The flip is over, and the click is sent on. Not cancelable. Hold the splash here while the page is not ready.
tilelaunchend event
CustomEvent<{ tile, action, url, reason: done | restore }>
The launch is over: the splash has gone (done), or the page was restored from the back/forward cache (restore).
hold(promise) method
(promise: Promise<unknown>) => void
In the detail of tilelaunchstart and tilelaunchactivate. Keeps the splash until the promise settles, or until the timeout is over.
preventPrefetch() method
() => void
In the detail of tilelaunchstart. Turns off the default prefetch of a link to the same origin for this launch.
launch(ready?) method
(ready?: Promise<unknown>) => Promise<void>
Runs the launch as a click does, whatever --ax-tile-launch says. ready is held as hold() would hold it. The promise resolves when the splash is gone; it rejects without doing anything when the tile has no action, the action is disabled, or a launch is already running.