Baseweb: [RFC] [Experimental] Support arbitrary style engines through styled function injection

Created on 9 Aug 2018  Â·  3Comments  Â·  Source: uber/baseweb

Problem

At the moment baseui has a hard dependency on styletron as its style engine. This could be a hurdle to adoption in the open source community, as many projects use styled-components, emotion, or one of the many other css-in-js flavors, and they may be unwilling to add styletron providers to their app just to use base-ui.

This issue explores a model that allows users to provide their own style engine. This is still highly experimental and has a couple issues – so for those reasons I'm not recommending that we try to use it now. But it seemed like it was an idea worth documenting and keeping in the back of our minds in case its an avenue we want to explore in the future.

Potential Solution

The general idea is pretty simple – internally we already use a styled() function to define all of our styled components, so what if we could allow users to supply their own styled implementation that conforms to a specific signature?

Here's what this might look like for a consumer who wants to use emotion:

import {ThemeProvider} from 'baseui';
import styled from 'react-emotion';

export default function emotionStyled(elemType, objOrFn) {
  return styled(elemType)(objOrFn);
}

export default () => (
  <ThemeProvider theme={LIGHT_THEME} styled={emotionStyled}>
    <App/>
  </ThemeProvider>
);

And here's a proof of concept demonstrating that, surprisingly, this can actually work:

cef27f1: Support injecting styled

This works by deferring styled component creation until the first time it is rendered at runtime, at which point we lazily instantiate the styled component using styled() from theme context.

The vast majority of our storybooks still rendered and looked pixel perfect. There were however, a couple stories that were broken, which brings us to...

Known Issues

  • Different style engines handle refs differently – for instance, styletron expects a $ref prop and emotion expect $innerRef. Technically you could write logic to map these but it requires some extra work. This may become less of an issue as these libraries start to support forward refs.
  • Similar to $ref, styletron has some other custom APIs for things like font-face and keyframes. No components are using those at the moment, but if they were, that would make it harder to decouple from styletron. (but maybe we won't need these features?)
  • Styletron helpers like withStyle no longer work because the styled components we export are no longer actual styletron components, but instead wrappers that render a styletron components when invoked. That said, we don't use withStyle within components internally, so it's possible to push this concern to consumers when they specify their own styled() implementation.

At the end of the day, style engines either need to (a) understand the style objects we have, or (b) we need to be able to write an adapter that transforms our style objects into something that the engine does understand.

Thoughts

As mentioned previously, I'm not proposing that we immediately add support for this (so I'll close this issue shortly), but it seemed like an interesting idea worth sharing in case decoupling from styletron is brought up by customers in the future. Feel free to share thoughts or other potential blockers.

rfc

Most helpful comment

I think this is a great idea and would like to see it happen. I have an application which uses styled-component, and I would be willing to pilot this idea in order to migrate to BaseUI.

All 3 comments

It would be fairly trivial to make styletron-engine-emotion (for example). The key part of the design of styletron-react@3 was to completely decouple the rendering engine from the component definitions. So this is sort of already supported on a framework level.

styletron-react@3 is actually more similar to "recompose" than it is styletron-react@2. In reality, all it is are some HOCs for working with style objects and has nothing to do with rendering. I need to do a better job explaining this change. It's pretty big difference in philosophy and implementation but because it has the same name it's not obvious. All real opinion comes from "styletron-engine-atomic", but this is trivially swapped out.

they may be unwilling to add styletron providers to their app just to use base-ui.

Of course, it would still require a context provider, but I doubt that would be much of a burden. I think the real blocker would be requiring a whole separate rendering engine in addition to what people already use. But anything that exposes a lower-level API (such as emotion or jss will be perfectly suited). styletron-react-core is purposefully lightweight and tree-shakeable. Any HOCs that aren't used (such as withStyle, etc.) simply aren't bundled and incur no cost).

font-face and keyframes. No components are using those at the moment, but if they were, that would make it harder to decouple from styletron. (but maybe we won't need these features?)

The engine is the thing that translates the style objects to actual rendering methods, so the engine itself can map the keyframe and font objects to whatever needs to be done, or in the worse case, just inject css on its own.

Closing for now since we don't plan to implement this immediately, but we can re-open if we decide to pursue this.

I think this is a great idea and would like to see it happen. I have an application which uses styled-component, and I would be willing to pilot this idea in order to migrate to BaseUI.

Was this page helpful?
0 / 5 - 0 ratings