npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@equinor/fusion-react-styles

v2.0.0

Published

style lib inspired by @material-ui/styles

Readme

@equinor/fusion-react-styles

npm version

A React styling library inspired by Material-UI's makeStyles API, built with JSS and designed for React 19 compatibility. This package provides a flexible, type-safe styling solution with support for theme-based styling, dynamic props, and scoped style isolation.

Features

  • React 19 Compatible - Built from the ground up for React 19
  • Type-Safe - Full TypeScript support with intelligent type inference
  • Theme Integration - Seamless integration with Fusion design system
  • Scoped Styles - Prevent CSS class name collisions with StylesProvider seeds
  • Dynamic Styles - Support for prop-based and theme-based styling
  • Performance - Cached stylesheets and optimized JSS instance
  • Nested Selectors - Full support for pseudo-selectors and nested rules

Installation

npm install @equinor/fusion-react-styles
# or
pnpm add @equinor/fusion-react-styles
# or
yarn add @equinor/fusion-react-styles

Peer Dependencies

  • React ^18 or ^19

Quick Start

import { makeStyles, ThemeProvider, theme } from '@equinor/fusion-react-styles';

const useStyles = makeStyles({
  root: {
    color: 'blue',
    padding: '16px',
  },
  title: {
    fontSize: '24px',
    fontWeight: 'bold',
  },
}, { name: 'MyComponent' });

function MyComponent() {
  const classes = useStyles();
  
  return (
    <div className={classes.root}>
      <h1 className={classes.title}>Hello World</h1>
    </div>
  );
}

function App() {
  return (
    <ThemeProvider theme={theme}>
      <MyComponent />
    </ThemeProvider>
  );
}

Core Concepts

1. Creating Styles

Use makeStyles to create a styles hook that generates CSS class names. Always provide a name option for better performance and debugging:

import { makeStyles } from '@equinor/fusion-react-styles';

const useStyles = makeStyles({
  root: {
    color: 'red',
    padding: '16px',
  },
  button: {
    backgroundColor: 'blue',
    '&:hover': {
      backgroundColor: 'darkblue',
    },
  },
}, { name: 'MyComponent' });

2. Using Styles in Components

Call the hook inside your component to get class names:

function Component() {
  const classes = useStyles();
  
  return (
    <div className={classes.root}>
      <button className={classes.button}>Click me</button>
    </div>
  );
}

3. Dynamic Styles with Props

Styles can be functions that receive props. Pass props when calling the hook:

interface ComponentProps {
  color: string;
  size: 'small' | 'large';
}

const useStyles = makeStyles({
  root: (props: ComponentProps) => ({
    color: props.color,
    padding: props.size === 'large' ? '24px' : '12px',
  }),
}, { name: 'DynamicComponent' });

function Component(props: ComponentProps) {
  const classes = useStyles(props);
  return <div className={classes.root}>Dynamic styling</div>;
}

4. Theme-Based Styles

Access theme values using theme functions. Theme properties are StyleProperty instances that require using the getVariable() method to extract CSS values:

import { makeStyles, createStyles, theme } from '@equinor/fusion-react-styles';

const useStyles = makeStyles((themeValue) =>
  createStyles({
    root: {
      color: themeValue.colors.text.static_icons__default.getVariable('color'),
      padding: themeValue.spacing.comfortable.medium.getVariable('padding'),
      backgroundColor: themeValue.colors.ui.background__default.getVariable('color'),
    },
    button: {
      backgroundColor: themeValue.colors.ui.background__default.getVariable('color'),
      '&:hover': {
        backgroundColor: themeValue.colors.ui.background__hover.getVariable('color'),
      },
    },
  }),
  { name: 'ThemedComponent' }
);

Important: Theme properties (colors, spacing, etc.) are StyleProperty instances:

  • Colors: Use theme.colors.*.getVariable('color') to get the color value as a CSS variable string
  • Spacing: Use theme.spacing.*.getVariable('padding') to get the spacing value as a CSS variable string
  • Typography: Use theme.typography.*.style.* to access typography properties (e.g., theme.typography.heading.h4.style.fontSize)

