initial commit

This commit is contained in:
René Schober
2026-03-13 06:23:06 +01:00
commit 4e34270786
314 changed files with 37280 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 NextUI Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,243 @@
---
name: heroui-native
description: "HeroUI Native component library for React Native (Tailwind v4 via Uniwind). Use when working with HeroUI Native components, installing HeroUI Native, customizing themes, or accessing component documentation. Keywords: HeroUI Native, heroui-native, React Native UI, Uniwind."
metadata:
author: heroui
version: "1.0.0"
---
# HeroUI Native Development Guide
HeroUI Native is a component library built on **Uniwind (Tailwind CSS for React Native)** and **React Native**, providing accessible, customizable UI components for mobile applications.
---
## CRITICAL: Native Only - Do Not Use Web Patterns
**This guide is for HeroUI Native ONLY.** Do NOT use any prior knowledge of HeroUI React (web) patterns.
### What Changed in Native
| Feature | React (Web) | Native (Mobile) |
| ------------ | -------------------- | ----------------------------------- |
| **Styling** | Tailwind CSS v4 | Uniwind (Tailwind for React Native) |
| **Colors** | oklch format | HSL format |
| **Package** | `@heroui/react@beta` | `heroui-native` |
| **Platform** | Web browsers | iOS & Android |
### WRONG (React web patterns)
```tsx
// DO NOT DO THIS - React web pattern
import { Button } from "@heroui/react";
import "./styles.css"; // CSS files don't work in React Native
<Button className="bg-blue-500">Click me</Button>;
```
### CORRECT (Native patterns)
```tsx
// DO THIS - Native pattern (Uniwind, React Native components)
import { Button } from "heroui-native";
<Button variant="primary" onPress={() => console.log("Pressed!")}>
Click me
</Button>;
```
**Always fetch Native docs before implementing.** Do not assume React web patterns work.
---
## Core Principles
- Semantic variants (`primary`, `secondary`, `tertiary`) over visual descriptions
- Composition over configuration (compound components)
- Theme variables with HSL color format
- React Native StyleSheet patterns with Uniwind utilities
---
## Accessing Documentation & Component Information
**For component details, examples, props, and implementation patterns, always fetch documentation:**
### Using Scripts
```bash
# List all available components
node scripts/list_components.mjs
# Get component documentation (MDX)
node scripts/get_component_docs.mjs Button
node scripts/get_component_docs.mjs Button Card TextField
# Get theme variables
node scripts/get_theme.mjs
# Get non-component docs (guides, releases)
node scripts/get_docs.mjs /docs/native/getting-started/theming
```
### Direct MDX URLs
Component docs: `https://v3.heroui.com/docs/native/components/{component-name}.mdx`
Examples:
- Button: `https://v3.heroui.com/docs/native/components/button.mdx`
- Dialog: `https://v3.heroui.com/docs/native/components/dialog.mdx`
- TextField: `https://v3.heroui.com/docs/native/components/text-field.mdx`
Getting started guides: `https://v3.heroui.com/docs/native/getting-started/{topic}.mdx`
**Important:** Always fetch component docs before implementing. The MDX docs include complete examples, props, anatomy, and API references.
---
## Installation Essentials
**CRITICAL**: HeroUI Native is currently in BETA.
### Quick Install
```bash
npm i heroui-native
```
### Required Peer Dependencies
```bash
npm i react-native-reanimated react-native-gesture-handler react-native-safe-area-context @gorhom/bottom-sheet react-native-svg react-native-worklets tailwind-merge tailwind-variants
```
### Framework Setup (Expo - Recommended)
1. **Install dependencies:**
```bash
npx create-expo-app MyApp
cd MyApp
npm i heroui-native uniwind tailwindcss
npm i react-native-reanimated react-native-gesture-handler react-native-safe-area-context @gorhom/bottom-sheet react-native-svg react-native-worklets tailwind-merge tailwind-variants
```
2. **Create `global.css`:**
```css
@import "tailwindcss";
@import "uniwind";
@import "heroui-native/styles";
@source "./node_modules/heroui-native/lib";
```
3. **Wrap app with providers:**
```tsx
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { HeroUINativeProvider } from "heroui-native";
import "./global.css";
export default function Layout() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<HeroUINativeProvider>
<App />
</HeroUINativeProvider>
</GestureHandlerRootView>
);
}
```
### Critical Setup Requirements
1. **Uniwind is Required** - HeroUI Native uses Uniwind (Tailwind CSS for React Native)
2. **HeroUINativeProvider Required** - Wrap your app with `HeroUINativeProvider`
3. **GestureHandlerRootView Required** - Wrap with `GestureHandlerRootView` from react-native-gesture-handler
4. **Use Compound Components** - Components use compound structure (e.g., `Card.Header`, `Card.Body`)
5. **Use onPress, not onClick** - React Native uses `onPress` event handlers
6. **Platform-Specific Code** - Use `Platform.OS` for iOS/Android differences
---
## Component Patterns
HeroUI Native uses **compound component patterns**. Each component has subcomponents accessed via dot notation.
**Example - Card:**
```tsx
<Card>
<Card.Header>
<Card.Title>Title</Card.Title>
<Card.Description>Description</Card.Description>
</Card.Header>
<Card.Body>{/* Content */}</Card.Body>
<Card.Footer>{/* Actions */}</Card.Footer>
</Card>
```
**Key Points:**
- Always use compound structure - don't flatten to props
- Subcomponents are accessed via dot notation (e.g., `Card.Header`)
- Each subcomponent may have its own props
- **Fetch component docs for complete anatomy and examples**
---
## Semantic Variants
HeroUI uses semantic naming to communicate functional intent:
| Variant | Purpose | Usage |
| ------------- | --------------------------------- | -------------- |
| `primary` | Main action to move forward | 1 per context |
| `secondary` | Alternative actions | Multiple |
| `tertiary` | Dismissive actions (cancel, skip) | Sparingly |
| `danger` | Destructive actions | When needed |
| `danger-soft` | Soft destructive actions | Less prominent |
| `ghost` | Low-emphasis actions | Minimal weight |
| `outline` | Secondary actions | Bordered style |
**Don't use raw colors** - semantic variants adapt to themes and accessibility.
---
## Theming
HeroUI Native uses CSS variables via Tailwind/Uniwind for theming. Theme colors are defined in `global.css`:
```css
@theme {
--color-accent: hsl(260, 100%, 70%);
--color-accent-foreground: hsl(0, 0%, 100%);
}
```
**Get current theme variables:**
```bash
node scripts/get_theme.mjs
```
**Access theme colors programmatically:**
```tsx
import { useThemeColor } from "heroui-native";
const accentColor = useThemeColor("accent");
```
**Theme switching (Light/Dark Mode):**
```tsx
import { Uniwind, useUniwind } from "uniwind";
const { theme } = useUniwind();
Uniwind.setTheme(theme === "light" ? "dark" : "light");
```
For detailed theming, fetch: `https://v3.heroui.com/docs/native/getting-started/theming.mdx`

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env node
/**
* Get complete component documentation (MDX) for HeroUI Native components.
*
* Usage:
* node get_component_docs.mjs Button
* node get_component_docs.mjs Button Card TextField
*
* Output:
* MDX documentation including imports, usage, variants, props, examples
*/
const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com";
const FALLBACK_BASE = "https://v3.heroui.com";
const APP_PARAM = "app=native-skills";
/**
* Convert PascalCase to kebab-case.
*/
function toKebabCase(name) {
return name
.replace(/([a-z])([A-Z])/g, "$1-$2")
.replace(/([A-Z])([A-Z][a-z])/g, "$1-$2")
.toLowerCase();
}
/**
* Fetch data from HeroUI Native API with app parameter for analytics.
*/
async function fetchApi(endpoint, method = "GET", body = null) {
const separator = endpoint.includes("?") ? "&" : "?";
const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`;
try {
const options = {
headers: {
"Content-Type": "application/json",
"User-Agent": "HeroUI-Native-Skill/1.0",
},
method,
signal: AbortSignal.timeout(30000),
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(url, options);
if (!response.ok) {
return null;
}
return await response.json();
} catch {
return null;
}
}
/**
* Fetch MDX directly from v3.heroui.com as fallback.
*/
async function fetchFallback(component) {
const kebabName = toKebabCase(component);
const url = `${FALLBACK_BASE}/docs/native/components/${kebabName}.mdx`;
try {
const response = await fetch(url, {
headers: { "User-Agent": "HeroUI-Native-Skill/1.0" },
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
return { component, error: `Failed to fetch docs for ${component}` };
}
const content = await response.text();
return {
component,
content,
contentType: "mdx",
source: "fallback",
url,
};
} catch {
return { component, error: `Failed to fetch docs for ${component}` };
}
}
/**
* Main function to get component documentation.
*/
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: node get_component_docs.mjs <Component1> [Component2] ...");
console.error("Example: node get_component_docs.mjs Button Card");
process.exit(1);
}
const components = args;
// Try API first - use POST /v1/components/docs for batch requests
console.error(`# Fetching Native docs for: ${components.join(", ")}...`);
const data = await fetchApi("/v1/components/docs", "POST", { components });
if (data && data.results) {
// Output results
if (data.results.length === 1) {
// Single component - output content directly for easier reading
const result = data.results[0];
if (result.content) {
console.log(result.content);
} else if (result.error) {
console.error(`# Error for ${result.component}: ${result.error}`);
console.log(JSON.stringify(result, null, 2));
} else {
console.log(JSON.stringify(result, null, 2));
}
} else {
// Multiple components - output as JSON array
console.log(JSON.stringify(data, null, 2));
}
return;
}
// Fallback to individual component fetches
console.error("# API failed, using fallback...");
const results = [];
for (const component of components) {
const result = await fetchFallback(component);
results.push(result);
}
// Output results
if (results.length === 1) {
// Single component - output content directly for easier reading
const result = results[0];
if (result.content) {
console.log(result.content);
} else {
console.log(JSON.stringify(result, null, 2));
}
} else {
// Multiple components - output as JSON array
console.log(JSON.stringify(results, null, 2));
}
}
main();

