Skip to content

Proposal: variant prop #800

@dburles

Description

@dburles

Expanding on #294 (and related to #706), I've been thinking about an idea of replacing the variant faux CSS property instead with a special variant prop.

As mentioned in #294, the variant API no longer becomes useful when dealing with components composed of multiple elements, this is a problem for both userland components (#294) and @theme-ui/components i.e. the Select component mentioned in #706.

Because of this, you are forced to abandon use of the variant API and instead implement something akin to what this variant prop proposal offers. The downside with that in the current implementation is then your component library will have two API's for using variants, (sx variant and variant prop), unless as I mentioned, you decide to implement a variant prop in your component library for all components that require it (as a workaround). With this proposal, that would not be required. e.g:

// Contrived example of the issue above
const Button = ({
  variant,
}) => {
  return (
    <button
      sx={{
        // ...
        variant: `buttons.Button.${variant}`,
      }}
    />
  )
};

In implementation of the variant prop, the example API @jxnblk mentioned in #706 could be altered as follows:

const theme = {
  ...
  forms: {
    select: {
      // styles for select element
      select: {
        borderRadius: 5,
      },
      container: {
        // styles for root element
      },
      arrow: {
        // styles for arrow icon go here
      },
    },
  },
}

The Select component from @theme-ui/components can now reference the variant prop (as now can any userland components too, and also easily pick from the theme object too if they wish):

Rough mockup:

export const Select = React.forwardRef((props, ref) => {
  const { theme } = useTheme();
  return (
    <Box
      {...getMargin(props)}
      sx={{
        display: 'flex',
        ...theme.forms.select.container,
      }}
    >
      <Box
        ref={ref}
        as="select"
        {...omitMargin(props)}
        __css={{
          display: 'block',
          width: '100%',
          p: 2,
          appearance: 'none',
          fontSize: 'inherit',
          lineHeight: 'inherit',
          border: '1px solid',
          borderRadius: 4,
          color: 'inherit',
          bg: 'transparent',
          ...theme.forms.select.select,
        }}
      />
      <DownArrow
        sx={{
          ml: -28,
          alignSelf: 'center',
          pointerEvents: 'none',
          ...theme.forms.select.arrow,
        }}
      />
    </Box>
  );
});

With some additional changes to the Select component, it would also be possible to support inline overrides, e.g:
This is probably not ideal as components with a single element would mean that variant={{ color: 'blue' }} would work too, but that would clash with the sx prop API. Better that the variant prop only extracts values from the theme.

<Select variant={{ arrow: { color: 'blue' } }}>...</Select>

Another change that will need to occur is that you can no longer use the variant property within the theme, but that can be solved by regular old object composition:

const input = {
  color: 'text'
};

const theme = {
  forms: {
    input,
    select: {
      // instead of variant: 'input',:
      ...input,
      // styles for select element
      borderRadius: 5,
      container: {
        // styles for root element
      },
      arrow: {
        // styles for arrow icon go here
      }
    }
  }
};

I'm definitely interested in any thoughts and ideas on this, I've spent some time thinking it through, however there may be some aspects that I've overlooked.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions