import React, { useRef } from 'react' import type { PropsWithChildren } from 'react' import type { UnityTheme } from './unity-theme-provider' import preview from '../../.storybook/preview' import { cn } from '../utils/cn' import { UnityThemeProvider, useUnityTheme } from './unity-theme-provider' const meta = preview.meta({ component: UnityThemeProvider, title: '3 - Component Reference/UnityThemeProvider', parameters: { docs: { description: { component: `\`UnityThemeProvider\` manages the active Unity theme for your application by setting the \`data-uy-theme\` attribute on a target DOM element (defaults to \`\`). All Unity CSS custom properties (\`--uy-*\`) resolve based on this attribute, so every \`uy:\` utility class updates automatically when the theme changes. Wrap your app (or a subtree) with this provider and use the \`useUnityTheme()\` hook to read or switch the active theme at runtime. You can scope a theme to a specific element by passing a React ref or CSS selector to the \`target\` prop. Supported themes: \`legacy\` (default) and \`rebrand\`.`, }, }, layout: 'padded', }, tags: ['autodocs'], }) // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function ThemedCard({ label, children, }: PropsWithChildren<{ label?: string }>) { return (

{label ?? 'Themed card'}

{children ?? (

This card uses semantic tokens. Its appearance changes when the active theme is switched between legacy and rebrand .

)}
) } function ThemeSwitcher() { const { theme, setTheme } = useUnityTheme() return (
Switch theme:
{(['legacy', 'rebrand'] as UnityTheme[]).map(t => ( ))}
) } // --------------------------------------------------------------------------- // Stories // --------------------------------------------------------------------------- /** * You can set up theming by wrapping your application with `UnityThemeProvider` and passing the desired theme via the `theme` prop. * The provider sets `data-uy-theme` on `` by default, which activates the corresponding set of `--uy-*` CSS custom properties so all `uy:` utility classes resolve to the correct palette. */ export const BasicSetup = meta.story({ name: 'Basic Setup', parameters: { docs: { source: { code: ` import { UnityThemeProvider } from '@payfit/unity-themes' function ThemedCard() { return (

Legacy theme

This card uses semantic tokens. Its appearance changes when the active theme is switched between legacy and rebrand.

) } function App() { return ( ) } `.trim(), }, }, }, render() { return (
) }, }) /** * You can switch the active theme at runtime by calling `setTheme()` from the `useUnityTheme()` hook. * The provider updates the `data-uy-theme` attribute on the target element, causing all `--uy-*` CSS custom properties to re-resolve automatically without prop drilling or page reloads. */ export const RuntimeThemeSwitching = meta.story({ name: 'Runtime Theme Switching', parameters: { docs: { source: { code: ` import { UnityThemeProvider, useUnityTheme } from '@payfit/unity-themes' import { cn } from '@payfit/unity-themes' import type { UnityTheme } from '@payfit/unity-themes' const themes: UnityTheme[] = ['legacy', 'rebrand'] function ThemeSwitcher() { const { theme, setTheme } = useUnityTheme() return (
Switch theme:
{themes.map(t => ( ))}
) } function ThemedCard() { return (

Themed card

This card uses semantic tokens. Its appearance changes when the active theme is switched between legacy and rebrand.

) } function App() { return (
) } `.trim(), }, }, }, render() { return (
) }, }) /** * You can scope a theme to a specific DOM element by passing a React ref or CSS selector string to the `target` prop. * This sets `data-uy-theme` on that element instead of ``. * * You can specify 3 possible value types: * - **`undefined` or not set:** targets `document.documentElement` (usually the `` tag) * - **A React ref:** targets the element that the ref points to (remember to use `useRef` and assign it via the `ref` attribute to the element). * - **A string with a valid CSS selector:** the provider will use `document.querySelector()` to find the element and attach the attribute. * * This will allow for different parts of the page to use different themes simultaneously. e.g: a rebrand preview panel embedded in a legacy app. * * > ⚠️ **Warning:** Nested `UnityThemeProvider`s _do not share any state between them_, which can lead to theme discrepancies, particularly when changing it at runtime. If you use scoped themes, it is your responsibility to keep them in sync (if they need to). */ export const ScopedTarget = meta.story({ name: 'Scoped Target', parameters: { docs: { source: { code: ` import { useRef } from 'react' import { UnityThemeProvider } from '@payfit/unity-themes' function ThemedCard({ label, children }: PropsWithChildren<{ label: string }>) { return (

{label}

{children}
) } function ScopedThemeDemo() { const ref = useRef(null) return (

The theme below is scoped to its own container via a ref. It does not change the global <html> attribute.

This card is inside a nested UnityThemeProvider with the rebrand theme. It will always pick up this theme despite the set theme in the root page element

This card will take whichever theme the html element has set up

) } `.trim(), }, }, }, render() { function ScopedDemo() { const ref = useRef(null) return (

The theme below is scoped to its own container via a{' '} ref. It does not change the global{' '} <html> attribute.

This card is inside a nested UnityThemeProvider with the rebrand theme. It will always pick up this theme despite the set theme in the root page element

This card will take whichever theme the html{' '} element has set up

) } return }, }) /** * The provider works by setting a `data-uy-theme` attribute on its target element. Unity's CSS defines two token blocks — `:root` for legacy and `[data-uy-theme="rebrand"]` for rebrand — so toggling the attribute instantly swaps all `--uy-*` custom properties. * Open DevTools and inspect the `` element to see the attribute change as you toggle between themes. */ export const HowItWorks = meta.story({ name: 'How It Works', parameters: { docs: { source: { code: ` import { UnityThemeProvider, useUnityTheme } from '@payfit/unity-themes' function ThemedCard() { return (

Themed card

This card uses semantic tokens. Its appearance changes when the active theme is switched between legacy and rebrand.

) } function Demo() { const { theme } = useUnityTheme() return (
Applied Code: <html data-uy-theme="{theme}">
) } function App() { return ( ) } `.trim(), }, }, }, render() { function Demo() { const { theme } = useUnityTheme() return (
Applied Code: <html data-uy-theme="{theme}">
) } return ( ) }, }) /** * You can apply styles conditionally per theme using the `theme-legacy:` and `theme-rebrand:` Tailwind custom variant prefixes. * These variants match elements inside the corresponding `data-uy-theme` context, letting you handle one-off visual differences between themes directly in utility classes without writing raw CSS. * * For example, `uy:theme-legacy:bg-red-l4 uy:theme-rebrand:bg-blue-l9` renders a red background in legacy and blue in rebrand. */ export const ThemeSpecificStyles = meta.story({ name: 'Targeting Specific Themes in Styles', parameters: { docs: { source: { code: ` import { UnityThemeProvider } from '@payfit/unity-themes' function Demo() { return (
theme-legacy:bg-yellow-l6
theme-rebrand:bg-orange-l7
theme-legacy:border-blue-l7
theme-rebrand:border-plum-l7

Toggle the theme to see different colors applied via theme-legacy: and theme-rebrand: variant prefixes.

) } function App() { return ( ) } `.trim(), }, }, }, render() { function Demo() { return (
theme-legacy:bg-yellow-l6
theme-rebrand:bg-orange-l7
theme-legacy:border-blue-l7
theme-rebrand:border-plum-l7

Toggle the theme to see different colors applied via{' '} theme-legacy: and theme-rebrand: variant prefixes.

) } return ( ) }, })