Example:

const useStyles = makeStyles((themeValue) =>
  createStyles({
    container: {
      // Color property - use getVariable('color')
      backgroundColor: themeValue.colors.ui.background__default.getVariable('color'),
      color: themeValue.colors.text.static_icons__default.getVariable('color'),
      
      // Spacing property - use getVariable('padding')
      padding: themeValue.spacing.comfortable.medium.getVariable('padding'),
      marginTop: themeValue.spacing.comfortable.small.getVariable('padding'),
      
      // Typography - access style properties directly
      fontSize: themeValue.typography.paragraph.body_short.style.fontSize,
      fontWeight: themeValue.typography.heading.h4.style.fontWeight,
    },
  }),
  { name: 'Container' }
);

API Reference

makeStyles(stylesOrCreator, options?)

Creates a hook that generates CSS class names from style definitions.

Parameters:

  • stylesOrCreator: Style rules object or a function (theme) => StyleRules
  • options.name: Required name prefix for debugging and performance. Without it, all instances share the same cache key, causing performance issues.

Returns: A hook function that returns Record<ClassKey, string> where ClassKey is inferred from your style rules, providing type-safe access to class names.

Examples:

// Static styles
const useStyles = makeStyles({
  root: { color: 'red' },
}, { name: 'MyComponent' });

// Theme-based styles
const useStyles = makeStyles((theme) => ({
  root: { 
    color: theme.colors.text.static_icons__default.getVariable('color'),
  },
}), { name: 'ThemedComponent' });

// Dynamic styles with props
const useStyles = makeStyles({
  root: (props: { color: string }) => ({
    color: props.color,
  }),
}, { name: 'DynamicComponent' });

createStyles(styles?)

Type-safe helper for creating style rules. Improves TypeScript inference for class keys, enabling type-safe access to class names.

import { createStyles, makeStyles } from '@equinor/fusion-react-styles';

const useStyles = makeStyles((theme) =>
  createStyles({
    root: { 
      color: theme.colors.text.static_icons__default.getVariable('color'),
    },
    button: { 
      padding: theme.spacing.comfortable.medium.getVariable('padding'),
    },
  }),
  { name: 'Component' }
);

function Component() {
  const classes = useStyles();
  // TypeScript will error if you try to access classes.foo - it knows the exact keys!
  return <div className={classes.root}>...</div>;
}

Benefits:

  • Type-safe class key access - TypeScript knows exactly which class keys exist
  • Better autocompletion in IDEs
  • Catches typos at compile time

ThemeProvider

Provides theme context to child components. Supports nested themes and theme composition.

Props:

  • theme: Theme object or function (outerTheme) => theme
  • children: React nodes

Example:

import { ThemeProvider, theme } from '@equinor/fusion-react-styles';

function App() {
  return (
    <ThemeProvider theme={theme}>
      <YourApp />
    </ThemeProvider>
  );
}

// Nested theme composition
function App() {
  return (
    <ThemeProvider theme={baseTheme}>
      <ThemeProvider theme={(outer) => ({ ...outer, custom: true })}>
        <YourApp />
      </ThemeProvider>
    </ThemeProvider>
  );
}

useTheme<T>()

Hook to access the current theme from ThemeProvider context.

import { useTheme } from '@equinor/fusion-react-styles';

function Component() {
  const theme = useTheme<FusionTheme>();
  
  if (!theme) {
    return <div>No theme available</div>;
  }
  
  return (
    <div style={{ 
      color: theme.colors.text.static_icons__default.getVariable('color'),
    }}>
      Hello
    </div>
  );
}

createTheme(customTheme?, baseTheme?)

Creates a new theme by merging custom properties with a base theme. Supports deep merging for nested properties like colors, typography, etc.

import { createTheme, theme } from '@equinor/fusion-react-styles';
import type { FusionTheme } from '@equinor/fusion-react-styles';

interface ExtendedTheme extends FusionTheme {
  app: {
    borderRadius: string;
    sidebarWidth: string;
  };
}

