@dclbs/ui
v0.1.5
Published
UI components for Platform AI SDK
Readme
@dclbs/ui
Reusable UI components and utilities for building consistent interfaces with the Caava AI SDK.
Features
- 🎨 Utility Functions - Class management and styling utilities
- 🎯 TypeScript - Full type safety
- 🧩 Framework Agnostic - Core utilities work with any framework
- 🎨 Tailwind Compatible - Designed to work with Tailwind CSS
- 📦 Lightweight - Minimal bundle size
Installation
npm install @dclbs/uiUtilities
This package currently provides core styling utilities. UI components will be added in future releases.
Class Management
import { cn } from "@dclbs/ui";
// Combine classes conditionally
const className = cn(
"base-class",
"conditional-class",
isActive && "active-class",
isDisabled && "disabled-class"
);
// Works great with Tailwind CSS
const buttonClass = cn(
"px-4 py-2 rounded-md font-medium",
variant === "primary" && "bg-blue-500 text-white",
variant === "secondary" && "bg-gray-200 text-gray-900",
size === "sm" && "text-sm px-3 py-1",
size === "lg" && "text-lg px-6 py-3",
disabled && "opacity-50 cursor-not-allowed"
);Usage Examples
React Component Styling
import { cn } from "@dclbs/ui";
interface ButtonProps {
variant?: "primary" | "secondary" | "outline";
size?: "sm" | "md" | "lg";
disabled?: boolean;
className?: string;
children: React.ReactNode;
}
function Button({
variant = "primary",
size = "md",
disabled = false,
className,
children,
...props
}: ButtonProps) {
return (
<button
className={cn(
// Base styles
"font-medium rounded-md transition-colors focus:outline-none focus:ring-2",
// Variant styles
variant === "primary" &&
"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
variant === "secondary" &&
"bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500",
variant === "outline" &&
"border border-gray-300 bg-white text-gray-700 hover:bg-gray-50",
// Size styles
size === "sm" && "px-3 py-1.5 text-sm",
size === "md" && "px-4 py-2 text-base",
size === "lg" && "px-6 py-3 text-lg",
// State styles
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
// Custom className
className
)}
disabled={disabled}
{...props}
>
{children}
</button>
);
}Chat Message Components
import { cn } from "@dclbs/ui";
interface MessageProps {
role: "user" | "assistant";
content: string;
isOptimistic?: boolean;
className?: string;
}
function ChatMessage({ role, content, isOptimistic, className }: MessageProps) {
return (
<div
className={cn(
"flex mb-4",
role === "user" ? "justify-end" : "justify-start",
className
)}
>
<div
className={cn(
"max-w-xs lg:max-w-md px-4 py-2 rounded-lg",
role === "user" && ["bg-blue-500 text-white", "rounded-br-none"],
role === "assistant" && [
"bg-gray-200 text-gray-900",
"rounded-bl-none",
],
isOptimistic && "opacity-70"
)}
>
{content}
</div>
</div>
);
}Widget Container
import { cn } from "@dclbs/ui";
interface WidgetContainerProps {
children: React.ReactNode;
position?: "fixed" | "embedded";
theme?: "light" | "dark";
className?: string;
}
function WidgetContainer({
children,
position = "fixed",
theme = "light",
className,
}: WidgetContainerProps) {
return (
<div
className={cn(
"border border-gray-200 rounded-lg shadow-lg overflow-hidden",
// Position styles
position === "fixed" && ["fixed bottom-4 right-4 w-80 h-96", "z-50"],
position === "embedded" && ["w-full h-full"],
// Theme styles
theme === "light" && "bg-white",
theme === "dark" && "bg-gray-900 border-gray-700",
className
)}
>
{children}
</div>
);
}Design Tokens
While this package focuses on utilities, you can establish design tokens using CSS variables:
:root {
/* Colors */
--color-primary: #3b82f6;
--color-primary-hover: #2563eb;
--color-secondary: #6b7280;
--color-background: #ffffff;
--color-surface: #f9fafb;
--color-text: #1f2937;
--color-text-muted: #6b7280;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Typography */
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
/* Border Radius */
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
/* Dark theme */
[data-theme="dark"] {
--color-background: #1f2937;
--color-surface: #374151;
--color-text: #f9fafb;
--color-text-muted: #d1d5db;
}Framework Integration
React
import { cn } from "@dclbs/ui";
// Use with React components as shown in examples aboveVue.js
<template>
<div :class="buttonClass">
<slot />
</div>
</template>
<script setup>
import { computed } from "vue";
import { cn } from "@dclbs/ui";
const props = defineProps({
variant: { type: String, default: "primary" },
size: { type: String, default: "md" },
disabled: Boolean,
});
const buttonClass = computed(() =>
cn(
"font-medium rounded-md transition-colors",
props.variant === "primary" && "bg-blue-600 text-white",
props.variant === "secondary" && "bg-gray-200 text-gray-900",
props.size === "sm" && "px-3 py-1.5 text-sm",
props.size === "md" && "px-4 py-2 text-base",
props.disabled && "opacity-50 cursor-not-allowed"
)
);
</script>Angular
import { Component, Input } from "@angular/core";
import { cn } from "@dclbs/ui";
@Component({
selector: "app-button",
template: `
<button [class]="buttonClass" [disabled]="disabled">
<ng-content></ng-content>
</button>
`,
})
export class ButtonComponent {
@Input() variant: "primary" | "secondary" = "primary";
@Input() size: "sm" | "md" | "lg" = "md";
@Input() disabled = false;
get buttonClass() {
return cn(
"font-medium rounded-md transition-colors",
this.variant === "primary" && "bg-blue-600 text-white",
this.variant === "secondary" && "bg-gray-200 text-gray-900",
this.size === "sm" && "px-3 py-1.5 text-sm",
this.size === "md" && "px-4 py-2 text-base",
this.disabled && "opacity-50 cursor-not-allowed"
);
}
}Vanilla JavaScript
import { cn } from "@dclbs/ui";
function createButton({
variant = "primary",
size = "md",
disabled = false,
text,
}) {
const button = document.createElement("button");
button.className = cn(
"font-medium rounded-md transition-colors",
variant === "primary" && "bg-blue-600 text-white hover:bg-blue-700",
variant === "secondary" && "bg-gray-200 text-gray-900 hover:bg-gray-300",
size === "sm" && "px-3 py-1.5 text-sm",
size === "md" && "px-4 py-2 text-base",
disabled && "opacity-50 cursor-not-allowed"
);
button.textContent = text;
button.disabled = disabled;
return button;
}Best Practices
Component Composition
// Base component with utilities
function BaseButton({ className, ...props }) {
return (
<button
className={cn(
"font-medium rounded-md transition-colors focus:outline-none focus:ring-2",
className
)}
{...props}
/>
);
}
// Specialized components
function PrimaryButton({ className, ...props }) {
return (
<BaseButton
className={cn(
"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
className
)}
{...props}
/>
);
}
function SecondaryButton({ className, ...props }) {
return (
<BaseButton
className={cn(
"bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500",
className
)}
{...props}
/>
);
}Responsive Design
function ResponsiveWidget({ className, ...props }) {
return (
<div
className={cn(
// Mobile: full screen overlay
"fixed inset-0 bg-white z-50",
// Tablet: bottom sheet
"md:fixed md:bottom-0 md:left-0 md:right-0 md:top-auto md:h-2/3",
"md:rounded-t-lg md:shadow-xl",
// Desktop: floating widget
"lg:relative lg:inset-auto lg:w-80 lg:h-96",
"lg:rounded-lg lg:shadow-lg",
className
)}
{...props}
/>
);
}Future Components
Planned UI components for future releases:
- Button - Various button styles and sizes
- Input - Text inputs with validation states
- Card - Container component for content
- Avatar - User avatar component
- Badge - Status and notification badges
- Spinner - Loading indicators
- Toast - Notification messages
- Modal - Overlay dialogs
- Dropdown - Select and menu components
API Reference
Functions
cn(...inputs: ClassValue[]): string- Combines class names conditionally
Types
type ClassValue =
| string
| number
| boolean
| undefined
| null
| ClassValue[]
| Record<string, any>;Dependencies
clsx- Conditional class name utilitytailwind-merge- Tailwind CSS class mergingclass-variance-authority- Component variant handling
License
MIT
