Usage Patterns

Practical ways to import, compose, and ship PyColors UI primitives in real SaaS interfaces without changing the public API.

UIUpdated September 18, 2026
UIUsage patterns

Use primitives as product building blocksLink to section

PyColors UI primitives are intentionally small. They give you accessible, token-driven pieces for SaaS interfaces, but your application owns the product state, data fetching, routing, validation, and business rules.

Use this page when you know which components you need and want to compose them into a real screen without reading package source.

Core idea

Import public primitives from @pycolors/ui, keep product logic in your app, and compose behavior at the page, feature, or pattern layer.

Try and copy the examplesLink to section

Complete UI installation first, including the Tailwind package scan and token CSS. Each Source tab below includes the complete file used by its Preview. Copy the file into your app; use the code-block copy button or select the code manually.

Dialog and Pagination use the optional lucide-react peer for their icons. Install it if your app does not already provide it:

pnpm add lucide-react

The four interactive examples keep state in memory. Refreshing resets them; no data is sent, stored remotely, or used to change an account. The form shows validation, the dialog commits or cancels an edit, the table changes pages, and the toast opens only after an action.

For example, save the form source as components/notification-settings.tsx and render it from a Next.js page:

app/page.tsx
import { NotificationSettings } from "../components/notification-settings";

export default function SettingsPage() {
  return <NotificationSettings />;
}

The page can remain a Server Component. Each interactive example declares its own client boundary and imports only React and the public @pycolors/ui entry point. Connect your app's persistence and server-side validation separately.

Required usage rulesLink to section

These rules are part of the supported usage contract.

  • Import from the public package entry point: @pycolors/ui.
  • Keep @pycolors/ui primitives app-agnostic. Do not put auth, billing, routing, or product-specific decisions inside shared UI components.
  • Preserve native semantics: buttons for actions, links for navigation, labels for form controls, and table markup for tabular data.
  • Add a client boundary only where your app uses browser-only behavior, local state, event handlers, or interactive Radix composition.
  • Use existing component docs for exact props and variants before inventing a local wrapper.
  • Keep accessibility responsibilities visible when composing overlays, forms, feedback, and dense data views.

These defaults are not hard requirements, but they keep product surfaces consistent.

  • Start with Button, Input, Card, Alert, and Table before adding local abstractions.
  • Use Dialog for focused decisions and short forms.
  • Use Sheet for side workflows that should keep page context visible.
  • Use Tabs for switching between related panels, not for primary navigation.
  • Use Toast for non-blocking feedback after actions.
  • Use Alert for inline, persistent feedback that affects the current task.
  • Use Pagination when the user needs stable page position and predictable record counts.

Import from the public entry pointLink to section

public-imports.tsx
import {
  Alert,
  AlertDescription,
  AlertTitle,
  Button,
  Checkbox,
  CheckboxField,
  CheckboxLabel,
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
  Input,
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
  Toast,
  ToastDescription,
  ToastProvider,
  ToastTitle,
  ToastViewport,
} from "@pycolors/ui";

Do not import from @pycolors/ui/src/* or @pycolors/ui/dist/*. Those paths are internal package details and are not part of the public contract.

Server and client component usageLink to section

Many PyColors primitives can be composed in a server-rendered page when you pass static content and no event handlers. The moment your file owns state, effects, or event handlers, move that composition to a small client component.

server-safe-summary-card.tsx
import { Alert, AlertDescription, AlertTitle, Button } from "@pycolors/ui";

export function BillingSummary() {
  return (
    <section className="space-y-4">
      <Alert>
        <AlertTitle>Plan active</AlertTitle>
        <AlertDescription>
          Your workspace is covered until the next renewal.
        </AlertDescription>
      </Alert>

      <Button asChild>
        <a href="/settings/billing">Manage billing</a>
      </Button>
    </section>
  );
}

Use a client boundary for event-driven composition.

Project: Website

Try renaming the project, submitting an empty name, and cancelling an edit. Escape dismisses the dialog and returns focus to its trigger. Saving changes only the displayed local name.

Client boundary rule

Make the smallest interactive component a client component. Keep surrounding pages and data loading server-side when possible.

Forms and settingsLink to section

Use forms when the user is editing product state. Keep labels, descriptions, and validation messages close to the control they explain.

Use an example address; nothing is sent.

Submit an empty or invalid email to see a connected field error, then enter builder@example.com and toggle the weekly summary. Saving updates the status message without navigating or making a network request.

Accessibility expectations:

  • Every form control needs a visible label or equivalent accessible name.
  • Validation errors should be text, not color alone.
  • Disabled controls should still communicate why the action is unavailable when that reason matters to the workflow.

OverlaysLink to section

Choose overlays based on the job.

NeedPrimitive
Confirm or complete a focused taskDialog
Work through a side panel without losing page contextSheet
Pick a compact row or menu actionDropdownMenu
filters-sheet.tsx
"use client";

import {
  Button,
  Checkbox,
  CheckboxField,
  CheckboxLabel,
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@pycolors/ui";

export function FiltersSheet() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="outline">Filters</Button>
      </SheetTrigger>

      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Filter customers</SheetTitle>
          <SheetDescription>
            Narrow the table without leaving the current view.
          </SheetDescription>
        </SheetHeader>

        <div className="mt-6 space-y-3">
          <CheckboxField>
            <Checkbox id="trialing" />
            <CheckboxLabel htmlFor="trialing">Trialing</CheckboxLabel>
          </CheckboxField>
          <CheckboxField>
            <Checkbox id="past-due" />
            <CheckboxLabel htmlFor="past-due">Past due</CheckboxLabel>
          </CheckboxField>
        </div>
      </SheetContent>
    </Sheet>
  );
}

Overlay accessibility expectations:

  • The trigger must have clear text or an accessible label.
  • Dialog and Sheet titles should describe the task.
  • Escape and focus-return behavior should remain intact.
  • Do not place long multi-step workflows in a modal when a page would be clearer.

Product sections with TabsLink to section

Use Tabs to switch between related panels inside the same page.

settings-tabs.tsx
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@pycolors/ui";

export function SettingsTabs() {
  return (
    <Tabs defaultValue="profile">
      <TabsList aria-label="Settings sections">
        <TabsTrigger value="profile">Profile</TabsTrigger>
        <TabsTrigger value="billing">Billing</TabsTrigger>
        <TabsTrigger value="security">Security</TabsTrigger>
      </TabsList>

      <TabsContent value="profile">Profile form</TabsContent>
      <TabsContent value="billing">Billing controls</TabsContent>
      <TabsContent value="security">Security settings</TabsContent>
    </Tabs>
  );
}

Use navigation links instead of tabs when the destination is a different route, bookmarkable page, or primary information architecture.

Tables and paginationLink to section

Use Table for comparable records. Add Pagination when the result set is split into stable pages.

Example workspace members
NameRole
Ava MartinOwner
Noah ChenMember

Page 1 of 3

Move through all three pages. Previous and Next are disabled at the boundaries; the current page is announced and marked with aria-current="page". These buttons paginate local example records. Use PaginationLink asChild with real links when your app owns paginated routes.

Accessibility expectations:

  • Keep column headers meaningful.
  • Use captions or nearby headings when the table purpose is not obvious.
  • Keep pagination controls keyboard-operable and labeled for the collection they control.

Feedback patternsLink to section

Use inline feedback when the message affects the current surface. Use toast feedback when the message confirms a completed action without blocking the user.

inline-feedback.tsx
import { Alert, AlertDescription, AlertTitle } from "@pycolors/ui";

export function PaymentIssueAlert() {
  return (
    <Alert variant="destructive">
      <AlertTitle>Payment failed</AlertTitle>
      <AlertDescription>
        Update your payment method to keep the workspace active.
      </AlertDescription>
    </Alert>
  );
}

    Activate the button to simulate a successful save, dismiss the notification, and trigger it again. Dismissing returns focus to the trigger. It also closes after five seconds when not paused by interaction. In your app, open the toast after the mutation succeeds. Keep blocking failures in persistent inline feedback. This standalone example owns one provider and viewport; reuse your existing provider when integrating it.

    Common mistakesLink to section

    Prefer

    • importing public primitives from @pycolors/ui
    • using Button asChild when a link should look like a button
    • keeping state, mutations, and routing in app code
    • using Alert for persistent task feedback
    • using Toast for non-blocking action confirmation
    • using Table only for structured, comparable records

    Avoid

    • importing from @pycolors/ui/src/* or @pycolors/ui/dist/*
    • using a clickable div instead of a real button or link
    • putting billing, auth, or product rules inside UI primitives
    • using a toast for a blocking error the user must fix now
    • using tabs for primary route navigation
    • hiding labels or validation behind color alone

    Next step

    Start with the component docs for exact props, then use Patterns when the interaction needs state, async behavior, or repeated product decisions.