const extendedTheme = createTheme<ExtendedTheme>({
  app: {
    borderRadius: '12px',
    sidebarWidth: '300px',
  },
}, theme);

// Use in ThemeProvider
<ThemeProvider theme={extendedTheme}>
  <App />
</ThemeProvider>

StylesProvider

Creates an isolated styling scope to prevent CSS class name collisions. Critical for micro-frontends and dynamically loaded modules.

Props:

  • seed: String prefix for class names (e.g., "my-module")
  • generateClassName: Optional custom class name generator
  • jss: Optional custom JSS instance
  • children: React nodes

Example:

import { StylesProvider } from '@equinor/fusion-react-styles';

// Main app
<StylesProvider seed="main-app">
  <App />
</StylesProvider>

// Dynamically loaded module
<StylesProvider seed="dynamic-module">
  <DynamicModule />
</StylesProvider>

createGenerateClassName(seed?)

Creates a class name generator function with an optional seed prefix for isolation.

import { createGenerateClassName, StylesProvider } from '@equinor/fusion-react-styles';

const customGenerator = createGenerateClassName('my-app');

function App() {
  return (
    <StylesProvider generateClassName={customGenerator}>
      <YourApp />
    </StylesProvider>
  );
}

clsx

Utility for combining class names. Re-exported from the clsx package.

import { clsx } from '@equinor/fusion-react-styles';

const className = clsx(classes.root, isActive && classes.active, 'custom-class');

Advanced Usage

Scoped Styles for Module Isolation

When building micro-frontends or dynamically loaded modules, use StylesProvider with unique seeds to prevent CSS collisions:

import { StylesProvider, makeStyles, ThemeProvider, theme } from '@equinor/fusion-react-styles';

// Shared styles
const sharedStyles = {
  root: { color: 'blue' },
};

// Module A
const useStylesA = makeStyles(sharedStyles, { name: 'ModuleA' });

function ModuleA() {
  const classes = useStylesA();
  return <div className={classes.root}>Module A</div>;
}

// Module B
const useStylesB = makeStyles(sharedStyles, { name: 'ModuleB' });

function ModuleB() {
  const classes = useStylesB();
  return <div className={classes.root}>Module B</div>;
}

function App() {
  return (
    <ThemeProvider theme={theme}>
      {/* Each module gets its own scope */}
      <StylesProvider seed="module-a">
        <ModuleA />
      </StylesProvider>
      
      <StylesProvider seed="module-b">
        <ModuleB />
      </StylesProvider>
    </ThemeProvider>
  );
}

Even though both modules use the same styles object, they'll generate different class names:

  • Module A: module-a-makeStyles-root-1
  • Module B: module-b-makeStyles-root-1

Nested Selectors

Full support for CSS-in-JS nested selectors:

import { clsx } from '@equinor/fusion-react-styles';

const useStyles = makeStyles({
  root: {
    color: 'blue',
    '&:hover': {
      color: 'red',
    },
    '& .child': {
      padding: '10px',
    },
    '&$disabled': {
      opacity: 0.5,
    },
  },
  disabled: {},
}, { name: 'NestedComponent' });

function Component() {
  const classes = useStyles();
  const [disabled, setDisabled] = useState(false);
  
  return (
    <div className={clsx(classes.root, disabled && classes.disabled)}>
      <div className="child">Child element</div>
    </div>
  );
}

Type-Safe Props with Theme

Use TypeScript to ensure type safety with dynamic styles and theme:

import { makeStyles, createStyles, theme } from '@equinor/fusion-react-styles';

interface StyleProps {
  color: string;
}

const useStyles = makeStyles((themeValue) =>
  createStyles({
    root: (props: StyleProps) => ({
      ...themeValue.typography.paragraph.body_short.style,
      color: props.color,
      padding: themeValue.spacing.comfortable.medium.getVariable('padding'),
      backgroundColor: themeValue.colors.ui.background__default.getVariable('color'),
    }),
  }),
  { name: 'MyComponent' }
);

