Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all 8541 articles
Browse latest View live

CSSのコードレビューのときに見ていること

$
0
0

社内のレビューで気をつけていることを書き出してみたらかなり一般的な内容だったので、せっかくだしQiitaに投稿します。

なお👀のマークをつけている項目は「環境が整っていなければ確認しているけどフォーマッターに任せたい」項目です。

それでは早速行きましょう!

👀誤字がないこと・存在しないプロパティや値を指定していないこと

  • プロパティや値の誤字はフォーマッターでチェックします
  • class名の誤字はフォーマッターでは拾えないと思うのでスペルチェッカーを入れておきます
NG例
/* 存在しないプロパティを指定している */.foo{wide:100px;}/* 綴りが間違っている */.informetion{color:red;}
OK例
/* プロパティが存在している */.foo{width:100px;}/* 綴りが正しい */.information{color:red;}

👀スペース、改行、インデントがルール通りに統一されていること

  • ※ルールがあることが前提です。
    • ない場合は作るか、stylelint-config-standard等のスタンダードっぽい設定に乗っかります。
    • OK例は、このルールが最高!と押し付けるつもりはなく、全体を通して統一されていることが大事です。
  • 動作に影響はないですが、見辛いので統一しましょう。
NG例
/* {}の前後のスペースや改行が揃っていない *//* :や;の前後のスペースの有無が揃っていない *//* インデントが揃っていない */.foo{width:100px;}.bar{color:red;}.baz{display:flex;}
OK例
/* スペース、改行、インデントが揃っている */.foo{width:100px;}.bar{color:red;}.baz{display:flex;}

👀カラーコードの書き方が統一されていること

  • ルールがあること前提です。
  • どの書き方が良いというつもりはありません。チーム内で決めたルールに沿うことが大事です。
