Theming
Customize the look and feel of Docyrus UI Native components with DocyThemeProvider, color schemes, and Tailwind CSS.
Overview
Docyrus UI Native uses Tailwind CSS v4 via Uniwind for component styling. Static styles are expressed as className using Tailwind utility classes and semantic color tokens (bg-card, text-foreground, border-border). Dynamic values that can't be expressed as className (DocyrusIcon colors, platform shadows, runtime color lookups) are accessed via the useDocyTheme() hook.
Two sources of truth, kept in sync by value:
- CSS tokens (
styles/docyrus-native/index.css, installed by the CLI) — what everyclassNameresolves against. Required; see Design Tokens. - JS palette (
useDocyTheme().colors) — the same colors as raw strings, for props that need one.
Setup
Import the design tokens from your Uniwind CSS entry (see Installation):
@import "tailwindcss";
@import "uniwind";
@import "./docyrus-native/index.css";Then wrap your app root with DocyThemeProvider:
import '../src/styles/global.css';
import { DocyThemeProvider } from '@/lib/docyrus/theme';
export default function RootLayout() {
return (
<DocyThemeProvider defaultMode="system" defaultColorScheme="docyrus">
{/* Your app content */}
</DocyThemeProvider>
);
}Provider Props
| Prop | Type | Default | Description |
|---|---|---|---|
colorScheme | ColorScheme | — | Controlled color scheme |
mode | ThemeMode | — | Controlled theme mode |
lang | ThemeLanguage | — | Controlled language |
defaultColorScheme | ColorScheme | 'docyrus' | Initial color scheme |
defaultMode | ThemeMode | 'system' | Initial theme mode |
defaultLang | ThemeLanguage | 'en' | Initial language |
brand | Record<string, string> | — | Custom brand colors |
fontFamily | ThemeFontFamily | System fonts | Custom font family |
messages | Record<string, any> | — | App-level message bag exposed through useDocyTheme(). Docyrus components no longer read it — translate component copy with <UiTranslationProvider> |
Design Tokens
index.css declares the tokens as Uniwind themes — one @variant light and one @variant dark block inside @layer theme { :root { … } }, with the identical variable set in both. Uniwind follows the device appearance (DocyThemeProvider's mode drives it through Appearance.setColorScheme), and Uniwind.setTheme('light' | 'dark' | 'system') forces one. Each --color-<name> becomes every color utility: bg-<name>, text-<name>, border-<name>, ring-<name>, bg-<name>/10, …
Core colors
| Token | Purpose |
|---|---|
background / foreground | App background and primary text |
card / card-foreground | Card surfaces and their text |
popover / popover-foreground | Popovers, dropdowns, menus |
modal / modal-foreground | Modal and dialog surfaces |
primary / primary-foreground | Primary actions (buttons, selected states) |
secondary / secondary-foreground | Secondary actions and surfaces |
muted / muted-foreground | Subdued backgrounds and secondary text |
accent / accent-foreground | Hover / highlighted rows and chips |
destructive / destructive-foreground | Destructive actions and errors |
success / success-foreground | Success states |
warning / warning-foreground | Warning states |
info / info-foreground | Informational states |
border | Default borders and dividers |
input | Input borders |
ring | Focus rings |
overlay | Default modal backdrop |
chart-1 … chart-5 | Chart series colors |
Extended tokens
| Token | Purpose |
|---|---|
overlay-strong | Darker backdrop for action sheets, dropdown menus and confirm dialogs |
surface-subtle | Low-contrast grouped surfaces (list sections, toolbars) |
surface-elevated | Raised surfaces above background |
sheet-surface | Bottom-sheet body |
sheet-chrome | Bottom-sheet header / handle area |
sheet-border | Bottom-sheet borders and separators |
field | Form field background |
field-border | Form field border |
field-divider | Dividers inside grouped fields |
field-placeholder | Placeholder text |
field-disabled | Disabled field background |
success-soft / warning-soft / info-soft / destructive-soft | 10% tinted status backgrounds (badges, alerts) |
Non-color tokens
| Token | Value | Utilities |
|---|---|---|
--spacing | 4px | p-4 = 16px, gap-2 = 8px, … |
--radius-sm … --radius-3xl | 6 / 8 (--radius) / 12 / 16 / 20 / 24 / 32 px | rounded-sm … rounded-3xl |
--font-sans, --font-sans-light/medium/semibold/bold | Inter_400Regular … Inter_700Bold | font-sans, font-sans-medium, … |
--field-height-sm/-/-lg | 40 / 48 / 56 px | Form field heights |
Overriding tokens
Don't edit the installed file — docyrus update rewrites it. Redefine tokens after the import in your own global.css, keeping the Uniwind structure and defining each token in both variants:
@import "tailwindcss";
@import "uniwind";
@import "./docyrus-native/index.css";
@layer theme {
:root {
@variant light {
--color-primary: hsl(221.2, 83.2%, 53.3%);
}
@variant dark {
--color-primary: hsl(217.2, 91.2%, 59.8%);
}
}
}Use plain CSS colors (hsl(h, s%, l%), hsla(…), rgba(…), hex). Uniwind logs Theme X is missing variable … when a variable is declared in one theme but not in the others.
Styling with Tailwind className
All components accept a className prop for Tailwind-based styling. Use semantic color tokens for theme-aware colors:
import { View, Text } from 'react-native';
function MyCard() {
return (
<View className="bg-card rounded-lg border border-border p-4">
<Text className="text-foreground font-semibold">Card title</Text>
<Text className="text-muted-foreground text-sm mt-1">Description</Text>
</View>
);
}className vs style Decision Guide
| Use Case | Approach | Example |
|---|---|---|
| Static layout/colors | className | className="flex-1 p-4 bg-card rounded-lg" |
| Semantic colors | className | className="text-foreground bg-primary/10" |
| Dark mode | className | className="bg-muted dark:bg-background" |
| Pressable states | className | className="active:opacity-70 active:scale-95" |
| Animated values | style | style={{ opacity: fadeAnim }} |
| Platform shadows | style | style={cardShadow(isDark, colors.border)} |
| DocyrusIcon color | color prop | color={colors.primary} |
| Dynamic theme lookup | style | style={{ backgroundColor: colors[key] }} |
useDocyTheme Hook
Access theme values for dynamic use cases only — static styling should use Tailwind className:
import { useDocyTheme } from '@/lib/docyrus/theme';
import { DocyrusIcon } from '@/components/docyrus-native/docyrus-icon';
function MyComponent() {
const { colors, isDark } = useDocyTheme();
return (
<View className="flex-row items-center gap-2 p-4 bg-card rounded-lg">
{/* DocyrusIcon needs raw color string — can't use className */}
<DocyrusIcon icon="fal circle-check" size="default" color={colors.success} />
<Text className="text-foreground font-medium">Verified</Text>
</View>
);
}Theme Object
const theme = useDocyTheme();
theme.colors // ThemeColors — semantic + Tailwind palette
theme.spacing // ThemeSpacing — numeric scale
theme.borderRadius // ThemeBorderRadius
theme.fieldHeight // ThemeFieldHeight
theme.fontFamily // ThemeFontFamily
theme.isDark // boolean — for conditional logic
theme.colorScheme // 'docyrus' | 'slate' | 'gray' | 'zinc' | ...
theme.mode // 'light' | 'dark' | 'system'
theme.lang // 'en' | 'tr'
theme.brand // Custom brand object
theme.messages // i18n messagesColor Schemes
Docyrus UI Native ships with 13 color schemes, each providing light and dark variants:
Base Schemes (Neutral)
| Scheme | Description |
|---|---|
slate | Blue-gray neutral tones |
gray | Pure gray neutral tones |
zinc | Warm gray neutral tones |
stone | Warm beige neutral tones |
neutral | True neutral (no hue) |
Accent Schemes
| Scheme | Primary Color | Base |
|---|---|---|
docyrus | Slate + Blue accent | Slate |
red | Red 600 | Zinc |
rose | Rose 600 | Zinc |
orange | Orange 500 | Stone |
green | Green 600 | Zinc |
blue | Blue 500 | Slate |
yellow | Yellow 500 | Stone |
violet | Violet 500 | Gray |
<DocyThemeProvider colorScheme="blue" mode="dark">
<App />
</DocyThemeProvider>colorScheme switches the JS palette (useDocyTheme().colors). The className tokens come from CSS: index.css ships only the default docyrus scheme. The other 12 are opt-in Uniwind custom themes in color-schemes.css (installed next to index.css), named <scheme>-light / <scheme>-dark, with the same variable set:
-
Import it after
index.css, by relative path:src/styles/global.css @import "tailwindcss"; @import "uniwind"; @import "./docyrus-native/index.css"; @import "./docyrus-native/color-schemes.css"; -
Register the theme names in Metro — Tailwind rejects an
@variantit doesn't know, so every scheme in the file must be listed. Restart Metro with--clearafterwards:metro.config.js const schemes = ['slate', 'gray', 'zinc', 'stone', 'neutral', 'red', 'rose', 'orange', 'green', 'blue', 'yellow', 'violet']; module.exports = withUniwindConfig(config, { cssEntryFile: './src/styles/global.css', polyfills: { rem: 14 }, extraThemes: schemes.flatMap(scheme => [`${scheme}-light`, `${scheme}-dark`]) }); -
Switch at runtime, keeping the JS palette in step:
import { Uniwind } from 'uniwind'; const { setColorScheme, isDark } = useDocyTheme(); function applyScheme(scheme: ColorScheme) { setColorScheme(scheme); Uniwind.setTheme(scheme === 'docyrus' ? 'system' : `${scheme}-${isDark ? 'dark' : 'light'}`); }A custom theme is not adaptive — switch between
<scheme>-lightand<scheme>-darkyourself when the appearance changes, and note thatdark:utilities only apply under the built-indarktheme.
Core colors in color-schemes.css mirror the JS palettes; the extended surface / field tokens reuse the default Docyrus values.
Semantic Color Tokens
Every color scheme provides these semantic tokens, available as both Tailwind classes and colors.* values (the full class-only list is in Design Tokens):
| Token | Tailwind Class | JS Access | Description |
|---|---|---|---|
background | bg-background | colors.background | App background |
foreground | text-foreground | colors.foreground | Primary text |
card | bg-card | colors.card | Card surfaces |
primary | bg-primary | colors.primary | Primary actions |
secondary | bg-secondary | colors.secondary | Secondary surfaces |
muted | bg-muted | colors.muted | Muted backgrounds |
destructive | bg-destructive | colors.destructive | Destructive actions |
border | border-border | colors.border | Borders |
success | bg-success | colors.success | Success states |
warning | bg-warning | colors.warning | Warning states |
info | bg-info | colors.info | Info states |
Use Tailwind classes for static colors (className="bg-card text-foreground"). Use colors.* only when a raw color string is needed (DocyrusIcon, chart libraries, cardShadow(), runtime lookups).
Tailwind Color Palette
In addition to semantic tokens, colors includes the full Tailwind palette (50–950 shades):
const { colors } = useDocyTheme();
// Only needed for dynamic runtime lookups — prefer className for static colors
colors.blue[500] // '#3b82f6'
colors.red[600] // '#dc2626'
colors.emerald[400] // '#34d399'Available palettes: red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose.
Dark Mode
With Tailwind Classes
Components support dark mode variants via Tailwind:
<View className="bg-muted dark:bg-background">
<Text className="text-foreground">Adapts to dark mode</Text>
</View>System-Based (Default)
<DocyThemeProvider mode="system">The theme automatically follows the device's appearance. isDark reflects the resolved state.
Manual Control
function SettingsScreen() {
const [mode, setMode] = useState<ThemeMode>('system');
return (
<DocyThemeProvider mode={mode}>
<Button onPress={() => setMode('dark')}>Dark</Button>
<Button onPress={() => setMode('light')}>Light</Button>
<Button onPress={() => setMode('system')}>System</Button>
</DocyThemeProvider>
);
}Checking Dark Mode
Use isDark for logic that can't be expressed as className (platform shadows, conditional rendering):
const { isDark, colors } = useDocyTheme();
// Platform shadows need style prop — no Tailwind equivalent
<View style={cardShadow(isDark, colors.border)}>Custom Fonts
Override the default font family:
<DocyThemeProvider
fontFamily={{
light: 'Inter-Light',
regular: 'Inter-Regular',
medium: 'Inter-Medium',
semiBold: 'Inter-SemiBold',
bold: 'Inter-Bold',
extraBold: 'Inter-ExtraBold',
}}
>With Expo, load fonts first using expo-font:
import { useFonts } from 'expo-font';
function App() {
const [fontsLoaded] = useFonts({
'Inter-Regular': require('./assets/fonts/Inter-Regular.ttf'),
'Inter-Bold': require('./assets/fonts/Inter-Bold.ttf'),
});
if (!fontsLoaded) return null;
return (
<DocyThemeProvider fontFamily={{ regular: 'Inter-Regular', bold: 'Inter-Bold' }}>
<App />
</DocyThemeProvider>
);
}Brand Colors
Add custom brand-specific colors available throughout the theme:
type MyBrand = {
accentBlue: string;
accentGold: string;
headerBg: string;
};
<DocyThemeProvider<MyBrand>
brand={{
accentBlue: '#1E90FF',
accentGold: '#FFD700',
headerBg: '#0A1128',
}}
>Access brand colors in components:
const { brand } = useDocyTheme<MyBrand>();
// Brand colors are dynamic — use style prop
<View style={{ backgroundColor: brand?.headerBg }}>Localization
The provider stores the active language (lang / defaultLang) and an optional messages bag for your own app code:
<DocyThemeProvider lang="tr">const { lang, messages } = useDocyTheme();Docyrus native components do not read lang or messages for their built-in copy. Every component renders an English fallback and looks up ui.<group>.<key> through <UiTranslationProvider> — the same keys as the web package, so one translation catalog covers both platforms. Explicit copy props (label, cancelLabel, messages, …) always win.
Web vs Native Comparison
| Feature | Web (CSS Variables) | Native (Tailwind + Theme) |
|---|---|---|
| Static styling | className="bg-primary p-4" | className="bg-primary p-4" |
| Token access | var(--primary) in CSS | className="bg-primary" or colors.primary |
| Dark mode | .dark class + CSS | className="dark:bg-background" or isDark |
| Color scheme | CSS variable swap | colorScheme prop on provider |
| Custom colors | CSS variable override | brand prop |
| Fonts | CSS font-family | fontFamily prop |
| Animated values | CSS transitions / motion | style prop with Animated/Reanimated |
| 3rd-party colors | CSS variable reference | colors.* from useDocyTheme() |