Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Pigment CSS v1: Package Structure and API Design #362

Open
brijeshb42 opened this issue Jan 6, 2025 · 1 comment
Open

[RFC] Pigment CSS v1: Package Structure and API Design #362

brijeshb42 opened this issue Jan 6, 2025 · 1 comment
Assignees
Labels
RFC Request For Comments status: waiting for maintainer These issues haven't been looked at yet by a maintainer

Comments

@brijeshb42
Copy link
Contributor

brijeshb42 commented Jan 6, 2025

What's the problem?

  1. The current Pigment CSS implementation is tightly coupled with Material-UI specific paradigms:

    • Contains styleOverrides and components handling for Material UI's theme
    • Includes slot functionality that's not core to CSS-in-JS
    • Theme handling is focused on Material-UI patterns rather than generic CSS tokens
    • When it was implemented, it was highly focussed on its usage with Material UI v6.
  2. The current architecture makes it difficult to:

    • Use Pigment CSS independently without linking it to Material UI's concepts
    • The variant support is not the based since it's the equivalent of compound variants in Stitches and we also don't support single variants.
    • Maintain clean separation between core CSS-in-JS functionality and UI framework specifics

What are the requirements?

  1. Core Functionality:

    • Framework-agnostic CSS-in-JS implementation
    • Build-time CSS extraction and optimization
    • Support for CSS token management and theming
    • Clear separation between core and framework-specific features
  2. Developer Experience:

    • Intuitive API design combining best aspects of Emotion and Stitches
    • Type-safe styling and theming
    • Easy integration with popular build tools
    • Support for variant-based styling
  3. Performance:

    • Efficient runtime performance through build-time optimization
    • Minimal runtime overhead for theme customization
    • Proper CSS layering for style precedence

What are our options?

Option 1: Enhance Current Architecture

  • Pros:
    • Minimal migration needed
    • Maintains compatibility with existing code
  • Cons:
    • Perpetuates tight coupling with Material-UI
    • Limits adoption by non-Material-UI users
    • Complicates maintenance

Option 2: Complete Rewrite

  • Pros:
    • Clean slate for better architecture
    • No legacy constraints
  • Cons:
    • High migration cost
    • Potential breaking changes
    • Longer development time

Option 3: Modular Refactor (Proposed)

  • Pros:
    • Clean separation of concerns
    • Maintains compatibility through adapter packages
    • Enables broader adoption
  • Cons:
    • Moderate migration effort
    • Additional package maintenance

Proposed solution

1. Package Structure

@pigment-css/
├── core/          # Framework-agnostic CSS-in-JS
├── theme/         # Theme token management
├── react/         # React-specific primitives
├── processors/    # Build-time processors
└── bundler-plugins/
    ├── vite/
    ├── webpack/
    └── nextjs/

2. Core APIs

Theme Management

// Theme definition
interface Theme {
  palette: {
    primary: { main: string },
    secondary: { main: string }
  },
  spacing: Record<string, number>
}

// Runtime theme customization
const themeStyles = createTheme({
  palette: {
    primary: { main: 'blue' }
  }
});

Styling

// Base styling with variants
const button = css({
  // goes into pigment.base layer
  padding: "6px 16px",
  variants: {
    // goes into pigment.variants layer
    size: {
      small: { padding: "4px 8px" },
      large: { padding: "8px 24px" }
    }
  },
  // goes into pigment.compoundvariants layer
  compoundVariants: [{
    size: 'small',
    color: 'primary',
    css: {
      padding: 4
    }
  }],
  defaultVariants: {
    size: "medium"
  }
});

// React components
const Button = styled("button")({
  minWidth: 64,
  variants: {
    variant: {
      contained: { /* styles */ },
      outlined: { /* styles */ }
    }
  }
});

Digressing for the currently released version

  1. theme -> In current version or the one supporting Material UI, the theme object, besides the tokens itself contains a lot of configuration, like sxConfig, utils like theme.breakpoints.up/down, component styling like theme.components, component's default props through theme.props.
    In v1, theme will purely be a nested collection of tokens and will mostly involve primitive values like string or number. You can still add methods to the theme but they won't be part of the final runtime. You can only use those methods in your css definition (same as theme.breakpoints.up). They won't carry any extra meaning for any of the components.
  2. Generated css (through css or styled calls will be part of css layers that'll decide the precedence so that it is easier to override base styles of derived styled components. This does not happen in the current release.
  3. Better variants supports through Stitches like API instead of just allowing variants array (similar to compound variants).

3. Bundler Integration

Mostly same as the current configuration with more options to customize the output css or class names, like

const pigmentConfig = {
  theme: tokenObject,
  sxConfig: {
    // to use `size` as css({size: 4}) outputs .classname {width: 4px; height: 4px;}
    size(value) {
      return {
        width: value,
        height: value,
      }
    },
    // to use `mobile` as css({mobile: { padding: 4 }}) outputs
    // @media only screen and (max-device-width: 480px) {.classname { padding: 4px } }
    mobile(cssObj) {
      return {
        '@media only screen and (max-device-width: 480px)': {
           // css for mobile devices
        },
      };
    },
  }

This will also be through typescript to get suggestions in the code.

4. CSS Output Structure

@layer pigment.tokens {
  /* Theme tokens as CSS variables */
}
@layer pigment.base {
  /* Component base styles */
}
@layer pigment.variants {
  /* Variant styles */
}
@layer pigment.compoundvariants {
  /* Variant styles */
}

The layers will help users to easily override the base or variant styles of derived styled components like -

const OriginalComponent = styled.button({
  color: 'red',
  variants: {
    primary: {
      color: 'blue',
    },
  },
});

const OverriddenComponent = styled(OriginalComponent)({
  color: 'yellow',
  variants: {
    primary: {
      color: 'green',
    },
  },
});

This can be easily achieved with layers in context of build time css extraction.

Output CSS

@layer pigment.base {

  .OriginalComponent {
    color: red;
  }

  .OverriddenComponent {
    color: yellow;
  }

}
@layer pigment.variants {

  .OriginalComponent-variant-primary-color {
    color: blue;
  }

  .OverriddenComponent-variant-primary-color {
    color: green;
  }

}

Migration Strategy

  1. Release core packages with stable API
  2. Provide Material-UI adapter package
  3. Support gradual migration through compatibility layer
  4. Document migration patterns and examples

Resources and benchmarks

https://github.com/mui/pigment-css/tree/master/packages/pigment-css-theme

#345
Core Package
React package

@brijeshb42 brijeshb42 added status: waiting for maintainer These issues haven't been looked at yet by a maintainer RFC Request For Comments labels Jan 6, 2025
@brijeshb42 brijeshb42 self-assigned this Jan 6, 2025
@brijeshb42 brijeshb42 pinned this issue Jan 9, 2025
@astrodomas
Copy link

astrodomas commented Jan 24, 2025

Hey, could you emphasize more on how will this work with nextjs dynamic api, will CSS be granular in respect to JS code splitting?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RFC Request For Comments status: waiting for maintainer These issues haven't been looked at yet by a maintainer
Projects
None yet
Development

No branches or pull requests

2 participants