Edited at

styled components documentation日本語訳①ーBasicsー


概要

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>
);

styled component1.png

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>
);

styled component 2.png


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>
);

styled component 3.png

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>
);

styled component 4.png

カスタム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 5.png


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>
);

styled compnent 6.png

これがinputColor propがDOMに渡されないが、typedefaultValueは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を書くことが推奨される。

``javascript

const StyledWrapper = styled.div


/* ... */

`

const Wrapper = ({ message }) => {

return {message}

}

```

こんな感じではなく。

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>
)

styled component 7.png

より複雑なセレクターには&を使うと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>
)

スクリーンショット 2018-11-09 1.11.18.png

セレクターを&なしで使用した場合には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>
)

スクリーンショット 2018-11-09 1.11.53.png

最後に、&は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>
)

スクリーンショット 2018-11-09 1.11.53.png


追加の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>
);

スクリーンショット 2018-11-09 1.39.12.png

ご覧の通り.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>&lt; 💅 &gt;</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との違いは、keyframescreateGlobalStyleヘルパーが使えないことだ。

なぜならReact Nativeはその両方をサポートしてないからだ。

またもしmediaクエリかネストさせたCSSを使用するときにも注意が必要だ。


注意:v2バージョンではパーセンテージ記法をサポートしている。

これを可能にするためすべての省略記法に向けunitを強化しなければならなかった。

もしv2に移行しているならば、codemodが使用できる。



metro bundlerを使ってより簡単に

styled-components/nativeの代わりに単純にstyled-componentsをimportしたいなら、"react-native"を含んだresolverMainFields configurationを使用しても良い。

これはデフォルトでmetroによってサポートされていたが(今はhaulでも動く)、どうやらいくつかの点で取り除かれてしまっているようだ。


まとめ

超速で訳したので間違いあれば言ってほしい。

疲れた。