はじめに: なぜCSSで計算するのか

Web開発をしていると、割引率元値から割引価格を計算し、画面に表示する場面がよくあります。通常はJavaScriptで計算してDOMに反映するのが一般的です。しかし、計算結果を表示するだけなら、CSSだけでも十分に解決できます。

この記事で紹介する手法は、まだすべてのブラウザでサポートされているわけではありませんが、attr()関数の進化とCSS数学関数(mod()round())の登場によって可能になった未来志向のテクニックです。このコードを通じて、CSSが単なるスタイリングを超えて「計算」までできる可能性を実感していただければ幸いです。

核となるアイデア: data-*属性とCSS変数の組み合わせ

このテクニックの核となるのは、以下の通りです。

  1. HTML要素にdata-pricedata-discount属性でデータを保存します。
  2. CSSのattr()関数を使って、この属性値をnumber型として読み取ります。
  3. CSS数学関数(calc()mod()round())で割引価格を計算します。
  4. 計算された値をcounter()contentプロパティで画面に表示します。

基本的なマークアップ構造

<div class="ott">
  <input type="checkbox" class="is-ott-discounted" id="discount-toggle">
  <label for="discount-toggle">学生割引を適用</label>

  <div class="ott-price"
       data-price="7.99"
       data-discount="20">
    $7.99
  </div>
</div>
  • data-price: 元の価格
  • data-discount: 割引率(%)

本格的なCSS計算ロジック

それでは、実際のCSSコードを見ていきましょう。まず、割引トグルがチェックされたときに元の価格に取り消し線を引き、新しい価格を計算して表示します。

/* 割引トグルがチェックされたとき */
.ott:has(.is-ott-discounted:checked) {
  .ott-price {
    /* 元の価格に取り消し線 */
    text-decoration: line-through;

    /*
      割引価格の計算:
      元の価格 * (1 - 割引率/100)
    */
    --n: calc(attr(data-price number) * (1 - attr(data-discount number) / 100));

    /* 計算された価格を表示 */
    &::after {
      display: inline-block;
      /*
        --n変数を2つのカウンターに分離:
        'a'はドル(整数部分)、'b'はセント(小数部分)
      */
      counter-set:
        a calc(round(down, var(--n)))
        b calc((mod(var(--n), 1)) * 100);

      /* 出力: スペース、$、整数部、ドット、小数部 */
      content: "\2000\2000
quot; counter(a) "." counter(b, decimal-leading-zero); } } }

コードの詳細解説

  • attr(data-price number): data-price属性の値を数値(number)型として読み取ります。従来のattr()関数はcontentプロパティでのみ限定的に使用されていましたが、現在はすべてのCSSプロパティで使用可能であり、型指定も可能になりました。
  • calc(): 読み取った値で四則演算を実行します。7.99 * (1 - 20/100) = 6.392となります。
  • round(down, var(--n)): --nの値を切り捨てて整数部分(ドル)のみを抽出します。つまり、6を取得します。
  • mod(var(--n), 1): --nの値を1で割った余り、つまり小数部分(0.392)を取得します。これに100を掛けてセント(39.2)に変換します。
  • counter-setおよびcounter(): 計算された数値をCSSカウンターに設定し、contentプロパティで文字列を組み合わせて画面に出力します。

実務適用時の注意点と制限

このテクニックは非常に興味深いですが、いくつか注意点があります。

  1. ブラウザサポート: この記事で使用したattr()関数の型指定構文は、まだ標準(Baseline)として採用されていません。最新のChromeやEdgeでは動作する可能性がありますが、FirefoxやSafariではサポートされていない可能性が高いです。実際のプロダクション環境に適用する前に、必ずターゲットブラウザのサポート状況を確認してください。
  2. 複雑なロジックは避ける: CSSは元々レイアウトとスタイリングのための言語です。非常に複雑な計算や条件分岐が必要な場合は、JavaScriptを使用する方がメンテナンスしやすいかもしれません。このテクニックは、簡単な計算をスクリプトなしで処理したい場合に力を発揮します。
  3. アクセシビリティ: contentプロパティで生成されたテキストは、スクリーンリーダーなどの支援技術が読み取れない可能性があります。重要な価格情報は、HTMLに実際のテキストとして含めることをお勧めします。

まとめ: CSSの未来を垣間見る

