IEのない世界で出来ること

IEのない世界で出来ること

なんか昔書いたIEがどうたらみたいな記事がじわじわ拡散されているようで、気がついたら一番閲覧数のある記事になってました。

あの記事、よく考えたらIEの有害性を訴えるだけでIEがなくなることによるメリットについて説明してないなと思ったので、IEサポートをしないことによって実現できる建設的なWeb制作業務についてのお話をしようと思います。

目次

前書き:IEの現状

StatCounter曰く、(2021年02月時点)IE11の利用率は全世界で1.94%、国内で7%だそうです。ここ一年の傾向としては微減寄りの横ばいというところでしょうか。

2020年02月-2021年02月のブラウザシェア率

この7%をどう捉えるかは各企業・各サービスの商業的判断に委ねられることかと思いますが、イノベーター理論でいうところのラガード中のラガードをサポートしなければならない立場の人はあと4年頑張ってください(延長しなければ2025年10月14日にWindows 10のサポートが終了予定。延長しなければ)。

ちなみにレガシー版Edge(Chromium化前)はもうじき死ぬので考慮不要です(4月13日に削除予定)。 去年はe-Taxとかいう戦犯のせいで悪い意味で話題になりましたが…。

まあなんていうか、かのジョン・レノンも「想像してごらん IEのない世界を」とか歌っていたことですし(歌ってない)、我々ウェブ屋もいいかげんIEは”一切”サポートしないという前提での案件を想定すべき状況になってきたわけです。で、「そういやIEがなくなるとどういうことができるんだっけ?」ってことをおさらいしていくのが今回の本旨になります。

注意事項

  • 「Chrome(およびChromium)で動くけどSafariやFirefoxでは動かない」みたいなテクニックについては掲載していません。

  • ただし、スマホブラウザに関しては基本的にChrome for AndroidかiOS Safari以外での動作は考慮しないものとしています(Androidブラウザ2.1~4.4.4ももはや化石なんで考慮してません)。シェア率的に考えて……。

  • 基本的に「IEでできないこと」の列挙になりますが、対策用のPolyfillが世の中に色々出回ってるのでその気になれば大体なんとかなる…かもしれません。保証はしません。

結構細かく調べたつもりですが、IEの弊害にまつわる全てを把握しているわけではないので「これ別にIEでもできるくね?」「○○がない、やり直し」みたいなのもあるかもしれません。もっと有用なネタがあるぞという方は気が向いたらコメント欄に情報提供してくれたらありがたいです。

IE11でも一応使えるやつ

IEでも使えなくはないけど、バグがあったり機能的制約があったりしたというものです。「もうバリバリ使いこなしてるよ!」という人も多いでしょうが、出来ることの幅が広がったものもあるのでここで改めておさらいしておきましょう。

Flexbox 関連

IEでもわりと使える方。とはいえ凝ったことをすると壊れがち…。 よくハマったFlexboxのバグまとめという記事でFlexbox関連のバグが21件ほどまとめられていますがこのうち12件はIEだけで生じる不具合なので、これらの対応を無視できるだけでも恩恵は大きいと言えるでしょう。 昔書いた記事でもIE対応に煮え湯を飲まされていましたが、今となっては考慮する必要のない話になりそうですね。

ちなみに上記記事内の「垂直方向の margin や padding が無視される」「Flex アイテムが潰れる」「min/max-width/heightが効かない」「align-items: baseline; が効かない」の4つもそれぞれのブラウザの最新版だと解消済みなので、(特殊な要件でない限り)今日日考慮する必要はないでしょう。

Grid Layout 関連

IEでも使えなくはないが、本当に「使えなくはない」というレベルの怪しげな挙動であり、前時代的オレオレ仕様も相まって対応や検証が非常にストレスフルです。Autoprefixerを使えば記述する作業はある程度マシになるとはいえ……。

あ、もちろんIE固有のバグもあります。そもそも機能が不十分だしな。

なんとIEではgrid-auto-flowプロパティが使えません。
grid-template-rowsやcolumnsならば-ms-grid-rowsといったように
ベンダープレフィックスをつければどうにかなるのですが。
ところがどっこい、grid-auto-flowは-ms-を頭につけても使えません。
これが現実……!

過去記事より抜粋

これからはこうした憂いと無縁になれるほか、repeat()関数minmax()関数も使えるようになるのもポイントですね。

Grid Layoutの例
エクセルでわかる(?)グリッドレイアウト用語。実際にはもうちょっと色々な用語があるが割愛。

トラックの繰り返し repeat()関数

グリッドの行および列(トラック)の幅や高さを”繰り返したい”時に使います。読んで字の如くですね。repeat([繰り返す回数], [トラックの大きさ])という具合で使うのですが、分かりにくいので実例を用意しました。

例(図)

例えばこういうものを作る時に、今までであれば、

.grid { grid-template: 40px 40px 80px 40px 40px / 40px 40px 80px 40px 40px; }

と書いていたものを

.grid { grid-template: repeat(2, 40px) 80px repeat(2, 40px) / repeat(2, 40px) 80px repeat(2, 40px); }

とも書けるようになったわけです。まあこの場合だと上の方が簡潔じゃんって思うかもしれませんが、後述の変数を組み合わせて使うこともできるので、存外出番は多いです。

また、「グリッド全体(コンテナ)の幅に合わせて、レスポンシブに子要素(アイテム)を敷き詰めたい」なんてこともあるでしょう。 一応Flexboxでも実現できることではありますが…Grid Layoutで実現できるような”敷き詰める”配置にするにはちょっと面倒だったりします。

Flexboxで子要素を敷き詰める例
Flexboxで子要素を敷き詰める例

Flexboxのflex-wrapを用いて子要素をレスポンシブに並べる場合、上記のように折り返しの端数となる部分の幅は勝手に広がってしまいます。これでいいならこれでいいんですが、幅が不揃いなのが不細工に見えてしまうこともあるでしょう。

このような場合、これまでであればメディアクエリを書いて折り返す画面幅に応じて子要素の大きさなどを変更して対応していたんですが…

こういったオーダーも、Grid Layoutであればサクッと対応することができます。repeat()の最初にauto-fitを指定してやるといいです。

画面幅に応じて折り返すアイテムの例

<style>
.sample {
display: grid;
grid-template-columns: repeat(auto-fit, 64px);
}
.item {
display: grid;
justify-content: center;
align-items: center;
border: 1px solid #393;
margin: 4px;
}
.item:nth-child(odd) {
background-color: #CEFECE;
}
</style>
<div class="gistwrap">
<div class="sample">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
</div>
view raw demo11_01.html hosted with ❤ by GitHub

また、auto-fitと似た値にauto-fillというのもありますが、これは配置スペースが余った場合の振る舞いが異なります。auto-fitの場合は自動でアイテムの大きさが開いたスペースを埋めるように広がりますが、auto-fillでは空のグリッドが埋まるような見た目になります。上記画像のように子要素の幅に固定値を指定していた場合はauto-fitでもauto-fillでも(見かけ上は)ほぼ同様の振る舞いをしますが、こちらは後述のminmax()関数を使用した際に変化が見られます。

最小~最大値の指定 minmax()関数

列幅や行の高さを固定値にしたくないな~、ということもあるでしょう。「画面の幅に応じて柔軟な列の大きさにしたいけど、それはそれとして画面幅が小さすぎる場合でも最低限の大きさは確保しておきたい」とかね。こういう場合はminmax()を使うといいです。

.sample2 {
  display: grid;
  grid-template-columns: minmax(200px, 50%) auto auto;
}

上記の例では最初の要素の幅が通常時は50%、画面幅が狭まった際の最小幅が200pxという風になります(残りはauto)。

このrepeat()関数とminmax()関数を組み合わせて使うことで、メディアクエリを使ってブレイクポイントごとに都度調整しなくても、より簡単にレスポンシブウェブデザインを実現できるようになるのも見逃せないメリットですね。

画面幅に応じて折り返し、アイテムの幅も可変する例

先ほど貼ったサンプルコード群内の.sample部分をgrid-template-columns: repeat(auto-fit, minmax(64px, 1fr));とすることで、上記のように画面幅に応じて折り返しつつ、一つ前の画像例と異なり画面幅に応じてアイテムの幅も広がるようになります。

fr単位:コンテナの大きさに対して自動でグリッドを分割してそのサイズを調整した大きさのこと。たとえば、「grid-template-columns: 1fr 1fr;」としたらそれぞれのアイテムの幅は実質的に50% 50%と同様になり、「grid-template-columns: 1fr 2fr 1fr;」とした場合の幅は25% 50% 25%と同様になる。また、「grid-template-columns: 20px repeat(6, 1fr) 20px;」とした場合は、両端が20px・中央部分は残る全体幅から6分割したサイズの幅になる…といった感じのイメージです。

なお、auto-fit箇所をauto-fillに変更すると空のトラック領域が確保された見た目になります。
参考例:【CSS】Grid Layout を使いこなす – その2 トラック、領域の値

これまではIEでの挙動を恐れながらおっかなびっくりで動かしていた感じですが、ようやくGrid Layoutをフル活用できる時代がやってきましたね。今後はじゃんじゃん使っていきましょう。

参考:
CSS Grid Layoutを極める!(基礎編)
CSSの関数はどんどん便利になっている!minmax()を使うとMedia Queries無しでレスポンシブが簡単に実装できる
CSS GridとFlexboxを使ってメディアクエリなしでレスポンシブにレイアウトする方法

値の計算 calc()関数

IEでも一応使えるけど、width: calc(100% / 3);とかやるとカラム落ちします。なぜならIEくんの計算能力は小学3年生以下なので…。

あとcalc関連でIEで出来なかったことというと、vwと%の計算calc()の入れ子使用あたりですかね。

<style>
/* calc()内でvwと%を併用する例 */
.container {
display: flex;
width: 100%;
padding: 2vw;
}
.sidebar {
width: 20vw;
margin-right: 2vw;
}
.main {
width: calc(100% - 26vw);
}
/*
calc()内の26vw = 4vw(.container内padding幅2vw、左右にあるので2倍) + 20vw(.sidebarのwidth) + 2vw(.sidebarのmargin)
マジックナンバーを書くのが嫌なら変数を使ってもよい(後述)
*/
</style>
<div class="gistwrap">
<div class="container">
<div class="sidebar">サイドメニュー的なものが入るイメージ</div>
<div class="main">メイン領域的なイメージ</div>
</div>
</div>
view raw demo11_2.html hosted with ❤ by GitHub

こういう書き方も今後はOKになるので覚えておきましょう。

その他未分類の小ネタ

見出しの包括 hgroup要素

W3C版HTML5(および5.X)では廃止されたりWHATWG版では続投していたりと曖昧な立場でしたが、結局生き残ることになりました。元からIEでも使えていたので機能的な追加要素はありませんが、「hX要素をグループ化したい時に使う要素」という位置づけになったようです。

<h1>hgroupを使った見出しの例と、そうでない例の違いについて</h1>
<h2>たとえばこんな感じでタイトルがあったとして</h2>
<h3>こんな風にタイトルを補足する見出しも書いてあったとする</h3>
<p>このテキストは上記h3要素のセクションに内包される</p>
<hgroup>
<h2>ホニャラウイルスの感染者数がhoge人を突破</h2>
<h3>――止まらない感染拡大、食い止めるためのpiyo策とは</h3>
</hgroup>
<p>このテキストはhgroupのセクションに内包される(上記のh2要素とh3要素は同階層扱いになる)</p>
view raw demo11_03.html hosted with ❤ by GitHub

参考:hgroup要素:見出しのグループ化 – havin’ a coffee break

グラデーション描画 linear-gradient()関数

開始点と終了点を併記した書き方ができるようになる。

background-color: linear-gradient(red 20%, orange 20%, orange 40%, yellow 40%, yellow 60%, green 60%, green 80%, blue 80%);

