Hatena::ブログ(Diary)

Webデザインの日々日記 RSSフィード

2016-05-01

Flexible Boxとfloatのグリッドを選択できるCSSフレームワークを作りました。

概要

BootstrapやFoundationを参考にしながら(パクりながら、ともいう)CSSフレームワークを作りました。

詳細はKomitsuboshi-cssにまとめています。GitHubでも公開中。

ある程度デバッグもできたので公開しましたが、利用する際はベータ版くらいの感覚で使ってください。バグがあれば教えてください。

名称は小三ツ星インターフェイスという屋号でやっているので「Komituboshi-css」という何のひねりもない名前です。当初kicssという名称で開発していたところ、GitHubに既に同じ名前のフレームワーク作っている人がいて、混乱を招きそうなので急遽変更しました。呼び名が長いんで、とりあえず、K-cssと略しますね。KocssとかKoshicssとかいい感じの略称つけたいなぁ。

作成の経緯

さて、世の優れたエンジニアの人たちがBootstrapとかスゴイの作っている中でなんでわざわざ作ったかというと、使わない機能が9割もあって私にはオーバースペックだったからです。

また、実際の案件ではボタンなどの装飾は全部作り直すのでむしろ邪魔になることもあります。グリッドのところだけコピペして使ったり、なんてことも一時期やってたんですが、数回使って以降はグリッドの作り方をまねてスクラッチで作るようになってしまいました。

大抵一度作ったものを使いまわしたりしてたんですが、その途中でBEMという手法を知ったり、SassやGitを使い始めたり、大規模案件にちょっと関わって大体のページのレイアウトパターンが見えてきたり、Foundationというフレームワークを知ったりと、自分がいろいろアップデートされてきたこともあり、一度まとめたいなと。

工夫したところとか

最初に(パクりながら)と書きましたが、実はパクったというほどには原形をとどめていません。

グリッドの配置方法

Bootstrapはrowのネガティブマージンを子要素のpaddingで相殺するやりかたですが、K-cssは親要素(rowではなく、float,flex,flex-rを指定します)のネガティブマージンを、子要素のmarginで相殺しています。paddingを使うと、background-colorを指定したときに隣の要素と完全に接触しちゃうんで都合が悪かったんです。borderをつけたいときとかも困ったのでこの点はBootstrapとは異なります。

これに伴い、子要素のwidthにcalcを使って、要素の幅はリキッドで、要素間のmarginは固定しています。

子要素間のmarginは、ブレイクポイント毎に違う値に設定できるようにしました。PCサイズの画面では要素間のmarginを20pxに、画面の小さいスマートフォンでは5pxにしてコンテンツの表示領域を広くとることが可能です。

なお、IE8はcalcプロパティおよびメディアクエリ非対応なので、IE8ではmarginも%指定になりウィンドウ幅に応じて縮みます。

グリッドの命名規則

Bootstrapのrowに当たるのがfloat, col-*に当たるのはcol-(画面サイズ)-(占有グリッド数)となります。

Bootstrapでは画面サイズの表記にxs, sm, md, lg がありますが、K-cssは sp, tab, pcの3種類だけです。文字通りスマートフォンサイズ、タブレットサイズ、パソコンモニタサイズを意味します。

ブレイクポイントはデフォルトで480px,768pxの2つで、containerのサイズを980pxにすることが多いので、これ以上細かくする必要はないと考えています。

基本は12グリッドなので、col-sp-4のように表記します。

指定した画面サイズ未満になると縦並びとなります。

12グリッド+2

12グリッドに加え、N/5, N/7の分割比も用意しました。col-sp-2by5のように表記します。

12グリッドだけで等分割パターンは1,2,3,4,6,12をカバーできますが、これに5と7を加えてより細かくカバーできるようにしました。

これに8から11を加えるかどうかは検討中です。拡張コンポーネントとして作るかもしれません。

子要素の右詰め

Foundationの場合、右端の子要素のみ、float:right;がかかるようになっているみたいです。これにヒントを得て、子要素のクラス名の末尾にrをつけるとその要素にはfloat:right;がかかるようにしました。表記はcol-sp-4rのようになります。

また、使い道は思いつきませんがrのあるものとないものを混ぜることも可能です。

オフセットは未対応

Bootstrapでは子要素の配置をオフセットして表示場所を入れ替えることができますが、その機能は今のところありません。

flexboxレイアウト対応

親要素にflexまたはflex-rをつけると、子要素の高さをそろえることができます。flexが左詰めの横並び、flex-rが右詰めの横並びです。

