カードの端からはみ出す画像の周囲に段落テキストを回り込ませるには、画像をフロートさせ、負の外側マージンを使います。曲線の輪郭には shape-outside を追加します。このチュートリアルでは、Mac の Safari で開ける完全なローカルサンプルを作成します。

はみ出した画像の周囲にテキストが回り込む仕組み

画像は段落のフロートレイアウトに参加する必要があります。CSS の float は要素をコンテナの片側に配置し、隣接するテキストをその横に回り込ませます。この例では右にフロートさせるため、テキストは左側を流れ、その後、下へ続きます。MDN: float。

結果を左右する要素は3つあります。フロートが回り込みの動作を作り、負の右マージンによって画像がコンテナからはみ出し、shape-outside が周囲のテキストに沿わせる輪郭を定義します。通常のフロートでは矩形状に回り込みますが、シェイプを使うと各行を円に沿わせられます。MDN: shape-outside。

画像を見た目の上で動かすだけでは不十分です。絶対位置指定された画像は通常フロー内でスペースを占めないため、周囲の要素は画像がないかのように配置されます。移動変換は通常のドキュメントフローを組み替えずに、描画位置を変えます。こうした動作があるため、画像を position: absolute または transform: translateX(...) で配置すると、重なった画像がテキストを覆うことがあります。MDN: position、MDN: CSS transforms の使用。

ここで扱う課題は明確です。円形の画像1枚が1つのテキストカードの右端を越え、カード内の段落が円の左側の輪郭に沿って流れます。画像と段落は、同じ回り込み領域に属します。

Mac で完全なサンプルを作成する

プレーンテキストのコードエディタで、overlap-wrap.html という名前のファイルを作成します。下のドキュメント全体を貼り付けて保存し、Safari で開きます。この例にはデータ URL として抽象的な SVG 画像が含まれているため、画像ファイル、パッケージ、ビルドツールをインストールする必要はありません。

フロートさせた画像は、その回り込み対象となる段落より前に置きます。この配置が重要なのは、フロートの位置がドキュメント内の先行コンテンツによって制約されるためです。見出しをフロートより前に置くと、見出しはカード内でテキスト幅全体を使えます。W3C: 視覚フォーマットモデル。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Overlapping image and paragraph wrapping</title>
  <style>
    * { box-sizing: border-box; }
    body {
      margin: 0;
      padding: 32px 96px 32px 32px;
      background: #eef1f5;
      color: #243247;
      font: 18px/1.65 system-ui, sans-serif;
    }
    .card {
      display: flow-root;
      width: 100%;
      max-width: 640px;
      margin: 0 auto;
      padding: 24px;
      background: white;
      box-shadow: 0 0 0 1px #cbd3df;
      overflow: visible;
    }
    .card h2 {
      margin: 0 0 16px;
      font-size: 26px;
      line-height: 1.25;
    }
    .card p { margin: 0 0 16px; }
    .card p:last-child { margin-bottom: 0; }
    .art {
      display: block;
      float: right;
      width: 240px;
      height: 240px;
      margin: 0 -88px 16px 16px;
      border-radius: 50%;
      shape-outside: circle(50%) border-box;
      shape-margin: 16px;
    }
    @media (max-width: 760px) {
      body { padding: 16px; }
      .art {
        float: none;
        width: min(240px, 100%);
        height: auto;
        margin: 0 auto 24px;
        shape-outside: none;
      }
    }
  </style>
</head>
<body>
  <article class="card">
    <h2>Study A: circles and spacing</h2>
    <img class="art" width="240" height="240" alt=""
      src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240' viewBox='0 0 240 240'%3E%3Crect width='240' height='240' fill='%233a628b'/%3E%3Ccircle cx='90' cy='85' r='60' fill='%23b9d8eb'/%3E%3Ccircle cx='165' cy='170' r='45' fill='%238db5cc'/%3E%3C/svg%3E">
    <p>Panel A pairs a circular graphic with a continuous passage of
      sample text. Follow the right edge of this paragraph as each
      line approaches the curved outline. Near the widest part of
      the circle, the available space becomes narrower.</p>
    <p>Panel B continues the same passage with a second paragraph.
      The graphic extends into the outer gutter while the words
      remain inside the white card. As the contour curves inward,
      later lines can use more of the available width.</p>
    <p>Panel C provides enough additional text to show the transition
      below the graphic. Once the passage continues beyond the
      wrapping region, its lines return to the full content width.
      Resize the window to compare the desktop arrangement with
      the stacked layout used in a narrower window.</p>
  </article>
