Skip to content

Design system

Monochrome, engineered, alive under the finger.

A standalone SvelteKit component library for Anomalia — published as an npm package and consumed by the app like any other dependency. The surface is near-monochrome and close to flat: hairlines and surface steps carry structure, colour carries meaning, and nothing decorates. Everything that happens after you touch it is Material: ripples from the contact point, state layers, emphasized easing, gestures that track the finger 1:1.

Principles

Quiet surfaces

Near-monochrome, close to flat, tight radii. Structure comes from hairlines and surface steps rather than shadow and fill, so a dense screen stays legible instead of loud.

Expressive interaction

Material owns everything after contact: ripples from the touch point, state layers, emphasized easing, and gestures that commit on distance or velocity rather than distance alone.

Colour is information

Ink is the interactive colour; the brand hue is reserved for identity, not controls; status hues appear only when something has a state. That is also what makes every pair clear WCAG without a fight.

Tokens, not overrides

Components read semantic tokens only. Restyle the whole system by redefining one layer — never by fighting a component with a more specific selector.

Install

bash
npm install @anomalia/ui
svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import '@anomalia/ui/styles.css';
  import { ThemeProvider, Toaster } from '@anomalia/ui';

  let { children } = $props();
</script>

<ThemeProvider>
  {@render children()}
  <Toaster />
</ThemeProvider>

Add the exported themeScript to app.html as well — it resolves the theme before first paint, which ThemeProvider alone cannot do (it runs after hydration, one frame too late).

Use

svelte
<script lang="ts">
  import { Button, Card, TextField, toast } from '@anomalia/ui';

  let email = $state('');
</script>

<Card>
  <TextField label="Email" bind:value={email} placeholder="you@studio.com" />
  <Button onclick={() => toast.success({ title: 'Saved' })}>Save</Button>
</Card>

What is in the box

  • Tokens — primitives (palette, type ramp, space, radius, motion) and the semantic layer components actually read, with light and dark resolved from one attribute.
  • Components — buttons, forms, overlays, navigation and feedback, each with its own scoped CSS and no styling dependency.
  • Gesturesuse:ripple, use:press and use:swipe, plus haptics, focus trapping, scroll locking and anchored positioning.
  • Motion — the Material 3 duration and easing scales, mirrored in CSS and JS so a Svelte transition and a CSS transition use the identical curve.
  • Accessibility — contrast enforced at the token layer by an audit that fails the build, full keyboard patterns, and answers for reduced motion, increased contrast, reduced transparency and forced colours.