と書いていたものが

background-color: linear-gradient(red 20%, orange 20% 40%, yellow 40% 60%, green 60% 80%, blue 80%);

と書けるようになった。また、下記のように書くことで色の切り替わる中間地点を変更することも可能に。

background-color: linear-gradient(lightgreen 10%, 30%, skyblue 90%);

こんな感じ

参考:
MDN Web Docs – linear-gradient()
【css】linear-gradientがIEで反映されなかった時

ベンダープレフィックスが不要になるもの

(説明するまでもないかもしれませんが)ベンダープレフィックスというのは、先進的なCSS機能を利用するために必要な識別子で、要は「特定のブラウザで動かすためのやーつ」です。しっかりした案件ならAutoprefixerで適当にやってると思うんでわざわざ覚えることもないんですが、小規模かつ古めの案件だと手作業修正とかもあるっちゃあると思うんで一応。

あと、IEうんぬん以前の話が二つあるんで先に書いておきます。

  • -moz-と-webkit-以外は忘れろ
    -o-は太古の昔にOperaで使われてましたがもう-webkit-になったので忘れてください。また、-ms-はIEと旧Edgeで動かすためのものだったのでこれから忘れていきましょう。「-moz-はFirefox、-webkit-はそれ以外(でも大抵はSafariの尻拭いに使う)」程度の認識があれば十分です。

  • Androidブラウザ4.4.4以前のことも忘れろ
    具体的に言うとanimationとかtransitionとか@keyframesとかtransformとか。昔はこれらに-webkit-をつける風習がありましたが、流石にもう不要なのでベンダープレフィックスつけるのはやめてください。

参考:そのベンダープレフィックス、いつまでつけてるの?

「IE(および旧Edge)では-ms-プレフィックスが必要だったけど、他のブラウザではベンダープレフィックス不要」系のプロパティに関しては、今後記述が楽になるので頭の片隅に入れておきましょう。

Grid Layout関連

既に触れているので省略。ベンダープレフィックスが不要なのはもちろん、「grid-column: 1 / 4;」に対して「-ms-grid-colum: 1; -ms-grid-span: 3;」も併記しなきゃいけないみたいなIE独自のイカれ仕様のことも忘れてOKです。ありがてぇ~

テキスト選択の制御 user-selectプロパティ

文章を範囲選択できるかどうかを制御する、結構ニッチな用途のやつ。具体例でいうと「サイト内の文章をコピペさせたくないんだけど」みたいなわけわかんねぇ要望がきた時に使うかもしれません(ソース開いたら結局コピペできちゃう都合、厳密な対応が必要な場合はこれだけだと不十分です)。また、現状ではEdgeやSafari対応のために-webkit-は必要です。

IE以外であればuser-select: all指定で「一度クリックしたら文章全体を自動で選択状態にしてくれるやつ」も実現可能なので、今後はそういう用途での出番があるかもしれませんね。

例:ここをクリックすると自動でテキスト選択状態になる

placeholder表示時の擬似クラス :place-holder-shown擬似クラス

placeholoderテキストが表示されている時のスタイルを制御できるやつ。「テキストが入力されてない時は入力欄を赤枠で表示して、入力されたら通常通り」みたいな簡易バリデーション表示とかに使ったり使わなかったりする。

ファイル形式について

IEサポートを切る場合利用可能になるファイル形式などについての覚え書きです。

ローカルフォントを読み込む場合

ローカルフォントを使う場合、@font-faceで色んなファイルを読み込んでいたと思いますが、もうWOFF2(.woff2)だけでOKです(IE11対応する場合は.woffを併記)。

あと、それ以外の.otf/.ttf/.eotはバカクソ古いIEに対応するとか大昔のAndroidブラウザに対応するとかじゃなきゃ不要なんでもう忘れてください。

画像について

従来、どのブラウザでも表示できる画像ファイルというと下記のようなものが挙げられると思います。

  • PNG(.png)
    透過可なため、ページを構成するもろもろのパーツによく使われる。

  • JPEG(.jpg/.jpeg)
    写真やイラストなど、比較的画像サイズが大きく、透過する必要がない画像。

  • SVG(.svg)
    透過可・アニメーション対応といった特徴のほか、ベクター形式なので拡大・縮小しても画質が劣化しない・XMLに準拠しているのでテキストエディタでの編集可・レイヤー機能を持つなどの特徴がある。アイコンやロゴ画像ではPNG形式に代わる主流として普及しつつある。

  • GIF(.gif)
    現在は専ら簡易的なアニメーション用途でしか使われない。大昔は主流だった(IE6までは透過PNGが対応していなかったので)。

  • BMP(.bmp)
    非圧縮フォーマット、要するにWebに不向きなので使うな。そもそもSafariでしか開けないからウェブページで使われることもほぼないけど、TIFF(.tif/.tiff)も同様。

この中だと、IE11で色々バグがあったSVGは今後より一層使いやすくなりましたねー。で、今後は下記のような画像ファイルも利用可能になります。

  • APNG(.apng)
    Animated PNGの略。読んで字の如しで、劣化のない一連のアニメーションに最適。GIFはもちろんのこと後述のWebPよりも性能は高い(ファイルサイズはやや重め)。

  • WebP(.webp)
    ウェッピーと読む(Web Picture formatの略)。アニメーション対応・めっちゃ軽い・高画質とこれまでの画像形式のいいとこどりな性能であるため、JPEG/PNG(APNG)/GIF全ての代替形式たりえる最強選手。

アイコンやロゴなど拡大表示にも対応したい画像はSVG、高画質なアニメーション画像を用いる場合はAPNGを使い(といっても大体はWebPで事足りる)、それ以外は全部WebPで統一するのが新しい時代のスタンダードになっていきそうです。

将来的にはそうなっていきそうなんですが…ここで要注意。MacのSafariではOSがBig Sur以降のバージョンでないとWebP画像は表示されません。そしてこのバージョンがリリースされたのは2020年11月13日と超最近の話でありまして…(なお、iOS版Safariでは2020年9月から先行対応)。参考までに前バージョンのCatalinaは2019年10月7日にリリースされたのですが、翌11月のシェア率が27.3%で、翌々月には32.65%と数字を伸ばしていき、1年後の10月に62.15%のシェア率になりました(StatCounterより)。でも裏を返せば約38%は1年経っても前バージョン以前のOSを使い続けてるわけで……まぁ~、実際のところWebPの本格利用は1年後(≒次のMacOSバージョンがリリースされるぐらい)まで待った方が良さそうです。

2015年9月30日リリースのEl Capitanの最終バージョンが2018年7月頭・2016年9月20日リリースのSierraの最終バージョンが2019年9月末で止まっているあたり、OSサポート期間を考慮するならCatalinaリリースから3年後の2022年の年末ぐらいからは完全にWebP移行してもよさそうといえるでしょう(シェア率を考慮するともうちょっと早めてもいいと思いますがね)。

幸いにしてIE以外のブラウザではpicture要素(後述)によって画像ファイルの代替表示が可能なため、向こう1~2年の間はこれを活用していきましょう。『古いMacOS/iOSのSafariでは.jpgや.png』を『それ以外のモダンブラウザでは.webp』という具合で出し分けが可能です。(background-imageで使う場合はJSライブラリに頼る必要がありちょっと面倒ですが、これも対応自体は可能です)

更にChromeではAVIF(.avif)というWebPの更に上位互換のような画像形式もサポートしているので(Operaも実は対応済み;Firefoxでもそのうちデフォルトサポート予定)、これまたpicture要素を使って.avifファイルの記述も併記しておくとChromeでより高速なページスピードを実現できるようになりオススメです。

画像周りに関しては、各ブラウザベンダーの思惑が交錯している部分もあってかイマイチ統一規格化するスピードが遅いですが、業界の傾向として「一旦何かが覇権を握れば最終的にはみんなそれに追従せざるをえなくなる」のが通例ですので、今後もウェブ技術の最新情報はつぶさにチェックしていきましょう。

参考:次世代画像形式のWebP、そしてAVIFへ

faviconについて

箇条書きでサラッといきます。

  • ICO(.ico)画像はもう不要。PNGでいい

  • ブラウザ用のfaviconサイズは32px*32pxで作る(規格が16px*16pxなのはIEだけです)

  • なんとSVG画像も使える(ダークテーマにも対応!ただしアニメーションは不可)。使う場合type=”image/svg+xml”指定が必須となる。
    記入例:<link rel="icon" href="/favicon.svg" type="image/svg+xml">

昔書いた記事ではfaviconの例で.icoを書いてましたが、あれはIE11対応を前提としていた時代に書いたもんなんでツッコまないでおくれ。まあ時代に合わせていつか加筆・修正することもあると思います(気が向いたら…)。

cursor:url()で使える画像について

任意のカーソル画像を私用する際、IEでは.cur(と.ani)という謎の拡張子しか使えませんでしたが、こいつの存在はもう忘れていいです。PNG/JPEG/GIF/SVGが使えるようになったので。

音声について

これまでは基本的にMP3ぐらいしかまともに扱えませんでしたが、晴れてWAVE(.wav)ファイルの再生が可能になりました。まあWAVEファイルは重たいので、ページスピードを下げてでも高音質での再生が必要とされる場面以外で使うことはなさそうですが(あとは効果音で使うぐらいか)。

なお、意外にもFLAC(.flac)も各主要ブラウザで対応していますので、ロスレス圧縮で音質とファイル容量の軽さを両立させたいならこちらを使うのも視野に入れておきましょう。

え? 「ロスレス再生でも音質は劣化するんですけお!!11!1」って? まあ理屈の上では確かにそうだけども……。一応、容量の他にもFLACにはFLACの利点がありますので、そうしたものを度外視してでも音質を重視したい場合はご自身の優れた聴覚とオーディオ・オカルティズムに殉じてWAVEファイルを使用してください。

その他のファイルについては、AAC(.aac)はFirefox、Ogg Vorbis(.ogg)やOpus(.opus)はSafariでそれぞれ再生不可という状況です。この辺の対応状況については今後もしばらく変化することがなさそう。

動画再生 video要素

特に変化はないです(IE11でもMPEG-4/H.264 ≒ .mp4は読み込めたので)。それ以外のファイル形式は大体Safariで動かないです。

HTML要素関連

※HTMLの仕様に関してはWHATWGのHTML Living Standardの方を参照しています。「よもや廃止されたW3CのHTML5(および5.X)をソースにHTMLについて語る人間はおるまいな?」という感じなんですが、一応念のため。よく分からんという人は過去記事で解説してるからそっち読んでください。

HTML Living Standardで追加・変更になった要素や属性の情報です。

デバイスの幅や比率に応じた画像ファイルの出し分け srcset属性とpicture要素

既に存在を知っている方も多いと思いますが、img要素にsrcset属性とsizes属性が使えるようになり、pitcure要素も使えるようになりました。

srcset属性とsizes属性

srcset属性では、表示させる画像の候補を指定できます。

仕様例:<img srcset="default.jpg 1x, high_definition.jpg 2x" src="default.jpg" alt="">

上記の例では、表示ディスプレイのピクセル比に応じて画像の出しわけができます(Retinaディスプレイではhigh_definition-2x.jpgが表示されるようになります)。

そのほか、画面幅に応じた画像の選別も(状況に応じてsizes属性と併用する形で)対応できるようになりました。使い方や仕組みについては srcsetとsizesが理解できなかった人のために、日本一分かりやすく解説してみた という記事が日本一分かりやすいので読んでみてください(丸投げ)。

なお、sizesの値にはメディアクエリやcalc()も使えるので、その気になれば結構厳密な指定も可能です。

picture要素

