Files
Primora/apps/frontend/COMPONENT_GUIDE.md
2026-04-10 12:03:31 +02:00

10 KiB

Primora Component Quick Reference

A quick reference guide for using Primora's enhanced UI components.


🚀 Quick Start

import {
  Button,
  Card,
  Modal,
  Tooltip,
  Dropdown,
  Progress,
  Tabs,
  toast,
  ToastContainer,
} from "./components";

📦 Components

Button

// Primary action
<Button variant="primary" onClick={handleClick}>
  Create Project
</Button>

// With icon
<Button variant="secondary" icon={<Icons.Plus />}>
  Add Item
</Button>

// Loading state
<Button variant="primary" loading={isSubmitting()}>
  Saving...
</Button>

// Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>

// Variants
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>

Card

// Basic card
<Card>
  <CardHeader title="Project Details" />
  <p>Card content goes here</p>
</Card>

// With eyebrow and description
<Card variant="elevated">
  <CardHeader 
    eyebrow="Overview"
    title="Dashboard"
    description="Monitor your metrics"
  />
  <CardContent>
    {/* Content */}
  </CardContent>
  <CardFooter align="right">
    <Button>Action</Button>
  </CardFooter>
</Card>

// Stat card
<StatCard
  label="Total Users"
  value={1234}
  icon={<Icons.Users />}
  trend="up"
  trendValue="+12%"
/>

// Interactive card
<Card variant="interactive" onClick={handleClick}>
  Clickable card
</Card>

Modal

const [open, setOpen] = createSignal(false);

<Modal
  open={open()}
  onClose={() => setOpen(false)}
  title="Confirm Action"
  description="Are you sure you want to proceed?"
  size="md"
>
  <p>Modal content</p>
  <ModalFooter align="right">
    <Button variant="secondary" onClick={() => setOpen(false)}>
      Cancel
    </Button>
    <Button variant="primary" onClick={handleConfirm}>
      Confirm
    </Button>
  </ModalFooter>
</Modal>

Tooltip

<Tooltip content="Delete this item" placement="top">
  <Button variant="ghost" icon={<Icons.Trash />} />
</Tooltip>

// With delay
<Tooltip content="Helpful hint" delay={500}>
  <span>Hover me</span>
</Tooltip>

Dropdown

<Dropdown
  trigger={<Button variant="secondary">Actions</Button>}
  placement="bottom-end"
  items={[
    {
      id: "edit",
      label: "Edit",
      icon: <Icons.Edit />,
      onClick: () => handleEdit(),
    },
    {
      id: "duplicate",
      label: "Duplicate",
      icon: <Icons.Copy />,
      onClick: () => handleDuplicate(),
    },
    { id: "divider", divider: true },
    {
      id: "delete",
      label: "Delete",
      icon: <Icons.Trash />,
      danger: true,
      onClick: () => handleDelete(),
    },
  ]}
/>

Progress

// Linear progress
<Progress 
  value={75} 
  max={100} 
  showLabel 
  label="Upload Progress"
  variant="success"
/>

// Circular progress
<CircularProgress 
  value={60} 
  showLabel 
  size={80}
  variant="primary"
/>

// Spinner
<Spinner size="md" variant="primary" />

Tabs

<Tabs
  variant="pills"
  defaultTab="overview"
  onChange={(tabId) => console.log(tabId)}
  tabs={[
    {
      id: "overview",
      label: "Overview",
      icon: <Icons.Dashboard />,
      content: <OverviewPanel />,
    },
    {
      id: "settings",
      label: "Settings",
      badge: "3",
      content: <SettingsPanel />,
    },
    {
      id: "disabled",
      label: "Disabled",
      disabled: true,
      content: null,
    },
  ]}
/>

// Variants
<Tabs variant="default" tabs={...} />
<Tabs variant="pills" tabs={...} />
<Tabs variant="underline" tabs={...} />

Toast

// Add to app root
<ToastContainer />

// Use anywhere
toast.success("Operation successful!");
toast.error("Something went wrong", "Error");
toast.warning("Please review your changes");
toast.info("New update available", undefined, 10000);

// Manual control
const id = toast.show({
  variant: "info",
  message: "Processing...",
  duration: 0, // Won't auto-dismiss
});

// Dismiss manually
toast.dismiss(id);
toast.dismissAll();

Input

// Text input
<Input
  label="Project Name"
  placeholder="Enter name"
  value={name()}
  onInput={(e) => setName(e.currentTarget.value)}
  error={errors().name}
/>

// Textarea
<Textarea
  label="Description"
  placeholder="Enter description"
  rows={4}
  value={description()}
  onInput={(e) => setDescription(e.currentTarget.value)}
/>

// Select
<Select
  label="Status"
  value={status()}
  onChange={(e) => setStatus(e.currentTarget.value)}
>
  <option value="active">Active</option>
  <option value="inactive">Inactive</option>
</Select>

// File input
<FileInput
  label="Upload File"
  accept="image/*"
  onChange={(e) => setFile(e.currentTarget.files?.[0])}
/>

Table

<Table
  columns={[
    { key: "name", header: "Name", width: "40%" },
    { key: "email", header: "Email" },
    { 
      key: "status", 
      header: "Status",
      render: (value) => <StatusBadge status={value} />
    },
    {
      key: "actions",
      header: "",
      align: "right",
      render: (_, row) => (
        <Button 
          variant="ghost" 
          size="sm"
          onClick={() => handleEdit(row)}
        >
          Edit
        </Button>
      ),
    },
  ]}
  data={users()}
  rowKey={(row) => row.id}
  onRowClick={(row) => console.log(row)}
  emptyMessage="No users found"
