Package detail

@noema/motif

noemaia18MIT0.3.0

A type-safe styling library for React components built on top of Vanilla Extract. @noema/motif allows you to create styled components with consistent theme-aware props that cleanly separate styling from component logic.

vanilla-extract, styled-system, css, system-props

readme

@noema/motif

A type-safe styling library for React components built on top of Vanilla Extract. @noema/motif allows you to create styled components with consistent theme-aware props that cleanly separate styling from component logic.

Installation

@vanilla-extract/css and @vanilla-extract/sprinkles will also need to get installed.

npm install @noema/motif @vanilla-extract/css @vanilla-extract/sprinkles

Basic Usage

1. Define your styles (in a .css.ts file)

// styles.css.ts
import { createSprinkles, defineProperties } from '@vanilla-extract/sprinkles';
import { motifStyle } from '@noema/motif';

// Define your style properties
export const styles = motifStyle(
    [
        'width',
        'height',
        'fontSize',
        // Add more CSS properties as needed
    ],
    {
        shorthands: {
            // Define shorthands if needed
            size: ['width', 'height'],
        },
    },
);

// Define your sprinkles (theme-based properties)
const colorProperties = defineProperties({
    properties: {
        color: {
            primary: 'blue',
            secondary: 'purple',
            // Add more theme colors
        },
        backgroundColor: {
            primary: 'white',
            secondary: 'lightgray',
            // Add more theme colors
        },
    },
});

export const sprinkles = createSprinkles(colorProperties);

2. Create your components

// components.tsx
import { createMotif, motif } from '@noema/motif';
import { sprinkles, styles } from './styles.css';

// Creae a single component
const BoxComp = motif('div', sprinkles, styles);

// Or multiple components
const elements = ['div', 'button', 'span', 'h1'] as const;

// Create your styled components
export const Styled = createMotif(elements, sprinkles, styles);

// Export individual components for convenience
export const { div: Box, button: Button } = Styled;

3. Use your components

import { Box, Button } from './components';

function MyComponent() {
    return (
        <Box width={200} height={100} color="primary">
            <Button fontSize={16} backgroundColor="secondary">
                Click me
            </Button>
        </Box>
    );
}

API Reference

motifStyle(properties, options)

Creates a style configuration for use with motif or createMotif.

Parameters:

  • properties: Array of CSS property names to be controlled via props
  • options: Configuration options
    • prefix: Prefix for CSS variable names (default: 'motif')
    • base: Base styles to apply to all components
    • shorthands: Record of shorthand property names mapping to CSS properties

Returns: Style configuration object

createMotif(elements, sprinkles, config)

Creates a collection of styled components.

Parameters:

  • elements: Array of HTML element types to create components for
  • sprinkles: Vanilla Extract sprinkles function
  • config: Style configuration created with motifStyle

Returns: Object with styled components for each element

motif(element, sprinkles, config)

Core function that creates a single styled component.

Parameters:

  • element: HTML element type
  • sprinkles: Vanilla Extract sprinkles function
  • config: Style configuration created with motifStyle

Returns: Styled React component

How It Works

Motif combines two styling approaches to create a powerful and flexible styling system:

Core Concepts

  1. motifStyle - Defines which CSS properties can be used as component props

    • Creates CSS variables for each property
    • Generates CSS classes that apply these variables
    • Allows defining shorthand props (like p for all paddings)
  2. Runtime Styling Flow

    • When you pass props to a motif component (e.g., width={200}):
      • The value is converted to a CSS variable value
      • The corresponding CSS class is applied to the component
      • The CSS variable is set as an inline style

This hybrid approach enables:

  • Type safety through TypeScript
  • Performance of atomic CSS
  • Flexibility of runtime values via CSS variables
  • Theme consistency via Vanilla Extract's design system

Example

// primitives.css.ts
import { createSprinkles, defineProperties } from '@vanilla-extract/sprinkles';
import { motifStyle } from './motif-style.js';

export const styles = motifStyle(
    [
        'width',
        'height',
        'minHeight',
        'minWidth',
        'maxWidth',
        'maxHeight',
        'gridTemplateColumns',
        'gridTemplateRows',
        'gridTemplateAreas',
        'gridColumn',
        'gridRow',
        'gridArea',
        'top',
        'bottom',
        'left',
        'right',
        'zIndex',
        'fontSize',
    ],
    { shorthands: {} },
);

// Structural properties for layout
const structureProperties = defineProperties({
    properties: {
        display: ['none', 'flex', 'block', 'inline', 'grid'],
        flexDirection: ['row', 'column'],
        // ... more properties
    },
});

// Box style with padding shorthands
export const boxStyle = motifStyle(
    ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
    {
        shorthands: {
            py: ['paddingTop', 'paddingBottom'],
            px: ['paddingLeft', 'paddingRight'],
            p: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
        },
    },
);

export const sprinkles = createSprinkles(structureProperties);
// primitives.tsx
import { MotifComponents, createMotif } from './create-motif.js';
import { sprinkles, styles } from './primitives.css.js';

const NODES = [
    'button',
    'div',
    'h1',
    'h2',
    'h3',
    'span',
    'table',
    'thead',
    'tbody',
    'th',
    'tr',
    'td',
] as const;

// Create primitive components
export const Primitive = createMotif(NODES, sprinkles, styles);
export type PrimitiveProps = MotifComponents<typeof Primitive>;

// Export common components
export const { button: Button, div: Box } = Primitive;

Additional Utilities

The library also includes additional utilities:

  • createContext - A utility for creating React context with improved type safety and error messages
  • composeRender - A utility for composing render props with improved type safety

License

MIT