「Gatsby x TypeScript」でブログを作りました (2. レスポンシブルデザインのコツ)
Gatsbyでブログを作った人の最初の記事と言えば、だいたい「Gatsbyでブログ作りましたー」でしょう。なので許してください。
「Gatsby x TypeScript」でブログを作る時に役に立った情報などを自分用のメモというか棚卸しというかまとめていきたいと思います。
このブログ一見シンプルに見えて、ワードプレスで作られたブログと引けを取らないくらい実はいろんな機能が盛り込まれてます。
- Disqusによるブログコメント投稿機能
- Algoliaによる全文検索機能
- Mailgunによる自動返信メール(お問い合わせ時)
- Google reCAPTCHA v3 (サーバーサイドバリデーションもあり)
- Google Analytics
- Iframelyによる記事へのiframeの埋め込み機能
- JSONLDを使った構造化マークアップ(SEO対策)
全4回に分けて、gatsbyのプラグインで一瞬で実装が終わったものもあればそうでないものもあるので実装する上で参考になったサイトと要点を軽くまとめなながら紹介できたらいいかと思います。
「Gatsbyでのブログ作成手順」は他のブログにあるので手順に関しては紹介しません。
この投稿では 2.レスポンシブルデザインのコツ
について書こうと思います。
Responsively app
レスポンシブル対応の動作確認をするのって大変ですよね。でもその問題を鮮やかな方法で解決しているツールがあります。 最近、GitHubで人気急上昇になっている色んな端末で一気に動作確認できるツールmanojVivek/responsively-appがすごく便利です。
良さそうでしょう。 たまにスクロールが連動してくれなくなる不具合とかがありますが、なかなか使えそうなプロダクトです。 一気に確認する時には役にたつかと思います。
インストールは簡単なので使ってみたらいいと思います。
brew cask install responsively
ですが連続した画面幅の変更によるデザインの確認に関しては各ブラウザの検証ツール(DevTools)を使ったがいいでしょう。
htmlタグのfont-size
各端末でfont-sizeを整えていくのは非常に辛い作業の一つだと思います。
フォントサイズの調整のやり方は px・em・rem
と色々ありますが rem
が使ったがいいと思います。
最終的には em
も rem
も px
に変換されるから px
でいいじゃないかと思われるかもしれないですが
px
だと以下の問題があるからよくないです。
ただし、こうした絶対値による文字サイズ指定はブラウザによってはユーザー操作による文字サイズの拡大/縮小に対応できなかったり、タブレットやスマートフォンなど、高解像度のディスプレイを備える端末で表示をした場合に、端末ごとに見た目の文字サイズが大きく異なって表示されたりする場合があります。
パソコン工房さんの記事がすごくわかりやすかったです。
rem
は親要素の影響を全く受けないので htmlタグ
に指定されたフォントサイズを基準として何倍かを指定していくやり方
です。
このパソコン工房さんの図をみたら一発で腑に落ちました。
rem
を使っていく上でぜひ設定して置きたい設定は、htmlタグの font-size
が 10px
になるように以下を指定しておく事です。主要なブラウザのデフォルト(初期設定)の文字サイズは16pxとなっているため、それを10pxにするためには 10/16で62.5%にすればいいという事です。
html {
// htmlタグのfont-sizeを10pxに設定する
font-size: 62.5%
}
コンポーネントのfont-size
コンポーネントのfont-sizeですが、レンダリングするものにクラス(css)の指定ができるかできないかで 少しだけ設定の仕方が変わってくると思います。
クラス指定ができる場合
コンポーネントでfont-sizeを設定していく時は、%
を使いましょう。
絶対値で指定してしまうと、コンポーネントの再利用性を損なってしまいます。
あと、文字がつまりすぎてるようだったら、line-height
も一緒に %
で指定したがいいかもしれません。
例えば、このPostLineコンポーネントの場合で説明すれば、
このようにfont-sizeに %
を指定してあります。
コードで書けばこんな感じです。
import React from 'react'
import ClassNames from 'classnames'
import styles from './PostLine.module.scss'
type Props = {
className?: string
}
const PostLine: React.FC<Props> = ({ className }) => {
const rootClassName = ClassNames({
[styles.root]: true,
[className]
})
return (
<div className={rootClassName}>
<div className={styles.title} />
<div className={styles.info} />
<div className={styles.excerpt} />
</div>
)
}
export default PostLine
.root { font-size: inherit; }
.title {
font-size: 150%;
line-height: 200%; // お好みで%で指定します。
}
.info { font-size: 80%; }
.excerpt { font-size: 100%; }
それで実際のfont-sizeは className
というpropsを通して設定します。
<PostLine className={styles.post_line} />
.post_line {
font-size: 2rem;
}
これで利用する側から自由にfont-sizeを指定できるようになります。
クラス指定ができない場合
クラス指定ができない場合、どういう場合があるかというと、markdownなファイルをパースしてhtmlに変換したものを
import React from 'react'
import ClassNames from 'classnames'
import styles from './BlogPost.module.scss'
type Props = {
className?: string
}
const BlogPost: React.FC<Props> = ({className}) => {
const rootClassName = ClassNames({
[styles.root]: true,
[className!]: className
})
return (
<div className={rootClassName}>
// markdownの内容がrenderingされる
<div dangerouslySetInnerHTML={{ __html: node.html }} />
</div>
)
}
export default BlogPost
HTMLタグに直接クラスを付与していく必要があります。
うまく適用されない場合は、!important
の指定がいるかもしれません。
<BlogPost className={styles.blog_post} />
.blog_post {
h1 { font-size: 150% !important; }
}
まとめ
ではここまでの内容をまとめます。
- コンポーネントのfont-sizeの指定
- 方針としてfont-sizeに絶対値を使わない。再利用性が下がるので
-
内部rootクラスに
font-size: inherit
を書く。-
■クラス指定ができる場合
- その他のクラスには、
font-size: **%
で指定する。 - 外部rootクラス(className)で
font-size: 2rem
とか指定する。
- その他のクラスには、
-
■クラス指定ができない場合(dangerouslySetInnerHTMLで結果をレンダリングする場合)
- 内部rootクラスの中でHTMLタグに直接
h2 { font-size: 150% !important; }
などとしていく。 - 外部rootクラス(className)で
font-size: 2rem
とか指定する。
- 内部rootクラスの中でHTMLタグに直接
-
画面幅に合わせて変動するfont-size
これはテクニックとして覚えておいたら便利だと思うので紹介しておきます。
例として、画面幅1280pxから画面幅300pxまででfont-sizeが40pxから10pxに変化する場合を考えてみます。
これをscssで使えるmixinとして作っておいたらいいと思います。
こちらの記事を参考にして以下のように作りました。内容としてはこれは中学生の問題ですね。「2点を結ぶ直線の方程式問題」です。
ですが、油断するとcalcの中で変数を展開する時にハマるという苦い思いをします。
// [ref]
// https://neos21.hatenablog.com/entry/2018/02/22/080000
@function strip-unit($num) {
@if type-of($num) == 'number' and not unitless($num) {
@return $num / ($num * 0 + 1);
}
@return $num;
}
@function gradient($fromFontSize, $toFontSize, $fromWidth, $toWidth){
@return ((strip-unit($toFontSize) - strip-unit($fromFontSize))/((strip-unit($toWidth) - strip-unit($fromWidth))));
}
@function rem($k, $toWidth) {
@return (1rem / $k) * strip-unit($toWidth);
}
// [ref]
// https://note.com/nomen_machine/n/n089fae61dd00
// $fromFontSize(rem)
// $toFontSize(rem)
// $fromWidth(px)
// $toWidth(px)
@mixin responsible-font-size($fromFontSize, $toFontSize, $fromWidth, $toWidth) {
$k: 10; // 10px = 1rem
$toWidthRem: rem($k, $toWidth);
$gradient: gradient($fromFontSize, $toFontSize, $fromWidth, $toWidth);
font-size: calc( #{$fromFontSize} + ((100vw - #{$toWidthRem} ) * #{$gradient} ));
}
使い方はこんな感じです。
.root {
@include responsible-font-size(1.6rem, 0.8rem, 1280px, 375px)
// もしくは
@include responsible-font-size(1.6rem, 0.8rem, 1280, 375)
}
$fromFontSize
は <p>
や <a>
や <li>
などのフォントサイズを100%と考えて設定したら
Typographyになる気がします。そして<h2>
とかのフォントサイズを150%とかにすればいい感じになるでしょう。
ですが実際にこのresponsible-font-size
mixinを使う時に渡す引数の値はよほど理由がないかぎり変わらないと思うので、具体的な値を入力した共通パーツを作っておくことをお勧めします。
@mixin common-responsible-font-size {
@include responsible-font-size(1.6rem, 0.8rem, 1280px, 375px);
}
仮に<p>
のLaptopでのサイズを1.6rem
から変えたくなったらここを変えれば済むので修正が楽になります。
これでコンポーネントでレスポンシブルなフォントサイズを扱えるようになり、レスポンシブルデザインに苦しまなくなるかと思います。
breakpointで見た目を変える
breakpointでデザインを変えたい場合ってあると思います。
例えば、コンポーネントを変えたい場合だとこのブログの投稿リストの要素コンポーネント(PostCard or PostLine)
だったり、適用したいスタイルを変えたい場合だったら、このブログの前のページ・次のページ(PrevNext)
だったりがあると思います。
それでは各例について具体的に説明していきます。
まず画面幅がbreakpointより大きいか小さいかでtrueかfalseを返すためのReact.hooks
を作っておきます。
import { useState, useEffect } from 'react'
import throttle from 'lodash.throttle'
const THROTTLE_DELAY_RESIZE = 100;
type Props = {
breakPoint: number
}
const useHandleResponsible = ({ breakPoint }: Props) => {
const [changeDesign, setChangeDesign] = useState(false)
useEffect(
() => {
const handleResize = () => {
setChangeDesign(window.innerWidth < breakPoint)
}
const throttleHandleResize = throttle(handleResize, THROTTLE_DELAY_RESIZE)
if (typeof window !== 'undefined') {
handleResize()
window.addEventListener('resize', throttleHandleResize)
}
return () => {
if (typeof window !== 'undefined') {
window.removeEventListener('resize', throttleHandleResize)
}
}
},
[]
)
return {
changeDesign
}
}
export default useHandleResponsible
使い方はこんな感じです。
// コンポーネントを変えたい場合
const { changeDesign: changeComponent } = useHandleResponsible({ breakPoint: 650 })
// 適用したいスタイルを変えたい場合
const { changeDesign: changeStyle } = useHandleResponsible({ breakPoint: 650 })
コンポーネントを変えたい場合
まぁこっちも適用するスタイルを変える場合で対応できるのですが、最新記事一覧のパーツとしても使っていたので それを再利用しました。
コードはこんな感じになります。
import React from 'react'
import { PostCard, PostLine } from '@/components/molecules'
// @はsrcへのパスを解決する@エイリアス
import { useHandleResponsible } from '@/hooks'
type Props = {}
const PostList: React.FC<Props> = (props) => {
// 650px以下だったらchangeComponentがtrue
const { changeDesign: changeComponent } = useHandleResponsible({ breakPoint: 650 })
if(changeComponent){
return <PostLine props />
} else {
return <PostCard props />
}
}
export default PostList
直感的にわかるので説明はいらないかと思います。
適用したいスタイルを変えたい場合
コードはこんな感じです。
import stlyes_lg from './PrevNextLarge.module.scss'
import styles_sm from './PrevNextSmall.module.scss'
import { useHandleResponsible } from 'src/components/hooks'
type Props = {
className?: string
}
type MakeProps = Props & {
styles: any
}
const MakePrevNext: React.FC<MakeProps> = ({ styles, className }) => {
return (
<div className={styles.root}>
# something
</div>
)
}
const PrevNext = React.FC<Props> = ({ className }) => {
// 画面幅が650px以下だとchangeDesignはtrue
const { changeDesign: changeStyle } = useHandleResponsible({ breakPoint: 650 })
if(changeStyle) {
return <MakePrevNext {...{ styles: styles_sm, className }} />
} else {
return <MakePrevNext {...{ styles: styles_lg, className }} />
}
}
export default PrevNext
こちらでの注意点は用意するスタイルのクラス名は完全に合わせる必要があるという点です。
それさえ守れば大丈夫です。
まとめ
レスポンシブルデザインをやっていく上で便利なテクニックを紹介しました。
- 「responsibly app」で複数端末で一気に確認できて便利。
- htmlタグのfont-sizeを
62.5%(10px)
にして、全体ではpx
ではなくrem
を使うと便利。 - コンポーネントのfont-sizeはrootはfont-sizeは
inherit
、あとは<p>
や<a>
や<li>
などのフォントサイズを100%
として%
を使って指定するのが便利。 - 画面幅に合わせてfont-sizeを変化させる事ができるmixinの
responsible-font-size
を作って置くとと便利。 -
breakpointで見た目を変えるために
useHandleResponsible
を作っておくと便利。- コンポーネントを差し替えたい時に役に立つ。
- コンポーネントに適用したいデザインを変えたい時に役に立つ。
以上です。次は、3.サードパーティーの使う上での豆知識 です。