概要
styled componentsのdocumentation日本語訳がなかったので翻訳する。
Basics、Advancedは翻訳確実、ほかは気が向いたら翻訳する。
以下翻訳。
Basics
動機
styled-componentはReact componentシステムにおけるCSSをどうやったら強化できるだろうかと悩んだ結果である。
個々の利用例にフォーカスし、開発者とエンドユーザの両方にとって最適な体験を構築することができた。
開発者向けに改善された体験に加えて、styled-componentsは以下の特徴を提供する。
- Automatic critical CSS:
styled-componentsはページにどのcomponentsがrenderされたかを追跡するとともに、彼らが保持するstyleだけを注入する。完全に自動的に。コードが分離できることにより、ユーザーは必要に応じて最小限のコードのみをロードする。 - No class name bugs:
styled-componentsは各々のスタイルにユニークなnameを生み出す。重複について心配する必要はもうない。 - Easier deletion of CSS:
コードベースのどこかであるクラス名が使われているかを知ることは難しい。styeld-componentsはこれを明らかにする。なぜならどのstylingもある特定のcomponentに紐付いているからだ。
もしcomponentが利用されなくなり(toolingが特定できる)、デリートされたとき、componentとともに紐づくstyleもすべてデリートされる。 - Simple dynamic styling:
componentのstyleをpropsやglobal themeに基づいて適用することはたくさんのclassesを手動で管理する必要のないシンプルで直感的なものである。 - Painless maintenance:
ファイルをまたいでcomponentに影響するstyleを探し出す必要はもうない。したがってどれだけコードベースが大きくてもメンテナンスは朝飯前だ。 - Automatic vendor prefixing:
現行基準のCSSを書き、残りはstyled-coponentsにまかせてしまおう。
各々のcomponentsに紐付けるだけで、熟知した愛すべきCSSを書いていることがこれらすべての恩恵を受けられる。
インストール
訳す価値ないので飛ばす。
ここを見てください。
Gettin Started
styled-componetsはcomponentsをsylingするためにタグのリテラルを利用する。
これによりcomponentとstyle間のマッピングが不要になる。
すなわち、styleを定義するとき実際には普通のReact componentを作っているのと変わらない。ただし、それはstyleを持っている。
この例はwrapperとtitleという2つのシンプルなstyle付きcomponentを作成している。
// Create a Title component that'll render an <h1> tag with some styles const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; `; // Create a Wrapper component that'll render a <section> tag with some styles const Wrapper = styled.section` padding: 4em; background: papayawhip; `; // Use Title and Wrapper like any other React component – except they're styled! render( <Wrapper> <Title> Hello World! </Title> </Wrapper> );

※documentationでは実際にコードに触れるので遊んで見てほしい。以下同様。
注意:ベンダープレフィックスはstyled-componentsよって自動適用されている。
propsの適用
functionをテンプレート構文を用いてstyled-componentsのリテラルに渡し、propsに基づいてstyleを適用することができる。
このボタンは色を変えるためのprimary stateを持っている。primary
propをtrueに設定すると、backgroundとtext colorをスワップすることができる。
const Button = styled.button` /* Adapt the colors based on primary prop */ background: ${props => props.primary ? "palevioletred" : "white"}; color: ${props => props.primary ? "white" : "palevioletred"}; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; render( <div> <Button>Normal</Button> <Button primary>Primary</Button> </div> );

styleの拡張
たった一回の使用のためだがわずかにcomponentを変化させ使いたいというときが頻繁にあると思う。
今まではテンプレート構文でfunctionを渡し、いくつかのpropsに基づきcomponentを変化させていたはずだ。
しかしそれにはstyleを一回変えるだけに多大な労力を要している。
別のcomponentをstyleを受け継ぐ新しいcomponentを簡単に作成するためには、ただstyled()
constructorでラップしてあげれば良い。
ここでは一つ前の章で作ったボタンをベースに色のcolorのstyleを拡張した特別なボタンを作ってみる。
// The Button from the last section without the interpolations const Button = styled.button` color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; // A new component based on Button, but with some override styles const TomatoButton = styled(Button)` color: tomato; border-color: tomato; `; render( <div> <Button>Normal Button</Button> <TomatoButton>Tomato Button</TomatoButton> </div> );

2つのruleを付け加えるだけで、Button
に似ている新しいTomatoButton
を作ることができた。
いくつかの場面でstyled componentがrenderするtagやcomponent
を変えたいときがある。
個々にstylingされているanchor linkやbuttonが入り交じるnavigation barを構築するときなどが典型例だ。
こういう場合のため回避法を用意している。
“as”というポリモーフィックを司るpropを使えば作成したstyleを受け取る要素を動的に変更することができる。
const Button = styled.button` display: inline-block; color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; const TomatoButton = styled(Button)` color: tomato; border-color: tomato; `; render( <div> <Button>Normal Button</Button> <Button as="a" href="/">Link with Button styles</Button> <TomatoButton as="a" href="/">Link with Tomato Button styles</TomatoButton> </div> );

カスタムcomponentに対しても完璧に動作する!
const Button = styled.button` display: inline-block; color: palevioletred; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; const ReversedButton = props => <button {...props} children={props.children.split('').reverse()} /> render( <div> <Button>Normal Button</Button> <Button as={ReversedButton}>Custom Button with Normal Button styles</Button> </div> );

Styled Component
styleのターゲットが単純な要素(styled.div
など)なら、styled-componentsはHTMLの属性と知られるすべての値をDOMにパスする。
カスタムReact component(styled(Mycomponent)
など)なら、styled-componentsはすべてのpropsをパスする。
この例はReact elements同様、Input componentのすべてのpropsがどのようにしてマウントされたDOM nodeに受け渡されるかを示している。
// Create an Input component that'll render an <input> tag with some styles const Input = styled.input` padding: 0.5em; margin: 0.5em; color: ${props => props.inputColor || "palevioletred"}; background: papayawhip; border: none; border-radius: 3px; `; // Render a styled text input with the standard input color, and one with a custom input color render( <div> <Input defaultValue="@probablyup" type="text" /> <Input defaultValue="@geelen" type="text" inputColor="rebeccapurple" /> </div> );