このsrcset属性とsizes属性を使えば、候補の中から画面に応じた最適な画像をブラウザが”自動的に”選択してくれるので非常に便利なのですが、一方で、「スマートフォンではトリミングした画像を、PC版ではフルサイズの画像を表示させたい」といったように、複数の画像の中から条件に応じて”手動で”選択したい場合はpicture要素を使います。アートディレクション(デバイスごとに最適な画像を選択する手法)ってやつですね。

<picture>
<source media="(max-width:320px)" srcset="smartphone.png 320w" sizes="100vw">
<source media="(max-width:640px)" srcset="tablet.png 640w" sizes="100vw">
<img src="pc.png">
</picture>
view raw demo11_04.html hosted with ❤ by GitHub

sourceが複数ある場合は、「media属性で指定した条件に見合った画像を選択し、どの条件も満たさなければimg要素を出す」という形で表示画像の選別が行われます。サイズだけが異なる全く同じ画像を出し分ける場合はimg srcsetを使い、内容が異なる画像を明示的に出し分ける場合はpicture要素を使うという認識で使い分けるといいでしょう。

また、picture要素はvideo要素と似たような要領で下記のような使い方もできます。

<p>.webp対応しているブラウザであれば前者の画像が、そうでなければ(つまりIEなら)後者が読み込まれるという例です。 </p>
<picture>
<source srcset="modern.webp" type="image/webp">
<img src="default.png">
</picture>
view raw demo11_05.html hosted with ❤ by GitHub

参考:なんでもかんでもpicture要素を使えばいいわけじゃない!レスポンシブ・イメージ実装の際の注意点

Web Componentsについて

別記事に書きました。HTML Living Standardに関連するだけでなく、CSSやJSの話も結構絡んできます。

このWeb Componentsというのは、「Webページ上の複雑怪奇なHTML/CSS/JS等の記述に影響を受けない、かつ、どのページでもいい感じに再利用できるような『コンポーネント(≒部品)』を使おうぜ」という仕組みになります。

Web Componentsを実現する上で、「Shadow DOM」「カスタム要素(Custom Elements)」「HTMLテンプレート」という三つの技術を組み合わせて使うことが多いです。

改行してもよい位置の明示 wbr要素

要素名の由来は「Word Break」の略。改行してもよい位置を示す際に使用する。「改行”してもよい”」という指定なので、必要がない場合は改行しない。

例:(領域を狭めた時の挙動を下の例と比べてみてください)

https://caniuse.com/?search=wbr%20element

下記のようにwbr要素を文中に含ませることによって、画面幅に応じて任意の改行が生じる(意図しない箇所で改行されるのを予防できる)。

wbrを含む例:

https://caniuse.com/?search=wbr%20element

上記wbrを含む例のソースコードは下記。

<p>https://<wbr>caniuse<wbr>.com/<wbr>?<wbr>search=<wbr>wbr<wbr>%20<wbr>element</p>
view raw demo11_06.html hosted with ❤ by GitHub

ぶっちゃけ使う機会はそんなにない。長い英文をガチャガチャしたい時やタイポグラフィを意識してテキストを画面上に配置したい時に出番があるかもしれないなあというぐらいか。

書字方向の隔離を明示 bdi要素

突然ですが「基本的には左横書きだけど、部分的に右横書き圏の単語や文章を挿入したい」というケースがあったとします。このような場合はHTMLのdir属性を使うことになります。

使用例:
<p><bdi dir="rtl">אֵין לֶחֶם וְאֵין מַים</bdi>という文はWikibooks曰く、ヘブライ語で『人はみな 草』という意味らしい(知らんけど…)。「人間は草花のように栄えては衰える儚いものだ」というニュアンスであり、決して「人間ワロタ」という意味ではない。</p>

dir属性とは書字方向(テキストの向き)を指定できる属性で、値をltrにすると左横書き・rtlにすると右横書きであることを明示できます。このdir属性はグローバル属性なのでどの要素でも使用することができますし、dir属性自体はIEでも普通に動作します。

なお、CSSのdirectionプロパティでも書字方向の指定可能です。…指定可能ですが、通常、この設定はCSSから直接行うのではなく、文書の一部としてHTMLのdir属性を使うなどして行うほうがよいでしょう。とあるので、本文中の右横書きテキストについては基本的にdir属性で対応した方がいいです。文章的な意味を持たない装飾目的の部分についてはdirection: rtl;で代用してもいいでしょうが。

で、やっと本題です。上記例内のbdi要素とは、書字方向の隔離を意味するもので、上記のように左横書き内に右横書きの単語や文が挿入されるようなケース(またはその逆)の場合に使用します。

このbdi要素はIE11に対応していなかったので昔はspan要素などで適当に囲っていましたが、こいつを使うことでより文書構造のセマンティック化ができるようになったね! ってハナシ。

おまけ:bdo要素とunicode-bidi

ついでなんで紹介しておくと、bdo要素を使うと現在の書字方向を上書きして、要素内の文字列が異なる方向に描画されるよう指定できます。例えば…

通常例:戰後の日本は、右橫書きから左橫書きで文字を書くやう改めました。

書字方向を上書き:戰後の日本は、右橫書きから左橫書きで文字を書くやう改めました。

<p>書字方向を上書き:<bdo dir="rtl">戰後の日本は、右橫書きから左橫書きで文字を書くやう改めました。</bdo></p>
view raw demo11_07.html hosted with ❤ by GitHub

bdi要素の場合は(日本語含む)左横書きの言語に対してdir=”rtl”指定しても右から読む形式にならないのに対して、bdo要素を用いた場合は強制的に逆さ書きにすることができます。「!すでのな」とか「ばーしぱす」みたいなやつですね。

このbdo要素はIE11対応していますが、下記のようにCSSで代用する場合はIEだとできないです(unicode-bidiがIEで未対応のため)。

.reverse_text {
  direction: rtl;
  unicode-bidi: bidi-override;
}

unicode-bidiについて
Unicode(UTF-8)では文中に左横書き文字と右横書き文字が混在していた場合でも、『双方向アルゴリズム』という仕組みに基づいて単語の順番を自動で解釈してくれるわけなのですが、このアルゴリズムを任意の形式に上書きしてくれるプロパティがunicode-bidiだそうです。シランケド 下記サイトの例が参考になるかも。
unicode-bidi「双方向アルゴリズムの変更」

おまけ:dialog要素について

ダイアログボックスやアラート表示させる目的で使用される要素として、dialog要素がHTML Living Standardで定義されました。

…定義されてはいるのですが、現状IEサポートを打ち切っても一部ブラウザで使用できません(Firefoxで要設定・Safariで未対応)。結構便利そうなんですが地味に実用化まで遠いです。まあIEの時代と違って「いつかは使えるようになるはず」なので、頭の片隅には入れておいてもいいかもしれません。

幸いなことにPolyfillは存在するので、一応使おうと思えば使えなくはないです。

その他HTML Living Standardで変更があった要素や削除された要素について詳しく知りたいという方は、分かりやすくまとめられている下記サイトを読むのがオススメです。
HTML Living standard|HTML5|HTML-havin’ a coffee break|珈琲とウェブデザイン

おまけのおまけ:menu要素

menu要素というものもHTML Living standardでは定義されているのですが、現状対応ブラウザが皆無に等しいので、今はこいつのことを知らなくても問題なさげです。

CSSプロパティ関連

IEが足枷になっていたせいで使えなかった(または使用に制限のあった)CSSプロパティ/擬似要素/関数あたりの情報です。

比較関数 min()やmax()など

具体的な使用例を添えて説明します。

最大値を設定

CSSで、「幅の大きさを50%としたいが、最大800px以下に収めたい」例。

width: 50%;
max-width: 800px;

これまでは上記のような書き方をしていましたが、min()関数を使えば以下のように書くこともできます。

width: min(50%, 800px);

これだけだったらmax-widthでいいじゃんって思うかもしれませんが、こいつが位置(position)や余白(padding/margin)でも使えるって聞いたら結構便利に思いませんか、という話。

最小値を設定

最大値が設定できるなら最小値も設定できるのが道理というものです。「50%の幅を取るが、最小でも800px以上になる」例が下記になります。

width: max(50%, 800px);

また、複数の単位からよりベターな方を選びたいこともあると思います。

width: max(50%, 88rem, 800px);

上記のような指定をした場合、「50%の幅を取るが、最小でも88remまたは800px以上(より大きい方が基準)」になります。min()関数の場合は逆で、2番目以降の引数の中で最も小さい値が最小値として設定されます。


min()なのに最大値?max()なのに最小値?と混乱するかもしれないですが、 「要はminなら(2番目以降の引数)以下maxなら以上」と覚えるといいかもです。

推奨値を設定しつつ、その最大値と最小値も設ける

見出しの通りで、○○を基準に□□以上△△以下の大きさに収めたい欲張りさん向けの関数もあります。

width: clamp(200px, 50%, 800px);

上記は「50%の幅を取るが、必ず200px以上800px以下の範囲に収まる」という例になります。

borderやfont-sizeなんかで使うといいかもしれませんね。たとえば「テキストをスマホで読みやすくしたいが、ディスプレイ表示した時に文字が大きくなりすぎるのも困る」なんてケースがあるわけで。

font-size: clamp(12px, 6vw, 24px);

上記のようにすれば「画面幅がどれだけ小さくても最低12px、逆にどれだけ大きい場合でも最大24pxの範囲に収まる」というフォント指定が可能になるというわけです。

ちなみに、これらの比較関数は単位のない数字を指定すると機能しなくなるのでそこだけ注意しましょう。
参考:CSSの比較関数が便利すぎる!min(), max(), clamp()の使い方を詳しく解説

カスタムプロパティ(変数)

なんとなんと、今やCSSメタ言語なしでCSSに変数が使えるようになりました。いい時代になったものだ。

/* グローバル変数はrootセレクタ内で定義できる */
:root {
--name: #F00;
}
.text {
/* colorに--nameの値(#F00;)が設定される */
color: var(--name);
}
.text2 {
--Name: #0F0;
color: var(--Name);
/* --Name.text2内およびその子要素で使用可能 */
}
view raw demo11_16.css hosted with ❤ by GitHub

こんな感じで、頭にハイフン2つつけて宣言して、var()で呼び出します(小文字と大文字は区別されるので、–nameと–Nameは別物として扱われてます)。また、

color: var(–selectedColor, –defaultColor);

とすれば「–selectedColorが定義されていない(または無効な値である)場合は–defaultColorとする」、なんて設定もできます。

更なる応用としてJS経由で定義済みのCSS変数値を変更なんてことも出来ちゃいます。天才だ~。

便利な機能ではありますが、多用しすぎて意味不明な変数が濫用されるようになってしまっては元の木阿弥なんで扱い方は要注意です。あくまで必要な範囲での使用に留めておくが吉。

朗報です、CSSハックを覚える必要は実質的になくなりました。 もちろんJavaScriptで(Chromeで段階的廃止が進められている)userAgentに頼る必要もありません。リンク先では2020年中にuserAgentが廃止される予定と書かれてますが、実際はコロナ禍で2021年内に延期されました。

これは機能クエリといって、「その宣言をサポートしている場合は{}内のスタイルを適用するよ」といったものになっております。要はメディアクエリのCSS宣言版(?)です。

@supports ([宣言]) {}といった感じで使うものなのですが、分かりにくいので下記に実例を書きました。backdrop-filterという、Firefoxで(実質)非対応のプロパティを使用した例になります。