子要素の表記はfloatの場合と全く同じです。ただし、親要素がflexの場合は子要素にrがついていても左詰め、flex-rの場合は逆に子要素にrがなくても右詰めになります。

ただし、flexboxに非対応のブラウザではfloat同様floatによる配置になるため、親要素flexの場合はrなし、flex-rの場合はrをつける必要があります。非対応のブラウザでもjquery.matchHeight.jsを使って高さがそろうようにしています。(わざわざflex使う必要があったのかどうか悩むところです。)

flex等幅分割

親要素にflex-(画面サイズ)-eqを指定すると、子要素が等幅分割されます。このとき子要素にはクラス名をつけなくてもOK。数が増減しても等幅分割になります。

IE8と10ではtableレイアウトを使っています。

画像キャプション対応

写真の下にキャプションを別途つけられるクラス名を標準で入れました。ただし画像の幅を計算するためのJavaScriptファイルを読み込む必要があります。

変数の管理

このフレームワークはSassを使って書いています。主な変数は_parameter.scssにまとめているので、これを編集してコンパイルすると各案件に合わせたCSSを得ることができます。

課題と今後の展開

解説ページを見ればわかりますが、導入が少しばかり面倒です。読み込むスタイルはstyle.cssのほか、ie8,ie9,ie10用のスタイル、さらにjQueryライブラリ、標準のjsファイル(kcss.js)、旧ブラウザ対応のためにhtml5shiv, jQuery.machHeigt.jsを各ブラウザ向けに読み込ませる必要があります。読み込みも、IE用の振り分けのほか、ユーザーエージェントを判別しての読み込みがあってややこしいことになってます。

よく使う機能についてはcomponentとして随時追加していく予定です。

また、カスタマイズにはSassを使える必要があります。もともとCSS使える人向けに作ったのでこの点はご容赦を。Sassの使い方がわからないという人は下のSassの教科書が1冊あればどうにかなります。

今後追加予定の機能
  1. サイドバーを固定幅にするレイアウトを標準機能として追加する予定です。コンテンツファーストで3カラムまで対応させる予定です。
  2. ハンバーガーボタンのコンポーネント
  3. グローバルナビのコンポーネントを数種類
  4. テーブルレイアウトのコンポーネントを数種類
  5. フォーム用のコンポーネント
  6. いわゆるJumbotronのコンポーネント
  7. アコーディオンメニューのコンポーネント

フォーム用のコンポーネントができたらバージョンを1に上げる予定です。

2016-04-23

中の画像の幅に応じて親要素の幅を調整するjQuery

ようやく目的の情報が見つかったのでメモ。元情報はこちら。

画像の幅を指定し、親要素のwidthに代入し、さらに画像にあてた説明文を画像のaltとして入れたい。 - dentaq log

やりたいこと

  • 量産を前提とする静的なサイトで、画像にキャプションをつけたい。
  • 一枚の画像の下に、画像と同じ幅のキャプションテキストを入れる。
  • キャプションテキストはaltなどから持ってくるのではなく、本文中にキャプションのテキストを入れる。
  • 2枚の画像を横に並べ、その下に2枚分の幅を持つ1つのキャプションテキスト、というレイアウトもある。(下図)

f:id:k3akinori:20160423093441p:image

HTML
<div class="caption">
<img alt="" src="https://placehold.jp/100x40.png" class="caption-img" />
<p class="caption-text">キャプションテキストは画像の幅で折り返す</p>
</div>
<div class="caption">
<div class="caption-img"><img src="https://placehold.jp/100x40.png"> <img src="https://placehold.jp/100x40.png"></div>
<p class="caption-text">2つの画像に対して1つのキャプションテキストを入れることもある。</p>
</div>
<p class="clear"> test</p>
jQuery
jQuery(document).ready(function(){
	$('.caption').each(function(i){
		var captionimg = $('.caption > .caption-img').eq(i).outerWidth();
		$('.caption').eq(i).css('max-width',captionimg);
	});
});

これが前出の元情報の記事をもとにして作ったコード。他の情報源では.eq(i)が抜けていて、そのためページ内のすべてのキャプションの幅が同じになってしまうという問題に直面していた。

