본문 바로가기

html&css

[styled-components] 테마

 

 

  styled-components는 <ThemeProvider>라는 컴포넌트를 포함한다. 이 컴포넌트는 컨텍스트 API를 통해 자체 아래의 모든 React 컴포넌트에 테마를 제공한다. 렌더링 트리에서 모든 스타일 컴포넌트는 여러 레벨과 뎁스인 경우에도 테마를 액세스할 수 있다.

const Button = styled.button`
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;
  color: ${props => props.theme.main};
  border: 2px solid ${props => props.theme.main};
`;

// ThemeProvider를 사용할 때 default props를 지정해서 사용할 수도 있다.
Button.defaultProps = {
  theme: {
    main: "palevioletred"
  }
}

const theme = {
  main: "mediumseagreen"
};

render (
  <div>
    <Button>Nomal</Button>
    
    <ThemeProvider theme={theme}>
      <Button>Themed</Button>
    </ThemeProvider>
  </div>
);

 

  테마 props에 함수를 전달할 수도 있다. 이 함수는 부모 테마 안에서 새로운 <ThemeProvider>를 사용해 다른 테마를 받는다. 이런 식으로 테마 자체를 상황에 맞게 만들 수도 있다.

const Button = styled.button`
  color: ${props => props.theme.fg};
  border: 2px solid ${props => props.theme.fg};
  background: ${props => props.theme.bg};
  
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;
`

const theme = {
  fg: "palevioletred",
  bg: "white"
};

const invertTheme = ({ fg, bg }) => {{
  fg: bg,
  bg: fg
}};

render(
  <ThemeProvider theme={theme}>
    <div>
      <Button>Default Theme</Button>
      
      <ThemeProvider theme={invertTheme}>
        <Button>Inverted Theme</Button>
      </ThemeProvider>
    </div>
  </ThemeProvider>
);

 

  • withTheme

  withTheme는 테마 개체에 액세스할 수 있게 해주는 HOC이다. 이 것은 테마에 따라 달라지는 스타일이 있는 컴포넌트를 만들거나 테마 관련 props를 하위 컴포넌트에 전달하려는 경우에 유용하다. 먼저, 전체 앱을 래핑하는 ThemeProvider를 만들고 테마 개체를 제공해야 한다.

import { ThemeProvider } from 'styled-components';

const theme = {
  primaryColor: '#006699',
  secondaryColor: '#ff9900'
}

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      {/* ... app components /*}
    </ThemeProvider>
  )
}

그런 다음 스타일이 지정된 컴포넌트에서 withTheme를 사용하여 테마 개체를 액세스할 수 있다.

import styled from 'styled-components'
import { withTheme } from 'styled-components'

const Button = styled.button`
  background-color: ${props => props.theme.primaryColor};
  color: white;
  padding: 10px;
`;

const ThemedButton = withTheme(Button);

const MyComponent = () => {
  return (
    <div>
      <ThemedButton>Click me</ThemedButton>
    </div>
  )
};

이 코드에서 테마의 primaryColor를 사용하는 Button 컴포넌트를 만든다. 그런 다음 withTheme HOC를 사용하여 테마 개체에 액세스할 수 있는 ThemedButton 컴포넌트를 만든다. 그런 다음 MyComponent 컴포넌트에서 ThemedButton을 사용했다.

 

  • ThemeContext

  React의 useContext와 styled-components의 ThemeContext를 사용해서도 이전의 기능을 구현할 수 있다.

import { ThemeProvider } from 'styled-components';

const theme = {
  primaryColor: 'blue',
  secondaryColor: 'green'
}

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Example>
    </ThemeProvider>
  )
}
import React, { useContext } from 'react';
import styled, { ThemeContext } from 'styled-components';

const StyledComponent = styled.div`
  background-color: ${props => props.theme.primaryColor};
`

const Example = () => {
  const themeContext = useContext(ThemeContext);
  
  return <StyledComponent theme={themeContext.theme}>Example</StyledComponent>
}

여기서 StyledComponent는 테마의 primaryColor를 사용하여 배경색을 설정하는 스타일 컴포넌트이다. Example은 useContext를 사용하여 ThemeContext에서 theme 객체를 액세스한 다음 StyledComponent에 props를 전달하는 기능적 컴포넌트이다. 

 

  • useTheme

  useTheme는 위의 방법을 구현하는 것들 중 가장 최근에 나온 방법이다. hook으로 사용한다.

import { useTheme } from 'styled-components';

const MyComponent = () => {
  const theme = useTheme();
  
  return (
    <div>
      <p>Primary color: {theme.primaryColor}</p>
      <p>Secondary color: {theme.secondaryColor}</p>
    </div>
  )
}

당연히 useTheme 역시 컴포넌트가 ThemeProvider의 자손이어야 한다. 컴포넌트 트리에 ThemeProvider가 없으면 useTheme에서 오류가 발생한다.

 

  마지막으로 테마를 부분적으로만 사용하거나 일부러 피하게도 할 수 있다.

const Button = styled.button`
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;
  
  color: ${props => props.theme.main};
  border: 2px solid ${props => props.theme.main};
`

const theme = {
  main: "mediumseagreen"
};

render(
  <div>
    <Button theme={{ main: "royalblue" }}>Ad hoc theme</Button>
    <ThemeProvider theme={theme}>
      <div>
        <Button>Themed</Button>
        <Button theme={{ main: "darkorange" }}>Overriden</Button>
      </div>
    </ThemeProvider>
  </div>
);