oneie
Version:
Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.
1,430 lines (1,298 loc) • 31.9 kB
Markdown
title: Ontology Ui
dimension: things
category: products
tags: 6-dimensions, ai, frontend, ontology
related_dimensions: connections, events, groups, knowledge, people
scope: global
created: 2025-11-03
updated: 2025-11-03
version: 1.0.0
ai_context: |
This document is part of the things dimension in the products category.
Location: one/things/products/ontology-ui.md
Purpose: Documents ontology ui specification
Related dimensions: connections, events, groups, knowledge, people
For AI agents: Read this to understand ontology ui.
# Ontology UI Specification
**Frontend-complete ontology: Data model + UI model = Complete specification**
## Overview
The ONE ontology extends beyond data modeling to include UI rendering instructions. For each thing type, we define not just **what data it contains**, but **how it should be displayed**.
**Benefits:**
- ✅ Generic components work for all 66 thing types
- ✅ Consistent UI across entire platform
- ✅ Add new type → UI updates automatically
- ✅ Frontend is just a renderer (no hardcoded logic)
- ✅ Customizable per organization
**Implementation:**
- Built on [shadcn/ui](https://ui.shadcn.com) - Beautiful, accessible components
- Uses Radix UI primitives (WAI-ARIA compliant)
- Styled with Tailwind CSS
- Dark mode included
- You own the code (copy-paste components)
## UI Metadata Schema
For each thing type, add a `ui` object:
```typescript
interface ThingTypeUI {
// Primary component type
component: "Card" | "List" | "Table" | "Kanban" | "Timeline" | "Custom";
// Layout configurations
layouts: {
grid?: { columns: number; gap: string; responsive?: boolean };
list?: {
orientation: "horizontal" | "vertical";
gap: string;
compact?: boolean;
};
detail?: { width: string; centered?: boolean; sidebar?: boolean };
table?: {
density: "compact" | "comfortable" | "spacious";
sortable?: boolean;
};
};
// Field rendering instructions
fields: Record<string, FieldUI>;
// Available views
views: {
card?: { default?: boolean; fields: string[] };
list?: { fields: string[] };
detail?: { fields: string[] | "*" };
table?: { fields: string[] };
};
// User actions
actions: {
primary?: ActionConfig;
secondary?: ActionConfig[];
context?: ActionConfig[]; // Right-click/long-press menu
};
// Connection display
connections?: Record<string, ConnectionUI>;
// Empty state
empty?: EmptyStateConfig;
// Search configuration
search?: {
fields: string[]; // Which fields to search
placeholder: string;
filters?: FilterConfig[];
};
// Grouping/sorting
organization?: {
groupBy?: string[]; // Fields that can group by
sortBy?: string[]; // Fields that can sort by
defaultSort?: { field: string; order: "asc" | "desc" };
};
}
interface FieldUI {
component: string; // 'Heading' | 'Text' | 'Price' | 'Image' | 'Badge' | etc.
label?: string; // Display label
required?: boolean;
hidden?: boolean; // Hide in certain views
// Component-specific props
[key: string]: any;
}
interface ActionConfig {
action: string; // 'create' | 'edit' | 'delete' | 'enroll' | 'purchase' | etc.
label: string;
icon?: string;
variant?: "primary" | "secondary" | "ghost" | "danger";
confirm?: string; // Confirmation message
permission?: string; // Required permission
}
interface ConnectionUI {
label: string; // "{count} students" or "by {name}"
icon?: string;
display: "badge" | "inline" | "avatar" | "list";
link?: boolean; // Make clickable
max?: number; // Max items to show
}
interface EmptyStateConfig {
icon: string;
title: string;
description: string;
action?: ActionConfig;
}
```
## Thing Type Specifications
### 1. Course
```typescript
{
type: "course",
description: "Educational course or training program",
properties: {
title: { type: "string", required: true },
description: { type: "string", required: true },
price: { type: "number", required: false },
currency: { type: "string", default: "USD" },
duration: { type: "string", required: false },
level: { type: "enum", values: ["beginner", "intermediate", "advanced"] },
category: { type: "string", required: false },
tags: { type: "array", items: "string" },
thumbnail: { type: "url", required: false },
publishedAt: { type: "timestamp", required: false },
featured: { type: "boolean", default: false },
status: { type: "enum", values: ["draft", "published", "archived"] }
},
ui: {
component: "Card",
layouts: {
grid: {
columns: 3,
gap: "lg",
responsive: true // 1 col mobile, 2 tablet, 3 desktop
},
list: {
orientation: "horizontal",
gap: "md",
compact: false
},
detail: {
width: "prose",
centered: true,
sidebar: true // Show related content in sidebar
}
},
fields: {
title: {
component: "Heading",
size: "xl",
weight: "bold",
truncate: 2, // Max 2 lines
required: true
},
description: {
component: "Text",
lines: 3, // Show 3 lines max
size: "base",
color: "muted",
expandable: true // "Read more" on click
},
price: {
component: "Price",
currency: "USD",
format: "compact", // $99 vs $99.00
badge: true, // Show as badge
free: { label: "Free", badge: true } // Show "Free" if price = 0
},
thumbnail: {
component: "Image",
aspect: "video", // 16:9
lazy: true,
placeholder: "/defaults/course.jpg",
fallback: "/defaults/course-fallback.jpg"
},
level: {
component: "Badge",
colors: {
beginner: "green",
intermediate: "yellow",
advanced: "red"
},
icon: {
beginner: "leaf",
intermediate: "zap",
advanced: "flame"
}
},
category: {
component: "Text",
size: "sm",
color: "muted",
icon: "folder"
},
tags: {
component: "TagList",
max: 3,
color: "blue",
moreLabel: "+{count} more"
},
duration: {
component: "Text",
size: "sm",
icon: "clock",
format: (value) => `${value} hours`
},
publishedAt: {
component: "Date",
format: "relative", // "2 days ago"
icon: "calendar"
},
featured: {
component: "Badge",
label: "Featured",
color: "purple",
icon: "star",
showOnlyIf: true // Only show if featured = true
},
status: {
component: "Badge",
colors: {
draft: "gray",
published: "green",
archived: "red"
}
}
},
views: {
card: {
default: true,
fields: ["thumbnail", "title", "description", "price", "level", "duration"]
},
list: {
fields: ["thumbnail", "title", "category", "duration", "price"]
},
detail: {
fields: "*" // All fields
},
table: {
fields: ["title", "category", "level", "price", "publishedAt", "status"]
}
},
actions: {
primary: {
action: "enroll",
label: "Enroll Now",
icon: "check",
variant: "primary"
},
secondary: [
{
action: "preview",
label: "Preview",
icon: "eye",
variant: "ghost"
},
{
action: "share",
label: "Share",
icon: "share",
variant: "ghost"
},
{
action: "bookmark",
label: "Save",
icon: "bookmark",
variant: "ghost"
}
],
context: [
{
action: "edit",
label: "Edit",
icon: "edit",
permission: "course.edit"
},
{
action: "duplicate",
label: "Duplicate",
icon: "copy"
},
{
action: "delete",
label: "Delete",
icon: "trash",
variant: "danger",
confirm: "Are you sure you want to delete this course?"
}
]
},
connections: {
enrolled_in: {
label: "{count} students enrolled",
icon: "users",
display: "badge",
link: true
},
contains: {
label: "{count} lessons",
icon: "list",
display: "inline",
link: true
},
created_by: {
label: "by {name}",
icon: "user",
display: "avatar",
link: true
},
tagged_with: {
label: "{name}",
icon: "tag",
display: "badge",
max: 5
}
},
empty: {
icon: "book",
title: "No courses yet",
description: "Create your first course to get started",
action: {
action: "create",
label: "Create Course",
icon: "plus",
variant: "primary"
}
},
search: {
fields: ["title", "description", "tags", "category"],
placeholder: "Search courses...",
filters: [
{
field: "level",
label: "Level",
type: "select",
options: ["beginner", "intermediate", "advanced"]
},
{
field: "category",
label: "Category",
type: "select"
},
{
field: "price",
label: "Price",
type: "range",
min: 0,
max: 500
}
]
},
organization: {
groupBy: ["category", "level", "status"],
sortBy: ["title", "price", "publishedAt", "enrollments"],
defaultSort: { field: "publishedAt", order: "desc" }
}
}
}
```
### 2. Lesson
```typescript
{
type: "lesson",
description: "Individual lesson within a course",
properties: {
title: { type: "string", required: true },
description: { type: "string", required: false },
content: { type: "markdown", required: true },
videoUrl: { type: "url", required: false },
duration: { type: "number", required: false }, // minutes
order: { type: "number", required: true },
completed: { type: "boolean", default: false },
type: { type: "enum", values: ["video", "text", "quiz", "assignment"] },
resources: { type: "array", items: "object" }
},
ui: {
component: "List",
layouts: {
list: {
orientation: "vertical",
gap: "sm",
compact: true
},
detail: {
width: "prose",
centered: true,
sidebar: true
}
},
fields: {
title: {
component: "Heading",
size: "lg",
weight: "semibold"
},
description: {
component: "Text",
size: "sm",
color: "muted",
lines: 2
},
content: {
component: "Markdown",
className: "prose prose-lg"
},
videoUrl: {
component: "Video",
controls: true,
autoplay: false,
muted: false
},
duration: {
component: "Text",
size: "sm",
icon: "clock",
format: (value) => `${value} min`
},
order: {
component: "Badge",
label: "Lesson {value}",
color: "gray",
size: "sm"
},
completed: {
component: "Checkbox",
label: "Mark as complete",
checkedIcon: "check-circle",
uncheckedIcon: "circle"
},
type: {
component: "Badge",
colors: {
video: "blue",
text: "gray",
quiz: "purple",
assignment: "orange"
},
icons: {
video: "play",
text: "file-text",
quiz: "help-circle",
assignment: "edit"
}
},
resources: {
component: "ResourceList",
showDownload: true,
showPreview: true
}
},
views: {
list: {
default: true,
fields: ["order", "title", "type", "duration", "completed"]
},
detail: {
fields: "*"
}
},
actions: {
primary: {
action: "start",
label: "Start Lesson",
icon: "play",
variant: "primary"
},
secondary: [
{
action: "complete",
label: "Mark Complete",
icon: "check",
variant: "ghost"
},
{
action: "next",
label: "Next Lesson",
icon: "arrow-right",
variant: "ghost"
}
]
},
connections: {
part_of: {
label: "Part of {name}",
icon: "folder",
display: "inline",
link: true
},
completed_by: {
label: "{count} completed",
icon: "check",
display: "badge"
}
},
empty: {
icon: "file-text",
title: "No lessons yet",
description: "Add lessons to build your course",
action: {
action: "create",
label: "Add Lesson",
icon: "plus"
}
}
}
}
```
### 3. Product
```typescript
{
type: "product",
description: "Physical product for sale",
properties: {
name: { type: "string", required: true },
description: { type: "string", required: true },
price: { type: "number", required: true },
compareAtPrice: { type: "number", required: false },
currency: { type: "string", default: "USD" },
sku: { type: "string", required: false },
inventory: { type: "number", default: 0 },
images: { type: "array", items: "url" },
category: { type: "string", required: false },
tags: { type: "array", items: "string" },
weight: { type: "number", required: false },
dimensions: { type: "object", required: false },
shippable: { type: "boolean", default: true },
featured: { type: "boolean", default: false }
},
ui: {
component: "Card",
layouts: {
grid: {
columns: 4,
gap: "md",
responsive: true
},
list: {
orientation: "horizontal",
gap: "md",
compact: false
},
detail: {
width: "full",
centered: false,
sidebar: true
}
},
fields: {
name: {
component: "Heading",
size: "xl",
weight: "bold",
truncate: 2
},
description: {
component: "Text",
size: "base",
color: "default",
lines: 3,
expandable: true
},
price: {
component: "Price",
currency: "USD",
size: "lg",
weight: "bold"
},
compareAtPrice: {
component: "Price",
currency: "USD",
size: "sm",
strikethrough: true,
color: "muted"
},
images: {
component: "ImageGallery",
aspect: "square",
lazy: true,
zoom: true,
thumbnails: true
},
inventory: {
component: "Badge",
label: (value) => value > 0 ? `${value} in stock` : "Out of stock",
color: (value) => value > 0 ? "green" : "red",
icon: "package"
},
sku: {
component: "Text",
size: "xs",
color: "muted",
label: "SKU:",
monospace: true
},
tags: {
component: "TagList",
max: 5,
color: "blue"
},
category: {
component: "Breadcrumb",
separator: "/"
},
featured: {
component: "Badge",
label: "Featured",
color: "yellow",
icon: "star",
showOnlyIf: true
}
},
views: {
card: {
default: true,
fields: ["images", "name", "price", "compareAtPrice"]
},
list: {
fields: ["images", "name", "price", "inventory", "sku"]
},
detail: {
fields: "*"
},
table: {
fields: ["name", "sku", "price", "inventory", "category"]
}
},
actions: {
primary: {
action: "addToCart",
label: "Add to Cart",
icon: "shopping-cart",
variant: "primary"
},
secondary: [
{
action: "wishlist",
label: "Add to Wishlist",
icon: "heart",
variant: "ghost"
},
{
action: "share",
label: "Share",
icon: "share",
variant: "ghost"
}
]
},
connections: {
purchased_by: {
label: "{count} sold",
icon: "shopping-bag",
display: "badge"
},
reviewed_by: {
label: "{count} reviews",
icon: "star",
display: "badge",
link: true
},
in_category: {
label: "{name}",
icon: "folder",
display: "inline",
link: true
}
},
empty: {
icon: "shopping-bag",
title: "No products yet",
description: "Add products to start selling",
action: {
action: "create",
label: "Add Product",
icon: "plus"
}
},
search: {
fields: ["name", "description", "sku", "tags"],
placeholder: "Search products...",
filters: [
{
field: "category",
label: "Category",
type: "select"
},
{
field: "price",
label: "Price",
type: "range",
min: 0,
max: 1000
},
{
field: "inventory",
label: "In Stock",
type: "boolean"
}
]
},
organization: {
groupBy: ["category", "featured"],
sortBy: ["name", "price", "inventory", "createdAt"],
defaultSort: { field: "createdAt", order: "desc" }
}
}
}
```
### 4. Post (Blog Post)
```typescript
{
type: "post",
description: "Blog post or article",
properties: {
title: { type: "string", required: true },
content: { type: "markdown", required: true },
excerpt: { type: "string", required: false },
featuredImage: { type: "url", required: false },
author: { type: "string", required: true },
publishedAt: { type: "timestamp", required: false },
slug: { type: "string", required: true },
category: { type: "string", required: false },
tags: { type: "array", items: "string" },
status: { type: "enum", values: ["draft", "published", "scheduled", "archived"] },
readTime: { type: "number", required: false }, // minutes
views: { type: "number", default: 0 },
featured: { type: "boolean", default: false }
},
ui: {
component: "Card",
layouts: {
grid: {
columns: 3,
gap: "lg",
responsive: true
},
list: {
orientation: "vertical",
gap: "lg",
compact: false
},
detail: {
width: "prose",
centered: true,
sidebar: false
}
},
fields: {
title: {
component: "Heading",
size: "2xl",
weight: "bold",
lineHeight: "tight"
},
content: {
component: "Markdown",
className: "prose prose-lg prose-headings:font-bold prose-a:text-blue-600"
},
excerpt: {
component: "Text",
size: "base",
color: "muted",
lines: 3,
italic: true
},
featuredImage: {
component: "Image",
aspect: "wide", // 21:9
lazy: true,
sizes: "(max-width: 768px) 100vw, 768px"
},
author: {
component: "Text",
size: "sm",
weight: "medium",
icon: "user"
},
publishedAt: {
component: "Date",
format: "full", // "January 15, 2024"
icon: "calendar"
},
category: {
component: "Badge",
color: "purple",
size: "sm"
},
tags: {
component: "TagList",
max: 5,
color: "gray"
},
status: {
component: "Badge",
colors: {
draft: "gray",
published: "green",
scheduled: "blue",
archived: "red"
}
},
readTime: {
component: "Text",
size: "sm",
color: "muted",
icon: "clock",
format: (value) => `${value} min read`
},
views: {
component: "Text",
size: "sm",
color: "muted",
icon: "eye",
format: (value) => `${value.toLocaleString()} views`
},
featured: {
component: "Badge",
label: "Featured",
color: "yellow",
icon: "star",
showOnlyIf: true
}
},
views: {
card: {
default: true,
fields: ["featuredImage", "category", "title", "excerpt", "author", "publishedAt", "readTime"]
},
list: {
fields: ["featuredImage", "title", "excerpt", "author", "publishedAt", "views"]
},
detail: {
fields: "*"
}
},
actions: {
primary: {
action: "read",
label: "Read Article",
icon: "arrow-right",
variant: "primary"
},
secondary: [
{
action: "bookmark",
label: "Save",
icon: "bookmark",
variant: "ghost"
},
{
action: "share",
label: "Share",
icon: "share",
variant: "ghost"
}
],
context: [
{
action: "edit",
label: "Edit",
icon: "edit",
permission: "post.edit"
},
{
action: "delete",
label: "Delete",
icon: "trash",
variant: "danger",
confirm: "Delete this post?"
}
]
},
connections: {
written_by: {
label: "by {name}",
icon: "user",
display: "avatar",
link: true
},
tagged_with: {
label: "{name}",
icon: "tag",
display: "badge",
max: 5
},
commented_on: {
label: "{count} comments",
icon: "message-circle",
display: "badge",
link: true
}
},
empty: {
icon: "file-text",
title: "No posts yet",
description: "Write your first blog post",
action: {
action: "create",
label: "Write Post",
icon: "plus"
}
},
search: {
fields: ["title", "content", "excerpt", "tags"],
placeholder: "Search posts...",
filters: [
{
field: "category",
label: "Category",
type: "select"
},
{
field: "author",
label: "Author",
type: "select"
},
{
field: "publishedAt",
label: "Published",
type: "dateRange"
}
]
},
organization: {
groupBy: ["category", "author", "status"],
sortBy: ["title", "publishedAt", "views"],
defaultSort: { field: "publishedAt", order: "desc" }
}
}
}
```
### 5. Person (Creator)
```typescript
{
type: "person",
subtype: "creator",
description: "Content creator, instructor, or team member",
properties: {
name: { type: "string", required: true },
email: { type: "email", required: true },
bio: { type: "markdown", required: false },
avatar: { type: "url", required: false },
role: { type: "enum", values: ["org_owner", "org_admin", "org_member", "instructor", "author"] },
title: { type: "string", required: false },
company: { type: "string", required: false },
location: { type: "string", required: false },
website: { type: "url", required: false },
social: { type: "object", required: false }, // { twitter, linkedin, github }
verified: { type: "boolean", default: false },
featured: { type: "boolean", default: false }
},
ui: {
component: "Card",
layouts: {
grid: {
columns: 4,
gap: "md",
responsive: true
},
list: {
orientation: "horizontal",
gap: "md",
compact: true
},
detail: {
width: "prose",
centered: true,
sidebar: true
}
},
fields: {
name: {
component: "Heading",
size: "xl",
weight: "bold"
},
email: {
component: "Text",
size: "sm",
color: "muted",
icon: "mail",
link: true, // mailto: link
privacy: "private" // Only show to authorized users
},
bio: {
component: "Markdown",
className: "prose prose-sm",
lines: 5,
expandable: true
},
avatar: {
component: "Avatar",
size: "xl",
shape: "circle",
fallback: "initials" // Show initials if no avatar
},
role: {
component: "Badge",
colors: {
org_owner: "purple",
org_admin: "blue",
org_member: "gray",
instructor: "green",
author: "yellow"
},
labels: {
org_owner: "Owner",
org_admin: "Admin",
org_member: "Member",
instructor: "Instructor",
author: "Author"
}
},
title: {
component: "Text",
size: "sm",
weight: "medium",
color: "default"
},
company: {
component: "Text",
size: "sm",
color: "muted",
icon: "building"
},
location: {
component: "Text",
size: "sm",
color: "muted",
icon: "map-pin"
},
website: {
component: "Link",
size: "sm",
icon: "external-link",
external: true
},
social: {
component: "SocialLinks",
display: "icons",
size: "md",
platforms: ["twitter", "linkedin", "github", "youtube"]
},
verified: {
component: "Badge",
label: "Verified",
color: "blue",
icon: "check-circle",
showOnlyIf: true
},
featured: {
component: "Badge",
label: "Featured",
color: "purple",
icon: "star",
showOnlyIf: true
}
},
views: {
card: {
default: true,
fields: ["avatar", "name", "title", "company", "role"]
},
list: {
fields: ["avatar", "name", "title", "location", "social"]
},
detail: {
fields: "*"
}
},
actions: {
primary: {
action: "viewProfile",
label: "View Profile",
icon: "user",
variant: "primary"
},
secondary: [
{
action: "message",
label: "Message",
icon: "mail",
variant: "ghost"
},
{
action: "follow",
label: "Follow",
icon: "user-plus",
variant: "ghost"
}
]
},
connections: {
created: {
label: "{count} courses",
icon: "book",
display: "badge",
link: true
},
taught: {
label: "{count} students",
icon: "users",
display: "badge"
},
member_of: {
label: "{name}",
icon: "building",
display: "inline",
link: true
}
},
empty: {
icon: "users",
title: "No team members yet",
description: "Invite people to join your team",
action: {
action: "invite",
label: "Invite Member",
icon: "user-plus"
}
},
search: {
fields: ["name", "email", "title", "bio"],
placeholder: "Search people...",
filters: [
{
field: "role",
label: "Role",
type: "select"
},
{
field: "verified",
label: "Verified",
type: "boolean"
}
]
},
organization: {
groupBy: ["role", "company"],
sortBy: ["name", "createdAt"],
defaultSort: { field: "name", order: "asc" }
}
}
}
```
## Field Component Types
All field components are built with shadcn/ui for consistency and accessibility:
### Text Components
- `Heading` - Headings (h1-h6)
- `Text` - Body text
- `Markdown` - Markdown content
- `RichText` - Rich text editor output
### Media Components
- `Image` - Single image
- `ImageGallery` - Multiple images
- `Video` - Video player
- `Audio` - Audio player
- `Avatar` - User avatar
### Data Components
- `Price` - Formatted price
- `Date` - Formatted date/time
- `Number` - Formatted number
- `Percentage` - Formatted percentage
- `Badge` - Status badge
- `Tag` - Single tag
- `TagList` - Multiple tags
### Interactive Components
- `Link` - Hyperlink
- `Button` - Action button
- `Checkbox` - Checkbox input
- `Toggle` - Toggle switch
- `Rating` - Star rating
- `Progress` - Progress bar
### Complex Components
- `Table` - Data table
- `List` - Item list
- `Grid` - Item grid
- `Timeline` - Event timeline
- `Chart` - Data visualization
- `Map` - Geographic map
### Social Components
- `SocialLinks` - Social media links
- `ShareButtons` - Share buttons
- `CommentList` - Comments
- `LikeButton` - Like/favorite
## Quick Setup
```bash
# Install shadcn/ui components
npx shadcn-ui@latest init
npx shadcn-ui@latest add card button badge avatar separator
```
That's it! Components use shadcn/ui automatically.
## Usage in Frontend
### Generic Component Example
```tsx
// frontend/src/components/generic/Card.tsx
import { useThingConfig } from "@/ontology/hooks";
import {
Card as ShadCard,
CardContent,
CardFooter,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
export function Card({ thing }) {
// Get UI config from ontology
const config = useThingConfig(thing.type);
const view = config.ui.views.card;
return (
<ShadCard>
<CardContent className="pt-6">
{view.fields.map((fieldName) => (
<Field
key={fieldName}
name={fieldName}
value={thing.properties[fieldName]}
config={config.ui.fields[fieldName]}
/>
))}
</CardContent>
{config.ui.actions.primary && (
<CardFooter>
<Button
onClick={() =>
handleAction(thing, config.ui.actions.primary.action)
}
>
{config.ui.actions.primary.label}
</Button>
</CardFooter>
)}
</ShadCard>
);
}
```
### Usage
```tsx
// Works for ANY thing type!
<Card thing={course} />
<Card thing={product} />
<Card thing={post} />
<Card thing={lesson} />
<Card thing={person} />
```
## Next Steps
### Immediate (Week 1)
- [ ] Complete UI specs for top 10 types
- [ ] Build generic Card component
- [ ] Build generic Field component
- [ ] Test with 3 different types
### Short Term (Week 2-4)
- [ ] Add UI specs for remaining 56 types
- [ ] Build all field component types
- [ ] Build ThingList, ThingDetail, ThingTable
- [ ] Refactor existing type-specific components
### Long Term (Month 2+)
- [ ] Add customization API (override UI config)
- [ ] Add theme support (light/dark)
- [ ] Add responsive breakpoints
- [ ] Add accessibility features
- [ ] Add internationalization
## Customization
Organizations can override UI config:
```typescript
// Custom UI for your organization
const customCourseUI = {
...defaultCourseUI,
fields: {
...defaultCourseUI.fields,
price: {
...defaultCourseUI.fields.price,
currency: "EUR", // Override to EUR
badge: false, // Don't show as badge
},
},
};
```
**With ontology-ui, ONE frontend becomes a universal renderer for all 66 thing types.**