npm.io
0.10.0 • Published 1 month ago

@apcrda/ui

Licence
MIT
Version
0.10.0
Deps
25
Size
395 kB
Vulns
0
Weekly
0

@apcrda/ui

npm version license react peer

Production-ready React 19 component library for APCRDA government applications. 58 accessible components covering forms, data display, navigation, feedback, and layout — built on Tailwind CSS v4 with full dark-mode and theming support.

Features

  • 58 components — forms, tables, modals, navigation, charts, and more
  • Accessible — WCAG 2.1 AA target; keyboard navigation; ARIA semantics via Radix UI primitives
  • Fully typed — complete TypeScript types exported for every component and prop
  • Themeable — CSS custom properties, data-theme attribute, light and dark modes out of the box
  • Tree-shakeable — ESM build with preserved module boundaries; only the components you import are bundled
  • FormField integration — label, hint, and error wiring handled automatically via React context
  • DataTable — sort, filter, paginate, pin, resize, reorder, virtual scroll (1 000+ rows), CSV/Excel export

Installation

npm install @apcrda/ui

Peer dependencies — install these in your project if not already present:

npm install react@^19 react-dom@^19

Setup

1. Import the stylesheet

Add this once in your application entry point (e.g. main.tsx):

import '@apcrda/ui/styles.css'
2. Wrap with ThemeProvider
import { ThemeProvider } from '@apcrda/ui'

export default function Root() {
  return (
    <ThemeProvider defaultTheme="light">
      <App />
    </ThemeProvider>
  )
}

ThemeProvider sets the data-theme attribute on <html> and persists the user's preference to localStorage automatically.


Usage

