14
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【お遊び?】Emotion で Utility First してみた

Last updated at Posted at 2022-07-21

Emotion の機能を使って、こんな感じで Utility First な記述を試してみました。

uf-emotion.tsx
<div css={limitWidth()}>
  <main
    css={[
      minHeight(640),
      pcOnly([paper, my(4)]),
      mbOnly(bgColor("#fafafa")),
      contentsStyle,
    ]}
  >
    <section css={[responsive({ mb: p(2), pc: p(3) })]}>
      <h1 css={[typography.h1]}>ああああ</h1>
      <p>
        Irofa nifofedo tirinuruwo wagayo tarezo tunenaram uwino okuyama kefu
        koete asaki yumemisi wefimo sezu
      </p>
    </section>
  </main>
</div>

(共通化は漏れが起きやすいので、こういう小さなユーティリティの組み合わせが楽なんですよね... )

利用しているライブラリのバージョン
パッケージ バージョン
@emotion/react 11.9.3
next 12.1.6
react, react-dom 18.1.0

Emotion の css Prop について

まず、css Prop の型は Interpolation<Theme> 型になっています。そして、Interpolation は以下のように定義されています。

export type Interpolation<Props> =
  | InterpolationPrimitive
  | ArrayInterpolation<Props>
  | FunctionInterpolation<Props>

css タグリテラル(基本形)

css`` の式の結果は、InterpolationPrimitive 型(のサブタイプ)になるので、

<div css={css`background-color: red;`}></div>

と記述できるわけです。

テーマを使うこともできる。

1つ飛ばして、次は3つ目の FunctionInterpolation を見てみましょう。

export interface FunctionInterpolation<Props> {
  (props: Props): Interpolation<Props>
}

となっているので、 Theme (つまり、Emotionで宣言したテーマ)を引数に取ることが出来ることが分かります。

<div
  css={(theme) =>
    css`background-color: ${theme.colors.primary};`
  }
>
  ああああ
</div>

配列も指定できる。

ArrayInterpolation を見てみましょう。これがミソです。

export interface ArrayInterpolation<Props>
  extends Array<Interpolation<Props>> {}

となっているので、配列の形で渡すことが出来ます。

<div
  css={[
    css`color: white;`,
    // 1) テーマを引数に取る指定も含められる
    (theme) => css`background-color: ${theme.colors.primary};`,
    // 2) && etc. による条件付き指定も可
    selected && css`background-color: red;`,
  ]}
>
  いいいい
</div>

コメントで記述したように、1) テーマを引数にとることで、テーマを利用することも出来ますし、2) && 演算子等による、条件つきの指定もできるようになっています。(もちろん三項演算子も使用可)

&& が使えるお陰で、clsx によるクラス指定みたいに、シンプルな表記で 適用/不適用 を制御できるのが嬉しいですね。

これを応用すれば...

勘の良い皆さまはもうお気づきでしょう。この配列を使った指定方法を取れば、Interpolation<Theme>型のを返す関数として切り出し、それらを呼び出して並べることで、スタイルを合成することができます。

これが、emotion で Utility First 的な記述が出来るカラクリです。

ユーティリティー関数群

最初に示した記述に使われているUtility群は、このように定義されています。

tokens.ts
type CSSInterpolation = Parameters<typeof css>[number];

const breakpoint = "640px";

export const pcOnly = (s: CSSInterpolation) =>
  css`
    @media screen and (min-width: ${breakpoint}) {
      ${s}
    }
  `;

export const mbOnly = (s: CSSInterpolation) =>
  css`
    @media screen and (max-width: ${breakpoint}) {
      ${s}
    }
  `;

export const spacing = (value: number | string) =>
  typeof value === "string" ? value : `${8 * value}px`;

// system

export const minHeight = (px: number) =>
  css`
    min-height: ${px}px;
  `;

export const my = (units: number | string) =>
  css`
    margin-top: ${spacing(units)};
    margin-bottom: ${spacing(units)};
  `;

export const mx = (units: number | string) =>
  css`
    margin-right: ${spacing(units)};
    margin-left: ${spacing(units)};
  `;

export const p = (units: number | string) =>
  css`
    padding: ${spacing(units)};
  `;

// typography
export const typography = {
  h1: [
    css`
      font-size: 2.25rem;
    `,
    pcOnly(css`
      font-size: 3rem;
    `),
  ],
};

// color
export const bgColor = (color: string) =>
  css`
    background-color: ${color};
  `;

// concrete

export const limitWidth = (width: number = 960) => css`
  max-width: ${width}px;
  padding: 0 ${spacing(2)}; // === 16px
  margin: 0 auto;
`;

export const paper = css`
  background-color: white;
  border-radius: 4px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.02), 0 8px 32px rgba(0, 0, 0, 0.1);
`;

参考記事(公式Doc)

14
3
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
14
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?