<h1>背景だけにエフェクトを加えるプロパティ</h1>
<p>ここでは、2021年1月現在Firefoxでデフォルトサポートされていない(要ブラウザ設定)、backdrop-filterというプロパティを例にします。</p>
<p>下記のテキストは、標準設定のFirefoxに限り表示されません。</p>
<div class="backdropFilter">
<blockquote cite="https://developer.mozilla.org/ja/docs/Web/CSS/backdrop-filter">
backdrop-filter は CSS のプロパティで、要素の背後の領域に、ぼかしや色変化のようなグラフィック効果を適用することができます。要素の背後のすべてに適用されるため、効果を見るためには少なくとも一部が透明な要素またはその背景を作成する必要があります。
</blockquote>
<p>簡単にいうと「要素内部のコンテンツには影響させることなく、背景のコントラストやぼかし設定を変更できる」やつってことです。</p>
</div>
<style>
body {
color: #FFF;
background: url(https://placeimg.com/1000/320/nature) no-repeat 0 0 / cover;
}
/* 値の省略は不可なので、(backdrop-filter: none)としておく */
@supports (backdrop-filter: none) {
.backdropFilter {
backdrop-filter: blur(40px);
}
}
/* backdrop-filterプロパティは、Safariだと現状ベンダープレフィックスが必要なため併記しておく */
@supports (-webkit-backdrop-filter: none) {
.backdropFilter {
-webkit-backdrop-filter: blur(40px);
}
}
/* or や and の使用も可能です */
@supports (backdrop-filter: none) or (-webkit-backdrop-filter: none) {
.backdropFilter {
padding: 24px;
margin: 40px 24px;
border: 1px solid #000;
border-radius: 8px;
color: #FFF;
}
}
/* not の場合は括弧の頭につける */
@supports (not (backdrop-filter: none)) and (not (-webkit-backdrop-filter: none)) {
.backdropFilter {
display: none;
}
}
/* なお、上記は@supports not ((backdrop-filter: none) and (-webkit-backdrop-filter: none)) {} と書いても可 */
</style>
view raw demo11_17.html hosted with ❤ by GitHub

orやand、notなんかも使えるのでフォールバックも存外簡単にできちゃいます。これで「今は対応してないブラウザもあるけど、将来的にサポートされるようになったら使いたいプロパティがある」なんてケースにも柔軟な対応ができるようになりましたね。

多言語対応に関する進展 論理プロパティ

「国内では縦書き表示・海外では横書き表示のブランドサイトを作る案件があるんだけど、お願いね。あ、ちなみにアラビア語も対応するらしい」

ミ°ってなりそうな発言ですが、もしもあなたの上司やクライアントがこのようなことを言ってきたらどうしますか? 謀反でも起こしましょうか。

…まあこんなやべー案件が筆者やこれを読んでいる人の身に実際に降りかかることがあるかどうかはさておき、このような大いなる試練を想定して覚えておくべき概念があります。それが論理プロパティです。

おさらい:ボックスモデルとその欠点

論理プロパティについて触れる前に、ボックスモデルという概念を思い出してみてください。 聞きなじみがない? 大丈夫です、この記事を読んでいる人なら説明せずともこの図を見れば一発で理解できるはずです。

ボックスモデル

これでピンと来ない人はこのサイトの解説とか読むと分かるようになると思います。

普段であればこのボックスモデルに基づいて、対象要素の位置と大きさ(幅/高さ)を指定した後paddingやmarginをみたいな感じで余白を設定していくことになりますが……。

  • style_ltr.css(左横書き)

  • style_rtl.css(右横書き);アラビア語やヘブライ語など。(かと本日の前戰とあ)

  • style_vrl.css(右縦書き);日本語や中国語の他、漢字に影響を受けた文化圏はこれに該当するが、ハングルやベトナム語はもう左横書きが基本らしい。知らんけど

  • style_vlr.css(左縦書き);キリル文字でない旧モンゴル語なんかは左から右の縦書きらしいよ。知らんけど

まあ最後の行は要らないにしても、冒頭の例のような要件だとこんな感じでCSSを分けなきゃならんわけで。

分けなきゃならない上に、基本的な意匠やコンポーネントは同じであるにも関わらずそれぞれの配置や余白を90度回転させたり反転させたりした想定での書き換え作業をしないといけないわけで。

一枚ペラのページならまだしも、これを数十ページ単位で適用させ、更新の都度全てのファイルをそれぞれの言語に合った形で更新していくとなると…破滅しますね。

論理プロパティの概念と書き方

そこで登場したのが論理プロパティ。たとえば

width: 300px;
height: 200px;

これが

inline-size: 300px;
block-size: 200px;

横書きの例

横書きのページにおいては上と下の記述は全く同じ振る舞いをします。しかし縦書きのページではどうなるか…?

この宣言にwriting-mode: vertical-rl;を加えて、縦書きモードにした結果を見てみましょう。

縦書きの例

※分かりやすいようにtext-orientation: uprightで縦書き表示にしています。

このような違いが生じます。これまでのボックスモデルでは、指定した要素の”物理的な”幅(width)と高さ(height)を指定していたため、今回のようなケースでは柔軟な対応ができませんでした。しかし、論理プロパティであれば、

  • 文字(≒コンテンツ)の始点から折り返し地点までの部分がinline-size

  • 文字の開始行から最終行までの部分をblock-size

上記の”論理”に基づいてブロックの大きさが定義されます。これを使うことによって要素の『向き』に依存しないレイアウトが実現可能になったのです。

もちろん、列幅と行幅の指定だけでは不十分ですから、他にも関連するプロパティがいくつかあります。表形式でまとめてご紹介。

左横書き時のボックスモデルと論理プロパティの対応表
プロパティの内容 ボックスモデル 論理プロパティ
幅の指定 width inline-size
height block-size
幅の最大値を指定 max-width max-inline-size
max-height max-block-size
幅の最小値を指定 min-width min-inline-size
min-height min-block-size
余白の指定(内側) padding-left padding-inline-start
padding-right padding-inline-end
padding-top padding-block-start
padding-bottom padding-block-end
枠線の指定 border-left border-inline-start
border-right border-inline-end
border-top border-block-start
border-bottom border-block-end
余白の指定(外側) margin-left margin-inline-start
margin-right margin-inline-end
margin-top margin-block-start
margin-bottom margin-block-end
水平方向の配置 text-align: left; text-align: start;
text-align: right; text-align: end;

要するに

inlineなら「列」に関する設定、
blockなら「行」に関する設定。

*-startの場合はその「始点」を基準に、
*-endの場合はその「終点」を基準に効果が適用される…ってなイメージです。

たとえば、左横書き画面でmargin-top(上部の余白)と書いていたものは、margin-block-start(行頭の余白)という風に書き換えることができます。

なお、上記の表では省略しましたがborder系のプロパティに関しては(線の)色・大きさ・種類についても個別指定可能です。
例)border-inline-start-color

一部ブラウザで動かないやつ

まあこんな感じなんですが、「(borderの)radiusはどうやって書くの?」とか「ショートハンドはどうやって書くんだ」とか思う方もいると思うのでそこいら辺の対応表も下記に置いておきます…が。

残念ながら下記の表に書いてあるプロパティは現状ブラウザ対応が完全ではないので使用時には注意が必要です。

左横書き時のボックスモデルと論理プロパティの対応表(非対応ブラウザあり)
プロパティの内容 ボックスモデル 論理プロパティ
位置の指定 left inset-inline-start
right inset-inline-end
top inset-block-start
bottom inset-block-end
位置の一括指定 なし(top, right, bottom, left) inset
なし(left, right) inset-inline
なし(top, bottom) inset-block
余白の一括指定(内側) padding(ショートハンド) なし
なし(padding-left, padding-right) padding-inline
なし(padding-top, padding-bottom) padding-block
線の一括指定 border なし
なし(border-left, border-right) border-inline
なし(border-top, border-bottom) border-block
線の角丸 border-top-left-radius border-start-start-radius
border-top-right-radius border-start-end-radius
border-bottom-left-radius border-end-start-radius
border-bottom-right-radius border-end-end-radius
余白の一括指定(外側) margin(ショートハンド) なし
なし(margin-left, margin-right) margin-inline
なし(margin-top, margin-bottom) margin-block
内容がはみ出した時の設定 overflow-x overflow-inline
overflow-y overflow-block
回り込みの指定 float: left; float: inline-start;
float: right; float: inline-end;
回り込みの解除 clear: left; clear: inline-start;
clear: right; clear: inline-end;
要素の寸法を変更 resize: horizontal; resize: inline;
resize: vertical; resize: block;

厳密にはこの他にoverscroll-behavior-inlineとかcaption-side: inline-start;とかあるけど割愛。

上記はEdge(およびその他Chromiumブラウザ)、Safari(iOS版も含む)で動作しません。更に、overflow-inline以下の項目についてはなんとChrome(Android版も含む)でも動きません。。。

ショートハンドが使えないのはまだいいとして、位置の指定と角丸が対応してないのはかなりキツイですね~。Chromeが対応してるやつはChromiumでもいずれ対応するだろうという希望がありますが、Safariがね…( 一一) IE11を思わせる中途半端な対応ぶりが嫌になっちゃう。

こぼれ話:近年の潮流

ついでなんで論理プロパティに関連する最近のニュースを紹介します。

2020年12月7日にリリースされたBootstrap 5 Beta 1(なお現在の最新版はBeta 2)にて右横書きのサポートが追加されたようで、水平方向に関係する変数名等が変更になったようです。still experimental and will probably evolveとあるのでまだまだ実験的な試みというレベルではあるようですが、論理プロパティという考え方も少しずつウェブ制作の世界に定着しつつあるようです。

実務レベルで今すぐ使う機会があるかどうかはさておき、将来的には使うこともありそうな知識ではあるので、この機にしっかりと覚えておきましょう。

完全な縦書き対応

論理プロパティで話していたコンテンツの「向き」に関連して、縦書きに関するプロパティを紹介します。

元々writing-mode: vertical-rl;を使えばIEでも一応縦書き表示にできるんですが(要ベンダープレフィックス)、それだけだと半角英数字は90度回転した表示になってしまうんですね。

writing-mode: vertical-rl;で縦書き表示にした例

IEサポートを切る場合、これに加えてtext-orientation: upright;を追加してやることで、半角の英数字も縦並べにすることが可能になります。Safariではベンダープレフィックス(-webkit-)が必要ですが。

text-orientation: upright;で英数字も縦書きにした例

また、『半角数字が縦並べになったのはいいけど、たとえば「2020年」と入力した際に数字部分を4行表示じゃなくて1行にしたい(縦中横表示)』ということもあるでしょう。

この場合は、数字部分にのみtext-combine-upright: all;と指定してやれば対応可能です。Safariでもプロパティ名も値も違うけど-webkit-text-combine: horizontal;とすれば一応対応可能。

数字部分にだけtext-combine-upright: all;して縦中横表記にした例

Safariくんさあ…という感じではありますが、これで完全な縦書き表記に対応できる時代にはなりました。

その他文字関連のプロパティ

フォント制御やタイポグラフィに関連しそうなあれこれを列挙しています。

数字の表示形式に関するあれこれ

font-variant-numericプロパティを使うと、数字の表示方法に関する制御ができます。

…といっても、任意の見た目になるかどうかはフォントが対応しているかどうかに依存するので、ここではfont-familyがsans-serifまたはserifの時に機能するものだけ紹介します。

説明 使用例
font-variant-numeric: slashed-zero; 斜線付きゼロ表記 404 NOT FOUND
font-family: diagonal-fractions; 分数表記(スラッシュ区切り) 9と3/4番線
font-family: stacked-fractions; 分数表記(横棒区切り) 1/3も伝わらない

他には序数記号を表示させるfont-variant-numeric: ordinal;や全ての数字を等幅にするfont-variant-numeric: tabular-numsなどがあります。font-variant-numericで数字の表示制御を行う場合は、表示させたい形式に対応しているフォント名をfont-familyに指定してから使うといいでしょう。

参考:その他の値指定時の表示例
font-variant-numericプロパティの使い方・サンプルコード
※上記リンク内ではEdge非対応と書いていますが、Chromium版Edgeでは動作するので現在は使用可能です。