今回の例は、attr()関数の進化とCSS数学関数の可能性を示す良い事例です。まだ実務ですぐに使用するには時期尚早ですが、ブラウザサポートが拡大されれば、簡単な計算ロジックをJavaScriptなしで処理するために有用になるでしょう。

  • 次のステップとしての学習方向: @propertyを使用したカスタムプロパティ登録、container queryとの組み合わせ、そしてCSS Houdini APIについて学習してみてください。CSSが単なるスタイリングを超えて、より多くの役割を果たせることを実感できるはずです。

合わせて読みたい記事:

Developer inspecting CSS code for dynamic price calculation on a web page Coding Session Visual

核心となるコード例

上記で説明した全コードを1か所にまとめました。以下のコードをコピーして、すぐに試すことができます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSS割引価格計算例</title>
  <style>
    .ott {
      font-family: sans-serif;
    }

    .ott-price {
      font-size: 2rem;
      font-weight: bold;
    }

    /* 割引トグルがチェックされたとき */
    .ott:has(.is-ott-discounted:checked) {
      .ott-price {
        /* 元の価格に取り消し線 */
        text-decoration: line-through;

        /*
          割引価格の計算:
          元の価格 * (1 - 割引率/100)
        */
        --n: calc(attr(data-price number) * (1 - attr(data-discount number) / 100));

        /* 計算された価格を表示 */
        &::after {
          display: inline-block;
          /*
            --n変数を2つのカウンターに分離:
            'a'はドル(整数部分)、'b'はセント(小数部分)
          */
          counter-set:
            a calc(round(down, var(--n)))
            b calc((mod(var(--n), 1)) * 100);

          /* 出力: スペース、$、整数部、ドット、小数部 */
          content: "\2000\2000
quot; counter(a) "." counter(b, decimal-leading-zero); } } } </style> </head> <body> <div class="ott"> <input type="checkbox" class="is-ott-discounted" id="discount-toggle"> <label for="discount-toggle">学生割引を適用</label> <div class="ott-price" data-price="7.99" data-discount="20"> $7.99 </div> </div> </body> </html>

このコードは、割引トグルをチェックすると$7.99に取り消し線が表示され、後ろに割引された価格$6.39が自動的に表示されることを確認できます。

E-commerce product card with CSS-only dynamic discount price display Dev Environment Setup

応用テクニック: 価格の丸め処理

もし割引価格が小数第3位まで出る場合、価格を四捨五入して表示したいことがあります。この場合は、round()関数を活用できます。

/* 価格を最も近い整数に四捨五入 */
.ott-price::after {
  counter-set: price calc(round(nearest, var(--n)));
  content: "
quot; counter(price); }

round(nearest, var(--n))は、--nの値を最も近い整数に四捨五入します。小数部分を切り捨てたい場合はround(down, ...)を、切り上げたい場合はround(up, ...)を使用します。

ブラウザサポート状況とフォールバック戦略

現在(2024年時点)、この記事で使用したattr()関数の型指定構文は、主要ブラウザで完全にサポートされていません。このため、実務ですぐに使用することは難しいです。ただし、以下のような戦略を検討できます。

  • プログレッシブエンハンスメント: 基本価格はHTMLにそのまま置き、CSS計算がサポートされているブラウザでのみ割引価格が表示されるように、@supportsルールを使用できます。
  • JavaScriptフォールバック: まだ標準ではないため、JavaScriptで同様の機能を実装しておくのも一つの方法です。ただし、この記事の目的はCSSの可能性を探求することなので、ブラウザサポートが拡大されるまで待つのも良い選択です。

Laptop showing a streaming service subscription page with CSS pricing logic Programming Illustration

まとめ: 実務適用のためのアドバイス

今日見てきたCSS計算テクニックはまだ実験的ですが、CSSが単なるスタイリングを超えてロジックを処理できる方向性を示しています。

実務ですぐに導入するというよりは、以下のような観点でアプローチされることをお勧めします。

  • 技術トレンドの把握: 最新のCSS機能を学習し、ブラウザサポート状況を定期的に確認してください。
  • 簡単なプロジェクトに適用: 個人プロジェクトやトイプロジェクトでこのテクニックを使用してみて、長所と短所を体感してみてください。
  • 可読性とメンテナンス性の考慮: CSSで複雑な計算を実装すると、コードが難解になる可能性があります。チームプロジェクトであれば、チームメンバーと十分な議論が必要です。

この記事がCSSの新しい可能性を発見する一助となれば幸いです。ご質問や他の良いアイデアがあれば、コメントで共有してください!

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。