Skip to main content

Overview

While Natura Design System provides ready-to-use themes for each brand, you may need to customize certain aspects for your specific use case. This guide covers the available customization options.
Important: Do not try to override theme palette or other core tokens. The Design System themes are designed to maintain consistency across all brands. Only customize when absolutely necessary.

Typography Customization

The buildTheme utility in @naturacosmeticos/natds-web supports typography customization:

Basic Typography Override

import { buildTheme } from '@naturacosmeticos/natds-web'

// Apply typography customization
const theme = buildTheme('natura', 'light', 'typography')
This option automatically applies:
  • fontFamily from the theme’s body typography
  • fontWeightRegular from the theme’s body typography

Implementation

buildTheme.ts
export type ThemeMode = 'light' | 'dark'
export type Brand = ThemeBrandName
export type CustomOption = 'typography'

export const buildTheme = (brand: Brand, mode: ThemeMode, customOption?: CustomOption) => {
  const theme = themes[brand][mode]

  if (customOption === 'typography') {
    return {
      ...theme,
      typography: {
        ...theme.typography,
        fontFamily: theme.typography.body?.regular.fontFamily,
        fontWeightRegular: theme.typography.body?.regular.fontWeight
      }
    }
  }

  return theme
}

Deep Customization

For advanced use cases, you can extend the theme object after building it:

Extending Theme Properties

import { buildTheme } from '@naturacosmeticos/natds-react'
import { Theme } from '@naturacosmeticos/natds-themes'

const baseTheme = buildTheme('natura', 'light')

const customTheme: Theme = {
  ...baseTheme,
  // Add custom properties specific to your app
  // Note: This should be used sparingly
  spacing: (factor: number) => `${8 * factor}px`
}
Extending core theme properties can break design system consistency. Use this approach only when you cannot achieve your goals through the standard API.

Theme Context in Components

Access the current theme in your components using react-jss:

Using withStyles

import React from 'react'
import { withStyles, WithStyles } from 'react-jss'
import { Theme } from '@naturacosmeticos/natds-themes'

const styles = (theme: Theme) => ({
  root: {
    backgroundColor: theme.color.primary,
    color: theme.color.onPrimary,
    padding: theme.spacing(2),
    borderRadius: theme.shape.borderRadius.medium
  },
  title: {
    ...theme.typography.h1,
    marginBottom: theme.spacing(2)
  }
})

interface Props extends WithStyles<typeof styles> {
  title: string
  children: React.ReactNode
}

const CustomCard: React.FC<Props> = ({ classes, title, children }) => (
  <div className={classes.root}>
    <h1 className={classes.title}>{title}</h1>
    {children}
  </div>
)

export default withStyles(styles)(CustomCard)

Using useTheme Hook

import React from 'react'
import { useTheme } from 'react-jss'
import { Theme } from '@naturacosmeticos/natds-themes'

const CustomComponent: React.FC = () => {
  const theme = useTheme<Theme>()

  return (
    <div
      style={{
        backgroundColor: theme.color.surface,
        color: theme.color.onSurface,
        padding: theme.spacing(3),
        borderRadius: theme.shape.borderRadius.large
      }}
    >
      <h2 style={theme.typography.h2}>
        Accessing Theme Values
      </h2>
      <p style={theme.typography.body1}>
        You can access any theme token through the useTheme hook.
      </p>
    </div>
  )
}

export default CustomComponent

Available Theme Tokens

Color Tokens

theme.color.primary
theme.color.secondary
theme.color.surface
theme.color.background
theme.color.error
theme.color.warning
theme.color.success
theme.color.info
theme.color.onPrimary
theme.color.onSecondary
theme.color.onSurface
theme.color.onBackground
// ... and many more

Typography Tokens

theme.typography.h1
theme.typography.h2
theme.typography.h3
theme.typography.h4
theme.typography.h5
theme.typography.h6
theme.typography.body1
theme.typography.body2
theme.typography.subtitle1
theme.typography.subtitle2
theme.typography.button
theme.typography.caption
theme.typography.overline

Spacing

theme.spacing(1)  // Base spacing unit
theme.spacing(2)  // 2x spacing
theme.spacing(3)  // 3x spacing
// etc.

Border Radius

theme.shape.borderRadius.none    // 0
theme.shape.borderRadius.small   // 2px
theme.shape.borderRadius.medium  // 4px
theme.shape.borderRadius.large   // 8px
theme.shape.borderRadius.circle  // 999px

Shadows

theme.shadows[0]   // No shadow
theme.shadows[1]   // Subtle shadow
theme.shadows[2]   // Small shadow
theme.shadows[4]   // Medium shadow
theme.shadows[8]   // Large shadow
theme.shadows[16]  // Extra large shadow

Icon & Avatar Sizes

theme.iconSizes.tiny
theme.iconSizes.small
theme.iconSizes.medium
theme.iconSizes.large

theme.avatarSizes.tiny
theme.avatarSizes.small
theme.avatarSizes.medium
theme.avatarSizes.large

Opacity

theme.opacity.transparent
theme.opacity.lower
theme.opacity.low
theme.opacity.medium
theme.opacity.high
theme.opacity.opaque

Responsive Design

The theme includes Material-UI breakpoints for responsive design:
import { useTheme } from 'react-jss'
import { useMediaQuery } from '@material-ui/core'
import { Theme } from '@naturacosmeticos/natds-themes'

