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>
);
※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でも動く)、どうやらいくつかの点で取り除かれてしまっているようだ。
まとめ
超速で訳したので間違いあれば言ってほしい。
疲れた。