# How Clash Nyanpasu Implements Material You Theming and Dynamic Color Systems

> Discover how Clash Nyanpasu implements Material You theming and dynamic color systems. Learn how it generates themes from seed colors and applies them throughout the app.

- Repository: [Nyanpasu/clash-nyanpasu](https://github.com/libnyanpasu/clash-nyanpasu)
- Tags: deep-dive
- Published: 2026-03-06

---

**Clash Nyanpasu generates a complete Material Design 3 theme from a user-defined seed color using Google's Material Color Utilities, then distributes the dynamic palette throughout the application via React context and CSS custom properties.**

Clash Nyanpasu is a modern GUI proxy client built with React and Tauri that leverages Google's Material You (Material Design 3) specification for its theming system. The implementation centers on dynamic color generation, where a single seed color propagates into a full tonal palette that adapts to both user preferences and operating system theme changes.

## Core Architecture of Material You Theming

The theming system is orchestrated through a custom React hook that bridges user settings, Material Design algorithms, and the component tree.

### The use-custom-theme.tsx Hook

Located at [`frontend/nyanpasu/src/components/layout/use-custom-theme.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/nyanpasu/src/components/layout/use-custom-theme.tsx), this hook serves as the central nervous system for theme generation. It performs five critical functions:

1. **Reads the user-defined seed color** from the `theme_color` setting, defaulting to `#1867c0` if undefined
2. **Invokes the Material You generator** via `createMDYTheme` from the workspace package `@nyanpasu/ui`
3. **Provides the theme** to the React tree through MUI's `<ThemeProvider>`
4. **Synchronizes OS-level color schemes** using Tauri's `appWindow.theme()` API and the `useColorScheme` hook
5. **Updates CSS custom properties** on the `<html>` element (e.g., `--background-color`, `--primary-main`) for non-MUI components

The hook uses `useMemo` to recompute the theme only when the seed color or color mode changes, ensuring efficient updates without full page reloads.

### Theme Generation with createMDYTheme

The `createMDYTheme` function (implemented in the `@nyanpasu/ui` workspace package) acts as the bridge between Google's color science and MUI's theming system:

```typescript
// Conceptual implementation based on source analysis
import { CorePalette, argbFromHex } from '@material/material-color-utilities';

const createMDYTheme = (seedColor: string, fontFamily: string) => {
  // Convert hex to ARGB and generate tonal palettes
  const seedArgb = argbFromHex(seedColor);
  const corePalette = CorePalette.of(seedArgb);
  
  // Map Material You tonal palettes to MUI palette structure
  return createMuiTheme({
    palette: {
      primary: generateTonalPalette(corePalette.a1),
      secondary: generateTonalPalette(corePalette.a2),
      tertiary: generateTonalPalette(corePalette.a3),
      error: generateTonalPalette(corePalette.error),
    },
    typography: { fontFamily }
  });
};

```

This function relies on `@material/material-color-utilities` (declared in [`frontend/ui/package.json`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/ui/package.json)) to execute the Material You algorithm, which extracts hue, chroma, and tone values to generate harmonious color families.

## Dynamic Color System Implementation

### Seed Color and Tonal Palettes

The dynamic color system originates from a single **seed color** stored in the application's settings. When the user selects a new accent color:

1. The setting updates via the `useSetting('theme_color')` hook
2. The new hex value flows into `createMDYTheme`
3. `@material/material-color-utilities` calculates tonal palettes for primary, secondary, tertiary, and error containers
4. These palettes map to MUI's `palette.primary`, `palette.secondary`, etc.

Each tonal palette contains 13 tones (from 0% to 100% luminance), ensuring accessible contrast ratios across light and dark modes.

### CSS Custom Properties Synchronization

To support components outside MUI's theme context (such as Tailwind-styled elements), the system synchronizes CSS custom properties:

```typescript
// From use-custom-theme.tsx
const applyRootStyleVar = (mode: 'light' | 'dark', theme: Theme) => {
  const root = document.documentElement;
  root.style.setProperty('--background-color', theme.palette.background.default);
  root.style.setProperty('--primary-main', theme.palette.primary.main);
  root.style.setProperty('--primary-contrast', theme.palette.primary.contrastText);
  // ... additional mappings
};

```

This dual-delivery system (React context + CSS variables) ensures consistent theming across the entire application stack.

## OS-Level Theme Synchronization

### Tauri Integration for System Theme Detection

The application respects the operating system's color scheme preference through Tauri's window API:

```typescript
// Located in scheme-provider.tsx and use-custom-theme.tsx
import { appWindow } from '@tauri-apps/api/webviewWindow';

// Initialize theme from OS
const systemTheme = await appWindow.theme(); // returns 'light' | 'dark'

// Listen for changes
const unlisten = await appWindow.onThemeChanged((event) => {
  setMode(event.payload); // Update Jotai atom and MUI scheme
});

```

This integration, combined with the [`scheme-provider.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/scheme-provider.tsx) component, ensures the application transitions seamlessly when the user toggles system dark mode.

## Practical Implementation Examples

### Accessing Theme Values in Components

```tsx
import { Card, CardContent, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';

export const InfoCard = () => {
  const theme = useTheme();               // <- receives Material You colours
  return (
    <Card sx={{ backgroundColor: theme.palette.primary.main }}>
      <CardContent>
        <Typography color="primary.contrastText">
          Material You themed card
        </Typography>
      </CardContent>
    </Card>
  );
};

```

### Updating the Seed Color Programmatically

```tsx
import { useSetting } from '@nyanpasu/interface';
import { Slider } from '@mui/material';
import { useState } from 'react';

export const ThemeColorPicker = () => {
  const { value: current, setValue } = useSetting('theme_color');
  const [hex, setHex] = useState(current ?? '#1867c0');

  const handleChange = (_, newHex: string) => {
    setHex(newHex);
    setValue(newHex);                     // triggers re‑generation of the MDY theme
  };

  return <Slider value={hex} onChange={handleChange} />;
};

```

### Synchronizing with System Theme Changes

```tsx
import { useEffect } from 'react';
import { appWindow } from '@tauri-apps/api/webviewWindow';
import { useSetAtom } from 'jotai';
import { themeMode as themeModeAtom } from '@/store';

export const SyncSystemTheme = () => {
  const setMode = useSetAtom(themeModeAtom);

  useEffect(() => {
    appWindow.theme().then(m => m && setMode(m));      // initialise
    const unlisten = appWindow.onThemeChanged(e => setMode(e.payload));
    return () => unlisten.then(fn => fn());
  }, []);
  return null;
};

```

## Summary

- **Central Hook**: The [`use-custom-theme.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/use-custom-theme.tsx) hook in `frontend/nyanpasu/src/components/layout/` orchestrates the entire theming system by reading user settings and generating MUI themes.
- **Material You Integration**: The `@nyanpasu/ui` workspace package implements `createMDYTheme`, which leverages `@material/material-color-utilities` to generate tonal palettes from a single seed color.
- **Dynamic Updates**: The system uses `useMemo` to recompute themes when the `theme_color` setting changes, applying updates via React context and CSS custom properties without page reloads.
- **OS Synchronization**: Tauri's `appWindow.theme()` API and `onThemeChanged` event listener ensure the application respects and reacts to system-level light/dark mode changes.
- **Dual Delivery**: Themes are distributed through both MUI's `<ThemeProvider>` and synchronized CSS variables (`--primary-main`, etc.), ensuring consistent styling across MUI components and Tailwind-styled elements.

## Frequently Asked Questions

### How does Clash Nyanpasu generate a full color palette from a single hex code?

Clash Nyanpasu uses Google's `@material/material-color-utilities` library to execute the Material You algorithm. When a user selects a seed color (e.g., `#1867c0`), the `createMDYTheme` function converts this to ARGB format and passes it to `CorePalette.of()`, which mathematically derives hue, chroma, and tone values to generate 13-tone palettes for primary, secondary, tertiary, and error colors.

### What happens when the user changes the theme color while the app is running?

The change triggers an immediate update through the [`use-custom-theme.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/use-custom-theme.tsx) hook. Because the theme generation is wrapped in `useMemo` with dependencies on `theme_color` and the current color mode, React recomputes the MUI theme object instantly. The hook then updates the `<ThemeProvider>` context and synchronizes CSS custom properties on the `<html>` element, causing the entire UI to repaint with the new palette without requiring a page reload.

### How does the application handle system dark mode or light mode changes?

The application listens for OS-level theme changes through Tauri's `appWindow.onThemeChanged` event listener, implemented in [`scheme-provider.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/scheme-provider.tsx) and the custom theme hook. When the system theme changes, the event payload updates a Jotai atom (`themeModeAtom`) and MUI's `useColorScheme` hook, which triggers the theme generator to switch between light and dark palette variants while maintaining the same seed color and tonal relationships.

### Can non-MUI components access the Material You color values?

Yes. While MUI components consume the theme through React context, the system exposes the same color values as CSS custom properties on the root `<html>` element. The `applyRootStyleVar` function in [`use-custom-theme.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/use-custom-theme.tsx) synchronizes variables such as `--primary-main`, `--background-color`, and `--primary-contrast` whenever the theme updates. This allows Tailwind CSS and custom styled components to reference these values using `var(--primary-main)` and remain visually consistent with the Material You palette.