/>

// With pagination
<DataTable
  columns={columns}
  data={currentPage()}
>
  <Pagination
    currentPage={page()}
    totalPages={totalPages()}
    onPageChange={setPage}
  />
</DataTable>

Badge

<Badge variant="primary">New</Badge>
<Badge variant="success">Active</Badge>
<Badge variant="warning">Pending</Badge>
<Badge variant="error">Failed</Badge>
<Badge variant="neutral">Draft</Badge>

// Status badge
<StatusBadge status="active" />
<StatusBadge status="pending" />
<StatusBadge status="completed" />
<StatusBadge status="error" />

Message

<Message variant="info" title="Information">
  This is an informational message.
</Message>

<Message variant="success" icon={<Icons.Check />}>
  Operation completed successfully!
</Message>

<Message 
  variant="error" 
  dismissible 
  onDismiss={() => setError(null)}
>
  {error()}
</Message>

Layout

<Layout
  sidebar={
    <Sidebar
      items={navItems}
      activeId={activeView()}
      onSelect={setActiveView}
      header={<Logo />}
      footer={<UserMenu />}
    />
  }
  header={
    <Header
      title="Dashboard"
      subtitle="Overview"
      actions={<Button>Action</Button>}
    />
  }
>
  <PageHeader
    eyebrow="Overview"
    title="Dashboard"
    description="Monitor your metrics"
    actions={<Button variant="primary">Create</Button>}
  />
  
  {/* Page content */}
</Layout>

🎨 CSS Utilities

Animations

<div class="animate-fade-in">Fade in</div>
<div class="animate-slide-up">Slide up</div>
<div class="animate-scale-in">Scale in</div>
<div class="animate-bounce-in">Bounce in</div>

Effects

<Card class="card-hover-lift">Lifts on hover</Card>
<Card class="spotlight">Shine effect</Card>
<div class="glass">Frosted glass</div>
<span class="text-shimmer">Shimmer text</span>

Loading States

<div class="skeleton h-4 w-32" />
<div class="skeleton-wave h-20 w-full" />
<SkeletonCard lines={3} />

Stagger Animations

<div class="stagger-fade-in">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</div>

🎯 Common Patterns

Form with Validation

<form onSubmit={handleSubmit} class="space-y-4">
  <Input
    label="Email"
    type="email"
    value={email()}
    onInput={(e) => setEmail(e.currentTarget.value)}
    error={errors().email}
  />
  
  <Input
    label="Password"
    type="password"
    value={password()}
    onInput={(e) => setPassword(e.currentTarget.value)}
    error={errors().password}
  />
  
  <Button 
    type="submit" 
    variant="primary" 
    loading={submitting()}
    class="w-full"
  >
    Sign In
  </Button>
</form>

Confirmation Dialog

const [showConfirm, setShowConfirm] = createSignal(false);

<Modal
  open={showConfirm()}
  onClose={() => setShowConfirm(false)}
  title="Confirm Deletion"
  description="This action cannot be undone."
  size="sm"
>
  <ModalFooter align="right">
    <Button 
      variant="secondary" 
      onClick={() => setShowConfirm(false)}
    >
      Cancel
    </Button>
    <Button 
      variant="danger" 
      onClick={handleDelete}
    >
      Delete
    </Button>
  </ModalFooter>
</Modal>

Dashboard Grid

<div class="dashboard-grid">
  <StatCard label="Users" value={users()} />
  <StatCard label="Revenue" value={`$${revenue()}`} />
  <StatCard label="Growth" value="+12%" trend="up" />
</div>

Action Menu

<Dropdown
  trigger={
    <Button variant="ghost" size="sm">
      <Icons.Menu />
    </Button>
  }
  items={[
    { id: "view", label: "View Details", icon: <Icons.Eye /> },
    { id: "edit", label: "Edit", icon: <Icons.Edit /> },
    { id: "divider", divider: true },
    { id: "delete", label: "Delete", icon: <Icons.Trash />, danger: true },
  ]}
/>

Loading State

<Show 
  when={!loading()} 
  fallback={
    <div class="flex items-center justify-center py-12">
      <Spinner size="lg" />
    </div>
  }
>
  {/* Content */}
</Show>

Empty State

<EmptyState
  icon={<Icons.Inbox class="h-12 w-12" />}
  title="No projects yet"
  description="Get started by creating your first project"
  action={
    <Button variant="primary" onClick={handleCreate}>
      Create Project
    </Button>
  }
/>

💡 Tips

Performance

  • Use createMemo for expensive computations
  • Leverage SolidJS fine-grained reactivity
  • Avoid unnecessary re-renders
  • Use Show instead of ternary for conditional rendering

Accessibility

  • Always provide labels for inputs
  • Use semantic HTML
  • Test keyboard navigation
  • Ensure color contrast

Styling

  • Use Tailwind utilities first
  • Leverage CSS custom properties for theming
  • Keep component styles scoped
  • Use consistent spacing

State Management

  • Keep state close to where it's used
  • Use signals for reactive state
  • Lift state only when necessary
  • Consider context for global state

  • apps/frontend/src/index.css - Global styles and design tokens
  • apps/frontend/tailwind.config.cjs - Tailwind configuration
  • FRONTEND_ENHANCEMENTS.md - Detailed enhancement documentation
  • project_frontend.md - Design system specification

Happy coding! 🚀