Quick Start

Get started with roi-ui components in minutes

Prerequisites

roi-ui components are built on top of Base UI. You'll need to install Base UI first to use these components.

Installation

1. Install Base UI

npm install @base-ui/react

2. Set up portals

Add a portal container to your root layout:

layout.tsx
<body>
<div className="root">
  {children}
</div>
</body>

3. Add global styles

Copy and paste the global CSS variables into your globals.css file:

globals.css
:root {
    --radius: 0.6rem;
    --radius-lg: 1rem;

    --background: oklch(0.9757 0.002 220);
    --foreground: oklch(0.35 0.008 220);
    --title: oklch(0.12 0.01 220);
    --title-hover: oklch(0.08 0.012 220);
    --card: oklch(1 0 0);
    --card-foreground: oklch(0.18 0.005 220);
    --popover: oklch(1 0 0);
    --popover-foreground: oklch(0.18 0.005 220);
    --dialog-overlay: oklch(0 0 0 / 0.4);
    --dialog-z: 100;

    --primary: oklch(0.1855 0.0326 233.92);
    --primary-foreground: oklch(0.99 0.002 220);

    --secondary: oklch(0.9283 0.0024 228.93);
    --secondary-foreground: oklch(0.3149 0.006 220);

    --muted: oklch(0.9771 0 228.93);
    --muted-foreground: oklch(0.6878 0.0367 271.46);

    --accent: oklch(0.9457 0.01 248.76);
    --accent-foreground: oklch(0.32 0.015 280);

    --border: oklch(0.9166 0.003 220);
    --input: oklch(0.98 0.002 220);
    --ring: oklch(0.42 0.05 240);

    --surface: oklch(0.985 0.002 220);
    --surface-foreground: oklch(0.38 0.006 220);

    --shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.02);
    --shadow-lg: 0 10px 15px -3px oklch(0 0 0 / 0.05), 0 4px 6px -2px oklch(0 0 0 / 0.02);

    --linenumber: oklch(0.7604 0 0 / 60%);

    --destructive: oklch(0.6553 0.1374 15.66);
    --destructive-foreground: oklch(0.9808 0.009 23.81);
    --success: oklch(0.722 0.1946 146.7);
    --success-foreground: oklch(0.9505 0.0792 149.64);
    --warning: oklch(0.8782 0.0981 74.65);
    --warning-foreground: oklch(0.5784 0.105 81.04);

    --info: oklch(0.7091 0.15 264.71);
    --info-foreground: oklch(0.7091 0.15 264.71);

    --selection-bg: oklch(0.1855 0.0326 233.92);
    --selection-text: oklch(0.8985 0.0098 252.82);

    --chart1: oklch(0.5759 0.1503 264.31);
    --chart2: oklch(0.4819 0.1788 263.5);

    --easeOut: cubic-bezier(0, 0.958, 0.858, 1.024);
}

[data-theme="dark"] {
    --background: oklch(0.1704 0.0106 284.91);
    --foreground: oklch(0.94 0.003 220);
    --text: oklch(0.82 0.005 220);
    --title: oklch(0.96 0.002 220);
    --title-hover: oklch(0.98 0.001 220);
    --card: oklch(0.1955 0.0126 284.91);

    --card-foreground: oklch(0.94 0.003 220);
    --popover: oklch(0.13 0.012 240);
    --popover-foreground: oklch(0.94 0.003 220);

    --primary: oklch(0.8985 0.0098 252.82);
    --primary-foreground: oklch(0.08 0.008 240);

    --secondary: oklch(0.2762 0.0243 274.47);
    --secondary-foreground: oklch(0.88 0.004 220);

    --muted: oklch(0.2455 0.0126 284.91);
    --muted-foreground: oklch(0.5247 0.0228 279.03);

    --accent: oklch(0.2612 0.015 280);
    --accent-foreground: oklch(0.85 0.008 280);

    --border: oklch(0.2949 0.0143 274.47);
    --input: oklch(0.2329 0.0143 274.47);
    --ring: oklch(0.78 0.08 240);

    --surface: oklch(0.12 0.01 240);
    --surface-foreground: oklch(0.78 0.005 220);

    --shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.15);
    --shadow-lg: 0 10px 15px -3px oklch(0 0 0 / 0.25), 0 4px 6px -2px oklch(0 0 0 / 0.15);

    --linenumber: oklch(0.7604 0 0 / 30%);
    --destructive: oklch(0.5219 0.1656 18.34);
    --destructive-foreground: oklch(0.8988 0.0447 17.32);
    --success: oklch(0.8325 0.2177 151.25);
    --success-foreground: oklch(0.5299 0.147 149.1059);
    --warning: oklch(0.829 0.105 81.04);
    --warning-foreground: oklch(0.5893 0.1083 78.11);
    --info: oklch(0.7091 0.15 264.71);
    --info-foreground: oklch(0.7091 0.15 264.71);
    --selection-bg: oklch(0.8985 0.0098 252.82);
    --selection-text: oklch(0.1855 0.0326 233.92);

    --chart1: oklch(0.6487 0.1856 264.84);
    --chart2: oklch(0.4187 0.2556 267.84);
}

* {
    box-sizing: border-box;
    padding: 0;
    margin: 0;
}

*:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

html,
body {
    max-width: 100vw;
    overflow-x: hidden;
    background-color: var(--background);
    color: var(--foreground);
}

a {
    color: inherit;
    text-decoration: none;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    color: var(--title);
    font-weight: 600;
    line-height: 1.2;
}

p {
    margin-top: 1.5rem;
    margin-bottom: 1.5rem;
}

/* Special margin for TSX file copy instruction - targets paragraph immediately before ComponentSource wrapper with TSX */
p:has(+ div .container[data-tsx="true"]) {
    margin-top: 48px;
    margin-bottom: 16px;
}

input,
button,
textarea,
select {
    font: inherit;
}

.root {
    isolation: isolate;
}

.main-content {
    padding-top: var(--header-height);
}

@media (max-width: 1023px) {
    .main-content {
        padding-top: var(--header-height);
    }
}

::selection {
    background-color: var(--selection-bg);
    color: var(--selection-text);
}

::-moz-selection {
    background-color: var(--selection-bg);
    color: var(--selection-text);
}

Your first component

Once you have Base UI set up, you can start using roi-ui components. Here's a simple button example:

button-demo.tsx
import { Button } from "@/components/ui/button/button";

export default function ButtonDemo() {
  return <Button>Button</Button>;
}