question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

[discussion] Standardizing the sx style language across MUI

See original GitHub issue

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Summary šŸ’”

With the deprecation of makeStyles(), there are a few things we lose as far as reusability and fidelity:

  • How do we create reusable components with portable styles and props? We used to do this with makeStyles() but now we canā€™t/shouldnā€™t?
  • Where do we define other non-theme global styles? Maybe some utility classes like text or flexbox alignment, or reusable styles you want to be present throughout your app?
  • How do we create reusable styles within a component? sx is cool, but no one wants to copy and paste a bunch of inline styles all over the place, right?
  • How do we add classNames? (I realize that thereā€™s a debate whether named styles are meaningful, and perhaps theyā€™re not).

This post explores some of that rationale, the challenges/pain, and some possible solutions.

Examples šŸŒˆ

No response

Motivation šŸ”¦

(This is a tactical abstract of a story I wrote about this suggestion).

The pain

With the deprecation of makeStyles(), there are a few things we lose as far as reusability and fidelity:

  • How do we create reusable components with portable styles and props? We used to do this with makeStyles() but now we canā€™t/shouldnā€™t?
  • Where do we define other non-theme global styles? Maybe some utility classes like text or flexbox alignment, or reusable styles you want to be present throughout your app?
  • How do we create reusable styles within a component? sx is cool, but no one wants to copy and paste a bunch of inline styles all over the place, right?
  • How do we add classNames? (I realize that thereā€™s a debate whether named styles are meaningful, and perhaps theyā€™re not).

Now, we can of course use styled() to wrap components in a style, but even then, thatā€™s not a set of reusable styles; theyā€™re individually defined on each element, and youā€™d have to define them again for every component you make.

Some ways Iā€™ve considered working around this:

Workaround 1

Add a custom style property to a theme object and using the spread operator (...), mix it into the rest of your style. I donā€™t mind this, but it doesnā€™t quite feel the same as adding a class to your component, because we never use the className prop. It also doesnā€™t know anything about the class name youā€™ve given it, so the generated HTML on the page never includes it in the class tag.

You also have to be careful with the spread operator and deep copies. Depending on what youā€™re doing, you might be better off using deepmerge here instead.

Workaround 2

Create a higher scoped variable and reference it from the sx property . The upside to this is itā€™s fast and we can use the power of sx again (šŸ„³), but the downsides are that we canā€™t do it globallyā€”it only works in your local component file (unless we made a separate moduleā€¦ maybe?), and the generated code will make a copy of the style for each of the sx references.

But also, while spread/deepmerge are fine, theyā€™re not very idiomatic, and thereā€™s a fair amount of room for error.

I also have found some REALLY weird things when using pre-defined sx props, and I donā€™t know if this is bug-worthy or not, I canā€™t repro it very well. In this case, I was mixing in a color prop { color: 'primary.main' } with a const { flexDirection: 'column' }. But I digress.

Workaround 3

Use an external CSS file. Iā€™m not sure I like this, though:

  • Adding an external stylesheet means you (probably) end up with a separate build step to manage your plain CSS. You might lose TypeScript validation checking fidelity here (e.g., a typo in your .css file might appear to be valid in your React componentā€™s className prop), and more moving parts means more things that could go wrong.
  • You break the opinionated model of React components and the MUI system, which could lead to a bunch of weird problems that are difficult to troubleshoot. External styles load in a different order, so youā€™ll spend your time wondering why styles arenā€™t applying, or why others are clobbering things you didnā€™t intend them to. Thereā€™s also the namespace conflict and collision risk.
  • Styling the component HTML for MUI components is tough. They often contain multiple nested elements, with MUI-specific names like .MuiSlider-thumb, and youā€™ll have to figure out what they all are. Difficult to maintain, difficult to write.

Ask

(Disclaimer: because sx is wrapping Emotion, there is maybe another better way of doing this I donā€™t know about. If so, please let me know!)

I wish there were a way to fix this pain using sx everywhere, with true support for reusable styles. Why?

  • sx is awesome, and I love it.
  • I donā€™t want to context switch. I want to know that whatever styles Iā€™m writing are all using the same superset of CSS so that thereā€™s no chance of getting confused. This is arguably the most important of all my pain points.
  • I want linting to work everywhere. If I write an sx style block in a theme declaration, the system should be able to tell me if I made a mistake. (Maybe less relevant for .js, but definitely for .ts).
  • Naming things doesnā€™t always matter, but itā€™s often helpful for debugging styles, and coming from a plain HTML/CSS background, not using identifiable class names feels like a loss? Not a deal breaker though.
  • MUI is opinionated about a lot of things, but how to declare global styles and utility classes doesnā€™t seem to be one of them. We should fix that.
  • I donā€™t understand the performance implications of the myriad ways of putting styles into an app. Are some better than others? Probablyā€”and Iā€™d rather the system just told me what to do, rather than me having to think about it.

So how could we make this better? These might be throwaway ideas, but at least could spark some ideas.

Idea 1

A new hook like makeStyles() that uses the sx superset.

import * as React from 'react'
import { styled } from '@mui/system'

const useStyles = styled.sx({
  flexCenter: {
    display: 'flex',
    alignItems: 'center', 
    justifyContent: 'center',
    flexBasis: 'fit-content',
    flexDirection: {
      xs: 'column',
      sm: 'row',
    }
  }
})