</body>
</html>

ビューポート幅が 760px を超える場合、意図した表示は、右側の余白にはみ出す円形グラフィックと、その左側に沿って流れる段落行です。幅が 760px 以下の場合、画像は段落の上に中央揃えのブロックとして表示されます。このブレークポイントは例示用の設計上の選択です。@media はクエリに一致したとき、囲まれた宣言を適用します。MDN: @media。

サンプルのグラフィックは装飾用なので、画像要素には alt="" を指定しています。情報を伝えるグラフィックに置き換える場合は、適切な代替テキストを用意してください。WHATWG は装飾画像と、情報に相当するテキストが必要な画像を区別しています。WHATWG: 画像の代替となるテキストを提供するための要件。

はみ出し量とテキストとの間隔を決めるには

まず画像の位置を決め、その後で輪郭を調整します。右フロートはマージンボックスを基準に配置されます。この例では負の右マージンにより、画像の右端がカードのコンテンツ端から 88px はみ出します。カードには右パディングが 24px あるため、白い背景からのはみ出し量は 64px です。これらはこの例の派生寸法で、CSS ピクセル単位です。W3C: フロートの配置。

項目 サンプル値 計算または目的
画像の幅 240px 表示される円の直径
コンテンツ端から外側への移動量 88px 負の右マージンの絶対値
カードのパディング 24px コンテンツ端と背景端の間隔
背景から見えるはみ出し量 64px 88 − 24
コンテンツ領域内に残る画像幅 152px 240 − 88
矩形の代替レイアウトで確保される幅 168px 左マージンを含めて 240 − 88 + 16

独自のレイアウトでは、負のマージンの絶対値を「必要なはみ出し量 + コンテナの右パディング」として計算します。たとえば、パディングが 24px で、はみ出し量を 40px にする場合は、右マージンを -64px にします。画像を大きくしたりコンテナ幅を狭めたりするたびに、残るテキスト幅を確認してください。

円形のバージョンでは、形状を正方形の画像ボックスに合わせるため shape-outside: circle(50%) border-box を使います。ここでは非対称なマージンによってマージンボックスが変わるため、border-box を明示的に選ぶと便利です。基準ボックスを明示しない場合、基本シェイプの既定値は margin-box です。MDN: shape-outside での基本シェイプ。

shape-margin: 16px は回り込みの輪郭の周囲に余白を加えます。テキスト側にできるこの間隔を確保するため、正の左マージンを指定しています。シェイプとそのシェイプマージンはフロートのマージンボックスでクリップされるため、shape-margin だけを大きくしても回り込み領域を際限なく広げることはできません。負の右マージンによって外側もクリップされますが、この例ではその側に段落テキストはありません。W3C: CSS Shapes Module Level 1。

矩形の画像にするには、border-radius を 0 に、shape-outside を none に、shape-margin を 0 に変更します。フロートとマージンはそのままにします。これにより、同じ位置ではみ出しながら、直線的な辺に沿ってテキストが回り込みます。シェイプだけを取り除いて円形の切り抜きを残すと、画像は円形のまま、テキストのための余白は矩形になります。

回り込みに影響するコンテナ境界

カードでは display: flow-root を使ってブロック整形コンテキストを作り、フロートを垂直方向に内包します。これにより、テキストが短くてもカードの高さに画像が反映されます。また、この回り込み領域を外部のフロートから切り離します。MDN: ブロック整形コンテキスト。