(スマホ画面での)テキストの自動拡大を制御を無効化したい

text-size-adjust: none;で、(モバイル端末での)テキストの自動拡大を防げます。ただしiOS Safariでは-webkit-が必要です。また、Firefox for Androidにも対応する場合は-moz-も必要になります。

ちなみに値部分に%指定すると拡大倍率を指定できます(これはFirefox for Androidでは非対応なので一応注意)。

なお、Firefoxと(Mac)のSafariではtext-size-adujustプロパティは非対応となっていますが、そもそもこのプロパティはPCブラウザでは無視されるのでどうでもいいです。昔は(PC版のChromeやSafariでテキストの拡大表示ができなくなる不具合があったため)text-size-adjustにnoneって指定しない方がいいみたいな話もありましたが、今は普通に使って構いません。

「こんなのどこで使うの?」って思うかもしれませんが、たとえばスマホを横向きにしても文字サイズをそのままにしておきたい場合なんかに使うことがあります。

フォント読み込み中の表示設定

ウェブフォントやローカルフォントを指定していた場合、フォントを読み込むまでの時間が結構かかります(特に和文フォントだと)。このフォント読み込み中の振る舞いをどのようにするかをfont-displayプロパティにて設定できます。

たとえば「ウェブフォントの読み込み中でも代替フォントを表示させて読めるようにしておきたい」なんて場合はfont-display: swap;を使うとよいでしょう。

参考:それぞれのプロパティ値による振る舞いの違いについて
Webフォントを使う場合に font-display 記述子を使ってすぐにテキストを表示させる

ページスピード改善が重要視される昨今の情勢的にも、OS非標準の和文フォントを使用する場合はこのプロパティを使った方がよさげですね。

ちょっち古いけどページスピード関連の書籍はこれがおすすめ。

バリアブルフォントの使用

バリアブルフォントというのは、文字の太さやら幅やらを自由に調整可能なフォントのことです。

日本語対応しているバリアブルフォントが存在していない(そして今後もウェブ上で利用可能な形で登場する見込みは薄い)都合使う機会は少ないと思いますが、「英字オンリーならこういう表現も可能」ってのは頭の片隅に入れておいてもいいかもしれません。

詳しい解説は次世代のフォント技術 バリアブルフォントの世界という記事が分かりやすいです。そうです丸投げです。

なお、font-variation-settingsプロパティ自体はどのブラウザでも使えるが、Safariのみ@font-face内では使えないようなので注意。

テキストが規定の行数を超えた場合は省略表示させる

下記はIE11でも使えたテクニックですが、1行の省略表示しかできませんでした。

