実践 Web Stories（旧 AMP Stories）

## はじめに
先日業務でWeb Stories（旧 AMP Stories）を用いたページを実装しました。
AMPが提供するコンポーネントを用いて、アニメーションやビジュアルに関するattributeを指定していくだけで、JavaScriptを一切書かずしてInstagramのストーリーに似たリッチなwebページを簡単に作ることができます。
![スクリーンショット 2020-08-17 19.46.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692844/269ddc3c-5f23-fed5-9552-5ef873e95979.png)
出典: https://amp.dev/about/stories/


本記事では、Web Storiesと合わせて使用した技術についての紹介のほか、SERP[^1]表出を目指してGoogleのガイドラインとじっくり向き合ったお話などについても書いていこうと思います。

[^1]: 検索エンジンの検索結果を表示するページのこと（Search Engine Result Page）。SERPsと複数形で使われることもあります。

## 技術スタック
現在自分が関わっているプロジェクトのフロントエンド部分に選定されている技術について、それぞれをWeb Stories（というより、AMP自体）と組み合わせた際に注意すべき点などについてまとめました。

### Next.js
せっかくストーリーページを実装したなら、Googleの検索結果に載せてたくさんの人に見て欲しいですよね。そのためには、作ったページを「有効なAMP」（以下、AMP validと表現します）にする必要があります（これ以外にもいくつか満たさなければならない基準があるのですが、それらについては後述します）。

Next.jsでのAMP対応は非常に簡単です。pageファイルにこう書くだけ。

