お問い合わせフォームの <textarea> の右下に、斜めの線が入った小さなつまみが表示されているのを見たことがあると思います。そこをドラッグすると入力欄が大きくなる、あのつまみです。これは textarea 特有の機能ではなく、CSS の resize プロパティがブラウザーの既定のスタイルで有効になっているために現れています。
つまり resize を自分で指定すれば、div のような普通の要素にも同じつまみを付けて、訪問者がドラッグでサイズを変えられるようにできます。この記事では、指定できる値の一覧から、div に使うときに必須となる overflow の条件、min-height や max-height でリサイズできる範囲を決める方法、そして「指定したのにつまみが出ない」ときの原因までを解説します。
目次
resize に指定できる値
resize は、その要素をユーザーがドラッグでリサイズできるかどうか、できるならどの方向に動かせるかを決めるプロパティです。指定できる値は次の6つです。
| 値 | 効果 |
|---|---|
none | リサイズできない。ほとんどの要素の既定値 |
both | 幅と高さの両方を変えられる |
horizontal | 幅だけを変えられる。高さは固定 |
vertical | 高さだけを変えられる。幅は固定 |
block | 書字方向のブロック方向だけ変えられる。横書きなら vertical と同じ |
inline | 書字方向のインライン方向だけ変えられる。横書きなら horizontal と同じ |
書き方はいたってシンプルで、リサイズさせたい要素に値をひとつ指定するだけです。
.box {
resize: both; /* 縦横どちらにも伸縮できる */
overflow: auto; /* div に使うときはこれが必須(後述) */
width: 200px;
height: 120px;
}
指定した width と height は「リサイズを始める前のサイズ」になります。下のデモで、4つの値の違いを実際に動かして確かめてみてください。右下のつまみをドラッグすると、値ごとに動く方向が変わります。
<div class="demo">
<div class="box box--both">
<strong>both</strong>
<span>縦・横どちらも</span>
</div>
<div class="box box--horizontal">
<strong>horizontal</strong>
<span>横だけ</span>
</div>
<div class="box box--vertical">
<strong>vertical</strong>
<span>縦だけ</span>
</div>
<div class="box box--none">
<strong>none</strong>
<span>掴めない(既定値)</span>
</div>
</div>
.box {
/* resize を効かせるには overflow が visible 以外である必要がある */
overflow: auto;
width: 150px;
height: 90px;
}
.box--both {
resize: both;
}
.box--horizontal {
resize: horizontal;
}
.box--vertical {
resize: vertical;
}
.box--none {
resize: none;
}
/* ここから下は見た目を整えるだけの指定 */
.demo {
display: flex;
flex-wrap: wrap;
gap: 16px;
padding: 20px;
font-family: system-ui, sans-serif;
}
.box {
display: grid;
align-content: center;
justify-items: center;
gap: 4px;
box-sizing: border-box;
padding: 10px;
border: 2px solid #1a73e8;
border-radius: 6px;
background: #eef4fe;
color: #1a3d6d;
font-size: 13px;
text-align: center;
}
.box strong {
font-size: 15px;
}
horizontal を指定したボックスは横にしか動かず、vertical のボックスは縦にしか動きません。none のボックスにはつまみ自体が表示されず、ドラッグしても何も起きません。
block と inline は書字方向に合わせて向きが決まる
block と inline は、vertical / horizontal の論理版にあたる値です。日本語や英語のような横書き(writing-mode: horizontal-tb)では、文字が流れていく方向(インライン方向)が横、行が積み重なる方向(ブロック方向)が縦なので、inline は horizontal と、block は vertical と同じ結果になります。
違いが出るのは縦書き(writing-mode: vertical-rl)にしたときで、このときは inline が縦、block が横のリサイズになります。縦書きと横書きを切り替えるサイトのように、書字方向が変わっても「行が伸びる方向だけリサイズさせたい」という場面で役に立ちます。ただしこの2つは both などに比べて新しい値なので、使う前にブラウザーの対応状況を確認しておくと安心です。
div をリサイズできるようにするには overflow が要る
resize でいちばん多いつまずきがここです。仕様上、resize が効くのは overflow の計算値が visible 以外の要素に限られます。overflow の既定値は visible なので、div に resize: both と書いただけではつまみは出ません。
下のデモは、同じ resize: both を指定した2つのボックスです。左は overflow: visible のまま、右は overflow: auto を添えてあります。右下をドラッグして違いを確かめてみてください。
<div class="demo">
<div class="box box--ng">
<strong>overflow: visible</strong>
<span>resize: both を指定しても掴めない</span>
</div>
<div class="box box--ok">
<strong>overflow: auto</strong>
<span>右下を掴んで動かせる</span>
</div>
</div>
.box {
resize: both;
width: 200px;
height: 100px;
}
/* 既定値のまま。resize を書いても効かない */
.box--ng {
overflow: visible;
}
/* visible 以外にすると resize が効くようになる */
.box--ok {
overflow: auto;
}
/* ここから下は見た目を整えるだけの指定 */
.demo {
display: flex;
flex-wrap: wrap;
gap: 16px;
padding: 20px;
font-family: system-ui, sans-serif;
}
.box {
display: grid;
align-content: center;
justify-items: center;
gap: 6px;
box-sizing: border-box;
padding: 10px;
border: 2px solid #999;
border-radius: 6px;
background: #f5f5f5;
font-size: 13px;
text-align: center;
}
.box--ok {
border-color: #1a73e8;
background: #eef4fe;
color: #1a3d6d;
}
左のボックスは、見た目は同じでもつまみが表示されず、まったく動かせません。overflow に指定する値は auto / scroll / hidden のどれでも条件を満たしますが、中身がはみ出したときにスクロールで読めるようにしたいなら auto、はみ出した部分を隠してよいなら hidden、と使い分けます。小さくリサイズされたときに中身が切れて読めなくなるのを避けたいので、迷ったら auto にしておくのが無難です。
ちなみに textarea で最初からつまみが出ているのは、ブラウザーの既定のスタイルシートでリサイズが有効になっているうえ、textarea 自身がもともとスクロールできる要素として扱われ、この overflow の条件を満たしているからです。
リサイズできる範囲を min / max で制限する
resize をそのまま有効にすると、ユーザーは要素を極端に小さくすることも、親要素からはみ出すほど大きくすることもできてしまいます。そこで使うのが min-width / max-width / min-height / max-height です。リサイズはこれらの制限を超えられないので、「この範囲でなら自由に変えてよい」という枠を決められます。
.box {
resize: both;
overflow: auto;
/* 開始サイズ */
width: 240px;
height: 140px;
/* ここから外には動かせなくなる */
min-width: 160px;
max-width: 100%;
min-height: 80px;
max-height: 400px;
}
max-width: 100% のように親要素を基準にした値を入れておくと、リサイズでレイアウトが横に突き抜けるのを防げます。レスポンシブなページで resize を使うときは、ほぼ必須の指定だと考えてよいでしょう。
textarea のリサイズを縦だけに限定する
フォームでよく使うのが resize: vertical です。textarea を既定の両方向のままにしておくと、横に広げられてフォームのレイアウトが崩れてしまいます。かといってリサイズを全部消すと、長文を書く人にとっては不便です。そこで「幅はレイアウトに合わせて固定し、高さだけユーザーに任せる」という形にします。
下のデモでは、それに加えて min-height と max-height で高さの範囲も決めてあります。つまみを上下に動かすと、決めた範囲の外には動かせないことが分かります。
<form class="demo">
<label class="field">
<span class="field__label">お問い合わせ内容(縦だけリサイズできます)</span>
<textarea class="comment" rows="3" placeholder="右下のつまみを上下に動かしてみてください"></textarea>
</label>
<p class="note">高さは 3em 〜 10em の範囲でしか変わりません。</p>
</form>
.comment {
/* 縦方向だけリサイズを許可する */
resize: vertical;
/* 幅はレイアウトに合わせて固定 */
width: 100%;
/* 動かせる範囲を決めておく */
min-height: 3em;
max-height: 10em;
}
/* ここから下は見た目を整えるだけの指定 */
.demo {
display: grid;
gap: 8px;
max-width: 420px;
padding: 20px;
font-family: system-ui, sans-serif;
}
.field {
display: grid;
gap: 6px;
}
.field__label {
font-size: 13px;
color: #555;
}
.comment {
box-sizing: border-box;
padding: 10px 12px;
border: 1px solid #bbb;
border-radius: 6px;
font: inherit;
line-height: 1.6;
}
.note {
margin: 0;
font-size: 12px;
color: #777;
}
幅は width: 100% で親要素いっぱいに固定してあるので、横方向のドラッグは起きません。高さも 3em から 10em の間でしか変わらないため、広げすぎて送信ボタンが画面外に押し出される、といったことも防げます。
resize: none で無効にするときに考えておきたいこと
デザインを崩したくないという理由で textarea { resize: none; } と書いてしまうことがありますが、これは少し慎重に判断したいところです。入力欄を広げられると、長文を書くときに全体を見渡しながら確認できます。文章の推敲が必要な人や、拡大表示を使っている人にとっては、この操作が使えるかどうかで入力のしやすさがかなり変わります。
レイアウトが崩れるのが心配なだけであれば、前述のように resize: vertical と max-height の組み合わせでほとんど解決します。完全に無効にするのは、検索ボックスのように1行しか入力しない欄や、高さが自動で変わる仕組み(field-sizing: content など)をすでに入れていてつまみが意味を持たない場合に限る、と考えておくとよいでしょう。
resize を指定しても掴めないとき
「CSS は書いたのにつまみが出ない」「動かしたのに元に戻ってしまう」というときは、だいたい次のどれかが原因です。
overflow が visible のままになっている
もっとも多い原因です。前の章で説明したとおり、overflow が visible(既定値)の要素には resize が適用されません。div や section に指定するときは、必ず overflow: auto などをセットで書いてください。
注意したいのは、overflow-x と overflow-y を片方だけ指定しているケースです。どちらか一方に visible 以外を指定すると、もう片方の visible は auto として扱われるため結果的に条件は満たしますが、意図しないスクロールバーが出ることがあります。まとめて overflow で指定したほうが分かりやすいです。
display が inline の要素に指定している
resize はインライン要素には効きません。<span> や <a> のように既定が display: inline の要素をリサイズさせたい場合は、display: inline-block や display: block に変えたうえで overflow を指定します。
.tag {
display: inline-block; /* inline のままでは resize が効かない */
overflow: auto;
resize: horizontal;
min-width: 80px;
}
img などの置換要素に直接指定している
<img> や <video>、<iframe> のような置換要素(中身をブラウザーが外部のリソースで置き換える要素)は、仕様上リサイズを許してもよいことになっているものの、実際にどこまで動くかはブラウザーによって差があります。画像に resize: both と書いてもつまみが出ない、ということは珍しくありません。
確実に動かしたいときは、画像を div で包んで包んだ側をリサイズさせるのが定番の回避策です。画像は幅いっぱいに広げておけば、枠の大きさに追従します。
/* 画像そのものではなく、包んでいる div を掴ませる */
.image-frame {
resize: horizontal;
overflow: hidden;
width: 320px;
min-width: 120px;
max-width: 100%;
}
.image-frame img {
display: block;
width: 100%;
height: auto;
}
リサイズした大きさはページを離れると元に戻る
resize でユーザーが変えたサイズは、そのページを開いている間だけブラウザーが保持しているものです。CSS や HTML に書き戻されるわけではないので、ページを再読み込みしたり別のページへ移動して戻ってきたりすると、スタイルシートで指定したサイズに戻ります。
サイズを覚えさせたい場合は、JavaScript で自前の保存が必要です。ResizeObserver で要素のサイズ変化を検知し、そのときの幅と高さを localStorage に保存しておいて、次回の読み込み時に復元する、という流れになります。逆にいえば、そこまでの実装をしないのであれば「一時的に広げられる」機能だと割り切って使うのが正しい理解です。
つまみの見た目を変えたい
右下に表示されるつまみのデザインは、標準の CSS では変更できません。Chrome や Safari などの WebKit / Blink 系ブラウザーには ::-webkit-resizer という独自の擬似要素があり、背景色などを指定できます。
/* WebKit / Blink 系だけで効く独自の擬似要素 */
.box::-webkit-resizer {
background: #1a73e8;
}
ただしこれは標準化されていない機能で、Firefox には効きません。対応していないブラウザーでは既定のつまみがそのまま表示されるため、「あくまで対応環境での飾り」と考えて、見た目に依存した設計は避けてください。つまみが目立たなくて気づかれないのが問題なのであれば、要素の角に模様を描く、説明文を添えるといった、ブラウザーに依存しない方法のほうが確実です。
まとめ
resize は、要素をユーザーがドラッグでリサイズできるようにする CSS プロパティです。値は none / both / horizontal / vertical と、書字方向に連動する block / inline の6つ。textarea で最初からつまみが出ているのは、ブラウザーの既定のスタイルでこれが有効になっているからでした。
使ううえでの要点は2つです。ひとつは、div などに指定するときは overflow を visible 以外にしないと効かないこと。もうひとつは、min-width / max-width / min-height / max-height で動かせる範囲を決めておくことです。フォームでは resize: vertical と max-height の組み合わせが扱いやすく、resize: none でまるごと消すのは入力のしやすさを損なうことがあるので、必要な場面かどうかを確かめてから使ってください。