Frankfurt
Live for 41 days
p50 latency 18 ms
Style 03 · motiv
Clean cards, pill buttons, a green you can swap in one line. The release screen below is plain server-rendered HTML: no client JavaScript at all, because nothing on it needs any.
Built 6 minutes ago from main · 128 checks passed
Live for 41 days
Live for 12 days
Rolling out
import { Button, buttonClassName, Card, CardBody, CardFooter, CardHeader } from '@driif/motiv';
const regions = [
{ name: 'Frankfurt', status: 'Live for 41 days', latency: '18 ms' },
{ name: 'Zürich', status: 'Live for 12 days', latency: '22 ms' },
{ name: 'Kyiv', status: 'Rolling out', latency: '31 ms' },
];
export function MotivRelease() {
return (
<div className="site-release">
<Card as="section" padding="lg" className="site-release__lead" aria-label="Release 2.4">
<CardHeader
title="Release 2.4 is ready"
titleAs="h2"
description="Built 6 minutes ago from main · 128 checks passed"
/>
<CardBody>
Ship it to every region at once, or start with one and watch the numbers first.
</CardBody>
<CardFooter>
<a href="#button" className={buttonClassName({ variant: 'ghost' })}>
Changelog
</a>
<Button variant="outline">Canary first</Button>
<Button>Deploy everywhere</Button>
</CardFooter>
</Card>
{regions.map((region) => (
<Card key={region.name} as="article" variant="outlined" padding="sm">
<CardHeader title={region.name} titleAs="h3" description={region.status} />
<CardBody>p50 latency {region.latency}</CardBody>
</Card>
))}
</div>
);
}Getting started
React 19 and react-dom are the only peers.
pnpm add @driif/motivAdd base.css too if you want a minimal reset.
import '@driif/motiv/styles.css';Named exports, no provider. Works in an Astro page as is.
import '@driif/motiv/styles.css';
import { Button, Card, CardBody, CardHeader } from '@driif/motiv';
export function Welcome() {
return (
<Card>
<CardHeader title="Welcome back" />
<CardBody>
<Button>Continue</Button>
</CardBody>
</Card>
);
}All library CSS sits in @layer motiv.*, so your unlayered rules win without !important.
:root {
--motiv-color-primary: #2563eb;
--motiv-color-on-primary: #ffffff;
--motiv-radius: 0.25rem;
}Five variants, three sizes, a loading state. Links borrow its look.
import { Button, buttonClassName } from '@driif/motiv';
export function ButtonDemo() {
return (
<>
<Button>Deploy</Button>
<Button variant="secondary">Schedule</Button>
<Button variant="outline">Preview</Button>
<Button variant="danger" size="sm">
Delete
</Button>
<Button loading>Saving</Button>
<a href="#card" className={buttonClassName({ variant: 'ghost' })}>
A link, dressed up
</a>
</>
);
}A surface with a header, a body and a footer for actions.
Active this week
import { Button, Card, CardBody, CardFooter, CardHeader } from '@driif/motiv';
export function CardDemo() {
return (
<Card style={{ maxWidth: '24rem' }}>
<CardHeader title="Users" description="Active this week" />
<CardBody>128 people signed in at least once during the last seven days.</CardBody>
<CardFooter>
<Button variant="outline" size="sm">
Export
</Button>
<Button size="sm">View all</Button>
</CardFooter>
</Card>
);
}The class-name joiner the components use, exported for yours.
className="note"import { Button, cx } from '@driif/motiv';
import { useState } from 'react';
export function CxDemo() {
const [urgent, setUrgent] = useState(false);
const className = cx('note', urgent && 'note--urgent', null);
return (
<>
<Button variant="outline" aria-pressed={urgent} onClick={() => setUrgent(!urgent)}>
Urgent
</Button>
<code>className="{className}"</code>
</>
);
}