Frameworks

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

One control contract

The React component and the plain HTML class share the same Vlak stylesheet and native button behavior.

ReactImports the component from @noorddev/vlak-react.
HTMLUses the stable rs-btn-primary class from vlak.css.

No control activated yet. Total activations: 0.

// 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