npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@true-armor/ta-atoms2-public

v0.3.6

Published

textarea / input

Readme

@true-armor/atoms

A comprehensive React component library built with TypeScript, Tailwind CSS, and Flowbite React. Features 20+ production-ready components with compound component patterns for maximum flexibility.

Whenever any changes are made to the components, please follow the steps below to publish an updated version.

1. Increment the patch version in package.json:

  "version": "0.0.7",

Create Build

  npm run build

Publish the package to the npm registry:

  npm publish

📦 Installation

npm install @true-armor/atoms

Required Peer Dependencies

npm install react react-dom flowbite flowbite-react lucide-react

⚙️ Setup

1. Import Styles

Add this to your main entry point (main.tsx or App.tsx):

import "@true-armor/atoms/styles.css";

2. Configure Tailwind CSS

Update your tailwind.config.js or tailwind.config.cjs:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
    // ⚠️ IMPORTANT: Include the library components
    "./node_modules/@true-armor/atoms/dist/**/*.{js,cjs}",
  ],
  theme: {
    extend: {
      fontFamily: {
        sans: ['Montserrat', 'sans-serif'],
      }
    }
  },
  plugins: []
}

3. Add Montserrat Font

Add to your index.html:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap" rel="stylesheet">

🚀 Quick Start

import { ButtonTA, ButtonTALabel } from "@true-armor/atoms";

function App() {
  return (
    <ButtonTA buttonType="primary">
      <ButtonTALabel>Click Me</ButtonTALabel>
    </ButtonTA>
  );
}

📚 Components Reference

ButtonTA

Primary, secondary, and tertiary button variants with icon support.

Import:

import { ButtonTA, ButtonTALabel, ButtonTAIcon, ButtonTAContent } from "@true-armor/atoms";
import { Download } from "lucide-react";

Usage:

// Simple button
<ButtonTA buttonType="primary">
  <ButtonTALabel>Click Me</ButtonTALabel>
</ButtonTA>

// Button with icon
<ButtonTA buttonType="secondary">
  <ButtonTAContent>
    <ButtonTAIcon>
      <Download size={16} />
    </ButtonTAIcon>
    <ButtonTALabel>Download</ButtonTALabel>
  </ButtonTAContent>
</ButtonTA>

// Tertiary button
<ButtonTA buttonType="tertiary" onClick={() => console.log('clicked')}>
  <ButtonTALabel>Tertiary</ButtonTALabel>
</ButtonTA>

Props:

  • buttonType: "primary" | "secondary" | "tertiary" (default: "primary")
  • onClick: () => void
  • disabled: boolean

TabsTA

Tabbed interface with multiple variants.

Import:

import { TabsTA, TabsTAList, TabsTAItem, TabsTAPanel } from "@true-armor/atoms";
import { Home, Settings } from "lucide-react";

Usage:

<TabsTA variant="tertiary" color="#173B4E">
  <TabsTAList>
    <TabsTAItem index={0} title="Home" icon={Home} />
    <TabsTAItem index={1} title="Settings" icon={Settings} />
    <TabsTAItem index={2} title="Profile" />
  </TabsTAList>
  <TabsTAPanel index={0}>
    <div className="p-4">Home content goes here</div>
  </TabsTAPanel>
  <TabsTAPanel index={1}>
    <div className="p-4">Settings content goes here</div>
  </TabsTAPanel>
  <TabsTAPanel index={2}>
    <div className="p-4">Profile content goes here</div>
  </TabsTAPanel>
</TabsTA>

Props:

  • variant: "primary" | "secondary" | "tertiary" (default: "tertiary")
  • color: string (default: "#173B4E")
  • TabsTAItem.index: number (required)
  • TabsTAItem.title: string (required)
  • TabsTAItem.icon: React.ComponentType (optional)

ToolTipTA

Dark and light tooltip variants.

Import:

import { ToolTipTA, ToolTipTAContent, ToolTipTAArrow, ToolTipTAWrapper } from "@true-armor/atoms";

Usage:

// Dark tooltip
<ToolTipTA type="dark">
  <button className="px-4 py-2 bg-gray-900 text-white rounded">
    Hover me (Dark)
  </button>
  <ToolTipTAWrapper>
    <ToolTipTAContent>This is a dark tooltip</ToolTipTAContent>
    <ToolTipTAArrow />
  </ToolTipTAWrapper>
</ToolTipTA>

// Light tooltip
<ToolTipTA type="light">
  <button className="px-4 py-2 bg-gray-500 text-white rounded">
    Hover me (Light)
  </button>
  <ToolTipTAWrapper>
    <ToolTipTAContent>This is a light tooltip</ToolTipTAContent>
    <ToolTipTAArrow />
  </ToolTipTAWrapper>
</ToolTipTA>

Props:

  • type: "dark" | "light" (default: "dark")

StepperTA

Multi-step progress indicator.

Import:

import { StepperTA, StepperTAStep, StepperTAConnector } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [currentStep, setCurrentStep] = useState(2);

<StepperTA currentStep={currentStep} color="#173B4E">
  <StepperTAStep label="Step 1" />
  <StepperTAStep label="Step 2" />
  <StepperTAStep label="Step 3" />
  <StepperTAStep label="Step 4" />
</StepperTA>

Props:

  • currentStep: number (default: 1)
  • color: string (default: "#173B4E")

DropDownTA

Dropdown selector with optional search functionality and avatar support. Uses a compound component pattern for maximum flexibility.