function Component(props: StyleProps) {
  const classes = useStyles(props);
  return <div className={classes.root}>Styled component</div>;
}

Extended Themes

Extend the Fusion theme with custom properties:

import { createTheme, theme, makeStyles } from '@equinor/fusion-react-styles';
import type { FusionTheme, StyleDefinition } from '@equinor/fusion-react-styles';

interface AppTheme extends StyleDefinition {
  app: {
    borderRadius: string;
    sidebarWidth: string;
  };
}

const extendedTheme = createTheme<AppTheme>({
  app: {
    borderRadius: '12px',
    sidebarWidth: '300px',
  },
});

// Use extended theme with makeStyles
const useStyles = makeStyles((themeValue: typeof extendedTheme) =>
  createStyles({
    root: {
      borderRadius: themeValue.app.borderRadius,
      padding: themeValue.spacing.comfortable.medium.getVariable('padding'),
    },
  }),
  { name: 'ExtendedThemeComponent' }
);

TypeScript Support

This package is written in TypeScript and provides full type definitions. All APIs are fully typed with intelligent inference:

Automatic Type Inference

Type inference works automatically, and using createStyles provides even better type safety:

// Basic type inference - works, but ClassKey is inferred as string
const useStyles = makeStyles({
  root: { color: 'red' },
  button: { padding: '10px' },
}, { name: 'Component' });
const classes = useStyles(); // classes: Record<string, string>

// With createStyles - ClassKey is inferred as literal union type
const useStyles = makeStyles((theme) =>
  createStyles({
    root: { 
      color: theme.colors.text.static_icons__default.getVariable('color'),
    },
    button: { 
      padding: theme.spacing.comfortable.medium.getVariable('padding'),
    },
  }),
  { name: 'Component' }
);
const classes = useStyles(); // classes: Record<'root' | 'button', string>
// TypeScript will error if you try to access classes.foo - it knows the exact keys!

Theme Types

The FusionTheme type can be extended with custom properties:

import type { FusionTheme, StyleDefinition } from '@equinor/fusion-react-styles';

interface MyAppTheme extends StyleDefinition {
  customProperty: string;
}

const useStyles = makeStyles((theme: FusionTheme<MyAppTheme>) => ({
  root: {
    color: theme.colors.text.static_icons__default.getVariable('color'),
    custom: theme.customProperty, // Fully typed!
  },
}), { name: 'Component' });

Type-Safe Class Access

When using createStyles, the return type is Record<ClassKey, string> where ClassKey is a literal union of your style keys. This provides:

  • Compile-time safety: TypeScript errors when accessing undefined class keys
  • Better autocompletion: IDEs show exactly which class names are available
  • Refactoring support: Rename class keys and TypeScript will catch all usages

Migration from Material-UI v4

This package provides a similar API to @material-ui/styles v4 but is built for React 19 compatibility. The main differences:

  1. No Material-UI dependency - Uses JSS directly
  2. React 19 compatible - No compatibility issues
  3. Improved TypeScript types - Better type inference
  4. Scoped styles - Enhanced StylesProvider with seed-based isolation
  5. Required name option - Always provide a name in makeStyles options for performance

Most code should work with minimal changes:

// Before (Material-UI v4)
import { makeStyles } from '@material-ui/styles';

// After (this package)
import { makeStyles } from '@equinor/fusion-react-styles';
// Add name option to makeStyles
const useStyles = makeStyles({ ... }, { name: 'ComponentName' });

React 19 Compatibility

This package is fully compatible with React 19 and has been tested extensively. The implementation uses React 19's new APIs and avoids deprecated patterns.

Performance Tips

  1. Always provide a name option - Without it, all instances share the same cache key, causing unnecessary re-renders and performance issues.

  2. Use createStyles for better type safety - While optional, it improves TypeScript inference and provides better autocompletion.

  3. Memoize style objects when possible - If creating styles inside components, consider moving them outside or memoizing them.

  4. Leverage style caching - The package automatically caches stylesheets based on styles, theme, and name, so identical style definitions are reused.

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

ISC

Related Packages