Help us understand the problem. What is going on with this article?

(SCSS)1px刻みでmargin,paddingを指定する専用のクラスを大量に自動生成する

More than 1 year has passed since last update.

はじめに

サイト制作の際にコンテンツ、パーツ間の幅の指定にpadding,marginはかなり使用頻度が高いものです。
各パーツごとのクラスセレクタ(.headerとか.sidebar-left等)を作成して個別にmargin,paddingを当てる方法もありますが、使用頻度の高い分Bootstrapのマルチクラス設計的な形でmarginやpaddingだけを適用する専用のセレクタを作成したほうが良い場合があります。

例えば、ヘッダーパーツが検索結果ページでだけmarginの値が異なる場合、別のクラスセレクタを作成してセレクタを上書きする方法があります。

各パーツごとのクラスセレクタを作成して個別にmargin,paddingを当てる.html
<style>
.header {
 margin-top: 50px;
}
//検索結果ページに当てるセレクタ
.header-result-page {
  margin-top: 100px;
}
</style>

<div class="header header-result-page">
  ヘッダーコンテンツ
</div>

しかし、上書きをすると使われないセレクタが増える結果になったり、後のアップデートでまた別のページでもmarginが変わるといった際に別のセレクタを作成する必要が出てきます。

そこで、SCSSのfor文を利用してmargin,paddingを指定するクラスセレクタを量産します。

コード

Margin

margin.scss
// 指定するmarginの範囲を定義
$margin-min: 1;
$margin-max: 100;

 @for $i from $margin-min through $margin-max {
     .mt#{$i} {
         margin-top: #{$i}px;
     }
     .mr#{$i} {
         margin-right: #{$i}px;
     }
     .mb#{$i} {
         margin-bottom: #{$i}px;
     }
     .ml#{$i} {
         margin-left: #{$i}px;
     }
 }

出力すると以下のようになります

margin.css
 .mt1 {
 margin-top: 1px;
}
 .mr1 {
 margin-right: 1px;
}
 .mb1 {
 margin-bottom: 1px;
}
.ml1 {
 margin-left: 1px;
}
//以下$margin-maxの値まで量産される

Padding

paddingもmarginと同様で、名称を変えるだけで大丈夫です。

padding.scss
// 指定するpaddingの範囲を定義
$padding-min: 1;
$padding-max: 100;

 @for $i from $padding-min through $padding-max {
     .pt#{$i} {
         padding-top: #{$i}px;
     }
     .pr#{$i} {
         padding-right: #{$i}px;
     }
     .pb#{$i} {
         padding-bottom: #{$i}px;
     }
     .pl#{$i} {
         padding-left: #{$i}px;
     }
 }

出力すると以下のようになります

padding.css
 .pt1 {
 padding-top: 1px;
}
 .pr1 {
 padding-right: 1px;
}
 .pb1 {
 padding-bottom: 1px;
}
.pl1 {
 padding-left: 1px;
}
//以下$padding-maxの値まで量産される

上記のコードでmargin,paddingが量産できました。
あとは適用箇所に応じてクラスセレクタをHTML要素につけていきます。

レスポンシブに対応したい場合

上記のコードで、例えば「検索結果ページだけヘッダーパーツのmargin-topを50pxにしたい」といった場合に.mt50を該当の要素に適用させるだけでmarginの上書きをする必要がなくなります。
しかし、上記のコードの場合、レスポンシブレイアウトにした時に問題が出てきます。
レスポンシブレイアウトは大抵、PCとモバイルで大幅にデザインが異なるためです。
そのため、「検索結果ページのヘッダーパーツはPCではmargin-topが50pxでいいが、モバイルではmargin-topを14pxにしたい」といった場合や、「モバイルではmargin-topをemにしたい」といった形でそもそもの単位が異なる要件にも対応する必要が出てきます。

そこで、そのような要件に対応するmixinと関数を作成します。
1. 単位をpxから別の単位に変換する関数
2. レスポンシブに対応したクラスセレクタを生成するmixin

1.単位をpxから別の単位に変換する関数

まずは現在のpxの単位を別の単位に変換する関数を作成します。
ここではemに変換する関数を作成します。

px-to-em.scss
// @desc - pxをemに変換します。
// @param {Number} $self - 指定したいpx。
// @param {Number} $parent - 基準となるpx。
// @example - _em(15px, 30px) => 0.5em
@function _em($self, $parent:14) {
  @return ($self / $parent) * 1em;
}

2.レスポンシブに対応したクラスセレクタを生成するmixin

次にレスポンシブに対応したクラスセレクタを生成します。
これはBootstrapの様な形で".col-sm-(n)"とか".col-lg-(n)"といったセレクタを有効にするブレイクポイント(smやlg)を指す文字列を加えることで対応していきます。

先ずはブレイクポイントを定義します。min-widthとmax-width両方に対応できるようにします。

