WordPressでCSSファイルをインライン化する最良の方法

ウェブサイトの表示速度の高速化の一環として、CSSファイルをlink要素で読み込むのではなく、CSSファイルの内容をstyle要素内に書き出すインライン化で、HTTPリクエストを減らす手法が有効な場合があります。

今回は、WordPressでCSSファイルをインライン化するための、最良だと思われる方法をご紹介します。

WordPressでインライン化したCSSコードを出力する方法

通常、WordPressでCSSファイルを読み込むには、functions.phpでwp_enqueue_scriptsアクションフックwp_enqueue_style関数を使用します。

add_action(
  'wp_enqueue_scripts',
  function() {
    $handle = 'my-style';
    $src = get_template_directory_uri(). '/style.css';
    $deps = array();
    wp_enqueue_style( $handle, $src, $deps );
  },
  10
);

インライン化したCSSコードを出力する場合は、wp_add_inline_style関数を使用するのが最良だと思われます。しかし、次のように記述しても、意図した通りにインライン化されたCSSコードは出力されません。

add_action(
  'wp_enqueue_scripts',
  function() {
    $handle = 'my-style';
    $data = '/* CSSコード */';
    wp_add_inline_style( $handle, $data );
  },
  10
);

というのも、wp_add_inline_style関数はwp_enqueue_style関数で読み込まれるCSSファイルを部分的に上書きするための関数だからです。

例えば、wp_enqueue_style関数で読み込まれるCSSファイル内に .important { color: red; } という部分があったとして、これを .important { color: yellow; } で上書きするといったケースでwp_add_inline_style関数を使用します。

そのため、wp_add_inline_style関数の「$handle」には、wp_enqueue_style関数で登録された、上書きしたいCSSファイルの「$handle」を指定するというのが正しい使い方になります。

add_action(
  'wp_enqueue_scripts',
  function() {
    $handle = 'my-style';
    $src = get_template_directory_uri(). '/style.css';
    $deps = array();
    $data = '/* CSSコード */';
    wp_enqueue_style( $handle, $src, $deps );
    wp_add_inline_style( $handle, $data );
  },
  10
);

wp_add_inline_style関数を使用しつつ、インライン化されたCSSコードのみを出力するには、wp_register_style関数を使用します。

add_action(
  'wp_enqueue_scripts',
  function() {
    $handle = 'my-style';
    $src = false;
    $deps = array();
    $data = '/* CSSコード */';
    wp_register_style( $handle, $src, $deps );
    wp_enqueue_style( $handle );
    wp_add_inline_style( $handle, $data );
  },
  10
);

wp_register_style関数はCSSファイルを登録するだけの関数です。「$src」に「false」を設定することで、「$handle」のみが登録され、wp_enqueue_style関数を使用してもlink要素は出力されません。

CSSコードは適用する順序によって、適用されるプロパティが変化する場合があるため、適用する順序は重要です。この適用する順序を設定できるのがwp_register_style関数の「$deps」です。

add_action(
  'wp_enqueue_scripts',
  function() {

    $handle = 'my-style-1';
    $src = false;
    $deps = array();
    $data = '/* CSSコード */';
    wp_register_style( $handle, $src, $deps );
    wp_enqueue_style( $handle );
    wp_add_inline_style( $handle, $data );

  },
  10
);

…

add_action(
  'wp_enqueue_scripts',
  function() {

    $handle = 'my-style-3';
    $src = false;
    $deps = array( 'my-style-1', 'my-style-2' );
    $data = '/* CSSコード */';
    wp_register_style( $handle, $src, $deps );
    wp_enqueue_style( $handle );
    wp_add_inline_style( $handle, $data );

    $handle = 'my-style-2';
    $src = false;
    $deps = array( 'my-style-1' );
    $data = '/* CSSコード */';
    wp_register_style( $handle, $src, $deps );
    wp_enqueue_style( $handle );
    wp_add_inline_style( $handle, $data );

  },
  9
);

上記のコードのように「$deps」を設定すると、アクションフックや関数の実行順序がバラバラであっても、「my-style-2」は「my-style-1」の後に、「my-style-3」は「my-style-1」と「my-style-2」の後に出力されます。

wp_add_inline_style関数を使用する方法は、回りくどい感じがしますが、「$deps」が設定できるというメリットがあるため、CSSコードをインラインで出力するための最良の方法だと思います。

CSSファイルからCSSコードを取得する方法

WordPressにはWP_Filesystemという、外部ファイルの内容を取得したり、外部ファイルに内容を保存できる仕組みが用意してあります。

load_template( ABSPATH. 'wp-admin/includes/file.php' );

…

add_action(
  'wp_enqueue_scripts',
  function() {
    global $wp_filesystem;
    if( WP_Filesystem() ) {
      $template_directory = get_template_directory();

      $data = $wp_filesystem->get_contents( "{$template_directory}/style.css" );
      if( !empty( $data ) ) {
        $handle = 'my-style';
        $src = false;
        $deps = array();
        wp_register_style( $handle, $src, $deps );
        wp_enqueue_style( $handle );
        wp_add_inline_style( $handle, $data );
      }#endif

    }#endif
  },
  10
);

WP_Filesystemを使用するには、WordPress本体にある「/wp-admin/includes/file.php」を読み込み、関数内でグローバル変数の「$wp_filesystem」を使えるようにし、WP_Filesystem関数で初期化するといった手順が必要です。

