LoginSignup
8
10

More than 5 years have passed since last update.

Airbnb CSS-in-JavaScript Style Guideを和訳してみた

Posted at

ご挨拶

こんにちは。株式会社POLでインターンをしております。
私たちのフロントエンドでは、.jsファイルの中にCSSを記述するCSS-in-JSという形式を取っています。

Airbnbでは、CSS-in-JavaScriptに対してもスタイルガイドがあるみたいですね。
Airbnb CSS-in-JavaScript Style Guide

他のJSに関するスタイルガイドはこちらから飛べます。
Airbnb JavaScript Style Guide() {

JSのスタイルガイドについては、和訳版ありますので参考にしてください。
Airbnb JavaScript スタイルガイド() {

CSS in JSに関する和訳記事がなかったので、勉強も兼ねて和訳しました。間違ってたら叱ってください。

これらが絶対正義ではないですが、いい所はPOLにも取り入れて、ルール化していきたいですねん。

目次

  1. 命名(Naming)
  2. 順序(Ordering)
  3. 入れ子(Nesting)
  4. インライン(Inline)
  5. テーマ(Themes)

命名(Naming)

  • Use camelCase for object keys (i.e. "selectors").
  • オブジェクトのキー(すなわち セレクター)にはキャメルケースを使用する。

Why? We access these keys as properties on the styles object in the component, so it is most convenient to use camelCase.

コンポーネント内のstylesオブジェクトのプロパティとしてこれらのキーにアクセスしますが、キャメルケースを使うのが最も都合が良いです。

// bad
{
  'bermuda-triangle': {
    display: 'none',
  },
}

// good
{
  bermudaTriangle: {
    display: 'none',
  },
}
  • Use an underscore for modifiers to other styles.
  • スタイルの修飾子には、アンダースコア( _ )を使用する。

Why? Similar to BEM, this naming convention makes it clear that the styles are intended to modify the element preceded by the underscore. Underscores do not need to be quoted, so they are preferred over other characters, such as dashes.

BEMと同様に、この命名規則はアンダースコアの前の要素がどのような意図で修飾されているかを明確にします。必ずしもアンダースコアである必要はありませんが、ダッシュ( - )のようなその他の文字列よりも好まれます。

// bad
{
  bruceBanner: {
    color: 'pink',
    transition: 'color 10s',
  },
n
  bruceBannerTheHulk: {
    color: 'green',
  },
}

// good
{
  bruceBanner: {
    color: 'pink',
    transition: 'color 10s',
  },

  bruceBanner_theHulk: {
    color: 'green',
  },
}
  • Use selectorName_fallback for sets of fallback styles.
  • selectorName_fallback をフォールバックスタイルの組として使用します。

Why? Similar to modifiers, keeping the naming consistent helps reveal the relationship of these styles to the styles that override them in more adequate browsers.
修飾子と同じように、命名規則の一貫性を保つことは、これらのスタイルとより適切なブラウザでオーバーライドするスタイルとの関係が明らかになります。

// bad
{
  muscles: {
    display: 'flex',
  },

  muscles_sadBears: {
    width: '100%',
  },
}

// good
{
  muscles: {
    display: 'flex',
  },

  muscles_fallback: {
    width: '100%',
  },
}
  • Use a separate selector for sets of fallback styles.
  • フォールバックスタイルの組には別のセレクタを使用します。

Why? Keeping fallback styles contained in a separate object clarifies their purpose, which improves readability.

別のオブジェクト内に含まれるフォールバックスタイルを保持すると、目的が明確となり、可読性が向上します。

// bad
{
  muscles: {
    display: 'flex',
  },

  left: {
    flexGrow: 1,
    display: 'inline-block',
  },

  right: {
    display: 'inline-block',
  },
}

// good
{
  muscles: {
    display: 'flex',
  },

  left: {
    flexGrow: 1,
  },

  left_fallback: {
    display: 'inline-block',
  },

  right_fallback: {
    display: 'inline-block',
  },
}
  • Use device-agnostic names (e.g. "small", "medium", and "large") to name media query breakpoints.
  • メディアクエリのブレークポイントに名前を付けるには、デバイスに依存しない名前("small"、"medium"、"large"など)を使用します。

Why? Commonly used names like "phone", "tablet", and "desktop" do not match the characteristics of the devices in the real world. Using these names sets the wrong expectations.

「電話」、「タブレット」、「デスクトップ」などの一般的に使用される名前は、現実世界のデバイスの特性と一致しません。これらの名前を使用すると間違った期待を設定します。

// bad
const breakpoints = {
  mobile: '@media (max-width: 639px)',
  tablet: '@media (max-width: 1047px)',
  desktop: '@media (min-width: 1048px)',
};

// good
const breakpoints = {
  small: '@media (max-width: 639px)',
  medium: '@media (max-width: 1047px)',
  large: '@media (min-width: 1048px)',
};

順序(Ordering)

  • Define styles after the component.
  • コンポーネントの後にスタイルを定義します。

Why? We use a higher-order component to theme our styles, which is naturally used after the component definition. Passing the styles object directly to this function reduces indirection.

コンポーネント定義の後で自然に使用されるスタイルをテーマにするために高次のコンポーネント(HoC:Higher-order Componrnts)を使用します。スタイルオブジェクトをこの関数に直接渡すと間接参照が減少します。

<参考>
Higher-Order Components - React
React の Higher-order Components の利用方法 - Qiita

// bad
const styles = {
  container: {
    display: 'inline-block',
  },
};

function MyComponent({ styles }) {
  return (
    <div {...css(styles.container)}>
      Never doubt that a small group of thoughtful, committed citizens can
      change the world. Indeed, its the only thing that ever has.
    </div>
  );
}

export default withStyles(() => styles)(MyComponent);

// good
function MyComponent({ styles }) {
  return (
    <div {...css(styles.container)}>
      Never doubt that a small group of thoughtful, committed citizens can
      change the world. Indeed, its the only thing that ever has.
    </div>
  );
}

export default withStyles(() => ({
  container: {
    display: 'inline-block',
  },
}))(MyComponent);

入れ子(Nesting)

  • Leave a blank line between adjacent blocks at the same indentation level.
  • 同じインデントレベル(インデントの深さ)を持つ隣接したブロック間で、空行を残します。

Why? The whitespace improves readability and reduces the likelihood of merge conflicts.

空白は可読性を向上させ、マージコンフリクトの可能性を低減させます。

// bad
{
  bigBang: {
    display: 'inline-block',
    '::before': {
      content: "''",
    },
  },
  universe: {
    border: 'none',
  },
}

// good
{
  bigBang: {
    display: 'inline-block',

    '::before': {
      content: "''",
    },
  },

  universe: {
    border: 'none',
  },
}

インライン(Inline)

  • Use inline styles for styles that have a high cardinality (e.g. uses the value of a prop) and not for styles that have a low cardinality.
  • カーディナリティが高い場合(例えばpropの値を使用する場合)、インラインスタイルを使用し、カーディナリティが低い場合には使用しないでください。

<参考>
DBについての記事ですが、概念は同じなので参考までに。
カーディナリティについてまとめてみた

Why? Generating themed stylesheets can be expensive, so they are best for discrete sets of styles.

テーマにまとめられたスタイルシートを作ることはコストがかかることがあるので、個別のスタイルセットに最適です。

// bad
export default function MyComponent({ spacing }) {
  return (
    <div style={{ display: 'table', margin: spacing }} />
  );
}

// good
function MyComponent({ styles, spacing }) {
  return (
    <div {...css(styles.periodic, { margin: spacing })} />
  );
}
export default withStyles(() => ({
  periodic: {
    display: 'table',
  },
}))(MyComponent);

テーマ(Themes)

  • Use an abstraction layer such as react-with-styles that enables theming. react-with-styles gives us things like withStyles(), ThemedStyleSheet, and css() which are used in some of the examples in this document.
  • テーマを可能にするreact-with-stylesのような抽象レイヤーを使用します。react-with-stylesは、このドキュメント内にあるいくつかの例に使われているwithStyles()ThemedStyleSheetcss()などが利用できます。

Why? It is useful to have a set of shared variables for styling your components. Using an abstraction layer makes this more convenient. Additionally, this can help prevent your components from being tightly coupled to any particular underlying implementation, which gives you more freedom.

共有された変数を持つことは、あなたのコンポーネントをスタイリングするのに役立ちます。抽象化レイヤーを用いることは、これをより使いやすくします。さらに、あなたのこんぽコンポーネントが下層の実装と密接に結合するのを防ぐことができます。これにより、自由度が増します。

  • Define colors only in themes.
  • カラーはテーマ内にのみ定義します。
// bad
export default withStyles(() => ({
  chuckNorris: {
    color: '#bada55',
  },
}))(MyComponent);

// good
export default withStyles(({ color }) => ({
  chuckNorris: {
    color: color.badass,
  },
}))(MyComponent);
  • Define fonts only in themes.
  • フォントもテーマ内にのみ定義します。
// bad
export default withStyles(() => ({
  towerOfPisa: {
    fontStyle: 'italic',
  },
}))(MyComponent);

// good
export default withStyles(({ font }) => ({
  towerOfPisa: {
    fontStyle: font.italic,
  },
}))(MyComponent);
  • Define fonts as sets of related styles.
  • 関連するスタイルのセットとして、フォントを定義します。
// bad
export default withStyles(() => ({
  towerOfPisa: {
    fontFamily: 'Italiana, "Times New Roman", serif',
    fontSize: '2em',
    fontStyle: 'italic',
    lineHeight: 1.5,
  },
}))(MyComponent);

// good
export default withStyles(({ font }) => ({
  towerOfPisa: {
    ...font.italian,
  },
}))(MyComponent);
  • Define base grid units in theme (either as a value or a function that takes a multiplier).
  • ベースのグリッドの単位をテーマ内に(値もしくは、乗数をとる関数として)定義します。
// bad
export default withStyles(() => ({
  rip: {
    bottom: '-6912px', // 6 feet
  },
}))(MyComponent);

// good
export default withStyles(({ units }) => ({
  rip: {
    bottom: units(864), // 6 feet, assuming our unit is 8px
  },
}))(MyComponent);

// good
export default withStyles(({ unit }) => ({
  rip: {
    bottom: 864 * unit, // 6 feet, assuming our unit is 8px
  },
}))(MyComponent);
  • Define media queries only in themes.
  • メディアクエリもテーマ内に定義します。
// bad
export default withStyles(() => ({
  container: {
    width: '100%',

    '@media (max-width: 1047px)': {
      width: '50%',
    },
  },
}))(MyComponent);

// good
export default withStyles(({ breakpoint }) => ({
  container: {
    width: '100%',

    [breakpoint.medium]: {
      width: '50%',
    },
  },
}))(MyComponent);
  • Define tricky fallback properties in themes.
  • トリッキーなフォールバックプロパティもテーマ内に定義します。

Why? Many CSS-in-JavaScript implementations merge style objects together which makes specifying fallbacks for the same property (e.g. display) a little tricky. To keep the approach unified, put these fallbacks in the theme.

多くの CSS-in-JavaScript の実装では、スタイルのオブジェクトがマージされ、同じプロパティのフォールバック(例:display)を少しトリッキーに指定します。アプローチを統一するために、これらのフォールバックもテーマ内に記述します。

// bad
export default withStyles(() => ({
  .muscles {
    display: 'flex',
  },

  .muscles_fallback {
    'display ': 'table',
  },
}))(MyComponent);

// good
export default withStyles(({ fallbacks }) => ({
  .muscles {
    display: 'flex',
  },

  .muscles_fallback {
    [fallbacks.display]: 'table',
  },
}))(MyComponent);

// good
export default withStyles(({ fallback }) => ({
  .muscles {
    display: 'flex',
  },

  .muscles_fallback {
    [fallback('display')]: 'table',
  },
}))(MyComponent);
  • Create as few custom themes as possible. Many applications may only have one theme.
  • できるだけカスタムテーマを作成しないでください。多くのアプリケーションは一つのテーマしか持つことができません。

  • Namespace custom theme settings under a nested object with a unique and descriptive key.

  • カスタムテーマの設定は、ネストされたオブジェクトの下でユニークかつ説明十分なキーとして、名前を分割します。

// bad
ThemedStyleSheet.registerTheme('mySection', {
  mySectionPrimaryColor: 'green',
});

// good
ThemedStyleSheet.registerTheme('mySection', {
  mySection: {
    primaryColor: 'green',
  },
});
8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10