This guide will help you migrate your application from the previous Stitches-based theming system to the new Emotion-based implementation in Abyss.
Good news! The migration effort should be minimal for most applications. We've designed the new Emotion-based implementation to be as compatible as possible with existing code. In many cases, your application will continue to work with just a few adjustments (see Breaking Changes below) after installing the new version but with the following added benefits:
Overview of changes
- server-side rendering support
- Improved style isolation for parcels with Shadow DOM support
- More consistent styling behavior across different environments and host applications
- Granular control of how styles are being processed and injected into the DOM
Breaking changes
ThemeProvider requirements
Before: CSS variables were added to the :root element whether or not createTheme or ThemeProvider was used and therefore some styles would still be applied to Abyss components.
After: In the new Emotion-based implementation ThemeProvider + createTheme is required:
- No styles will be applied to Abyss components if they're not encapsulated within a
ThemeProviderthat includes a theme provided bycreateTheme. - CSS variables are only scoped to the
ThemeProviderwrapper elements - No default theme is created if a
ThemeProvideris used without a theme
Provider props
The following props have been moved from ThemeProvider to createTheme:
brandAssetsCdnincludeBaseCss
Before:
<ThemeProvider theme={theme} brandAssetsCdn="https://example.com/assets" includeBaseCss={true}> <App /></ThemeProvider>After:
const theme = createTheme('uhc', { brandAssetsCdn: 'https://example.com/assets', includeBaseCss: false,});
<ThemeProvider theme={theme}> <App /></ThemeProvider>;globalCss
The globalCss utility from @uhg-abyss/web/tools/styled was part of the v1 Stitches API and has been deprecated. Please use Emotion's Global component from @uhg-abyss/web/ui/ThemeProvider to define global styles instead.
Before:
import { ThemeProvider } from '@uhg-abyss/web/ui/ThemeProvider';import { createTheme } from '@uhg-abyss/web/tools/theme';import { globalCss } from '@uhg-abyss/web/tools/styled';
const globalStyles = globalCss({ body: { backgroundColor: '#f0f0f0', },});
const theme = createTheme('uhc');
export function App() => { globalStyles(); return <ThemeProvider theme={theme}>Your app</ThemeProvider>}After:
import { ThemeProvider, Global } from '@uhg-abyss/web/ui/ThemeProvider';import { createTheme } from '@uhg-abyss/web/tools/theme';
const globalStyles = { body: { backgroundColor: '#f0f0f0', },};
const theme = createTheme('uhc');
const App = () => { return ( <ThemeProvider theme={theme}> <Global styles={globalStyles} /> ... </ThemeProvider> );};Styling API changes
While the underlying styling engine has changed from Stitches to Emotion, we've worked hard to preserve the styled utility API to ensure minimal migration work. Most of your existing styling configurations should continue to work without changes.
However, due to fundamental differences between the styling engines, some specific patterns may require updates. The following are the most common patterns we've identified, but given the potential variations and complexity of custom styling, this list isn't exhaustive:
Component selectors
Replace component reference selectors with class-based selectors:
- [`${StyledTrigger}[data-state=open] &`]: { ... }+ '.abyss-accordion-trigger[data-state=open] &': { ... }CSS pseudo-selectors
Some pseudo-selectors need updates for compatibility:
- '&:first-child': { ... }+ '&:first-of-type': { ... }Adjacent sibling selectors
Keep using class-based selectors for adjacent siblings:
- '& + &': { ... }+ '& + .abyss-form-input-wrapper': { ... }Content property
String values in the content property need to be properly escaped:
- content: '',+ content: "''",CSS property names
Use camelCase for CSS property names instead of kebab-case with quotes:
- 'align-items': 'flex-start',+ alignItems: 'flex-start',Migration scenarios
Basic usage
For most applications, simply update your ThemeProvider usage and ensure all components that need theme access are within a ThemeProvider:
import { ThemeProvider } from '@uhg-abyss/web/ui/ThemeProvider';import { createTheme } from '@uhg-abyss/web/tools/theme';
const theme = createTheme('uhc');
export default function App() { return ( <ThemeProvider theme={theme}> <YourApp /> </ThemeProvider> );}Next.js server-side rendering
The Emotion-based ThemeProvider has built-in support for Next.js server-side rendering (SSR). It integrates with Next.js's style extraction mechanisms to prevent style flashing during hydration and ensure consistent styling between server and client. For most Next.js applications, using ThemeProvider alone is sufficient:
// Basic Next.js SSR setupimport { ThemeProvider } from '@uhg-abyss/web/ui/ThemeProvider';import { createTheme } from '@uhg-abyss/web/tools/theme';
const theme = createTheme('uhc');
export default function App({ children }) { return <ThemeProvider theme={theme}>{children}</ThemeProvider>;}For more control over style extraction and injection, use the NextStyleProvider (works with both App Router and Pages Router):
import { NextStyleProvider } from '@uhg-abyss/web/ui/ThemeProvider/NextStyleProvider';import { ThemeProvider } from '@uhg-abyss/web/ui/ThemeProvider';import { createTheme } from '@uhg-abyss/web/tools/theme';
const theme = createTheme('uhc');
// For App Router: app/layout.js// For Pages Router: pages/_app.jsexport default function App({ children, Component, pageProps }) { const content = Component ? <Component {...pageProps} /> : children;
return ( <NextStyleProvider cacheOptions={{ key: 'my-app' }}> <ThemeProvider theme={theme}>{content}</ThemeProvider> </NextStyleProvider> );}Style isolation and Parcels
Another benefit of the new Emotion-based theming system is improved style isolation for parcels. The StyleRootProvider with Shadow DOM support ensures that styles from the host application don't leak into your parcel and vice versa.
// MyParcel.jsximport React from 'react';import { StyleRootProvider } from '@uhg-abyss/web/ui/ThemeProvider/StyleRootProvider';import { ThemeProvider } from '@uhg-abyss/web/ui/ThemeProvider';import { createTheme } from '@uhg-abyss/web/tools/theme';
const theme = createTheme('uhc');
export const MyParcel = () => ( <StyleRootProvider useShadowDom theme={theme} cacheOptions={{ key: 'my-parcel' }} > <ThemeProvider theme={theme}> {/* Your parcel content here */} </ThemeProvider> </StyleRootProvider>);Key Benefits:
- Complete style isolation from host applications
- Consistent theming regardless of embedding context
- No class name collisions with host applications
- Styles scoped only to your parcel for better performance
Advanced usage
For advanced use cases, refer to the documentation for:
- StyleRootProvider - For applications that need fine-grained control over CSS injection or Shadow DOM isolation
- NextStyleProvider - Optimized for Next.js applications with server-side rendering support