532 |
533 |
534 | ```
535 |
536 | #### Blockを親セレクタにしない
537 |
538 | 詳細度を強くしたり、スコープを作るためにBlockを親セレクタにしてElementとModifierを指定しないようにしましょう。Blockの名前を継承することで擬似的にスコープを作るようにしましょう。詳細度はなるべくフラットに保ってカスケーディングのコントロールをしやすいようにします。
539 |
540 | ```css
541 | /* Good */
542 | .block {}
543 | .block__element {}
544 | .block--modifier {}
545 |
546 | /* Bad */
547 | .block {}
548 | .block .block__element {}
549 | .block .block--modifier {}
550 | ```
551 |
552 | #### 他のBlockに依存させない
553 | Blockは他のBlockに依存せず、単独で機能するようにしましょう。例えば、`Block1`のスタイルを変更するために`Block2`を親セレクタにして`Block1`を上書きするようなことはできるだけ避けましょう。同じ詳細度であれば、セレクタを加えなくても後から記述するだけで適応されます。
554 |
555 | ```css
556 | /* Good */
557 | .block1 {}
558 | .block__element {}
559 |
560 | .block2 {}
561 |
562 | /* Bad */
563 | .block1 {}
564 | .block__element {}
565 |
566 | .block2 .block1 {}
567 | ```
568 |
569 | また、例えば`Block1`と`Block2`を左右に配置したい場合は、グリッドのようにレイアウトをするための`Block3`を定義するようにしましょう。Blockの機能を拡張しすぎたり、依存関係を作ってしまうよりも、Blockを組み合わせることでページを構築するようにします。
570 |
571 | #### Sassのネストを制限する
572 | Sassを使用している場合、ネストによってBEMの記述が楽になりますが、なるべく使用しないようにしましょう。理由はBlockに関連するルールセットが際限なくつながり、長くなることで可読性やメンテナンス性を損なう可能性が高いからです。
573 |
574 | ```scss
575 | // Bad
576 | .block {
577 | &__element {
578 | ...
579 | }
580 | &__element {
581 | ...
582 | }
583 | &__element {
584 | ...
585 | }
586 | &--modifier {
587 | ...
588 | }
589 | &__element {
590 | ...
591 | }
592 | &__element {
593 | &--modifier {
594 | ...
595 | }
596 | }
597 | }
598 | ```
599 |
600 | また、ネストを深くしすぎると、HTMLのclass属性から該当するスタイルを検索するときに支障をきたす可能性が増します。速く書くよりも、読みやすくメンテナンスしやすくなるようにしましょう。
601 |
602 | ### 接頭辞(Prefix)
603 | 接頭辞(Prefix)をclassセレクタとIDセレクタに使用することで、名前が重複する可能性を下げたり、その役割を簡潔に示すことができます。FLOCSSで定義されている接頭辞に以下のような接頭辞を加えて使用するのを推奨します。
604 |
605 | * `c-` Componentレイヤーのコンポーネントあることを示します
606 | * `p-` Projectレイヤーのコンポーネントであることを示します
607 | * `u-` Utilityレイヤーのコンポーネントであることを示します
608 | * `js-` JavaScriptのフックとして使用されるclassやIDであることを示します(CSSでスタイルは指定しない)
609 | * `is-`, `has-` 要素の表示の切り替えなどの状態の変化があることを示します
610 | * `t-` Themeレイヤーのコンポーネントであることを示します。
611 | * `s-` Scopeレイヤーのコンポーネントであることを示します。
612 | * `qa-`, `t-` QA/Testレイヤーのコンポーネントであることを示します。
613 |
614 | 役割ではなく、プロジェクトの名前やバージョンを示すために接頭辞をつけるのも有効です。例えば`.pj-`(project)のようにプロジェクト名を短縮させたり、`.pj02-`のようなバージョンをつけると重複を避けることができます。
615 |
616 | 接頭辞をつけることはスタイルの追加をより安全にしたり、他のCSSフレームワークやライブラリと共存するベターな方法です。
617 |
618 | ### 接尾辞(Suffix)
619 | classに`-sm`や`-md`などを付与してブレイクポイントを指定していることを示すことがあります。規則性をもたせるためにクラス名の最後に接尾辞を付けることを推奨します。より明示的に名前をつけるために`@`を接尾辞として使用してもいいでしょう。
620 |
621 | ```css
622 | /* Good */
623 | @media (min-width: 768px) {
624 | .u-col1of3\@md { width: width: 33.33333% !important; }
625 | .u-col2of3\@md { width: width: 66.66667% !important; }
626 | .u-col3of3\@md { width: width: 100% !important; }
627 | }
628 | ```
629 |
630 | `@`をスタイルシートで指定する際にはバックスラッシュ(`\`)でのエスケープが必要ですが、HTMLで指定する際にはエスケープは必要ありません。詳しくは以下の記事を参照してください。
631 |
632 | [BEMIT: Taking the BEM Naming Convention a Step Further | CSS Wizardry](http://csswizardry.com/2015/08/bemit-taking-the-bem-naming-convention-a-step-further/)
633 |
634 | ### 単語の省略を制限する
635 | 意味が汲み取れない単語の省略は禁止します。例えば`navigation`を`nav`と省略することは許容しますが、`title`を`ttl`と省略するのは意味が読み取れませんし、ほとんど短くなってもいません。基本的には省略をせずに名前をつけましょう。
636 |
637 | ```css
638 | /* Good */
639 | .navigation {}
640 | .title {}
641 | .text {}
642 | .icon {}
643 |
644 | /* allow */
645 | .nav {}
646 |
647 | /* Bad */
648 | .ttl {}
649 | .txt {}
650 | .ico {}
651 | ```
652 |
653 | 省略する場合にはドキュメントに残すことを推奨します。認識を共有することもできますし、`nav`と`navi`が混在してしまうようなことを防ぐこともできます。
654 |
655 | ### 絶対値を表すクラス名を避ける
656 | `.mb10`のような値を固定した名前をつけることを禁止します。理解はしやすい名前ですが、値を変更したい場合にHTMLに記述したclass属性も変更する必要が出てきます。こうした変更に柔軟に対応するために`.mbs`(margin-bottom smallの意味)のような相対的な名前をつけましょう。
657 |
658 | ```css
659 | /* Good */
660 | .mbs { margin-bottom: 10px; }
661 |
662 | /* Bad */
663 | .mb10 { margin-bottom: 10px; }
664 | ```
665 |
666 | また、`small`, `medium`, `large`のようなパターンに制限するルールを設けることで、際限なくサイズ違いが増えてしまうことを防ぐこともできます。
667 |
668 | カラーネーム(`.red`や`.blue`)のようなクラス名も禁止します。ある要素の色を変更したい場合にクラス名と値の意味が合わなくなってしまい、class属性を変更しなくてはいけなくなることが考えられるからです。`.success`や`.brand`のような値を変更しても意図が変わらない名前にしましょう。
669 |
670 | ```css
671 | /* Good */
672 | .brand {}
673 | .success {}
674 |
675 | /* Bad */
676 | .red {}
677 | .blue {}
678 | ```
679 |
680 | ## セレクタ
681 | ### インライン記述を使用しない
682 | HTMLの`style`属性に直接スタイルを記述するインライン記述は使用を禁止します。理由はスタイルシートで一括管理できないこと、詳細度が高いこと、HTMLのファイルサイズを無闇に増やしてしまうこと、単一のスタイルごとしか指定できない(レスポンシブに対応できない)からです。
683 |
684 | ```html
685 |
686 |
687 | ```
688 |
689 | JavaScriptによるインラインでのスタイル追加もできるだけ避けて、classを追加するようにします。
690 |
691 | ### HTML(DOM)構造に依存させない
692 | マークアップは変更される可能性があります。例えば`article`を`section`にしたり`h2`を`h3`に変更するかもしれません。特定のHTML要素に依存したセレクタを指定している場合はその都度変更しなくてはいけません。
693 |
694 | ```css
695 | /* Bad */
696 | article h2 {}
697 | ```
698 |
699 | classを使ったセレクタを定義していればHTML構造が変更されたとしても、CSSを変更する必要がなくなり、可搬性が高まります。
700 |
701 | ```css
702 | /* `article`要素の見出しに指定するクラス */
703 | .article__title {}
704 |
705 | /* 汎用的な見出しのクラスを用意 */
706 | .u-h2 {}
707 | ```
708 |
709 | ### 不要な要素セレクタを連結させない
710 | classやIDセレクタで指定する時に要素セレクタを連結させるのは詳細度を高めるだけで意味がありません。
711 |
712 | ```css
713 | /* Good */
714 | .foo {}
715 |
716 | /* Bad */
717 | h2.foo {}
718 | ```
719 |
720 | もし、特定の要素に指定してほしいのであれば、コメントによってそれを示しましょう。
721 |
722 | ```css
723 | /* Good */
724 | /* h2要素に指定してください。 */
725 | .foo {}
726 |
727 | /* h2 */.foo {}
728 | ```
729 |
730 | ### セレクタを短くする
731 | セレクタは必ずしもHTML構造に沿う必要はありません。場合によってはセレクタを短くすることで詳細度を低く抑えることができます。
732 |
733 | ```css
734 | /* Good */
735 | ul a {}
736 | th {}
737 |
738 | /* Bad */
739 | ul li a {}
740 | table th {}
741 | ```
742 |
743 | ### セマンティックでないセレクタの指定を制限する
744 | 特定の意味を持たないセレクタ(`div`, `span`)は使用される範囲も広いため、セレクタに対する影響範囲も広くなります。使用する場合はできるだけ範囲を狭くして指定してください(classセレクタで指定するのが望ましいです)。また、単体でのセレクタは禁止とします。
745 |
746 | ```css
747 | /* Good */
748 | .foo > div {}
749 | .foo > span {}
750 |
751 | /* Bad */
752 | div {}
753 | span {}
754 | .foo div {}
755 | .foo span {}
756 | ```
757 |
758 | ### 拡張できるようにオブジェクトを定義する
759 | 保守性や拡張性を高めるために構造を含んだベースになるclassを定義し、装飾的なclassで拡張するようにしましょう。ベースになるスタイルを変更したい場合でも1箇所変更するだけでよくなりますし、拡張するclassにも最低限のスタイルを追加するだけになります。
760 |
761 | 1つのclassでまとめて指定してしまうと、バリエーションが増えるごとに同じスタイルを何回も指定することになってしまいます。ベースになるスタイルを変更したい場合に何箇所も変更する必要も出てきます。
762 |
763 | 下記の例ではベースになるclassの`.p-alert`に装飾的なスタイルが指定されていますが、オブジェクトの特性上、問題はごくわずかだと判断しています。
764 |
765 | ```html
766 |
767 |
768 |
769 |
770 | ```
771 |
772 | ```css
773 | /* Good */
774 | .p-alert {
775 | padding: 1em;
776 | border: 1px solid #e7edf0;
777 | border-radius: 3px;
778 | color: #47525d;
779 | background-color: #f7f9fa;
780 | }
781 |
782 | .p-alert--success {
783 | border-color: #d9f4e1;
784 | color: #2c683f;
785 | background-color: #edfaf1;
786 | }
787 |
788 | .p-alert--error {
789 | border-color: #ffcaca;
790 | color: #921515;
791 | background-color: #ffe3e3;
792 | }
793 |
794 | /* Bad */
795 | .p-alert-default {
796 | padding: 1em;
797 | border: 1px solid #e7edf0;
798 | border-radius: 3px;
799 | color: #47525d;
800 | background-color: #f7f9fa;
801 | }
802 |
803 | .p-alert-success {
804 | padding: 1em;
805 | border: 1px solid #d9f4e1;
806 | border-radius: 3px;
807 | color: #2c683f;
808 | background-color: #edfaf1;
809 | }
810 |
811 | .p-alert-error {
812 | padding: 1em;
813 | border: 1px solid #ffcaca;
814 | border-radius: 3px;
815 | color: #921515;
816 | background-color: #ffe3e3;
817 | }
818 | ```
819 |
820 | ### State単体にスタイルを指定しない
821 | [SMACSS](http://shop.smacss.com/)というCSSの設計手法にState(ステート)という状態をあらわすルールがあります。例えば`.is-open`というclassはある要素を開いた状態にするために使用されます。
822 |
823 | このStateに単体でスタイルをもたせないようにしましょう。Stateはコンポーネントに対して指定するようにして、影響範囲を制限しましょう。
824 |
825 | ```css
826 | /* Good */
827 | .foo.is-open {}
828 |
829 | /* Bad */
830 | .is-open {}
831 | ```
832 |
833 | ### IDセレクタを多用しない
834 | CSSはカスケーディングをコントロールしなければ、すぐに破綻してしまいます。IDセレクタは詳細度が圧倒的に高いことや、ページ内に1度しか使用できないため再利用性にも欠けます。classではなくIDで指定する理由が明確に説明できる場合にのみ使用しましょう。
835 |
836 | CSSのIDセレクタとHTMLのid属性とは区別して考えます。id属性はページ内リンクやJavaScriptでのフックとして利用される場合があるためです。
837 |
838 | ### メディアクエリをまとめて管理しない
839 | ファイル容量の削減のためにメディアクエリをブレイクポイントごとにまとめて記述することを禁止します。
840 |
841 | ```css
842 | /* Good */
843 | .foo {}
844 |
845 | @media screen and (min-width: 768px) {
846 | .foo {}
847 | }
848 |
849 | /* Bad */
850 | @media screen and (min-width: 768px) {
851 | .foo {}
852 | .bar {}
853 | .baz {}
854 | }
855 | ```
856 |
857 | 同じ目的のオブジェクトは同じ場所(Sassの場合は同じファイル)に記述するようにしましょう。全体像の把握を難しくさせたり、モジュール(ある機能のひとまとまり)として管理するのを難しくさせます。短期的なパフォーマンスよりも見通しの良さやメンテナンス性を重視しましょう。
858 |
859 | ## プロパティ
860 | ### !importantを多用している
861 | `!important`は基本的に使用を禁止しますが、意図的に使用する場合は許容されます。例えばヘルパークラスは確実に適応させるために使用することがあります。
862 |
863 | ```css
864 | /* Good */
865 | .u-col1of12 { width: 8.33333% !important; }
866 | .u-col2of12 { width: 16.66667% !important; }
867 | .u-col3of12 { width: 25% !important; }
868 | ```
869 |
870 | その場合もスタイルシートの終盤に記述をして、影響範囲を抑えましょう。
871 |
872 | ### スタイルを取り消している
873 | スタイルの取り消しとは、あるスタイルを`0`や`none`などで値をリセットすることです。スタイルの取り消しがあった場合は多くのスタイルを持ちすぎている、スタイルを早く指定してしまっていることが考えられます(リセットCSSは除く)。
874 |
875 | ```css
876 | /* Bad */
877 | .foo {
878 | border: 1px solid #ddd;
879 | }
880 |
881 | /* ある要素内ではボーダーが邪魔になるためリセット */
882 | .bar .foo {
883 | border: none;
884 | }
885 | ```
886 |
887 | Modifierで定義しておくと必要な時にだけ指定することができます。
888 |
889 | ```css
890 | /* Good */
891 | .foo {}
892 | .foo--bordered {
893 | border: 1px solid #ddd;
894 | }
895 | ```
896 |
897 | ### 要素セレクタに装飾的なスタイルを指定しない
898 | `h1`や`p`といったセマンティックな要素セレクタには装飾的なスタイルを指定してはいけません。必ずあとでスタイルを取り消すことになります。
899 |
900 | ```css
901 | /* Bad */
902 | h2 {
903 | padding-bottom: 0.5em;
904 | border-bottom: 1px solid #888;
905 | }
906 | ```
907 |
908 | 要素セレクタにはブラウザのデフォルトスタイルシートやリセットCSSで指定されるような最低限のスタイルにとどめておきましょう。
909 |
910 | ```css
911 | /* Good */
912 | h2 {
913 | font-size: 2rem;
914 | line-height: 1.2;
915 | margin-top: 0;
916 | margin-bottom: 24px;
917 | }
918 | ```
919 |
920 |
921 | ### 高さを指定しない
922 | 閲覧されるデバイスによって同じコンテンツであっても横幅が変わるため高さも可変します。高さを指定しているとコンテンツが隠れてしまったり、余分な余白ができてしまいます。
923 |
924 | ```css
925 | /* Bad */
926 | .foo {
927 | height: 1000px;
928 | }
929 | ```
930 |
931 | 高さは指定せず、コンテンツによって決まるものと考えましょう。ロゴなどの特定のサイズを持ったものはこれに当たりません。
932 |
933 | ### 固定の幅を持たせない
934 | コンテンツブロックは基本的に固有の幅を持つことを禁止します。`width`を固定していると、ある場所では幅が足りず、ある場所でははみ出てしまうなどの弊害が出ます。横幅は可変するようにしておいて、コンテナブロックによってサイズが決まるようにしておきます。
935 |
936 | 幅を指定したい時は、コンテナブロックに`max-width`を指定する、`width`のヘルパークラスを使用する、Modifierでサイズを指定するなどの方法があります。
937 |
938 | ```css
939 | /* Good */
940 | .c-button {}
941 |
942 | .c-wrapper {
943 | max-width: 1200px;
944 | }
945 | .c-button--size-full {
946 | width: 100%;
947 | }
948 |
949 | /* Bad */
950 | .c-button {
951 | width: 300px;
952 | }
953 | ```
954 |
955 | ### カラーコードは短縮させる
956 | `color`プロパティなどで色を指定する時は可読性をあげるために、可能な場合は短縮しましょう。
957 |
958 | ```css
959 | /* Good */
960 | .foo { color: #ddd; }
961 |
962 | /* Bad */
963 | .foo { color: #dddddd; }
964 | ```
965 |
966 | ### 文字列には引用符(ダブルクォート)をつける
967 | `content`プロパティやURLの指定、属性セレクタの指定をする時にはダブルクウォートを使用しましょう。
968 |
969 | ```css
970 | /* Good */
971 | .foo {
972 | content: "this is content";
973 | background: url("logo.png");
974 | }
975 | input[type="submit"] {}
976 |
977 | /* Bad */
978 | .foo {
979 | content: 'this is content';
980 | background: url(logo.png);
981 | }
982 | input[type=submit] {}
983 | input[type='submit'] {}
984 | ```
985 |
986 | ### 0に単位をつけない
987 | 値が0の場合はpxや%といった単位は必要がないため使用しません。
988 |
989 | ```css
990 | /* Good */
991 | .foo { margin: 0; }
992 |
993 | /* Bad */
994 | .foo { margin: 0px; }
995 | ```
996 |
997 | ### 小数点のあたまの0を省略しない
998 | `0.5em`などの小数点の前の0は省略しません。ファイルサイズの削減は考えずに、明示的に指定しましょう。
999 |
1000 | ```css
1001 | /* Good */
1002 | .foo { font-size: 0.5em; }
1003 |
1004 | /* Bad */
1005 | .foo { font-size: .5em; }
1006 | ```
1007 |
1008 | ### すべて小文字で記述する
1009 | プロパティとプロパティ値は大文字でも小文字でも同様に扱われますが、小文字に統一しましょう。
1010 |
1011 | ```css
1012 | /* Good */
1013 | .foo { color: #fff; }
1014 |
1015 | /* Bad */
1016 | .foo { COLOR: #FFF; }
1017 | ```
1018 |
1019 | ### ショートハンドはなるべく避ける
1020 | `font-size`や`margin`などショートハンドが用意されているプロパティがありますが、ショートハンドに指定しなかったプロパティに初期値が渡されてしまうことから意図せずプロパティがリセットされてしまったり、指定する必要がないところにまで指定する必要が出てきます。明示的に何が目的なのかを示すという意味でも使用をなるべく避けましょう。
1021 |
1022 | ```css
1023 | /* Good */
1024 | .foo {
1025 | /* 中央配置にする。 */
1026 | margin-right: auto;
1027 | margin-left: auto;
1028 | }
1029 |
1030 | /* Bad */
1031 | /* 上下を0に指定する必要がない。 */
1032 | .foo { margin: 0 auto; }
1033 | ```
1034 |
1035 | ### ベンダープレフィックスは手動で書かない
1036 | `-webkit-`や`-moz-`などのベンダープレフィックスは手動で書かず、[Autoprefixer](https://github.com/postcss/autoprefixer)を使用しましょう。Sassの@mixinを使用する方法もありますが、@mixinを管理する手間が発生します。Gulpなどの環境が使えない場合は[Prepros](https://prepros.io/)を推奨します。
1037 |
1038 |
1039 | ## Sass
1040 | BootstrapやFoundaitonなどのCSSフレームワークでも使われているようにSassを利用してCSSを書くことがデファクトスタンダードになっています。Sass特有の機能にいくつかのルールを設けます。
1041 |
1042 | ### SCSS記法を使う
1043 | Sassには拡張子が.sassのSASS記法と拡張子が.scssのSCSS記法があります。どちらも一長一短がありますが、SCSS記法を使うことを推奨します。SCSS記法であればCSSの構文とほとんど変わらないので、誰にとっても読みやすいからです。また、SCSS記法の方がよく使われている(CSSフレームワークはほとんどがSCSS記法で書かれている)ので、ソースコードの流用がしやすいこともメリットです。
1044 |
1045 | また、jQueryプラグインなどを使う時もデフォルトのCSSファイルの拡張子を.scssに変更するだけでインポートできるようになります。
1046 |
1047 | ### 機能ごとにコードを分割する
1048 | SassにはCSSとしてアウトプットさせないファイルを定義するパーシャルという機能があります。コードをファイルごとに分割してモジュール化して管理しましょう。scssファイルをアンダースコア(`_`)から始めることでパーシャルファイルとして認識されます。
1049 |
1050 | ```scss
1051 | // パーシャルファイルをインポートする
1052 | @import "_grid";
1053 |
1054 | // インポートしない場合
1055 | // @import "_grid";
1056 | ```
1057 |
1058 | スタイルシートのアウトプットする場所を指定することでカスケーディングを管理しやすくなったり、使うファイルだけインポートすることができます。
1059 |
1060 | ### 変数は繰り返しや演算する場合にだけ使用する
1061 | Sassの変数は特定の値を一括で管理できる便利な機能ですが、使いすぎると可読性を損なったり変数名がバッティングする危険性が増します。同じ値を繰り返し指定する場合や四則演算などで値を変更・再計算する場合にだけ定義しましょう。
1062 |
1063 | ```scss
1064 | // Good
1065 | $unit-base: 1em !dafault;
1066 | .foo {
1067 | margin-left: (-$unit-base);
1068 | }
1069 | .foo__bar {
1070 | padding-left: $unit-base;
1071 | }
1072 |
1073 | // Bad
1074 | $foo-unit: 1em !dafault;;
1075 | .foo {
1076 | padding: $foo-unit;
1077 | }
1078 | ```
1079 |
1080 | CSSフレームワークでは大量の変数が定義されています。これは、コードを変更・追加せずに変数でスタイルを変更するためです。通常のスタイルシートではこういった機能はいらない場合がほとんどでしょう。
1081 |
1082 | ### モジュール内でだけ使用する変数は個別に定義する
1083 | モジュール内で、ある変数や値が繰り返し使用されている場合はモジュール専用の変数を定義しましょう。プロジェクト全体で使用される変数に変更があった場合でも各モジュールで対応できるようにしておきます。
1084 |
1085 | ```scss
1086 | // Good
1087 | $unit-base: 1em !default;
1088 |
1089 | /**
1090 | * #Media
1091 | */
1092 | $media-gutter: $unit-base !default;
1093 | ```
1094 |
1095 | ### 変数だけでなくキーワードや相対値を利用する
1096 | 数値やカラーは必ずしも変数で固定値を指定する必要はありません。例えば親要素の計算値を継承する[inherit](https://developer.mozilla.org/ja/docs/Web/CSS/inherit)キーワードやプロパティの初期値を指定する[initial](https://developer.mozilla.org/ja/docs/Web/CSS/initial)キーワード、同じ要素の`color`プロパティを取り込む[currentColor](http://www.hcn.zaq.ne.jp/___/WEB/css-color-ja.html#currentcolor-color)キーワードなどがあります。
1097 |
1098 | 数値([length](https://developer.mozilla.org/ja/docs/Web/CSS/Length))を指定するときも`px`のような絶対値で指定するよりも、当該要素のフォントサイズを基準にする`em`やルート要素(通常は`html`要素)のフォントサイズを基準にする`rem`のような相対値で指定したほうが柔軟に対応することができます。
1099 |
1100 | ### 変数には!defaultフラグを必ず指定する
1101 | 変数を定義する際には`!default`フラグを必ず指定するようにしましょう。その変数が定義されているファイルより先に同じ変数を定義し直すことで変数の値を確実に上書きすることができます。これによって変数をまとめて管理することが可能になります(必ずしもまとめて変更する必要はなく、あくまでオプションです)。
1102 |
1103 | ```scss
1104 | // Good
1105 | $unit-base: 1em !default;
1106 |
1107 | // Bad
1108 | $unit-base: 1em;
1109 | ```
1110 |
1111 | ### 直接的な変数名は用途をあらわす変数名に格納する
1112 | カラーパレットとして`$color-red`のような変数名をつける場合は(例えば`$color-error`のような)用途をあらわす変数名にいったん格納してから指定するようにしましょう。値を変更したい場合にも一箇所を書き換えるだけで済むようになりますし、変数名と値が矛盾することを避けることができます。ハードコード(特定の状況に固定してしまうこと)は避けて、常に変更がしやすいようにしておきましょう。
1113 |
1114 | ```scss
1115 | // Good
1116 | $color-red: red;
1117 | $color-error: $color-red;
1118 | .foo { color: $color-error; }
1119 |
1120 | // Bad
1121 | $color-red: red;
1122 | .foo { color: $color-red; }
1123 | ```
1124 |
1125 | ### 変数や関数に名前空間をつける
1126 | BootstrapやBourbonといったフレームワークやライブラリを併用した時に変数や関数の名前の衝突が起きないように、名前空間(namespace)をつけましょう。冗長にはなってしまいますが、確実に指定することができるようになります。
1127 |
1128 | ```scss
1129 | // Good
1130 | $my-variable: "variable";
1131 | @funciton my-function() {}
1132 | @mixin my-mixin() {}
1133 |
1134 | // Bad
1135 | $variable: "variable";
1136 | @funciton function() {}
1137 | @mixin mixin() {}
1138 | ```
1139 |
1140 | ### ネストは擬似要素やメディアクエリだけに制限する
1141 | 兄弟セレクタやBEMのElementやModifierを指定するためにSassのネストは便利です。ですが、ネストを制限なく使用すると際限なく縦に長くなってしまい、可読性を損ないます。ネストは擬似要素、擬似クラス、メディアクエリの使用にとどめます。
1142 |
1143 | ```scss
1144 | // Good
1145 | .foo {
1146 | &:hover,
1147 | &:focus {
1148 | ...
1149 | }
1150 | @media (min-width: 768px) {
1151 | ...
1152 | }
1153 | }
1154 |
1155 | // Bad
1156 | .foo {
1157 | &__bar {
1158 | ...
1159 | ...
1160 | ...
1161 | }
1162 | &__baz {
1163 | ...
1164 | ...
1165 | ...
1166 | }
1167 | }
1168 | ```
1169 |
1170 | ネストするセレクタが明確に関係している場合はネストの使用を許容します。別々のルールセットにして記述するよりも理解しやすいと考えられるためです。
1171 |
1172 | ```scss
1173 | // Good
1174 | .foo--gutter-medium {
1175 | margin-left: - $foo-gutter;
1176 | > .foo__item {
1177 | padding-left: $foo-gutter;
1178 | }
1179 | }
1180 | ```
1181 |
1182 | ### 数値の計算には括弧()を常に使う
1183 | Sassでは四則演算を使うことができます。複数の数値を計算させるときに想定外の計算結果になってしまうことを防ぐために括弧`()`で囲いましょう。
1184 |
1185 | ```scss
1186 | // Good
1187 | .foo {
1188 | width: (100% / 3);
1189 | }
1190 |
1191 | // Bad
1192 | .foo {
1193 | width: 100% / 3;
1194 | }
1195 | ```
1196 |
1197 | ### extendはできるだけ使用しない
1198 | extendは継承を可能にするため、Sassファイル内を簡潔にできます。しかし、継承元のセレクタを記述した場所にまとめて出力されるため、カスケーディングを複雑にしてしまう可能性があります。また、CSSにコンパイルした時に膨大なセレクタのついたルールセットができてしまうことで可読性を失う可能性があります。
1199 |
1200 | もし使用するのであれば、同じモジュール内で完結させるか、継承元のセレクタを1つのモジュールにまとめてカスケーディングを管理しやすいようにしましょう。
1201 |
1202 | ### 更新用のCSSファイルを作成する
1203 | Sassを使えないクライアントがCSSファイルを変更する可能性がある場合は、クライアントがスタイルを追加するためのCSSファイルを作成しておくと管理がしやすくなります。
1204 |
1205 | また、そのような場合にはextendは極力使用しないようにして、読みやすいCSSファイルになるようにしましょう。
1206 |
--------------------------------------------------------------------------------
/how-to-bem.md:
--------------------------------------------------------------------------------
1 | # BEM(MindBEMding)によるCSS設計
2 |
3 | ## BEMとは?
4 |
5 | [BEM](https://github.com/juno/bem-methodology-ja/blob/master/definitions.md)は[Yandex](https://www.yandex.com/)という主にロシア人で構成される、検索エンジンなどを作っている企業が使っているCSSの設計方法です。[BEM Tools](https://github.com/bem/bem-tools)という、jsonでデータを管理してBEMの規則に則ったHTMLを生成する仕組みによってサイトが管理されているようです。
6 |
7 |
BEMツールに触れてみる - < /gecko >
8 |
9 | 命名規則はBEMから派生した[MindBEMding](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/)の方が広く使われているので、MindBEMdingをベースに考えていきます。
10 |
11 | MindBEMdingは[csswizardryことHarry Roberts](https://github.com/csswizardry)が提唱したCSSの命名規則のことです。Harry RobertsはCSS界では世界的に有名で、コンサルタントやスピーカーとして世界中で活躍されています。
12 |
13 | * [CSS-Guidelines](http://cssguidelin.es/)
14 | * [csswizardry-grids](https://github.com/csswizardry/csswizardry-grids)
15 | * [inuitcss](https://github.com/inuitcss/inuitcss)
16 |
17 | ---
18 |
19 | これ以降の内容は忠実なBEMの方法論ではなく、BEMの考え方にいくつかのアイデアを加えたものになります。
20 | 公式ドキュメントは日本語に翻訳された[bem-methodology-ja](https://github.com/juno/bem-methodology-ja/blob/master/definitions.md)が公開されています。
21 |
22 | ## BEMの概念
23 | BEMはブロック(Block)、エレメント(Element)、モディファイア(Modifier)の頭文字をとったものです。
24 | Blockはあるパーツ(コンポーネント)の親要素です。BlockはElementと呼ばれる子孫要素を持つことができます。Modifierはバリエーションや状態を変化させるときに指定するもので、BlockかElementと同階層に指定します。
25 |
26 | 命名規則はハイフン2つやアンダースコア2つでつなげます。
27 |
28 | * `.Block`
29 | * `.Block__Element`
30 | * `.Block--Modifier`
31 | * `.Block__Element--Modifier`
32 |
33 | 詳しい説明は後述します。
34 |
35 | ### Block(親要素)
36 | Blockはサイトを構成するパーツのことです。例えばボタン、グリッド、タブ、見出し、画像などで、簡単に言ってしまえばパーツの親要素です。BEMではBlockを起点として考え、サイトはBlockを組み合わせることで構成していきます。
37 |
38 | 例えばブロック要素で余白を持ったシンプルなBlockの`.box`を作りました。
39 |
40 | ```scss
41 | .box {
42 | display: block;
43 | padding: 1em;
44 | }
45 | ```
46 |
47 | ボタンもBlockとして考えることができます。ここではボタンのベースになるスタイルだけを持っています。
48 |
49 | ```scss
50 | .button {
51 | display: inline-block;
52 | margin: 0;
53 | padding: 0.75em;
54 | border: none;
55 | border-radius: 3px;
56 | color: inherit;
57 | font-family: inherit;
58 | font-size: inherit;
59 | line-height: 1;
60 | text-align: center;
61 | text-decoration: none;
62 | background: transparent;
63 | cursor: pointer;
64 | appearance: none;
65 | &:hover,
66 | &:active,
67 | &:focus {
68 | text-decoration: none;
69 | }
70 | &:disabled,
71 | &.is-disabled {
72 | opacity: 0.5;
73 | pointer-events: none;
74 | }
75 | }
76 | ```
77 |
78 | 2つのBlock(`.box`と`.button`)を組み合わせて1つのパーツを作ることもできます。
79 |
80 | ```html
81 |
84 | ```
85 |
86 | ### Element(子孫要素)
87 | ElementはBlockを構成するパーツの1つ1つのことです。例えばグリッドを構成するカラム、タブのボタンやコンテンツ部分、見出しや画像などです。
88 |
89 | BEMではBlock(親要素)から見て下の階層にいる要素はすべてElement(子孫要素)として扱います。なので、Blockは1つしかありませんし、Elementは0個以上から構成されます。
90 |
91 | クラス名は`.block__element`のようにBlockの名前を引き継いでアンダースコア2つでつなぎます。
92 |
93 | 例えばグリッドレイアウトであれば、`.grid`がBlockになり、`.grid__item`がElementになります。
94 |
95 | ```scss
96 | .grid {
97 | display: block;
98 | margin: 0;
99 | padding: 0;
100 | font-size: 0;
101 | list-style-type: none;
102 | }
103 |
104 | .grid__item {
105 | display: inline-block;
106 | width: 100%;
107 | font-size: medium;
108 | font-size: 1rem;
109 | vertical-align: top;
110 | }
111 | ```
112 |
113 | 先ほどの`.box`というBlockのElementとしてボタンを定義しました。Block特有のスタイルがある場合は`Block__Element`として定義します。
114 |
115 | ```html
116 |
117 |
122 | ```
123 |
124 | ボタンのベーススタイルを持っている`.button`というBlockをマルチクラスで指定しました。`.box__button-item`は色やサイズなどの装飾的な指定をするだけでよくなりました。
125 |
126 | ```html
127 |
128 |
133 | ```
134 |
135 | ### Modifier(バリエーション)
136 | ModifierはBlockとElementのバリエーションや状態の変化をつくるときに指定します。
137 |
138 | クラス名は`.block__element--modifier`か`.block--modifier`のようにBlockとElementの名前を引き継ぎ、ハイフン2つでつなぎます。Modifier名は以下のようにつけるといいでしょう。
139 |
140 | * `Block--small` - あるBlockの余白やサイズなどが小さいバージョン
141 | * `Block--pad-small` - あるBlockの`padding`が小さいバージョン
142 | * `Block--item-small` - Block内の`item`というElementが小さいバージョン
143 |
144 | 例えば`.box`というBlockに背景色をつけたパターンが出てきたとします。
145 |
146 | ```scss
147 | .box {
148 | display: block;
149 | padding: 1em;
150 | }
151 | ```
152 |
153 | Block自体ではなく、Blockに背景色を持たせるためのModifierを作ります。
154 |
155 | ```scss
156 | .box--highlight {
157 | background-color: gray;
158 | }
159 | ```
160 |
161 | これで、元のBlockのスタイルを変更することなく、背景色をつけたパターンを作ることができました。HTMLはこのようにマルチクラスで指定することになります。
162 |
163 | ```html
164 |
165 |
166 | ```
167 |
168 | 基本的にリセットしていくよりも、足していくようにすると管理しやすくなります。
169 |
170 | ```scss
171 | /* NG */
172 | .box {
173 | display: block;
174 | padding: 1em;
175 | background-color: gray;
176 | }
177 |
178 | .box--no-highlight {
179 | background-color: transparent;
180 | }
181 | ```
182 |
183 | このパターンはOOCSSの原則のひとつである「構造と見た目の分離」と同じことをしています。つまり、BEMとOOCSSは組み合わせることもできます。
184 |
185 | ## BEMのメリット
186 |
187 | ### 採用するメリットと導入コスト
188 | BEMの概念や命名規則の方法など、導入するためのコストは必要です。ですが、BEMのルールは基本的に案件ごとに変わることはないので、導入コストは最初の案件だけに限られると思います。
189 |
190 | また、逆にルールがあるということは、自分で考える必要がないということです。自分で考えるはずだった時間を、他の重要なことに使えます。後述する命名規則の明確さもメリットのひとつです。
191 |
192 | 基本的なルールが決まっていること、ルールを共通言語にすることでコミュニケーションを円滑にできること。BEMを採用するメリットはこのあたりにあるのかなと思います。
193 |
194 | ### 命名規則の明確さ
195 | BEMを使うメリットのひとつにクラス名からクラスが持っている役割が伝わりやすいことがあります。
196 |
197 | クラス名の単語をハイフンやアンダースコアで区切ると、
198 |
199 | ```scss
200 | .widget-list {}
201 | .widget_list {}
202 | ```
203 |
204 | のようになり、ウィジェットリストなのか、ウィジェットの中にあるリストなのか、人によって解釈が変わってしまう恐れがあります。
205 |
206 | BEMであれば、ウィジェットリストなら
207 |
208 | ```scss
209 | .widget-list {}
210 | ```
211 |
212 | になりますし、ウィジェットの中にあるリストなら、
213 |
214 | ```scss
215 | .widget__list {}
216 | ```
217 |
218 | のようにクラス名だけで判断することができます。
219 |
220 | ---
221 |
222 | BootstrapやFoundationがハイフン区切りでも問題がないのは、
223 |
224 | * フレームワークとして完成した状態で使うことを前提としているのでブレない
225 | * ドキュメントが充実している
226 |
227 | ことがあげられます。実際の案件ではじめからこの条件を満たすことはまずできません。いちから組んでいってもブレにくいルールが必要になります。
228 |
229 | #### 命名規則のアレンジ
230 | BEMのクラス名はBlockの名前を引き継いだりハイフン2つやアンダースコア2つで区切ること、マルチクラス(複数のクラスを指定すること)を前提していることから、class属性の指定が長くなりがちです。
231 |
232 | クラス名をなるべく短くするために、命名規則を案件ごとに微調整することがよくあります。命名規則には3つのパターンがあります。
233 |
234 | 1つ目、通常は`block-name`のように単語をハイフンで区切ります。
235 |
236 | ```scss
237 | .block-name {}
238 | .block-name__element {}
239 | .block-name--modifier {}
240 | ```
241 |
242 | 2つ目、単語をローワーキャメルケースにした場合。
243 |
244 | ```scss
245 | .blockName {}
246 | .blockName__element {}
247 | .blockName--modifier {}
248 | ```
249 |
250 | 3つ目、単語をローワーキャメルケースにして、区切り文字を1つにした場合。
251 |
252 | ```scss
253 | .blockName {}
254 | .blockName_element {}
255 | .blockName-modifier {}
256 | ```
257 |
258 | Elementが複数の単語からなっている場合はより短くできます。
259 |
260 | ```scss
261 | .block-name__element-name {}
262 | .blockName__elementName {} // 2文字少ない
263 | .blockName_elementName {} // 3文字少ない
264 | ```
265 |
266 | ## BEMの基本ルール
267 |
268 | ### クラスセレクタ単体に指定する
269 | BEMではIDセレクタや要素セレクタは使用せず、クラスセレクタで指定します。
270 |
271 | ```scss
272 | /* Good */
273 | .block-name {}
274 | .block-name__element {}
275 | .block-name--modifier {}
276 | ```
277 |
278 | クラスセレクタ単体にスタイルを指定していくので、詳細度は0,0,1,0のままになります。`!important`を使うこともほとんどありません。
279 |
280 | IDセレクタを使わないのはページ内で使い回せるようにするためです。
281 |
282 | ```scss
283 | /* NG */
284 | #block {} // ページ内で1つしか使えない
285 | #block__element {} // Elementが2つ以上ある場合に使えない
286 | #block--modifier {}
287 | ```
288 |
289 | 要素を使わないのは影響範囲を限定させるためです。HTMLタグを限定させないことで使い回しをしやすくできるメリットもあります。
290 |
291 | ```scss
292 | /* NG */
293 | .block > p {} // Block直下の`
`すべてに適応されてしまう
294 | ```
295 |
296 | 案件によってルールを緩くした方が運用しやすいこともあります。どこまで厳密にするかは案件の規模、制作者の人数やレベル、運用をするひとのレベルなどから決めるといいでしょう。
297 |
298 | ### ElementとModiferを単体で使用しない
299 | 「ここは`.box__element`と同じデザインだから、`.box__element`だけ持ってこよう。」と考えてはいけません。ElementとModifierは親となるBlockの中でだけ指定することができます。
300 |
301 | ```html
302 |
303 |
307 | ```
308 |
309 | `.box__element`が汎用的に使えるのであれば、`.box__element`ではなく、別のBlockとして定義しましょう。今は`.box__element`は汎用的に使えるかもしれませんが、スタイルが変更される可能性もあります。
310 |
311 | ## BEMのよくある問題
312 |
313 | ### Element__Element
314 | BEMでは必然的にElementの数が多くなります。Blockから見て孫要素ができることもあります。このときに`.block__element__element`のようなクラス名にしているケースがあります。
315 |
316 | ```html
317 |
318 |
324 | ```
325 | BEMはHTMLの構造をElementによって表す必要はありません。`child-element`のような名前にしても伝わりますし、より具体的です。
326 |
327 | ```html
328 |
329 |
335 | ```
336 |
337 | 例えば多階層のナビゲーションを`element__element`でマークアップした場合、極端な例かもしれませんがこのように階層が深くなるごとに名前が長くなってしまいます。
338 |
339 | ```html
340 |
341 |
342 | -
343 |
347 |
348 |
349 | -
350 |
354 |
355 |
356 |
357 | ```
358 |
359 | 僕なら以下のようにマークアップします。`.nav__items`の子要素は`.nav__child-items`のように`child`をつけています。`.nav__child-items`の子要素は`.nav__child-item`のように`s`を抜いて名前をつけています。
360 |
361 |
362 | ```html
363 |
364 |
365 | -
366 |
370 |
371 |
372 | -
373 |
377 |
378 |
379 |
380 | ```
381 |
382 | 階層が深くなっても2つの単語で収めるようにできればコードが読みやすくなると思います。Blockという名前のスコープがあるので、意味が理解しやすい範囲でBlock内で重複しなければ十分です。
383 |
384 | なるべく階層が深くならないようにBlockを組み合わせて使うようにすると、名前の付け方はシンプルになると思います。
385 |
386 | 例えば、上記のナビゲーションをラップする`