Tally UI

Design System

Shared design tokens, theming, and styling utilities

Overview

Tally UI uses a shared design system built on Tailwind CSS 4 and Uniwind. The @tallyui/theme package provides semantic design tokens, light/dark theming, and a cn() utility for className composition.

All components use className for styling — no StyleSheet.create() or inline style objects. This works across iOS, Android, and Web through Uniwind's build-time compilation.

pnpm add @tallyui/theme

Design Tokens

Tokens are defined in @tallyui/theme/tokens.css using Tailwind 4's @theme directive. They map to semantic Tailwind classes:

Backgrounds

TokenClassLightDark
--color-bgbg-bg#f8f9fa#0d1117
--color-surfacebg-surface#ffffff#161b22
--color-surface-altbg-surface-alt#f3f4f6#1c2128

Text

TokenClassLightDark
--color-foregroundtext-foreground#111827#c9d1d9
--color-mutedtext-muted#6b7280#8b949e
--color-brighttext-bright#f9fafb#f0f6fc

Status

TokenClassLightDark
--color-successtext-success#059669#3fb950
--color-dangertext-danger#dc2626#f85149
--color-warningtext-warning#d97706#d29922
--color-infotext-info#3b82f6#58a6ff

Commerce

TokenClassLightDark
--color-pricetext-price#059669#3fb950
--color-saletext-sale#dc2626#f85149

Other

TokenClassLightDark
--color-borderborder-border#e5e7eb#30363d
--color-primarybg-primary#6366f1#818cf8
--color-primary-foregroundtext-primary-foreground#ffffff#1e1b4b

Theming

Tally UI supports light and dark themes out of the box. Tokens automatically switch values based on the active theme.

Switching Themes

import { Uniwind } from 'uniwind';

// Set a specific theme
Uniwind.setTheme('dark');
Uniwind.setTheme('light');

// Follow device preferences
Uniwind.setTheme('system');

Using Dark Variants

You can also use Tailwind's dark: prefix for one-off overrides:

<View className="bg-surface dark:bg-surface-alt">
  <Text className="text-foreground">Adapts to theme</Text>
</View>

The cn() Utility

The cn() function combines clsx (conditional class logic) with tailwind-merge (conflict resolution). Import it from @tallyui/theme:

import { cn } from '@tallyui/theme';

// Conditional classes
cn('text-sm', isActive && 'text-primary')
// → 'text-sm text-primary' (when active)

// Overriding defaults
cn('text-base text-foreground', className)
// → consumer's className wins over defaults

Overriding Component Styles

Every Tally UI component accepts a className prop. Defaults are applied internally, and your classes override them via cn():

// Default styling
<ProductTitle doc={doc} />

// Override with your own classes
<ProductTitle doc={doc} className="text-2xl font-bold text-primary" />

This works because components use cn() internally:

// Inside ProductTitle
<Text className={cn('text-base font-semibold text-foreground', className)}>
  {getName(doc)}
</Text>

Your text-2xl replaces the default text-base, your font-bold replaces font-semibold, and text-primary replaces text-foreground. Non-conflicting classes merge normally.

Opacity Modifiers

Tailwind's opacity syntax works with all tokens. This is useful for backgrounds on status badges:

<View className="bg-success/15">  {/* 15% opacity green background */}
<View className="bg-danger/10">   {/* 10% opacity red background */}

Setup for New Apps

If you're building a new app with Tally UI components, you need to import the design tokens.

React Native (Expo + Uniwind)

Create a global.css at your app root:

@import 'tailwindcss';
@import 'uniwind';
@import '@tallyui/theme/tokens.css';

@source '../../packages/components';

Import it in your root layout:

import './global.css';

And wrap your Metro config:

const { withUniwindConfig } = require('uniwind/metro');

module.exports = withUniwindConfig(config, {
  cssEntryFile: './global.css',
});

Web (Next.js / Vite)

Import the tokens in your global CSS file:

@import 'tailwindcss';
@import '@tallyui/theme/tokens.css';

On this page