View File

@@ -0,0 +1,154 @@
#!/usr/bin/env node
/**
* Get non-component HeroUI Native documentation (guides, theming, releases).
*
* Usage:
* node get_docs.mjs /docs/native/getting-started/theming
* node get_docs.mjs /docs/native/releases/beta-12
*
* Output:
* MDX documentation content
*
* Note: For component docs, use get_component_docs.mjs instead.
*/
const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com";
const FALLBACK_BASE = "https://v3.heroui.com";
const APP_PARAM = "app=native-skills";
/**
* Fetch documentation from HeroUI Native API.
* Uses v1 endpoint pattern: /v1/docs/:path
*/
async function fetchApi(path) {
// The v1 API expects path without /docs/ prefix
// Input: /docs/native/getting-started/theming
// API expects: native/getting-started/theming (route is /v1/docs/:path(*))
let apiPath = path.startsWith("/docs/")
? path.slice(6) // Remove /docs/ prefix
: path.startsWith("/")
? path.slice(1) // Remove leading /
: path;
const separator = "?";
const url = `${API_BASE}/v1/docs/${apiPath}${separator}${APP_PARAM}`;
try {
const response = await fetch(url, {
headers: { "User-Agent": "HeroUI-Native-Skill/1.0" },
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
console.error(`# API Error: HTTP ${response.status}`);
return null;
}
return await response.json();
} catch (error) {
console.error(`# API Error: ${error.message}`);
return null;
}
}
/**
* Fetch MDX directly from v3.heroui.com as fallback.
*/
async function fetchFallback(path) {
// Ensure path starts with /docs and ends with .mdx
let cleanPath = path.replace(/^\//, "");
if (!cleanPath.endsWith(".mdx")) {
cleanPath = `${cleanPath}.mdx`;
}
const url = `${FALLBACK_BASE}/${cleanPath}`;
try {
const response = await fetch(url, {
headers: { "User-Agent": "HeroUI-Native-Skill/1.0" },
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
return { error: `HTTP ${response.status}: ${response.statusText}`, path };
}
const content = await response.text();
return {
content,
contentType: "mdx",
path,
source: "fallback",
url,
};
} catch (error) {
return { error: `Fetch Error: ${error.message}`, path };
}
}
/**
* Main function to get documentation for specified path.
*/
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: node get_docs.mjs <path>");
console.error("Example: node get_docs.mjs /docs/native/getting-started/theming");
console.error();
console.error("Available paths include:");
console.error(" /docs/native/getting-started/theming");
console.error(" /docs/native/getting-started/colors");
console.error(" /docs/native/getting-started/styling");
console.error(" /docs/native/releases/beta-12");
console.error();
console.error("Note: For component docs, use get_component_docs.mjs instead.");
process.exit(1);
}
const path = args[0];
// Check if user is trying to get component docs
if (path.includes("/components/")) {
console.error("# Warning: Use get_component_docs.mjs for component documentation.");
const componentName = path.split("/").pop().replace(".mdx", "");
const titleCase = componentName.charAt(0).toUpperCase() + componentName.slice(1);
console.error(`# Example: node get_component_docs.mjs ${titleCase}`);
}
// Validate Native path
if (!path.startsWith("/docs/native/")) {
console.error("# Warning: Native documentation paths should start with /docs/native/");
console.error(`# Provided path: ${path}`);
}
console.error(`# Fetching Native documentation for ${path}...`);
// Try API first
const data = await fetchApi(path);
if (data && data.content) {
data.source = "api";
console.log(data.content);
return;
}
// Fallback to direct fetch
console.error("# API failed, using fallback...");
const fallbackData = await fetchFallback(path);
if (fallbackData.content) {
console.log(fallbackData.content);
} else {
console.log(JSON.stringify(fallbackData, null, 2));
process.exit(1);
}
}
main();

View File

@@ -0,0 +1,222 @@
#!/usr/bin/env node
/**
* Get theme variables and design tokens for HeroUI Native.
*
* Usage:
* node get_theme.mjs
*
* Output:
* Theme variables organized by light/dark with HSL color format
*/
const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com";
const APP_PARAM = "app=native-skills";
// Fallback theme reference when API is unavailable
const FALLBACK_THEME = {
borderRadius: {
full: 9999,
lg: 12,
md: 8,
sm: 6,
},
dark: {
colors: [
{
category: "base",
name: "--color-background",
value: "hsl(0, 0%, 14.5%)",
},
{
category: "semantic",
name: "--color-foreground",
value: "hsl(0, 0%, 98.4%)",
},
{
category: "semantic",
name: "--color-accent",
value: "hsl(264.1, 100%, 55.1%)",
},
{
category: "status",
name: "--color-danger",
value: "hsl(25.3, 100%, 63.7%)",
},
{
category: "status",
name: "--color-success",
value: "hsl(163.2, 100%, 76.5%)",
},
{
category: "status",
name: "--color-warning",
value: "hsl(86.0, 100%, 79.5%)",
},
],
},
latestVersion: "beta",
light: {
colors: [
{
category: "base",
name: "--color-background",
value: "hsl(0, 0%, 100%)",
},
{
category: "semantic",
name: "--color-foreground",
value: "hsl(285.89, 5.9%, 21.03%)",
},
{
category: "semantic",
name: "--color-accent",
value: "hsl(253.83, 100%, 62.04%)",
},
{
category: "status",
name: "--color-danger",
value: "hsl(25.74, 100%, 65.32%)",
},
{
category: "status",
name: "--color-success",
value: "hsl(150.81, 100%, 73.29%)",
},
{
category: "status",
name: "--color-warning",
value: "hsl(72.33, 100%, 78.19%)",
},
],
},
note: "This is a fallback. For complete theme variables, ensure the API is accessible.",
opacity: {
disabled: 0.4,
hover: 0.8,
pressed: 0.6,
},
source: "fallback",
theme: "default",
};
/**
* Fetch data from HeroUI Native API with app parameter for analytics.
*/
async function fetchApi(endpoint) {
const separator = endpoint.includes("?") ? "&" : "?";
const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`;
try {
const response = await fetch(url, {
headers: { "User-Agent": "HeroUI-Native-Skill/1.0" },
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
console.error(`# API Error: HTTP ${response.status}`);
return null;
}
return await response.json();
} catch (error) {
console.error(`# API Error: ${error.message}`);
return null;
}
}
/**
* Format colors grouped by category.
*/
function formatColors(colors) {
const grouped = {};
for (const color of colors) {
const category = color.category || "semantic";
if (!grouped[category]) {
grouped[category] = [];
}
grouped[category].push(color);
}
const lines = [];
for (const [category, tokens] of Object.entries(grouped)) {
lines.push(` /* ${category.charAt(0).toUpperCase() + category.slice(1)} Colors */`);
for (const token of tokens) {
const name = token.name || "";
const value = token.value || "";
lines.push(` ${name}: ${value};`);
}
lines.push("");
}
return lines.join("\n");
}
/**
* Main function to get theme variables.
*/
async function main() {
console.error("# Fetching Native theme variables...");
const rawData = await fetchApi("/v1/themes/variables?theme=default");
let data;
let version;
if (!rawData) {
console.error("# API failed, using fallback theme reference...");
data = FALLBACK_THEME;
version = FALLBACK_THEME.latestVersion || "unknown";
} else {
// Handle API response format
data = rawData;
version = rawData.latestVersion || "unknown";
}
// Output as formatted structure for readability
console.log("/* HeroUI Native Theme Variables */");
console.log(`/* Theme: ${data.theme || "default"} */`);
console.log(`/* Version: ${version} */`);
console.log();
// Light mode colors
if (data.light && data.light.colors) {
console.log("/* Light Mode Colors */");
console.log(formatColors(data.light.colors));
}
// Dark mode colors
if (data.dark && data.dark.colors) {
console.log("/* Dark Mode Colors */");
console.log(formatColors(data.dark.colors));
}
// Border radius
if (data.borderRadius) {
console.log("/* Border Radius */");
for (const [key, value] of Object.entries(data.borderRadius)) {
console.log(` --radius-${key}: ${value};`);
}
console.log();
}
// Opacity
if (data.opacity) {
console.log("/* Opacity */");
for (const [key, value] of Object.entries(data.opacity)) {
console.log(` --opacity-${key}: ${value};`);
}
console.log();
}
// Also output raw JSON to stderr for programmatic use
console.error("\n# Raw JSON output:");
console.error(JSON.stringify(rawData || data, null, 2));
}
main();

