Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@import "fumadocs-ui/css/neutral.css";
@import "fumadocs-ui/css/preset.css";
@import "../shared/design-system/styles/variables/index.css";
@import "@openuidev/react-ui/styles/index.css";
@import "@openuidev/react-ui/layered/styles/index.css";

body {
--color-fd-background: var(--openui-background);
Expand Down
6 changes: 4 additions & 2 deletions docs/content/docs/api-reference/react-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import { Copilot, FullScreen, BottomTray } from "@openuidev/react-ui";

### Cascade-layer contract

`@openuidev/react-ui/components.css` wraps every component rule in `@layer openui`. Unlayered consumer CSS overrides OpenUI components without specificity matching or `!important`. See [Chat → Theming](/docs/chat/theming#override-component-styles-with-css) for override patterns, and [Chat → Installation](/docs/chat/installation#3-for-tailwind-v4-set-the-cascade-layer-order) for the Tailwind v4 layer-order setup.
The default stylesheet exports (`./components.css`, `./styles/*`) are **unlayered**. Two parallel exports — `@openuidev/react-ui/layered-components.css` and `@openuidev/react-ui/layered/styles/*` — ship the same rules wrapped in `@layer openui`; with the layered variant, unlayered consumer CSS overrides OpenUI components without specificity matching or `!important`. When using the layered variant, import it from exactly one place in your app — multiple import sites under chunk-splitting bundlers can register the `openui` layer in the wrong order. See [Chat → Theming](/docs/chat/theming#override-component-styles-with-css) for override patterns, and [Chat → Installation](/docs/chat/installation#3-optional-opt-into-cascade-layered-styles) for the Tailwind v4 layer-order setup.

Browser support: Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+ (all baseline from March 2022).
`./defaults.css` and the `ThemeProvider` runtime style injection are always unlayered, so theme tokens override component defaults in both modes.

Browser support for the layered variant: Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+ (all baseline from March 2022); older browsers drop the `@layer` block entirely. The default unlayered styles have no such floor.

## Layout components

Expand Down
28 changes: 24 additions & 4 deletions docs/content/docs/chat/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,39 @@ export default function RootLayout({ children }: { children: React.ReactNode })

These imports give you the default chat layout styling and theme tokens.

## 3. (For Tailwind v4) Set the cascade-layer order
## 3. (Optional) Opt into cascade-layered styles

OpenUI's component styles live in `@layer openui`. If your app uses Tailwind v4, declare the layer order in your global stylesheet so `openui` sits above Tailwind's reset (`base`) but below `components` and `utilities`:
The default import above ships **unlayered** CSS — overrides work via normal specificity, exactly as in earlier versions. If you prefer your plain app CSS to win over OpenUI styles without specificity matching (recommended for Tailwind v4 apps), import the **layered** variant instead.

Import it from a **single place** — your global stylesheet, not the root layout — so its `@layer openui` block registers in a predictable order:

```css
/* app/globals.css */
@import "@openuidev/react-ui/layered-components.css";
```

```tsx
// app/layout.tsx — import only globals.css (drop the react-ui CSS imports from step 2)
import "./globals.css";
```

With Tailwind v4, declare the layer order at the top of `globals.css`, **before** the layered import, so `openui` sits above Tailwind's reset (`base`) but below `components` and `utilities`:

```css
/* app/globals.css */
@layer theme, base, openui, components, utilities;
@import "tailwindcss";
@import "@openuidev/react-ui/layered-components.css";
```

This places OpenUI above Tailwind's Preflight (so its element resets don't override component styles) while keeping Tailwind utilities like `bg-red-500` winning over OpenUI. Without this declaration, the cascade order is bundler-dependent and `openui` may end up declared *after* `utilities`, which prevents utility overrides from taking effect.
This places OpenUI above Tailwind's Preflight (so its element resets don't override component styles) while keeping Tailwind utilities like `bg-red-500` winning over OpenUI. The layered import must come **after** the `@layer` declaration: loading it first registers the `openui` layer before the order statement and locks the wrong order (and without the declaration at all, the cascade order is bundler-dependent — `openui` may end up after `utilities`, preventing utility overrides).

Two rules when using the layered variant:

- Import OpenUI CSS from **exactly one place** (your root layout or global stylesheet, not both) — multiple import sites under chunk-splitting bundlers like Turbopack can lock the wrong layer order.
- Wrap your own global resets in a layer below `openui` (e.g. `@layer base { ... }`) — unlayered resets beat all layered styles regardless of specificity.

Tailwind v3, plain CSS, CSS Modules, and CSS-in-JS need no configuration — their styles are unlayered and beat anything in `@layer openui` automatically.
The layered variant requires cascade-layer support: Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+ (March 2022 baseline). Tailwind v3, plain CSS, CSS Modules, and CSS-in-JS need no configuration with either variant.

See [`@openuidev/react-ui`](/docs/api-reference/react-ui#cascade-layer-contract) for the full styling integration contract.

Expand Down
8 changes: 5 additions & 3 deletions docs/content/docs/chat/theming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,19 @@ import { FullScreen } from "@openuidev/react-ui";

## Override component styles with CSS

OpenUI's component styles live in `@layer openui`. Any unlayered consumer CSS overrides them without `!important` or matching specificity:
By default OpenUI ships **unlayered** styles: override them like any other stylesheet — with a selector of equal or higher specificity loaded after the component CSS.

If you import the **layered** variant (`@openuidev/react-ui/layered-components.css`), all component styles live in `@layer openui`, and any unlayered consumer CSS overrides them without `!important` or specificity matching:

```css
.openui-button-base-primary {
background: hotpink;
}
```

For Tailwind v4 apps, declare `@layer theme, base, openui, components, utilities;` ahead of `@import "tailwindcss";` in your global stylesheet (see [Installation](/docs/chat/installation#3-for-tailwind-v4-set-the-cascade-layer-order)) so utility classes also override OpenUI styles. CSS Modules, CSS-in-JS, and Tailwind v3 utilities emit unlayered CSS and override OpenUI automatically with no configuration.
For Tailwind v4 apps using the layered variant, declare `@layer theme, base, openui, components, utilities;` ahead of `@import "tailwindcss";` in your global stylesheet (see [Installation](/docs/chat/installation#3-optional-opt-into-cascade-layered-styles)) so utility classes also override OpenUI styles. CSS Modules, CSS-in-JS, and Tailwind v3 utilities emit unlayered CSS and override the layered variant automatically with no configuration.

Browser support: Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+ (all baseline from March 2022).
Browser support for the layered variant: Chrome 99+, Firefox 97+, Safari 15.4+, Edge 99+ (all baseline from March 2022). The default unlayered styles have no browser floor.

<div className="grid md:grid-cols-2 gap-6 my-6">
<div>
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/openui-lang/standard-library.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { openuiLibrary } from "@openuidev/react-ui";
<Renderer library={openuiLibrary} response={streamedText} isStreaming={isStreaming} />;
```

The compiled stylesheet wraps component rules in `@layer openui` so your own CSS overrides them without specificity matching. See [Chat → Theming](/docs/chat/theming#override-component-styles-with-css) for the override contract, or [Chat → Installation](/docs/chat/installation#3-for-tailwind-v4-set-the-cascade-layer-order) for the Tailwind v4 setup.
The `components.css` import ships unlayered styles, so you can override them via normal CSS specificity. If you prefer cascade-layered styles, import `@openuidev/react-ui/layered-components.css` instead — see [Chat → Installation](/docs/chat/installation#3-optional-opt-into-cascade-layered-styles) for details.

## Generate prompt

Expand Down
27 changes: 9 additions & 18 deletions examples/hands-on-table-chat/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
"use client";

import "@openuidev/react-ui/components.css";
import { openAIMessageFormat, openAIAdapter } from "@openuidev/react-headless";
import { Copilot } from "@openuidev/react-ui";
import { spreadsheetLibrary } from "@/lib/spreadsheet-library";
import { TableProvider, useTableContext } from "./TableContext";
import { useState, useEffect, useCallback } from "react";
import { openAIAdapter, openAIMessageFormat } from "@openuidev/react-headless";
import { Copilot } from "@openuidev/react-ui";
import "@openuidev/react-ui/layered-components.css";
import { MessageSquare, PanelRightClose } from "lucide-react";
import dynamic from "next/dynamic";
import { useCallback, useEffect, useState } from "react";
import { TableProvider, useTableContext } from "./TableContext";

const PersistentSpreadsheet = dynamic(
() => import("./PersistentSpreadsheet"),
{ ssr: false }
);
const PersistentSpreadsheet = dynamic(() => import("./PersistentSpreadsheet"), { ssr: false });

function ChatPanel({ onClose }: { onClose: () => void }) {
const { threadId } = useTableContext();
Expand Down Expand Up @@ -42,16 +39,14 @@ function ChatPanel({ onClose }: { onClose: () => void }) {
agentName="Spreadsheet AI"
welcomeMessage={{
title: "Spreadsheet AI",
description:
"I can help you analyze, visualize, and modify your product revenue data.",
description: "I can help you analyze, visualize, and modify your product revenue data.",
}}
conversationStarters={{
variant: "long",
options: [
{
displayText: "Chart revenue by quarter",
prompt:
"Show me a bar chart comparing Q1 through Q4 revenue for all products.",
prompt: "Show me a bar chart comparing Q1 through Q4 revenue for all products.",
},
{
displayText: "Add Vision Pro to the lineup",
Expand Down Expand Up @@ -102,11 +97,7 @@ export default function Home() {
{chatOpen && <ChatPanel onClose={closeChat} />}

{!chatOpen && (
<button
onClick={() => setChatOpen(true)}
className="chat-fab"
aria-label="Open chat"
>
<button onClick={() => setChatOpen(true)} className="chat-fab" aria-label="Open chat">
<MessageSquare size={22} />
<span className="chat-fab__label">AI Chat</span>
</button>
Expand Down
2 changes: 1 addition & 1 deletion examples/mastra-chat/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import "@openuidev/react-ui/components.css";
import "@openuidev/react-ui/layered-components.css";

import { useTheme } from "@/hooks/use-system-theme";
import { agUIAdapter } from "@openuidev/react-headless";
Expand Down
2 changes: 1 addition & 1 deletion examples/multi-agent-chat/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
@layer theme, base, openui, components, utilities;
@import "tailwindcss";
@import "@openuidev/react-ui/components.css";
@import "@openuidev/react-ui/layered-components.css";
29 changes: 9 additions & 20 deletions examples/multi-agent-chat/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
"use client";

import "@openuidev/react-ui/components.css";

import { useChat } from "@ai-sdk/react";
import { useRef, useEffect, useState } from "react";
import { useTheme } from "@/hooks/use-system-theme";
import { useThreads } from "@/hooks/use-threads";
import { AssistantMessage } from "@/components/assistant-message";
import { ChatHeader } from "@/components/chat-header";
import { ChatInput } from "@/components/chat-input";
import { ConversationStarters } from "@/components/conversation-starters";
import { AssistantMessage } from "@/components/assistant-message";
import { UserMessage } from "@/components/user-message";
import { ThinkingIndicator } from "@/components/thinking-indicator";
import { Sidebar } from "@/components/sidebar";
import { ThinkingIndicator } from "@/components/thinking-indicator";
import { UserMessage } from "@/components/user-message";
import { useTheme } from "@/hooks/use-system-theme";
import { useThreads } from "@/hooks/use-threads";
import { useChat } from "@ai-sdk/react";
import { useEffect, useRef, useState } from "react";

export default function Page() {
useTheme();
Expand Down Expand Up @@ -88,10 +86,7 @@ export default function Page() {
sidebarOpen ? "md:ml-[280px]" : "ml-0"
}`}
>
<ChatHeader
isSidebarOpen={sidebarOpen}
onToggleSidebar={() => setSidebarOpen((o) => !o)}
/>
<ChatHeader isSidebarOpen={sidebarOpen} onToggleSidebar={() => setSidebarOpen((o) => !o)} />

<div ref={scrollRef} className="flex-1 overflow-y-auto">
{isEmpty ? (
Expand All @@ -100,13 +95,7 @@ export default function Page() {
<div className="max-w-3xl mx-auto px-4 py-6 space-y-6">
{messages.map((m) => {
if (m.role === "assistant") {
return (
<AssistantMessage
key={m.id}
message={m}
onSend={handleSend}
/>
);
return <AssistantMessage key={m.id} message={m} onSend={handleSend} />;
}
if (m.role === "user") {
return <UserMessage key={m.id} message={m} />;
Expand Down
16 changes: 6 additions & 10 deletions examples/openui-artifact-demo/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client";
import "@openuidev/react-ui/components.css";
import "@openuidev/react-ui/layered-components.css";

import { useTheme } from "@/hooks/use-system-theme";
import { artifactDemoLibrary } from "@/library";
import { openAIAdapter, openAIMessageFormat } from "@openuidev/react-headless";
import { FullScreen } from "@openuidev/react-ui";
import { artifactDemoLibrary } from "@/library";

export default function Page() {
const mode = useTheme();
Expand All @@ -31,23 +31,19 @@ export default function Page() {
options: [
{
displayText: "React login form",
prompt:
"Build me a React login form with email and password validation",
prompt: "Build me a React login form with email and password validation",
},
{
displayText: "Python REST API",
prompt:
"Create a FastAPI REST API with CRUD endpoints for a todo app",
prompt: "Create a FastAPI REST API with CRUD endpoints for a todo app",
},
{
displayText: "CSS animation",
prompt:
"Write a CSS animation for a bouncing loading indicator",
prompt: "Write a CSS animation for a bouncing loading indicator",
},
{
displayText: "SQL schema",
prompt:
"Design a SQL schema for a blog with users, posts, and comments",
prompt: "Design a SQL schema for a blog with users, posts, and comments",
},
],
}}
Expand Down
2 changes: 1 addition & 1 deletion examples/openui-chat/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import "@openuidev/react-ui/components.css";
import "@openuidev/react-ui/layered-components.css";

import { useTheme } from "@/hooks/use-system-theme";
import { openAIAdapter, openAIMessageFormat } from "@openuidev/react-headless";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import type { Starter } from "@/starters";
import type { Library } from "@openuidev/react-lang";
import "@openuidev/react-ui/components.css";
import "@openuidev/react-ui/layered-components.css";
import { useRef, useState } from "react";
import { DashboardProvider, useDashboard } from "./context";
import { ConversationPanel } from "./ConversationPanel";
Expand Down
2 changes: 1 addition & 1 deletion examples/shadcn-chat/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import "@openuidev/react-ui/components.css";
import "@openuidev/react-ui/layered-components.css";

import { useTheme } from "@/hooks/use-system-theme";
import { shadcnChatLibrary } from "@/lib/shadcn-genui";
Expand Down
2 changes: 1 addition & 1 deletion examples/supabase-chat/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import "@openuidev/react-ui/components.css";
import "@openuidev/react-ui/layered-components.css";

import { openAIAdapter, openAIMessageFormat } from "@openuidev/react-headless";
import { FullScreen } from "@openuidev/react-ui";
Expand Down
2 changes: 1 addition & 1 deletion examples/vercel-ai-chat/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
@layer theme, base, openui, components, utilities;
@import "tailwindcss";
@import "@openuidev/react-ui/components.css";
@import "@openuidev/react-ui/layered-components.css";
21 changes: 8 additions & 13 deletions examples/vercel-ai-chat/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
"use client";

import "@openuidev/react-ui/components.css";

import { useChat } from "@ai-sdk/react";
import { useRef, useEffect, useState } from "react";
import { useTheme } from "@/hooks/use-system-theme";
import { useThreads } from "@/hooks/use-threads";
import { AssistantMessage } from "@/components/assistant-message";
import { ChatHeader } from "@/components/chat-header";
import { ChatInput } from "@/components/chat-input";
import { ConversationStarters } from "@/components/conversation-starters";
import { AssistantMessage } from "@/components/assistant-message";
import { UserMessage } from "@/components/user-message";
import { ThinkingIndicator } from "@/components/thinking-indicator";
import { Sidebar } from "@/components/sidebar";
import { ThinkingIndicator } from "@/components/thinking-indicator";
import { UserMessage } from "@/components/user-message";
import { useTheme } from "@/hooks/use-system-theme";
import { useThreads } from "@/hooks/use-threads";
import { useChat } from "@ai-sdk/react";
import { useEffect, useRef, useState } from "react";

export default function Page() {
useTheme();
Expand Down Expand Up @@ -78,10 +76,7 @@ export default function Page() {
sidebarOpen ? "md:ml-[280px]" : "ml-0"
}`}
>
<ChatHeader
isSidebarOpen={sidebarOpen}
onToggleSidebar={() => setSidebarOpen((o) => !o)}
/>
<ChatHeader isSidebarOpen={sidebarOpen} onToggleSidebar={() => setSidebarOpen((o) => !o)} />

<div ref={scrollRef} className="flex-1 overflow-y-auto">
{isEmpty ? (
Expand Down
Loading
Loading