Laravel Mixを使って、CSSフレームワークをカスタマイズする
spectre.cssはコード量がとても少ないのに、たくさんのコンポーネントをサポートしている、とても優秀なCSSフレームワークです。 でも、実際に使っていると、flexboxのjustify-contentのクラスがなかったりして、物足りなさを感じました。 そこで、Tachyonsというフレームワークからflexboxの定義を追加することにしました。
TachyonsはUtilityファーストで、非常に直行性が高いCSSフレームワークです。 プロパティとプロパティ値の組み合わせに、推測可能なクラス名が割り当てられます。 例えば、flexboxのjustify-content: space-around;を使う場合には、クラスにjustify-aroundを指定することで実現できます。
SCSSのファイルもきれいに分割されていて、メンテナンスが高く、簡単にspectre.cssにTachyonsのflexboxの定義を追加することができました。 そこで、その設定を紹介します。
まず、webpack.mix.jsです。サイト共通のcssとしてapp.cssを、のみログのcssとしてnomilog.cssをそれぞれpublic/css以下に出力します。
mix.sass('resources/sass/app.scss', 'public/css')
.sass('resources/sass/nomilog.scss', 'public/css')
.scripts(['node_modules/zxcvbn/dist/zxcvbn.js'], 'public/js/zxcvbn.js')
.scripts(['resources/js/password_validation.js'], 'public/js/password_validation.js');
次にサイト共通のcssを出力するapp.scssです。 本来は、node_modules以下にあるspectre.scssを読み込むのですが、ファイルを修正するので、ローカルにコピーしています。
@import 'variables';
@import 'common';
// Tachyons' Flexbox
@import 'node_modules/tachyons-sass/scss/_flexbox';
// Spectre
// @import "node_modules/spectre.css/src/spectre";
@import "spectre";
app.scssで最初に読み込んでいる_variables.scssには、色などのカスタマイズを設定しています。 z-indexはどれがどの値なのかひと目でわかるように変数にしています。 また、Tachyonsの_flexbox.scssをコンパイルするのに必要な$breakpointも、ここで定義しています。
$primary-color: #0277bd;
$primary-color-dark: darken($primary-color, 3%);
$primary-color-light: lighten($primary-color, 3%);
$light-color: #fff;
$top-menu-z-index: 2;
$bottom-menu-z-index: 2;
// Used in spectre and flexbox by tachyons
$breakpoint-not-small: '(min-width: 559px)' !default;
$breakpoint-medium: '(min-width: 560px) and (max-width: 959px)' !default;
$breakpoint-large: '(min-width: 960px)' !default;
_commons.scssは、独自に追加したクラスを定義します。
.d-table {
display: table;
}
.d-table-cell {
display: table-cell;
}
.nowrap {
white-space: nowrap;
}
.overflow-x-scroll {
overflow-x: scroll;
}
.overflow-y-scroll {
overflow-y: scroll;
}
Tachyonsの_flexbox.scssを読み込みます。
// Converted Variables
// Custom Media Query Variables
/*
FLEXBOX
Media Query Extensions:
-ns = not-small
-m = medium
-l = large
*/
.flex { display: flex; }
.inline-flex { display: inline-flex; }
spectre.scssを読み込みます。_layout.scssの中身を修正したいので、_layout.scss以外はオリジナルのscssを読み込むように修正します。 まず、以下がオリジナルのspectre.scssです。
// Variables and mixins
@import "variables";
@import "mixins";
/*! Spectre.css v#{$version} | MIT License | github.com/picturepan2/spectre */
// Reset and dependencies
@import "normalize";
@import "base";
// Elements
@import "typography";
@import "asian";
@import "tables";
@import "buttons";
@import "forms";
@import "labels";
@import "codes";
@import "media";
// Layout
@import "layout";
@import "hero";
@import "navbar";
// Components
@import "accordions";
@import "avatars";
@import "badges";
@import "breadcrumbs";
@import "bars";
@import "cards";
@import "chips";
@import "dropdowns";
@import "empty";
@import "menus";
@import "modals";
@import "navs";
@import "pagination";
@import "panels";
@import "popovers";
@import "steps";
@import "tabs";
@import "tiles";
@import "toasts";
@import "tooltips";
// Utility classes
@import "animations";
@import "utilities";
次に修正後のspectre.sccsです。 _layout.sccsについては、ローカルのものを読み込み、それ以外については、オリジナルのファイルを読み込むように設定しています。
// Variables and mixins
@import "node_modules/spectre.css/src/variables";
@import "node_modules/spectre.css/src/mixins";
/*! Spectre.css v#{$version} | MIT License | github.com/picturepan2/spectre */
// Reset and dependencies
@import "node_modules/spectre.css/src/normalize";
@import "node_modules/spectre.css/src/base";
// Elements
@import "node_modules/spectre.css/src/typography";
@import "node_modules/spectre.css/src/asian";
@import "node_modules/spectre.css/src/tables";
@import "node_modules/spectre.css/src/buttons";
@import "node_modules/spectre.css/src/forms";
@import "node_modules/spectre.css/src/labels";
@import "node_modules/spectre.css/src/codes";
@import "node_modules/spectre.css/src/media";
// Layout
@import "layout";
@import "node_modules/spectre.css/src/hero";
@import "node_modules/spectre.css/src/navbar";
// Components
@import "node_modules/spectre.css/src/accordions";
@import "node_modules/spectre.css/src/avatars";
@import "node_modules/spectre.css/src/badges";
@import "node_modules/spectre.css/src/breadcrumbs";
@import "node_modules/spectre.css/src/bars";
@import "node_modules/spectre.css/src/cards";
@import "node_modules/spectre.css/src/chips";
@import "node_modules/spectre.css/src/dropdowns";
@import "node_modules/spectre.css/src/empty";
@import "node_modules/spectre.css/src/menus";
@import "node_modules/spectre.css/src/modals";
@import "node_modules/spectre.css/src/navs";
@import "node_modules/spectre.css/src/pagination";
@import "node_modules/spectre.css/src/panels";
@import "node_modules/spectre.css/src/popovers";
@import "node_modules/spectre.css/src/steps";
@import "node_modules/spectre.css/src/tabs";
@import "node_modules/spectre.css/src/tiles";
@import "node_modules/spectre.css/src/toasts";
@import "node_modules/spectre.css/src/tooltips";
// Utility classes
@import "node_modules/spectre.css/src/animations";
@import "node_modules/spectre.css/src/utilities";
Tachyonsでは、_variables.scssに3つのブレイクポイントが設定されています。
$breakpoint-not-small: '(min-width: 30em)' !default;
$breakpoint-medium: '(min-width: 30em) and (max-width: 60em)' !default;
$breakpoint-large: '(min-width: 60em)' !default;
一方、spectre.cssでは、_variables.scssに6つのブレイクポイントが設定されています。
$size-xs: 480px !default;
$size-sm: 600px !default;
$size-md: 840px !default;
$size-lg: 960px !default;
$size-xl: 1280px !default;
$size-2x: 1440px !default;
こんなにたくさんのブレイクポイントは必要ないので、spectre.cssのブレイクポイントをTachyons側に合わせて減らします。 すでに_variables.scssに設定済みです。最終的に、以下のように_layout.scssを修正しました。
// Layout
.container {
margin-left: auto;
margin-right: auto;
padding-left: $layout-spacing;
padding-right: $layout-spacing;
width: 100%;
$grid-spacing: ($layout-spacing / ($layout-spacing * 0 + 1)) * $html-font-size;
// &.grid-ns {
// max-width: $grid-spacing * 2 + $size-ns;
// }
&.grid-m {
max-width: $grid-spacing * 2 + $size-m;
}
&.grid-l {
max-width: $grid-spacing * 2 + $size-l;
}
}
// Responsive breakpoint system
.show-ns,
.show-m,
.show-l {
display: none !important;
}
// Responsive grid system
.columns {
display: flex;
flex-wrap: wrap;
margin-left: -$layout-spacing;
margin-right: -$layout-spacing;
&.col-gapless {
margin-left: 0;
margin-right: 0;
& > .column {
padding-left: 0;
padding-right: 0;
}
}
&.col-oneline {
flex-wrap: nowrap;
overflow-x: auto;
}
}
.column {
flex: 1;
max-width: 100%;
padding-left: $layout-spacing;
padding-right: $layout-spacing;
&.col-12,
&.col-11,
&.col-10,
&.col-9,
&.col-8,
&.col-7,
&.col-6,
&.col-5,
&.col-4,
&.col-3,
&.col-2,
&.col-1,
&.col-auto {
flex: none;
}
}
.col-12 {
width: 100%;
}
.col-11 {
width: 91.66666667%;
}
.col-10 {
width: 83.33333333%;
}
.col-9 {
width: 75%;
}
.col-8 {
width: 66.66666667%;
}
.col-7 {
width: 58.33333333%;
}
.col-6 {
width: 50%;
}
.col-5 {
width: 41.66666667%;
}
.col-4 {
width: 33.33333333%;
}
.col-3 {
width: 25%;
}
.col-2 {
width: 16.66666667%;
}
.col-1 {
width: 8.33333333%;
}
.col-auto {
flex: 0 0 auto;
max-width: none;
width: auto;
}
.col-mx-auto {
margin-left: auto;
margin-right: auto;
}
.col-ml-auto {
margin-left: auto;
}
.col-mr-auto {
margin-right: auto;
}
@media #{$breakpoint-not-small} {
.col-ns-12,
.col-ns-11,
.col-ns-10,
.col-ns-9,
.col-ns-8,
.col-ns-7,
.col-ns-6,
.col-ns-5,
.col-ns-4,
.col-ns-3,
.col-ns-2,
.col-ns-1,
.col-ns-auto {
flex: none;
}
.col-ns-12 {
width: 100%;
}
.col-ns-11 {
width: 91.66666667%;
}
.col-ns-10 {
width: 83.33333333%;
}
.col-ns-9 {
width: 75%;
}
.col-ns-8 {
width: 66.66666667%;
}
.col-ns-7 {
width: 58.33333333%;
}
.col-ns-6 {
width: 50%;
}
.col-ns-5 {
width: 41.66666667%;
}
.col-ns-4 {
width: 33.33333333%;
}
.col-ns-3 {
width: 25%;
}
.col-ns-2 {
width: 16.66666667%;
}
.col-ns-1 {
width: 8.33333333%;
}
.col-ns-auto {
width: auto;
}
.hide-ns {
display: none !important;
}
.show-ns {
display: block !important;
}
}
@media #{$breakpoint-medium} {
.col-m-12,
.col-m-11,
.col-m-10,
.col-m-9,
.col-m-8,
.col-m-7,
.col-m-6,
.col-m-5,
.col-m-4,
.col-m-3,
.col-m-2,
.col-m-1,
.col-m-auto {
flex: none;
}
.col-m-12 {
width: 100%;
}
.col-m-11 {
width: 91.66666667%;
}
.col-m-10 {
width: 83.33333333%;
}
.col-m-9 {
width: 75%;
}
.col-m-8 {
width: 66.66666667%;
}
.col-m-7 {
width: 58.33333333%;
}
.col-m-6 {
width: 50%;
}
.col-m-5 {
width: 41.66666667%;
}
.col-m-4 {
width: 33.33333333%;
}
.col-m-3 {
width: 25%;
}
.col-m-2 {
width: 16.66666667%;
}
.col-m-1 {
width: 8.33333333%;
}
.col-m-auto {
width: auto;
}
.hide-m {
display: none !important;
}
.show-m {
display: block !important;
}
}
@media #{$breakpoint-large} {
.col-l-12,
.col-l-11,
.col-l-10,
.col-l-9,
.col-l-8,
.col-l-7,
.col-l-6,
.col-l-5,
.col-l-4,
.col-l-3,
.col-l-2,
.col-l-1,
.col-l-auto {
flex: none;
}
.col-l-12 {
width: 100%;
}
.col-l-11 {
width: 91.66666667%;
}
.col-l-10 {
width: 83.33333333%;
}
.col-l-9 {
width: 75%;
}
.col-l-8 {
width: 66.66666667%;
}
.col-l-7 {
width: 58.33333333%;
}
.col-l-6 {
width: 50%;
}
.col-l-5 {
width: 41.66666667%;
}
.col-l-4 {
width: 33.33333333%;
}
.col-l-3 {
width: 25%;
}
.col-l-2 {
width: 16.66666667%;
}
.col-l-1 {
width: 8.33333333%;
}
.col-l-auto {
width: auto;
}
.hide-l {
display: none !important;
}
.show-l {
display: block !important;
}
}
以上で設定は終了しました。npm run production
を実行してコンパイルしましょう。
本来、CSSフレームワークに他のフレームワークを導入することは難しいのでしょうが、TachyonsがUtility FirstのCSSフレームワークであったため、簡単に切り出して追加することができました。
コード量も46.0KBから48.9KBへと、わずか2.9KBの増加で済ませることができました。
Laravel Mixは処理をチェーンメソッドでつないで記述することができるので、非常にわかりやすく使いやすかったです。
すばらしい仕組みに感謝です。