NG例
/* 省略するしないが混在している *//* 大文字小文字が混在している */.foo{color:#FF0000;}.bar{color:#0f0;}
OK例
/* 統一されている */.foo{color:#f00;}.bar{color:#0f0;}

👀重複したセレクタやプロパティがないこと

  • 重複している=打ち消しが発生しているので、ただただBADです
NG例
/* .fooが重複している *//* .barの中のcolorが重複している */.foo{width:100px;}.bar{color:red;font-size:14px;color:blue;}.foo{width:200px;}
OK例
/* 重複ナシ */.foo{width:200px;}.bar{color:blue;font-size:14px;}

class名やid名がルールに則って命名されていること

  • こちらもルール自体は何でも良いのですが、ルールに則っていることが大事です。
    • スネークケースが良いとかキャメルケースが良いとかBEMが良いとか、そういうのはありません。
    • オレオレなルールであってもちゃんと統一されていれば(コードレビューの観点としては)問題ありません。
  • 意外とフォーマッターで解決できなかったので目視しています。
    • 出来るやり方をしっている人がいたら教えてください🙇‍♂️
NG例
/* MindBEMdingとローワーキャメルケースが混在している */.foo__bar{width:100px;}.fooBaz{color:red;}
OK例
/* MindBEMdingで統一されている */.foo__bar{width:100px;}.foo__baz{color:red;}

👀詳細度が高すぎる指定をしていないこと

  • 基本全部classセレクタで、入れ子もなく指定されているのが分かりやすくて個人的には好きです。
  • とは言え詳細度を上げることが必要な場面もありますので「詳細度が高すぎる指定をしない」という表現にとどめています。
NG例
/* .barが.fooの外に出たら適用されなくなる *//* .barを上書きしたいときに大変 */div.foo.bar{width:100px;}
OK例
/* スタイリングしたいclassそのものだけを記述している */.bar{width:100px;}

プロパティの初期値を書いていないこと

  • これはもしかしたらプロパティによっては明示した方が良いシーンもあるかもしれません。
  • ですが、基本スタンスとしては最初から設定されているものをもう一度書くのは勿体無いと思っています。
NG例
/* 要素セレクタで指定しているのはわかりやすさのためです *//* pのdisplayの初期値はblock */p{display:block;}/* spanのdisplayの初期値はinline */span{display:inline;}

不要な値が書いていないこと

  • 苦労した後がそのまま残っていて、記述が肥大化しているコードを見かけることがあります。
NG例
/* 中央揃えにしたいらしい */.foo{text-align:center;position:absolute;left:0;right:0;margin:auto;}
OK例
/* 中央揃えになっている */.foo{margin:auto;}

変数が存在するときにベタで書いていないこと

  • SASSの変数、あるいはCSS変数などを使っている場合のみの話です
NG例
/* 定義済みの値を手で書いている *//* variables.scss */$main-color:#f00;/* styles.scss */.foo{color:#f00;}
OK例
/* 定義済みの値を手で書いている *//* variables.scss */$main-color:#f00;/* styles.scss */.foo{color:$main-color;}

マジックナンバーを使っていないこと

  • あとから見返したときに謎が謎を呼ぶので使わない。
  • 何かの計算結果の値であるならその計算式をコメントに書く。
NG例
/* CSSだけで作るアコーディオン、みたいなときに使われがちなコード(若干例が悪い気もする) */.foo{max-height:0;overflow:hidden;}/* この200pxはどこから出てきたのか分からない */.bar:checked+.foo{max-height:200px;}
OK例
/* 理由がコメントで書いてある */.foo{max-height:0;/* アコーディオンが閉じているので高さが0になるように指定 */overflow:hidden;}.bar:checked+.foo{max-height:200px;/* アコーディオンが開いたとき、中に高さ50pxのメニューx4つ存在しているので高さが200px */}

👀適切なベンダープレフィックスを付与していること

  • 「適切な」というのは、過不足のないベンダープレフィックスを付与するということです。
NG例
/* IE11で崩れる */.foo{display:grid;grid-template-columns:100px1fr100px;}/* 存在しないベンダープレフィックスを書いている */.bar{-ms-animation:someAnimation300msease;-webkit-animation:fadeIn1sease;animation:someAnimation300msease;}
OK例
/* IE11でも大丈夫 */.foo{display:-ms-grid;display:grid;-ms-grid-columns:100px1fr100pxgrid-template-columns:100px1fr100px;}/* 必要なベンダープレフィックスだけを書いている */.bar{-webkit-animation:fadeIn1sease;animation:someAnimation300msease;}

打ち消して実装せず、追加して実装していること

  • 例はちょっと極端ですが、普段だと@extendを使うときなどにも見ていることです。
  • CSSの性質上、打ち消しをたくさん発生させるよりは後から必要なものを付け加えた方が後々楽なことが多いです。
NG例
/* 段落ごとに上に10pxの余白をつけるつもりで指定 */p{margin-top:10px;}/* 余白の要らない段落には、全てに打ち消す記述をつけるハメに */.paragraph{margin-top:0;font-size:14px;}.headline{margin-top:0;font-size:20px;color:#f00;}
OK例
/* 全体にかかるmargin-topを消したことで余計な記述がなくなる */.paragraph{font-size:14px;}.headline{font-size:20px;color:#f00;}

presentational componentっぽい単位のクラスに余白を設定していないこと

  • 急にちょっとややこしくなりましたが、平たく言えば「ボタンやカードなど、単体の要素に余白を記述していないか」を見ています。
  • 上の話と少し似ていますが、繰り返し使われる単位に余白を設定してしまうとロクなことにならないので注意しています。
NG例
/* .buttonというclassには毎回margin-topがついてしまう */.button{background-color:#f00;color:#fff;padding:10px;margin-top:20px;}
OK例
/* .button自体へのmargin指定はしない */.button{background-color:#f00;color:#fff;padding:10px;}/* ユーティリティ系のclassを用意しておくか、componentとは別な単位のclassを作るかしてマルチクラスでmarginを設定するなど */

まとめ

  • 見ている箇所を挙げましたが、もっと色々あるような気がするので適宜追加していきます。
  • コードをレビューする人だけでなく書く人にもお役に立てたら嬉しいです。
  • フォーマッターは是非とも入れましょう👀

Backstop.jsでクリスマスを少し幸せにするリファクタリング

$
0
0

「見た目に変わりがないこと」を保証するのはつらい

  • 複数人で書いたコードのルールを統一したい
  • ページはできたものの時間の都合でコードがぐちゃぐちゃ
  • 開発環境を移行したい etc...

見た目を全く変化させずに、スタイリングや開発環境などを変更したい状況は度々発生します。
当然ですが、目視でのチェックのみだと高確率で見落としが起こり、事故に繋がりかねません。

目視より安全な方法として、例えばデザインカンプとの比較ならZeplinやGoogleChrome拡張のPerfectPixelなどを使用すれば、単純な目視チェックよりも正確に確認をすることができます。
ただ、デザイン修正などによって比較したいキャプチャの内容が何度も変わってしまうと、その都度キャプチャをとるのはかなりの手間で、とてもつらいです。

本記事ではBackstop.jsというライブラリを使用して、視覚的な変化を「確実かつ少ない労力で避ける」方法についてまとめておきます。

BackstopJS

BackstopJS automates visual regression testing of your responsive web UI by comparing DOM screenshots over time.

具体的には

  • ローカル環境の画面キャプチャを取り
  • コードを修正し
  • 変更後、キャプチャと比較し差分が出ていないかをテスト

ということをしていきます。

細かいことはいいのでとりあえず動かしたい!」という方はまとめからどうぞ。
コマンドや設定などがシンプルなので、少し知識があれば問題なく使用できると思います。

Backstop.jsでできること

簡単なコマンドで視覚的な変更の有無を確認することができます。
実行サンプルは以下で、REFERENCE(キャプチャ)とTEST(開発中の画面)を比較しています。

まずは見た目に差分が出ず、テストを通過するパターン。
_Users_un-t_ieda_work_develop_191218_11ty_sample_backstop_data_html_report_index.html (2).png

試しに画像のmargin-rightを 10px → 20px に変更してみます。
差分は以下のように確認することができます。

_Users_un-t_ieda_work_develop_191218_11ty_sample_backstop_data_html_report_index.html (1).png

画像と文字の改行位置が変わり、 DIFF(差分)が出ていることが確認できます。
Backstop.jsはこのように視覚的な差分を数コマンドで確認することができるツールです。

開発環境

  • node-version 10.15.3
  • Backstop.js 4.4.2

導入編

インストール

公式ではグローバルインストールが推奨されていますが、ローカルでも問題なく使用できます。
自分はプロジェクトルートで

$ npm install backstopjs -D

を実行し、開発環境依存のパッケージとしてモジュールを追加しています。

初期化

インストールが完了したら、

$ backstop init

を実行します。

これでキャプチャの参照先などの設定が書かれたbackstop.jsonと、キャプチャのデータなどをまとめてあるbackstop_dataディレクトリが自動生成されます。
デフォルトではbackstop.jsonの中身は以下のようになっています。

{"id":"backstop_default","viewports":[{"label":"phone","width":320,"height":480},{"label":"tablet","width":1024,"height":768}],"onBeforeScript":"puppet/onBefore.js","onReadyScript":"puppet/onReady.js","scenarios":[{"label":"BackstopJS Homepage","cookiePath":"backstop_data/engine_scripts/cookies.json","url":"https://garris.github.io/BackstopJS/","referenceUrl":"","readyEvent":"","readySelector":"","delay":0,"hideSelectors":[],"removeSelectors":[],"hoverSelector":"","clickSelector":"","postInteractionWait":0,"selectors":[],"selectorExpansion":true,"expect":0,"misMatchThreshold":0.1,"requireSameDimensions":true}],"paths":{"bitmaps_reference":"backstop_data/bitmaps_reference","bitmaps_test":"backstop_data/bitmaps_test","engine_scripts":"backstop_data/engine_scripts","html_report":"backstop_data/html_report","ci_report":"backstop_data/ci_report"},"report":["browser"],"engine":"puppeteer","engineOptions":{"args":["--no-sandbox"]},"asyncCaptureLimit":5,"asyncCompareLimit":50,"debug":false,"debugWindow":false}

色々オプションがありますが最低限の設定として、開発中のローカルサーバーからキャプチャを取りたいので、
"url": "https://garris.github.io/BackstopJS/"をローカルサーバーのURLに変更しておきます。

自分の環境では
"url": "http://localhost:8080"にしていますが、ここは各々の環境に応じてキャプチャを取りたいページのURLを設定しておけば大丈夫です。

キャプチャ

ローカルサーバーを立ち上げた状態で

$ backstop reference

を実行すると
backstop_data/bitmaps_referenceディレクトリ以下にpng形式で画面のキャプチャが保存されます。
デフォルトでは以下の名前で二枚キャプチャが保存されているかと思います。

  • backstop_default_BackstopJS_Homepage_0_document_0_Tablet_view.png
  • backstop_default_BackstopJS_Homepage_0_document_1_PC_view.png

画像名やキャプチャの画面幅など、オプションの設定については後述します。
キャプチャを保存したら、コードの修正を行なっていきます。

テスト

コード修正後、

$ backstop test

を実行することでブラウザが立ち上がり、先ほど保存したキャプチャと修正後のローカル環境が比較されます。

承認

テストで差分が出なかった場合、
または差分が出たが想定される通りの挙動であった場合、

$ backstop approve

を実行することでキャプチャが更新されます。

更新前のキャプチャとテストの結果は
backstop_data/bitmaps_testディレクトリ以下にアーカイブされていきます。

実践編

最初にキャプチャを取る時のみ
$ backstop referenceを使用します。

最初のキャプチャを取った後は、基本的には
1. コード修正
2. 比較 $ backstop test
3. 承認 $ backstop approve

の手順を繰り返すことで、安全にリファクタリングを行うことができます。
簡単でとても嬉しいですね!

カスタマイズ編

ここまでライトにBackstop.jsを使用する方法について書いてきましたが、キャプチャ前の挙動などをもっと細かくオプションで設定することができます。
backstop.jsonの中身を見ればある程度察しがつくかと思うのですが、簡単な設定方法をいくつか書いておきます。

フォームの入力やキャプチャしたいセレクターの選択など、ここに書いていないオプションも設定することができるので、詳細は公式のリファレンスをご確認ください。

複数のビューポートで見比べたいんですが...

"viewports":[{"label":"phone","width":320,"height":480},{"label":"tablet","width":1024,"height":768}]

の部分を変更します。
デフォルトではPhone(320px)とTablet(768px)の2サイズでキャプチャが保存されるようになっています。
自分は基本的にSP(375px), Tablet(768px), PC(1024px)の3サイズで比較を行うように設定しています。
labelはキャプチャの画像名に反映されるので、サイズの追加などに合わせて適宜変更します。

比較したいページが数ページあるんですが...

"scenarios":[{"label":"BackstopJS Homepage","cookiePath":"backstop_data/engine_scripts/cookies.json","url":"https://garris.github.io/BackstopJS/","referenceUrl":"","readyEvent":"","readySelector":"","delay":0,"hideSelectors":[],"removeSelectors":[],"hoverSelector":"","clickSelector":"","postInteractionWait":0,"selectors":[],"selectorExpansion":true,"expect":0,"misMatchThreshold":0.1,"requireSameDimensions":true}]

の箇所に比較したいページを追加していきます。
公式によるとscenariosに必須なのはlabelとurlで、他のオプションは省略可能なようです。

scenarios – This is where you set up your actual tests. The important sub properties are...
scenarios[n].label – Required. Also used for screenshot naming.
scenarios[n].url – Required. Tells BackstopJS what endpoint/document you want to test. This can be an absolute URL or local to your current working directory.
TIP: no other SCENARIO properties are required. Other properties can just be added as necessary

の3ページをチェックしたいなら、以下のように追加、変更していきます。
(以下はオプションを全て省略したパターンです。)

"scenarios":[{"label":"page_index","url":"http://localhost:8080/",},{"label":"page_2019","url":"http://localhost:8080/we_wish_you_a_merry_christmas.html",},{"label":"page_2020","url":"http://localhost:8080/happy_new_year.html",}]

これで設定した3ページ分のキャプチャをとってくれるようになります。
ビューポートの設定もしていれば、ページ数×ビューポート数のキャプチャが保存されます。

hoverの確認もしたいんですが...

  "hoverSelector": "",

にhoverを確認したいセレクターを記述します。

  "hoverSelector": ".content_btn",

のような感じですね。
これでポインタをhoverした状態でキャプチャを取ってくれます。

要素をクリックした後の画面を比較したいのですが

モーダルやアコーディオンなどですね。
hoverと同じような感じですが

  "clinkSelector": "",

にclick時の挙動を確認したいセレクターを記述します。

  "clinkSelector": ".content_btn",

のような感じですね。
これで設定したセレクターをクリック後にキャプチャを取ってくれるようになります。

毎回差分が出てしまう要素があるんですが...

動的に内容が変化するコンテンツなど、キャプチャのたびに見た目が変わってしまう要素は非表示を設定することで対応します。

  "hideSelectors": [],

の中にセレクターを記述することで、要素が非表示になった状態でキャプチャが取られます。

見た目が変わっているのにテストを通っているんですが...

_Users_un-t_ieda_work_develop_191218_11ty_sample_backstop_data_html_report_index.html (4).png

上のキャプチャのように「もっと読む」の部分に差分が出ているはずなのですが、テストを通ることがあります。
backstop.jsonにはどの程度の変化まで許容するかを設定するオプションがあり、差分が設定値より小さかった場合テストをパスします。

  "misMatchThreshold" : 0.1,

の値を変更することで、テストの厳密さを指定することができます。
デフォルトでは0.1%の差分までは許容されており、上のキャプチャだとdiffの値が0.08%となっているためテストをパスしている訳ですね。

試しに"misMatchThreshold" : 0.05で設定してみると
_Users_un-t_ieda_work_develop_191218_11ty_sample_backstop_data_html_report_index.html (9).png

diffの値は変わらず0.08%ですが、テストはパスしなくなります。
(余談なのですが、テスト画面でキャプチャ名の上にポインタをhoverすると、上記のdiff値などの少し細かい情報が見られます。)

令和の差分がちょっとつらいんですが...

来年はもっといい一年になるよう、サンタさんにお願いしておきましょう。

まとめ

導入の流れ

  1. npm install backstopjs -Dでインストール
  2. backstop init→ 設定ファイル(backstop.json)を編集して初期設定
  3. backstop referenceで設定ファイルに記述されたURLの画面をキャプチャ

実践の流れ

  1. コード修正
  2. backstop testでキャプチャと比較
  3. backstop approveでキャプチャを更新
  4. 気がすむまで 4 ~ 6 を繰り返し

これでコードのリファクタリングも安心ですね。
自分で何度もキャプチャと開発環境を見比べる必要が無くなり、クリスマスも少し幸せに過ごせそうです。

残すところ10日程度ですが、皆様良い2019年をお過ごしください。
来年も良いことがありますよう。

cssうんちをストーカーさせてみた

$
0
0

マウスストーカー

マウスに追従するようなアニメーションのことをマウスストーカーというらしい。
というわけでうんち追従させてやんよ💩
単純にマウスカーソルを💩画像にするのは面白くないので、そこは違う形で実装するぞ!

Let's Try

↓画面内でマウスを動かしてみよう!


See the Pen
unchi-stalker
by petapetapeta (@petapetapeta)
on CodePen.


ポイント

html(pug)

htmlでカーソルのポイント用の要素を2つ用意する
1つはその時のマウスポイントの位置を表示する要素(.cursor-small)、もう1つは遅延で追従する要素だ(.cursor-big)

#app.cursor(v-show="!isOut").cursor-small(ref="cursorSmall").cursor-big.unchi(ref="cursorBig")

css(stylus)

デフォルトのカーソルはいらないのでnoneで消してしまいます。
css
body
cursor none !important

.cursor-smallは今回は円、.cursor-bigはうんちのcssを書き、初期に表示されないように画面外にポジションを設定しておきます。

javascript

一番のポイントはmousemoveのイベントでマウス位置を取得し、その位置に.cursor-smallをもってくる+.cursor-bigは前回のマウス位置と現在のマウス位置でちょこっと計算をはさみ、遅延させるようにすることです。
あとはmouseleaveのイベントで要素を非表示にするようにしておくと画面外にマウスカーソルが出た時にうんちが残ってしまうということがなくなるぞ💩

CSSとJavaScriptで3つのタブを作る方法

$
0
0
<!DOCTYPE html><htmllang=jadir="ltr"><head><metacharset="utf-8"><title></title></head><body><style>.tab1{background-color:orange;}.tab2{background-color:green;display:none;}.tab3{background-color:blue;color:white;display:none;}.tab{width:500px;height:100px;clear:left;}.tabcontrola{width:100px;display:block;float:left;border-radius:10px10px00;text-align:center;color:white;}.Tabcontrola:hover{text-decoration:underline;}.tabcontrola:nth-child(1),.tabbodydiv:nth-child(1){background-color:orange;}.tabcontrola:nth-child(2),.tabbodydiv:nth-child(2){background-color:green;}.tabcontrola:nth-child(3),.tabbodydiv:nth-child(3){background-color:blue;}</style><divid="Tabcontrol"class="tabcontrol"><ahref="#tabpage1"class="tab1"onclick="changeTab">タブ1</a><ahref="#tabpage2"class="tab2"onclick="changeTab">タブ2</a><ahref="#tabpage3"class="tab3"onclick="changeTab">タブ3</a></div><divid="tabbody"><divid="tabpage1"class="tab tab1"><p>これはタブ1です。</p></div><divid="tabpage2"class="tab tab2"><p>これはタブ2です。</p></div><divid="tabpage3"class="tab tab3"><p>これはタブ3です。</p></div></div></body><script type="text/javascript">vartabs=document.getElementById('Tabcontrol').getElementsByTagName('a');varpages=document.getElementById('tabbody').getElementsByTagName('div');functionchangeTab(){vartargetid=this.href.substring(this.href.indexOf('#')+1,this.href.length);for(vari=0;i<pages.length;i++){if(pages[i].id==targetid){pages[i].style.display="block";}else{pages[i].style.display="none";}}for(vari=0;i<tabs.length;i++){tabs[i].style.zIndex="0";}this.style.zIndex="10";returnfalse;}for(vari=0;i<tabs.length;i++){tabs[i].onclick=changeTab;}tabs[0].onclick();</script></html>

【ライフハック】「背景とアイコンを同化させニキ」に対抗したい

$
0
0

TL;DR

  • chrome extensionを自作したい!という人向けです。

  • プロフィール画像を微妙に変更するchrome extensionを作る
    (chrome extensoionを自作して、特定のDOMに独自のcssを適用する)

はじめに

以下のようなことが、よくありますね。(?)

IKkgyaXeVVS7soP1576812316_1576812517.png

_アイコン画像が真っ白なので、背景に同化してしまっています。 _

もちろんアイコン画像を何にしようと、その人の勝手なのですが、
これ、Facebookのメッセンジャーでやられてしまうと既読したのかどうかわかりません。

環境

Google Chrome ver.79

やったこと

この記事などを参考に、chromeのextensionを作ります。

ディレクトリ の構成はこんな感じです

chromeExtension/
      ┣ img/
        ┗ icon_img.png
          ┣ chrome_changeColor.css
      ┗ manifest.json

ファイルは以下の通りです

manifest.json
{"name":"iconchnage","version":"1.0.0","manifest_version":2,"description":"iconchnage Chrome Extension","icons":{"32":"img/icon_img.png","48":"img/icon_img.png","128":"img/icon_img.png"},"content_scripts":[{"matches":["https://www.facebook.com/*"],"css":["chrome_changeColor.css"]}]}
chrome_changeColor.css
._4jzq._jf4{background-color:#111;}

Facebookの既読アイコンは Command+Shift+Cで調べると
"._4jzq._jf4" だったので、cssではこれを指定して、バックグラウンドの色を変更しています。

icon_img.pngには適当な画像を置きます。

結果

W8A5DKVye6iRHSE1576814349_1576814382.png

これで既読が補足できます(^ ^)

参考

Chrome拡張の作り方 (超概要)

単一コンポーネントライブラリ内のscoped cssの話

$
0
0

私は本当にscoped cssが好きである。
cssのmodule設計は幾多あるが、だいたいがmoduleのネストで親子間の影響がネックになってmodule粒度が大きくなる傾向があった。
おそらくそういう文脈もあって、そこと向き合ったBEMはみんなに愛されているんだろうと思う。

ブラウザがネイティブでcssにscopeを与えようとした仕様は残念ながら消えてしまったがvue-loaderとかで変換される.vueファイルのようにcssにscopeを与えながら、単一コンポーネントをwebで実現する手段はいくつかある

vueファイルの例
<template><div><pclass="heading">piyo</p></div></template><stylescoped>p{font-weight:bold;}</style>

ここで定義されたpのスタイルは上述の.heading要素にしか当たらないようにコンパイルされる

compile後のコード

変換後のコードを見てみる

<style>p[data-v-12345]{font-weight:bold;}</style><divdata-v-12345><pclass="heading"data-v-12345>piyo</p></div>

するとhtml側にもstyle側にもそれぞれの要素・セレクタの末尾にdata属性が指定されている
セレクタの末尾にコンポーネントごとにユニークな[data-v-xxxx]を付与することでそれだけに効くスタイルができあがるというロジックらしい

簡易loader

踏まえてザルな簡易ローダーを作ってみるとこんな感じ

loader({template:`
    <div>
      <p class="heading">piyo</p>
    </div>
  `,style:`
    p {
      font-weight: bold;
    }
  `});functionloader(component){lethash=String(Date.now());// 大雑把なuuid替わりreturn`
    <style>
      ${transformCSS(component.style,hash)}</style>
    ${transformHTML(component.template,hash)}
  `;}functiontransformHTML(html,hash){letdataAttr=`data-${hash}`;returnhtml.replace(/<([^/]+?)>/mg,(body,$1)=>{return`<${$1}${dataAttr}>`})}functiontransformCSS(style,hash){letdataAttr=`[data-${hash}]`;returnstyle.replace(/([^{])\s*\{\s*([^}]*?)\s*}/mg,(body,$1,$2)=>{letselector=$1.split(',').map(v=>{return`${v.trim()}${dataAttr}`;}).join(',');return`${selector} {
      ${$2}
    }`;})}

出力

<style>p[data-1576831975938]{font-weight:bold;}</style><divdata-1576831975938><pclass="heading"data-1576831975938>piyo</p></div>

selectorには@rule系とかあったり、擬似セレクタもあって末尾に必ずつけていいわけでもないのでcssのちゃんとしたparser使ってtransformした方がいいがおおよそ原理はこんな風になっているらしい。

全部大文字のテキストに text-transform: capitalize が絶対に効かない理由

$
0
0

比較的ベーシックなプロパティであるtext-transformに知らない仕様があったのと、
ちょうどCSSアドベンドカレンダーに空きがあったので投稿します!

text-transformは、文字の大文字小文字の表示を変更するプロパティです。

一番想定される使い方は、欧文の大文字/小文字の変換です。
使えるキーワードと効果は次のように記憶していました。 1

  • uppercase: 大文字に変換
  • lowercase: 小文字に変換
  • capitalize: 単語ごとに先頭を大文字にして、ほかは小文字にする

「何を今更」と思われるかもしれませんが、上記の理解は誤りでした。下のCodePenを見てください。

See the Pen text-transform: capitalize does not effect if the text is all-caps by y_hokkey (@hokkey) on CodePen.

このように、text-transform: capitalizeは、全部大文字のテキストには効果がありません。

これは仕様通りの挙動です。

CSS 1 仕様書

'capitalize'
uppercases the first character of each word
Cascading Style Sheets, level 1

意訳: それぞれの単語の最初の文字を大文字にする。

CSS 2.1 仕様書

capitalize
Puts the first character of each word in uppercase; other characters are unaffected.
Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification

意訳: それぞれの単語の最初の文字を大文字にする。それ以外の文字には影響しない。

CSS Text Module Level 3 仕様書(暫定)

capitalize
Puts the first typographic letter unit of each word, if lowercase, in titlecase; other characters are unaffected.
CSS Text Module Level 3

意訳: それぞれの単語の最初のtypographic letter unitが小文字の場合にそれを大文字にする。それ以外の文字には影響しない。

細かいことは違いますが、一貫して単語の1文字目にしか言及していないことが分かります。
つまり、全部大文字の単語の2文字目以降を小文字にする機能はtext-transform: capitalizeにはありません。

AngularのPipeとは挙動が違う

話が急にAngularへ飛びます。

Angularの標準のPipeである uppercase, lowercase, titlecaseは、表示でなく出力する文字自体を置き換える機能ですが、その変換の挙動はtext-transformと似ています。

しかし、こちらの機能は2文字目以降にも影響してくれるため、

<p>{{ 'HELLO WORLD' | titlecase }}</p>

の出力は

<p>Hello World</p>

と同じになります。最近Angularをよく使っていたため、このtitlecaseの挙動がtext-transform: capitalizeと一緒だと誤解してしまっていた。

CSSだけで常にtitlecaseにする方法はまだない

.alternative-capitalize{text-transform:lowercase;&:first-letter{text-transform:uppercase;}}

なお上記のようなコードで、1文字目だけを常に大文字化することは可能ですが、単語ごとに1文字目を大文字化することはやっぱり実装できません。

おわりに

何年CSS書いてるんだっていう感じですが、非常にベーシックなプロパティすら完全には把握していなかったのが残念でした。これからも謙虚な気持ちで学んでいきたいものです!


  1. full-widthの挙動は記事のテーマと関係しないので話のスコープから外します。 

年末まで毎日webサイトを作り続ける大学生 〜63日目 寿司屋の日常〜

$
0
0

はじめに

こんにちは!@70days_jsです。

寿司屋の日常を作りました。
寿司屋の店主をホバーしたら客が「旨い!」と言うだけですがご了承ください。

63日目。(2019/12/20)
よろしくお願いします。

サイトURL

https://sin2cos21.github.io/day63.html

やったこと

これを作りました。(gif)↓
test3.gif

html↓

<body><h1>寿司屋の日常</h1><imgid="taisyo"src="day63_1.png"alt="寿司屋の大将"/><imgid="sushi"class="sushi"src="day63_2.png"alt="寿司"/><imgid="kyaku"src="day48men.png"alt="客"/><divid="fukidashi"class="fukidashi"></div><div>↑マウスを乗せてね</div></body>

css↓

body{margin:0;}img{width:100px;height:auto;}#kyaku{position:absolute;left:700px;}.sushi{display:none;}.sushiZanmai{animation:go1sease0.5s;animation-fill-mode:forwards;}@keyframesgo{0%{transform:translateX(0px);}100%{transform:translateX(500px);}}.fukidashi{display:none;width:50px;margin-top:10px;position:absolute;left:800px;text-align:center;padding:30px;background-color:black;border-radius:100%;z-index:10;}.fukidashi::after{color:white;content:"旨い!";}.fukidashi::before{left:-5px;margin-top:2px;content:"";position:absolute;display:block;z-index:1;border-style:solid;border-color:transparentblack;border-width:10px10px10px0px;}

JavaScript↓

taisyo.addEventListener("mouseover",function(){sushi.classList.remove("sushi");sushi.classList.add("sushiZanmai");setTimeout(function(){fukidashi.style.display="inline-block";sushi.classList.remove("sushiZanmai");sushi.classList.add("sushi");},2000);});

ほとんどcssで実装しています。
JavaScriptはマウスが乗ったかどうかを検知して、スタイルの付け替えだけしています。

「旨い!」の吹き出しはcssの.fukidashiクラスと::befer, ::afterを用いて作っています。

寿司の移動はanimationを用いています。

寿司の移動↓

@keyframes go {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(500px);
}
}

animation-fill-modeをforwardsにすることで、animation終了時の状態を保持し、animationが終了しても寿司が店主のもとに帰らないようにしています。
客の元に残った寿司はsetTimeoutで2秒後に消えるようになっています。

setTimeout(function() {
fukidashi.style.display = "inline-block";
sushi.classList.remove("sushiZanmai");
sushi.classList.add("sushi");
}, 2000);

感想

こういうちょっとしたアニメーションでも、あるのとないのとではwebサイトの印象が変わるのかなとか思いました。
どう変わるかは分かりませんが

最後まで読んでいただきありがとうございます。明日も投稿しますのでよろしくお願いします。

参考

アイコン素材ダウンロードサイト「icooon-mono」 | 商用利用可能なアイコン素材が無料(フリー)ダウンロードできるサイト | 6000個以上のアイコン素材を無料でダウンロードできるサイト ICOOON MONO

素材を使わせていただきました!


Webサイトってどうやって作るの?(具体的なことを全然知らない超初心者に読んでもらいたい。)

$
0
0

新参者、よろしくお願いします。

 初めまして、今回記事を書かせていただく「じゅのす」です。
始めに僕のプログラミング経験について。
 ほぼ0に等しいです。笑
以前に少しだけ"Python"っていうプログラミング言語を学んでいたのですが、別にそれを使って何か出来るというわけでもなく、もうほとんど覚えていません。

 じゃあ今回記事に何を書くのって?
こんな知識を持たない僕がこれから2週間かけて"Webページ"づくりに挑戦します!(2019年12月20日にこの記事は投稿予定。今日は12月7日。間に合ってるかな・・・。)
 プログラミングに慣れている方からすればそんなの簡単だよって思うかもしれません。でも実際、今の僕にとっては高い壁なんです。以前にも自分の考えを外の世界に飛ばしたいなと思ったのですが、ハードルが高く感じて諦めちゃいました。きっと同じ思い、悩みを持つ人はたくさんいるはずです。
 そんなこんなでこれから毎日少しずつ調べ、勉強し、実行し、この記事とWebページを仕上げていきます!僕は有言実行する男です。きっといいWebページが出来上がり、みなさんが今読んでくださっている頃にはいいものができあがっているはずです!
 ちなみに今回作るWebページの内容は、「イベント用ホームページ」です。
 それではよろしくお願いします。

さぁ、始めよう。

 とは思いながらも、何から始めればいいのか...。いろいろ調べてみたところ、Webページ作成において最も使われている(必要とされている)プログラミング言語は"HTML"と"CSS"ということが分かりました。
 初心者なりにこの2つの言語の特徴を調べてみました。簡単にまとめると、"HTML"は文字を、"CSS"はそこに色や配置のバリエーションを加えてくれるようです。

 う~んまだまだ分からないことが多い、、、とりあえず触れてみます!

勉強開始。

 僕は HTML と CSS を学習するのに Progate のレッスンを使わせて頂くことにしました。周囲の子が使っていて、評判良かったので!(宣伝ではありませんよー。)Progate には様々な言語の学習プログラムがありますが、その中の HTML & CSS コースを進めていきます。

 ひとまず、基本的なHPの構成方法、色や配置の設定方法まで学んだところで実際に試作することにしました!

 んん?学んだコードはどこに書いたらいいの?

 せっかく学んだコードをどこに書けばページが生成されるのか分からん・・・。ここも色々と調べてみたところ、自分でプログラミングに取り組むには開発環境の設定(少しだけ装備を整えるだけです。難しくないよ。)が必要でした。
 その準備の1つであり、先ほどのコードの悩みを解消してくれるのが「テキストエディタ」です。
 簡単に言えば、テキストを編集するツールです。Webページってたくさーーーんの文字(テキスト)で構築されています。それらを書き記す媒体としてテキストエディタが存在します。
 数あるテキストエディタの中で、私は「Atom」を使うことにしました。口コミでもだいぶ評判の高いソフトです。(ここも宣伝じゃないですよー。)インストールも二手間ぐらいであっさり終わりました。

 それでは実際にHTMLとCSSのコードを書いていきます!

実践

 どこまでこの記事に詳細を書こうか迷いましたが1個1個コードを記すのも量が膨大になるので、今回は大まかな作成手順と僕がつまづいた点を書いていきます。

 HPの大まかな構成を先に述べておくと、「head」と「body」があります。詳細は下でそれぞれ書きます!

 まずは「head」部分の作成。
 これはホームページの基本情報を設定する場所です。実際にHPを開いたときに見える要素はありませんが、ここを丁寧に書くことでHP内での文字化けの防止や、ネットで検索結果に引っかかるようになります。この辺の作業はHTMLだけで終わります。

 次に「body」部分の作成。
 ここがHPで実際に見る部分になります。今回作成するHPの「body」は「header」、「main」、「footer」の3部構成にすることにしました。

 「header」はページの1番上のスペースを構築してくれます。HPに飛ぶと上の方にページタイトルやログインボタン、色々と目次のようにメニューが設定されている印象がありませんか?それです!
 今回はそこにタイトルだけ設置しました。ここはHTMLで行います。そしてそこに色付けやサイズ変更をCSSで加えてアレンジします。

 「main」はページの大部分を占める主役の部分なので、1番アレンジしがいのある場所です。
 ここにはイベント情報、過去のイベント実績といった情報を盛り込みました。CSSを用いればこれらもただの箇条書きではなく、色付けしたり、グループ分けしたり、フォントを変えてみたりと自由にオシャレできます。文字で出来ているくせになんて素敵なビジュアルを作れるのだろう、と少し感動しました。笑

 だいぶHPらしくなってきました。

 最後に「footer」です。HPの1番下に行くと小さな文字で細かな情報がだぁーっと載っているイメージありません?それです!
 ここにはイベントの運営者情報やお問い合わせといったフォームを設けました。しかし、今回作成するHPの場合はそこまでここに盛り込む内容はありませんでした。

 改めてHPの構成を整理すると、
◯head
◯body
・header
・main
・footer
といった形です。
headとbodyが2本柱、その中にheader、main、footerがある感じです。

本当はここにそのHPを載せたかったのですが、Webサーバーの準備までは間に合わなかったので公開までは行きませんでした。泣

が、約2週間の学習でHPの作成はマスターできた気がします。つまづいた点もありましたが、色々と分かったことも多く、勉強になりました。意外とやる気になれば簡単に出来るものですね!

もっともっとプログラミングスキル高められるよう精進する予定です!良かったらこれを読んでくれているあなたも一緒に!それでは!

読んでくださりありがとうございました。

Lightning Web Components における CSS の基礎と応用

$
0
0

Lightning Web Components (以下、LWC) に対するスタイルの設定方法を、Aura コンポーネントの場合との比較を交えつつ、様々な例を挙げて紹介します。なお、この記事では Salesforce Platform 上 の LWC の開発を前提としています。

Lightning Design System の使用

Salesforce 標準のデザインに沿ったコンポーネントを作成するのであれば、Lightning Design System (LDS)を使用しましょう。カスタムの LWC は Aura と同じく自動的に LDS が適用されますので、LDS の CSS クラスを必要に応じて用いることができます。もちろん、自分でゼロから作り始める前に、Base Componentを組み合わせられないかは先に確認しましょう。

ldsExample.html
<template><!-- 例) slds-p-horizontal_small は左右にパディングを取る。
    https://www.lightningdesignsystem.com/utilities/padding/ --><divclass="slds-p-horizontal_small">
         Hello!
    </div></template>

また、デザイントークンを使用することで、値のハードコーディングを避けることができます。先頭に --lwc-を付与し、リンク先のドキュメントでケバブケースで表記されているトークンを、キャメルケースに置き換えればOKです。

designTokenExample.css
.error{background-color:var(--lwc-colorBackgroundNotificationNew);color:var(--lwc-colorTextError);}
designTokenExample.html
<template><spanclass="error">この文字は赤くなり背景は薄いグレーになります。</span></template>

ちなみに、Spring '20 から Aura で定義したカスタムのデザイントークンが使用できるようになるようです。

カスタムのスタイルを適用

コンポーネント毎に、同じ名称の CSS ファイルを1つだけ含めることができます。その CSS ファイルで定義されたスタイルは、そのコンポーネントに対してのみ適用されます。Aura と異なり THIS CSS クラスを定義する必要はありません。

hello.html
<template><h1>Hello LWC!</h1></template>
hello.css
h1{font-size:2rem;color:rgb(0,112,210);}

親コンポーネントで定義されたスタイルは、子コンポーネントには継承されません。(→ Playground で結果を確認)

hello.html
<template><h1>Hello LWC!</h1><c-child></c-child></template>
child.html
<template><h1>Hello Child!</h1><p>これは子コンポーネントです。</p></template>
hello.css
/* child の h1 はそのまま */h1{font-size:2rem;color:rgb(0,112,210);}

ここで、c-childに外枠を付けるにはどうしたらいいでしょうか。

hello.css
h1{color:rgb(0,112,210);}/* よくない例 */c-child{display:block;border:1pxsolidrgb(200,200,200);}

上記のように、親の CSS に c-childセレクタを定義することもできますが、親のスタイル定義に子のスタイル定義を混ぜてしまうと、見通しが悪くなりメンテナンス性にも欠けます。子コンポーネントのスタイルは、子コンポーネント側で定義しましょう。:host()疑似クラスを用いると、そのコンポーネント自身(ホスト要素) にアクセスすることができます。

child.css
:host{display:block;border:1pxsolidrgb(200,200,200);}

下記のように、カッコとセレクタを使って、ホストのスタイルを状態に応じて変えることもできます。(→ Playgroundで結果を確認)

child.css
:host{display:block;border:1pxsolidrgb(200,200,200);}:host(.active){border-width:3px;}
hello.html
<template><divclass="app"><h1>Hello LWC!</h1><c-child></c-child><c-childclass="active"></c-child><!--これだけ枠が太くなる--><c-child></c-child></div></template>

動的なスタイルの適用

例えば、チェックボックスにチェックを入れたら文字を薄くする、ボタンを押したら特定の要素を非表示にするなど、スタイルを動的に切り替えるにはいくつかの方法があります。

クラスのバインディング

まずはクラスを可変にする方法です。Aura では、マークアップ内に Expression (式)を用いて条件つきスタイルを定義することができました。

AuraDynamicStyleExample.cmp
<aura:component><divclass="{!v.record.isActive__c ? 'slds-section slds-is-open' : 'slds-section'}"><!--中略--></div></aura:component>

LWC では、Aura のように式の中で演算子が利用できないため、JavaScript 側にロジックを記載する必要があります。

lwcDynamicStyleExample.html
<template><divclass={sectionClass}><!--中略--></div></template>
lwcDynamicStyleExample.js(※説明に必要な箇所のみ抜粋)
getsectionClass(){//getter を定義returnthis.record.isActive__c?'slds-section slds-is-open':'slds-section';}

JavaScript に直接 CSS クラス名を書きたくない場合は、<template if:true|false={condition}>で要素を切り替えることもできます。

lwcDynamicStyleExample.html
<template><templateif:true={record.isActive__c}><!-- boolean を返す getter または property を用いる--><divclass="slds-section slds-is-open"><!--中略--></div></template><templateif:false={record.isActive__c}><divclass="slds-section"><!--中略--></div></template></template>

Aura に慣れていると、書きづらい、不便だと感じるかもしれませんが、LWC ではマークアップとロジックが必ず分離されるため安全だという見方ができます。

ループ内での動的なスタイル

ループ内の場合はもう少し工夫が必要です。例えば、Aura コンポーネントだと以下のような場合。

auraIterationDynamicStyleExample.cmp
<aura:component><aura:attributetype="Contact[]"name="contacts"/><aura:iterationitems="{!v.contacts}"var="contact"><divclass="{!contact.isActive__c ? 'slds-section slds-is-open' : 'slds-section'}">...</div></aura:iteration></aura:component>

対応方法1: ループ内要素を子コンポーネント化し、ループアイテムを受けとる

スタイルやロジックは子コンポーネントで定義します。

parent.html
<template><templatefor:each={contacts}for:item="contact"><c-childcontact={contact}key={contact.Id}></c-child></template></template>
child.html
<template><divclass={sectionClass}>...</div></template>
child.js(※説明に必要な箇所のみ抜粋)
@apicontact;getsectionClass(){returnthis.contact.isActive__c?'slds-section slds-is-open':'slds-section';}

対応方法2: ループアイテムにプロパティを追加する

1コンポーネントで済ませたいのであればこちら。@wireを使用している場合は、Property ではなく、Function として Apex を呼び出し、結果を加工すると良いでしょう。

parent.html
<template><templatefor:each={contacts}for:item="contact"><divclass={contact.styleClass}key={contact.record.Id}>{contact.record.Name}</div></template></template>
parent.js(※説明に必要な箇所のみ抜粋)
@wire(getContacts)wiredContacts({error,data}){if(error){// エラー処理}elseif(data){this.contacts=data.map(contact=>{return{record:contact,styleClass:contact.isActive__c?'slds-section slds-is-open':'slds-section'}})}}

実行時のスタイルの追加と削除

DOM を操作してスタイルを制御することもできます。template.querySelector()を用いて要素を特定し、classListからスタイルを追加・削除・切替します。Aura では、aura:idcomponent.find()でコンポーネントを特定して、 $A.util.addClass()$A.util.removeClass()でクラスを制御しました。template.querySelector()では id 属性によるクエリは非推奨のため注意が必要です。

example.html
<template><divid="first">First</div><divclass="unique-class-name">Second</div></template>
example.js
import{LightningElement}from'lwc';exportdefaultclassExampleextendsLightningElement{demoQuerySelector(){//querySelector の例this.template.querySelector('div');// OK. <div>First</div>this.template.querySelectorAll('div');// OK. [<div>First</div>, <div>Second</div>]this.template.querySelector('#first');// NGconstsecondDiv=this.template.querySelector('.unique-class-name');// OK. <div>Second</div>//スタイルを追加する例secondDiv.classList.add('slds-is-open');}}

比較

クラスのバインディング (式を使用したスタイルの適用) はコンポーネントの状態を利用できますが、実行時のスタイルの追加・削除 (DOM 操作) にはそれがありません。しかし、規模が大きい場合はパフォーマンスの観点で DOM 操作に分があるように思います。

ボタンをクリックした際にメッセージの表示を切り替える簡単な例で、書き方の違いを再確認しましょう。

Base Component の内部に対するスタイルの適用

Base Component の中身のスタイルを変更したいシーンがあります。例えば、以下のように、<lightning-textarea>の高さを変更したい場合。

<template><div><h2class="header">テキストエリアの高さを変えたい</h2><lightning-textareaname="input1"label="テキストを入力"class="mytextarea"></lightning-textarea></div></template>
.mytextareatextarea{min-height:300px;}

上記の例では、テキストボックス自体の高さは指定した値になりませんが、これは期待通りの結果です。

dom.png

<lightning-textarea>の実装は Lightning Design System のドキュメントの通りですが、前述の通り、親コンポーネントで定義したスタイルは、子コンポーネントである <lightning-textarea>内の要素には適用されません。

残念ながら、現状これに対するスマートな解決策はありませんが、実装可能な方法には以下があります。

対応方法1: Base Component をカスタムで再定義する

先のドキュメントに基づいて、Base Component を自ら作り直せば、好きなようにスタイルを設定できます。しかし、この方法でコンポーネントを様々なコンテキストで再利用するには、動的なスタイルの実装が増え煩雑になってしまうでしょう。

yourOwnLightningTextarea.html
<divclass="slds-form-element"><labelclass="slds-form-element__label"for="textarea-id-01">Textarea Label</label><divclass="slds-form-element__control"><textareaid="textarea-id-01"class="slds-textarea your_own_class"></textarea></div></div>

対応方法2: style タグを差し込む

これは Base Component に限らない方法で、比較的簡易に実装できますが、標準のスタイルも上書きできてしまうため注意が必要です。

exportdefaultclassYourComponentextendsLightningElement{hasRendered=false;renderedCallback(){if(this.hasRendered)return;this.hasRendered=true;conststyle=document.createElement('style');style.innerText=`
      c-your-component lightning-textarea textarea {
        min-height: 300px;
      }
    `;this.template.querySelector('.mytextarea').appendChild(style);}}

対応方法3: 静的リソースの CSS を読み込んでスタイルを上書きする

ツリーを見ると、Salesforce Platform 上では LWC はネイティブの Shodow DOM を使っていないことがわかります。Synthetic Shadowと呼ばれる Polyfill を使用されており、ここではグローバルなCSSによるスタイルの上書きが可能です。これも予期しないスタイルの上書きには注意が必要です。

baseComponentCssOverrideExample.js
import{LightningElement}from'lwc';importcustomStylefrom'@salesforce/resourceUrl/your_custom_css';exportdefaultclassBaseComponentCssOverrideExampleextendsLightningElement{connectedCallback(){loadStyle(this,customStyle).then(()=>{});}}
your_custom_css(静的リソース)
.mytextareatextarea{min-height:300px;}

ちなみに、Shadow DOM の外側からスタイルを定義する方法として CSS Shadow Partsが W3C の Working Draft としてあり、主要なブラウザでサポートが進んでいます。「LWCのゴールは、LWCではなくなること」とのアナウンスもあり、遅かれ早かれ Salesforce Platform 上の LWC でもネイティブの Shadow DOM が使用されるようになり、この仕組みが利用できるようになることを期待しています。

参考リンク

自作WebサービスをXserverで公開するときの注意点と学び

$
0
0

1.はじめに

2020年1月から転職活動を始める予定なのですが、ポートフォリオとして作成した自作WebサービスをXserverというレンタルサーバーにアップしました。

その時の注意点や学びを書きますのでご参考になればと思います。

このような方にはぜひ読んでいただきたい内容になっています。
・プログラミングの勉強を始めてWebサイト、Webサービスを作成中の方
・作成したサイト、サービスをこれからサーバーで公開する予定の方
・サーバーで公開しようと思ったけどうまく出来ていない方

逆に
・現役バリバリのエンジニアの方
・すでに問題なく自作サイト、サービスをサーバーで公開済みの方
には正直見る価値はないかなと思います(笑)

ではご覧ください!!

2.自作サービスの内容

いきなりですが、作成したWebサービス兼ポートフォリオについて紹介させてください!

サービス名:KoSoDATE
サービス内容:子育てに関する情報共有サービス
使用言語:
・HTML
・css
・JavaScript(jQuery)
・PHP
・MySQL
製作期間:25日
製作時間:75h
フレームワークなし、フルスクラッチで作成しました。

こちらから見ることができますので、見ていただけたら幸いでございます。
「KoSoDATE」はこちら

3.Xserverへのアップ手順

Xserverへのアップ手順はkana。さん(@kgkgon)の記事でかなり詳しく説明されていてわかりすいので参考にして無事にできました。
ただし、ソースファイルをXserver上にアップロードするときに一気にアップできるsshを上手く使うことができなかったのでWebFTPを使って1つずつアップしました。

kana。さんの記事「WebサービスをXserverで公開する方法」はこちら

流れだけ簡単にまとめると以下の通りです。
1.レンタルサーバー(Xserver)を契約
2.独自ドメインを取得(今回はお名前.comを利用しました)
3.DNSサーバの設定(DNS:Domain Name System)
4.データベースをローカルからレンタルサーバに移行
5.ソースファイルをレンタルサーバーにアップロード

僕はここまででサーバー上で見ることができるようになりました。

4.Xserverで公開するときの注意点

無事にXserverで自作サービスを公開することができたのですが、つまづいたことやあらかじめ知っておいた方が良いと思うことがあるので今からあげていきます。

4-1.トップページとして表示するファイル名はindex.○○にする

Webサービスのドメインでアクセスしたときに最初に表示されるページ(トップページ)のファイル名は「index.○○」にしなければ表示されませんので要注意です。
Xserverの公式HPにも書いてます。

私はトップページをtoppage.phpという名前で作成していたのでまずindex.phpに変更し、他ページからtoppage.phpに遷移する処理のコードを全て変更することになりました。

なのでこれからWebサイトなりWebサービスを作ろうとしている方はトップページをindex.htmlなりindex.phpにすることをオススメします!!

プログラミングの学習教材では、何か作るときにだいたいindex.htmlが使われてて、「なんでindexなんやろ?」って思っていましたが、この時にその理由がわかりました。

4-2.PHPでDB接続する時のホスト名はMySQL設定のホスト名を入力する

Xserverにデータベースを移行したら、データベース接続設定を編集することになるのですが、ホスト名に注意です。

dbConnect.php
functiondbConnect(){//DBへの接続準備$dsn='mysql:dbname=データベース名;host=ホスト名;charset=utf8';$user='ユーザーネーム';$password='パスワード';$options=array(//SQL実行失敗時にはエラーコードのみ設定PDO::ATTR_ERRMODE=>PDO::ERRMODE_SILENT,//デフォルトフェッチモードを連想配列形式に設定PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,//バッファードクエリを使う(一度結果をセットを全て取得し、サーバー負荷を軽減)//SELECTで得た結果に対してもrowCountメソッドを使えるようにするPDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,);//PDOオブジェクトを生成(DBへ接続)$dbh=newPDO($dsn,$user,$password,$options);return$dbh;}

入力するホスト名はXserverサーバーパネルのMySQL設定に記載されいるホスト名にしてください。
私はサーバー情報に記載されてるホスト名を入力してしまい、接続エラーになりました。
間違える人いないかもしれませんが私は間違えましたので記事として残しておきます(笑)
image.png

4-3.プロトコルをhttps化する方法

Xserverで公開した場合、デフォルトではプロトコルはhttpになっていますのでhttps化する方法を紹介します。
こちらもXserverの公式HPに説明があるのでその通りにすれば簡単にできます。

ちなみにhttpとhttpの違いについてはサイバーセキュリティ.comさんの記事で以下の通り記載されております。

HTTPとは「Hyper Text Transfer Protocol(ハイパーテキストトランスファープロトコル)」の事です。ホームページのデータは文字だけでなく画像や動画などがあります。これらのデータをサーバとクライアントの間で通信するときの通信規約(プロトコル)がHTTPです。

HTTPに対してHTTPSには文字列の最後に「S」が付いています。この最後の「S」は「Secure(セキュア)」の「S」です。「Secure」という単語を辞書で調べてみると、
安全な、危険のない、安全で、安定した、心配のない、保証された、確実な、約束された、安心して、心配がなくて(secureの意味・使い方 - 英和辞典 Weblio辞書)
と意味が記載されています。IT業界では「セキュリティがしっかりしている」といった意味で使われている単語です。
HTTPSもHTTPと同じプロトコルの一つですが、HTTPSではSSL(Secure Socket Layer)というプロトコルが使用されています。厳密に言うと現在はSSLではなくTLS(Transport Layer Security)というプロトコルが使われています。SSLという言葉が長い間使われてきたため、現在でもSSLと呼んだり、あるいはSSL/TLSと呼ばれたりする状況になっています。

といった感じで書かれていますが、簡単にいうとhttpsの方がセキュリティがしっかりしているということです。

ユーザー登録をするようなサービスでは個人情報を扱うため漏洩するとかなりの問題となるのでhttps化しておくのが良いと思います。

5.おわりに

本記事では私が自作WebサービスをXserverで公開した時に感じた注意点と学びについて紹介させていただきました。
同じところでつまづいた人の問題の解決に少しでも役に立てれば良いなと思って書きました。

ご覧いただいた方は少しは参考になったでしょうか?

もし参考になった方がいましたら幸いです。
いいね、コメントなどしていただけたら嬉しいです。

railsアセットプリコンパイルまとめ

$
0
0
  • javascript
  • scss

javascript

プロジェクトフォルダのapp/assets/javascript以下のファイルをすべて読み込む。
railsの初期ファイルのapplication.jsは何かというと、assets/javascript以下のファイルをすべて読み込みまとめたものにあたる。勝手にファイルをまとめてくれるので、application.jsに追加でrequireしなくてもよいのである。
しかしgemなどで追加した機能を読み込みたい場合は、application.jsにrequireする必要がある。

これはどこで使用するのかというともちろんviewである。だいたいは、layouts/application.html.erbに書かれている。<%= javascript_include_tag 'application', オプション %> のように記述すればよい。'application'の部分は、他のファイルでもよいがPATHを通す必要がある。

scss

railsでbootstrapをgemで追加する場合はcssではなく拡張子がscssのファイルになる。ここでのcssとscssの違いは記述の仕方である。cssは、javascriptと同じように//=requireのよういに記述する必要がある。また//=require_treeがあるのだがこれは、javascriptと同じですべてのファイルをまとめて読み込むというのを意味している。cssにもapplication.cssというファイルがあり、javascriptと使い方は同じである。

それに比べてscssは、@importのようにして機能をインポートする必要がある。そしてcssと違ってファイルを自動的に読み込んでくれないため、一つ一つインポートする必要がある。
例えば、A.scssというファイルとB.scssというファイルがあるとする。そしてjavascriptと同じで、application.scssというファイルがある。scssではA.scssとB.scssの両方を適用したいのならapplication.scssに、@import 'A.scss'; @import 'B.scss';と記述する必要がある。

bootstarpのテンプレートをrailsに適用するときの注意

以上の知識より、ダウンロードしたテンプレートファイルのcss部分とjs部分をそれぞれ、assets配下に配置する。
テンプレートのファイルには、bootstrap.min.cssなどのファイルが含まれているが、これはbootstrapの機能を使えるようにしますよというファイルである。なのでrails側ですでにgemでbootstrapを追加している場合は、assets配下に配置してはならない。
jquery.min.jsなどのファイルもあるがこれも同様である。

ダウンロードしたテンプレートのファイルが機能を提供するものなのか、それはrails側ですでに追加されているのかを判断してからssets配下に置くとよい

LPを作るときに役立ちそうなTips集

$
0
0

この記事は、 ドワンゴ Advent Calendar 2019の21日目の記事です。

はじめに

2017年はエンジニアさんで埋められるカレンダーの中、わりと緊張しながらデザイナーの僕も「コンポーネント指向フロントエンド開発におけるデザイナーの参画について」という記事を書くことができました。

記事としても反響を頂き、登壇までさせていただくなど、大変大きな一歩になる記事でした。

さて、あれから2年経ったわけですが、 今年の9月にニコニコ生放送の開発を離れ、特設ランディングページ(以下LPと表記)やポータルサイトなどを作る部署に異動しました

異動による環境/技術スタックの変化

部署の異動により、環境が大きく変化しました

ニコニコ生放送

  • 規模の大きなプロダクト・チーム
  • プラットフォームのUIデザイン
  • React/TypeScript/scss/css-modules
  • 堅牢な機能開発・ワークフロー

現在の部署

  • ペライチ ~ 3ページ、多くても5テンプレートの軽量~ポータル程度のページを2人作業
  • グラフィックデザイン、演出、アニメーションやインタラクションを重視
  • レスポンシブが標準
  • ejs/scss/vanila js + babel/jQuery ~ Nuxt(Vue)
  • イベント合わせの納期・期間限定の時間の制約、変更が大きい短期開発

のようになり、大きく環境が変わりました。

環境が新しくなったことにより、まだ異動してわずか3ヶ月満たないぐらいですが、非常に多くの知見を得ました。
今回はそのなかでも特に有用そうな技術的Tipsを、実例とともに細々と紹介していきます。

Intersection Observer API

LPでは、要素がウィンドウ内に表示されたときにアニメーションするというようなことがとても多くあります。
今まで、これを書くには「jQueryなどでscrollTopとウィンドウサイズとその要素の位置関係を計算」したり、in-view.jsなどのライブラリを使うことが多かったと思います。

よく出てくる割には、計算が面倒だったり、ライブラリを探す手間、scroll/resizeイベントの抜け漏れ、イベントの発生回数によるパフォーマンスの低下など厄介なことが多くありました。

今ではこれは、Intersection Observer APIというブラウザ標準のAPIとして提供されており、これによってかなり楽に実装できるようになり、イベントの発生回数もscroll/resizeと比べるとかなり少なく、パフォーマンスも良くなります。
基本的にIE以外はすでに実装されている上、polyfillも準備されており、スムーズに導入できます。

使用例: 「画面内に入ってきたらフェードインさせる」をサクッと書く

HTML

index.html
<divclass="foo-section">...</div><divclass="bar-section">...</div>

SCSS

will-changeを入れる必然性はないですが、LPでは面積の大きな要素を動かすことも多く、なめらかなアニメーションによる快適な体験が求められるので、僕は will-changeも書いておくことが多いです。

style.scss
@mixinfade{transition:opacity.4sease,transition.4sease;will-change:opacity,transition;&[data-intersecting="false"],&:not([data-intersecting="true"]{opacity:0;transform:translateY(32px);}&[data-intersecting="true"]{opacity:1;transform:translateY(0);}}.foo-section{@includefade;}.bar-section{@includefade;}

JavaScript(es6/babel環境)

polyfillは npm install intersection-observerなどで入れておくと便利です

script.js
// polyfillの読み込みrequire('intersection-observer');// observeしたときのaction定義constaction=(entry)=>{// entry.target に検出したDOMが入ってくる// entry.isIntersecting に画面内かどうかが入ってくるentry.target.setAttribute('data-intersecting',entry.isIntersecting);}// IntersectionObserverの利用constobserver=newIntersectionObserver(entries=>{entries.forEach(entry=>{action(entries);});});// observeするDOMを取得// 他の要素にも適応したい際は、ここにどんどん追加しておくだけでOKconsttargets=[document.querySelector('.foo-section'),document.querySelector('.bar-section')];// observeするtargets.forEach(target=>{observer.observe(target);});

これだけでよくあるアニメーションをサクッと実装することができます。なれると何も見ずに5分程度で作ることができ、要素の追加/削除にも柔軟に適応できるため非常に重宝しています。

LPに限らず、Reactのようなコンポーネント指向な世界でも、scrollやresizeはwindowのようなglobalなものを持ちがちになってしまうので、このようなAPIがあるのは非常に嬉しいことだと思います。

ECSSによるHTML/CSSのワークフロー

LPでは、書き捨てで納期も短いものも多く、要素の変更にも柔軟に対応する必要があるため、厳密で堅牢や再利用性というよりは、終盤で最低限秩序が崩れない(namespaceがある/変更で崩れないよう要素とスタイルが1:1対応である)、理解が容易なものが良いです。

そのため、BEMよりラフ(だと思っている)で冗長なECSSを採用しています。

ECSSの概要と考え方のまとめが非常にわかりやすいですが、要点は以下の1行です。

namespace-ComponentName_ChildNode-variant

現在は namespace = ページ名を2文字にしたprefixとして採用しています。

使用例: カンプからHTMLをおこし、SCSSファイルを作成する

僕がコーディングを担当したページではないため、実際とは異なりますが、良いサンプルだったので ハレスタの以下の部分(大人の事情で画像はぼかしました)をHTMLに起こしてみようと思います。

スクリーンショット

topページなので、namespaceは tp-とします。
RootのComponentNameは ScheduleSectionとします。

schedule-section.ejs
<sectionclass="tp-ScheduleSection"><divclass="tp-ScheduleSectionBackground"aria-hidden="true"><divclass="tp-ScheduleSectionBackground_DecorationText">Pick up</div><imgclass="tp-ScheduleSectionBackground_Image"src=""alt=""><div><headerclass="tp-ScheduleSectionHeader"><h2class="tp-ScheduleSectionHeader_En">Schedule</h2><divclass="tp-ScheduleSectionHeader_Ja"aria-hidden="true">スケジュール</div></header><divclass="tp-ScheduleContent"><sectionclass="tp-SchedulePickUpContainer"><divclass="tp-SchedulePickUpThumbnail"><divclass="tp-SchedulePickUpThumbnail_Image"><divclass="tp-SchedulePickUpThumbnail_Filter"></div><divclass="tp-SchedulePickUpInfo"><timeclass="tp-SchedulePickUpDateTime"datatime="略"><spanclass="tp-SchedulePickUpDateTime_Date"><spanclass="tp-SchedulePickUpDateTime_Month"><%list[0].date.month%></span><spanclass="tp-SchedulePickUpDateTime_DateSeparator">/</span><spanclass="tp-SchedulePickUpDateTime_Day"><%list[0].date.day%></span></span><spanclass="tp-SchedulePickUpDateTime_WeekDay">(<%list[0].date.weekday%>)</span><spanclass="tp-SchedulePickUpDateTime_Time"><%list[0].date.startTime%><%list[0].date.endTime%></span></time><h3class="tp-SchedulePickUpTitle"><%list[0].title%></h3></div></section><divclass="tp-ScheduleSubContainer"><%for(leti = 1;i<list.length;i++){%><sectionclass="tp-ScheduleSubContainer"><divclass="tp-ScheduleSubThumbnail"><divclass="tp-ScheduleSubThumbnail_Image"><divclass="tp-ScheduleSubThumbnail_Filter"></div><divclass="tp-ScheduleSubInfo"><timeclass="tp-ScheduleSubDateTime"datatime="略"><spanclass="tp-ScheduleSubDateTime_Date"><spanclass="tp-ScheduleSubDateTime_Month"><%list[i].date.month%></span><spanclass="tp-ScheduleSubDateTime_DateSeparator">/</span><spanclass="tp-ScheduleSubDateTime_Day"><%list[i].date.day%></span></span><spanclass="tp-ScheduleSubDateTime_WeekDay">(<%list[i].date.weekday%>)</span><spanclass="tp-ScheduleSubDateTime_Time"><%list[i].date.startTime%><%list[i].date.endTime%></span></time><h3class="tp-ScheduleSubTitle"><%list[i].title%></h3></div></section><%}%></div></div></section>

長くなりましたが、上記のような感じになります。

命名/HTML設計の際のポイント

class名とHTML要素は1:1の関係にする

後述するJSでのDOMの拾い方にも影響しますが、基本的にclass名は1文書でユニークになるようにします(liなど繰り返すもの以外)。また、1つのelementに複数のclass名が当たらないようにします。
仮になにか属性を付けたいときはVariantか、変化するものであればdata属性やaria属性を利用します。
レイアウトやスタイルで繰り返すパターンが有る際は、mixinやextendを利用するようにします。(スタイルの話をclass名に持ち込まず、スタイルの話はスタイルファイルで解決するようにする。)

ChildNodeよりも ComponentNameがたくさんあるような状態になるようどんどん区切ることを心がける

単純に書いていくとたった2階層で ChildNodeになってしまうため、ComponentNameを意識して増やしていくことが大切です。

divが必要になったが命名に迷ったらとりあえず {ChildName}Containerとしておいて、あとからリネーム

上の ComponentNameの積極利用と合わせての話ですが、 ComponentNameの名付けは結構難しいです。これは、全体を書いたあとで分かるというケースが経験的には多く、全部書いてみてあとからリファクタするほうが早いので、ひとまず Containerという名前を便利に使って {ChildName}Containerとしてしまって、あとから全置換で対応するほうが早くできる事が多いです。

また、LPだと特に、以下のような名付け/HTML設計のパターンを予めやっておくと有効です。

Backgroundでも要素として作り、Sectionの直下に持ってくる

LPでは演出を盛ることが多く、Backgroundにも演出を入れることが多いため、cssの backgroundプロパティや、疑似要素では限界になることが多いです。そのため、最初からBackgroundという要素を持っておくと柔軟に対応できます。

h1~h6タグには Headerではなく Heading

これらのタグに Headerを指定すると、 <header>がきたときに困ることが多いので、 Headingという名前が有効です

サムネイルの <img>はとりあえず <div>で囲っておく

UI要素としてのサムネイルもフィルターやhoverで拡大のような装飾、インタラクションをもたせることが多いため、 Thumbnail > Thumbnail_Image / Thumbnail_Filterのように、常に <img>にWrapperをもたせるようにしておくと、あとからアニメーションを入れるなどのときに有効になります。

ECSSで命名した後のSCSSの生成

OneClickCSSという、HTMLを投げるとセレクタを吐き出してくれるという便利なサービスがあります。

スクリーンショット 2019-12-02 2.25.52.png

画像のようにHTMLをペーストし、「SimpleCSS」 ボタンで一発でセレクタを吐き出してくれ、これをコピーして.scssとして保存すればそれであとはスタイルを追加していくだけのscssの雛形が完成します。

ECSSで書くことによるJSでのアクセス

今まで、jsでアクセスするHTML要素は js- prefixをつけたり、idをつけたりすることが多かったとおもいます。
しかし、これではHTMLがjsに依存を持ってしまいます。
例えば、jsで扱う要素の数が増えると、必ずHTMLになにか手を加えることになります。
変更の多いLPでは地味に手数が増える作業になります。
これは、jsはHTMLに依存するが、HTMLはjsに依存しないようにして、依存の方向性を単方向にするほうが良いと思います。

そこで、ECSSではclass名と要素が基本的に1:1の関係になっているため、そのまま document.querySelector('.ns-ComponentName_ChildName')で呼び出しています。
HTMLのclass名をインターフェースとし、JSとCSSはそれを参照する、HTMLはJSとCSSに依存しないようにすることで、あまり複雑にならずに保つことができます。

(querySelectorにはパフォーマンス上の懸念があるため、場合によっては最適化することはあります)

ECSSの冗長性とclass名と要素が1:1の関係になっているおかげで、このようなスピーディで、最後までclass名のスコープで悩まないワークフローを成立させることができます。

all: unset

普段、reset.css, normalize.css, sanitize.cssなどを使うことが多いと思いますが、詳細度への配慮や、環境によってreset, normalize, sanitizeどれになっているかの確認、また作るときの選定にも地味に時間がかかります。

そこで、cssには allというプロパティがあります。

https://developer.mozilla.org/ja/docs/Web/CSS/all

この all: unsetは デフォルトの値がinheritでないものはすべてinitializeしてくれる便利なプロパティで、reset.cssに近いものですが、上書きなどをあまり考えずに使うことができ、cssも見やすくなるため有用だと思います。

使用例: <a><button>が交じるけど同じスタイルのmixinで共通化したい

all: unset;だと以下のように書けます

@mixinblue-button-style(){display:inline-block;padding:12px24px;min-width:120px;font-size:14px;line-height:1.25;text-align:center;white-space:nowrap;color:blue;border:2pxsolidblue;border-radius:4px;&:hover{cursor:pointer;}}.anchor{all:unset;@includeblue-button-style;}.button{all:unset;@includeblue-button-style;}

というように書くと、 <button><a>に対して同じボタンのスタイルを適応させることができます。

スクリーンショット
(アカウント取るのが面倒だったのでスクショですみません :bow:

all: unsetとのセットで、他のページやプロジェクトにも使い回すことができて非常に便利だと思います。

使用上の注意

IE, Edgeが非対応の上、Safariで意図通りに動かないケースがあります(特にcolor周り)。

↓safariでの表示
Safariでの表示

polyfillがあるのですが、これでも意図通りに動かないケースを確認しています。
https://www.npmjs.com/package/postcss-all-unset

ですので、現在では以下のようにmixinを作る方法が現実的です。

mixinの作り方( @supportsを使わない)

上記のpolyfillから、以下のようなmixinをつくり、いらない/使わないものを軽量化するというのが一つの手です。

ひとまずそのままコピペ

@mixinall-unset(){animation:none0sease0s1normalnonerunning;backface-visibility:visible;background:transparentnonerepeat0%0%/autoautopadding-boxborder-boxscroll;border:mediumnonecurrentColor;border-radius:0;border-collapse:separate;//(...以下略)}

出典を書いておき、使わないプロパティをコメントアウトしておくと、ある程度保守性の高い、コントロールされたmixinにできるかと思います。

@mixinall-unset(){// polyfill: https://www.npmjs.com/package/postcss-all-unset から改変background:transparentnonerepeat0%0%/autoautopadding-boxborder-boxscroll;border:mediumnonecurrentColor;border-radius:0;border-collapse:separate;// (...以下略)// ==== 以下は使わない ====// animation: none 0s ease 0s 1 normal none running;// backface-visibility: visible;// (...以下略)}

mixinの作り方( @supportsを使う / 少し未来の話)

将来的にsafariがきれいに動けば、chromeやfirefoxではうまく動くので、 @supports構文をつかったほうが、インスペクタに大量のプロパティが出てこなくなって良いと思います。

単純に考えると、mixinは以下のようになると思います。

@mixinall-unset(){@supports(all:unset){all:unset;}@supportsnot(all:unset){background:transparentnonerepeat0%0%/autoautopadding-boxborder-boxscroll;border:mediumnonecurrentColor;border-radius:0;border-collapse:separate;// (...以下略)}}

がこれでは

.anchor{@includeall-unset;color:red;}

と書いたとき、展開されるcssは

.anchor{color:red;}@supports(all:unset){.anchor{all:unset;}}@supportsnot(all:unset){.anchor{background:transparentnonerepeat0%0%/autoautopadding-boxborder-boxscroll;border:mediumnonecurrentColor;border-radius:0;border-collapse:separate;//(...以下略)}}

となってしまい、あとに書いたはずのcolorが打ち消されてしまいます。

そのため、mixinとそれのincludeは以下のように書く必要があります。

@mixinall-unset(){@supports(all:unset){all:unset;@content;}@supportsnot(all:unset){background:transparentnonerepeat0%0%/autoautopadding-boxborder-boxscroll;border:mediumnonecurrentColor;border-radius:0;border-collapse:separate;// (...以下略)@content;}}.anchor{@includeall-unset(){color:red;}}

この書き方は将来的にall: unsetが実用段階になったときに、もしかしたらちょっと置換がしづらいかもしれません。

WAI-ARIAの利用

.isShow.hideなど、表示非表示のクラス名は人によってぶれがちですが、 WAI-ARIA の利用によって割と楽になります。

こちらは Webアクセシビリティ Advent Calendar 2019の 14日目の記事として以下ページに書きました。合わせてご参照ください。

https://qiita.com/ln-north/items/8571782a9ec28ce622f4

終わりに

非常に長くなってしまいましたが、以上がTipsの紹介になります。

部署を移って期間が短いため、まだ正直いろんな感覚になれていないところがあるのですが、ぐいぐいコードを書くことが多くなり、ロゴなどのグラフィックデザインをすることも増え、非常に充実した日々を送っています。

現在すこし規模の大きいページも受けるようになったため、ejs/scss/jsからNuxt(Vue).jsでの開発にも挑戦しています。

エフェクトやモーションについても、シェーダ、パーティクル、WebGL、pixi.jsやGLSLやなど、演出手法、そしてその実現方法についてもたくさん勉強を重ねる必要を感じています。

また、そこで知見が溜まってきたら小出しに紹介できるように頑張りたいと思います。

ここまで読んでいただきありがとうございました。

条件によってテキストが変わるコンポーネントを分割して共通スタイルを適用する

$
0
0

こちらは、弁護士ドットコム Advent Calendar 2019 - Qiitaの 21 日目の記事です。

要望

条件によって中身が変わるからコンポーネントは分けたいけど、スタイルは共通化したい。
JS によるロジックは特にない。

具体的なケース

ユーザーの権限によってテキストが変わるコンポーネント。

共通のスタイルを使用したいのですが、一つのコンポーネントにまとめようとすると v-ifの嵐になってしまい、可読性がかなり落ちてしまいます。

イメージ
<pv-if="condition"class="style1">◯◯権限を持っているので、☓☓が出来ます。</p><pv-elseclass="style1">◯◯権限を持っていないので、☓☓が出来ません。</p><pv-if="condition"class="style2">△△権限を持っているので、□□が出来ます。</p><pv-elseclass="style2">ただし△△権限を持っていないので、□□が出来ません。</p><pclass="style3">どのユーザーも☆☆は出来ます。</p><!-- どの権限でも同じ -->

解決策

条件ごとにコンポーネントを分け、条件分岐・共通スタイル定義を親コンポーネントで行います。

スタイルを共通化させると、v-ifをコンポーネントの切り替えの 1 つだけにできるので可読性が上がります。

実装

親コンポーネントにスタイルを持たせて、条件によって子コンポーネントを切り替えるようにします。

親コンポーネント
<template><divclass="wrapper"><component:is="componentName"v-bind="propData"/></div></template><script>importComponent1from'./Component1.vue'importComponent2from'./Component2.vue'exportdefault{components:{Component1,Component2},props:{condition:{type:String,required:true},username:{type:String,required:true}},data(){return{propData:{username:this.username}}},computed:{componentName(){switch(this.condition){case'cond1':returnComponent1case'cond2':returnComponent2default:returnnull}}}}</script><stylescoped>.wrapper>>>.style1{font-size:20px;}.wrapper>>>.style2{font-size:16px;}.wrapper>>>.style3{font-size:16px;font-weight:bold;}</style>
Component1.vue
<template><div><h1>{{username}}さん</h1><pclass="style1">閲覧権限を持っているので、プロパティの閲覧が出来ます。</p><pclass="style2">ただし編集権限を持っていないので、プロパティの編集が出来ません。</p><pclass="style3">どのユーザーもログインは出来ます。</p></div></template><script>exportdefault{props:{username:{type:String,required:true}}}</script>
Component2.vue
<template><div><h1>{{username}}さん</h1><pclass="style1">閲覧権限を持っているので、プロパティの閲覧が出来ます。</p><pclass="style2">編集権限を持っているので、プロパティの編集が出来ます。</p><pclass="style3">どのユーザーもログインは出来ます。</p></div></template><script>exportdefault{props:{username:{type:String,required:true}}}</script>

注意

>>>(ディープセレクタ)は子孫要素すべてを対象とするので、scoped にしているからといって同じクラス名を使っているとスタイルがあたってしまいます。
コンポーネント名を prefix として付ける、BEM などの命名規則を適用する、などの対策が必要です。

また、SCSS 等を使用している場合は、>>>ではなく /deep/を使用する必要があります。

他の選択肢

CSS を外部ファイル化して @importで読み込む

HTML, CSS, JavaScript が一緒に管理できる SFC の利点が消えてしまうので見送りました。
「全く違う場所で利用するコンポーネントだけどスタイルは共通化させたい」というときは Minxin 的に使えるかもしれません。

共通化しない

個々のコンポーネントとして取り扱えたほうがいいことが往々にしてあるので、選択肢としてはありだと思います。
今回は、共通に定義したスタイルを片方だけ変更するということが基本的にないことがわかっていたので、共通化したほうが後々楽だと判断しました。

あとがき

自分が実装したケースではこのやり方がフィットしましたが、子コンポーネント側のコードが二重管理になってしまうので、ケースごとに検討することが必要だと思います。

他に同じような悩みを抱えている人の糧になれば幸いです。

参考

CSSの継承〜親から子へ受け継がれる意志〜

$
0
0

アドベントカレンダー初挑戦!21日目(プラコレ的には◯日目)
こんにちは!横田です。
不適切なところがありましたらご指摘いただけると幸いです。
よろしくお願いします!

今一度見直したい「継承」

フロントをやっていて、そこまで日は浅くないのですが、これまであまりcssプロパティの「継承」について深く考えたことがありませんでした。
親要素からプロパティを指定していって、子要素で効いていなかったらまた指定しよう、ぐらいでした。
しかしたびたび思うのです。「またcolor: #555;って書くのか...」と。
そこで、継承についておさらいしてみました。
Sassなどでスマートに書ける今、変数や関数をつかってかっこよく書けるようになりましょう!

継承とは

親要素のプロパティの値が、子要素に引き継がれることです。
プロパティによって継承されるものとされないものに分かれます。

継承されるプロパティ

使う機会が比較的多いプロパティをまとめました。

  • border-collapse
  • color
  • cursor
  • font
  • letter-spacing
  • line-height
  • list-style
  • quotes
  • text-align
  • text-shadow
  • visibility
  • word-spacing

継承されないプロパティ

継承されそうでされないプロパティもあります。

  • background
  • content
  • margin
  • table-layout
  • text-decoration
  • vertical-align
  • width

ここで挙げたプロパティは一部です。
ざっと見ると、継承されるプロパティはテキストに関するものが多いです。
裏を返せばテキスト関連以外は要素ごとにちゃんと指定する必要がありますね!

colorが継承されないのはなぜ?

しかし上記で挙げた継承されるプロパティでも、子要素に継承されないケースがあります。

親要素のp要素と子要素のa要素
<p>あいうえお<ahref="#">かきくけこ</a></p>
表示
あいうえおかきくけこ

実際は「かきくけこ」にアンダーラインも引かれていて、よく見るテキストリンクの表示です。
a要素に指定していないプロパティが設定されている。そう、これはユーザーエージェントの仕業でした。

デベロッパーツールのuser agent stylesheet

デベロッパーツールの「Elements」を見てみると、親要素のpよりも上にa要素に対して「user agent stylesheet」がきていて、プロパティの値が上書きされています。これはブラウザごとに定義されたデフォルトのCSS設定です。
先程の例は、ユーザーエージェントによってすでに子要素に指定されているため親要素の値は継承されない状態です。
継承は、優先度が低いことが分かります。
各ユーザーエージェントのプロパティと値についてはこちら

親要素を継承したい

ユーザーエージェントなんていうクライアントの環境に左右されるのはこりごりだ!親からもらったこの形質を大事にしたいんだ!と思うこともあると思います。
その場合はデベロッパーツールにもよく出てくる「inherit」が活躍します。
他にもプロパティ値として度々目にするinitial、unsetについてもおさらいしましょう。

inherit

親要素からプロパティの値を継承します。継承しないプロパティでも指定できます。
先程のa要素にもcolor: inherit;で親のp要素のcolorの値が有効になります!

注意するところは、ショートハンドプロパティ絡みの場合です。ショートハンドプロパティの中で、例えばborder: 2px dotted inhefit;としても無効です(部分的に効くとかもないです)。

html
<p>親要素<span>子要素</span></p>
css
p{border:1pxsolidblue;}span{border:inhefit;}/* OK */span{border:2pxdottedinhefit;}/* 無効 *//* 継承したいプロパティを個別に書くとOK */span{boder-color:inherit;}

また、fontなど継承するプロパティでも、子要素でショートハンドプロパティ内のプロパティの値を省略すると、初期値になってしまいます。

css
p{font:normalbold16px/1.2Arial,sans-serif;}span{font:italic16px/1.2Arial,sans-serif;/* 省略したfont-weightは、boldが継承されず初期値のnormalになる */}span{font:italicinherit16px/1.2Arial,sans-serif;/* 無効 */}/* 継承したいプロパティを個別に書くとOK */span{font-weight:inherit;}

initial

要素にプロパティの初期値を指定します。
https://developer.mozilla.org/ja/docs/Web/CSS/initial

主なプロパティの初期値(initialの値)

プロパティ初期値
colorユーザーエージェントに依存
displayinline
font-sizemedium(ユーザーの既定のフォントサイズ)
line-heightnormal(ユーザーエージェントに依存)
vertical-alignbaseline

ここに挙げたものはブラウザ依存のプロパティばかりですが、初期値はユーザーエージェントの値とは別のものです。
例えばmarginの初期値は0ですが、ユーザーエージェントの値は何かしらの値が設定されていたりします。

unset

その要素の任意のプロパティをリセットします。
親から継承できるプロパティの場合はinherit、継承できないプロパティはinitialの振る舞いになります。
https://developer.mozilla.org/ja/docs/Web/CSS/unset

allプロパティを使ってまとめて指定

その要素のすべてのプロパティ(例外を除く)を指すallプロパティがあります。
指定できる値はinitialinheritunsetrevertの4つです。
all: inherit;とすれば親要素で指定したプロパティすべてを継承できるので、一部分だけ子要素独自のプロパティの値を指定したい時などは、その下に追記する、といった書き方ができます。
https://developer.mozilla.org/ja/docs/Web/CSS/inherit

inheritの使いどころ

※あくまで個人的見解です!

以上をまとめると、cssの値の優先度(正しい言葉だと詳細度)では

↑高い

  • IDセレクター
  • classセレクター、擬似クラス
  • 要素型セレクター、疑似要素
  • ユーザーエージェント
  • 継承

↓低い

となるので、cssの指定の流れは

  1. クロスブラウザにするべくリセットcssを読み込む
  2. 1.の設定を要素で指定して微調整する
  3. class指定する

がシンプルかなと考えました。
1.は、user agent stylesheetの設定を上書きします。「リセットcss」などで検索すると、毎年ブラウザに対応したcssが出ているので、サイトデザインの仕様に適した、お手頃なものを探して入れます。
2.は、この要素の設定だけ直したい、など少し手を加える程度です。common.cssやbase.cssなどを作って書きます。
3.は、階層も深くせず、idは極力使わないようにします(詳細度の計算がややこしくなるため)
ここで、継承させたいプロパティにinherit

まとめてみると思った以上に普通でした。
ユーザーエージェントとリセットcssにより、すでに要素に指定されている値があるため、自然の要素のプロパティ継承は、これまで通りあまり意識することはなさそう...です。
デベロッパーツールを見ながら賢くinheritしましょう!

継承よりもユーザーエージェントが気になってきた

これまで見て見ぬ振りをしてきたユーザーエージェントのスタイルシートをのぞいてみると、この書き方は何なんだ、というものがたくさんありました。
調べても不明なプロパティ値がありましたが、これ以上深入りは禁物なので、覚えておくと良さそうな範囲で調べたものを少しご紹介します。

【セレクター】 a:-webkit-any-link

:から始まるので擬似クラス。そして-webkit-と続くのでChrome、Safariのベンダープレフィックス。
:any-linkは :link または :visited に一致する要素(この場合はa要素)を指定します。

【セレクター】 :matches(article, aside, nav, section)

:matches()は擬似クラス関数といい、:is()(新しい書き方)や:any()(接頭辞が必要)と同じ動作をします。
引数はセレクターとなる要素で、上記は
article, aside, nav, sectionと書くのと同じです。
:matches(article, aside, nav, section) p:hover
と書くと便利さがわかります。

https://developer.mozilla.org/ja/docs/Web/CSS/:is

【プロパティ】 margin-block-start

margin-block-startは新しい書き方!インライン軸とブロック軸を考えた時、これはmargin-topに相当します。

★英語の場合
margin-block-start = margin-top
margin-block-end = margin-bottom
margin-inline-start = margin-left
margin-inline-end = margin-right

日本語の縦書きや多言語に考慮した考え方が主流になるときは近いのかもしれませんね。
くわしくはこちら

まとめ

後半散らかってしまいましたが、いろいろな過程を経てその形質が表れていると思うと、感慨深いです。
プロパティの継承が親や祖先から子へ引き継ぐことならば、ユーザーエージェントとは国みたいなものだと思ってしまうのは私だけでしょうか。国(ブラウザ)ごとにルールがあり、差が生まれて...願わくば世界平和です。
未来の子どもたちにとって、来年も希望に満ちた平和な年でありますように!

明日は人物の描写力はピカイチ☆webデザインもさることながら漫画も大好評のあずあずこと田辺さんにバトンタッチです!
私のアイコンの似顔絵も描いていただきました☆
よろしくお願いしますー!

読んでいただきありがとうございました!

・・・

〜自由な結婚式をつくる人を増やす〜
プラコレではエンジニア、デザイナーも幅広く募集しています。
冒険法人プラコレは、プラコレWeddingを中心に全国展開。
花嫁メディアDressy(ドレシー)やfarny(ファーニー)なども運営しています。
ぜひご覧いただけますと嬉しいです!

参考文献

CSS初学者の最初の壁「スタイルの継承」をしっかり理解する
https://www.webprofessional.jp/css-inheritance-introduction/

各ブラウザごとのデフォルトのスタイルシート、user agent stylesheetのまとめ -Chrome, Safari, Firefox, Edge
https://coliss.com/articles/build-websites/operation/css/user-agent-stylesheets.html

[CSS]知っておくと便利な論理プロパティ、ボックスモデルにおける古い方法とこれからの方法
https://coliss.com/articles/build-websites/operation/css/new-css-logical-properties.html


フィーリングでHTMLとCSSを書いていた新卒が模写コーディングをやった話

$
0
0

Ateam Lifestyle Advent Calendar 2019の21日目は
株式会社エイチームライフスタイル @kmfjが担当します!
初めての投稿で至らぬ点もありますがよろしくお願いします。

はじめに

新卒でwebデザイナーをやっています。
普段はビジュアル〜マークアップ領域までを業務として行なっています。

きっかけ

一からサイトを作った経験はあるものの独学で生き抜いてきたため、入社前までは

  • コピペで組み立て
  • コードの意味はなんとなく理解
  • 見た目を見ながら無理やり調整

パワー型脳筋コーディングでなんとか組み立てていました。

なんとなくは理解しているため、ProgateのHTML、CSSコースなどは問題なくクリアできました。
ですが、いざ実務レベルでコーディングとなると「どう構造化して実装したら良いか」がわかりませんでした。
その対策として取り組んだ模写コーディングが勉強になったと思っているので、どのように行なって、何がよかったかを記載したいと思います。

やったこと

行ったことは以下の通りです。

1. 模写するサイトを選ぶ

選んだ基準としては、サイト構造が自社のサイトに近く、今後活かせそうなものを選びました。デザインが好みのサイトなど、自分が楽しんでできるサイトなどでもいいそうです。

2. 同じ見た目になるよう自分なりにコードを書く

どうしたら同じようになるか考えながらHTMLとCSSでコードを書いていきます。

3. 本物のサイトと比較する

Chromeの検証機能などを活用して、自分が書いたコードとどこが違うか?違う場合はどうして違うか?を考えます。

気をつけた点

模写元のコードは基本的に見ない

自分で構成することを身に付けることが目的なので、極力コードは見ないようにしました。ですが、画像やカラーコードなど考えても意味のないものはコピペしたり省略したりしました。

px単位での正確性は追求しない

見た目からコードに落とし込むことが重要なので、サイズや余白などは全体レイアウトが合っていれば問題ないと思います。
「良いレイアウトを学ぶこと」や「デザインに対して完璧なコードを書くこと」が目的であればもちろんpx単位で合わせるべきですが、今回の目的とはズレるためこのルールを設定しています。

よかったこと

見た目からHTMLの組み立て方がわかってくる

ここはこういう構造にするとうまくいきそう、という勘所がわかってきます。

単純に実装パターンの幅が広がる

これが表現したい時はこうすればいいのか、が調べながら制作するうちにわかってきました。

学んだこと

上記に書いた「よかったこと」以外にも、自分で書いたコードと模写元のコードを比較すると気づくことも多かったです。大きく感じた差異は以下の2点です。

意味を考えてタグを選ぶこと

意味に合わせてタグを選ぶことで、共同作業者も理解しやすく、機械も理解しやすいコードになるのだと分かりました。
例えば、似たようなリンク要素が並んでいるコードに以下のような違いがありました。

私の書いたコード
<divclass="p-content__list"><ahref="/"class="p-card"><imgsrc="images/xx.png"alt="ほげ"class="p-card__img"/><divclass="p-card__caption"><spanclass="c-textS">ほげ</span></div></a><ahref="/"class="p-card"><imgsrc="images/yy.png"alt="ほげほげ"class="p-card__img"/><divclass="p-card__caption"><spanclass="c-textS">ほげほげ</span></div></a></div>
模写したサイトのコード
<ul><li><divclass="category"><ahref="/"class="item"><pclass="thumb"><imgsrc="images/xx.png"alt="ほげ"></p><divclass="categoryLabel"><span>ほげ</span></div></a></div></li><li><divclass="category"><ahref="/"class="item"><pclass="thumb"><imgsrc="images/yy.png"alt="ほげほげ"></p><divclass="categoryLabel"><span>ほげほげ</span></div></a></div></li></ul>

※内容を変更しています

いくつか異なる点がありますが、例えば複数の要素が並列で並んでいる場合、私は単純に<div>で囲んでいましたが、模写元のサイトでは<ul>要素で囲んでいました。意味を考えると、<ul>要素でくくるべきだと思います。

要素が単体で存在できるような記述

長期的に運用していく場合は、要素が入れ替わったり、無くなったりしても崩れないコードが理想だと思います。現在利用されているサイトでは、そういった今後の運用を見越した実装になっていると思いました。

例えば私が書いたコードは周りを囲う要素にpaddingを持たせていましたが、模写元は要素それぞれにpaddingを持たせていました。要素が入れ替わる可能性のある場合、そちらの方がいいかもしれません。

それ以外にも、コーディングだけでなくレイアウトや命名の仕方で学ぶ点も多々ありました。

まとめ

部分部分でのコードはかけるけど、一から作ることが苦手な人にとっては、組み立て方を学ぶいい手段になると思います。
また、実際に利用されているサイトのコードと比較することで、気づかされることも多くあると思いました。

最後に

ここまで読んでいただきありがとうございました!

Ateam Lifestyle Advent Calendar 2019の22日目は、@hytkgamiがお送りします!

"挑戦"を大事にするエイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/

railsでapplication.scssと同じディレクトリのscssファイルが読み込まれないときの対処法

$
0
0

背景と対象

usersコントローラを作成したときに生成された、app/assets/users.scssに記述したscssが反映されない。つまりはアセットパイプラインの仕組みが適用されていない状況になってしまっている状況。全部app/assets/application.scssに書けばcssの適用は出来ますがめっちゃ見ずらいのでやめた方がいいです。このような状況でつまっている方の役に立てればと思い書きました。ちなみにアセットパイプラインについては以下のページがおすすめです!
https://diveintocode.jp/blogs/Technology/AssetPipeline

解決方法

とっても簡単です!

app/assets/application.scssのコメントアウトの最下部に*= require_selfとrequire_tree を以下のように追加してください。それぞれのコードの意味はコード内に書いてあります。ちなみにこれらの順番はどっちでもよく、require_selfを先に書けばapplication.scssが先に読み込まれます。後に書けばapplication.scss以外のscssファイルが先に読み込まれます。

app/assets/application.scss
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets,oranyplugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You'refreetoaddapplication-widestylestothisfileandthey'llappearatthebottomofthe*compiledfilesothestylesyouaddheretakeprecedenceoverstylesdefinedinanyotherCSS/SCSS*filesinthisdirectory.Stylesinthisfileshouldbeaddedafterthelastrequire_*statement.*Itisgenerallybettertocreateanewfileperstylescope.**=require_self まずはこのファイルを読み込むという記述追加!*=require_treeapp/assets以下のcssファイルを読み込んでいる 追加!*/
}

これでアセットパイプラインの仕組みを利用して分かりやすく、すっきりしたコードが書けます!

あなたの好きなCSSプロパティは何ですか?

$
0
0

次の中から選んでね

  • transform: translate
  • margin: 0 auto
  • position: absolute

transformが好きなあなた

普通の人。みんな大好きtransform。
要素を変形するには万能なCSSプロパティ。

<divclass="box"></div><divclass="box box--move"></div>
.box{width:100px;height:100px;border:1pxsolidred;box-sizing:border-box;}.box--move{transform:translate(100px,0);}

スクリーンショット 2019-12-22 2.46.35.png

まあこうなる。

では、これは?

<divclass="box box--move-large"></div><divclass="box box--large-move"></div>
.box--move-large{transform:translate(100px,0)scale(2);}.box--large-move{transform:scale(2)translate(100px,0);}

スクリーンショット 2019-12-22 2.46.42.png

順番を変えただけで見え方が変わってしまった。
これには理由がある。

適用される 1 つ以上の CSS 変形関数です。変形関数は、左から右へ順に重ねられ、つまり右から左の順に変形の混合の効果が適用されます。
https://developer.mozilla.org/ja/docs/Web/CSS/transform

つまり、 2倍大きくなってからの translate(100px, 0)200px移動することになる。

margin: 0 auto が好きなあなた

安定志向。
昔ながらの伝統的な中央寄せ。

<divclass="box box--center"></div>
.box{width:100px;height:100px;border:1pxsolidred;}.box--center{margin:0auto;}

コンポーネント指向が浸透した昨今は

レイアウトは親要素に任せる方が適切なシーンが多い。
.boxはどこに配置さるかを気にしない

<divclass="container"><divclass="box"></div></div>
.container{display:flex;justify-content:center;}

position: absoluteが好きなあなた

きっと、すいも甘いも知った人。
absoluteの従順さに惚れ惚れする。

<divclass="container"><divclass="box box--bottom"></div></div>
.box--bottom{position:absolute;bottom:10px;right:10px;}.container{width:80%;height:300px;border:1pxsolidblue;margin:20pxauto;}

スクリーンショット-2019-12-22-3.02.49.png

親要素をたどって起点を探す

absoluteの bottom, rightが基準となるのは position: relativeがついている親要素。

つまり、 .containerposition: relativeを設定すれば
.containerを基準としたポジションとなる。

.container{position:relative;}

スクリーンショット-2019-12-22-3.03.24.png

従順なやつだなと思う。

※動画解説版
https://www.youtube.com/watch?v=VFkwIGoKATo

初めてのtailwindcss (Vue.js + PurgeCSS)

$
0
0

この記事は

CSS Advent Calendar 2019 21日目の記事です。
気になってたtailwindcssを、Vue.jsでやってみましたという記事です。
リポジトリはこちら→ https://github.com/hisako135/tailwindcss_vue_demo
スクリーンショット 2019-12-22 3.10.21.png

やったことは以下のような感じです。

tailwindcssってなんぞ

"Bootstrapをゴリゴリいじってカスタマイズしても良いのだが、そこに時間かけるんならCSSフレームワーク使う利点なくない...?:thinking: " などと思ったことがある人はいませんか...?
グリッドシステムとForm周りさえあればよくて、他の必要なコンポーネントは自分で作った方が楽だよな、とかゴニョゴニョ...(小声)

そこでtailwindcssです!

Most CSS frameworks do too much.
(ほとんどのCSSフレームワークは多くのことをやり過ぎている)
https://tailwindcss.com/#what-is-tailwind

"あらゆる種類のコンポーネントが用意されているCSSフレームワークって初期段階に素早く作るのには確かにとっても便利。でもカスタマイズしようとするとしんどいよね。事前に設計されたコンポーネントの代わりに、低レベルなUtilityクラスを提供することによって、HTMLを離れることなくデザインをカスタマイズできるよ。"
というのがtailwindcssです。

ちなみにCSSでのUtilityクラスとは、.font-small {font-size: 10px;}.text-left {text-align: left;}のように、単一のプロパティを定義した汎用的なクラスを指します。Helperクラスとも呼ばれたり。
FLOCSSではUtilityレイヤーに、FLOCSSのベースとなっているMCSSではCosmeticsレイヤーあたりに記載されるやつです。

tailwindcssのUtility-firstの考え方についてはこちらの翻訳記事に詳細書いてありますので是非!
:book:CSS Utility Classes and "Separation of Concerns" 翻訳

セットアップ手順

何はともあれVue.jsでのtailwindcssのセットアップをやっていきましょう。
まずはvue-cliでVue.jsプロジェクトを作成します。

以降、tailwindcss/setup-examplesを参考に進めます。

  • tailwindcssをインストール
npm install tailwindcss
  • postcss.config.js ファイルを作成しプラグインとして設定
postcss.config.js
consttailwindcss=require('tailwindcss');constautoprefixer=require('autoprefixer');module.exports={plugins:[tailwindcss,autoprefixer,]}
  • src/assets/tailwind.cssというtailwindのスタイル用のCSSファイルを作成します
src/assets/tailwind.css
@tailwindbase;@tailwindcomponents;@tailwindutilities;
  • main.jstailwind.cssをimportします
src/main.js
importVuefrom'vue'importAppfrom'./App.vue'import'@/assets/tailwind.css'//これVue.config.productionTip=falsenewVue({render:h=>h(App),}).$mount('#app')

セットアップは以上です! :tada:

今回はNavigationやCardの例を利用してサクッと画面を作っています。
あんまりtailwindcssで作り込まれてると <transition>コンポーネント使いづらいな...と、うっすら思いましたとさ(結果、使ってない)。

src/components/Header.vue
<template><navclass="flex items-center justify-between flex-wrap bg-teal-500 p-6"><divclass="flex items-center flex-shrink-0 text-white mr-6"><svgclass="fill-current h-8 w-8 mr-2"width="54"height="54"viewBox="0 0 54 54"xmlns="http://www.w3.org/2000/svg"><pathd="M13.5 22.1c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05zM0 38.3c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05z"/></svg><spanclass="font-semibold text-xl tracking-tight">Tailwind CSS</span></div><div@click='hideMenu=!hideMenu'class="block lg:hidden"><buttonclass="flex items-center px-3 py-2 border rounded text-teal-200 border-teal-400 hover:text-white hover:border-white"><svgclass="fill-current h-3 w-3"viewBox="0 0 20 20"xmlns="http://www.w3.org/2000/svg"><title>Menu</title><pathd="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg></button></div><div:class='{"hidden md:hidden sm:hidden":hideMenu}' class="w-full block flex-grow lg:flex lg:items-center lg:w-auto">
      <divclass="text-sm lg:flex-grow"><ahref="#responsive-header"class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white mr-4">
          Docs
        </a><ahref="#responsive-header"class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white mr-4">
          Examples
        </a><ahref="#responsive-header"class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">
          Blog
        </a></div><div><ahref="#"class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white mt-4 lg:mt-0">Download</a></div></div></nav></template><script>exportdefault{data(){return{hideMenu:true}}}</script>

ざっくり紹介tailwindcss

さて、tailwindcssの特徴を極一部ですがご紹介します!
めちゃくちゃ詳しく書いて下さっている記事はこちらですので是非!
:book:TailwindCSS入門 ~ Utility First + デザインシステムの構築 ~i

:(コロン)でブレークポイントや擬似クラスの指定ができる

lg:inline-blockhover:text-whiteでブレークポイントや擬似クラスへの指定をしています。1つのスタイルを使い回せるの便利だな〜。

src/components/Header.vue
<ahref="#responsive-header"class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">Blog</a>

@apply

組み合わせて自由に見た目を作れるのが利点とはいえ、何度も同じクラスを並べるのは苦痛ですよね。@applyで、utilityの組み合わせをカスタムコンポーネントのクラスとしてまとめることができます。

src/assets/tailwind.css
@tailwindbase;@tailwindcomponents;/* Extracting Components */.btn-teal{@applybg-teal-500text-whitepy-2px-4roundedw-full;}.btn-teal:hover{@applybg-teal-700transition-colorstransition-250transition-linear;}@tailwindutilities;

カスタムコンポーネントのクラスは、utilitiesの前に読み込まないとダメだそうです。

カスタマイズが簡単

tailwindcssで定義されているスタイルは、 tailwind.config.jsでカスタマイズできます。

npx tailwind init //tailwind.config.jsファイルを作成するコマンド
tailwind.config.js
module.exports={theme:{extend:{}},variants:{},plugins:[]}

作成された tailwind.config.jsにカスタマイズしたいプロパティや追加したいプロパティを記載していきます。
tailwindcssでは現状 transitionのようなアニメーションに関するプロパティが定義されていません。
そういうものの追加にも便利そうですね。拡張が簡単なのは嬉しい :relieved:
(今回はtransitionのutilityクラスを定義するのにtailwindcss-transitionsを使用しました。)

PurgeCSSで使ってないCSSを削除

さて、なんだかスマートに感じるtailwindcssですが、ファイル容量は実は結構重いです。
スクリーンショット 2019-12-22 2.08.01.png

PurgeCSSで、使ってないCSSを削除していきましょう。

  • PurgeCSSをインストール
npm install @fullhuman/postcss-purgecss --save-dev
postcss.config.js
constautoprefixer=require('autoprefixer');consttailwindcss=require('tailwindcss');constpurgecss=require('@fullhuman/postcss-purgecss')({// テンプレートファイルへのパス。今回は'./src/**/*.vue'content:['./src/**/*.vue'],// https://medium.com/@kyis/vue-tailwind-purgecss-the-right-way-c70d04461475defaultExtractor:(content)=>{constcontentWithoutStyleBlocks=content.replace(/<style[^]+?<\/style>/gi,'')returncontentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g)||[]},whitelistPatterns:[/-(leave|enter|appear)(|-(to|from|active))$/,/^(?!cursor-move).+-move$/,/^router-link(|-exact)-active$/],})module.exports={plugins:[autoprefixer,tailwindcss,// 開発中はビルド時間がかかってしまうので、productionの時のみ実行...process.env.NODE_ENV==='production'?[purgecss]:[]]}

Before/After

npm buildして、 dist/css/配下のCSS容量を見て見ます。

Before

スクリーンショット 2019-12-21 22.07.39.png

After

スクリーンショット 2019-12-21 22.18.58.png

644KB減!!
すごいぞ!PurgeCSS!! :tada:

今回はサンプルのためスタイリング自体が少ないので、実際のプロジェクトではもっと大きくなるんでしょうけれども、いやぁしかし気持ち良いですね :relieved:

まとめ

以上、初めてのtailwindcss (Vue.js + PurgeCSS)でした!
個人的にはめちゃくちゃ好きな思想のフレームワークです。
シンプルで良い。自由 is フリーダム。必要なものは自分で作る :relieved:

緻密かつ堅牢なCSSフレームワークに疲弊している方、ぜひトライしてみては如何でしょうか?

参考にさせて頂いたものまとめ

tailwindcssなのかTailwindcssなのか、はたまたTailwindCSSなのか、正しい表記が未だに分かりません :relieved:

CSSで描くパーティクルアニメーション

$
0
0

この記事は CSS Advent Calendar 2019 22日目の記事です。
CSSでこんなこともできるんだなぁぐらいの軽い気持ちでみていただけますと幸いです。

パーティクルアニメーションとは

パーティクルとは粒子のことで、粒子に対して色々な動きや変化を与えることで、
神秘的な演出や派手な演出・キュートな演出など様々な表現ができます。

CSSで描いてみた

以下、制作したパーティクルアニメーションのサンプルとなります。

※Qita上では動作が重いので、サンプル右上のCodePenロゴかDEMOをクリックしてご覧ください。
※SPではあまり綺麗に映りません…

● DEMO

See the Pen Particle Rotation #02 by Hisami Kurita (@hisamikurita) on CodePen.

解説

サンプルはCSSアニメーションを使用して動かしています。
以下、ポイントとなる技術的な部分を解説していきたいと思います。

Pug + SCSS

メタ言語のPugとSCSSを使用しています。
主にfor文でdivを100~生成したり、div一つ一つに固有のスタイルを当てるために使用します。

Pug
- for (var i = 0; i < 200; i++)
    div.rotate
      div.flip_rotate
        div.flip_pos
          div.flip

コンパイルします↓

HTML
<divclass="rotate"><divclass="flip_rotate"><divclass="flip_pos"><divclass="flip"></div></div></div></div><!-- 上記の複数のdivが200個生成されます。 -->
SCSS
@for$ifrom1through200{.rotate:nth-of-type(#{$i}){}}

コンパイルします↓

CSS
.rotate:nth-of-type(1){}.rotate:nth-of-type(2){}.rotate:nth-of-type(3){}/* 上記のクラス指定を200番目まで固有に指定することができます。 */

メタ言語を使用しないで、HTMLとCSSのみでも書くことも可能ですが、流石に100~固有にスタイルを当てていくのは大変だと思います:sweat_smile:

ランダム関数

パーティクルアニメーションはランダムな値を与えたい時が多いです。
例えば、この要素は速く動かしたいけど別の要素はゆっくり動かしたいことがよくあります。
その時にrandom()を使用します。

SCSS
animation:rotationlinearinfinitereverse;animation-duration:random(50000)+20000+ms;

サンプルでは円周上を回転させる際のCSSアニメーションにかかる時間の秒数に対して、
上記のように引数50000の中でランダムな値が出るように設定しています。
更に、20000を足すことで、20001ms~70000msの範囲でランダムな値が出るように設定しています。このように設定することで極端に遅いアニメーションをさせないようにしています。

animation-delayに負の値を指定する

animation-delayは負の値を指定することで、アニメーション周期の指定した時間から直ちに開始されます。

SCSS
animation:rotationlinearinfinitereverse;animation-delay:random(9999)+50000*-1ms;

サンプルでは円周上を回転させる際のCSSアニメーションの開始時間に対して、
上記のように負の値を設定しています。
更に、ランダム関数を使用することで-40001ms~-49999msの範囲でランダムな値が出るように設定しています。animation-duration20001ms~70000msの範囲で設定しているのでアニメーションの経過時間が収束するようになっています。

まとめ

主な技術的ポイントは以上になります。
何か、誤っている点がございましたらご指摘いただけますと幸いです。

パーティクルアニメーションは値を少し変化させるだけで、
全く違った演出を表現できます。

↓下記はサンプルの背景色、奥いき等を変化させて制作してみました。

● DEMO

See the Pen Perticle Rotation #03 by Hisami Kurita (@hisamikurita) on CodePen.

良ければ、CodePenのコードを書き換えて遊んでみてください。

この記事をよんでパーティクルアニメーション、CSSに興味を持っていただけたら嬉しいです。

終わりに

こんな作品を週一(目標)で作って、CodePenに投稿してるので、良かったらフォローお願いします!

参考
https://developer.mozilla.org/ja/docs/Web/CSS/animation-delay
https://sass-lang.com/documentation/modules/math

Viewing all 8541 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>