.ellipsis {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

これからは複数行の省略表示であっても恐れる必要がなくなりました。たとえば4行を超えた場合省略表示させる場合はこんな感じ。

.line-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* -webkit-line-clamp部分に行数を指定する */

これは下記のような見た目になります。

表示例

現状どのブラウザでも-webkit-が必要なようですが、だいぶお手軽に実装できるようになりましたね。

ルビの配置指定

ruby-positionプロパティでルビの位置を上にするか下にするか決定できます。

<style>
.under {
ruby-position: under;
-webkit-ruby-position: after;
}
</style>
<p>デフォルトだと文字の上にルビが表示される<br>
<ruby>
<rb>黒色槍騎兵艦隊</rb>
<rp></rp><rt>シュワルツ・ランツェンレイター</rt><rp></rp>
</ruby>
</p>
<p>rubyプロパティにruby-position: under;を指定した例<br>
<ruby class="under">
<rb>X↑X↓</rb>
<rp></rp><rt>天上天下</rt><rp></rp>
</ruby>
</p>
view raw demo11_18.html hosted with ❤ by GitHub

上記のソースはこんな感じの見た目になります。

ruby-positionの使用例

なお、Safariでは要ベンダープレフィックス(-webkit-)で、値も異なります。ruby-position: over;が-webkit-ruby-position: before;に、ruby-position: under;は-webkit-ruby-position: after;になります。

この他ルビ関連の話だとruby-alignやdisplay: ruby;があるんですけど、現時点ではFirefox以外で動かないので省略します。

テキストの傍線にまつわる調整

テキストに対して、例えばこのように下線を引く場合、text-decoration: underline;を使っていたことかと思います。しかし、これには「テキスト-線間の余白や線の太さの調整ができない」という欠点があったため、やむを得ずborderプロパティで代用していた方も多かったかと思います。

しかし、text-decoration-thicknessプロパティでは線の太さを、text-underline-offsetで(underline限定ですが)余白の設定を変更できるようになったので、今後はborderに頼る機会が減りそうです。

※なお、これらのプロパティはFirefox for AndroidやSamsung Internet・Opera Mobile(※Opera Miniとかいう更新止まった化石じゃないよ)といった、シェア率低めな一部のモバイルブラウザでは現状非対応なのでちょっと注意です。

text-decoration-skip-inkとかもあるけどSafariで動かないし割愛。

リストの装飾

これを「文字関連のプロパティ」に含めていいかは微妙ですが…

リスト項目の箇条書き記号ボックス(ふつうは黒丸や番号)を装飾する時は、::marker擬似要素を使うといいです。

この疑似要素は、<li>などdisplay: list-itemが設定された要素に対して適用可能。

<style>
.list01::marker {
color: red;
}
.list02::marker {
color: pink;
}
.list03::marker {
color: lime;
}
.list04::marker {
color: violet;
}
.list05::marker {
color: orange;
}
</style>
<ul>
<li class="list01">リンゴ</li>
<li class="list02">モモ</li>
<li class="list03">ナシ</li>
<li class="list04">さくらんぼ</li>
<li class="list05">オレンジ</li>
</ul>
view raw demo11_19.html hosted with ❤ by GitHub

上記はこんな感じになる。

::marker疑似要素の使用例

::marker疑似要素内では一部のCSSプロパティしか使えないので注意です。また、SafariおよびOperaの場合、animationやtransitionを設定することはできないらしいです。

グラフィック関連のプロパティ

グラフィック系(主に画像加工)にまつわるあれこれを紹介します。

画像効果の設定

filterプロパティを使うことで、彩度やら明度やらコントラストやらグレースケールやらボカシやら画像に色々効果をつけることができます。しかも組み合わせての使うことも可能です。

画像例

たとえば上記の眠そうな男性の画像に対して、filter: sepia(100%);とか指定すると

画像例(フィルタ適用後)

こんな感じでセピア調になったりします。「多分この数時間後に本番環境でやらかしちゃったことが発覚するんだろうな…」みたいな、妙な物語性が付与されましたね。

filterプロパティでは以下のような関数が使えます。

  • blur()
    画像をぼかす。%値の指定は不可。

  • brightness()
    明度の調整を行う。明るくしたい場合は100%(または1)を超える数値に設定、暗くしたい場合は100%未満にすればよい。

  • contrast()
    コントラストの調整を行う

  • drop-shadow()
    影を追加する。box-shadowに似たような具合で使い、例えばfilter: drop-shadow(4px, 4px, 4px #000);と指定した場合は、水平・垂直方向に4px、4pxぼかした#000の影を画像に対して適用するということになる。ぼかし度合と色は省略可能。

  • grayscale()
    グレースケールに変換する

  • hue-rotate()
    色相を回転させる

  • invert()
    階調を反転する。俗にいうネガポジ反転ってやつ

  • opacity()
    透明度を適用する。実質opacityプロパティと同様だが、フィルターの併用を行う(後述)場合に使うことがある。詳しい違いが知りたければMDN Web Docsの記述を参照されたし。

  • saturate()
    彩度を変化させる

  • sepia()
    セピア調にする

なお、filter: sepia(100%) opacity(50%);といった具合で関数を組み合わせて使うことも可能です。

簡単な画像加工のためにいちいちPhotoshopを立ち上げる時代は終わりました。やったね。

実はIEにもかつて「filter:progid:DXImageTransform.Microsoft.なんちゃら」っていうのがあったんですが、(IE11で動かないような)そんな大昔の独自規格の話はどうでもいいので省略。


それ以外の使い方としては、filter: url("/path/filters.svg#filter-id"); とすることで、SVGフィルタを使ってより高度な画像効果設定ができます。できるのですが、

ほとんどのSVGフィルタは広くサポートされています。しかし、効果の適用具合はブラウザ間で多少違いが出ることがあります。これはSVGフィルタ効果に使われる個々のフィルタプリミティブに対するサポート状況や、ブラウザバグによるものです。また、SVGフィルタを適用するのがSVG要素かHTML要素かによってもブラウザサポート状況が変わることがあります。

【SVGフィルタ講座①】基本的な使い方&画像にぼかし効果を加える方法より

って感じなんで、SVGフィルターを扱う場合は必要に応じて適宜調べてから使うとよさげです。

コンテンツのリサイズ/トリミングをする

objective-fitプロパティでは、画像や動画を親要素に対してどのようにはめ込むか決めることができます。…っていうとイメージ湧かないけど、要するにリサイズとトリミング(切り抜き)。

例えば、元々800px*533pxの横長な画像に対して下記のようなスタイルを適用したとします。

.object_fit {
  width: 240x;
  height: 240px;
  border: 2px solid #333;
}

案の定、これは最悪な見た目になります。

object-fit: fill;の例

object-fitを指定しなかった場合(あるいは、初期値であるobject-fit: fill;を指定した場合)はこうなります。

これだとあまりにもアレなので、画像が引き延ばされないように調整したくなるのが人の性だと思います。

.object_fit.contain {
  object-fit: contain;
}

object-fit: contain;の例

昔は画像をdivで囲ってdisplay: flexだのalign-items: centerだのjustify-content: centerだのつらつら書いて画像の天地中央寄していたわけですが、これでだいぶ楽になりましたね!

これでうまいことリサイズできたわけですが、人は時に欲深いので「トリミングして枠いっぱいに画像を表示させたいな」とか思うこともあります。

object-fit: none;と指定した場合、リサイズせずそのまま表示します(デフォルトだと画像の中央を中心に表示)。

.object_fit.none {
  object-fit: none;
}

object-fit: none;の例

この画像の場合はこれでもそこそこ様になっているので良さげですが、「元々横長の画像なんだから、縦の大きさに合わせて左右をトリミングしたいんだよね」ということもあると思います。その場合はこうする。

.object_fit.cover {
  object-fit: cover;
}

object-fit: cover;の例

なお、縦長の画像の場合は逆で上下箇所をトリミング表示してくれます。

更に更に
(1)「object-fit: noneでいいけど画像の中央じゃなくて任意の場所を指定したい」
(2)「object-fit: coverでいいけど画像の右側部分だけいらない」
なんてこともあると思います。

/*(1)の例 */.object_fit.none.pos {
  object-position: 100% -134px;
}

(1)の例

/*(2)の例 */.object_fit.cover.pos {
  object-position: left center;
}

(2)の例

object-positionプロパティを使えば、トリミングの基準位置を指定できます(左上が座標0,0として扱われます)。

そのほか、object-fit: scale-down;と指定した場合はnoneとcontainのうち小さい方のサイズに合わせて表示されます(上記の例でいうなら、画像が枠のサイズより小さかった場合は幅を広げずそのまま天地中央寄せになり、そうでなければcontain表示となる)。

用途に応じて画像を切り出したり、コンテンツの幅や高さに合わせて画像を拡大・縮小したり…なんてのをCSSだけでサクッと実現できるようになってかなりありがたいですね~。

画像を自由にトリミング

上記の例でトリミングについて説明したんですが、「いや画像をもっと自由にくり抜きたいんだよな(例えば円形とか)」という場合もあるかと思います。このように画像を任意の形に切り抜きたい場合はclip-pathプロパティを使うといいです。

諸注意:

このように、現状ではブラウザによって対応状況がまちまちなため、現時点では実質「basic-shape(円形/楕円形/多角形)」の切り抜きのみに用途が限られているプロパティだと思った方がよいです。将来的にはSVGのようなpath()指定でどんな複雑な形でも対応できるようになる気がしますが。

たとえば円ならこんな感じでcircle()を使う。

.clip_path.circle {
  clip-path: circle(50%);
}

clip-path: circle()の例

下記のような具合で中心点を変更することもできるので、扇形とかも対応可能だったりする。

.clip_path.circle2 {
  clip-path: circle(100% at 0 0);
}

clip-path: circle()の例2

楕円の場合はellipse()を使用。円と似たような書き方ですが、楕円なんで水平方向と垂直方向の値を指定します。

.clip_path.ellipse {
  clip-path: ellipse(200px 50%);
}

clip-path: ellipse()の例

多角形はpolygon()を使います。まずは正三角形にしてみます。

.clip_path.polygon {
  clip-path: polygon(0 100%, 100% 100%, 50% 0);
}

clip-path: polygon()の例

その気になれば以下のようなアバンギャルドな形にもできる。

.clip_path.polygon2 {
  clip-path: polygon(0 100%, 100% 100%, 50% 0, 100% -35%);
}

clip-path: polygon()の例2

今のご時世滅多にないとは思いますが、もしIEでの表示も視野に入れるならSVG直書きしてガチャガチャやれば一応対応可能です。また、clip-pathを完全サポートしているFirefoxでは外部SVGを読み込んだらうまいことその形に画像が切り抜かれるようなので、将来的にはこうなったらいいなという感じですねー。参考外部SVGによるクリッピング サンプル

画像の上からマスキング CSSマスク

mask-*系プロパティを使うことでCSSでマスキングすることができます。このmask関連のプロパティをCSSマスク(CSS Masking)なんて総称したりもするようです。

マスキングというのは……若干説明が難しいんですが、一つ上のレイヤーに置いた画像に合わせて、下に配置した画像(など)の表示をどうこうすることです。 Photoshopでいうところのレイヤーマスク機能をイメージしてもらえると分かりやすいかも。

たとえばimg要素に対して以下の指定をしてやれば、右側に透過グラデーションがかかった画像にすることができます。

mask-image: linear-gradient(to right, #000 40%, transparent 100%);

mask-imageの例

これまたPhotoshopいらずの便利な機能ですね~。

また、mask-imageに背景透過したSVGやPNG画像を指定してやると、指定した画像の形に合わせて実質的な表示領域の切り抜きが実現できます。

さっきの猫画像に対して、下記の透過PNG画像(sample.png)を配置してみます。

sample.png

mask-repeat: no-repeat;
mask-position: center;
mask-image: url(/path/sample.png);

mask-imageの例

clip-pathでも実現が難しいような、複雑な形の画像切り抜きはこのような方法を用いるのが現状おすすめです。

mask-*から始まるプロパティはいくつかありますが、よく使うものについてはbackground-*系のプロパティと似ているのでわざわざ新たに覚える必要はないです。たとえばmask-repeatでは繰り返しの有無、mask-positionでは位置の指定、mask-sizeでは大きさの指定ができます。それ以外のものについては興味があったら調べてください(一部ブラウザによって使えないものもあります)。

なお2021年03月現在、ChromeやSafari、Operaではベンダープレフィックス(-webkit-)が必要なので注意です。

参考mask-imageプロパティ | cocochido

ブレンドモードの指定

二つの要素が重なった時の見え方をmix-blend-modeで指定できます。つまるところ、Photoshopにおける「比較(明)」「比較(暗)」「乗算」「焼きこみ」あたりがブラウザ上でも出来るようになったということです。

例えば下記のようなものがあったとします。

ブレンドモード未指定

ブレンドモードを指定して、円が重なる箇所の色を変えてみましょう。

ブレンドモード指定

mix-blend-modeではこのように、重なり部分の表示を設定できます。ソースは下記。

<style>
.venn_diagram {
position: relative;
max-width: 240px;
height: 172px;
padding: 24px;
border: 1px solid #232323;
}
.circle {
position: absolute;
top: 24px;
display: flex;
justify-content: center;
align-items: center;
width: 120px;
height: 120px;
border-radius: 50%;
mix-blend-mode: multiply; /* multiply = 乗算 */
}
.circle01 {
left: 24px;
background-color: #9DCADE;
}
.circle02 {
right: 24px;
background-color: #F7E6E3;
}
</style>
<div class="venn_diagram">
<span class="circle circle01">ダイヤモンド</span>
<span class="circle circle02">パール</span>
</div>
view raw demo11_20.html hosted with ❤ by GitHub

値に応じて色々と見た目が変わるのですが、画像例つきで全部説明するのは面倒なので割愛します。気になる方は下記参照。
mix-blend-mode-CSSリファレンス

なお、Safariおよび(Chrome for Android含む)ほとんどのスマホブラウザの場合、SVG要素に対してはmix-blend-modeプロパティが現状効かないので注意が必要です。

背景画像の場合

ところで、このブレンドモード指定をbackground-imageなどで使いたい場合は、background-blend-modeプロパティを使います。

background-color: #F00;
background-image: url(/path/sample_photo.jpg);
background-blend-mode: multiply;

背景色と背景画像を乗算指定した例

これはフレンドのメールリンク踏んだら、さっきからポップアップが止まらねえ・・・という題の画像ですが、元画像と比べて「あなたは赤い部屋が好きですか?」という風になりましたね。って、フラッシュのネタを擦るのはもう古いか…。

mix-blend-mode同様、こっちもSVG対応はブラウザによりけりなので注意です。

参考CSS3のブレンドモードが素敵! 新プロパティmix-blend-modeを使いこなそう – ICS MEDIA

コンテンツの回り込み方を自由に設定 CSS Shapes

新聞記事のように写真の横に文章を回り込ませる必要がある場合、floatプロパティを使って実現することが多いかと思います。

float: left;の表示例

で、上記のように画像が矩形(四角形)なら別にいいんですが、たとえば画像が円形だったとして、その円の形に沿うようにテキストを配置したい…なんて要望に応えることができます。そう、CSS Shapesならね。

このCSS Shapesというのはshape-outsideshape-marginshape-image-thresholdプロパティの総称でして、これらを使いこなすことで下記画像例のような複雑なコンテンツ配置を実現できるようになります。

画像例

上記のソースコード例は下記になります。

<style>
.shaped_image {
float: left;
width: 200px;
height: 200px;
border-radius: 50%;
margin-right: 8px;
shape-margin: 4px;
shape-outside: circle(50%);
background: url(/path/Osamu_Dazai.jpg) no-repeat center center / cover;
}
</style>
<div>
<div class="shaped_image"></div>
<p>恥の多い生涯を送って来ました。自分には、人間の生活というものが、見当つかないのです。自分は東北の田舎に生れましたので、汽車をはじめて見たのは、よほど大きくなってからでした。自分は停車場のブリッジを、上って、降りて、そうしてそれが線路をまたぎ越えるために造られたものだという事には全然気づかず、ただそれは停車場の構内を外国の遊戯場みたいに、複雑に楽しく、ハイカラにするためにのみ、設備せられてあるものだとばかり思っていました。しかも、かなり永い間そう思っていたのです。ブリッジの上ったり降りたりは、自分にはむしろ、ずいぶん垢抜けのした遊戯で、それは鉄道のサーヴィスの中でも、最も気のきいたサーヴィ</p>
</div>
view raw demo11_22.html hosted with ❤ by GitHub

ざっくり一言で説明すると、shape-outsideで対象となる要素の形状を定義して、shape-marginで余白を設定するといった感じです。なお、これらのプロパティはfloatを定義していないと動かないので注意です。

shape-outsideには、円形(circle)の他、楕円(ellipse)・長方形(inset)・多角形(polygon)の指定ができます。前述のclip-pathと同様ですね。ついでにborder-boxやcontent-boxといった具合に、領域に関する設定も可能。

それ以外だとurl()で画像指定もできるんですが、shape-image-thresholdプロパティはこの時に併せて使用します。

<style>
.shaped_image2 {
float: left;
margin-right: 12px;
shape-outside: url(/wp-content/uploads/2021/03/sample.png);
shape-image-threshold: 0.5;
shape-margin: 12px;
}
</style>
<div>
<p>ワンワンワン ワンワンワン ワンワンワワワン ワンワンワン</p>
<img src="/wp-content/uploads/2021/03/sample.png" alt="sample" class="shaped_image2">
<p>僕らは イヌだぞ 元気だぞ 寒さになんか 負けないぞ<br>ワンワンワン ワンワンワン ワンワンワワワン ワンワンワン<br>僕らは イヌだぞ 元気だぞ お腹が減っても 負けないぞ<br>ワンワンワン ワンワンワン ワンワンワワワン ワンワンワン<br>僕らは イヌだぞ 元気だぞ 靭帯切れても 頑張るぞ<br>ワンワンワン ワンワンワン ワンワンワワワン ワンワンワン<br>僕らは イヌだぞ 元気だぞ 水の中でも 元気だぞ<br>ワンワンワン ワンワンワン ワンワンワワワン ワンワンワン<br>僕らは イヌだぞ 元気だぞ 両手はいらない 犬だもの<br>ワンワンワン ワンワンワン ワンワンワワワン ワンワンワン
</p>
</div>
view raw demo11_23.html hosted with ❤ by GitHub

こんなコードを書くと以下のようになります。

shape-outsideに画像を指定した例

ワンはワンでもMetallicaのOneじゃねーかな…という感じですが。

上記のように複雑な図形の場合、polygonでいちいち座標指定していては日が暮れてしまいます。

だから、shape-outsideに画像を指定して、shape-image-thresholdでシェイプとして認識する閾値を設定してやることで自動で形状を識別してしまおうというお話でございます。省略した場合はshape-image-threshold: 0;とみなされ、完全な透過箇所以外はシェイプとして認識されます。

※shape-outsideに指定できる画像形式は、PNGやGIFのほかSVGも対応してます。もっとも、そもそもSVGには座標情報が書いてあるので『将来的には』SVG画像に対して回り込む場合はpath()指定すれば済みそうですが。ただし、現状Safariがpath()表記に対応してないため『将来的には』の話になります…。

デザインの幅が広がる技術の一つなので、ウェブデザイナーの方なんかも(具体的な実装方法はさておき)「こんなこともできるんだー」程度の知識は頭の片隅に入れておいた方がいいかもしれませんね。

参考矩形レイアウトを超えた !? – CSS Shapes で Web のレイアウトはもっと自由になる | PSYENCE:MEDIA

フォーム関連のあれこれ

Webフォームでよく使いそうなものを紹介します。ほとんど疑似クラスの話。

:focus-visibleは現状Safariで動かないですが、Technology Preview版では使えるらしいのでそのうち実装されるかも(input要素にフォーカス時「マウス操作時は枠線を表示させないが、キーボード操作時だけはoutlineを明示」みたいなことができる)。

子孫要素がフォーカスされた場合に対応

inputやselectなどフォーム系の要素を選択した際、その選択された要素に対して:focusが機能しますが、たとえば「テキストボックスが選択(フォーカス)された時はフォーム全体の背景色を変更する」といったことはCSSだけで実現できませんでした。

:focus使用時

:focus-withinであれば、子孫要素がフォーカスされた場合でも動作してくれるのでそれが可能になるというわけです。

:focus-within使用時

2つの画像例のソースは下記。

<style>
.normal_focus, .focus_within {
padding: 12px;
margin-top: 32px;
background: #FEF;
}
.normal_focus input {
width: 300px;
}
.normal_focus input:focus {
color: #F00;
}
.focus_within:focus-within {
background-color: #EFE;
}
.focus_within:focus-within label {
color: #0F0;
}
.focus_within input {
width: 320px;
}
</style>
<!-- 1つ目の例 -->
<form class="normal_focus">
<label>氏名:</label>
<input type="text" value=":focusだと、input内の装飾しかできない">
</form>
<!-- 2つ目の例 -->
<form class="focus_within">
<label>氏名:</label>
<input type="text" value=":focus-withinでinput外の要素を装飾できる">
</form>
view raw demo11_24.html hosted with ❤ by GitHub

参考CSSの疑似クラス「:focus-within」が素晴らしい理由 | コリス

編集不可/可能なものだけ装飾

:read-only疑似クラスを使うと、下記のように編集不可能なものだけを装飾することができます。

<style>
/* 編集不可のものだけ赤文字にする */
:read-only {
color: red;
}
</style>
<p>p要素なので当然編集できない</p>
<textarea value="編集できません" readonly=""></textarea>
<input type="text" value="編集可能な普通のinputなのでcolor: red;にはならない">
view raw demo11_25.html hosted with ❤ by GitHub

また、:read-write疑似クラスでは、下記のように編集可能な箇所を装飾することができます。

上記ソースの例

<style>
:read-write {
color: blue;
}
p:read-write {
color: green;
}
</style>
<textarea>ごく普通のテキストエリア</textarea>
<p contenteditable>contenteditable属性があるとp要素でも編集できるのだ。知ってた?</p>
view raw demo11_26.html hosted with ❤ by GitHub

規定値の範囲内/範囲外の時だけ装飾

input要素への入力値がminからmaxの範囲内であれば:in-range、そうでなければ:out-of-rangeが適用されるよ…っていうものがあります。

イメージが湧きにくいと思うので下記にサンプルとそのソースコードを置いておきます。

数字が1~10なら文字色が緑色になり、そうでなければ背景が赤色になるやつ

<style>
.number_range {
padding: 4px 8px;
margin-top: 8px;
}
.number_range:in-range {
color: green;
}
.number_range:out-of-range {
color: default;
background-color: red;
}
</style>
<input type="number" value="0" min="1" max="10" class="number_range">
view raw demo11_21.html hosted with ❤ by GitHub

ぶっちゃけ<input type="number">以外での使い道があるかどうかは知らんです(誰か教えてくれ)。

min~maxの範囲内/範囲外じゃなくて、min~max内かつ「規定値の」範囲内/範囲外で装飾する、みたいなことができればtype="range"とかtype="date"とかでも応用が効いていい感じなんですが、現状だとそういうのはJSで手を加える必要があります…。

デフォルトの外観をリセット

appearanceプロパティというものがありまして、これは閲覧しているブラウザ標準の外観になるよう指定するためのプロパティです。…が、もっぱらフォーム系の要素に対してappearance: none;で標準の外観設定をリセットし、独自の装飾をする目的で使われがちです。

none以外の値を指定した場合の外観についてはHTMLクイックリファレンスをそれぞれのOS/ブラウザ/デバイスで確認してみてください。

Safari(およびOpera)では要-webkit-なので忘れないよう注意です。

その他未分類

なんか分類に困ったやつらです。かなり有用そうなものもあればそんなに使わなさそうなものもありますが、なんとなく頻繁に使いそうなものから順に書いています。

スクロールしてもついてくるやつ(スティッキー〇〇)

画面をスクロール/スワイプしても固定表示してくれる、(ヘッダーとかバナーとかページ上部に飛ぶやつとか)色々使われがちなスティッキーなんとかは、position: sticky;で楽に実装できます。

ただページの上部/下部に固定表示させたいだけならposition: fixed;を使えばいいわけですが、常時固定されていては困るということもあると思います。

たとえば、この記事のサムネイル画像部分に下記のスタイルを加えただけでこんな風になります。

position: sticky;
top: 0;
z-index: 1;

position: sticky;の例

昔はスクロール位置に応じてpositionの値を変更するJSなんかを書いてお茶を濁していましたが、こうした手間は不要になりました。

スクロール/スワイプに関連するもの

スクロール/スワイプした時に次のコンテンツが全体表示される位置でピタッと止まるサイトとかあるじゃないですか。こういうやつです。

コンテンツ1

コンテンツ2

コンテンツ3

コンテンツ4

上記の例であれば、親要素にscroll-snap-type: y mandatory;(y軸にスクロール停止の仕方を設定)、子要素にscroll-snap-align: start;(スクロールが停止する位置を設定)という流れで実装できます。

scroll-snap関連プロパティ

scroll-snap-type
スクロールのスナップを設定。x/y(またはblock/inline;両方向であればboth)といった風に対象となる軸を指定した後、mandatoryかproximityと設定する。この二つは、スクロール停止位置がコンテンツとコンテンツの中間だった場合の設定で、mandatoryであれば強制的にscroll-snap-alignで設定した位置に移動するが、proximityだとユーザーが任意の位置でスクロールを止めることもできる。

scroll-snap-align
スクロール時のスナップ位置を決定する。startなら視点・endなら終点・centerならコンテンツ中央で止まるようになる。scroll-snap-typeを設定した要素の子要素に設定する。

scroll-padding
スクロール全体の余白(padding)を設定。scroll-snap-typeを設定した要素に指定すると、スクロール始点・終点のスナップ位置に影響する。

scroll-margin
各スクロールの余白(margin)を設定。scroll-snap-typeを設定した要素の子要素に指定することで、子要素のスナップ位置をずらすことができる。

scroll-paddingおよびscroll-marginが分からんという方は下記の記事が分かりやすいです。あと、どっちもSafariでバグがあるそうです。
参考CSSのスクロールスナップの便利な使い方、実装の注意点を徹底解説

scroll-margin-block-endみたいな論理プロパティもあるにはあるけどSafariで動かないです。


上記の他にscroll-snap-stopというプロパティもあり、これはユーザーが大きく画面スクロールした時に各コンテンツを通り過ぎてしまわないように設定できるものなのですが、現状FirefoxとSafariで動かないです。

ついでなんで紹介しとくと、下記はshould not be usedな没プロパティなので使っちゃダメです(一部動くブラウザもあるけど原則使用禁止なので忘れてOK)。

  • scroll-snap-coordinate
  • scroll-snap-destination
  • scroll-snap-type-x
  • scroll-snap-type-y
  • scroll-snap-points-x
  • scroll-snap-points-y
おまけ:将来的な話

ご存知の方も多いでしょうが、パララックスエフェクトという言葉があります。これは、視差効果で奥行きを表現する手法で、ウェブの世界では一般的に手前のコンテンツ‐奥のコンテンツ(または背景)間でスクロールの速度に差をつけることで実現されます。

このパララックスエフェクトやスライダーを実装するために、人はプラグインを導入したりJSをガチャガチャ書いたりしていたのですが、もうちょっと遠い将来に解消する…かもしれません(今はまだ無理)。

CSS Object Model (CSSOM)という、CSSに関する新しい仕様があります。で、その中にscroll-behaviorプロパティというがあります。

残念ながらこいつは現状Safariで使えないので詳しい説明は省きますが、先ほどのscroll-snapとこれを組み合わせて使うことで、CSSだけで色々なものをかなり表現できるようになります。

コンテンツ1

コンテンツ2

コンテンツ3

コンテンツ4

パララックスと呼べるかどうかは微妙だけどなんかこんな感じのフロアナビゲーションとかもJS不要でなんとかなったり。ソースは下記。

<style>
.scroll_wrap {
margin-top: 20px;
}
.scroll_buttons {
position: sticky;
top: 0;
display: flex;
z-index: 1;
}
.scroll_buttons a {
display: flex;
justify-content: center;
align-items: center;
width: 25%;
height: 56px;
border: 1px solid #63ACB7;
background-color: rgba(255, 255, 255, 0.8);
}
.scroll_buttons + .scroll_area {
margin-top: 0;
scroll-behavior: smooth; /* ここでボタン押下時の動作を決めている(スルッとスクロールするよう設定) */
background: url(/path/sample_photo.jpg) no-repeat center bottom / cover;
}
.scroll_wrap .scroll_contents {
-webkit-mask-image: linear-gradient(rgba(0,0,0,1) 15%, rgba(0,0,0,0.8) 100%);
mask-image: linear-gradient(rgba(0,0,0,1) 15%, rgba(0,0,0,0.8) 100%);
}
</style>
<!-- 1つ目の例(scroll-behavior未使用) -->
<div class="scroll_area">
<div class="scroll_contents">
<p>コンテンツ1</p>
</div>
<div class="scroll_contents">
<p>コンテンツ2</p>
</div>
<div class="scroll_contents">
<p>コンテンツ3</p>
</div>
<div class="scroll_contents">
<p>コンテンツ4</p>
</div>
</div>
<!-- 2つ目の例 -->
<div class="scroll_wrap">
<div class="scroll_buttons">
<a href="#jump01">コンテンツ1</a>
<a href="#jump02">コンテンツ2</a>
<a href="#jump03">コンテンツ3</a>
<a href="#jump04">コンテンツ4</a>
</div>
<div class="scroll_area">
<div id="jump01" class="scroll_contents">
<p>コンテンツ1</p>
</div>
<div id="jump02" class="scroll_contents">
<p>コンテンツ2</p>
</div>
<div id="jump03" class="scroll_contents">
<p>コンテンツ3</p>
</div>
<div id="jump04" class="scroll_contents">
<p>コンテンツ4</p>
</div>
</div>
</div>
view raw demo11_27.html hosted with ❤ by GitHub

参考scroll-snapと​scroll-behaviorで​実現できることを​解説するよ

ま、最初に言った通りscroll-behaviorはSafariで動かないので、あくまで「将来的には」こういう風にできるかもね程度の認識でOKです。

ダークモード対応する

ダークモードに対応しているOS/ブラウザの場合、下記のメディアクエリを使うことでその識別が可能です。

@media (prefers-color-scheme: dark) {}

{}内にダークモード時用のスタイルを書けばOKということですね。

当然@media (prefers-color-scheme: light) {}とすればライトモード時の指定もできますが、こっちに関してはデフォルトと同じなのであえて明示したい時ぐらいしか使わないかもしれません。


ついでなので、手っ取り早くダークモード対応するための裏技があるのでついでに紹介しておきます。

@media (prefers-color-scheme: dark) {
html {
filter: invert(1) hue-rotate(180deg);
}
img, iframe {
filter: invert(1) hue-rotate(180deg);
}
}
view raw demo11_28.css hosted with ❤ by GitHub

要は「全部色反転させちゃえば良くね?(でも画像やiframeまで反転しちゃうと困るから再度反転させる形で除外)」という手法になります。雑なやり方でいいならこういうやり方もあるよというご紹介。

参考たった1行のCSSでこれなら簡単!すでに制作済みのWebサイト・スマホアプリをダークモードに対応させる方法

複雑なアニメーションを軽量化する(かも)

will-changeプロパティというものがあります。これはアニメーションに関連するもので、「上手く使えば」ブラウザの負荷軽減やアニメーション動作の軽減に繋がります。

will-change プロパティは、どのような要素の変更が予測されているかブラウザーに助言します。MDN Web Docsより

たとえば、「この要素はホバーされたらなんか動くよ」とか「この要素はJSアコーディオンでどうたらするつもりなのでクリック/タップに応じて開閉の動作が起こると思うよ」みたいなことをブラウザにお伝えしてくれるということです。

何のメリットがあるかっていうと、これを指定しておくことで事前にブラウザ側が動作に備えてくれるので、ページの動作スピードを改善してアニメーションをより滑らかにしてくれる”可能性がある”最終兵器って感じです。注意すべき点が多いので、will-change(変わる’であろう’)という言葉の意味を理解して使うことが肝要です。

will-changeプロパティに関する注意点

・アニメーションする要素に全部will-changeを書けばいいってもんじゃない
むしろパフォーマンスが低下する(ことがある)ため。ブラウザもバカじゃないので可能な限りの最適化はしているわけでして。「使わないで済むなら使わない」ぐらいのものだと考えておいてください。

事前に「これから円の面積を求める問題を出します」って伝えられていたら「あー。確か半径×半径×3.14だよな~」ってスッと解答の準備ができるけど、「これから1+1は?って聞きます」なんてのは前もって伝えるだけ時間の無駄になるよね…みたいな話。

・アニメーションの直前に使え(アニメーションと同時に動かすな)
will-changeはあくまで事前報告として使うものです。

たとえば、ホバーしたらアニメーションする要素に対して:hover擬似クラスで動作の記述と一緒にwill-changeを指定しても「いやwill-changeどころかhoverによる変化後なんですが!?」ってなるわけで、全く意味がありません。

ではその要素の基本的なスタイリングと一緒にwill-changeも記述しておけばいいかというとそうでもなく。というのも、『ホバーしていない場合は動かない』はずのものにwill-changeを指定しているとそれはそれで問題があるのです。ホバーしていようがいまいがブラウザはず~っとアニメーションに備えて無意味に身構えてるわけですからね。

だから、動作する直前だけにwill-changeを付与すべきなのです。この例の場合は、親要素がホバーされたら(恐らく次に子要素がホバーされるだろうから)アニメーション対象の要素にwill-changeを付与する…という書き方がベターな方法になるでしょう。

このプロパティは何か目に見える動作や装飾を行うというものではないので、will-changeが実際に有効に働いているかどうかは開発者ツールを使って調べるといいでしょう。

脳死で使っていい代物ではないので扱いは難しいですが、複雑なアニメーションを実装する場合は考慮すべきプロパティですね。

視差効果を減らしたいユーザーに対応

Flash等に頼らなくてもWeb上でリッチな表現ができるようになって久しいですが、その一方で「コンテンツが激しく動くと酔う」「アニメーションうぜえ」と思ったユーザーはOS側で視差効果を減らす設定をすることがあります。

これを検知して対応できるメディアクエリが下記になります。

@media (prefers-reduced-motion) {}

コンテンツや背景が大きく動くサイトの場合は、これを使って各要素のアニメーション設定を減らす(または無くす)ようにするとアクセシビリティの向上に繋がるでしょう。

JSでしか使わない疑似クラス? スコープとなる要素を選択

:scope疑似クラスというものがあり、これはstyle要素内やCSSファイル上で使う場合は:rootと同じ働きをするので使う意味がないです。

では何のために使うかというと、JSでDOM操作したい時なんかに使われることがあります。

<div class="target">
<div class="parent-1">
<div class="children-1">テキスト(ここはどうでもいい)</div>
<div class="children-2">テキスト(要するに、targetで既に受け取っている要素に対して)</div>
</iv>
<div class="parent-2">
<div class="children-3">テキスト(selectedのように直接の子を取得できたりするということ)</div>
</div>
</div>
<p>
選択されたクラス名 :
<span class="results"></span>
</p>
<script>
const target = document.querySelector('.target');
// スコープ対象(.target)直下のdiv要素を選択
const selected = target.querySelectorAll(':scope > div');
// .results内に選択されたクラス名を出力
document.querySelector('.results').innerHTML = Array.prototype.map.call(selected, function (element) {
return '.' + element.getAttribute('class');
}).join(', ');
</script>
view raw demo11_15.html hosted with ❤ by GitHub

ソース参考::scope – CSS: カスケーディングスタイルシート | MDN

使用例としてはこんな感じ。

JSについて

HTML/CSSの話が続きましたが、IEサポートとという重い足枷から解き放たれたことにより、JSでも出来ることの幅はかなり増えました。 これでようやくES2015(ES6)以降の話が出来る…というところですが、今回の記事では内容に触れません。

だって、今ES2020ですよ!? ここから更に5年分のアップデートについて解説していくとなるとまたすごい文量になってしまうわけで。ググれば入門記事とかいっぱい出てくるだろうしそっちで補って欲しい。

ECMAScriptについて

ところで、ES〇〇ってなんだろうって思う人もいるかもしれないので、導入程度に解説しておきます。かつてのJavaScriptはブラウザごとに独自拡張が行われててカオス化していたため、標準の仕様として生まれたものがECMAScriptです。

このECMAScriptを略してES〇〇と表記しているわけです。また、ES6以降は「ECMAScript 2015」というような具合で発行年が表記されるようになりました。なぜやたらにこのES2015(ES6)が取り沙汰されるかというと、機能面でのアップデートが多かったためです。

しかしながら、IE11がリリースされたのは2013年でありES6の技術を使うことは出来ませんでした。だからPolyfill入れたりBabelでどうにかしたりしていたのですが、今となってはこうしたものも不要ですし、各ブラウザのバージョンアップに応じてES6だけでなくそれ以降の技術も使用することができる、という流れなのでした。

IE11を取り巻く状況

IEがいかにロクでもないものかについては過去記事で触れてるので割愛するとして、実際のところIE11対応が現状だとどのぐらい続いているのか調べてみることにします。

メジャーどころ(?)のCSSフレームワークについてはこんな感じです。

名前 最新バージョン(小数点略) 状況
Bootstrap 5 非対応(4以前は対応)
Zurb Foundation 6 対応(IE9までサポート)
UI KIT 3 対応
Pure CSS 2 対応(IE10までサポート)
Bulma 0.9 非対応
Semantic UI(Fomantic-UI) 2 対応
TailWind CSS 2 非対応(1.9以前は対応)
Material UI 4 対応
Schema 2 対応
Materialize CSS 1 対応

現状だとサポートしてるのも案外多いですね。ま、大きなところで言うとBootstrapのIE11サポート打ち切りでしょうか。 次はJSのプラグイン…はキリがないから、JSフレームワーク/ライブラリ。

名前 最新バージョン(小数点略) 状況
Angular 11 対応
React 17 非対応
Vue.js 3 非対応
Riot 4 非対応(v3では対応)
jQuery 3 対応(IE9までサポート)
Backbone.js 1 対応
Knockout.js 3 対応(IE6までサポート。まじ?)

老舗(?)では動くけどReactやVueといった新顔(言うほど新しくもないが)はもうサポート切ってる感じですね。

それ以外のとこで関連ニュースというと、WordPressがIE11のサポート終了を検討中とか?


こんな感じで今後もどんどんIE外しの波は加速していくと思うので、いつまでもIE11の墓守みたいなことはしてないで新しいスキルを身につけていきましょう。


以下、2021/04/12加筆。

追記:書き忘れ

書き忘れたやつを適当にまとめておきます。

セレクタをまとめる疑似クラス

:is()疑似クラスで複数のセレクタを一つにまとめることができます。

ul li span, ol li span, .list span { /* 略 */ }

こういうのを

:is(ul li, ol li, .list) span { /* 略 */ }

こうできるというもの。無くても困らないけどあると便利になる系の代物ですね。

Operaおよび(Chrome for AndroidやiOS Safariを除く)一部のモバイルブラウザだと:is()じゃなくて:-webkit-any()でサポートしているので一応注意。

詳細度で悩みたくない人用

:is()と全く同じ働きをする:where()疑似クラスというものがあります。

では何が違うかというと、:where()の括弧内の詳細度が0として扱われます。厳密には下記。

:where() と :is() の違いは、 :where() は詳細度が常に 0 であるのに対して、 :is() は引数内で最も詳細度の高いセレクターの詳細度を取ります。 :where() – CSS: カスケーディングスタイルシート | MDN

Normalize的用途で使うなら:where()を使ったほうがよさげです。

ただしOperaおよびOpera Mobile・Samsung Internetあたりでは使えないようなのでこちらも注意。

ちなみに余談ですが、:not()とか:emptyとか:targetは普通にIE11でも使えます。なんか使えなさそうなイメージあるよね


2021/05/31 更に加筆。

Internet Explorer(IE)が来年6月16日にサポート終了、その日がくると起動せずとのことです。ついにMicrosoft直々に死刑宣告が出されましたね!

ちなみに、筆者は現在IEからは解放されたものの、Cordovaベースのスマホアプリ系の案件をやっていてAndroid Browser 4.4.Xをサポートしているというオチ。なんでだよ

CSSの非同期読み込み

CSSファイルを読み込む際、下記のような記述を書くことになるかと思います。

<link rel="stylesheet" href="style.css">

ウェブサイトを高速化する上で、上記のようなCSSファイルの読み込みはボトルネックとなることがあります。なぜかというと、上記の記述はパース(ファイル内記述の解釈)が終わるまで、レンダリングブロック(画面描画の停止)を引き起こすためです。

CSSファイルの同期中(読み込んでいる間)は画面描画が停止してしまうため、当然ページスピードには影響が出てしまいます。これを防ぐため、ページスピード改善ガチ勢の間では以下のようなテクニックが使われるようになりました。

  1. ファーストビュー(ページ初回読み込み時、スクロールせず見える範囲のコンテンツのこと)を表示するために必要最低限のスタイルをインライン化(head内にstyle要素を使って直書き)する※CSSファイルからファーストビューに関するスタイルだけを抽出するにはCritical Path CSS Generatorあたりを使うとよいでしょう

  2. ファーストビューに関連しないスタイルが書かれたCSSファイルを非同期読み込みする

「ファーストビューだけなるべく早く表示させるようにしてやれば、とりあえず白い画面で固まることは防げるよね?」という発想の手法でして、ページスピード測定によく使われるPageSpeed Insightsのドキュメントにもなんかそれっぽいことが書いてあります。

rel="preload"を使う

ファーストビューのスタイルをインライン化するのは従来通りなので置いといて。CSSの非同期読み込みを行う上で、ちょっと前まではハックめいた方法を使わなくてはならなかったのですが、最近は下記のような書き方で実現できるようになりました。

<link rel="preload" href="style.css" as="style">

必要最低限のスタイルだけstyle要素に書いて、残りは全部preloadとすれば簡潔ですね。

一応ごく一部のスマホブラウザで動きませんが、Firefoxでも使えるようになったので、国内向けサイトであればPoryfillも不要になったと言えるでしょう。

なお、JSの非同期読み込みについてはIEの時代からdefer属性,async属性が使えたので割愛します。画像の遅延読み込み(img loading)もSafariが対応してくれればラクになるんだがなあ。

扇形のグラデーション

そういえばconic-gradient()関数で扇形(円錐形)のグラデーションが描画できます。linear-gradient()のところでついでに書こうとして忘れたままだったやつ。

例1:普通な感じ

例2:円グラフっぽく

例3:グラデーションの中心位置をずらす(ついでになんかパズルのピースみたいな形にする)

<style>
.conic_gradient.conic_gradient1 {
background: conic-gradient(#F33, #3F3, #33F);
}
.conic_gradient.conic_gradient2 {
background: conic-gradient(#F33 90deg, #3F3 90deg 160deg, #33F 160deg 210deg, #999 210deg);
border-radius: 50%;
}
.conic_gradient.conic_gradient3 {
position: relative;
background: conic-gradient(from 90deg at 25% 50%, #F33, #3F3, #33F);
}
.conic_gradient_child {
position: absolute;
top: 30px;
left: 75px;
width: 40px;
height: 40px;
border-radius: 50%;
background: #FFF;
}
</style>
<p>例1:普通な感じ</p>
<div class="conic_gradient conic_gradient1"></div>
<p>例2:円グラフっぽく</p>
<div class="conic_gradient conic_gradient2"></div>
<p>例3:グラデーションの中心位置をずらす(ついでになんかパズルのピースみたいな形にする)</p>
<div class="conic_gradient conic_gradient3">
<div class="conic_gradient_child"></div>
</div>
view raw demo11_29.html hosted with ❤ by GitHub

他になんか思い出したら気まぐれで随時加筆します。