Frameworks

One stylesheet import, then components. Each framework below is what the packages actually export.

// app/layout.tsx
import "@noorddev/vlak-react/css";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>{children}</body>
    </html>
  );
}

// app/page.tsx: a Server Component. Dialog is "use client" already.
import { Button, Dialog } from "@noorddev/vlak-react";

App router. Import the stylesheet once in the root layout. Components that hold state carry "use client" inside the package, so a Server Component can render them directly. Static export works; this site is one:

// next.config.mjs
export default { output: "export", trailingSlash: true };

For the dark scheme without a flash, set data-theme in an inline script before hydration and add suppressHydrationWarning on <html>. See Theming. Writing your own StyleX leaves in Next is covered in StyleX.

// src/main.tsx
import "@noorddev/vlak-react/css";
import { createRoot } from "react-dom/client";
import { App } from "./App";

createRoot(document.getElementById("root")!).render(<App />);

Nothing to configure. Vite bundles the CSS import and keeps the package's ESM modules. Add @stylexjs/unplugin only if you write leaves of your own.

// app/root.tsx (React Router 7 framework mode, Remix)
import "@noorddev/vlak-react/css";

// or, with the links() convention:
import vlakStyles from "@noorddev/vlak-react/css?url";
export const links = () => [{ rel: "stylesheet", href: vlakStyles }];

Side-effect CSS imports work in React Router 7 framework mode and Remix on Vite. Projects on the links() convention use the ?url import instead. Dialog, Sheet, and Drawer render a native <dialog> and open after hydration, so server-rendered markup stays inert until React attaches.

---
// src/layouts/Base.astro
import "@noorddev/vlak-react/css";
import { Button } from "@noorddev/vlak-react";
import { Dialog } from "@noorddev/vlak-react";
---
<html lang="en">
  <body>
    <Button>Static, no JavaScript shipped</Button>
    <Dialog client:load open={false}>…</Dialog>
    <slot />
  </body>
</html>

Import the stylesheet in a layout. Stateless components (Button, Badge, Card, Table) render to HTML with no client JavaScript. Interactive ones (Dialog, Select, Tabs, the menus) need a client directive; client:load is the simplest.

<!doctype html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />
  </head>
  <body>
    <div class="rs-field">
      <label class="rs-field-label" for="name">Name</label>
      <input class="rs-input rs-input-full" id="name" />
    </div>
    <button class="rs-btn-primary">Save</button>
  </body>
</html>

Install @noorddev/vlak, or copy vlak.css and the Inter files with npx @noorddev/vlak-cli init. Every component page lists its markup and classes; the behaviour that needs no script (details, dialog, popover, scroll snap) comes from the browser. Set data-theme="dark" on <html> for the dark scheme.

SpecifierContains
@noorddev/vlak-reactEvery component, cx, rs, the icon set, the token vars
@noorddev/vlak-react/cssThe precompiled stylesheet, 42 KB, layered
@noorddev/vlak-react/components/<name>One component module
@noorddev/vlak-react/tokens.stylexvlak and mq for your own StyleX leaves
@noorddev/vlakvlakTokens, the registry, the radius helpers
@noorddev/vlak/cssvlak.css, 84 KB, every rs-* class
@noorddev/vlak/css/components.cssComponents only, for pages that already load the React stylesheet
@noorddev/vlak/css/tokens.cssThe custom properties alone
@noorddev/vlak/css/components/<name>.cssOne component's CSS
@noorddev/vlak/tokensThe tokens as JSON
@noorddev/vlak/propsEvery export's props as JSON