Documentation
Theme's Components

Theme's Components

In modern UI development, components are the only blocks we use to build our applications. Every piece of our UIs is defined by components, from the simple button to the navbar to the page itself.

Components won as a concept because they are the perfect abstraction for reusable UIs: Make one button and use it everywhere you need a button, the same for a card, or a container.

It's basically the Don't Repeat Yourself (DRY) concept applied to visual elements.

Reusable Styles

Sometimes we also want to reuse styles other than components, Morfeo makes it possible with the concept of the Theme's components:

const classes = morfeo.css({
  base: {
    radius: 'm',
    px: 'm',
    py: 's',
    fontSize: 'm',
  },
  filled: {
    bg: 'gray',
    color: 'black',
  },
  outlined: {
    bg: 'transparent',
    border: 'strong',
    borderColor: 'gray.darkest',
  },
});
 
export function Button({ variant, children }) {
  return <button className={classes('base', variant)}>{children}</button>;
}

We can refactor this component in this way:

const classes = morfeo.css({
  base: {
    componentName: 'Button',
  },
  filled: {
    variant: 'filled',
  },
  outlined: {
    variant: 'outlined',
  },
});
 
export function Button({ variant, children }) {
  return <div className={classes('base', variant)}>{children}</div>;
}
⚛️

For React users, this can also be simplified further:

export const Button = morfeo.component('Button', {
  variant: props => pros.variant,
});

Read more about morfeo.component API here.

With the Theme's components, you can encapsulate the style of a component and all its variants inside the theme, this way every other component can reuse it or extend it:

const classes = morfeo.css({
  link: {
    componentName: 'Button',
    variant: 'outlined',
  },
});
 
export function Link() {
  return (
    <a href="/docs" target="_blank" className={classes('link')}>
      Go to the docs
    </a>
  );
}

Define Theme's Components

theme's components visualization

Basic configuration

A Theme Component, in its more basic form, it's just an object that contains a style:

morfeo.ts
const theme = {
  components: {
    Button: {
      style: {
        bg: 'primary',
        radius: 'm',
        px: 'm',
        py: 's',
        textAlign: 'center',
      },
    },
  },
};
 
export const morfeo = createMorfeo({ theme });

Once the style is defined we can use the property componentName in any style to refer to it:

const classes = morfeo.css({
  button: {
    componentName: 'Button',
  },
});

In other words, componentName: "Button" is an alias for:

{
  "bg": "primary",
  "radius": "m",
  "px": "m",
  "py": "s",
  "textAlign": "center"
}

Define variants

A component usually can be displayed in different ways, for example, a button can be filled by default, or transparent but surrounded with a nice border if outlined.

We call these variations of a base component style variants:

morfeo.ts
const theme = {
  components: {
    Button: {
      style: {
        // ...
      },
      variants: {
        outlined: {
          style: {
            bg: 'transparent',
            border: 'strong',
            color: {
              light: 'gray.darkest',
              dark: 'white',
            },
            borderColor: {
              light: 'gray.darkest',
              dark: 'white',
            },
          },
        },
      },
    },
  },
};
 
export const morfeo = createMorfeo({ theme });

Once the variant is defined we can use the property variant combined with componentName in any style to refer to it:

const classes = morfeo.css({
  button: {
    componentName: 'Button',
    variant: 'outlined',
  },
});

When the variant is specified, the base style of the component is merged with the style of the variant, variant's style overrides the base style in case of conflicts.

Define states

Component style is defined by 3 things:

  • The base style
  • A specialization of the base aka variant
  • The state of the component

Let's stick to the Button component example, we might want to define the styles of the button in case it's disabled:

const theme = {
  components: {
    Button: {
      style: {
        // ...
      },
      variants: {
        // ...
      },
      states: {
        disabled: {
          opacity: 'light',
          cursor: 'not-allowed',
        },
      },
    },
  },
};
 
export const morfeo = createMorfeo({ theme });

Once the states are defined we can use the property state combined with componentName (and also variant) in any style to refer to it:

const classes = morfeo.css({
  button: {
    componentName: 'Button',
    variant: 'outlined',
    '&:disabled': {
      state: 'disabled',
    },
  },
});

For React users

If you're using React, you can use the API morfeo.component which gives you another set of functionalities.

Define element tag

const theme = {
  components: {
    Button: {
      tag: 'button',
      style: {
        // ...
      },
      variants: {
        // ...
      },
      states: {
        // ..
      },
    },
  },
};
 
export const morfeo = createMorfeo({ theme });
Variants overrides

Variants can also redefine the tag in case is needed:

const theme = {
  Typography: {
    tag: 'p',
    style: {},
    variants: {
      title: {
        tag: 'h1',
        style: {},
      },
      subtitle: {
        tag: 'h2',
        style: {},
      },
    },
  },
};

Define initial props

const theme = {
  components: {
    Button: {
      tag: 'button',
      props: {
        type: 'button',
        'data-testid': 'morfeo-button',
      },
      style: {
        // ...
      },
      variants: {
        // ...
      },
      states: {
        // ..
      },
    },
  },
};
 
export const morfeo = createMorfeo({ theme });
Variants overrides

Variants can also redefine props in case is needed:

const theme = {
  Typography: {
    tag: 'p',
    style: {},
    props: {
      'data-testid': 'morfeo-paragraph',
    },
    variants: {
      title: {
        tag: 'h1',
        style: {},
        props: {
          'data-testid': 'morfeo-title',
        },
      },
      subtitle: {
        tag: 'h2',
        style: {},
        props: {
          'data-testid': 'morfeo-subtitle',
        },
      },
    },
  },
};