.cssに'max-width'を使っているのは、レスポンシブ対応するため。(imgタグにはwidth:auto; max-width:100%;のCSSをかけていて、画面幅が画像より狭くなってもはみ出さないようにしている。

CSS
.caption {
    max-width: 100%;
    box-sizing: content-box;
    line-height: 1.2;
    display: block;
}
.caption-img {
    display: inline-block;
    max-width: 100%;
}
.caption-img > img {
    max-width: 100%;
}
.caption-text {
    display: inline;
    font-size: 14px;
}

その他

キャプションをつける画像が1枚だけの場合、jQueryを使うまでもなく、画像の親要素である.captionに display: table; width: 10px; というCSSだけでキャプション用のボックスを作ることができる。親要素のテーブルよりもコンテンツの画像の幅が大きいとき、テーブルのほうが広げられるという性質を利用している。

ただ、かなり無理やり感のあるやりかただし、複数枚の画像に対して一つのキャプションテキストをつけたい場合には使えなかった。(ボックス内で、最大の幅を持つ画像の幅までしか親要素のtableは広がらない)


Firefox45, IE8〜11(10以下は11の開発者モードによるエミュレーション)で表示の確認済み。Macではどうなるかわからない。

2016-04-16

タップで開閉するメニュー(2段階)

タップ(クリック)で開閉するメニュー - Webデザインの日々日記の応用で2段のメニューを作ったのでメモ。

HTML

<nav class="gnav">
<div class="toggle burg">
  <span class="burg-inner"></span>
</div>
<ul class="menu1">
  <li class="toggle2"><span class="toggle2-inner">menu1</span>
    <ul class="menu2">
      <li><a href="#">menu1-child</a></li>
      <li><a href="#">menu1-child</a></li>
        ・
        ・
    </ul>
  </li>
  <li class="toggle2"><span class="toggle2-inner">menu2</span>
    <ul class="menu2">
      <li><a href="#">menu2-child</a></li>
      <li><a href="#">menu2-child</a></li>
        ・
        ・
    </ul>
  </li>
</ul>
</nav>

jQuery

<script>
jQuery(function(){
  jQuery('.toggle').click(function(){
	jQuery(this).next('.menu1').toggleClass('visible');
	jQuery(this).toggleClass('visible');
	jQuery('.toggle2').removeClass('visible2');
  });
  jQuery('.toggle2').click(function(){
	jQuery(this).toggleClass('visible2');
	jQuery(this).siblings().removeClass('visible2');
  });
});
</script>

CSS

/* メニューの配置 */
.gnav {
	position: absolute;
	top: 0;
	left: 0;
}
/* ハンバーガーボタンのデザイン */
.burg {
	display: block;
	width: 40px;
	height: 40px;
	margin: 0;
	background-color: #654d44;
	position: relative;
	border-radius: 6px;
	cursor: pointer;
}
.burg-inner, .burg-inner::before, .burg-inner::after {
	display: block;
	margin: 4px;
	width: 31px;
	height: 6px;
	background-color: #FFF3E4;
	position: absolute;
	border-radius: 6px;
}
.burg-inner {
	top: 14px;
	left: 2px;
	transition: transform 0.1s linear;
}
.burg-inner:before {
	content: " ";
	top: -16px;
	left: -4px;
	transition: width 0.1s linear;
	transition: top 0.1s linear;
}
.burg-inner:after {
	content: " ";
	left: -4px;
	bottom: -16px;
	transition: width 0.1s linear;
	transition: top 0.1s linear;
}
/* タップ時にアニメーションで矢印に変形させるギミック */
.visible .burg-inner {
	transform: rotate(360deg);
	transition: transform 0.1s linear;
}
.visible .burg-inner::before {
	transform: rotate(150deg);
	width: 25px;
	top: -10px;
	transition: width 0.1s linear;
	transition: top 0.1s linear;
}
.visible .burg-inner::after {
	transform: rotate(30deg);
	width: 25px;
	top: 1px;
	transition: width 0.1s linear;
	transition: top 0.1s linear;
}
/* メニューの開閉 1段目 左の画面外からアニメーションで引き出し表示 */
.toggle {
	position: relative;
}
.menu1 {
	position: absolute;
	width: 140px;
	left: -110%;
	top: 40px;
	text-align: left;
	background-color: #FFF;
	overflow: visible;
	transition: left 0.3s linear;
}
.menu1.visible {
	left: 0;
	transition: left 0.3s linear;
}
/* メニューの開閉 2段目 */
.toggle2 {
	box-sizing: border-box;
	display: table;
	position: relative;
	height: 40px;
	width: 100%;
	padding: 0 4px;
	border-bottom: 1px solid #654d44;
	border-left: 1px solid #654d44;
	border-right: 1px solid #654d44;
}
.toggle2-inner {
	display: table-cell;
	width: 100%;
	height: 100%;
	vertical-align: middle;
	cursor: pointer;
}
.menu2 {
	position: absolute;
	left: -110%;
	width: 100%;
	border-top: 1px solid #654d44;
}
.menu2 li {
	display: table;
	width: 100%;
	height: 40px;
}
.menu2 li>a {
	display: table-cell;
	width: 100%;
	height: 100%;
	vertical-align: middle;
	background-color: #FFF;
	border-bottom: 1px solid #654d44;
	border-left: 1px solid #654d44;
	border-right: 1px solid #654d44;
	padding: 0 4px;
	text-decoration: none;
}
.visible2 .menu2 {
	left: 140px;
	margin-top: -1px;
}

解説

第1階層メニューの表示

<div class="burg toggle">はハンバーガーボタンとなり、これがメニューを表示するスイッチとなる。

jQueryで、"toggle"クラスの要素をクリックすると、"menu1"のクラスに"visible"クラスが追加される。

"menu1"クラスは、初期状態では画面の左端より外側に配置されている(left:110%;)が、"visible"クラスが付与されると、ハンバーガーボタン直下に配置される(left:0;)。このleftの変化に対してCSSアニメーションをつけることで、画面の左側からニュッっとメニューが引き出されてくる。

この"visible"クラスの追加、削除はjQueryの

jQuery('.toggle').click(function(){

jQuery(this).next('.menu1').toggleClass('visible');

}

で行っている。

この関数内の

jQuery(this).toggleClass('visible');

は、"toggle"クラスへの"visible"クラスの追加・削除を行う式で、ハンバーガーボタンの形状変化をこれで制御している。

また、

jQuery('.toggle2').removeClass('visible2');

は、ハンバーガーボタンを押したときに、第2階層の表示も消すためのものである。これがないと、第2階層まで表示した状態でハンバーガーボタンを押してメニューを引っ込めても、表示していた第2階層が残ったままになる。

第2階層メニューの表示

"menu1"直下のliには"toggle2"クラスが付与されている。"toggle2"をタップ(クリック)すると、その直下のの"menu2"クラスに"visible2"クラスが追加される。

"menu2"も"menu1"同様、初期状態では画面の左外側に配置(left:110%;)しておき、"visible2"クラスが追加されると、"menu1"の隣に並んで表示されるようにしている(left:140px;)。この"left:140px;"の140pxは、"menu1"に与えたwidth1と同じ値である。

".visible2 .menu2"の"margin-top:-1px;"は、borderの分、"menu2"が下にずれるのを補正している。


"toggle2"クラスの追加、削除は

jQuery('.toggle2').click(function(){

jQuery(this).toggleClass('visible2');

}

で行われている。これにより、"toggle2"をタップ(クリック)したときに、"visible2"が無ければそれをつけ、あれば消すという動作を行う。

この式だけでは、第1階層メニューを1回ずつタップしていくと、他の第2階層メニューが開いたままになる。他の第1階層メニュー("toggle2")をタップした時には、既に開いている第2階層メニューは閉じるようにしなければならないため、

jQuery(this).siblings().removeClass('visible2');

の式を使う。これは"toggle2"をタップした時、兄弟セレクタにある"visible2"を消す命令となっている。

その他

今回、第1階層メニューにはCSSアニメーションをつけたが、第2階層メニューにはつけていない。第2階層メニューは高さ0からニュッと縦に伸びるようにしたかったが、あまりきれいに動いてくれない(内部の文字が先に表示され、アニメーションのほうが遅れる)ため、今回はCSSアニメーションはつけなかった。

2016-04-10

CSSの::after疑似要素で改行をする

レスポンシブデザインの設計では、画像の元サイズより画面幅が大きいときは元サイズで、画像の元サイズより画面幅が小さい場合は画面幅まで縮小して表示する、という動作にすることがよくあります。CSSで書くとこんな風に。

img {
  width: auto;
  max-width: 100%;
  height: auto;

ただ、この設定をすると、"画面幅が元画像の幅よりちょっとだけ大きい"条件になると、回り込みできる範囲が狭くなり1行当たり数文字という見苦しい感じになってしまいます。

これに対処するため、タブレットやpcサイズでは

img {
  width: auto;
  max-width: 50%;
  height: auto;

として問題を回避しますが、スマートフォンではどうしても1つ目のCSSでいきたい。

そこで、スマートフォンサイズでは回り込みを解除するとともに、画像のあとに改行を入れて文字を画像の下から始めるようにします。

.alignleft {
  float: left;
}
.alignright {
  float: right;
}
@media screen and (max-width:480px) {
  img.alignleft, img.alignright {
  float: none;
  display: block;
  }
  img.alignleft::after, img.alignright::after {
    content: "\A";
    white-space: pre;
  }
}

alignleft, alignrightのクラスは、imgに回り込みを設定するクラスです。

imgタグにこれを設定している場合、スマートフォンサイズで回り込みを解除(float:none;)、after疑似要素で改行コード(\A)を入れるとともに、これを有効にするため"white-space:pre;"を併記します。

で、ここまでは調べると普通に出てくるのですが、重要なのはimg要素に"display:block;"をつけること。これがないと改行が機能してくれません。

2016-03-17

どちらかというとPHPを勉強する入り口としてWordPressかなと。

「WordPressをカスタマイズしたいからPHPを勉強します」は違うと思う。そうじゃない。 | たらハコ

せやな、と。

僕の場合はPHPを勉強する入り口としてWordPressを選んだクチ。それなりに使えるようになってテーマをスクラッチで作れるようにもなった。ただしPHPはまだまだ。

そもそも、PHPを勉強してもWordPressのカスタマイズはできない

WordPressのカスタマイズを行うには、WordPressの関数を知らなくてはいけなくて、それはPHPベースではあるけどWordPress独自のプログラミング、みたいな感じになる。

先にPHPを勉強しても、PHPのコードをどうやってWordPressと連携させればよいのか、というところで引っかかり、結局WordPress関数を勉強することになる。

だったら最初からWordPressを勉強しましょう。

で、WordPressの関数を勉強するためにCodex見てもたぶん初心者は困る。

まずは

WordPress逆引きデザイン事典PLUS[3.x対応]

WordPress逆引きデザイン事典PLUS[3.x対応]

デフォルトテーマのコードが意味しているところを勉強して、カスタマイズに進むには

WordPress関数リファレンスガイド

WordPress関数リファレンスガイド

を読むといい。WordPress関係の本は散々選んできたけど、役立ったのは上の2冊。

初心者向けに適切っぽいものだと

あたりが良いと思う。買ってないけど店頭で読んだ限りではこれから勉強する人にちょうど良いと思った。

ググるときには公式レファレンスも必ず確認すること

困ったときには検索すればよいのだけど、たまにバッドノウハウがあったり、非推奨になっている関数が紹介されていることもあるので、検索した結果出てきたコードの関数についてはWordPress Codex 日本語版でちゃんと調べたほうがいい。

バッドノウハウの代表例はpost_queryを書き換えるもの。これかなり検索で引っかかるけど、公式マニュアルでは「それは本来カスタマイズに使うことは想定していない関数で、デメリットも多いから他のもっといい方法を使いなさい。」と明記されている。

知っている人に聞いたところで分からないよ。

口やメールで説明するのがどれだけ大変か。そもそも関数知らないのに聞いてわかるのかと。何が分からないのかすらわからない状態で聞いてもわからないだろと。まずは「ググレカス」。で、「とりあえずコードを書け」。

そこまでやって動かない時、あるいは動いても何がどうなっているから動いているのかわからないって時に「人に聞け」だと思う。いきなり人に聞いたら上達がむしろ遅くなる。

で、うまくいったらブログにメモる

同じコードを何度も書くのは面倒くさいので、ブログとかにそのコードをメモしておく。やっとかないと、「あの操作はどうすればいいんだっけ」で時間が無駄になる。自分のブログならあとから探すのも早い。

最後に。WordPressは簡単じゃない

最初に触ったCMSMovableTypeで、これHTMLを知っている人なら比較的とっつきやすいんだよね。その感覚でWordPressに踏み込んだら、痛い目にあった。

MTプラグイン入れてテンプレートコードを入れればいいんだけど、WPはプログラミングにぐっと近くなる。かなり大変だった。CSSでカスタマイズするだけのことも、結構大変。開発者ツールでクラス名調べてCSSのプロパティ確認して…ってのの繰り返し。(だから僕は既存のテーマのカスタマイズじゃなくて、テーマを自分で作るほうにシフトした。)

一時期「ブロガーならWordPress使ったほうがいい」みたいな記事があったりしたけど、趣味でやるなら普通の商用ブログのほうがシステムの管理を気にしないでいいんで楽。仕事でやるなら気合い入れて頑張りましょう、ってことで。