Skip to content

jayrdeaton/React-Native-Auto-Paper

Repository files navigation

@rific/auto-paper

Adaptive theming for react-native-paper. Give it one color and an appearance setting — it generates a triadic MD3 palette and handles light/dark/system mode automatically.

Features

  • Triadic color palette from a single seed color (primary → secondary → tertiary, 120° apart on the color wheel)
  • System/light/dark appearance with live updates via Appearance API
  • Tinted surface, surfaceVariant, outline, and elevation levels derived from the seed
  • Optional Redux slice for wiring appearance and color into your store
  • All color utilities exported for standalone use

Installation

npm install @rific/auto-paper

Peer dependencies:

npm install react-native react-native-paper

Usage

Provider (simplest)

import { Provider as AutoPaperProvider } from '@rific/auto-paper'

export default function App() {
  const [appearance, setAppearance] = useState<'system' | 'light' | 'dark'>('system')
  const [color, setColor] = useState('#6750a4')

  return (
    <AutoPaperProvider appearance={appearance} color={color}>
      {/* your app */}
    </AutoPaperProvider>
  )
}

Provider renders null on the first render while the theme computes, then wraps your app in PaperProvider with the computed theme, a matching StatusBar, and a background View. Use onReady to hook into that moment — e.g. to dismiss a splash screen:

<AutoPaperProvider appearance={appearance} color={color} onReady={SplashScreen.hideAsync}>
  {/* your app */}
</AutoPaperProvider>

With Redux

The package exports an optional Redux slice. Use createThemeReducer to register it in your store with a custom initial color:

// store.ts
import { createThemeReducer, themeActions } from '@rific/auto-paper'

export const store = configureStore({
  reducer: {
    theme: createThemeReducer({ color: '#4caf50' }),
    // ...
  }
})

Then read from the store and pass into Provider:

// App.tsx
import { useSelector } from 'react-redux'
import { Provider as AutoPaperProvider } from '@rific/auto-paper'

export default function App() {
  const { appearance, color } = useSelector((state: RootState) => state.theme)
  return (
    <AutoPaperProvider appearance={appearance} color={color} onReady={SplashScreen.hideAsync}>
      {/* your app */}
    </AutoPaperProvider>
  )
}
// SettingsScreen.tsx
import { themeActions } from '@rific/auto-paper'
import { useDispatch } from 'react-redux'

dispatch(themeActions.setColor('#e91e63'))
dispatch(themeActions.setAppearance('dark'))

With useComputedTheme

Use the hook directly when you need to extend the theme before passing it to PaperProvider — for example, to merge in a navigation theme or add custom color keys:

import { useComputedTheme } from '@rific/auto-paper'
import { DarkTheme, DefaultTheme, ThemeProvider as NavThemeProvider } from '@react-navigation/native'
import { Provider as PaperProvider } from 'react-native-paper'

export default function App() {
  const theme = useComputedTheme('system', '#6750a4')
  if (!theme) return null

  const navTheme = {
    ...(theme.dark ? DarkTheme : DefaultTheme),
    colors: {
      ...(theme.dark ? DarkTheme : DefaultTheme).colors,
      background: theme.colors.background,
      card: theme.colors.background
    }
  }

  return (
    <NavThemeProvider value={navTheme}>
      <PaperProvider theme={theme}>
        {/* your app */}
      </PaperProvider>
    </NavThemeProvider>
  )
}

API

Provider

Prop Type Description
appearance 'system' | 'light' | 'dark' Appearance mode
color string Seed color (hex, rgb, or CSS name)
children ReactNode
onReady () => void Called once when the theme first resolves
statusBarProps StatusBarProps Spread over the auto-derived StatusBar defaults
style StyleProp<ViewStyle> Applied to the wrapper View

useComputedTheme(appearance, color)

Returns MD3Theme | null. null on the first render while the theme computes.

const theme = useComputedTheme('system', '#6750a4')

createThemeReducer(initialState?)

Returns a Redux reducer with an optional initial state override. Useful for setting a custom default color without dispatching an action at startup.

createThemeReducer({ color: '#4caf50' })

Redux exports

import { themeReducer, themeActions, selectThemeAppearance, selectThemeColor } from '@rific/auto-paper'
import type { ThemeState, ThemeAppearance } from '@rific/auto-paper'
Action Payload
initialize Partial<ThemeState>
setAppearance ThemeAppearance
setColor string

Selectors accept ThemeState directly — compose them with your root state selector:

const appearance = useSelector((state: RootState) => selectThemeAppearance(state.theme))

Color utilities

import { getTriadicPalette, getBlendedColor, isDarkColor, getRgb, getHex } from '@rific/auto-paper'

getTriadicPalette('#ff0000')
// → { primary: '#ff0000', secondary: '#00ff00', tertiary: '#0000ff' }

getBlendedColor('#ff0000', '#0000ff', 0.5)  // → '#800080'
isDarkColor('#6750a4')                       // → true
getRgb('coral')                              // → { r: 255, g: 127, b: 80 }
getHex('rgb(255, 0, 0)')                     // → '#ff0000'

License

MIT