Skip to main content

Theming

Grauity provides a powerful theming system that supports multiple themes (light and dark) with scoped theme switching. The theming system is built on styled-components and provides CSS variables for easy customization.

Overview

The theming system consists of two main components:
  • GrauityThemeProvider: The root theme provider that wraps your entire application
  • NSThemeScope: A component for applying different themes to specific sections of your app
All Grauity components automatically adapt to the active theme using CSS variables and theme context.

GrauityThemeProvider

The GrauityThemeProvider is the foundation of Grauity’s theming system. It should wrap your root-level element (e.g., in your App component) to enable theming throughout your application.

Basic Usage

import { GrauityThemeProvider } from '@newtonschool/grauity';

function App() {
  return (
    <GrauityThemeProvider rootThemeScopeTheme="light">
      {/* Your app content */}
    </GrauityThemeProvider>
  );
}

Props

themeConfig
object
Configuration object containing theme definitions for light and dark themes.
rootThemeScopeTheme
'light' | 'dark'
default:"'light'"
The initial theme to apply at the root level.
children
React.ReactNode
Your application content.

Theme Configuration

Grauity comes with pre-defined light and dark theme configurations that you can use or customize.

Default Theme Objects

The NS_LIGHT_THEME_CONFIG constant (exported from ui/themes/lightThemeConstants.ts:3) defines all light theme values:
import { NS_LIGHT_THEME_CONFIG } from '@newtonschool/grauity';

// Theme structure
const lightTheme = {
  colors: {
    bg: {
      subtle: {
        primary: {
          default: 'var(--neutral-0, #FFF)',
          hover: 'var(--neutral-100, #F6F7F9)',
          disabled: 'var(--neutral-200, #EDEFF3)',
        },
        brand: {
          default: 'var(--brand-0, #E5F1FF)',
        },
        // ... more color tokens
      },
      emphasis: {
        brand: {
          default: 'var(--brand-500, #0673F9)',
          hover: 'var(--brand-400, #2989FF)',
          disabled: 'var(--brand-200, #94C4FF)',
        },
        // ... more emphasis colors
      }
    },
    text: { /* ... */ },
    border: { /* ... */ },
  },
  visibility: {
    lightVisible: 'block',
    darkVisible: 'none',
  },
};

Custom Theme Configuration

You can provide your own theme configuration by passing a custom themeConfig prop:
import { GrauityThemeProvider } from '@newtonschool/grauity';

const customThemeConfig = {
  light: {
    colors: {
      // Your custom light theme colors
    },
    visibility: {
      lightVisible: 'block',
      darkVisible: 'none',
    },
  },
  dark: {
    colors: {
      // Your custom dark theme colors
    },
    visibility: {
      lightVisible: 'none',
      darkVisible: 'block',
    },
  },
};

function App() {
  return (
    <GrauityThemeProvider 
      themeConfig={customThemeConfig}
      rootThemeScopeTheme="light"
    >
      {/* Your app */}
    </GrauityThemeProvider>
  );
}

Scoped Theming with NSThemeScope

The NSThemeScope component (defined in ui/elements/ThemeScope/ThemeScope.tsx:21) allows you to apply different themes to specific sections of your application without affecting the global theme.

Basic Usage

import { NSThemeScope } from '@newtonschool/grauity';

function MyComponent() {
  return (
    <div>
      <p>This is in light theme</p>
      
      <NSThemeScope applyTheme="dark">
        <p>This section is in dark theme</p>
      </NSThemeScope>
      
      <p>Back to light theme</p>
    </div>
  );
}

Props

applyTheme
'light' | 'dark'
Explicitly set the theme for this scope.
invert
boolean
Invert the current theme (light becomes dark, dark becomes light).
as
React.ElementType
default:"'div'"
The HTML element or component to render as.
className
string
Additional CSS classes to apply.
children
React.ReactNode
Content to render within the themed scope.
NSThemeScope provides a scopedTheme prop in styled-components that you can access via props.theme.scopedTheme to detect the locally scoped theme.

Legacy Theme Classes

Grauity also supports legacy theme classes that can be applied directly to the body element or any container:
  • .grauity-theme-light - Applies light theme
  • .grauity-theme-dark - Applies dark theme
These classes are automatically managed by the ThemeContext (see ui/themes/ThemeContext.tsx:106) and applied to document.body when using the NSThemeWrapper component.
import { NSThemeWrapper } from '@newtonschool/grauity';

function App() {
  return (
    <NSThemeWrapper defaultTheme="light">
      {/* Theme class automatically applied to body */}
      {/* Your app content */}
    </NSThemeWrapper>
  );
}

System Preference Detection

The NSThemeWrapper component can automatically detect and respond to the user’s system color scheme preference:
import { NSThemeWrapper } from '@newtonschool/grauity';

function App() {
  return (
    <NSThemeWrapper 
      defaultTheme="light" 
      usePreferredColorScheme={true}
    >
      {/* Theme automatically matches system preference */}
      {/* Your app content */}
    </NSThemeWrapper>
  );
}
When usePreferredColorScheme is enabled, Grauity listens to the prefers-color-scheme media query and automatically switches themes when the system preference changes.

Theme Context and Hooks

For advanced use cases, you can access theme context directly:
import { NSThemeContext } from '@newtonschool/grauity';
import { useContext } from 'react';

function MyComponent() {
  const { theme, handleToggleTheme } = useContext(NSThemeContext);
  
  return (
    <div>
      <p>Current theme: {theme.themeName}</p>
      <button onClick={() => handleToggleTheme('dark')}>
        Switch to Dark
      </button>
      <button onClick={() => handleToggleTheme('light')}>
        Switch to Light
      </button>
    </div>
  );
}
For detecting the scoped theme in child components, use the useThemeScope hook:
import { useThemeScope } from '@newtonschool/grauity';

function ThemedComponent() {
  const { theme } = useThemeScope();
  
  return (
    <div>
      <p>Current scoped theme: {theme}</p>
    </div>
  );
}

Best Practices

Use Theme Tokens

Always use CSS variables and theme tokens instead of hardcoded colors for automatic theme switching.

Scope Themes Sparingly

Use NSThemeScope only when necessary. Most apps should use a single global theme.

Test Both Themes

Always test your components in both light and dark themes to ensure proper contrast and readability.

Respect User Preference

Consider using usePreferredColorScheme to respect users’ system-level theme preferences.
When nesting multiple NSThemeScope components, be aware that each creates a new theme context. Child scopes inherit from their parent scope, not the root theme.

Build docs developers (and LLMs) love