Import:

import { 
  DropDownTA,
  DropDownTATrigger,
  DropDownTAContent,
  DropDownTASearch,
  DropDownTAItem,
  DropDownTAEmpty
} from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [isOpen, setIsOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
const [selectedUser, setSelectedUser] = useState(null);

const teamMembers = [
  { 
    id: 1, 
    name: "Leslie Alexander", 
    avatar: "https://randomuser.me/api/portraits/women/44.jpg", 
    isYou: true 
  },
  { 
    id: 2, 
    name: "Michael Gough", 
    avatar: "https://randomuser.me/api/portraits/men/32.jpg" 
  },
  { 
    id: 3, 
    name: "Lana Byrd", 
    avatar: "https://randomuser.me/api/portraits/women/68.jpg" 
  },
];

const filteredItems = teamMembers.filter((item) =>
  item.name.toLowerCase().includes(searchTerm.toLowerCase())
);

const handleSelect = (item) => {
  setSelectedUser(item);
  setIsOpen(false);
  setSearchTerm("");
};

<DropDownTA isOpen={isOpen} onToggle={setIsOpen}>
  <DropDownTATrigger
    selectedItem={selectedUser}
    placeholder="Select a team member"
    getItemLabel={(item) => item.name}
  />
  <DropDownTAContent>
    <DropDownTASearch
      searchTerm={searchTerm}
      onSearchChange={setSearchTerm}
      placeholder="Search team members..."
    />
    {filteredItems.map((item) => (
      <DropDownTAItem
        key={item.id}
        item={item}
        getItemLabel={(item) => item.name}
        onSelect={handleSelect}
      />
    ))}
    {filteredItems.length === 0 && (
      <DropDownTAEmpty message="No results found" />
    )}
  </DropDownTAContent>
</DropDownTA>

Simple Usage (without search):

const [isOpen, setIsOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);

const users = [
  { id: 1, name: "John Doe", email: "[email protected]" },
  { id: 2, name: "Jane Smith", email: "[email protected]" },
];

<DropDownTA isOpen={isOpen} onToggle={setIsOpen}>
  <DropDownTATrigger
    selectedItem={selectedUser}
    placeholder="Select user"
    getItemLabel={(item) => item.name}
  />
  <DropDownTAContent>
    {users.map((user) => (
      <DropDownTAItem
        key={user.id}
        item={user}
        getItemLabel={(item) => item.name}
        onSelect={(item) => {
          setSelectedUser(item);
          setIsOpen(false);
        }}
      />
    ))}
  </DropDownTAContent>
</DropDownTA>

Props:

DropDownTA (Main Component)

  • isOpen: boolean (default: false) - Controlled state for dropdown visibility
  • onToggle: (open: boolean) => void (default: () => {}) - Callback when dropdown state changes
  • className: string (optional) - Additional CSS classes
  • children: React.ReactNode (required) - DropDownTATrigger and DropDownTAContent components

DropDownTATrigger

  • selectedItem: any (default: null) - Currently selected item object
  • placeholder: string (default: "Select an item") - Placeholder text when no item is selected
  • getItemLabel: (item: any) => string (default: (item) => item?.name || "") - Function to extract label from item
  • isOpen: boolean (auto-injected from DropDownTA) - Whether dropdown is open
  • onToggle: (open: boolean) => void (auto-injected from DropDownTA) - Toggle handler

DropDownTAContent

  • isOpen: boolean (auto-injected from DropDownTA) - Whether dropdown is open
  • children: React.ReactNode (required) - DropDownTASearch, DropDownTAItem, and DropDownTAEmpty components

DropDownTASearch

  • searchTerm: string (default: "") - Current search value
  • onSearchChange: (term: string) => void (default: () => {}) - Callback when search term changes
  • placeholder: string (default: "Search...") - Search input placeholder

DropDownTAItem

  • item: any (required) - Item object to display
  • getItemLabel: (item: any) => string (default: (item) => item?.name || "") - Function to extract label
  • onSelect: (item: any) => void (default: () => {}) - Callback when item is selected

DropDownTAEmpty

  • message: string (default: "No results found") - Message to display when no items match

Features:

  • ✅ Avatar support (displays item.avatar if available)
  • ✅ "(You)" indicator (displays when item.isYou === true)
  • ✅ Search/filter functionality
  • ✅ Customizable item rendering
  • ✅ Empty state handling
  • ✅ Fully controlled component pattern

AccordionTA

Collapsible content panels.

Import:

import { AccordionTA, AccordionPanel, AccordionTitle, AccordionContent } from "@true-armor/atoms";

Usage:

<AccordionTA>
  <AccordionPanel>
    <AccordionTitle>AI Findings</AccordionTitle>
    <AccordionContent>
      <p>Your content here...</p>
    </AccordionContent>
  </AccordionPanel>
  <AccordionPanel>
    <AccordionTitle>Analysis Results</AccordionTitle>
    <AccordionContent>
      <p>Detailed analysis results...</p>
    </AccordionContent>
  </AccordionPanel>
</AccordionTA>

AlertTA

Alert messages with multiple types.

Import:

import { AlertTA, Alert, AlertIcon, AlertTitle, AlertDescription } from "@true-armor/atoms";
import { CheckCircle2, AlertCircle } from "lucide-react";

Usage:

// Simple alert
<AlertTA 
  type="success" 
  title="Success!" 
  message="Operation completed successfully" 
  icon={<CheckCircle2 size={20} />}
/>

// Compound alert
<Alert type="error">
  <AlertCircle size={20} className="text-red-600" />
  <div>
    <AlertTitle type="error">Error</AlertTitle>
    <AlertDescription>Something went wrong</AlertDescription>
    </div>
</Alert>

Props:

  • type: "success" | "error" | "warning" | "info"
  • title: string
  • message: string
  • icon: React.ReactNode

BadgeTA

Status indicators and labels.

Import:

import { BadgeTA, Badge, BadgeIcon, BadgeLabel } from "@true-armor/atoms";
import { CheckCircle2, Star, AlertTriangle } from "lucide-react";

Usage:

// Simple badge
<BadgeTA label="Active" color="success" icon={CheckCircle2} />
<BadgeTA label="New" color="info" icon={Star} />
<BadgeTA label="Warning" color="warning" icon={AlertTriangle} />

// Compound badge
<Badge color="success">
  <BadgeIcon icon={CheckCircle2} />
  <BadgeLabel>Verified</BadgeLabel>
</Badge>

Props:

  • color: "success" | "info" | "warning" | "failure" | "purple" | "yellow" | "blue" | "green" | "gray" | "indigo" | "pink"
  • label: string
  • icon: React.ComponentType

BreadCrumbTA

Navigation breadcrumbs.

Import:

import { BreadCrumbTA, BreadCrumbItem, BreadCrumbSeperator } from "@true-armor/atoms";
import { Home, ChevronRight, ShoppingBag, Package } from "lucide-react";

Usage:

<BreadCrumbTA>
  <BreadCrumbItem label="Home" icon={Home} href="/" />
  <BreadCrumbSeperator icon={ChevronRight} />
  <BreadCrumbItem label="Products" icon={ShoppingBag} href="/products" />
  <BreadCrumbSeperator icon={ChevronRight} />
  <BreadCrumbItem label="Current" icon={Package} href="#" active />
</BreadCrumbTA>

Props:

  • BreadCrumbItem.label: string (required)
  • BreadCrumbItem.icon: React.ComponentType
  • BreadCrumbItem.href: string
  • BreadCrumbItem.active: boolean

CheckboxTA

Custom checkbox component.

Import:

import { CheckboxTA, CheckboxTAInput, CheckboxTALabel } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [isChecked, setIsChecked] = useState(false);

<CheckboxTA checked={isChecked} onChange={setIsChecked}>
  <CheckboxTAInput />
  <CheckboxTALabel>Accept terms and conditions</CheckboxTALabel>
</CheckboxTA>

Props:

  • checked: boolean (required)
  • onChange: (checked: boolean) => void (required)
  • disabled: boolean

ContextMenuTA

Context menu with actions.

Import:

import { ContextMenuTA, ContextMenuTAItem, ContextMenuTAIcon } from "@true-armor/atoms";
import { Edit2Icon, Trash2, Share2 } from "lucide-react";

Usage:

<ContextMenuTA onSelect={(item) => console.log("Clicked:", item)}>
  <ContextMenuTAItem label="Edit" icon={Edit2Icon} />
  <ContextMenuTAItem label="Delete" icon={Trash2} />
  <ContextMenuTAItem label="Share" icon={Share2} />
</ContextMenuTA>

Props:

  • onSelect: (label: string) => void
  • ContextMenuTAItem.label: string (required)
  • ContextMenuTAItem.icon: React.ComponentType

CustomRadioTA

Custom radio button.

Import:

import { CustomRadioTA, CustomRadioTAInput, CustomRadioTALabel } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [selected, setSelected] = useState("option1");

<CustomRadioTA
  name="demo-radio"
  checked={selected === "option1"}
  onChange={() => setSelected("option1")}
>
  <CustomRadioTAInput />
  <CustomRadioTALabel>Option 1</CustomRadioTALabel>
</CustomRadioTA>

<CustomRadioTA
  name="demo-radio"
  checked={selected === "option2"}
  onChange={() => setSelected("option2")}
>
  <CustomRadioTAInput />
  <CustomRadioTALabel>Option 2</CustomRadioTALabel>
</CustomRadioTA>

Props:

  • name: string (required)
  • checked: boolean (required)
  • onChange: () => void (required)
  • disabled: boolean

DatePickerTA

Date selection component.

Import:

import { DatePickerTA, DatePickerTAInput, DatePickerTACalendar } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [selectedDate, setSelectedDate] = useState<Date | null>(null);

<DatePickerTA 
  initialDate={selectedDate || new Date()} 
  onDateChange={(date) => setSelectedDate(date)}
>
  <DatePickerTAInput placeholder="Select date" />
  <DatePickerTACalendar />
</DatePickerTA>

Props:

  • initialDate: Date (required)
  • onDateChange: (date: Date) => void (required)

FileInputTA

File upload with drag-and-drop.

Import:

import { FileInputTA, FileInputTADropzone, FileInputTAIcon, FileInputTAText, FileInputTAButton } from "@true-armor/atoms";

Usage:

<FileInputTA accept="image/*">
  <FileInputTADropzone>
    <FileInputTAIcon />
    <FileInputTAText 
      mainText="Click to upload" 
      subText="SVG, PNG, JPG or GIF (MAX. 800x400px)" 
    />
    <FileInputTAButton label="Browse File" />
  </FileInputTADropzone>
</FileInputTA>

Props:

  • accept: string (e.g., "image/*", ".pdf")
  • onChange: (files: FileList | null) => void
  • multiple: boolean

InputTA

Text input and textarea.

Import:

import { InputTA, InputTALabel, InputTAField, InputTAError } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [email, setEmail] = useState("");
const [message, setMessage] = useState("");

// Text input
<InputTA id="email" multiline={false}>
  <InputTALabel>Email Address</InputTALabel>
  <InputTAField 
    type="email" 
    placeholder="Enter your email"
    value={email}
    onChange={(e) => setEmail(e.target.value)}
  />
</InputTA>

// Textarea
<InputTA id="message" multiline={true} rows={5}>
  <InputTALabel>Message</InputTALabel>
  <InputTAField 
    placeholder="Enter your message"
    value={message}
    onChange={(e) => setMessage(e.target.value)}
  />
</InputTA>

// With error
<InputTA id="email-error">
  <InputTALabel>Email</InputTALabel>
  <InputTAField 
    type="email" 
    error="Invalid email address"
  />
  <InputTAError>Invalid email address</InputTAError>
</InputTA>

Props:

  • id: string (required)
  • multiline: boolean (default: false)
  • rows: number (for textarea)
  • InputTAField.type: string
  • InputTAField.error: string
  • InputTAField.showPasswordToggle: boolean

ModalTA

Modal dialog.

Import:

import { ModalTA, ModalTAHeader, ModalTABody, ModalTAFooter } from "@true-armor/atoms";
import { ButtonTA, ButtonTALabel } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [isModalOpen, setIsModalOpen] = useState(false);

<ButtonTA buttonType="primary" onClick={() => setIsModalOpen(true)}>
  <ButtonTALabel>Open Modal</ButtonTALabel>
</ButtonTA>

<ModalTA open={isModalOpen} onClose={() => setIsModalOpen(false)}>
  <ModalTAHeader>Confirm Action</ModalTAHeader>
  <ModalTABody>
    <p>Are you sure you want to proceed?</p>
  </ModalTABody>
  <ModalTAFooter>
    <ButtonTA buttonType="secondary" onClick={() => setIsModalOpen(false)}>
      <ButtonTALabel>Cancel</ButtonTALabel>
    </ButtonTA>
    <ButtonTA buttonType="primary" onClick={() => setIsModalOpen(false)}>
      <ButtonTALabel>Confirm</ButtonTALabel>
    </ButtonTA>
  </ModalTAFooter>
</ModalTA>

Props:

  • open: boolean (required)
  • onClose: () => void (required)

PaginationTA

Pagination controls.

Import:

import { PaginationTA, PaginationTAControls } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [currentPage, setCurrentPage] = useState(1);

// Default pagination
<PaginationTA 
  totalPages={10} 
  initialPage={currentPage}
  onChange={(page) => setCurrentPage(page)}
/>

// Custom controls
<PaginationTA totalPages={10} initialPage={1} onChange={(page) => console.log(page)}>
  <PaginationTAControls previousLabel="<" nextLabel=">" />
</PaginationTA>

Props:

  • totalPages: number (required)
  • initialPage: number (default: 1)
  • onChange: (page: number) => void (required)

ProgressBarTA

Progress indicator.

Import:

import { ProgressBarTA, ProgressBarTALabel, ProgressBarTABar } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [progress, setProgress] = useState(75);

<ProgressBarTA progress={progress} height="h-2">
  <ProgressBarTALabel />
  <ProgressBarTABar />
</ProgressBarTA>

Props:

  • progress: number (0-100, required)
  • height: string (Tailwind height class)

RangeSliderTA

Range slider input.

Import:

import { RangeSliderTA, RangeSliderTAInput, RangeSliderTATooltip, RangeSliderTAMarks, RangeSliderTAMark } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [sliderValue, setSliderValue] = useState(50);

// Controlled mode
<RangeSliderTA 
  min={0} 
  max={100} 
  value={sliderValue} 
  step={1} 
  onChange={(val) => setSliderValue(val)}
>
  <RangeSliderTATooltip />
  <RangeSliderTAInput />
  <RangeSliderTAMarks />
</RangeSliderTA>

// Uncontrolled mode
<RangeSliderTA min={0} max={100} defaultValue={50} step={1}>
  <RangeSliderTATooltip />
  <RangeSliderTAInput />
  <RangeSliderTAMarks />
</RangeSliderTA>

Props:

  • min: number (default: 0)
  • max: number (default: 100)
  • value: number (controlled)
  • defaultValue: number (uncontrolled)
  • step: number (default: 1)
  • onChange: (value: number) => void

TableTA

Data table component.

Import:

import { TableTA } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [page, setPage] = useState(0);
const rowsPerPage = 10;

const columns = [
  { key: "name", label: "Name", header: "Name" },
  { key: "email", label: "Email", header: "Email" },
  { key: "status", label: "Status", header: "Status" }
];

const tableData = [
  { id: 1, name: "John Doe", email: "[email protected]", status: "Active" },
  { id: 2, name: "Jane Smith", email: "[email protected]", status: "Inactive" },
];

const paginatedData = tableData.slice(
  page * rowsPerPage,
  (page + 1) * rowsPerPage
);

<TableTA
  columns={columns}
  tableData={paginatedData}
  length={tableData.length}
  page={page}
  setPage={setPage}
  rowsCount={rowsPerPage}
/>

Props:

  • columns: Array<{ key: string; label?: string; header?: string }> (required)
  • tableData: any[] (required)
  • length: number (total items)
  • page: number
  • setPage: (page: number) => void
  • rowsCount: number
  • onRowClick: (row: any) => void
  • striped: boolean
  • hoverable: boolean

ToastTA

Toast notifications.

Import:

import { ToastTA, ToastTAItem, ToastTAIcon, ToastTAMessage, ToastTAProgress } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [toasts, setToasts] = useState([]);

function showToast(type) {
  setToasts((prev) => [
    ...prev,
    {
      id: Date.now(),
      type,
      message: type === "success" 
        ? "Operation completed successfully" 
        : "Something went wrong",
    },
  ]);
}

<button onClick={() => showToast("success")}>Show Success Toast</button>
<button onClick={() => showToast("error")}>Show Error Toast</button>

<ToastTA data={toasts} position="top-right" duration={3000}>
  {toasts.map((toast) => (
    <ToastTAItem toast={toast} key={toast.id}>
      <ToastTAIcon />
      <ToastTAMessage />
      <ToastTAProgress />
    </ToastTAItem>
  ))}
</ToastTA>

Props:

  • data: Array<{ id: number | string; type: "success" | "error" | "warning" | "info"; message: string }> (required)
  • position: "top-right" | "top-left" | "bottom-right" | "bottom-left"
  • duration: number (milliseconds, default: 3000)

ToggleSwitchTA

Toggle switch component.

Import:

import { ToggleSwitchTA, ToggleSwitchTAControl, ToggleSwitchTALabel, ToggleSwitchTADescription } from "@true-armor/atoms";
import { useState } from "react";

Usage:

const [isSwitchEnabled, setIsSwitchEnabled] = useState(false);

<ToggleSwitchTA checked={isSwitchEnabled} onChange={setIsSwitchEnabled} size="md">
  <ToggleSwitchTAControl />
  <div>
    <ToggleSwitchTALabel>Enable notifications</ToggleSwitchTALabel>
    <ToggleSwitchTADescription>Receive email notifications</ToggleSwitchTADescription>
  </div>
</ToggleSwitchTA>

Props:

  • checked: boolean (required)
  • onChange: (checked: boolean) => void (required)
  • size: "sm" | "md" | "lg" (default: "md")
  • disabled: boolean

CalendarTA

Calendar component for date selection.

Import:

import { CalendarTA } from "@true-armor/atoms";

Usage:

<CalendarTA
  initialDate={new Date()}
  onOk={(date) => console.log('Selected:', date)}
  onCancel={() => console.log('Cancelled')}
/>

Props:

  • initialDate: Date (required)
  • onOk: (date: Date) => void (required)
  • onCancel: () => void (required)

CardTA

Card component with icon, title, and body sections. Perfect for dashboard widgets, statistics cards, and feature highlights. Uses a compound component pattern for flexible composition.

Import:

import { 
  CardTA, 
  CardTAIcon, 
  CardTAContent, 
  CardTATitle, 
  CardTABody 
} from "@true-armor/atoms";
import { UserCircle2, Users, Shield, TrendingUp } from "lucide-react";

Usage:

// Basic card with all components
<CardTA borderColor="#C60000">
  <CardTAIcon icon={UserCircle2} bg="#A80C0C" color="white" />
  <CardTAContent>
    <CardTATitle count={42} title="Total Users" />
    <CardTABody instances={12} label="Active Now" />
  </CardTAContent>
</CardTA>

Multiple Cards Example:

<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
  {/* Users Card */}
  <CardTA borderColor="#C60000">
    <CardTAIcon icon={UserCircle2} bg="#A80C0C" color="white" />
    <CardTAContent>
      <CardTATitle count={42} title="Total Users" />
      <CardTABody instances={12} label="Active Now" />
    </CardTAContent>
  </CardTA>

  {/* Teams Card */}
  <CardTA borderColor="#059669">
    <CardTAIcon icon={Users} bg="#047857" color="white" />
    <CardTAContent>
      <CardTATitle count={8} title="Teams" />
      <CardTABody instances={24} label="Members" />
    </CardTAContent>
  </CardTA>

  {/* Security Card */}
  <CardTA borderColor="#DC2626">
    <CardTAIcon icon={Shield} bg="#B91C1C" color="white" />
    <CardTAContent>
      <CardTATitle count={156} title="Scans Completed" />
      <CardTABody instances={3} label="In Progress" />
    </CardTAContent>
  </CardTA>
</div>

Card with Title Only:

<CardTA borderColor="#173B4E">
  <CardTAIcon icon={TrendingUp} bg="#1E40AF" color="white" />
  <CardTAContent>
    <CardTATitle title="Revenue Growth" />
    <CardTABody instances="+24%" label="This Month" />
  </CardTAContent>
</CardTA>

Card with Custom Content:

<CardTA borderColor="#7C3AED" className="max-w-sm">
  <CardTAIcon icon={Shield} bg="#6D28D9" color="white" />
  <CardTAContent>
    <CardTATitle count={99.9} title="Uptime" color="#7C3AED" />
    <div className="mt-3">
      <p className="text-sm text-gray-600">
        System is running smoothly with no issues detected.
      </p>
    </div>
  </CardTAContent>
</CardTA>

Props:

CardTA (Main Component)

  • borderColor: string (default: "#C60000") - Left border color (6px solid border)
  • className: string (optional) - Additional CSS classes
  • children: React.ReactNode (required) - CardTAIcon and CardTAContent components

CardTAIcon

  • icon: React.ComponentType<any> (default: UserCircle2) - Icon component from lucide-react
  • bg: string (default: "#A80C0C") - Background color of the icon container
  • color: string (default: "white") - Icon color

CardTAContent

  • children: React.ReactNode (required) - CardTATitle, CardTABody, or custom content

CardTATitle

  • count: number | string (optional) - Large number displayed at the top
  • title: string (optional) - Title text displayed below the count
  • color: string (default: "#111827") - Color of the count text

CardTABody

  • instances: number | string (optional) - Number or text displayed before the label
  • label: string (default: "Instances") - Label text displayed after instances
  • color: string (default: "#6B7280") - Color of both instances and label text

Features:

  • ✅ Customizable left border color
  • ✅ Icon support with custom background and color
  • ✅ Flexible content layout
  • ✅ Optional count and title display
  • ✅ Optional instances and label display
  • ✅ Fully customizable styling
  • ✅ Responsive design ready

NoDataPageTA

Empty state component for displaying "no data" or "empty" states in your application. Perfect for when lists are empty, search results return nothing, or when users need guidance on what to do next. Uses a compound component pattern with context-based prop sharing for flexible composition.

Import:

import { 
  NoDataPageTA, 
  NoDataPageTAImg, 
  NoDataPageTABody, 
  NoDataPageTATitle, 
  NoDataPageTAButton 
} from "@true-armor/atoms";

Usage:

Basic Example (using context props):

<NoDataPageTA 
  title="No data available" 
  buttonText="Add New Item"
  image="/path/to/empty-state-image.svg"
>
  <NoDataPageTAImg />
  <NoDataPageTABody>
    <NoDataPageTATitle />
    <NoDataPageTAButton onClick={() => console.log("Clicked!")} />
  </NoDataPageTABody>
</NoDataPageTA>

With Direct Props (overriding context):

<NoDataPageTA>
  <NoDataPageTAImg src="/custom-empty-image.png" />
  <NoDataPageTABody>
    <NoDataPageTATitle>No users found</NoDataPageTATitle>
    <NoDataPageTAButton onClick={handleAddUser}>
      Create First User
    </NoDataPageTAButton>
  </NoDataPageTABody>
</NoDataPageTA>

Minimal Example (only image):

<NoDataPageTA image="/empty-state.svg">
  <NoDataPageTAImg />
</NoDataPageTA>

Custom Content Example:

<NoDataPageTA title="No results found">
  <NoDataPageTAImg src="/search-empty.svg" />
  <NoDataPageTABody>
    <NoDataPageTATitle>Try adjusting your search filters</NoDataPageTATitle>
    <div className="mt-4">
      <p className="text-gray-500 text-xs">
        We couldn't find any items matching your criteria.
      </p>
    </div>
    <NoDataPageTAButton onClick={handleResetFilters}>
      Clear Filters
    </NoDataPageTAButton>
  </NoDataPageTABody>
</NoDataPageTA>

Without Button:

<NoDataPageTA title="No items to display" image="/empty-box.svg">
  <NoDataPageTAImg />
  <NoDataPageTABody>
    <NoDataPageTATitle />
  </NoDataPageTABody>
</NoDataPageTA>

Not Found / 404 Page Example:

import { 
  NoDataPageTA, 
  NoDataPageTAImg, 
  NoDataPageTABody, 
  NoDataPageTATitle, 
  NoDataPageTAButton 
} from "@true-armor/atoms";
import { useNavigate } from "react-router-dom"; // or your routing library

function NotFoundPage() {
  const navigate = useNavigate();

  return (
    <NoDataPageTA 
      title="Page Not Found" 
      buttonText="Go Back Home"
      image="/404-illustration.svg"
    >
      <NoDataPageTAImg />
      <NoDataPageTABody>
        <NoDataPageTATitle>Oops! The page you're looking for doesn't exist.</NoDataPageTATitle>
        <div className="mt-2">
          <p className="text-gray-500 text-xs">
            The page might have been moved or deleted.
          </p>
        </div>
        <NoDataPageTAButton onClick={() => navigate("/")}>
          Go Back Home
        </NoDataPageTAButton>
      </NoDataPageTABody>
    </NoDataPageTA>
  );
}

Not Found with Custom Message:

<NoDataPageTA>
  <NoDataPageTAImg src="/not-found.svg" />
  <NoDataPageTABody>
    <NoDataPageTATitle>404 - Page Not Found</NoDataPageTATitle>
    <div className="mt-3 space-y-2">
      <p className="text-gray-500 text-xs">
        Sorry, we couldn't find the page you're looking for.
      </p>
      <p className="text-gray-400 text-xs">
        Check the URL or go back to the homepage.
      </p>
    </div>
    <NoDataPageTAButton onClick={() => window.history.back()}>
      Go Back
    </NoDataPageTAButton>
  </NoDataPageTABody>
</NoDataPageTA>

Simple Not Found (Minimal):

<NoDataPageTA 
  title="404 - Not Found" 
  image="/404.svg"
>
  <NoDataPageTAImg />
  <NoDataPageTABody>
    <NoDataPageTATitle />
  </NoDataPageTABody>
</NoDataPageTA>

Props:

NoDataPageTA (Main Component)

  • title: string (optional) - Title text to display (can be overridden by NoDataPageTATitle children)
  • buttonText: string (optional) - Button text (can be overridden by NoDataPageTAButton children)
  • image: string (optional) - Image URL/path (can be overridden by NoDataPageTAImg src prop)
  • children: React.ReactNode (required) - NoDataPageTAImg, NoDataPageTABody, and their children

NoDataPageTAImg

  • src: string (optional) - Image URL/path (overrides context image if provided)
  • Uses context image if src is not provided
  • Returns null if no image is available

NoDataPageTABody

  • children: React.ReactNode (required) - NoDataPageTATitle, NoDataPageTAButton, or custom content
  • Wraps content in a centered, text-aligned container

NoDataPageTATitle

  • children: React.ReactNode (optional) - Title text (overrides context title if provided)
  • Uses context title if children are not provided
  • Returns null if no title is available
  • Styled with text-[#4B5563] text-sm

NoDataPageTAButton

  • children: React.ReactNode (optional) - Button text (overrides context buttonText if provided)
  • onClick: () => void (optional) - Click handler function
  • Uses context buttonText if children are not provided
  • Returns null if no button text is available
  • Uses ButtonTA with buttonType="primary"

Features:

  • ✅ Context-based prop sharing for flexible composition
  • ✅ Direct prop override support (props take precedence over context)
  • ✅ Conditional rendering (components return null if no data provided)
  • ✅ Centered, responsive layout
  • ✅ Custom image support
  • ✅ Integrated with ButtonTA component
  • ✅ Flexible content structure
  • ✅ Perfect for empty states, no results, and onboarding flows

Use Cases:

  • Empty lists or tables
  • No search results
  • Empty cart or checkout
  • No notifications
  • Empty dashboard sections
  • Onboarding flows
  • Error recovery states

CircularLoaderTA

Animated circular loading spinner component with customizable colors, size, and animation speed. Perfect for indicating loading states, data fetching, or processing operations.

Import:

import { CircularLoaderTA, CircularLoaderTASpinner } from "@true-armor/atoms";

Usage:

Basic Loader:

<CircularLoaderTA size={80}>
  <CircularLoaderTASpinner />
</CircularLoaderTA>

Custom Size:

<CircularLoaderTA size={120}>
  <CircularLoaderTASpinner />
</CircularLoaderTA>

Custom Colors:

const customColors = [
  '#FF6B6B',
  '#4ECDC4',
  '#45B7D1',
  '#FFA07A',
  '#98D8C8'
];

<CircularLoaderTA size={100}>
  <CircularLoaderTASpinner colors={customColors} />
</CircularLoaderTA>

Custom Animation Speed:

<CircularLoaderTA size={80}>
  <CircularLoaderTASpinner 
    animationDuration={1.5} 
    sticks={8}
  />
</CircularLoaderTA>

Full Page Loader:

<div className="relative min-h-screen">
  <CircularLoaderTA size={100}>
    <CircularLoaderTASpinner />
  </CircularLoaderTA>
</div>

Props:

CircularLoaderTA (Main Component)

  • size: number (default: 80) - Size of the loader in pixels (width and height)
  • children: React.ReactNode (required) - CircularLoaderTASpinner component

CircularLoaderTASpinner

  • colors: string[] (optional) - Array of color hex codes for the spinner. Defaults to a blue gradient palette
  • sticks: number (default: 11) - Number of spinner sticks/segments
  • animationDuration: number (default: 2.2) - Animation duration in seconds

Features:

  • ✅ Smooth color rotation animation
  • ✅ Customizable size
  • ✅ Custom color palette support
  • ✅ Adjustable animation speed
  • ✅ Configurable number of spinner segments
  • ✅ Absolute positioning for overlay loading states
  • ✅ Responsive and lightweight

Use Cases:

  • Page loading indicators
  • Data fetching states
  • Form submission loading
  • Image upload progress
  • API request indicators
  • Background processing states

GroupedMultiSelectTA

Hierarchical multi-select component with nested groups and checkboxes. Supports parent-child relationships with automatic selection propagation. Perfect for category selection, permission management, and nested data selection. Uses a compound component pattern with recursive rendering for unlimited nesting depth.

Import:

import { GroupedMultiSelectTA, GroupedMultiSelectTAItem } from "@true-armor/atoms";
import { useState } from "react";

Usage:

Basic Example:

const [selected, setSelected] = useState<string[]>([]);

<GroupedMultiSelectTA value={selected} onChange={setSelected}>
  <GroupedMultiSelectTAItem value="fruits">
    Fruits
    <GroupedMultiSelectTAItem value="apple">Apple</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="banana">Banana</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="orange">Orange</GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
  
  <GroupedMultiSelectTAItem value="vegetables">
    Vegetables
    <GroupedMultiSelectTAItem value="carrot">Carrot</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="broccoli">Broccoli</GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>

Deeply Nested Groups (Permission Management):

const [permissions, setPermissions] = useState<string[]>([]);

<GroupedMultiSelectTA value={permissions} onChange={setPermissions}>
  <GroupedMultiSelectTAItem value="admin">
    Admin Access
    <GroupedMultiSelectTAItem value="users">
      User Management
      <GroupedMultiSelectTAItem value="create-user">Create User</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="edit-user">Edit User</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="delete-user">Delete User</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="view-user">View User</GroupedMultiSelectTAItem>
    </GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="settings">
      Settings
      <GroupedMultiSelectTAItem value="general">General Settings</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="security">Security Settings</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="notifications">Notification Settings</GroupedMultiSelectTAItem>
    </GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="reports">
      Reports
      <GroupedMultiSelectTAItem value="view-reports">View Reports</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="export-reports">Export Reports</GroupedMultiSelectTAItem>
    </GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>

Controlled Component with Initial Selection:

const [selectedItems, setSelectedItems] = useState<string[]>(['apple', 'banana']);

<GroupedMultiSelectTA value={selectedItems} onChange={setSelectedItems}>
  <GroupedMultiSelectTAItem value="fruits">
    Fruits
    <GroupedMultiSelectTAItem value="apple">Apple</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="banana">Banana</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="orange">Orange</GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>

// Access selected values
console.log(selectedItems); // ['fruits', 'apple', 'banana']

Category Selection with Subcategories:

const [categories, setCategories] = useState<string[]>([]);

<GroupedMultiSelectTA value={categories} onChange={setCategories}>
  <GroupedMultiSelectTAItem value="electronics">
    Electronics
    <GroupedMultiSelectTAItem value="phones">
      Phones
      <GroupedMultiSelectTAItem value="smartphone">Smartphone</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="feature-phone">Feature Phone</GroupedMultiSelectTAItem>
    </GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="computers">
      Computers
      <GroupedMultiSelectTAItem value="laptop">Laptop</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="desktop">Desktop</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="tablet">Tablet</GroupedMultiSelectTAItem>
    </GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
  <GroupedMultiSelectTAItem value="clothing">
    Clothing
    <GroupedMultiSelectTAItem value="men">Men's</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="women">Women's</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="kids">Kids</GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>

File/Folder Structure Example:

const [selectedFiles, setSelectedFiles] = useState<string[]>([]);

<GroupedMultiSelectTA value={selectedFiles} onChange={setSelectedFiles}>
  <GroupedMultiSelectTAItem value="documents">
    Documents
    <GroupedMultiSelectTAItem value="projects">
      Projects
      <GroupedMultiSelectTAItem value="project-1">Project 1</GroupedMultiSelectTAItem>
      <GroupedMultiSelectTAItem value="project-2">Project 2</GroupedMultiSelectTAItem>
    </GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="reports">Reports</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="notes">Notes</GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
  <GroupedMultiSelectTAItem value="images">
    Images
    <GroupedMultiSelectTAItem value="photos">Photos</GroupedMultiSelectTAItem>
    <GroupedMultiSelectTAItem value="screenshots">Screenshots</GroupedMultiSelectTAItem>
  </GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>

Props:

GroupedMultiSelectTA (Main Component)

  • value: string[] (required) - Array of selected item values. When a parent is selected, all its children are automatically included.
  • onChange: (value: string[]) => void (required) - Callback function triggered when selection changes. Receives the updated array of selected values.
  • children: React.ReactNode (required) - GroupedMultiSelectTAItem components representing the tree structure

GroupedMultiSelectTAItem

  • value: string (required) - Unique identifier for the item. Must be unique across all items in the tree.
  • children: React.ReactNode (required) - Label text (first text node) and/or nested GroupedMultiSelectTAItem components for creating sub-items

Features:

  • ✅ Unlimited nesting depth (recursive rendering)
  • ✅ Hierarchical/nested structure support
  • ✅ Parent-child selection propagation
  • ✅ Indeterminate state (partial selection) with minus icon
  • ✅ Expandable/collapsible groups with chevron icons
  • ✅ Fully controlled component pattern
  • ✅ Automatic value collection (selecting parent includes all descendants)
  • ✅ Visual feedback with checkmarks (✓) and minus (-) icons
  • ✅ Hover states and smooth transitions
  • ✅ Click to expand/collapse groups
  • ✅ Independent checkbox selection

Selection Behavior:

  • Selecting a parent: Automatically selects all children and descendants
  • Deselecting a parent: Automatically deselects all children and descendants
  • Selecting all children: Automatically selects the parent
  • Partial selection: Shows indeterminate state (minus icon) when some but not all children are selected
  • Leaf nodes: Can be selected independently without affecting siblings
  • Value array: Contains all selected values including parents and all their descendants

Visual States:

  • Unselected: Empty checkbox with white background
  • Fully Selected: Checkmark icon (✓) with dark blue background (#173B4E)
  • Partially Selected: Minus icon (-) with dark blue background (#173B4E)
  • Expanded Group: ChevronDown icon pointing down
  • Collapsed Group: ChevronRight icon pointing right

Use Cases:

  • Permission management systems (role-based access control)
  • Category selection with subcategories (e-commerce filters)
  • File/folder selection (file managers, cloud storage)
  • Feature toggles with groups (feature flags)
  • Multi-level filtering (advanced search)
  • Hierarchical data selection (organizational structures)
  • Tag management with categories
  • Settings configuration with grouped options

🎨 Styling

All components use:

  • Tailwind CSS for utility classes
  • Montserrat font family
  • Flowbite React for base components
  • Lucide React for icons

📝 TypeScript Support

Full TypeScript support with type definitions included. All components are fully typed.

🔗 Icon Library

This library works seamlessly with Lucide React:

npm install lucide-react

Common icons used:

  • Home, Settings, User
  • CheckCircle2, AlertCircle, AlertTriangle, Info
  • Download, Upload, Share2, Edit2Icon, Trash2
  • ChevronRight, ChevronLeft, ChevronUp, ChevronDown

📄 License

MIT

🔗 Links


Made with ❤️ by TrueArmor