Basic form
import { Button, FormField, Input, PasswordInput, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@apcrda/ui'

export function LoginForm() {
  return (
    <form className="flex flex-col gap-4">
      <FormField label="Employee ID" required>
        <Input placeholder="EMP-0001" />
      </FormField>

      <FormField label="Password" required>
        <PasswordInput placeholder="Enter password" />
      </FormField>

      <FormField label="Department">
        <Select>
          <SelectTrigger>
            <SelectValue placeholder="Select department" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="revenue">Revenue</SelectItem>
            <SelectItem value="survey">Survey</SelectItem>
          </SelectContent>
        </Select>
      </FormField>

      <Button type="submit">Sign in</Button>
    </form>
  )
}
FormField with validation error

FormField wires label, hint text, and error message to the input automatically — no manual id, aria-describedby, or aria-invalid threading required.

<FormField
  label="Aadhaar Number"
  hint="12-digit number printed on your Aadhaar card"
  error="Aadhaar number must be 12 digits"
  required
>
  <Input placeholder="XXXX XXXX XXXX" />
</FormField>
Government ID inputs
import { MaskedInput, PhoneInput, OtpInput, PinInput, CurrencyInput } from '@apcrda/ui'

// Aadhaar — auto-formats to XXXX XXXX XXXX
<MaskedInput mask="aadhaar" value={aadhaar} onChange={(e) => setAadhaar(e.target.value)} />

// PAN — auto-uppercases to AAAAA9999A
<MaskedInput mask="pan" value={pan} onChange={(e) => setPan(e.target.value)} />

// IFSC — auto-uppercases to AAAA0XXXXXX
<MaskedInput mask="ifsc" value={ifsc} onChange={(e) => setIfsc(e.target.value)} />

// Phone — +91 badge, emits raw 10-digit string
<PhoneInput value={phone} onChange={setPhone} />

// OTP — 6 visible digit cells
<OtpInput value={otp} onChange={setOtp} />

// PIN — 4 masked cells
<PinInput value={pin} onChange={setPin} />

// Currency — ₹ prefix, formats as ₹1,23,456.00 on blur
<CurrencyInput value={amount} onChange={setAmount} />
DataTable
import { DataTable } from '@apcrda/ui'
import type { ColumnDef } from '@apcrda/ui'

type Application = {
  id: string
  applicantName: string
  status: string
  submittedOn: string
}

const columns: ColumnDef<Application>[] = [
  { accessorKey: 'id',            header: 'Application ID', size: 160 },
  { accessorKey: 'applicantName', header: 'Applicant Name', size: 260 },
  { accessorKey: 'status',        header: 'Status',         size: 160 },
  { accessorKey: 'submittedOn',   header: 'Submitted On',   size: 160 },
]

export function ApplicationsTable({ data }: { data: Application[] }) {
  return (
    <DataTable
      data={data}
      columns={columns}
      pagination
      selectable
      columnFilters
      exportCsv
      stickyHeader
    />
  )
}
Notifications
import { Button } from '@apcrda/ui'
import { useToast } from '@apcrda/ui'

export function SaveButton() {
  const { toast } = useToast()

  return (
    <Button
      onClick={() =>
        toast({ title: 'Saved', description: 'Application updated successfully.', variant: 'success' })
      }
    >
      Save
    </Button>
  )
}

Components

Form & Input
Component Description
Input Text input — sizes sm/md/lg, left/right icons, loading spinner
Textarea Multi-line input with optional character counter
PasswordInput Password field with show/hide toggle
NumberInput Numeric stepper with min, max, step
CurrencyInput ₹ prefix, Indian locale (en-IN) comma formatting on blur
SearchInput Locked Search icon, X clear button, loading spinner
EmailInput Locked Mail icon, type="email"
UrlInput Locked Link2 icon, type="url"
PhoneInput Fixed +91 badge, 10-digit field, auto-space after 5th digit
OtpInput 6-cell OTP — auto-advance, backspace, paste support
PinInput 4-cell masked PIN — same keyboard behaviour as OTP
MaskedInput Government ID masks: Aadhaar, PAN, IFSC, Date (DD/MM/YYYY)
SignaturePad Canvas signature with mouse + touch drawing and clear button
FileUpload File picker — button or drag-and-drop dropzone variant
Checkbox Accessible checkbox with indeterminate state
RadioGroup Controlled radio button group
Switch Toggle switch
Select Single-value dropdown
MultiSelect Multi-value select with chips, count, or first+count summary
Combobox Searchable single-select dropdown
Autocomplete Free-text input with suggestions list
DatePicker Single-date calendar picker
DateRangePicker Date range calendar picker
FormField Label + input + hint + error wrapper (context-based)
Label Accessible form label
Data Display
Component Description
DataTable Full-featured table with 30+ capabilities
StatCard KPI metric card with trend indicator
Calendar Standalone month calendar
Timeline Vertical event timeline
ScrollArea Styled scrollable container
Progress Linear and indeterminate progress bar
Skeleton Loading placeholder shimmer
Navigation
Component Description
Navbar Top application bar
Sidebar Collapsible side navigation with nested items
Breadcrumb Page hierarchy trail
Tabs Horizontal tab panels
Pagination Page navigation controls
Stepper Multi-step workflow indicator
Accordion Expand/collapse content sections
Feedback & Overlay
Component Description
Alert Inline status message — info, success, warning, error
AlertDialog Blocking confirmation modal with cancel/confirm actions
Dialog General-purpose modal dialog
Drawer Slide-in side panel
Toast Ephemeral notification — top-right stack
Tooltip Hover hint on any element
Popover Anchored floating content panel
NotificationCenter Notification feed with read/unread state
EmptyState Zero-data placeholder with icon and action
Layout & Display
Component Description
Card Elevated surface container with optional header and footer
Divider Horizontal or vertical separator
Typography Heading and Text variants with size and weight props
Avatar User avatar with image or fallback initials
Badge Status and count pill
Tag Removable label chip
Spinner Accessible loading indicator
Kbd Keyboard shortcut key display
Utility
Component Description
CommandPalette ⌘K command search overlay
ContextMenu Right-click context menu

Theming

Light and dark mode

APCRDA UI uses CSS custom properties scoped to a data-theme attribute:

<html data-theme="light"></html>
<html data-theme="dark"></html>

ThemeProvider manages the attribute and syncs it with localStorage:

// Force a specific theme
<ThemeProvider defaultTheme="dark">…</ThemeProvider>

// Follow system preference
<ThemeProvider defaultTheme="system"></ThemeProvider>
Custom tokens

Override any design token after importing the stylesheet:

@import '@apcrda/ui/styles.css';

:root {
  --apcrda-color-primary:   #0052cc;
  --apcrda-color-primary-foreground: #ffffff;
  --apcrda-radius-md:       0.25rem;
  --apcrda-font-sans:       'Noto Sans Telugu', system-ui, sans-serif;
}
Tailwind CSS plugin

If you use Tailwind CSS in your app, register the APCRDA tokens as a plugin so your own classes can reference the same variables:

// tailwind.config.ts
import apcrdaPlugin from '@apcrda/ui/plugin.css'

export default {
  plugins: [apcrdaPlugin],
}

TypeScript

All components, props, and utility types are fully typed and exported from the package root:

import type {
  ButtonVariant,
  ColumnDef,
  DataTableProps,
  DataTableRowAction,
  InputProps,
  InputSize,
} from '@apcrda/ui'

The package ships .d.ts declaration files alongside the ESM bundle — no @types/ package needed.


Accessibility

  • Target: WCAG 2.1 AA
  • Keyboard navigation: all interactive components are fully keyboard-accessible
  • Screen readers: semantic HTML and Radix UI ARIA patterns throughout
  • Focus management: visible focus rings, focus trapping in modals and drawers
  • Motion: animations respect prefers-reduced-motion
  • Form errors: error messages are linked to inputs via aria-describedby and announced as role="alert"

DataTable capabilities

Feature Prop
Client-side pagination pagination
Server-side pagination serverPagination
Infinite scroll infiniteScroll
Global search built-in
Column filters (text / select / range / date) columnFilters
Multi-column sort multiSort
Column visibility toggle columnVisibility
Column resize columnResize
Column pinning columnPinning
Column reorder (drag and drop) columnReorder
Row grouping grouping
Row expand / sub-rows getRowCanExpand + renderSubRow
Tree data getSubRows
Row selection + bulk actions selectable + bulkActions
Row actions menu rowActions
Virtual scroll (1 000+ rows) virtualize
CSV export exportCsv
Excel export exportExcel
Print print
Copy to clipboard copy
Density toggle (compact / normal / spacious) density
Sticky header stickyHeader
Striped rows striped
Loading skeleton loading
Empty state emptyMessage / emptyDescription
Footer aggregations column footer + aggregationFn

Bundle size

The package is built as ESM with Vite in library mode. Unused components are tree-shaken automatically by your bundler. Runtime dependencies (Radix UI primitives, CVA, date-fns, etc.) are bundled. React and ReactDOM are peer dependencies and are not included.


Deployment (CI/CD)

Storybook is deployed as a Docker container to the applications VM and served at https://ui.theamaravaticity.com behind host Nginx with a Let's Encrypt certificate. The whole flow is driven by GitLab CI (.gitlab-ci.yml).

Deployment flow
  1. test — split into parallel jobs:
    • test:unit — executes linting, typechecking, and standard JSDOM unit tests.
    • test:storybook — executes browser-based interaction tests inside a Playwright container.
  2. publish:npm — bumps the patch version and publishes @apcrda/ui (skips if the version already exists).
  3. dockerize — builds the image and pushes :latest + :<commit-sha> to the GitLab Container Registry.
  4. deploy — runs docker/deploy.sh, which:
    • records the currently running image tag (the rollback target),
    • validates the compose config (docker compose config -q),
    • starts the new image with docker compose up -d,
    • health-checks the published port,
    • rolls back automatically if the health check fails,
    • prunes old images, keeping the last few for rollback.

The deploy job uses resource_group: production, so only one deployment can run at a time.

How to deploy

Just push to Dev:

git push origin Dev

Watch the pipeline in GitLab (CI/CD → Pipelines). A green deploy job means the new version is live and healthy. A red deploy job means the change was rejected and the previous version was restored automatically (see below).

How rollback works

Automatic — built into deploy.sh. If the freshly deployed image does not pass its health check within the timeout, the script restores the previously running image, re-checks it, and then fails the pipeline with a clear error. No manual action is required; the live site stays on the last known-good build.

Manual — to pin an older build (e.g. a bad image that still passed its health check), run docker/rollback.sh on the VM from the repo's docker/ directory:

# list images that are available locally on the host
docker images gitlab.apcrda.org:5050/crda/apcrda-design-system

# roll back to a specific commit SHA
./rollback.sh <git-short-sha>

The last KEEP_IMAGES (default 5) SHA-tagged images are always kept on the host so a recent rollback target is available.

Tuning

deploy.sh / rollback.sh read these environment variables (with sensible defaults), so nothing is hard-coded:

Variable Default Purpose
HEALTH_RETRIES 10 Health-check attempts before failing
HEALTH_INTERVAL 3 Seconds between attempts
KEEP_IMAGES 5 SHA-tagged images retained for rollback
HOST_PORT 8203 Published container port
DEPLOY_HOST 10.247.16.113 Host the health check targets
Troubleshooting
Symptom Likely cause / fix
deploy job red, log shows "rolled back to … (now healthy)" New image failed its health check; site is safely on the previous build. Check the app/build for the failing commit, fix, and push again.
deploy job red, "rollback … is unhealthy" Both new and previous images are unhealthy. SSH to the VM and inspect: docker logs apcrda-storybook. Manually roll back to a known-good SHA with ./rollback.sh <sha>.
Health check never passes Confirm the container is up: docker ps. Check it serves on the port: curl -I http://127.0.0.1:8203/. Review docker logs apcrda-storybook.
Pipeline stuck pending The deploy resource_group is busy with another run, or no runner is available. Check CI/CD → Pipelines and the runner status.
docker compose: not found in deploy The deploy job installs docker-cli-compose; confirm the before_script ran.
Site shows old version after green deploy Hard-refresh / clear CDN-or-browser cache. Verify the live tag: docker inspect --format '{{.Config.Image}}' apcrda-storybook.

License

MIT APCRDA