View File

@@ -0,0 +1,134 @@
#!/usr/bin/env node
/**
* List all available HeroUI Native components.
*
* Usage:
* node list_components.mjs
*
* Output:
* JSON with components array, latestVersion, and count
*/
const API_BASE = process.env.HEROUI_NATIVE_API_BASE || "https://native-mcp-api.heroui.com";
const APP_PARAM = "app=native-skills";
const LLMS_TXT_URL = "https://v3.heroui.com/native/llms.txt";
/**
* Fetch data from HeroUI Native API with app parameter for analytics.
*/
async function fetchApi(endpoint) {
const separator = endpoint.includes("?") ? "&" : "?";
const url = `${API_BASE}${endpoint}${separator}${APP_PARAM}`;
try {
const response = await fetch(url, {
headers: { "User-Agent": "HeroUI-Native-Skill/1.0" },
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
console.error(`HTTP Error ${response.status}: ${response.statusText}`);
return null;
}
return await response.json();
} catch (error) {
console.error(`API Error: ${error.message}`);
return null;
}
}
/**
* Fetch component list from llms.txt fallback URL.
*/
async function fetchFallback() {
try {
const response = await fetch(LLMS_TXT_URL, {
headers: { "User-Agent": "HeroUI-Native-Skill/1.0" },
signal: AbortSignal.timeout(30000),
});
if (!response.ok) {
return null;
}
const content = await response.text();
// Parse markdown to extract component names from pattern: - [ComponentName](url)
// Look for links under the Components section (### Components)
const components = [];
let inComponentsSection = false;
for (const line of content.split("\n")) {
// Check if we're entering the Components section (uses ### header)
if (line.trim() === "### Components") {
inComponentsSection = true;
continue;
}
// Check if we're leaving the Components section (another ### header)
if (inComponentsSection && line.trim().startsWith("### ")) {
break;
}
// Extract component name from markdown link pattern
// Match: - [ComponentName](https://v3.heroui.com/docs/native/components/component-name)
// Skip "All Components" which links to /components without a specific component
if (inComponentsSection) {
const match = line.match(
/^\s*-\s*\[([^\]]+)\]\(https:\/\/v3\.heroui\.com\/docs\/native\/components\/[a-z]/,
);
if (match) {
components.push(match[1]);
}
}
}
if (components.length > 0) {
console.error(`# Using fallback: ${LLMS_TXT_URL}`);
return {
components: components.sort(),
count: components.length,
latestVersion: "unknown",
};
}
return null;
} catch (error) {
console.error(`Fallback Error: ${error.message}`);
return null;
}
}
/**
* Main function to list all available HeroUI Native components.
*/
async function main() {
let data = await fetchApi("/v1/components");
// Check if API returned valid data with components
if (!data || !data.components || data.components.length === 0) {
console.error("# API returned no components, trying fallback...");
data = await fetchFallback();
}
if (!data || !data.components || data.components.length === 0) {
console.error("Error: Failed to fetch component list from API and fallback");
process.exit(1);
}
// Output formatted JSON
console.log(JSON.stringify(data, null, 2));
// Print summary to stderr for human readability
console.error(
`\n# Found ${data.components.length} Native components (${data.latestVersion || "unknown"})`,
);
}
main();