この領域内では、段落を通常のブロックフローに保ちます。画像が直接の flex または grid アイテムであれば、そのフロートでは段落を回り込ませる配置になりません。同様に、別の段落ラッパーに独自のブロック整形コンテキストを与えると、外側のフロートはそのラッパー内のコンテンツに影響しなくなります。外側のグリッドでカード全体を配置していても、カードの内側にはフローレイアウトを使えます。MDN: ブロック整形コンテキスト。

クリッピングの問題は個別に確認してください。はみ出しを描画できるよう、この例では overflow を visible にしています。フロートを内包するために overflow を使うと、クリッピングやスクロールバーが発生することがあります。flow-root なら、その選択を必要とせずにフロートを内包できます。画像が祖先要素の境界でぴったり消える場合は、周囲のコンポーネントも調べてください。MDN: 内部フロートの包含。

CSS Shapes には方向による制約もあります。右フロートでは左側に、左フロートでは右側にテキストを回り込ませられます。1つのフロートでは、中央の画像の両側にテキストの通り道を作れません。この課題では、端に寄せたフロートを使います。W3C: CSS Shapes とフロートの動作。

Tailwind CSS でもこの方法は使える?

はい。既存の Tailwind プロジェクトでは、float-right が float: right を設定し、float-none がフロートを解除します。これらのユーティリティを提供するのは Tailwind ですが、テキストのレイアウトを行うのはブラウザです。Tailwind CSS: float。

輪郭の宣言には、Tailwind の任意プロパティ構文を使って [shape-outside:circle(50%)_border-box] と [shape-margin:16px] を記述できます。アンダースコアは border-box の前のスペースを表します。これらは設定済みの Tailwind プロジェクトで使うクラス名の断片であり、上記の完全なドキュメントの代わりにはなりません。これに置き換える場合も、同じ寸法、マージン、コンテナレイアウト、狭いウィンドウでのリセットを維持してください。Tailwind CSS: カスタムスタイルの追加。

回り込みが意図どおりに見えないときの調べ方

まずローカルサンプルを基準にし、既存のスタイルを一度に1つずつ加えます。次の手順で、レイアウトを決める条件を確認します。

  1. まず矩形状の回り込みを確認します。 一時的に shape-outside: none を設定します。それでもテキストが画像の下まで続く場合は、画像がフロートしているか、絶対位置指定されているか、flex/grid の直接の子要素になっているかを調べます。
  2. ドキュメント内の順序を確認します。 回り込ませたい段落より前にフロートを配置します。
  3. 回り込み領域を確認します。 フロートと段落のコンテンツを同じブロック整形コンテキスト内に保ち、段落ラッパーに独立したコンテキストが不要なら取り除きます。
  4. 輪郭を確認します。 円と明示的な border-box の基準を戻します。円形に切り抜く前に、表示画像が正方形であることを確認します。
  5. 余白を確認します。 ビューポート幅を広げ、カードまたはその祖先要素でクリッピングが起きていないか調べます。ページがはみ出し分の外側スペースを十分に確保しているか確認します。
  6. 狭いレイアウトを確認します。 サンプルのブレークポイントをまたいでサイズを変え、フロートと負のマージンの両方がリセットされることを確認します。

これらの確認は、フロートの配置、整形コンテキスト、シェイプの基準ボックスに関する文書化されたルールに沿っています。個々の行を直すために手動で改行を加えるのは避けてください。そうすると、特定のウィンドウ幅にしか合わなくなります。

MDN によると、shape-outside は広く利用可能ですが、構文の一部についてはサポートに差がある場合があります。この例では、文書化された circle() の形式を使っています。対応対象とする Mac ブラウザでページを比較し、行末が完全に一致することを求めるのではなく、余白と読みやすさを確認してください。シェイプの宣言が利用できない場合、フロートは矩形状の回り込みを維持します。MDN: shape-outside。

完成したレイアウトでは、画像サイズとはみ出し量を合わせて決め、文章が流れるための十分な幅を確保し、狭い画面用に縦積みの代替レイアウトを残します。これらの寸法が決まったら、輪郭を使って画像周辺の間隔を整えます。これにより、テキストや利用可能な幅が変わっても段落が再配置されます。

参照元