編集履歴
2017-06-24 初投稿。
2017-10-04 ソースにバグがあったので修正しました。
この記事について
React軍の侵攻により
HTML城はほぼ抜け殻にされかかっている
(Reactを使うとなぜjQueryが要らなくなるのか - Qiita
などを参照のこと)。
React軍はCSS城を次の標的と定めた css-in-js 大作戦を準備している気配である
(Reactと一緒に使う時のCSS in JSのライブラリ選定とか所感とか - Qiita)
などを参照のこと)。
React軍の中でもっとも勢いがあるのは
styled componentsが率いる小隊であろうか。
・・・すみません、ここからは普通に書きます。
styled components を使うと名前空間の問題などが解決されるということでたしかによさげです
(SPAにおけるCSSについて、ひとつの解 - エンジニアをリングするなどを参照のこと)。
しかしながら、css の歴史は長くかなりのノウハウが蓄積しているため、
急に切り替えるのが難しいところがあります。
また、名前空間の問題はBEM記法でなんとかなったり、そもそも小規模なサイトでは
問題にすらならなかったりします。
そこで、大掛かりな著名 css-in-js モジュールまで使うほどのものではない気がするが、
そうはいっても、css の中で変数を使ったり、条件コンパイル指令みたいなものを使ったりはしたいよね、少しづつ css-in-js みたいなものを実現していこうよ、そんな記事です。
ずばりソースです
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>css-is-js!!!!!?</title>
</head>
<body>
<div id="app">
<form action="#"><ul><li><label><span>Your name: </span><input name="yourName" class="txtfiled"></label></li><li><label><span>Your address: </span><input name="yourAddr" class="txtfiled"></label></li><li><input value="SUBMIT" type="submit"></li></ul></form>
</div>
</body>
<script>
// css-in-js!!!!!?
const gss = (strings, ...values) => {
let ssTxt = strings;
if (strings.raw) {
ssTxt = "";
for (let i = 0; i < strings.length; i++) {
ssTxt += strings[i];
if (i < values.length) ssTxt += values[i];
}
}
const rule = ssTxt.match(/[\w@\-_#:\.]+?\s+[\s\S]*?{[\s\S]*?}|[\w@\-_#:\.]+?\s+[\s\S]*?;|\/\*[\s\S]*?\*\//g);
if (rule) {
const style = document.createElement("style");
document.head.appendChild(style);
rule.forEach(val => {
//console.log("> "+val);
if (val[0] != '/') {
style.sheet.insertRule(val, style.sheet.cssRules.length);
}
});
}
};
// 変数を使う例
const formWidth = 600;
gss`
/*まず、フォーム全体の囲み罫や背景などを指定*/
form {
width:${formWidth}px;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
color: #666;
background: #F0F8F1;
/*↓背景グラデーションの指定*/
background: -moz-linear-gradient(top, #FBFCFC, #F0F8F1);
background: -webkit-linear-gradient(top, #FBFCFC, #F0F8F1);
background: linear-gradient(#FBFCFC, #F0F8F1);
}
/*リスト要素<ul>を初期化*/
form ul {
padding:0;
margin:0
}
/*<li>も初期化し、ボーダーなどを指定*/
form ul li {
list-style:none;
margin:0px;
padding:10px;
border-top: 2px solid #FFF;
font-size: 16px;
}
/*最初の<li>にはボーダーをつけない(:first-child疑似クラスによる指定)*/
form ul li:first-child {
border-top:none;}
/* span要素をブロック化しフロートでフォーム部品と横に並べる*/
form ul li span {
width: 10em;
margin:0px;
display:block;
float:left;
/* floatでなく、CSS3 の「display: inline-block」を使う手もアリ
display: inline-block;*/
}
/*「:after」で <li>の末尾でフロートをクリア*/
form ul li:after {
content:".";
display:block;
height:0;
visibility:hidden;
clear:both; /*←フロートをクリア*/
}
/* テキスト入力部品の見た目を統一する */
form ul li label .txtfiled, form select {
width:250px;
padding:3px 5px;
margin:0px;
color:#666;
border: solid 1px #ccc;
/*テキストエリア内にシャドウをつける。最初にbackground初期化が必要*/
background: #fff;
-webkit-box-shadow: 2px 3px 5px -2px #ddd inset;
-moz-box-shadow: 2px 3px 5px -2px #ddd inset;
box-shadow: 2px 3px 5px -2px #ddd inset;
/*角丸にする*/
border-radius:6px;
-webkit-border-radius:6px;
-moz-border-radius:6px;
}
form select {
/*selectは250では大きすぎるので*/
width:100px;
}
/*チェックボックス、ラジオボタンのブロックの指定*/
form ul li p {
margin:5px 0 0 40px;
}
/*チェックボックス、ラジオボタンの label要素を横に並べる*/
form ul li p label {
margin-right: 2em;
display: block;
float: left;
}
/*input要素とテキストがくっつかないように右にマージンを少し*/
form ul li p label input {
margin-right:0.5em;
}
/*チェックボックス、ラジオボタンの label要素にロールオーバー時の変化を指定する*/
form ul li p label:hover {
background : #FFF;
color: #096;
font-weight:bold;
}
/*チェックボックスの<label>の横幅を統一する(<p>にクラス名.checkをつけて)*/
form ul li p.check label {
width: 9em;
}
/*送信・リセットボタンの体裁を指定([type="○○"]で属性の値でセレクト)*/
form ul li input[type="submit"] ,
form ul li input[type="reset"] {
cursor:pointer; /*←これでカーソルを指先型に変化させる*/
font-size:130%;
font-weight: bold;
width:150px;
padding: 5px 0;
margin-right:0.5em;
border-style:none;
color: #fff;
background:#90E733;
/*↓背景グラデーションの指定です*/
background: -moz-linear-gradient(top, #90E733, #428000 50%, #90E733);
background: -webkit-linear-gradient(top, #90E733, #428000 50%, #90E733);
background: linear-gradient(#90E733, #428000 50%, #90E733);
/*↓以下、ボックスの角丸、シャドウ、テキストシャドウの指定*/
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-moz-box-shadow: 2px 2px 3px 1px #666;
-webkit-box-shadow: 2px 2px 3px 1px #666;
box-shadow: 2px 2px 3px 1px #666;
text-shadow: 1px 1px 2px #000;
}
form ul li input[type="submit"]:hover ,
form ul li input[type="reset"]:hover {
/*↓ここではロールオーバー時の透明度を変更。もちろんグラデーションを指定してもOK*/
opacity: 0.8;
font-size:135%;
}
`;
</script>
</html>
-
サンプルなので React でレンダリングせずにDOMをHTMLに直接記述しています。
-
css の部分は別のjsファイルに分けるのが普通だと思いますが、シンプルなサンプルにするために1つのファイル内に記述しています。
-
gss
関数はモジュールとして別のファイルに記述するのが素直だと思いますが、シンプルなサンプルにするために1つのファイル内に記述しています。 -
css はほんっとにはじめての HTML5 と CSS3というサイトで掲載されていたものを僕が少し弄ったものです。
もしかしたら「gss
の後ろの `
はなんだろう?」という方がいらっしゃるかもしれません。
これは「テンプレートリテラル(Template literal)」とか「テンプレートストリング(Template string)」
とかいうものでES2015で追加された大変便利な機能です。
知らない方はこちら
テンプレート文字列 - JavaScript | MDN
を参照したりググったりしてみてください。
${formWidth}
という書き方がテンプレートリテラルの「テンプレート」たる所以です。
テンプレートリテラルは単なるヒアドキュメントではないのです!
せっかくなので、もう少しだけテンプレートリテラルについて補足(蛇足)します。
${}
の中には式が書けます。
width:${formWidth + 1}px;
ということは、即時関数を使えばほぼ何でもできることになります。
width:${(()=>{let i = formWidth; i++; return i;})()}px;
もっとも可読性が落ちるので素直に変数に容れて埋め込んだほうがいいと思います。
let i = formWidth;
i++;
・・・・・・・
width:${i}px;
最後に
いかがでしたでしょうか。
ソースファイルのライセンスは MIT です。ご自由にお使いください。
少しでも皆様の力になれればうれしいです。