Week 6 - Joy UI and Styling - CI256

Week 6 - Joy UI and Styling

There are 3 primary ways to customize JoyUI component styles.

  • The SX Prop
  • Custom Components
  • Theming
    • Modifying theme colors and attributes
    • Component Customization

The SX Prop

The SX Prop is the primary way to apply styles on an individual component. While you can apply style or className to JoyUI components, using the SX prop has a few advantages:

  • Theme aware - the SX property is theme aware
    • We can use something like primary.main to use our primary theme color
  • React/JS friendly syntax
    • ex, backgroundColor vs `background-color
  • Special utilities
    • Spacing Utilities - This is by far the most helpful
      • m: 1 - apply margin to all sides of a “standard value” of 1
      • mx: 2 - apply left and right margins of a “standard value” of 2
      • py: 2 - apply top and bottom padding of a “standard value” of 2
    • Typography - utilize standard fonts and font-weights by name
      • fontWeight: light might mean a weight of 400 or 500, depending on font configuration
    • Responsive Design: Setting different CSS styles based on the size of the screen
    • Example: Display items in a column on screens up to a “large” size, then display as a row
    {display: 'flex', flexDirection: {xs: 'column', lg: 'row'}}
<Input sx={{display: 'flex', flexDirection: 'column'}} />

When specifying the SX prop:

  • We need a double set of curly braces {{ /* */ }}.
    • The first set says “open the Javascript context”
    • The second set says “create a new object”
  • We cannot use any keys with a - in them, as that is invalid JS
    • Ex, background-color is invalid - we’d need to use backgroundColor instead

What we’re really doing here is passing a JS object to the SX prop:

const style = {
	display: 'flex',
  flexDirection: 'column'
}
return <Input sx={style} />

Conditional Style Overrides with Arrays

By passing an Array to the SX prop, you can override certain values with conditions. The last item passed to the array will take precedence

<Box
  sx={[
    // note we're passing an array of objects, instead of an object
    {
      '&:hover': {
        color: 'red',
        backgroundColor: 'white',
      },
    },
    // apply this if foo is true
    foo && {
      '&:hover': { backgroundColor: 'grey' },
    },
    // apply this if bar is true
    // if both foo and bar are true, 
    // 'yellow' take precendence as it's last
    bar && {
      '&:hover': { backgroundColor: 'yellow' },
    },
  ]}
/>

Spacing - Padding and Margin

Remember, padding includes the background, margin does not

https://lab.ci256.cloud/week6?id=spacing-margin-padding

Custom Components

Whenever you have a component in the UI that is used multiple times, we typically want to make a custom component from it. We’ve already created components in this class, but usually for a single use.

For example, we might want to create an input field that handles displaying errors to the user.

type Props = {  
  value?: string // initial value  
  onChange?: (value: string) => void; // on change callback  
  error?: string; // error message to display  
}  
export function InputField({value, onChange, error}) {  
  const [val, setValue] = useState(value);  
  useEffect(() => {  
    onChange?.(val)  
  }, [val])  
  return <Box sx={{display: 'flex-col'}}>  
    <Input 
      value={val}  
      color={error ? 'danger' : 'primary'}  
      onChange={(e) => setValue(e.target.value)}  
      sx={{mb: 1}}  
    />  
    {error && <Alert color='danger'>{error}</Alert>}  
  </Box>  
}

// usage
<InputField  />  
<InputField error='Invalid Password: too short'  />

We’ve defined a couple of style/display attributes here:

  • Display Input and Alert on top of one-another using flex-col layout
  • Create margin between the Input and the item below it
  • Only display an “Alert” colored “Danger” when error is present

Now, we won’t need to repeat ourselves if we want another input box with an alert below it. We can simply re-use the newly defined InputField component in multiple places.

This is a major concept of modern web development - think of components as re-usable functions. We want to be DRY programmers - “Don’t Repeat Yourself”

JoyUI Theme Customization

You can read more about themes here: https://mui.com/joy-ui/customization/approaches/#theming

Theming will be not be required for this class, but you are welcome to play around with it if you’d like.