「$wp_filesystem->get_contents()」で内容を取得し、wp_add_inline_style関数の「$data」に設定すれば、インライン化されたCSSコードが出力されるようになります。

取得したCSSコードを出力する前の注意点

取得したCSSコードをそのまま出力すると、文法エラーになったり、不具合が発生する場合もあるため、次のような処理を行います。

  • @charsetがある場合、style要素内では無効のため取り除く
  • @importがある場合、読み込むCSSファイルの内容を取得して置き換える
  • url関数値に相対パスが使用されている場合、絶対パスに置き換える
  • HTMLファイルの肥大化を抑えるため、CSSコードを圧縮・軽量化する

これらの処理を一括して行うための次のような関数を用意しておくといいでしょう。

function my_inline_style( $style_data, $style_url ) {
  global $wp_filesystem;

  $root = parse_url( $style_url, PHP_URL_SCHEME ). '//'. parse_url( $style_url, PHP_URL_HOST );
  $dir = dirname( $style_url );

  // `@import …;` を読み込まれるCSSファイルの内容で置き換える
  if( preg_match_all( '/@import\s+(?:url\()?[\"\']?([^\"\')]+)[\"\']?(?:\))?;/', $style_data, $matches ) ) {
    $replaces = array();
    $site_url = site_url( '/' );
    foreach( $matches[ 1 ] as $import_key => $import_url ) {
      if( strpos( $import_url, $site_url ) !== 0 && ( strpos( $import_url, 'https:' ) === 0 || strpos( $import_url, 'http:' ) === 0 || strpos( $import_url, '//' ) === 0 ) ) {
        $import_data = wp_remote_retrieve_body( wp_remote_get( $import_url ) );
      } else {
        if( strpos( $import_url, '/' ) === 0 ) {
          $import_url = $root. $import_url;
        } else {
          if( strpos( $import_url, './' ) === 0 ) {
            $import_url = substr( $import_url, 2 );
          }
          $count = substr_count( $import_url, '../' );
          $import_url = ( $count > 0 ) ? str_replace( str_repeat( '../', $count ), dirname( $dir, $count ). '/', $import_url ) : $dir. '/'. $import_url;
        }
        $import_data = $wp_filesystem->get_contents( str_replace( $site_url, ABSPATH, $import_url ) );
      }
      $replaces[ $matches[ 0 ][ $import_key ] ] = (string) $import_data;
    }
    $style_data = str_replace( array_keys( $replaces ), array_values( $replaces ), $style_data );
  }

  $replaces = array(

    // `@charset …;` を取り除く
    '/@charset [^;]+;/' => '',

    // url関数値の `"` と `'` を取り除く
    '/([\s:]url\()[\"\']([^\"\']+)[\"\'](\)[\s;}])/' => '${1}${2}${3}',

    // url関数値の相対パスを絶対パスに置き換える
    '/([\s:]url\((?!\/\/|data:|http:|https:))\/([^)]+\)[\s;}])/' => '${1}'. $root. '/${2}',
    '/([\s:]url\((?!\/\/|data:|http:|https:))(?:\.\/)?([^)]+\)[\s;}])/' => '${1}'. $dir. '/${2}',
    '/(?:\/[^.\/]+){4}\/(?:\.\.\/){4}|(?:\/[^.\/]+){3}\/(?:\.\.\/){3}|(?:\/[^.\/]+){2}\/(?:\.\.\/){2}|(?:\/[^.\/]+){1}\/(?:\.\.\/){1}/' => '/',

    // CSSコード全体の圧縮・軽量化
    '/(\/\*(?=[!]).*?\*\/|\"(?:(?!(?<!\\\)\").)*\"|\'(?:(?!(?<!\\\)\').)*\')|\s+/' => '${1} ',
    '/(\/\*(?=[!]).*?\*\/|\"(?:(?!(?<!\\\)\").)*\"|\'(?:(?!(?<!\\\)\').)*\')|\/\*.*?\*\/|\s+([:])\s+|\s+([)])|([(:])\s+/' => '${1}${2}${3}${4}',
    '/\s*(\/\*(?=[!]).*?\*\/|\"(?:(?!(?<!\\\)\").)*\"|\'(?:(?!(?<!\\\)\').)*\'|[ :]calc\([^;}]+\)[ ;}]|[$&+,\/;<=>?@^_{|}~]|\A|\z)\s*/' => '${1}',

  );
  $style_data = preg_replace( array_keys( $replaces ), array_values( $replaces ), $style_data );

  return $style_data;
}

後は、先程のPHPコードに以下のように追加するだけです。

load_template( ABSPATH. 'wp-admin/includes/file.php' );

…

add_action(
  'wp_enqueue_scripts',
  function() {
    global $wp_filesystem;
    if( WP_Filesystem() ) {
      $template_directory = get_template_directory();
      $template_directory_uri = get_template_directory_uri();

      $data = $wp_filesystem->get_contents( "{$template_directory}/style.css" );
      if( !empty( $data ) ) {
        $handle = 'my-style';
        $src = false;
        $deps = array();
        $data = my_inline_style( $data, "{$template_directory_uri}/style.css" );
        wp_register_style( $handle, $src, $deps );
        wp_enqueue_style( $handle );
        wp_add_inline_style( $handle, $data );
      }#endif

    }#endif
  },
  10
);

ウェブサイトの表示速度を改善したい場合に試してみてください。

関連記事