0
0

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 3 years have passed since last update.

styled-componentsで条件付きmixinをする方法

Last updated at Posted at 2021-11-27

はじめに

styled-componentsで条件付きレンダリングをする場合、以下のように、項目ごとに場合分けをする例をよく見ます
基本はこれで問題ないのですが、場合分けの種類が増えて来ると、管理が面倒になります。
この場合、場合分けの条件ごとにclassのようにスタイリングをまとめたいと思うのが自然だと思います。

そこで、複数の項目をclassの有無のように一気に設定した場合についてわからなかったので、調べました。

const Wrapper = styled.div.attrs((props) => ({
  ...props,
}))`
background: ${(props) => (props.hoge ? '#fff' : '#000')};
`

やりたいこと

以下のようにpropsの条件一つでスタイリングが大きく変わる場合を想定します。
上の場合と下の場合で、個別にcssを管理することを目標とします。

b.gif

まずはデフォルトの状態を準備

まずは、props.typeに何も指定していない、アニメーションのない状態を実装します。

import React, { VFC } from "react";
import styled from "styled-components";

export type ButtonProps = {
  className?: string;
  type?: "bgleft" | "bgright";
};

const Button: VFC<ButtonProps> = ({ className, type }: ButtonProps) => {
  return (
    <WrapperButton className={className} type={type}>
      <span>ボタン</span>
    </WrapperButton>
  );
};

const WrapperButton = styled.a.attrs((props) => ({ ...props }))`
  /* default */
  position: relative;
  overflow: hidden;

  text-decoration: none;
  display: inline-block;

  border: 1px solid #555;
  padding: 10px 30px;
  text-align: center;
  outline: none;
  transition: ease 0.2s;

  span {
    position: relative;
    z-index: 3;
    color: #333;
  }
  :hover {
    span {
      color: #fff;
    }
  }
`;

export default Button;

MixInの定義

次に左右から動作するアニメーションをMixInで定義します。
最初に、styled-componenstからcssをインポートします。

import React, { VFC } from "react";
import styled, { css } from "styled-components"; //ここ

次にMixInを定義します。
それぞれ、左右から動作する部分のcssです。

const MixInBgLeft = css`
  &:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    background-color: #333;
    width: 100%;
    height: 100%;
    transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
    transform: scale(0, 1);
    transform-origin: right top;
  }

  &:hover:before {
    transform-origin: left top;
    transform: scale(1, 1);
  }
`;

const MixInBgRight = css`
  &:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    background-color: #333;
    width: 100%;
    height: 100%;
    transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
    transform: scale(0, 1);
    transform-origin: left top;
  }

  &:hover:before {
    transform-origin: right top;
    transform: scale(1, 1);
  }
`;

条件付きでレンダリング

最後にstyledの中にMixInを条件付きで追加します。

const WrapperButton = styled.a.attrs((props) => ({ ...props }))`
   //....省略

  /* typeにbgleftを選択した場合 */
  ${(props) => props.type === "bgleft" && MixInBgLeft}

  /* typeにbgrightを選択した場合 */
  ${(props) => props.type === "bgright" && MixInBgRight}
`;


このbuttonを使う側は以下のようにして使います。

import Button from '***'


<Button type="bgleft" />
// or
<Button type="bgright" />

最後に

今回の例だと、最初に紹介した、項目ごとに場合分けする場合でもよかったかも知れませんが、props.typeが増える場合を考えると、このような方ほでの実装も悪くはないかも知れません。
もっと良い方法があればぜひ共有してください。

最後に全部まとめたコードを貼ります。

import React, { VFC } from "react";
import styled, { css } from "styled-components";

export type ButtonProps = {
  className?: string;
  type?: "bgleft" | "bgright";
};

const Button: VFC<ButtonProps> = ({ className, type }: ButtonProps) => {
  return (
    <WrapperButton className={className} type={type}>
      <span>ボタン</span>
    </WrapperButton>
  );
};

const MixInBgLeft = css`
  &:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    background-color: #333;
    width: 100%;
    height: 100%;
    transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
    transform: scale(0, 1);
    transform-origin: right top;
  }

  &:hover:before {
    transform-origin: left top;
    transform: scale(1, 1);
  }
`;

const MixInBgRight = css`
  &:before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    background-color: #333;
    width: 100%;
    height: 100%;
    transition: transform 0.6s cubic-bezier(0.8, 0, 0.2, 1) 0s;
    transform: scale(0, 1);
    transform-origin: left top;
  }

  &:hover:before {
    transform-origin: right top;
    transform: scale(1, 1);
  }
`;

const WrapperButton = styled.a.attrs((props) => ({ ...props }))`
  /* default */
  position: relative;
  overflow: hidden;

  text-decoration: none;
  display: inline-block;

  border: 1px solid #555;
  padding: 10px 30px;
  text-align: center;
  outline: none;
  transition: ease 0.2s;

  span {
    position: relative;
    z-index: 3;
    color: #333;
  }
  :hover {
    span {
      color: #fff;
    }
  }

  /* typeにbgleftを選択した場合 */
  ${(props) => props.type === "bgleft" && MixInBgLeft}

  /* typeにbgrightを選択した場合 */
  ${(props) => props.type === "bgright" && MixInBgRight}
`;

export default Button;
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?