export default function MyComponent(props) {
  const classes = useStyles(props)
  return <div className={classes.flexCenter} />
}

Or perhaps thereā€™s a way we can create variables with all our reusable styles?

import * as React from 'react'
import { styled } from '@mui/system'

const flexCenter = styled.sx({
  display: 'flex',
  alignItems: 'center', 
  justifyContent: 'center',
  flexBasis: 'fit-content',
  flexDirection: {
    xs: 'column',
    sm: 'row',
  }
})

const flexCenterDiv = flexCenter(MyComponent)( {
  return <div>My content</div>
})

Idea 2

Support all of the sx superset in theme objects. (Note that this doesnā€™t solve for reusability).

import * as React from 'react'
import { styled, createTheme, ThemeProvider } from '@mui/system'

const MyThemeComponent = styled('div')(({ theme }) => ({
  color: 'primary.contrastText',
  backgroundColor: 'primary.main',
  paddingX: { 
    xs: 1,
    sm: 2,
    md: 3,
  },
  borderRadius: theme.shape.borderRadius,
}))

export default function ThemeUsage() {
  return (
    <ThemeProvider theme={customTheme}>
      <MyThemeComponent>Styled div with theme</MyThemeComponent>
    </ThemeProvider>
  )
}

Idea 3

Create a wrapper or dedicated section in a theme to support utility or global classes.

import { createTheme, styled } from '@mui/material/styles'

let theme = createTheme({
  shape: {
    borderRadius: 4,
  },
})

theme = createTheme(theme, {
  classes: styled.sx({
    flexCenter: {
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      flexBasis: 'fit-content',
      flexDirection: {
        xs: 'column',
        sm: 'row',
      }
    },
    makeItRound: {
      borderRadius: theme.shape.borderRadius,
    })
  },
})

If we didnā€™t want to worry about naming things, this idea works just as well without classNames, if we wanted to define sx props at the root:

import { createTheme, styled } from '@mui/material/styles'

let theme = createTheme({
  sx: {
    borderRadius: 4,
  },
})

theme = createTheme(theme, {
 sx: {
   display: 'flex',
   alignItems: 'center',
   justifyContent: 'center',
   flexBasis: 'fit-content',
   flexDirection: {
     xs: 'column',
     sm: 'row',
   }
   borderRadius: theme.sx.borderRadius,
 },
})

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:4
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
brandonscriptcommented, Nov 24, 2021

Thanks for the reply @mnajdova, I finally have a few moments to sit down with your comments!

I agree for overriding and defining universal styles at the global level makes perfect sense, but one gap in coverage here is that we canā€™t explicitly turn a style on and off, like you would with a class name. Consider:

<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
    <Button>I <3 MUI!</Button>
</Box>

I can make a box have this style via a global theme override, either defining some style there (but not with sx notation!), or overriding the Mui class name.

But what if I wanted to have a generic utility class for use in some Box elements, or maybe a Container element, or even some Typography. I could create custom components for these using styled():

const CenteredBox = styled(Box, { 
    display: 'flex', 
    flexDirection: 'column',
    alignItems: 'center', 
    justifyContent: 'center' 
})

But even here, I have to do this for each different component type. Which is fine, but feels like a lot of overhead compared to a single CSS class.

I think there are several ways to work around this, and maybe if nothing else, having an MUI opinion on what the right way of doing it in the docs is worth even more than trying to make a code change to support something new. (Adding sx support to global styles and theme objects notwithstanding!). You allude to this in

Whatever you had previously in the makeStyles input can now be a simple object/function. You can reuse them anywhere.

so maybe thatā€™s the best way to do it, and it should just be prevalent in the docs? The only thing I wasnā€™t super confident on is the duplication of styles. A CSS class can be written once and used everywhere, but this still looks to me like each component where itā€™s used is going to get its own unique class, potentially duplicating a lot of generated code. If Iā€™m wrong, let me know!


There are differences in styled() vs. sx vs. makeStyles that really do make context switching hard, which you covered in your reference to consolidating these (yay!), but this doesnā€™t seem like it covers using those in theme as well, which means in theme, we still can only use standard CSS syntax? Context switching the different syntaxes is HARD, and I do really hope we can get to a point where we can use the same sx superset everywhere.


The rest of what youā€™ve replied with mostly makes sense, perhaps the only thing that stands out is that the class naming syntax on styled() via the slot and name props, which just feels a little awkward, but it works, so šŸ¤·ā€ā™‚ļø !

0reactions
elyobocommented, Aug 31, 2022

Is there a plan to un-experimental the experimental_sx?

Read more comments on GitHub >

github_iconTop Results From Across the Web

[discussion] Standardizing the sx style language across MUI
How do we create reusable styles within a component? sx is cool, but no one wants to copy and paste a bunch of...
Read more >
The sx prop - MUI System
The sx prop is a shortcut for defining custom styles that has access to the theme. The sx prop lets you work with...
Read more >
How to use the sx prop in MUI v5. Still using makeStyles?
The sx prop can be used in a similar manner as the style prop in standard react inline styling. It can be applied...
Read more >
@mui/styles | Yarn - Package Manager
MUI Styles - The legacy JSS-based styling solution of Material UI. react, react-component, mui, styles. readme. @mui/styles. You can leverageĀ ...
Read more >
AP English Language and Composition
It is only through a commitment to equitable preparation and access that true equity and excellence can be achieved. Designers: Sonny Mui and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found