Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add built in support for theming with React #414

Open
joseDaKing opened this issue Oct 6, 2021 · 2 comments
Open

Add built in support for theming with React #414

joseDaKing opened this issue Oct 6, 2021 · 2 comments

Comments

@joseDaKing
Copy link

Although there is a guide on how to achieve theming with styletron-react, the guide doesn't work so well with typescript and it just gives you a headache.

@SukkaW
Copy link

SukkaW commented Feb 11, 2022

Although there is a guide on how to achieve theming with styletron-react, the guide doesn't work so well with typescript and it just gives you a headache.

For people searching styletron theme on Google and found this issue:

Let's assume you already have a theme setup:

interface Theme {
  // Whatever you want
}

const theme: Theme = {
  // blah blah blah
} as const;

Then create a context:

const ThemeContext = React.createContext<Theme>(theme);

And here is the new styled:

// First let us import a pile of things
import { driver, getInitialStyle, type StyleObject } from 'styletron-standard';
import { createStyled, type StyletronComponent, type StyletronWrapper } from 'styletron-react';

const wrapper: StyletronWrapper = (StyledComponent) => React.forwardRef((props, ref) => (
  <ThemeContext.Consumer>
    {theme => <StyledComponent ref={ref} {...props} $theme={theme} />}
  </ThemeContext.Consumer>
));

// And here comes the type, kaboom!
interface StyleFn {
  <K extends keyof JSX.IntrinsicElements>(
    component: K,
    style: StyleObject | ((props: React.ComponentProps<K> & { $theme: Theme }) => StyleObject)
  ): StyletronComponent<JSX.IntrinsicElements[K]>;
  <P extends React.ComponentType<any>>(
    component: P,
    style: StyleObject | ((props: React.ComponentProps<P> & { $theme: Theme }) => StyleObject)
  ): StyletronComponent<React.ComponentProps<P>>;
  <EP extends object, K extends keyof JSX.IntrinsicElements>(
    component: K,
    style: StyleObject | ((props: React.ComponentProps<K> & { $theme: Theme } & EP) => StyleObject)
  ): StyletronComponent<JSX.IntrinsicElements[K] & EP>;
  <EP extends object, P extends React.ComponentType<any>>(
    component: P,
    style: StyleObject | ((props: React.ComponentProps<P> & { $theme: Theme } & EP) => StyleObject)
  ): StyletronComponent<React.ComponentProps<P> & EP>;
}

// Finally, our new styled:
const styled: StyleFn = createStyled({
  wrapper,
  driver,
  getInitialStyle
});

The new styled is strictly typed (shout out to @Jack-Works, he is the true typescript master!):

interface Theme {
    background: '',
    color: {
        red: [''],
        blue: ['', '']
    }
}

const A = styled('a', {});
<A href="" />; // href can be validated

const B = styled('a', (props) => ({ color: props.$theme.color.red[0], background: props.href })); // Theme available via $theme
<B href="" />;

const C = styled<{ $isActive?: boolean }, 'a'>('a', (props) => ({ color: props.$isActive ? props.$theme.color.red[0] : props.href })); // You can pass $isActive here
<C $isActive />;

function Component(props: { myProps: number }) { return null }

const D = styled(Component, {});
<D myProps={1} />; // You can even validate myProps

const E = styled(Component, props => ({ background: String(props.myProps) }));
<E myProps={2} />;

const F = styled<{ $isActive?: boolean }, typeof Component>(Component, (props) => ({ color: props.$isActive ? props.$theme.color.red[0] : String(props.myProps) }));
<F $isActive myProps={1} />; // All $theme, $isActive and myProps can be validated

useStyletron hook is a lot easier:

import { useStyletron as useStyletronFromStyletron } from 'styletron-react';
const useStyletron = () => {
  const theme = React.useContext(ThemeContext);
  const [css] = useStyletronFromStyletron();

  return [css, theme] as const;
};

@hrasoa
Copy link

hrasoa commented Aug 13, 2022

@SukkaW why should we add all these overload types for StyleFn when the source only contains this ?

export type StyledFn = {
  <T extends React.ElementType, Props>(
    component: T,
    style: StyleObject | ((props: Props) => StyleObject),
  ): StyletronComponent<T, Props>;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants