Get started
Material Design 3 components for Svelte 5 and Tailwind CSS 4, built on Bits UI.
Install
The package is on both npm and the GitLab package registry. Install it from whichever you
prefer, along with the peer dependencies your project doesn't have yet. It works with svelte 5, @sveltejs/kit 2 or 3, and tailwindcss 4. tailwind-scrollbar and @internationalized/date are peers too.
bun add @noxlovette/material tailwind-scrollbar @internationalized/date
Use npm install or pnpm add with the same package names if you prefer.
Components such as Card and NavbarItem read SvelteKit's $app modules, so the package expects a SvelteKit project.
To install from GitLab instead of npm, point the @noxlovette scope at it in the .npmrc file at your project root first:
@noxlovette:registry=https://gitlab.com/api/v4/projects/87362543/packages/npm/
Set up the CSS
In your Tailwind entry stylesheet, import Tailwind, then the library's styles, then the
themes. The order matters: the library's tokens build on Tailwind's, and the themes override
the tokens. The dark variant has to follow Tailwind's import.
@import 'tailwindcss'; @import '@noxlovette/material/styles'; @import '@noxlovette/material/theme/light'; @import '@noxlovette/material/theme/dark'; @custom-variant dark (&:is(.dark *)); @source '../node_modules/@noxlovette/material/dist'; @plugin 'tailwind-scrollbar';
The @source line is required. Tailwind skips node_modules when it looks
for class names, so without it the components' classes are never generated and they render unstyled.
Adjust the path to where the stylesheet sits relative to node_modules. @plugin registers the scrollbar utilities that the command
palette, theme switcher and sticky panes use.
The styles also add Material Design 3 (M3) spacing tokens next to Tailwind's numeric scale: p-spacing-200 is M3's 16 density-independent pixels (dp), the same as p-4. See Tokens.
The library sets no font family. Define --font-sans in a Tailwind @theme block to use the typeface you want; this site uses Google Sans Flex.
Wrap your app
App goes around everything in your root layout. It sets the surface background, the
tooltip provider and icon loading, and it runs the theme: dynamic color, and the dark class on <html>, set before first paint so the wrong
scheme never flashes.
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { App } from '@noxlovette/material';
import '../app.css';
const { children } = $props();
</script>
<App>
{@render children()}
</App> Keep lang on your <html> element. SvelteKit's template sets lang="en". The type scale reads it to pick line heights for the script.
Use components
Import components from the package root. Props such as padding take named sizes and
are typed, so your editor shows the options:
<script lang="ts">
import { Body, Button, Card, Title } from '@noxlovette/material';
</script>
<Card padding="md">
<Title>Hello world</Title>
<Body>Your first Material card.</Body>
<Button onclick={() => alert('Hello')}>Say hello</Button>
</Card> Style your own markup with the same tokens the components use, such as bg-md-sys-color-surface-container, md-sys-typescale-body-medium and gap-spacing-200.
Icons
Icons are Material Symbols. App loads the font with the icons the components use. Pass
any other icon names your app uses, or they render as plain text:
<App iconProviderProps={{ extraIcons: ['search', 'add', 'settings'] }}> To serve the font yourself and make no request to Google Fonts, set selfHosted,
declare an @font-face for each family you use, and import the class stylesheet. Each
font must be a variable font with the opsz, wght, FILL and GRAD axes.
<App iconProviderProps={{ selfHosted: true }}> @import '@noxlovette/material/styles/icon-font.css';
@font-face {
font-family: 'Material Symbols Rounded';
src: url('/fonts/material-symbols-rounded.woff2') format('woff2');
font-weight: 100 700;
font-display: block;
} Next steps
Set a brand color in Theming, lay out your pages with Layout, and browse every component, with its props and live examples, in Storybook.