Guide

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 every className resolves 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):

src/styles/global.css
@import "tailwindcss";
@import "uniwind";
@import "./docyrus-native/index.css";

Then wrap your app root with DocyThemeProvider:

app/_layout.tsx
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

PropTypeDefaultDescription
colorSchemeColorScheme—Controlled color scheme
modeThemeMode—Controlled theme mode
langThemeLanguage—Controlled language
defaultColorSchemeColorScheme'docyrus'Initial color scheme
defaultModeThemeMode'system'Initial theme mode
defaultLangThemeLanguage'en'Initial language
brandRecord<string, string>—Custom brand colors
fontFamilyThemeFontFamilySystem fontsCustom font family
messagesRecord<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

TokenPurpose
background / foregroundApp background and primary text
card / card-foregroundCard surfaces and their text
popover / popover-foregroundPopovers, dropdowns, menus
modal / modal-foregroundModal and dialog surfaces
primary / primary-foregroundPrimary actions (buttons, selected states)
secondary / secondary-foregroundSecondary actions and surfaces
muted / muted-foregroundSubdued backgrounds and secondary text
accent / accent-foregroundHover / highlighted rows and chips
destructive / destructive-foregroundDestructive actions and errors
success / success-foregroundSuccess states
warning / warning-foregroundWarning states
info / info-foregroundInformational states
borderDefault borders and dividers
inputInput borders
ringFocus rings
overlayDefault modal backdrop
chart-1 … chart-5Chart series colors

Extended tokens

TokenPurpose
overlay-strongDarker backdrop for action sheets, dropdown menus and confirm dialogs
surface-subtleLow-contrast grouped surfaces (list sections, toolbars)
surface-elevatedRaised surfaces above background
sheet-surfaceBottom-sheet body
sheet-chromeBottom-sheet header / handle area
sheet-borderBottom-sheet borders and separators
fieldForm field background
field-borderForm field border
field-dividerDividers inside grouped fields
field-placeholderPlaceholder text
field-disabledDisabled field background
success-soft / warning-soft / info-soft / destructive-soft10% tinted status backgrounds (badges, alerts)

Non-color tokens

TokenValueUtilities
--spacing4pxp-4 = 16px, gap-2 = 8px, …
--radius-sm … --radius-3xl6 / 8 (--radius) / 12 / 16 / 20 / 24 / 32 pxrounded-sm … rounded-3xl
--font-sans, --font-sans-light/medium/semibold/boldInter_400Regular … Inter_700Boldfont-sans, font-sans-medium, …
--field-height-sm/-/-lg40 / 48 / 56 pxForm 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:

src/styles/global.css
@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 CaseApproachExample
Static layout/colorsclassNameclassName="flex-1 p-4 bg-card rounded-lg"
Semantic colorsclassNameclassName="text-foreground bg-primary/10"
Dark modeclassNameclassName="bg-muted dark:bg-background"
Pressable statesclassNameclassName="active:opacity-70 active:scale-95"
Animated valuesstylestyle={{ opacity: fadeAnim }}
Platform shadowsstylestyle={cardShadow(isDark, colors.border)}
DocyrusIcon colorcolor propcolor={colors.primary}
Dynamic theme lookupstylestyle={{ 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 messages

Color Schemes

Docyrus UI Native ships with 13 color schemes, each providing light and dark variants:

Base Schemes (Neutral)

SchemeDescription
slateBlue-gray neutral tones
grayPure gray neutral tones
zincWarm gray neutral tones
stoneWarm beige neutral tones
neutralTrue neutral (no hue)

Accent Schemes

SchemePrimary ColorBase
docyrusSlate + Blue accentSlate
redRed 600Zinc
roseRose 600Zinc
orangeOrange 500Stone
greenGreen 600Zinc
blueBlue 500Slate
yellowYellow 500Stone
violetViolet 500Gray
<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:

  1. 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";
  2. Register the theme names in Metro — Tailwind rejects an @variant it doesn't know, so every scheme in the file must be listed. Restart Metro with --clear afterwards:

    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`])
    });
  3. 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>-light and <scheme>-dark yourself when the appearance changes, and note that dark: utilities only apply under the built-in dark theme.

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):

TokenTailwind ClassJS AccessDescription
backgroundbg-backgroundcolors.backgroundApp background
foregroundtext-foregroundcolors.foregroundPrimary text
cardbg-cardcolors.cardCard surfaces
primarybg-primarycolors.primaryPrimary actions
secondarybg-secondarycolors.secondarySecondary surfaces
mutedbg-mutedcolors.mutedMuted backgrounds
destructivebg-destructivecolors.destructiveDestructive actions
borderborder-bordercolors.borderBorders
successbg-successcolors.successSuccess states
warningbg-warningcolors.warningWarning states
infobg-infocolors.infoInfo 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

FeatureWeb (CSS Variables)Native (Tailwind + Theme)
Static stylingclassName="bg-primary p-4"className="bg-primary p-4"
Token accessvar(--primary) in CSSclassName="bg-primary" or colors.primary
Dark mode.dark class + CSSclassName="dark:bg-background" or isDark
Color schemeCSS variable swapcolorScheme prop on provider
Custom colorsCSS variable overridebrand prop
FontsCSS font-familyfontFamily prop
Animated valuesCSS transitions / motionstyle prop with Animated/Reanimated
3rd-party colorsCSS variable referencecolors.* from useDocyTheme()

On this page