有種用了就回不去的感覺。
styled component 中的 props
既然它是 Component,那當然也有 props 可以用,直接來示範怎麼用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
   | const Button = styled.button`   border-radius: 4px;   padding: 8px 12px;   background-color: #e4e4e4;   border: none;   color: black;   cursor: pointer;   flex-shrink: 0;      & + & {     margin-left: 8px;   }
    ${     props =>  props.isDone && `       background-color: #0e920e66;       color: white;     `   } `
  function TodoItem ({ content, handlebuttonClick }) {   return (     <TodoItemWrapper>       <TodoContent>{content}</TodoContent>       <TodoButtonWrapper>         // 在這裡傳入         <Button isDone={true}>已完成</Button>         <Button onClick={handlebuttonClick}>刪除</Button>       </TodoButtonWrapper>     </TodoItemWrapper>   ) }
   | 
 

在 styled Component 接收 props 的方式是透過 ${...},${...} 裡面會寫一個 function 來接收 props 參數,你就可以根據它來寫不同的  style 了。
繼承 style
這個跟 class 中的繼承有點類似,你可以先繼承某個 styled component, 再下新的 style 來覆寫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | const Button = styled.button`   border-radius: 4px;   padding: 8px 12px;   background-color: #e4e4e4;   border: none;   color: black;   cursor: pointer;   flex-shrink: 0;      & + & {     margin-left: 8px;   }
    ${     props =>      props.size === 'XL' ? 'font-size:20px' : 'font-size: 12px'   } `
 
  const PinkButton = styled(Button)`   background-color: pink; `
   | 
 

如果要對「React」的 component 重新設定 style
先講一個觀念:
只有 styled component 才吃的到樣式,所以你用的時候一定是放 styled component,不是 react component。
用 React component 包裝起來的 styled components
這種要搭配 props 來傳入 className:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | function TodoItem ({ className, children, handlebuttonClick }) {   return (     <TodoItemWrapper className={className}>       <TodoContent>{children}</TodoContent>       <TodoButtonWrapper>         <Button>已完成</Button>         <Button onClick={handlebuttonClick}>刪除</Button>       </TodoButtonWrapper>     </TodoItemWrapper>   ) }
 
 
  const TodoItem2 = styled(TodoItem)`   background-color: #d6d6d6; `
  | 
 

- 用 style component 來包裝的 React component
 
跟剛剛反過來:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   |  const OrangeButton = styled(TestButton)`   ${defaultButton}   border-color: ${({ theme }) => theme.orange};   color: ${({ theme }) => theme.orange};   &:hover {     background-color: ${({ theme }) => theme.orange};   } `;
 
  function TestButton ({ className }) {   return <button className={className}>測試用</button> }
 
  | 
 

Medai query
其實就是直接在 styled 中寫 @media 就行了,不過更好的做法是把 breakpoint 拆出來寫成常數引入會更好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   |  import { MEDIA_TABLET, MEDIA_PC } from './constants/breakpoint'
  const Button = styled.button`   border-radius: 4px;   padding: 8px 12px;   background-color: #e4e4e4;   border: none;   color: black;   cursor: pointer;   flex-shrink: 0;      & + & {     margin-left: 8px;   }
    ${MEDIA_TABLET} {     font-size: 1.2em;   }
    ${MEDIA_PC} {     font-size: 1.5em;   }
  `
 
  | 
 

變數的運用
首先要用 <ThemeProvider> 把整個 component 給包住:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | import { ThemeProvider } from 'styled-components';
 
  const theme = {   blue: 'royalblue',   orange: 'darkorange',   green: 'mediumseagreen',   red: 'palevioletred', }
  ReactDOM.render(      <ThemeProvider theme={theme}>     <App />   </ThemeProvider>,   document.getElementById('root') )
  | 
 
接著在其他的 style component 就可以透過 props.theme 來存取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   | const Button = styled.button`   border-radius: 4px;   padding: 8px 12px;   background-color: transparent;   border: 1px solid ${props => props.theme.blue};   color: ${props => props.theme.blue};   cursor: pointer;   flex-shrink: 0;      & + & {     margin-left: 8px;   } `
  const GreenButton = styled(Button)`   border-color: ${props => props.theme.green};   color: ${props => props.theme.green}; ` const OrangeButton = styled(Button)`   border-color: ${props => props.theme.orange};   color: ${props => props.theme.orange}; ` const RedButton = styled(Button)`   border-color: ${props => props.theme.red};   color: ${props => props.theme.red}; `
   | 
 
成果大概就像這樣:

不同的標籤想套用相同樣式
假設我寫了一個 <button> 的 styled component,但今天又想在 <a> 上用一樣的樣式時,不需要重新寫一遍,只要利用 as 就可以了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | const Button = styled.button`   border-radius: 4px;   padding: 8px 12px;   background-color: transparent;   border: 1px solid ${props => props.theme.blue};   color: ${props => props.theme.blue};   cursor: pointer;   flex-shrink: 0;      & + & {     margin-left: 8px;   }   &:hover {     background-color: ${props => props.theme.blue};     color: white;   } `
  function TestScope () {   return (     <TodoButtonWrapper style={{       marginTop: '20px'     }}>       <Button>Button</Button>       // 用 as 變成希望的標籤        <Button as="a" href="#">Link</Button>       <Button as="a" href="#">Link</Button>     </TodoButtonWrapper>   ) }
   | 
 

或甚至是變成另一個 component 也行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | function TestScope () {   return (     <TodoButtonWrapper style={{       marginTop: '20px'     }}>       <Button>Button</Button>       <Button as={ReversedButton}>Button</Button>       <ReversedButton>顛倒文字的按鈕</ReversedButton>
      </TodoButtonWrapper>   ) }
  function ReversedButton ({ children }) {   return <Button>{children.split('').reverse()}</Button> }
  | 
 

自動判斷是不是 HTML 元素的屬性
1 2 3 4 5 6 7
   | <TodoItemWrapper>   <TodoContent>{todo.content}</TodoContent>   <TodoButtonWrapper>     <GreenButton isDone={todo.isDone} data-id={todo.id} onClick={handleCompletedButtonClick}>{todo.isDone ? '已完成' : '未完成'}</GreenButton>     <RedButton data-id={todo.id} onClick={handleRemoveButtonClick}>刪除</RedButton>   </TodoButtonWrapper> </TodoItemWrapper>
   | 
 
以這個例子來說,GreenButton 寫了 isDone 和 data-id 兩個屬性,但實際上會被渲染出來的只有 data-id,不會有 isDone:

這就是 style component 會做的自動判斷。
不過有一種情況是可能我想在 style component 傳 id 這個 props:
1 2 3 4
   | <GreenButton    id={todo.id}   onClick={handleButtonClick('isCompleted')}>{todo.isDone ? '已完成' : '未完成'} </GreenButton>
   | 
 
可是又不希望被渲染到 HTML 上,這時候就可以改用 $ 來傳(Transient props):
1 2 3 4
   | <GreenButton    $id={todo.id}   onClick={handleButtonClick('isCompleted')}>{todo.isDone ? '已完成' : '未完成'} </GreenButton>
   | 
 
這樣就不會被渲染了。
所以建議養成一種習慣,只要是給 style component 用的 props 就一律用 $ 來表示,可讀性會更好。
設定屬性值
要在 style component 上設定 HTML 元素的屬性有兩種方式,第一種是直接寫在 Component 上:
1 2 3 4 5 6 7 8 9 10 11 12
   | const RadioButton = styled.input``
  function TestScope () {   return (     <TodoButtonWrapper style={{       marginTop: '20px'     }}>       <RadioButton type="radio" name='gender' value="man"></RadioButton>       <RadioButton type="radio" name='gender' value="female"></RadioButton>     </TodoButtonWrapper>   ) }
   | 
 
第二種是透過 style.attrs 屬性:
1 2 3 4 5 6 7 8 9 10 11 12
   | const RadioButton = styled.input.attrs({ type: 'radio' })``
  function TestScope () {   return (     <TodoButtonWrapper style={{       marginTop: '20px'     }}>       <RadioButton name='gender' value="man"></RadioButton>       <RadioButton name='gender' value="female"></RadioButton>     </TodoButtonWrapper>   ) }
  | 
 
看起來是第二種會好一點,因為只要寫在一個地方就好,之後要改會比較方便。
設定全域空間的樣式
當想要改 <body> 或是 reset 的樣式時,應該就會用到。這是透過 createGlobalStyle 來達成的。
首先要先寫好全域的 style component:
1 2 3 4 5 6 7 8 9 10 11
   | import { createGlobalStyle } from "styled-components";
  export const GlobalStyle = createGlobalStyle`   body {     // 全域空間的 style     background-color: pink;   }   .bg-dark {     background-color: black;   } `
  | 
 
接著引入到 entry 就會套用了:
1 2 3 4 5 6 7 8 9
   | ReactDOM.render(   <ThemeProvider theme={theme}>     // 放在這裡,不是外面     // 另外因為是包在 ThemeProvide 裡,所以也能存到 theme 的 props     <GlobalStyle />     <App />   </ThemeProvider>,   document.getElementById('root') )
   | 
 
定義在 Global 的 className 也可以在底下的 component 使用:
1 2 3 4 5 6 7 8 9
   | function TestScope () {   return (     <TodoButtonWrapper className="bg-dark">       <Button>123</Button>       <Button>456</Button>       <Button>789</Button>     </TodoButtonWrapper>   ) }
  | 
 

給選取器更高的權重:&&
先來看段 code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   |  const Span = styled.span`   && {     color: blue   } `
  const GlobalStyle = createGlobalStyle`   body {     // 全域空間的 style   }   span${Span} {     color: red;   } `
  ReactDOM.render(   <ThemeProvider theme={theme}>     <GlobalStyle />     <App />     <Span>I'm span</Span>   </ThemeProvider>,   document.getElementById('root') )
 
  | 
 
簡單來說,我在 Global 宣告 span${Span} 要是紅色,但我希望實際是藍色,所以就在 Span 中用 && 來覆寫全域設定:

注意 & 跟 && 的差別,一個會被覆寫一個不會。
把會重複使用的樣式存起來
style component 有提供 css 方法讓你把會共用的樣式儲存起來,它的寫法是這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
   | import { css } from 'styled-components'
  const buttonDefault = () => css`   border-radius: 4px;   padding: 8px 12px;   background-color: transparent;   cursor: pointer;   flex-shrink: 0;
    // 這個寫法會有一些問題,留到下面來解釋   & + & {     margin-left: 8px;   } ` const BlueButton = styled.button`   // 直接引用   ${buttonDefault}   border: 1px solid ${props => props.theme.blue};   color: ${props => props.theme.blue};   &:hover {     background-color: ${props => props.theme.blue};     color: white;   } ` const GreenButton = styled.button`   // 直接引用   ${buttonDefault}   border-color: 'green';   color: 'green';   &:hover {     background-color: 'green';   } ` const OrangeButton = styled.button`   // 直接引用   ${buttonDefault}   border-color: 'orange';   color: 'orange';   &:hover {     background-color: 'orange';   } ` const RedButton = styled.button`   // 直接引用   ${buttonDefault}   border-color: 'red';   color: 'red';   &:hover {     background-color: 'red';   } `
  | 
 
這樣就能做出這樣的效果:

不過會發現 & + & 的部分並沒有套用到,為什麼?
這是因為當你建立一個新的 style component 時其實會重新產生一個 className,所以你的 & + & 其實是這樣子:
1 2 3 4 5 6 7 8 9
   | BlueButton + BlueButton {   margin-left: 8px } greenButton + greenButton {   margin-left: 8px }  RedButton + RedButton {   margin-left: 8px }
  | 
 
上圖中的綠按鈕旁邊接的是紅按鈕,所以才沒有套用這個規則。
要解決這個問題的辦法有兩種,一種是改用「繼承」的寫法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   | const BlueButton = styled.button`   border-radius: 4px;   padding: 8px 12px;   background-color: transparent;   border: 1px solid ${props => props.theme.blue};   color: ${props => props.theme.blue};   cursor: pointer;   flex-shrink: 0;      & + & {     margin-left: 8px;   }   &:hover {     background-color: ${props => props.theme.blue};     color: white;   } ` const GreenButton = styled(BlueButton)`   // ... ` const OrangeButton = styled(BlueButton)`   // ... ` const RedButton = styled(BlueButton)`   // ... `
   | 
 
因為是透過繼承,所以 BlueButton 的部分不會重新產生 className,只有後來新增的才會。
另一種方式是把 & + & 的規則透過父層來指定:
1 2 3 4 5 6 7 8
   | const TodoButtonWrapper = styled.div`   display: flex;   align-items: center;   // 底下的 button + button 會套用   button + button {     margin-left: 8px;   } `
   | 
 
至於哪種寫法比較好就見仁見智,我個人是覺得繼承的寫法比較直覺一點,不過缺點是元件本身會多一條限制,因此「可重用性」會比較差。
其他補充
style.button 跟 style('button') 是等價的東西 
- 不要把 style component 寫在 React component 裡面,會有效能問題(每次 render 就重新宣告)