Theming
Comprehensive design token system with four built-in themes: Dark, Light, Oatmeal, and Glass.
Built-in Themes
Hyena includes four complete themes. Each theme provides a full set of color, spacing, and effect tokens.
Dark Theme
Premium dark theme optimized for OLED displays. Deep blacks (#050505), high contrast white text, vibrant accents. Default theme.
Light Theme
Clean, bright theme for daytime use. Pure white background (#ffffff), dark text, softer shadows.
Oatmeal Theme
Warm, neutral theme with cream and beige tones (#F5F2ED). Earthy palette with reduced blue light and muted accents.
Glass Theme
Glassmorphism theme with translucent surfaces (rgba(255,255,255,0.65)), backdrop blur effects, and subtle shadows.
ThemeProvider
Wrap your app with ThemeProvider to enable theming:
import { ThemeProvider } from '@hyena-studio/react-native'
function App() {
return (
<ThemeProvider defaultTheme="dark">
<MyApp />
</ThemeProvider>
)
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultTheme | 'light' | 'dark' | 'oatmeal' | 'glass' | 'dark' | Initial theme |
children | ReactNode | — | App content |
useTheme Hook
Access theme context in any component:
import { useTheme } from '@hyena-studio/react-native'
function MyComponent() {
const {
themeName, // Current theme: 'dark' | 'light' | 'oatmeal' | 'glass'
theme, // Full theme object with all tokens
tokens, // Alias for theme
setTheme, // Function to change theme
availableThemes, // Array of available theme names
isGlass, // Helper boolean: true when themeName === 'glass'
} = useTheme()
return (
<View style={{ backgroundColor: theme.colors.background }}>
<Text style={{ color: theme.colors.text.primary }}>
Current theme: {themeName}
</Text>
{isGlass && <Text>Glass mode active!</Text>}
<Button onPress={() => setTheme('light')}>
Switch to Light
</Button>
</View>
)
}
Theme Toggle Example
function ThemeToggle() {
const { themeName, setTheme } = useTheme()
return (
<View style={{ flexDirection: 'row', gap: 8 }}>
<Button
variant={themeName === 'dark' ? 'primary' : 'ghost'}
onPress={() => setTheme('dark')}
>
Dark
</Button>
<Button
variant={themeName === 'light' ? 'primary' : 'ghost'}
onPress={() => setTheme('light')}
>
Light
</Button>
<Button
variant={themeName === 'oatmeal' ? 'primary' : 'ghost'}
onPress={() => setTheme('oatmeal')}
>
Oatmeal
</Button>
<Button
variant={themeName === 'glass' ? 'primary' : 'ghost'}
onPress={() => setTheme('glass')}
>
Glass
</Button>
</View>
)
}
Design Tokens
Hyena uses a token-based system for consistent styling. Import tokens directly from @hyena-studio/react-native.
Colors
import { colors } from '@hyena-studio/react-native'
// Background hierarchy (5 levels)
colors.bg.base // '#050505' — App background
colors.bg.raised // '#0a0a0a' — Slightly elevated
colors.bg.surface // '#141414' — Cards, inputs
colors.bg.elevated // '#1f1f1f' — Modals, popovers
colors.bg.overlay // 'rgba(0,0,0,0.8)' — Backdrop
// Text hierarchy
colors.text.primary // '#ffffff' — Main text
colors.text.secondary // 'rgba(255,255,255,0.6)' — Secondary
colors.text.muted // 'rgba(255,255,255,0.4)' — Hints
colors.text.inverse // '#0a0a0a' — On light backgrounds
// Borders
colors.border.default // 'rgba(255,255,255,0.1)'
colors.border.muted // 'rgba(255,255,255,0.06)'
colors.border.strong // 'rgba(255,255,255,0.2)'
// Accent colors (each with light/DEFAULT/dark)
colors.accent.blue.light // '#60a5fa'
colors.accent.blue.DEFAULT // '#3b82f6'
colors.accent.blue.dark // '#2563eb'
colors.accent.green.DEFAULT // '#22c55e'
colors.accent.amber.DEFAULT // '#f59e0b'
colors.accent.red.DEFAULT // '#ef4444'
colors.accent.purple.DEFAULT // '#a855f7'
// Semantic colors
colors.semantic.success // '#22c55e'
colors.semantic.warning // '#f59e0b'
colors.semantic.error // '#ef4444'
colors.semantic.info // '#3b82f6'
Spacing
Based on a 4px grid system:
import { spacing, space } from '@hyena-studio/react-native'
// Numeric scale
spacing[0] // 0
spacing[1] // 4px
spacing[2] // 8px
spacing[3] // 12px
spacing[4] // 16px
spacing[6] // 24px
spacing[8] // 32px
spacing[12] // 48px
spacing[16] // 64px
// Semantic aliases
space.none // 0
space.xs // 4px
space.sm // 8px
space.md // 16px
space.lg // 24px
space.xl // 32px
Typography
import {
fontFamilies,
fontSizes,
fontWeights,
textStyles
} from '@hyena-studio/react-native'
// Font families
fontFamilies.sans // 'Inter'
fontFamilies.mono // 'JetBrains Mono'
// Font sizes
fontSizes.xs // 12px
fontSizes.sm // 14px
fontSizes.base // 16px
fontSizes.lg // 18px
fontSizes.xl // 20px
fontSizes['2xl'] // 24px
fontSizes['3xl'] // 30px
fontSizes['4xl'] // 36px
// Font weights
fontWeights.normal // '400'
fontWeights.medium // '500'
fontWeights.semibold // '600'
fontWeights.bold // '700'
// Pre-composed text styles
textStyles.h1 // { fontSize: 36, fontWeight: '700', ... }
textStyles.h2 // { fontSize: 30, fontWeight: '700', ... }
textStyles.body // { fontSize: 16, fontWeight: '400', ... }
textStyles.code // { fontFamily: 'JetBrains Mono', ... }
Border Radius
import { radius } from '@hyena-studio/react-native'
radius.sm // 6px
radius.md // 10px
radius.lg // 14px
radius.xl // 20px
radius.full // 9999px (circular)
Shadows
Cross-platform shadow system:
import { shadows } from '@hyena-studio/react-native'
// Apply to any View
<View style={shadows.sm} /> // Subtle shadow
<View style={shadows.md} /> // Medium shadow
<View style={shadows.lg} /> // Large shadow
<View style={shadows.xl} /> // Extra large shadow
// Automatically uses:
// - shadowColor/shadowOffset/shadowOpacity/shadowRadius on iOS
// - elevation on Android
Using Tokens
Direct Import (Static Tokens)
Use the static token exports for theme-agnostic values:
import { colors, spacing, radius, fontSizes } from '@hyena-studio/react-native'
import { View, Text, StyleSheet } from 'react-native'
const styles = StyleSheet.create({
card: {
backgroundColor: colors.bg.surface,
borderRadius: radius.lg,
padding: spacing[4],
borderWidth: 1,
borderColor: colors.border.default,
},
title: {
color: colors.text.primary,
fontSize: fontSizes.lg,
marginBottom: spacing[2],
},
})
Theme-Aware (Dynamic Tokens)
Use useTheme() for theme-reactive styles:
import { useTheme } from '@hyena-studio/react-native'
function Card({ children }) {
const { theme } = useTheme()
return (
<View style={{
backgroundColor: theme.colors.surface,
borderRadius: theme.radius.card || 14,
borderColor: theme.colors.border,
}}>
{children}
</View>
)
}
With NativeWind
If using NativeWind, tokens are available as Tailwind classes:
<View className="bg-surface rounded-lg p-4 border border-default">
<Text className="text-primary text-lg mb-2">Title</Text>
</View>
Theme Reference
Theme Colors
Each theme provides these color properties:
| Property | Description |
|---|---|
colors.background | Main app background |
colors.surface | Card/component surfaces |
colors.surfaceElevated | Elevated surfaces (modals, popovers) |
colors.border | Default border color |
colors.borderSubtle | Subtle/muted borders |
colors.text.primary | Main text color |
colors.text.secondary | Secondary text |
colors.text.muted | Muted/hint text |
colors.accent.* | Accent color variants |
colors.semantic.* | Success, warning, error, info |
Dark Theme Values
{
background: '#050505',
surface: '#141414',
surfaceElevated: '#1f1f1f',
border: 'rgba(255, 255, 255, 0.1)',
borderSubtle: 'rgba(255, 255, 255, 0.06)',
text: {
primary: '#ffffff',
secondary: 'rgba(255, 255, 255, 0.6)',
muted: 'rgba(255, 255, 255, 0.4)',
}
}
Light Theme Values
{
background: '#ffffff',
surface: '#f5f5f5',
surfaceElevated: '#ffffff',
border: 'rgba(0, 0, 0, 0.1)',
borderSubtle: 'rgba(0, 0, 0, 0.05)',
text: {
primary: '#1a1a1a',
secondary: '#666666',
muted: '#999999',
}
}
Oatmeal Theme Values
{
background: '#F5F2ED',
surface: '#EDE9E3',
surfaceElevated: '#FFFFFF',
border: 'rgba(0, 0, 0, 0.08)',
borderSubtle: 'rgba(0, 0, 0, 0.04)',
text: {
primary: '#2D2A26',
secondary: '#6B6560',
muted: '#9A938C',
}
}
Glass Theme Values
{
background: '#E8E4DF',
backgroundSubtle: '#F2EFEB',
surface: 'rgba(255, 255, 255, 0.65)',
surfaceElevated: 'rgba(255, 255, 255, 0.8)',
surfaceSubtle: 'rgba(255, 255, 255, 0.5)',
border: 'rgba(255, 255, 255, 0.8)',
borderSubtle: 'rgba(0, 0, 0, 0.06)',
text: {
primary: '#1a1a1a',
secondary: '#666666',
muted: '#888888',
inverted: '#ffffff',
},
effects: {
blur: { sm: 12, md: 24, lg: 40 },
shadow: {
sm: '0 2px 8px rgba(0, 0, 0, 0.04)',
md: '0 4px 24px rgba(0, 0, 0, 0.06)',
lg: '0 8px 40px rgba(0, 0, 0, 0.08)',
}
}
}
Glass Theme
The Glass theme is a complete theme (not a mode overlay) that provides glassmorphism effects with translucent surfaces and backdrop blur.
Enabling Glass Theme
import { ThemeProvider } from '@hyena-studio/react-native'
function App() {
return (
<ThemeProvider defaultTheme="glass">
{/* All components render with glass styling */}
</ThemeProvider>
)
}
How Glass Theme Works
When the Glass theme is active:
- Surfaces use translucent backgrounds (
rgba(255,255,255,0.65)) - Components apply backdrop blur effects via the
GlassSurfacewrapper - Borders use translucent white for frosted edge effects
- Shadows are softer and more diffused
- The
isGlasshelper fromuseTheme()returnstrue
GlassSurface Component
Build custom glass-styled components:
import { GlassSurface } from '@hyena-studio/react-native'
<GlassSurface
intensity={16} // Blur intensity (0-100)
opacity={0.7} // Background opacity
borderRadius={14} // Corner radius
shadow="md" // Shadow size: 'none' | 'sm' | 'md' | 'lg'
bordered // Add translucent border
>
<Text>Content on glass surface</Text>
</GlassSurface>
Glass-Enabled Components
These components automatically apply glass styling when the Glass theme is active:
Surfaces & Containers: Card, Dialog, Sheet, Toast, Popover, EmptyState
Menus & Selection: Dropdown, ContextMenu, Select, Command, NavigationMenu, ActionSheet
Interactive Elements: AlertDialog, Tooltip, HoverCard, Accordion, Collapsible, Tabs
Layout & Navigation: Onboarding, Announcement, Sidebar, Navbar
Detecting Glass Theme
Check if Glass theme is active in your components:
import { useTheme } from '@hyena-studio/react-native'
function MyComponent() {
const { isGlass, theme } = useTheme()
return isGlass ? (
<GlassSurface intensity={16} borderRadius={14}>
<Text style={{ color: theme.colors.text.primary }}>
Glass mode content
</Text>
</GlassSurface>
) : (
<View style={{ backgroundColor: theme.colors.surface }}>
<Text style={{ color: theme.colors.text.primary }}>
Standard mode content
</Text>
</View>
)
}
Recommended Backgrounds
Glass effects work best over colorful or textured backgrounds:
<View style={{
flex: 1,
backgroundColor: '#E8E4DF', // Warm background from glass theme
}}>
<Card>
{/* Card renders with glass effect */}
</Card>
</View>
Performance Considerations
- Backdrop blur uses
backdrop-filteron web andBlurViewon native - Glass effects can impact performance on lower-end devices
- Consider providing a non-glass fallback for accessibility
- Use
useReducedMotionhook to respect user preferences
CSS Variables (Web)
On web, Hyena uses CSS variables for theming. This enables smooth theme transitions and runtime customization.
:root {
--color-bg-base: #050505;
--color-bg-surface: #141414;
--color-text-primary: #ffffff;
--color-accent-blue: #3b82f6;
/* ... */
}
[data-theme="light"] {
--color-bg-base: #ffffff;
--color-bg-surface: #f5f5f5;
--color-text-primary: #1a1a1a;
/* ... */
}
Creating a Custom Theme
Step 1: Define Your Theme
// theme/custom.ts
import type { Theme } from '@hyena-studio/react-native'
export const customTheme: Theme = {
name: 'custom',
colors: {
background: '#1a1625', // Deep purple-black
surface: '#2d2640',
surfaceElevated: '#3d3556',
border: 'rgba(255, 255, 255, 0.1)',
borderSubtle: 'rgba(255, 255, 255, 0.06)',
text: {
primary: '#f8f4ff',
secondary: 'rgba(248, 244, 255, 0.7)',
muted: 'rgba(248, 244, 255, 0.4)',
},
accent: {
blue: { light: '#818cf8', DEFAULT: '#6366f1', dark: '#4f46e5' },
green: { light: '#4ade80', DEFAULT: '#22c55e', dark: '#16a34a' },
amber: { light: '#fbbf24', DEFAULT: '#f59e0b', dark: '#d97706' },
red: { light: '#f87171', DEFAULT: '#ef4444', dark: '#dc2626' },
purple: { light: '#c084fc', DEFAULT: '#a855f7', dark: '#9333ea' },
},
semantic: {
success: '#22c55e',
warning: '#f59e0b',
error: '#ef4444',
info: '#6366f1',
},
},
effects: {
blur: 0,
shadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
},
radius: {
card: 14,
},
}
Step 2: Register with ThemeProvider
Currently, custom themes require extending the ThemeProvider. For now, we recommend customizing the existing themes via CSS variables on web or creating a fork for native apps.