これがinputColor
propがDOMに渡されないが、type
とdefaultValue
はDOMに渡される仕組みである。
それが自動的に標準でないattributesをフィルタしてくれるstyled-componentsである。
Coming from CSS
コンポーネントないでStyled Componentがどのように動いているか
もしcomponentsにCSSをインポートすること(CSSModulesなど)に精通しているなら、このような操作に慣れているだろう。
import React from 'react' import styles from './styles.css' export default class Counter extends React.Component { state = { count: 0 } increment = () => this.setState({ count: this.state.count + 1 }) decrement = () => this.setState({ count: this.state.count - 1 }) render() { return ( <div className={styles.counter}> <p className={styles.paragraph}>{this.state.count}</p> <button className={styles.button} onClick={this.increment}> + </button> <button className={styles.button} onClick={this.decrement}> - </button> </div> ) } }
Styled Componentはstyleするelementとruleの組み合わせだから、Counter
をこのように書こうと思う。
import React from 'react' import styled from 'styled-components' const StyledCounter = styled.div` /* ... */ ` const Paragraph = styled.p` /* ... */ ` const Button = styled.button` /* ... */ ` export default class Counter extends React.Component { state = { count: 0 } increment = () => this.setState({ count: this.state.count + 1 }) decrement = () => this.setState({ count: this.state.count - 1 }) render() { return ( <StyledCounter> <Paragraph>{this.state.count}</Paragraph> <Button onClick={this.increment}>+</Button> <Button onClick={this.decrement}>-</Button> </StyledCounter> ) } }
ここで”Styled”という接頭辞をStyledCounter
に加えたことに注意してほしい。
これはReact componentのCounter
とStyled ComponentのStyledCounter
の名前が衝突せず、かつRect Developer ToolsとWeb Inspectorで用意に判別できるようにするためのものである。
Styled Componentはrenderメソッドの外側で定義しよう
renderメソッドの外でstyled componentsを定義することが重要である。
さもなければrenderの過程ごとに作り直されることになる。
renderメソッドの中にstyled componentを定義することはキャッシュを妨げrenderのスピードを劇的に低下させる。
したがって回避すべきことだ。
このようにstyled componentsを書くことが推奨される。
const StyledWrapper = styled.div` /* ... */ ` const Wrapper = ({ message }) => { return <StyledWrapper>{message}</StyledWrapper> }
こんな感じではなく。
const Wrapper = ({ message }) => { // WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!! const StyledWrapper = styled.div` /* ... */ ` return <StyledWrapper>{message}</StyledWrapper> }
Recommended reading: Talia Marcassaが実際の利用法について有益な実践的洞察、代替案との比較について述べた素晴らしいレビューを書いている。
Styled Components: To Use or Not to Use?
疑似要素、疑似セレクタ、ネスト
私達が使用しているプリプロセッサのstylisはネストするstylingに自動的に対応するためのscssライク文法をサポートしている。
例を出そう。
const Thing = styled.div` color: blue; `
疑似セレクターと疑似要素はこれ以上の改良を必要とせずcomponentに適用される。
const Thing = styled.button` color: blue; ::before { content: '🚀'; } :hover { color: red; } ` render( <Thing>Hello world!</Thing> )

より複雑なセレクターには&を使うとcomponentそれ自身を参照できる。
考えられるいくつかの例をあげよう。
const Thing = styled.div.attrs({ tabIndex: 0 })` color: blue; &:hover { color: red; // <Thing> when hovered } & ~ & { background: tomato; // <Thing> as a sibling of <Thing>, but maybe not directly next to it } & + & { background: lime; // <Thing> next to <Thing> } &.something { background: orange; // <Thing> tagged with an additional CSS class ".something" } .something-else & { border: 1px solid; // <Thing> inside another element labeled ".something-else" } ` render( <React.Fragment> <Thing>Hello world!</Thing> <Thing>How ya doing?</Thing> <Thing className="something">The sun is shining...</Thing> <div>Pretty nice day today.</div> <Thing>Don't you think?</Thing> <div className="something-else"> <Thing>Splendid.</Thing> </div> </React.Fragment> )

セレクターを&なしで使用した場合にはcomponentのchildrenを参照することになる。
const Thing = styled.div` color: blue; .something { border: 1px solid; // an element labeled ".something" inside <Thing> display: block; } ` render( <Thing> <label htmlFor="foo-button" className="something">Mystery button</label> <button id="foo-button">What do I do?</button> </Thing> )

最後に、&はcomponentへのruleの明確性を増すためにも使用できる。
これはstyled-componentsとバニラCSSが入り交じりstyleが競合する可能性のある環境で有用である。
const Thing = styled.div` && { color: blue; } ` const GlobalStyle = createGlobalStyle` div${Thing} { color: red; } ` render( <React.Fragment> <GlobalStyle /> <Thing> I'm blue, da ba dee da ba daa </Thing> </React.Fragment> )

追加のpropsの付与 (v2)
ただpropsをrenderされるcomponentやelementにわたすだけの不要なラッパーを取り除くために、.attrs constructor
を使うことができる。
これにより追加のprops(もしくは”attributes”)をcomponentに適用することができる。
このようにスタティックなpropsと要素に適用したり、React RouterのLink componentへのactiveClassName
のような外部からのpropsを渡すことができる。
更により動的なpropsをcomponentに適用することもできる。.attrs
objectは関数も受け取ることができ、その関数はcomponentが受け取るpropsを引数として取ることができる。
その戻り値はその下で用いられるprops(例ではmarginとpaddingで用いられるprops)にマージされる。
Input
Comonentにいくつかのスタティックattributeと動的なattributeを追加してみよう。
const Input = styled.input.attrs({ // we can define static props type: "password", // or we can define dynamic ones margin: props => props.size || "1em", padding: props => props.size || "1em" })` color: palevioletred; font-size: 1em; border: 2px solid palevioletred; border-radius: 3px; /* here we use the dynamically computed props */ margin: ${props => props.margin}; padding: ${props => props.padding}; `; render( <div> <Input placeholder="A small text input" size="1em" /> <br /> <Input placeholder="A bigger text input" size="2em" /> </div> );

ご覧の通り.attrの中で作られた新たなpropsにアクセスすることができ、そしてtype
attributeは要素に受け渡される。
アニメーション
@keyframes
を用いたCSSアニメーションは一つのcomponentのscopeに限られていないが、それでも名前の衝突を防ぐためglobalスコープにしたくないと考えられる。
これがappを通してユニークなインスタンスを作るkeyframes
ヘルパーを導入した理由である。
// Create the keyframes const rotate = keyframes` from { transform: rotate(0deg); } to { transform: rotate(360deg); } `; // Here we create a component that will rotate everything we pass in over two seconds const Rotate = styled.div` display: inline-block; animation: ${rotate} 2s linear infinite; padding: 2rem 1rem; font-size: 1.2rem; `; render( <Rotate>< 💅 ></Rotate> );
注意:keyfmesは
react-native
ではサポートされていないので、代わりにReactNative.Animated APIを使おう。
keyframesは使用されたとき初めて遅延評価され、そうしてコードの分離を実現している。
そしてこういった場合shared style fragments向けのcss helperを使用しなければならない。
const rotate = keyframes`` // ❌ This will throw an error! const styles = ` animation: ${rotate} 2s linear infinite; `; // ✅ This will work as intended const styles = css` animation: ${rotate} 2s linear infinite; `
注意:v3以下のバージョンではkeyframesを用いたコード分離は行っておらず、上のエラーは起こらない。
v3より上のバージョンにアップグレードする場合には、必ずcss helperを使うようにしなければならない。
React Native
styled-componentsはReact Nativeでも同じ仕様法、import方法で利用できる。
以下の例をSnack by Expoを用いて実行してみてほしい。
import React from 'react' import styled from 'styled-components/native' const StyledView = styled.View` background-color: papayawhip; ` const StyledText = styled.Text` color: palevioletred; ` class MyReactNativeComponent extends React.Component { render() { return ( <StyledView> <StyledText>Hello World!</StyledText> </StyledView> ) } }
また普通arrayになるようなより複雑なstyles(transformなど)や、省略記法もcss-to-react-nativeのおかげでサポートできている。
注意:
flex
propertyはReact NativeではReact Nativeのレガシーなflex
propertyではなく、CSSの省略記法として振る舞う。flex: 1
と設定することはflexShrink: 1
とすることに等しい。
React Nativeのプロパティをどう書けばいいか、それをCSSに翻訳する方法を推測したなら、それは多分正しい。
const RotatedBox = styled.View` transform: rotate(90deg); text-shadow-offset: 10px 5px; font-variant: small-caps; margin: 5px 7px 2px; `
webのstyled-componentsとの違いは、keyframes
とcreateGlobalStyle
ヘルパーが使えないことだ。
なぜならReact Nativeはその両方をサポートしてないからだ。
またもしmediaクエリかネストさせたCSSを使用するときにも注意が必要だ。
注意:v2バージョンではパーセンテージ記法をサポートしている。
これを可能にするためすべての省略記法に向けunitを強化しなければならなかった。
もしv2に移行しているならば、codemodが使用できる。
metro bundlerを使ってより簡単に
styled-components/native
の代わりに単純にstyled-components
をimportしたいなら、"react-native"
を含んだresolverMainFields configurationを使用しても良い。
これはデフォルトでmetroによってサポートされていたが(今はhaulでも動く)、どうやらいくつかの点で取り除かれてしまっているようだ。
まとめ
超速で訳したので間違いあれば言ってほしい。
疲れた。