```ts
// pages/**.tsx
export const config = { amp: true };
```
上記はあくまでAMP対応するだけで、validかどうかは別途確認する必要があるので注意しましょう。
Next.jsは、開発時に自動で[amphtml-validator](https://www.npmjs.com/package/amphtml-validator)による検証を行ってくれます。エラーと警告はNext.jsを起動したターミナルに表示されます。

さて、Next.jsを使って開発する場合は`_document.tsx`や`_app.tsx`に全ページ共通の要素を入れている場合もあるのではないでしょうか。ところがストーリーページは、bodyタグ直下に`amp-story`以外の要素がいるとAMP validになりません。わたしたちの場合は、ここに計測タグを入れているためにバリデーションエラーになっていました。
これを避けるため、URLを正規表現でチェックしてストーリーページである場合は計測タグを挿入しない、という処理を書くことになりました。

### TypeScript
AMPコンポーネントには `@types/react` のような型定義がサポートされていないので、自分で型を定義する必要があります。
例えば`amp-story`コンポーネントは、以下のようにして型を定義しました。

```tsx
namespace JSX {
  // ReactAmp型の定義
  type ReactAmp = React.DetailedHTMLProps<
    React.HTMLAttributes<HTMLElement>,
    HTMLElement
  >;

  // ReactAmp型を拡張してAmpStory型の定義を作る
  interface AmpStory extends ReactAmp {
    standalone?: "";
    title: string;
    publisher: string;
    "publisher-logo-src": string;
    "poster-portrait-src": string;
  }
}
```
属性は[公式のドキュメント](https://amp.dev/documentation/components/amp-story/?format=stories#attributes)を見ながら設定しました。
属性に入る値が限定されているようなケースであれば、以下のようにユニオンで指定してもよいでしょう。

```ts
interface AmpStoryGridLayer extends ReactAmp {
  template: "fill" | "vertical";
}
```

### styled-components
CSSはstyled-componentsを使って記述しました。
ストーリーページの構造は以下の図のようになっており、大まかなレイアウトはAMP側が提供するものになるのですが、`amp-story-grid-layer`内のデザインは自由に作ることができます。
styled-componentsを用いてスタイリングしたコンポーネントは全てこの中に配置しました。
![amp-story-tag-hierarchy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692844/fe5130bb-3694-98c9-3b09-40c713a75096.png)
出典: https://amp.dev/documentation/components/amp-story/?format=stories#amp-story-format

styled-componentsに限った話ではありませんが、デザインの自由度に関しては少しばかりの制限があります。
以下、デザインでつまずいたポイントです。解決策がないので、参考にはならないかもしれませんが……

#### bodyのfont-sizeが操作される
`amp-story`コンポーネントを読み込んだ際に挿入されるcssによって、bodyのfont-sizeが操作されてしまいます。
![amp-story-css.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692844/549e4552-e98b-ddcd-1548-632e68a3a700.png)
他のコンポーネントのfont-sizeをremで管理している場合、これによってfont-sizeが意図通りにならなくなり、思ったようなスタイリングができなくなってしまいます。しかしながら、裏を返せば端末のサイズに応じたfont-size調整を自動で行ってくれるということなので、サイズに細かいこだわりがない場合はむしろ都合が良いとも言えますね。

#### AMPが動的に挿入してくる要素のスタイルは上書き厳禁
AMPコンポーネントに付随して、いくつかのDOMが動的に読み込まれます。そのような要素には、 `i-amphtml-xx` というクラス名が付与されています。
![スクリーンショット 2020-08-16 14.02.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692844/a1b94818-ed96-f952-d687-f58a7aa5b028.png)
これらの要素にはあらかじめスタイリングが施されているため、そのままでも見た目は悪くはありません。ただ、デザインルールに則り余白の調整がしたいとか、もう少しだけ小さくしたいとか、どうしても細かい調整を加えたくなるものです。
これらの要素のスタイルを上書きすること自体は可能ですが、そうするとvalidatorに引っかかってしまい、AMP validなページになりません。

以上のように、デザイン上の制限がいくつかあるので、用意してあるデザインをパーフェクトピクセルで再現するのはまず無理だと考えた方が良いでしょう。デザイナーと相談しながら最適な落としどころを探るのがベストであると言えそうです。

## SERP表出のために注意した点
作ったストーリーをGoogleの検索結果に載せるには、以下の2点が守られている必要があります。

- AMP validであること
- Googleが公開している「[ウェブストーリーのガイドライン](https://developers.google.com/search/docs/guides/enable-web-stories?hl=ja#technical-guidelines)」に準拠していること

AMP validであるかについては、上述のようにNext.jsを使うことでバリデーション結果をターミナルに表示させることができますし、[Chromeの拡張機能](https://chrome.google.com/webstore/detail/amp-validator/nmoffdblmcmgeicmolmhobpoocbbmknc?hl=ja)や[web版のvalidator](https://validator.ampproject.org/)などでもチェックできます。あとはvalidatorに怒られている箇所をひとつひとつ潰していきましょう。

続いてガイドラインの準拠についてです。上記のガイドラインに加えて、[Best Practice for creating a successful Web Story](https://amp.dev/ja/documentation/guides-and-tutorials/start/create_successful_stories/?format=stories#text)の記述内容も見ながら実装していきました。

ストーリーページのためにオリジナルのコンテンツを用意できれば良いのですが、このようなモダンな技術を使ったページはどちらかというとエンジニア発案で実装に至るケースが多いのではないでしょうか。そのため、ストーリーページは既存のコンテンツを流用して作成することが多いように感じます（実際、わたしたちのプロジェクトではそうでした）。
どういったコンテンツをストーリー化するべきかは、ガイドラインの内容と密接に関わっています。

### ストーリーの長さ
> ウェブ ストーリーの長さは 5～30 ページ（できるなら 10～20 ページ）にすることをおすすめします。
出典: https://developers.google.com/search/docs/guides/enable-web-stories?hl=ja#technical-guidelines

ストーリーのためにオリジナルのコンテンツを用意できる場合は、あまり気にしなくてよい項目でしょう。
わたしたちの場合は、既に存在するユーザーの投稿ページをストーリーのコンテンツにしました。ユーザーの投稿には画像が紐づいており、それをストーリーとして表示できるような具合です。画像の数は投稿によってバラつきがあるため、ストーリーの推奨枚数に満たないものもたくさんありました。
今回はNext.jsのSSRを使って、投稿に紐づく画像の枚数が規定に満たないようなケースではストーリーページにしないように工夫しました。
この基準に沿わないページがAMP invalidになるわけではないのですが、クローラーがやってきた時に低品質なページであると判断されるリスクがあるかもしれない、という考えから上記のような実装にした経緯があります。

### ストーリーのタイトル
> Keep the title clear and clean, ideally under 10 words in less than 40 characters. Include the author and publication name and add a publish date if the story is time sensitive.
(訳) タイトルは簡潔に、10単語40文字以内が望ましい。著者と発行者の名前を含め、必要であれば公開日も入れる。
出典: https://amp.dev/ja/documentation/guides-and-tutorials/start/create_successful_stories/?format=stories#text

ページのタイトルとは別に、`amp-story`タグの属性として指定するものです。
「著者と発行者」とありますが、これはUGCサービスを想像してもらえるとわかりやすいかと思います。著者がユーザー、発行者はサービス名ですね。
ただしこれらを全て内包したタイトルを40文字以内に収めるのはとても難しいです。今回はあくまで「推奨」であると割り切って、できる限り短くすることにしました。
既存コンテンツをストーリー化して公開する場合は、既存のページとタイトルが重複しないように注意することも必要です。

### 各ページのテキスト
> 各ページのテキストは短くします（1 ページあたり 200 文字未満）。各ページの主題は 1 つに絞ります。
出典: https://developers.google.com/search/docs/guides/enable-web-stories#technical-guidelines

タイトルと違って、テキストは特定のコンポーネントの属性ではありません。ストーリーページ内のコンテンツとしてのテキストを指しています。テキストが挿入されるのは、`amp-story`コンポーネントの子要素の`amp-story-page`の、更に子要素である`amp-story-grid-layer`の子要素部分です。ストーリーページは画像だけでも成立するので、テキストは必須の要素ではありません。

>Size 24 font should be the minimum size used, but make it as large and legible as possible. Contrast text color with the story page background or image.
(訳) フォントサイズは24を最小値とし、できるだけ大きくします。背景の色や画像とのコントラストがはっきりするようにしましょう。
出典: https://amp.dev/ja/documentation/guides-and-tutorials/start/create_successful_stories/?format=stories#text

以下は公式ドキュメントから引用した画像ですが、これを見る限りfont-sizeが24px（pt？）以下のテキストが混入すること自体が悪いわけではなさそうです。一番強調したいテキストのfont-sizeが規定値を下回らないようにすればよい、と考えて良いでしょう。

|Good|Bad|
|:-:|:-:|
|![readability1-do.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692844/d7c1bdca-6d37-260b-9380-07a37fa2f8a9.jpeg)|![readability1-dont.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692844/57a9e9ad-e24c-66ef-ac60-2e36b169c064.jpeg)|
出典: https://amp.dev/ja/documentation/guides-and-tutorials/start/create_successful_stories/?format=stories#text


### その他
ガイドラインには他にも、アフィリエイトに関する注意事項や動画を載せる場合に関する指標なども記されています。
今回実装したコンテンツとはあまり関係がなさそうだったので、これらについては深く調査していませんが、上記のドキュメントを参考にしながら実装していけばあまり難しいことはないだろうと思います。

### 取り組みの結果
残念ながらオーガニックサーチでの流入はまだないのですが、なんとGoogle Discoverへの表出が確認できました！
どうやらここ最近DiscoverフィードにWeb Storiesコンテンツの掲載が増えてきているようです。
>今 Google は Web Story のプロモーションに力を入れているようです。
>例でも取り上げた [Forbes の Web Story 事例の日本語訳記事](https://developers-jp.googleblog.com/2020/07/forbes-web-story.html)をデベロッパー向け公式ブログが公開しています。
出典: [Google Discoverでの露出を増やすならWeb Storyが狙い目？ | 海外SEO情報ブログ](https://www.suzukikenichi.com/blog/web-stories-in-discover/)

フィードに大きく画像が表示されるDiscoverとWeb Storiesの相性は、確かに非常に良いように感じられます。
また、Discoverに表示されるコンテンツはユーザーの嗜好に合わせて変化するので、クリック率が非常に高くなるのが特徴です。
狙って載せるのは難しいところですが、ガイドラインに即したコンテンツを作れば結果に繋がることがわかりました。

## まとめと今後の展望
モダンな技術とWeb Storiesの組み合わせから、SERP表出のために調べたことなどを記事にしてみました。
SEOまで考慮しての実装は骨が折れる作業でしたが、単純に作るだけであればとても簡単で、かつ楽しいです。
本当は構造化データの設計や、計測絡みで死ぬほど苦労した点についても書きたかったのですが、これらに関してはまだ十分な調査ができていないため、次の記事で解説できたらと考えています。