@noambz/ui
v0.11.0
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
Avatar · Checkbox · CloseIcon · icons (MenuIcon, RefreshIcon, DownloadIcon, TrashIcon, ArchiveIcon, ChevronBackIcon, PlusIcon, StarIcon, InboxIcon) · Input · Kbd · Label · Popover · RadioGroup · Separator · Skeleton · Spinner · Tag · Textarea · Toggle · Typography · VisuallyHidden
Molecules
Accordion · Alert · Breadcrumbs · Button · Card · DropdownMenu · EmptyState · Form · FormField · FormFieldController · FormSubmitError · IconButton · LanguageToggle · MultiToggle · NumberInput · OTPInput · Pagination · Progress · SearchInput · SignalCard · SignalSeverityBadge · SummaryHeader · Tabs · TagInput · ThemeToggle · 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, default), lg (h-11 for 44px shell targets).
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 · DataGrid · DateRangePicker · DatePicker · Drawer · FileUpload · FullPageError · FullPageLoading · LineChart · Modal · NotFoundState · PaginatedDataGrid · Select · ShortcutHelpOverlay · Sparkline · legacy CustomSelect
Utility wrappers
InstallPrompt · LocaleSync · QueryProvider · ServiceWorkerRegistration · ThemeProvider · ThemedToaster · toast (re-export from sonner)
Hooks
useClickOutside · useCopyToClipboard · useDebounce · useDisclosure · useKeyboardShortcuts · useLocale · useLocalStorage · useMediaQuery · usePaginatedQuery
Utils
apiFetch · createApiError · cn · ddmmToISO · formatCurrency · formatDate · formatNumber · formatRelativeDuration · formatTweetPublishedAt · 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, 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, and rounded-md resolve against the theme.
| Token | Light | Dark |
|---|---|---|
| --ui-background | #ffffff | #0a0a0a |
| --ui-foreground | #0a0a0a | #fafafa |
| --ui-surface | #ffffff | #111113 |
| --ui-surface-muted | #f4f4f5 | #18181b |
| --ui-border-base | #e4e4e7 | #27272a |
| --ui-border-strong | #d4d4d8 | #3f3f46 |
| --ui-muted-foreground | #71717a | #a1a1aa |
| --ui-primary | #2563eb | #3b82f6 |
| --ui-ring | #3b82f6 | #60a5fa |
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, and BarChart wrap recharts with opinionated defaults that respect the theme via currentColor. Pass your own color to override.
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.
