とあるLPの制作現場

Webデザインの本などで、「架空のカフェのサイトを作ってみよう！」みたいなフワフワしたチュートリアルはよくありますが、実際のサイトができあがっていく様をあまり見たことがないなと思い、LPの制作と並行して記事を書きました。

工程のすべてを書くのはつらいので、ハマった所や気をつけた所、後ろめたかった所を中心にまとめています。見苦しい所が多々あると思うので、そのときはそっと目を閉じてください。

※ 記事にする許可は先方からいただいています

![lp.gif](https://qiita-image-store.s3.amazonaws.com/0/89103/f6faf239-62a1-8a67-58b3-3a6c11d1a302.gif)

**LPとは**
ランディングページ。ユーザーが訪れる最初のページという意味らしいのだけど、商品の魅力を訴求する1枚の縦長ページという認識の方が強い。このページは長くないけれど。

# 書くこと
* [環境構築](#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%81%AE%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89)
* [HTML](#html)
* [CSS](#css)
* [JavaScript](#javascript)
* [公開前の確認](#%E5%85%AC%E9%96%8B%E5%89%8D%E3%81%AE%E7%A2%BA%E8%AA%8D)
* [ロリポップ](#%E3%83%AD%E3%83%AA%E3%83%9D%E3%83%83%E3%83%97)

各項目に対して思ったことを書いているので、バランスはバラバラです。

# LPと作業の概要
カードゲームの紹介ページです。

公開時はLP1枚だけですが、今後ページを追加する可能性があります。サーバーはロリポップで、ドメイン取得済です。

HTMLの修正や画像の差し替えなどは、フロントエンドに慣れていない人が行う可能性があるため、HTMLをそのまま触れるようにしておきます。リポジトリをクローンしてヤーンしてから編集したものをコミットしてください、みたいな寝言は言えません。

CSSやJSは触らないという話だったので、そこのところは自由にやります。

デザインはすでにできているので、画像の切り出しから入ります。

工数は1日。

# Photoshopから画像を切り出す
いまだに雰囲気でPhotoshopを使っている。

もう長いこと使っている気がするのだけど、スライスとアセットぐらいしかできない。いくら覚えようと思ってもなかなか身につかないので、Photoshopを使いこなしている方々には尊敬の念しかないです。

そんなこんなで苦しみつつ、デザインデータから素材を切り出します。

[レイヤーからの画像アセットの生成](https://helpx.adobe.com/jp/photoshop/using/generate-assets-layers.html)
[スライスを使った基本操作](https://helpx.adobe.com/jp/photoshop/kb/224107.html)

# フロントエンドの環境構築
エディターさえあればWebサイトは作れますが、SassやBabelを使った方が快適に開発を進めることができるので、環境を整えていきます。ただ、環境構築に時間をかけ過ぎると、Webサイトを作りたいのか、環境を作りたいのかわからなくなってくるので、完璧は目指さず手短に済ませます。

## やりたいこと
|   | やりたいこと |
|:---:|:---:|:---:|
| HTML | そのまま |
| CSS | Sass |
| JS | Babel |
| 画像 | 圧縮 |

やりたいことはWebpackだけでも実現できますが、今後ページが増えていくことを考えるとgulpを使用したほうがスッキリ書けそうなので、gulpとWebpackを併用しました。最近gulpの名前をめっきり聞かなくなりましたが、元気にしているだろうか。

## Webpackとgulpの使い分け、あるいは共存

### Webpack
>  webpack is a static module bundler for modern JavaScript applications.
> 
> https://webpack.js.org/concepts/

色々なファイルをまとめて、モリモリなJSファイルを出力してくれる。BabelやSassなどのローダーを設定するだけで、いい感じに変換してくれる。プラグインを使えばHTMLやCSSなどのファイル出力もできます。

### gulp
> gulp is a toolkit for automating painful or time-consuming tasks in your development workflow
> 
> https://gulpjs.com/

色々なタスクを実行してくれる。BabelやSassなどのタスクを設定すると、いい感じにファイルを吐き出してくれます。gulpからWebpackのタスクを呼び出すこともできます。

### 使い分け/共存
Webpackとgulpは共存も可能です。SPAを作る場合は、諸々のファイルをWebpackでまとめるだけで事足りますが、普通のWebサイト制作で、複数のHTMLやCSSを出力したくなった時には、Webpackだけだと辛くなってきます。

プラグインを組み合わせれば、本来のJSのバンドルから離れた事でも大体なんとかなりますが、なんとかなる頃にはコンフィグが超大作になっているので、素直にgulpを併用した方がシンプルに書けるかもしれません。

ひとつのツールにまとめたいのが本音ですが。

### 盛者必衰
ユーザーが減ってくるとプラグインのメンテナンスが滞りがちなので、gulpのプラグインに依存する恐怖はありますが、長い目で見ればWebpackも似たようなものなので、気にしないことにします。

「複雑なことはしない」ことにさえ気をつけていれば、ツールの寿命が来た時にサクッと乗り換えられるはずなので、なんとかなると信じたい。

ダラダラと書いていたら長くなってしまったので、使用したタスクのリンクだけ張っておきます。

https://github.com/noplan1989/gemba/tree/master/gulpfile.babel.js

# HTML
ファイル分割などはせず、ひとつのファイルにガッと書いていきます。PugよりもHTMLのままの方が好きです。

## タグを積む
ブロックごとに、タイトルがあって説明がある綺麗なデザインだったため、sectionでまとめて積んでいきます。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=640">
  <title>SPECIAL MOOOVE!｜カードゲーム</title>
  <!-- メタ -->
  <link rel="stylesheet" href="/assets/css/specialmooove.css">
</head>
<body>
  <div class="container">
    <div class="contents">
      <article class="main">
        <section class="section mv"></section>
        <section class="section intro"></section>
        <!-- 他のコンテンツが色々 -->
      </article>
      <footer class="footer"></footer>
    </div>
    <div class="modal"></div>
  </div>
  <script src="/assets/js/specialmooove.js"></script>
</body>
</html>
```

## コメントを多めにする
日頃HTMLを見慣れていない人が、ぱっと見ただけで編集する場所がわかりやすいように、コメントを多めに残します。

```html
<!-- スライダー ここから ======================================== -->
<ul class="system-step-wrapper swiper-wrapper">
  <li class="system-step-slide swiper-slide">
    <img src="/assets/img/specialmooove/system/step/step01.png" alt="[1]山札を1枚表にします。...">
  </li>
  <li class="system-step-slide swiper-slide">
    <img src="/assets/img/specialmooove/system/step/step02.png" alt="[2]全ヒーローは、...">
  </li>
  ...
</ul>
<!-- スライダー ここまで ======================================== -->
```

## viewport 640
```html
<meta name="viewport" content="width=640">
```
スマホメインのサイトを短い時間でデザイン通りに構築するには手っ取り早いのでviewportを固定しました。今どきviewport固定とかありえない、と言われたらぐうの音も出ない。震える。

## 組み上がり
まだスタイルがあたっていないので、画像が積まれているだけの状態です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/89103/8a84a7a3-2a2c-ca74-69fc-1854ca62d99e.png" width="400">

https://github.com/noplan1989/gemba/blob/master/src/specialmooove/index.html

# CSS
あまり複雑なレイアウトはないため、深く考えずにガシガシとCSSを書いていきます。

## SassとSCSS
Sassには2つの記法があり、ミニマルなSass記法の方が好みだったのですが、CSSに慣れているとSCSS記法の方が読みやすいということもあり、新規で作る場合はSCSS記法を使うようにしています。

[What’s the Difference Between Sass and SCSS?](https://www.sitepoint.com/whats-difference-sass-scss/)

### Sass記法 - ミニマルなやつ
```sass:sass
=bg-cover
  background-position: center center
  background-repeat: no-repeat
  background-size: cover

.hoge
  +bg-cover()
```

### SCSS記法 - CSSっぽいやつ
```scss:scss
@mixin bg-cover {
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
}

.hoge {
  @include bg-cover();
}
```

## ファイル構成
```scss
@charset "utf-8";

// ==========================================================================
// 変数とmixin
// ==========================================================================
@import "foundation/variables";
@import "foundation/mixin";

// ==========================================================================
// リセットとか
// ==========================================================================
@import "base/reset";
@import "base/helper";

// ==========================================================================
// ライブラリ
// ==========================================================================
@import "vendor/swiper";

// ==========================================================================
// Special Mooove
// ==========================================================================
@import "specialmooove/base";
@import "specialmooove/mv";
@import "specialmooove/intro";
@import "specialmooove/system";
@import "specialmooove/movie";
@import "specialmooove/choice";
@import "specialmooove/footer";
@import "specialmooove/modal";
```
変数などは専用のディレクトリにまとめて、LPのスタイルはセクションごとにファイルを分割して管理しています。

## クラスの命名
```scss
.intro {
  position: relative;
  &-title {
    position: relative;
    z-index: 1;
    padding-top: 200px;
    text-align: center;
  }
  &-copy {
    //
  }
  &-text {
    //
  }
}
```
短いLPなのでクラス名が重複する危険性が少なく、使い回すパーツもないので、ブロック毎にクラスを振って子要素をハイフンでつなげるだけの、ゆるいクラス名にしました。

## CSS適用後
動きはありませんが、デザインが無事に反映されました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/89103/ccc2269a-0030-719d-7ede-97be5e71c9a8.png" width="400">

https://github.com/noplan1989/gemba/tree/master/src/assets/sass

# JavaScript
スライダーやモーダルでJSが必要なので、順番に実装していきます。工数を短縮するべく、できる限りライブラリを使いました。

## メインビジュアル

![mv.gif](https://qiita-image-store.s3.amazonaws.com/0/89103/2b2511a2-8884-6456-cbda-ed2166c4ddb2.gif)

```js:mv.js
import { TweenMax, Expo } from 'gsap'
import imagesLoaded from 'imagesloaded'

export default function () {
  const anime = () => {
    // パッケージ
    TweenMax.fromTo('#mv-package', 0.6, {
      x: 600
    }, {
      x: 0,
      ease: Expo.easeOut
    })

    // 他に色々
  }

  // 画像読み込み完了後アニメーション
  imagesLoaded('#mv', { background: true }, () => anime())
}
```

斜めなロゴとSpecial Moooveという名前が印象的だったので、ナナメにギュインとムーブさせる事で、多少のゲーム感を出しています。アニメーションライブラリにはTweenMaxを使いました。

[TweenMax](https://greensock.com/tweenmax)
[imagesLoaded](https://imagesloaded.desandro.com/)

## ループで横に流れるやつ

![intro.gif](https://qiita-image-store.s3.amazonaws.com/0/89103/5fa1de82-300c-d082-be7f-df9085039a74.gif)

```js:card.js
import { TweenMax, Power0 } from 'gsap'
import imagesLoaded from 'imagesloaded'
import { eachElement } from './helper'

export default function () {
  const SPEED = 45
  const cards = document.querySelectorAll('.intro-card')

  eachElement(cards, card => {
    imagesLoaded(card, () => {
      const inside = card.querySelector('.intro-card-inside')
      const img = inside.querySelector('img')
      const distance = img.clientWidth
      const duration = distance / SPEED
      const toRight = card.classList.contains('mod-reverse')

      inside.appendChild(img.cloneNode(true))

      TweenMax.fromTo(inside, duration, {
        x: toRight ? -distance : 0
      }, {
        x: toRight ? 0 : -distance,
        ease: Power0.easeNone,
        repeat: -1
      })
    })
  })
}
```

最初はCSSアニメーションを `infinite` してループさせていたのですが、iOSのSafariでなぜか時々チラついてしまい、原因の特定に時間がかかりそうだったので、気持ちを切り替えてTweenMaxで `repeat` させました。画像を複製して左右にループさせています（適当）。


## スライダー

![system.gif](https://qiita-image-store.s3.amazonaws.com/0/89103/3c354700-6818-9c25-f3ab-cf941d1fa2f9.gif)

```js:slider.js
new Swiper('.swiper-container', {
  pagination: {
    el: '.system-step-pagination',
    clickable: true
  },
  navigation: {
    nextEl: '.system-step-next',
    prevEl: '.system-step-prev'
  }
})
```

スライダーはタッチ対応などが面倒なので、[Swiper](http://idangero.us/swiper/)というライブラリを使いました。

[Swiper](http://idangero.us/swiper/)

## モーダル

![choice.gif](https://qiita-image-store.s3.amazonaws.com/0/89103/6d5286dc-4f97-bfa3-f558-f148ea3850af.gif)

```js:modal.js
import { TweenMax } from 'gsap'
import { eachElement } from './helper'

export default function () {
  const el = {
    // DOM
  }

  const open = () => {
    // 開く処理
  }

  const close = () => {
    // 閉じる処理
  }

  // ボタンのクリックで開く
  eachElement(el.triggers, trigger => {
    trigger.addEventListener('click', () => {
      const target = trigger.getAttribute('data-target')
      el.active = document.getElementById(target)

      open()
    })
  })

  el.modalContents.addEventListener('click', e => e.stopPropagation())
  el.modal.addEventListener('click', () => close())
  el.close.addEventListener('click', () => close())
}
```

モーダルは使い慣れているライブラリがなかったので、TweenMaxでやりました。

## まとめてガッ

```js:index.js
import FastClick from 'fastclick'
import mv from './mv'
import card from './card'
import slider from './slider'
import modal from './modal'

document.addEventListener('DOMContentLoaded', () => {
  FastClick.attach(document.body)

  card()
  slider()
  modal()
  mv()
})
```
もろもろの処理を、 `DOMContentLoaded` のときにまとめてガッと実行します。

[DOMContentLoaded | MDN](https://developer.mozilla.org/ja/docs/Web/Reference/Events/DOMContentLoaded)

## JavaScript適用後

![lp.gif](https://qiita-image-store.s3.amazonaws.com/0/89103/f6faf239-62a1-8a67-58b3-3a6c11d1a302.gif)

# 公開前の確認
このままサクッと公開したいところですが、コーディングに不備がないか確認していきます。

## リント
|対象|ツール|
|:---:|:---:|
| HTML | [HTMLHint](https://marketplace.visualstudio.com/items?itemName=mkaufman.HTMLHint) |
| CSS | なし |
| JS | [ESLInt](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) |

HTMLとJSは、VSCodeに拡張をいれてコーディング中にチェックしています。CSSはSassのコンパイルが通ればOKというラフな感じで、特にチェックしていません。

## リンク切れ
ページにリンク切れがあると残念なので、ツールを使って確認します。こちらのChrome拡張が使いやすいです。
https://chrome.google.com/webstore/detail/check-my-links/ojkcdipcgfaekbeaelaapakgnjflfglf?hl=en-GB

## alt
altも同様に確認します。Chrome拡張を使うと、ブラウザ上にaltが表示されるので確認しやすいです。
https://chrome.google.com/webstore/detail/check-my-links/ojkcdipcgfaekbeaelaapakgnjflfglf?hl=en-GB

## Google Analytics
とりあえず設置して、リアルタイムで取得できていることを確認します。
https://www.google.com/intl/ja/analytics/

## SNSでシェアしたときの見た目

### Facebook

<img src="https://qiita-image-store.s3.amazonaws.com/0/89103/ebc5dde5-62dd-b278-fd59-ef7d8bf48b85.png" width="400">

[シェアデバッガー](https://developers.facebook.com/tools/debug/)という公式のツールで確認します。


### Twitter

<img src="https://qiita-image-store.s3.amazonaws.com/0/89103/9c1b9464-7b59-ff9b-46c4-db64f17fc8e6.png" width="400">

[Card validator](https://cards-dev.twitter.com/validator)という公式のツールで確認します。

## ブラウザ確認
| OS | ブラウザ |
|:---:|:---:|
| Win | Chrome/Firefox/Edge/IE11|
| Mac | Chrome/Firefox/Safari |
| iOS | Safari |
| Android | Chrome |

この街のどこかにも、IEというブラウザを使っている人がいるらしいので、念のために表示を確認します。flexやSVGまわりで表示が崩れることや、JSの仕様が追いついていなくてエラーが出ることが多いので、そこら辺を重点的に確認します。一発ですんなり行けたことは、かつて一度もない。

はやくIE11のサポートが切れてEdgeだけにならないかなぁ。できればEdgeもいらないなぁ。と思ったのだけど、日頃VSCodeを使わせてもらっているので、あまり悪いことは言えない。最近は、IE対応をVSCodeの利用料だと思うことにしている。

スマホは手元の実機確認のみ。震える。

[勝手にふるえてろ](http://furuetero-movie.com/comment/)

## パフォーマンス

![speed.jpg](https://qiita-image-store.s3.amazonaws.com/0/89103/4fb96100-18d0-44d9-4796-189f5d260914.jpeg)


点数を上げることに熱中し始めると時間がかかるので、明らかなボトルネックがないかだけを確認します。HTMLの圧縮や画像のキャッシュは、先方が更新する際に厄介なのでスルーしました。

[PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/?hl=ja)


# ロリポップ
なんでこんな名前にしたんだろう。いよいよ公開作業です。

## ファイルをアップするだけ
当たり前のことなのだけど、ファイルをアップするだけで公開されることに感動。EC2とかS3とかを設定せずにアップするだけといいうのは、想像以上に楽でいい。

## 証明書が無料

<img src="https://qiita-image-store.s3.amazonaws.com/0/89103/a6418c2e-f6df-cec4-fea8-bde65406e530.png" width="400">

安いプランでも、申請するだけで[Let's Encrypt](https://letsencrypt.org/)で証明書を取ってくれます。嬉しい。

https://lolipop.jp/manual/user/ssl-free-order/

# デフォルトでgzip圧縮
何も設定してないのにgzip圧縮してくれてる。ありがたい。

# 公開！

[![lp.gif](https://qiita-image-store.s3.amazonaws.com/0/89103/f6faf239-62a1-8a67-58b3-3a6c11d1a302.gif)](https://oyatsugames.jp/specialmooove/)

https://oyatsugames.jp/specialmooove/

# 所要時間
|工程|時間|
|:---:|:---:|
| 環境構築 | 0.5 |
| Photoshop | 1 |
| HTML | 1 |
| CSS | 1.5 |
| JS | 4 |
| 公開前の確認 | 2 |
| 合計 | 10時間 |

長くなりましたが、現場からは以上です。

# ソース
https://github.com/noplan1989/gemba