const ResponsiveComponent: React.FC = () => {
  const theme = useTheme<Theme>()
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
  const isTablet = useMediaQuery(theme.breakpoints.between('sm', 'md'))
  const isDesktop = useMediaQuery(theme.breakpoints.up('md'))

  return (
    <div>
      {isMobile && <MobileView />}
      {isTablet && <TabletView />}
      {isDesktop && <DesktopView />}
    </div>
  )
}
Breakpoint values:
  • xs: 0px (extra-small)
  • sm: 600px (small)
  • md: 960px (medium)
  • lg: 1280px (large)
  • xl: 1920px (extra-large)

Custom Component Styling

Create custom styled components using theme tokens:
import React from 'react'
import { createUseStyles } from 'react-jss'
import { Theme } from '@naturacosmeticos/natds-themes'

const useStyles = createUseStyles((theme: Theme) => ({
  card: {
    backgroundColor: theme.color.surface,
    borderRadius: theme.shape.borderRadius.medium,
    boxShadow: theme.shadows[2],
    padding: theme.spacing(3),
    marginBottom: theme.spacing(2),
    transition: 'box-shadow 0.3s ease',
    
    '&:hover': {
      boxShadow: theme.shadows[8]
    }
  },
  
  header: {
    ...theme.typography.h3,
    color: theme.color.primary,
    marginBottom: theme.spacing(2)
  },
  
  content: {
    ...theme.typography.body1,
    color: theme.color.onSurface,
    lineHeight: 1.6
  },
  
  footer: {
    marginTop: theme.spacing(2),
    paddingTop: theme.spacing(2),
    borderTop: `1px solid ${theme.color.divider}`
  }
}))

interface CardProps {
  title: string
  content: string
  footer?: React.ReactNode
}

const StyledCard: React.FC<CardProps> = ({ title, content, footer }) => {
  const classes = useStyles()
  
  return (
    <div className={classes.card}>
      <h3 className={classes.header}>{title}</h3>
      <p className={classes.content}>{content}</p>
      {footer && <div className={classes.footer}>{footer}</div>}
    </div>
  )
}

export default StyledCard

Dynamic Theming

Switch themes dynamically based on user preferences or application state:
import React, { useState, useEffect } from 'react'
import { ThemeProvider, buildTheme } from '@naturacosmeticos/natds-react'
import type { Brand, ThemeMode } from '@naturacosmeticos/natds-react'

const App: React.FC = () => {
  const [brand, setBrand] = useState<Brand>('natura')
  const [mode, setMode] = useState<ThemeMode>('light')

  // Persist theme preferences
  useEffect(() => {
    const savedBrand = localStorage.getItem('theme-brand') as Brand
    const savedMode = localStorage.getItem('theme-mode') as ThemeMode
    
    if (savedBrand) setBrand(savedBrand)
    if (savedMode) setMode(savedMode)
  }, [])

  useEffect(() => {
    localStorage.setItem('theme-brand', brand)
    localStorage.setItem('theme-mode', mode)
  }, [brand, mode])

  // Detect system dark mode preference
  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
    const handleChange = (e: MediaQueryListEvent) => {
      setMode(e.matches ? 'dark' : 'light')
    }
    
    mediaQuery.addEventListener('change', handleChange)
    return () => mediaQuery.removeEventListener('change', handleChange)
  }, [])

  const theme = buildTheme(brand, mode)

  return (
    <ThemeProvider theme={theme} cssPrefix="app">
      <YourApp 
        onBrandChange={setBrand}
        onModeChange={setMode}
        currentBrand={brand}
        currentMode={mode}
      />
    </ThemeProvider>
  )
}

export default App

CSS Variables Approach

For better runtime performance, consider exporting theme tokens as CSS variables:
import React, { useEffect } from 'react'
import { useTheme } from 'react-jss'
import { Theme } from '@naturacosmeticos/natds-themes'

const ThemeVariablesProvider: React.FC = ({ children }) => {
  const theme = useTheme<Theme>()

  useEffect(() => {
    const root = document.documentElement
    
    // Set CSS variables
    root.style.setProperty('--color-primary', theme.color.primary)
    root.style.setProperty('--color-secondary', theme.color.secondary)
    root.style.setProperty('--color-surface', theme.color.surface)
    root.style.setProperty('--color-background', theme.color.background)
    root.style.setProperty('--spacing-unit', `${theme.spacing(1)}px`)
    root.style.setProperty('--border-radius-medium', `${theme.shape.borderRadius.medium}px`)
    // ... set other variables as needed
  }, [theme])

  return <>{children}</>
}

export default ThemeVariablesProvider
Then use in CSS:
.my-component {
  background-color: var(--color-surface);
  padding: calc(var(--spacing-unit) * 2);
  border-radius: var(--border-radius-medium);
}

Best Practices

Always use theme tokens instead of hardcoded values to maintain consistency and enable easy theme switching.
Don’t override palette colors, typography scales, or spacing systems. These are carefully designed for brand consistency.
Before creating custom styled components, check if existing Design System components can meet your needs.
If you customize themes, test your components across all brand themes and both light/dark modes.
Document any theme customizations in your codebase to help future developers understand why they exist.

See Also

Build docs developers (and LLMs) love