@tatumio/tatum-design-system
v1.27.2
Published
Tatum Design System — React components and Tailwind design tokens for building Tatum-styled UIs.
Readme
Tatum Design System
A modern, reusable React component library. Published to public npm as
@tatumio/tatum-design-system
under the MIT licence.
Using with Claude Code
The repo ships a Claude Code skill that teaches the assistant to reach for existing TDS components and semantic Tailwind tokens. It ships in this package under skills/tds/ and is published at /skill/ on the docs site.
Installation
yarn add @tatumio/tatum-design-system
# or
npm install @tatumio/tatum-design-systemNo tokens, no .npmrc, no private registry — the package comes from
registry.npmjs.org like any other public dependency.
react and react-dom are peer dependencies, and TDS requires ^19.0.0.
React 18 is not supported: under @types/react@18 a ComponentType<IconProps>
prop rejects lucide-react's icons, because React 18's propTypes typing makes
size invariant. React 19 removed propTypes, so the problem disappears there.
Usage
// Import components
import {Button, Card} from '@tatumio/tatum-design-system';
// Import styles to get the fonts
import '@tatumio/tatum-design-system/styles.css';
function App() {
return (
<Card>
<h2>Hello World</h2>
<Button>Click me</Button>
</Card>
);
}Alternative Import Methods
You can also import individual components:
import {Button} from '@tatumio/tatum-design-system/components/Button';Icons
TDS does not ship an icon set. The icons its own components draw (close
buttons, chevrons, checkmarks, toast status icons, …) default to
lucide-react. To use a different set, wrap the app in TdsIconProvider and
pass replacements by TDS name — any you leave out keep the Lucide default:
import {TdsIconProvider, type TdsIcons} from '@tatumio/tatum-design-system';
import {CloseIcon, SuccessIcon} from 'your-icon-set';
// Module-level, so the provider's value stays stable across renders.
const icons: Partial<TdsIcons> = {Close: CloseIcon, ToastSuccess: SuccessIcon};
<TdsIconProvider icons={icons}>
<App/> {/* include wherever <Toaster/> is rendered */}
</TdsIconProvider>The names are the keys of defaultTdsIcons (typed TdsIconName). Most are
shared by several components; the toast status icons (ToastSuccess,
ToastError, ToastWarning, ToastInfo, ToastLoading) and AddressCopied
have their own, so they can be styled differently from the same glyph
elsewhere. Providers nest and merge. Your icon set stays in your own bundle;
TDS never ships it.
Components that render a caller-supplied icon (FeaturedIcon, FileIcon,
AddressHandle) take an icon prop typed React.ComponentType<IconProps>, so
you pass a component from whichever icon library you use. IconProps is
exported from the package root:
interface IconProps extends React.SVGProps<SVGSVGElement> {
size?: number
}Any icon component matching that shape works — lucide-react
is what the Storybook stories use, and it is the same geometry TDS renders
internally:
import {FeaturedIcon} from '@tatumio/tatum-design-system';
import {CircleAlert} from 'lucide-react';
<FeaturedIcon icon={CircleAlert} size="md" color="error"/>Widget Integration
When using the design system in widget environments, portal-based components (like modals, tooltips...) are
automatically handled for Shadow DOM environments. If you're using prefixed Tailwind CSS, you need to add the
data-tds-root="true" attribute to your widget container to ensure proper styling:
function MyWidget() {
return (
<div data-tds-root="true">
{/* Your widget content */}
<Dialog>
{/* The dialog renders inside the widget container with its styles */}
</Dialog>
</div>
);
}Without the data-tds-root attribute, portal components would render in document.body and lose access to your
prefixed Tailwind styles.
Keyboard focus inside a shadow root
Radix UI, which TDS builds on, tracks focus with document.activeElement. Inside
a shadow root that resolves to the host element, so keyboard navigation in
Tabs, RadioGroup and Dropdown and focus trapping in Dialog and Popover
break there. Until Radix fixes this upstream
(radix-ui/primitives#3674),
apps that render TDS in a shadow root patch @radix-ui/react-focus-scope and
@radix-ui/react-roving-focus themselves with
patch-package, applying that PR's
change: route every document.activeElement read through a helper that
descends into shadowRoot.activeElement.
Using the Tailwind Plugin
The Tatum Design System includes a custom Tailwind CSS plugin that provides predefined utilities, components, and theming capabilities for your projects.
Installation
Add the plugin to your Tailwind configuration:
// tailwind.config.js
import tdsPlugin from '@tatumio/tatum-design-system/tailwind-plugin';
import animate from 'tailwindcss-animate';
export default {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
"./node_modules/@tatumio/tatum-design-system/dist/**/*.{js,ts,jsx,tsx}",
],
plugins: [tdsPlugin, animate],
}In a CommonJS config, the plugin is the module's default export:
require('@tatumio/tatum-design-system/tailwind-plugin').default.
TDS's overlays (dialogs, popovers, menus) use the enter/exit animation classes
from tailwindcss-animate,
so add it to your project (yarn add -D tailwindcss-animate) and register it
next to the TDS plugin.
Features
The plugin includes:
- Colors: Brand colors, accent colors, semantic colors
- Typography: Text sizes, weights, line heights, font families
- Borders: Border widths, radii, and styles
- Shadows: Elevation levels and interactive states
- Transitions: Animation timing, easing functions
- Z-index: Layering system for consistent stacking
For comprehensive examples of all available design tokens and their usage, please refer to the Storybook documentation where you can find interactive examples for each feature.
Color Tokens: Primitives vs. Semantic
The plugin exposes two tiers of color tokens. Understanding the difference is important for building components that correctly support dark mode.
Primitive colors (tatum-*)
Static, hardcoded RGBA values. They never change between light and dark mode.
tatum-gray-100 → rgb(245 245 245) — always this value
tatum-primary-500 → rgb(79 55 253) — always this valueUse primitives when you need a specific color that must stay the same regardless of theme — e.g. a brand logo background, a fixed overlay, or an illustration fill.
<div className="bg-tatum-primary-500 text-tatum-white">Always purple</div>Semantic tokens
CSS custom properties that resolve to different values in light and dark mode. The plugin defines the light values on
:root / :host and overrides them inside .dark / :host(.dark).
Each token category has its own Tailwind utility — the class is {utility}-{token}:
text-primary → var(--tds-text-primary) light: tatum-gray-900 / dark: tatum-dark-gray-50
bg-primary → var(--tds-bg-primary) light: tatum-white / dark: tatum-dark-gray-950
border-secondary → var(--tds-border-secondary) light: tatum-gray-200 / dark: tatum-dark-gray-700
text-fg-primary → var(--tds-fg-primary) icon / foreground color
bg-utility-brand-100 status / palette color
ring-focus-ring → var(--tds-focus-ring) focus ring color
shadow-xs → var(--tds-shadow-xs) shadow color with baked-in opacityAlways prefer semantic tokens for component styling. They give you dark mode support for free.
<div className="bg-primary text-primary border border-secondary">
<Icon className="text-fg-brand-primary" />
</div>Dark Mode
Dark mode is activated by adding the .dark class or data-mode="dark" attribute to the root element:
<!-- class-based (Tailwind default) -->
<html class="dark">
<!-- attribute-based -->
<html data-mode="dark">Both strategies are supported simultaneously.
Using tailwind-merge with TDS Tokens
TDS defines custom text-* utilities for both font sizes (text-tatum-heading-xs) and colors (text-tatum-primary-500). A vanilla tailwind-merge instance cannot tell them apart — both match the text- prefix, so tw-merge groups them together and the last class silently wins, stripping the other.
The design system ships a withTds tw-merge plugin that solves this. Any project that uses TDS tokens with tailwind-merge must apply it.
Setup
Plug withTds into your project's clsxm / cn helper:
import { type ClassValue, clsx } from 'clsx'
import { extendTailwindMerge } from 'tailwind-merge'
import { withTds } from '@tatumio/tatum-design-system/tw-merge'
const twMerge = extendTailwindMerge(withTds)
export function clsxm(...classes: ClassValue[]) {
return twMerge(clsx(...classes))
}If you have additional Tailwind extensions beyond TDS, compose them alongside the plugin:
const twMerge = extendTailwindMerge(
{ extend: { /* your own additional extensions */ } },
withTds,
)Note:
tailwind-merge≥ 2.x is required. If your project uses v1.x, upgrade first — the plugin API was introduced in v2.
Licence
MIT — see the LICENSE file in this package.
Dependencies are installed alongside TDS and ship under their own licences;
none is bundled into dist.
External Resources
Some parts of TDS load resources from other hosts at runtime. Allow them in your Content Security Policy, and account for them in your privacy setup:
styles.cssimports the Poppins font from Google Fonts (fonts.googleapis.com,fonts.gstatic.com).CodeHighlightloads the Roboto Mono font from Google Fonts.ChainIconfetches the chain list fromhttps://blockchains.tatum.io/blockchains2.jsonand renders each chain's logo from the URL listed there (currently also onblockchains.tatum.io). If the list can't be loaded, the icon stays a placeholder.
TypeScript Support
All components include TypeScript definitions. For proper module resolution in consuming projects, we recommend using
TypeScript with "moduleResolution": "node16" or "bundler".
Contributing
TDS is developed by Tatum in a private repository and does not accept outside contributions. For questions or problems, contact Tatum.
