Getting started

What to install, what to import, and the handful of rules an app has to follow.

ground is a Svelte 5 component library: accessible primitives, Tailwind v4 semantic tokens, light and dark from the same names. It is internal and resolved through npm workspaces — not published, and with no build step. Consumers compile the .svelte source directly through their own Vite toolchain.

Requirements

Peer dependencies: svelte ^5.27 and tailwindcss ^4.0.

Installing

Declare it in the app’s package.json at the same version as the package itself. Workspaces resolves it to a symlink, so there is nothing to install or build separately.

{
  "dependencies": {
    "ground": "^0.70.1"
  }
}

Importing the tokens

The app’s app.css is exactly one line, and nothing else:

@import 'ground/theme.css';

That file is the single source for the palette, the type scale, the spacing track, the focus rule and the global colour transition. Change it once and every app updates.

Importing components

Either the barrel or a single component — both resolve to the same file, so pick whichever reads better at the call site.

<script>
  import { Button, Card, CardHeader, CardTitle } from 'ground';
  // or
  import { Button } from 'ground/button';
</script>

<Card>
  <CardHeader>
    <CardTitle>Payout schedule</CardTitle>
  </CardHeader>
</Card>

Composables work the same way. Call the factory — the class behind it never leaves its module, so new should not appear in app code.

<script>
  import { AppShell, createTheme } from 'ground';

  const theme = createTheme();
</script>

<AppShell appName="Admin" {theme} nav={[]}>
  <!-- routed page content -->
</AppShell>

The rules an app has to follow

  • Never hardcode a colour, and never override a token from app code. Reference the role: bg-card, text-muted, bg-selected. The one exception is merchant-supplied branding, which is content rather than chrome.
  • Never add focus or transition classes. Both are owned globally by theme.css: one :focus-visible rule that draws inside the box, and one 200ms colour transition on every interactive element. A component that adds its own gets two of each.
  • Any list with search, filtering, sorting or pagination is a DataTable. The raw Table primitives are for static tables — a handful of rows with no controls.
  • A field never names a colour. bg-field surface-field, and the surface underneath decides what that resolves to. Setting a background on a field from the call site is both wrong and, since surface-field paints over it, inert.
  • Read Patterns before building a page. It is the distilled rulebook — borders, shadows, selected and hover states, table and popover shape, sizing — behind every screen already built on ground.

Types

Props are documented as JSDoc in the source, which is what this site reads to build its API tables and what your editor shows on hover. There is no tsconfig or checkJs step in the package, so they are documentation rather than a type checker — do not rely on them to catch a mismatch.