@noambz/ui
v0.15.3
Published
Shared React UI library: atomic design system, Tailwind v4 tokens, Radix primitives, Storybook docs. Built for Next.js 16 + React 19.
Maintainers
Readme
@noambz/ui
Shared React UI library: atomic design system, Tailwind v4 tokens, Radix primitives, Storybook docs. Built for Next.js 16 + React 19.
Consuming in a new project
This package is published as a local workspace library — not on npm. Consume it one of two ways:
A. Tarball install (recommended, matches production resolution)
cd ~/dev/projects/utils/ui
npm pack # produces noambz-ui-<version>.tgz
cd <your-new-app>
npm install ~/dev/projects/utils/ui/noambz-ui-0.6.1.tgzB. Git install (when you want version tracking)
npm install git+ssh://[email protected]:<you>/ui.git#v0.6.1Avoid npm install file:../ui — npm 10+ installs file: as a symlink, and Next.js / Turbopack trips over the nested node_modules (notably react-hook-form resolves from two places and breaks the form generics).
Install peer dependencies
npm install \
react@>=19 react-dom@>=19 \
next@>=15 next-themes@>=0.4 \
@tanstack/react-query@>=5 \
sonner@>=1 \
react-hook-form@^7.73 zod@^4 @hookform/resolvers@^5react-hook-form, zod, and @hookform/resolvers are only required if you use <Form> / <FormFieldController>, but they're safe to install in any consumer.
Setup (Next.js 16 + Tailwind v4)
1. next.config.ts — Next must transpile the package source (it ships ESM with JSX runtime):
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
transpilePackages: ["@noambz/ui"],
};
export default nextConfig;2. Tailwind entry stylesheet (e.g. app/globals.css):
@import "tailwindcss";
@import "@noambz/ui/styles/theme.css";
@source "../node_modules/@noambz/ui/dist";The @source directive tells Tailwind v4 to scan the compiled package output for class names so nothing gets tree-shaken in production.
3. Root layout providers:
import {
ThemeProvider,
QueryProvider,
ThemedToaster,
} from "@noambz/ui/components";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider>
<QueryProvider>
{children}
<ThemedToaster />
</QueryProvider>
</ThemeProvider>
</body>
</html>
);
}ThemeProvider wraps next-themes with the right defaults for this theme (attribute="class", system preference, transitions disabled during switches). ThemedToaster binds sonner to the active theme.
For the dark-mode toggle in your header, skip wiring useTheme yourself — use the preset:
import { ThemeToggleConnected } from "@noambz/ui/components";
<ThemeToggleConnected />4. Toast usage — toast is re-exported from the package so you don't need a separate sonner import:
import { toast } from "@noambz/ui/components";
toast.success("Saved");Subpaths
| Import | Contents |
|---|---|
| @noambz/ui | Everything (convenience barrel) |
| @noambz/ui/components | All components + toast re-export |
| @noambz/ui/hooks | All hooks |
| @noambz/ui/i18n | createI18n factory |
| @noambz/ui/utils | Formatters, API client, pagination helpers, cn |
| @noambz/ui/styles/theme.css | Semantic token CSS (import in the consumer's global stylesheet) |
Component index
Organized by atomic level.
Atoms
AspectRatio · Avatar · Checkbox · CloseIcon · CountBadge / CountBadgeAnchor · icons (MenuIcon, RefreshIcon, DownloadIcon, TrashIcon, ArchiveIcon, ChevronBackIcon, ChevronUpIcon, ChevronDownIcon, PlusIcon, MinusIcon, StarIcon, InboxIcon, CheckIcon, CopyIcon, CalendarIcon, ClockIcon, EyeIcon, EyeOffIcon, ExternalLinkIcon, LinkIcon, GripVerticalIcon) · Input (default md is 16px / 44px so iOS does not zoom) · Kbd · Label · LiveRegion · Meter · Popover · RadioGroup · ScrollArea · Separator · Skeleton / SkeletonText / SkeletonCard · SkipLink · Slider (Radix; default lg is 44px) · Spinner · StatusDot · Switch (44px track, RTL thumb) · Tag · TextLink · Textarea (16px / 44px min height) · Toggle (default lg is 44px, RTL thumb) · Typography · VisuallyHidden
Molecules
Accordion · Alert · AnnouncementBar · AvatarGroup · BackLink · Breadcrumbs · Button (default lg is 44px) · ButtonGroup · Card · ChipBar · CircularProgress · CodeBlock · Collapsible / CollapsiblePanel · CopyButton · DataGridRowActions · DescriptionList · DropdownMenu · EditionSummaryCard · EmptyState · FilterSortRow · Form · FormField · FormFieldController · FormSubmitError · HoverCard · IconButton · InfiniteScrollSentinel · InlineFormRow · InputGroup · LabeledSeparator · LabeledSwitch (44px row) · LanguageToggle · LoadingOverlay · MultiToggle (default lg is 44px) · NumberInput · OTPInput · PageHeader · PageSection · PalettePicker · PasswordInput · Pagination · Progress · QueryState · SearchInput (44px clear) · SignalCard · SignalSeverityBadge · Stack · Stepper · SummaryHeader · Table · Tabs · TagInput · TextArtifactFeed · ThemeToggle (default lg is 44px) · Timeline · Toolbar · Tooltip
IconButton
Square icon-only chrome control for toolbars, shell, and secondary/destructive row actions. Allowed variants: ghost (default), text, secondary, destructive — never primary/outline/link filled CTAs. Requires aria-label. By default wraps TooltipPanel with that label as the tooltip; pass tooltip={false} to skip or tooltip={...} to override. Sizes: sm (h-8), md (h-9), lg (h-11, default 44px tap target). Compact chrome should pass sm or md explicitly.
import { IconButton, IconButtonSizeEnum, TrashIcon } from "@noambz/ui/components";
<IconButton aria-label="Remove" variant="destructive" size={IconButtonSizeEnum.LG}>
<TrashIcon />
</IconButton>Organisms
AppShellLayout · AppShellPreset · SidebarNav · ModuleTabs · ModuleSubnav · Autocomplete · BarChart · Calendar · CommandPalette · ConfirmDialog · ContextMenu · DataGrid · DateRangePicker · DatePicker · Drawer · ErrorBoundary · FileUpload · FullPageError · FullPageLoading · LineChart · Modal · NotFoundState · PaginatedDataGrid · PieChart · ResizablePanelGroup · Select · ShortcutHelpOverlay · Sparkline · TreeView · legacy CustomSelect
AppShellLayout renders <main id="main-content" tabIndex={-1}> so SkipLink can jump past chrome. Activating SkipLink focuses that landmark (SPA routers do not reliably honor in-page hash navigation). It accepts optional background for a fixed, non-scrolling atmosphere in the main column (below the header, beside the sidebar). Page chrome in children stacks above it and below the sticky header and sidebar; .app-shell-main-content clips horizontal overflow so wide rails scroll inside the pane instead of the document. QueryState shows a spinner row while loading; pass loadingFallback for layout-shaped skeletons. PipelineRail and ChipBar are min-w-0 row scrollers.
DataGrid column filters (column.filter + controlled filters / onFiltersChange) render above the table/card list as a single-row scroller (placeholders + aria-labels, no column titles). Pass filterBarFramed for a bordered card around the filter bar. Localized apps should pass emptyState, loadingLabel, selectRowAriaLabel, selectAllAriaLabel, filterBarAriaLabel, filterColumnAriaLabel, filterAllLabel, and filterPlaceholder. Use PaginatedDataGrid.toolbar for custom search/actions outside column filters. Dense row chrome uses DataGridRowActions (up to two IconButtons; overflow behind a vertical-ellipsis menu). ConfirmDialog is a small labeled confirm/cancel modal; inject all copy from the app. ChipBar is a 44px single-row filter/sort scroller. InlineFormRow is a labeled input plus submit. InfiniteScrollSentinel observes a foot node and requests the next page. EditionSummaryCard is a linked archive card with a 44px minimum hit area; inject LinkComponent, tags, and metrics. HeatmapGrid starts at one column, then two from 360px. SelectTrigger, SelectItem, CustomSelect, TagInput, Autocomplete, and Pagination controls use 16px text and 44px min height. FileUpload accepts injectable selected-files and remove labels. TagInput and Autocomplete accept injectable remove labels. Checkbox and RadioGroupItem sit in a 44px hit area.
Utility wrappers
InstallPrompt · LocaleSync · QueryProvider · ServiceWorkerRegistration · AppearanceSync · ThemeProvider · ThemedToaster · toast (re-export from sonner)
Hooks
useClickOutside · useControllableState · useCopyToClipboard · useCountdown · useDebounce · useDisclosure · useDocumentDir · useEventListener · useHover · useIntersectionObserver · useInterval · useIsClient · useIsOverflowing · useKeyboardShortcuts · useLatest · useLocale · useLocalStorage · useLockBodyScroll · useMediaQuery · useMergedRefs · useOnlineStatus · usePaginatedQuery · usePrefersReducedMotion · usePrevious · useResizeObserver · useThrottle · useTimeout · useToggle · useUnmount · useWindowSize
Utils
apiFetch · createApiError · clamp · cn · ddmmToISO · formatCurrency · formatDate · formatNumber · formatNumberOrFallback · formatPercent · formatPrice · formatQuantity · formatRelativeDuration · formatTweetPublishedAt · formatUsd · formatUsdOrFallback · mergeRefs · buildPaginatedResponse · parsePaginationParams
Forms (react-hook-form + zod)
import { Form, FormFieldController, FormSubmitError, Input, Button } from "@noambz/ui/components";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({
email: z.email(),
password: z.string().min(8),
});
type IValues = z.infer<typeof schema>;
export function LoginForm() {
const form = useForm<IValues>({ resolver: zodResolver(schema), mode: "onBlur" });
return (
<Form form={form} onSubmit={async (values) => await signIn(values)}>
<FormFieldController<IValues> name="email" label="Email">
<Input type="email" />
</FormFieldController>
<FormFieldController<IValues> name="password" label="Password">
<Input type="password" />
</FormFieldController>
<FormSubmitError />
<Button type="submit" loading={form.formState.isSubmitting}>Sign in</Button>
</Form>
);
}FormFieldController clones its single child input and auto-wires value, onChange, onBlur, ref, id, name, aria-invalid, and aria-describedby from the react-hook-form field state. The presentational FormField is still available for forms that manage their own state.
Design tokens
Colors, radii, elevation, and motion are exposed as CSS custom properties (--ui-*) and mapped into Tailwind's @theme, so utility classes like bg-surface, text-foreground, border-border-base, bg-danger-muted, shadow-md, and rounded-md resolve against the theme.
Light palettes (data-light-palette): bright (default zinc/blue), soft (warm paper/indigo), mist (sky/blue-gray), warm (parchment/terracotta).
Dark palettes (data-dark-palette): soft (default charcoal/blue), deep (near-black/indigo), slate (navy/sky), midnight (ink/teal).
| Token | Role |
|---|---|
| --ui-background / --ui-foreground | Page canvas and body text |
| --ui-surface / --ui-surface-muted / --ui-surface-raised / --ui-surface-hover | Cards, wells, hover fills |
| --ui-primary / --ui-primary-muted / --ui-primary-hover | Brand actions and selected chrome |
| --ui-success-* / --ui-warning-* / --ui-danger-* / --ui-info-* | Semantic fills, borders, and on-muted text |
| --ui-overlay / --ui-glass | Modal backdrops and frosted surfaces |
| --ui-shadow-sm/md/lg | Theme-aware elevation (shadow-sm / shadow-md / shadow-lg) |
Load a webfont by setting --ui-font-sans-family and --ui-font-mono-family on <html> (for example via next/font). PalettePicker renders live token previews without switching the document theme.
Override any token in your app's CSS to rebrand:
:root {
--ui-primary: oklch(0.6 0.22 145);
}RTL is wired automatically via a [dir="rtl"] helper in the theme stylesheet.
Charts
Sparkline, LineChart, BarChart, and PieChart wrap recharts with opinionated defaults that respect the theme via currentColor. Pass your own color to override. Shared categorical and named series palettes are exported as CHART_CATEGORY_COLORS and CHART_SERIES_COLORS so pies, list swatches, and overlay series stay in sync. PieChart soft-expands the hovered slice with CSS scale/opacity animation (respects reduced motion) and shows label/value/share in the tooltip and (for donuts) a center readout.
Telegram signal kit
SignalCard, SignalSeverityBadge, SummaryHeader, and EmptyState compose a dashboard for investment signal feeds. They're generic enough for any feed-shaped UI.
Development
npm install
npm run typecheck # tsc --noEmit
npm run lint # eslint
npm run build # tsup -> dist/
npm run storybook # storybook dev -p 6006
npm run build-storybookStorybook is the canonical component playground (npm run storybook). Prefer shipping a *.stories.tsx next to each public component.
Authoring checklist:
- Cover the default case plus meaningful variants/compositions (sizes, tones, presets, empty/error)
- Add
docs.description.componentand usefulargTypesfor interactive props - Spot-check dark mode and RTL via the Storybook toolbar
- Avoid leftover domain copy from other products; prefer investment-finder-neutral examples
- Prefer inline status text or Storybook actions over
alert()in interactive demos - New presentational patterns (e.g.
HtmlContent,TextArtifactCardbody formats) should be story-covered before release
Versioning
Semver. Breaking API changes require a major bump. Token renames in theme.css are also treated as breaking because consumers may reference them directly.
0.6.1— Readiness: Vitest + Testing Library with smoke tests, GitHub Actions CI (typecheck/lint/test/build/storybook),Badgealias forTag,ThemeToggleConnectedpreset,DrawerPanelpreset,FormFieldControllerexplicitmodeprop (text/number/boolean/select/raw). DataGridrowKey/selection now acceptsstring | number. Verified against a fresh Next.js 16 playground app.0.6.0— Inputs + scaffolds + composition:SearchInput,NumberInput,OTPInput,TagInput,FileUpload,CommandPalette(cmdk),FullPageLoading,FullPageError,NotFoundState,PaginatedDataGridpreset. Tech debt: safer ButtonasChild+loading, safer DataGrid default sort comparator, Calendar locale docs. Storybook a11y rules now run as errors.0.5.0— Foundations for a new project:DropdownMenu,RadioGroup,Alert,Skeleton,Accordion,Avatar,Progress,Breadcrumbs,Drawer,Kbd,VisuallyHidden,ThemeProvider,toastre-export,useMediaQuery/useDebounce/useLocalStorage/useClickOutside/useCopyToClipboard/useDisclosure,formatCurrency/formatNumber/formatDate,<Form>+<FormFieldController>built on react-hook-form + zod.0.4.0— Composite components:DataGrid,Autocomplete,DatePicker,DateRangePicker,Charts, signal-kit molecules.0.3.0— Form atoms, modal, select, ports,/utilssubpath.0.2.0— Atomic design folders,cva, Storybook 10.