breakpoint.scss
// min-width
$breakpoint-up: (
  'sm': 'screen and (min-width: 400px)',
  'md': 'screen and (min-width: 768px)',
  'lg': 'screen and (min-width: 1000px)',
  'xl': 'screen and (min-width: 1200px)',
) !default;

// max-width
$breakpoint-down: (
  'sm': 'screen and (max-width: 399px)',
  'md': 'screen and (max-width: 767px)',
  'lg': 'screen and (max-width: 999px)',
  'xl': 'screen and (max-width: 1199px)',
) !default;

定義を上書きする可能性も踏まえ、 !defaultを使用したほうが良いです。

次に、定義したブレイクポイント毎にクラスセレクタを作成するmixinです。
min-widthで作成するかmax-widthでセレクタを作成するかをわかりやすくするため、別々の名称をつけます。

responsive.scss
// @desc - class名とルールセットを指定することで、レスポンシブに対応したスタイルを生成します。
// @param {String} - 共通のclass名をクウォートとドットをつけて渡します(e.g. `.foo`)。
// @param {Strind} - 使用するブレイクポイントを定義した変数を渡します。
// @example
// @include _responsive(".display-none") {
//   display: none;
// }
// @example css - CSS output
// .display-none {
//   display: none;
// }
// @media screen and (min-width: 400px) {
//   .display-none-sm {
//     display: none;
//   }
// }
@mixin _responsive($class, $bp: $_breakpoint-up) {
  #{$class} {
    @content;
  }
  @each $suffix, $value in $bp {
    @media #{$value} {
      #{$class}-#{$suffix} {
        @content;
      }
    }
  }
}

@mixin _responsive_down($class, $bp: $_breakpoint-down) {
  #{$class} {
    @content;
  }
  @each $suffix, $value in $bp {
    @media #{$value} {
      #{$class}-#{$suffix} {
        @content;
      }
    }
  }
}

レスポンシブ対応のmargin,paddingを指定するクラスセレクタを量産する

上記関数及びmixinを利用してレスポンシブに対応したコードを量産します。(paddingは名称を変更するのみで大丈夫なため省略)

margin.scss
// 指定するmarginの範囲を定義
$margin-min: 1;
$margin-max: 100;

 @for $i from $margin-min through $margin-max {
     @include _responsive(".mt#{$i}") {
         margin-top: #{$i}px;
     }
     @include _responsive_down(".mt#{$i}em-down") {
         margin-top: _em($i);
     }
     @include _responsive(".mr#{$i}") {
         margin-right: #{$i}px;
     }
     @include _responsive_down(".mr#{$i}em-down") {
         margin-right: _em($i);
     }
     @include _responsive(".mb#{$i}") {
         margin-bottom: #{$i}px;
     }
     @include _responsive_down(".mb#{$i}em-down") {
         margin-bottom: _em($i);
     }
     @include _responsive(".ml#{$i}") {
         margin-left: #{$i}px;
     }
     @include _responsive_down(".ml#{$i}em-down") {
         margin-left: _em($i);
     }
 }

上記コードではpxはmin-widthを、emはmax-widthを使用しています。
pxの方にmax-widthでブレイクポイントを作りたい場合はmixinを"responsive_down"にして作成します。
emの方にmin-widthでブレイクポイントを作りたい場合はmixinを"
repsonsive"にして作成します。

以下のように出力されます。

margin.css
.mt1 {
  margin-top: 1px;
}

@media screen and (min-width: 400px) {
  .mt1-sm {
    margin-top: 1px;
  }
}

@media screen and (min-width: 768px) {
  .mt1-md {
    margin-top: 1px;
  }
}

@media screen and (min-width: 1000px) {
  .mt1-lg {
    margin-top: 1px;
  }
}

@media screen and (min-width: 1200px) {
  .mt1-xl {
    margin-top: 1px;
  }
}

//一部省略

.mt1em-down {
  margin-top: 0.07143em;
}

@media screen and (max-width: 399px) {
  .mt1em-down-sm {
    margin-top: 0.07143em;
  }
}

@media screen and (max-width: 767px) {
  .mt1em-down-md {
    margin-top: 0.07143em;
  }
}

@media screen and (max-width: 999px) {
  .mt1em-down-lg {
    margin-top: 0.07143em;
  }
}

@media screen and (max-width: 1199px) {
  .mt1em-down-xl {
    margin-top: 0.07143em;
  }
}
//以下$margin-maxの値まで量産される

これで、「検索結果ページのヘッダーパーツはPCではmargin-topが50pxでいいが、モバイルではmargin-topを14pxにしたい」といった場合の対応や、「モバイルではmargin-topをemで表示したい」といった単位の異なる要件に対応できます。

ex.html
<!-- PCサイズ(min-width: 768px)では50px、SPサイズ(max-width: 767px)では14pxで表示 -->
<div class="header mt50-md mt14-down-md">
コンテンツ
</div>

<!-- PCサイズ(min-width: 768px)では50px、SPサイズ(max-width: 767px)では14pxをemに変換して表示 -->
<div class="header-result mt50-md mt14em-down-md">
コンテンツ
</div>

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした