個人サイトをつくる
何をつくるか
- ポートフォリオサイト、ブログ機能、あと実験的に色々動かせるようにしておきたい
技術スタック
- 基本的にはNext.jsベースで、カスタマイズしていく
- TypeScriptで開発する
- ESLintも入れる
- デプロイはVercel上で行う
- カスタムドメインも取りたい。AWSかGoogleかで迷い中
サイトデザイン
npmとyarnってどっちがいいんだろうか?
とりあえずTypeScript設定にしてNext.jsのデフォルトサイトをつくりましょう
ちなみに各種バージョン
npm -v
6.12.1
# Node.js
npm view react version
17.0.2
node -v
v12.13.1
# TypeScript
tsc -v
Version 4.3.2
Next.jsのプロジェクトの標準作成方法
npx create-next-app --typescript
これでプロジェクトフォルダが作成されるので、階層を一個下がって、
// npm
npm run dev
// yarn
yarn dev
で、localhostのビルドが試せる。公式のメッセージだとyarn dev
を推してくる
ESLintの導入をしよう
基本的にはこの記事の通りに作業する
2021年のプラクティスとしては下記を見るが良さそう。
.eslintrc
をJavaScripitで生成してしまったので、やり直す
npm i --save-dev eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin
# 今回の場合、Next.jsの生成時にTypeScriptを指定したので、このコマンドは不要
# npx tsc --init
npx eslint --init
npx eslint --init
で対話式に設定ができる
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / [Yes]
✔ Where does your code run? · browser, node
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · airbnb
✔ What format do you want your config file to be in? · JSON
Checking peerDependencies of eslint-config-airbnb@latest
The config that you've selected requires the following dependencies:
eslint-plugin-react@^7.21.5 @typescript-eslint/eslint-plugin@latest eslint-config-airbnb@latest eslint@^5.16.0 || ^6.8.0 || ^7.2.0 eslint-plugin-import@^2.22.1 eslint-plugin-jsx-a11y@^6.4.1 eslint-plugin-react-hooks@^4 || ^3 || ^2.3.0 || ^1.7.0 @typescript-eslint/parser@latest
✔ Would you like to install them now with npm? · No / [Yes]
(以下略)
eslint-config-prettierが非推奨になっている、という話も見たんだけど、この辺の事情が読んでもよくわからなかったので、とりあえず入れる
npm install --save-dev prettier
npm install --save-dev eslint-config-prettier
インストールの後、.eslintrc.json
にprettierを追加する
{
"extends": [
"some-other-config-you-use",
"prettier"
]
}
で、.prettierrc.js
を追加する
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}
公式サンプルそのまま。 YAMLやJSの例は↓
プロジェクトディレクトリ直下にtemp.ts
をつくって、試す
let text = "Hello, world"
$ npx eslint temp.ts
(omitted)/temp.ts
1:5 error 'text' is never reassigned. Use 'const' instead prefer-const
1:5 error 'text' is assigned a value but never used no-unused-vars
✖ 2 problems (2 errors, 0 warnings)
1 error and 0 warnings potentially fixable with the `--fix` option.
$ npx prettier temp.ts
let text = 'Hello, world'
なおググるとprettier/@typescript-eslint や prettier/react を指定している例を見るが、
eslint-config-prettier が Version 8.0.0 になって、extends に prettier/@typescript-eslint や prettier/react の設定は不要になりました。(設定するとエラーになります。)
とのこと。
VSCodeにもESLintを適用した。
VSCodeから、ESLint/Prettier - Code formatterの2つの拡張機能をインストール。
設定ファイルを書きのように書きかえる。
{
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" // フォーマッタをprettierに指定
},
"editor.formatOnSave": true, // ファイル保存時にPrettierでフォーマット
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true // ファイル保存時にESLintでフォーマット
}
}
(setting.jsonの開き方: Ctrl + ,
で設定を開いて、右上のドキュメントアイコンを押す)
これで適用完了。
Lintのワーニングがエディタ上に出るし、保存また⌘+Shift+Fでフォーマティング。
Airbnbのスタイルだと、セミコロンはつけないらしい。
どうもセミコロンを常に挿入するルールはASI(Auto Semicolon Insertion)と呼ばれて、議論の的らしい。
無事Lintは入ったが、index.tsx
が真っ赤になった
やったこととしては、
ESLintのパッケージをインストール(元々入ってたかも?)
npm i -D eslint-plugin-react
Reactのバージョンを検知するように
"settings": {
"react": {
"version": "detect"
}
},
ルールでNext.js特有のものを色々無視
"rules": {
"react/jsx-filename-extension": [
"error",
{
"extensions": [
".jsx",
".tsx"
]
}
],
"react/jsx-props-no-spreading": "off",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off"
}
上から順に
- .tsxを拡張子に使ったファイル名のワーニングを無視させる
-
Prop spreading is forbidden
に対応 -
'Component' is missing in props validation react/prop-types
に対応 - ``React' must be in scope when using JSX `に対応
あ、あとPrettierの設定がデフォルトだと4スペースで、Next.jsは2スペースだったので、2スペースにしています
Lintの設定ができたので、いよいよデザイン変更に入っていく
とりあえずまっさらにした。
ここからどう完成形になっていくか
自分のプロフィール画像、とりあえずTwitterから落としたものをソース内に埋めたが、
Twitterのソース見たら、
という形式で取得していて、取れるっちゃ取れそう。
このIDがなんなのかよくわからなかったので、採用は見送り。
プロフィール画像を円形にしたいというモチベーションがあって、400 x 400のプロフィール画像(Twitterから落とすもの)を下記で円形にくり抜けた。
.profileImage {
clip-path: circle(50%);
}
画像が四角のままだと、結局faviconにしても扱いづらいので、下記のツールで切り抜いた
Next.jsのImageの引数がわからないと思っていたら、公式ドキュメント見たらちゃんとあった
ブラウザのタブに表示される画像やfaviconも丸くしたいなと思ってたんだけど、あんまりこだわるところでもないかと思いなおした。
faviconはちゃんとやるなら、Webとモバイルで最適化しないといけないっぽい
React/CSSのコンポーネント分割が難しいな。
キレイに設計するには、経験がないと厳しそう
ヘッダ部分は実装できた。
いよいよプロフィール部分のコンテンツに入っていく
SVGファイルのimport指定で手こずった
↓ここのsvgファイルをそのまま入れようとしたら、相対パス指定が全然通らなくてあれこれ試してしまった。
結論は、拡張子を.svgにしてたのがダメだった。
あくまで.tsx
ファイルとして扱わないと、export文が動かない。
SVGが思ったようにスケーリングできない
とりあえず粗々でできた。
明日はCSSでレイアウト整えてみよう
CSSを使うと、縦書きも簡単に実現できる
CSSのデザインはFlexboxが中心になる。
チートシートが便利
デバッグ術。
どの範囲が操作対象なのかわからなくなったとき
.anyElement {
background-color: antiquewhite; // 背景色を指定する
border: 1px solid red; // 外枠を指定する
}
CSSで要素Aかつ要素Bを指定する方法。
ホワイトスペースで区切る
.a .b {
}
ちなみにカンマだとOR条件になる
CSSセレクタの、クラス指定とHTML要素指定の違いをちゃんと理解してなかった
<div className={styles.contents}>
<h1>Career</h1>
</div>
この要素にCSSを効かせたいときは、
.contents h1 {
text-decoration: underline;
}
こうする。
h1(最上位見出し)は、HTML要素なので、.はつけてはならない。
padding: 2rem 2rem 2rem 2rem;
こんな書き方をしていたが、
padding: 2rem
これで四隅指定できているらしい
next/linkは外部アドレスを指定する場合は特に何もしない(というかアイコンが変わらない気がする)ので、普通に<a href></a>
を指定した
Flexboxの外枠をぼかしたいと思ったら、box-shadow:
というCSS属性があった
Flexbox内のテキストが思ったような配置にならなくて悩んでいたら、pタグにデフォルトマージンがあるらしい
(Next.jsは何もしてなくて、たぶんブラウザの仕様……?)
.workContent p {
/* line-height: 0px; */
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
無効にしたければこれでよし
flexboxの子要素を横スクロールにしたいなという気持ちになってきた
↑これをやってみたら、ページ全体が横に広がってしまった……
これ、こだわり出したら無限に時間溶けるなあ……
Qiitaのfavicon、公式見たら配ってたので、PNG→SVG変換をかけたら、埋めこむことができた。
ただ緑色のままになってしまったので、そこはCSSで無理矢理黒くすることにした。
表を作っていく
マウス乗せたときにリンクがある文字に対して下線が引かれて欲しかったんだけど、作動しなかったのは、どうも↓みたいなCSSがないせいみたい
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
globals.css
に下記を入れた
a:hover,
a:focus,
a:active {
text-decoration: underline;
}
VSCodeのduplicateがないの不便なので、入れた
notion-blogからパクった、ここのところでpropsにParameter 'props' implicitly has an 'any' type.
というワーニングが出てしまい、
Vercelにデプロイできない。
const Github = (props) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" {...props}>
// 略
</svg>
)
export default Github
export default function OtherSites() {
return (
<div className={otherSiteStyles.links}>
{otherSites.map(({ Comp, link, alt }) => {
return (
<a key={link} href={link} aria-label={alt}>
<Comp height={32} />
</a>
)
})}
</div>
)
}
使っているところ見たら、heightを渡してるだけなので、これもう消して直書きしてもいいな、とちょっと思ったけれど、
再利用性考えるとこっちで指定する方がまっとうか、と思いなおした。
ちゃんとpropsに型を指定しよう。
(やり方わからないけれど)
type Props = {
height: number
}
const Github = (props: Props) => (
// 略
こんな感じで指定してやった。
アクセスレベルどうなってるんだ?
何もアクセス修飾子つけてないけれど、挙動を見るとファイル内までしかアクセスできないっぽい
調べてもよくわからなかったが、一旦まあいいか。
一旦デプロイした
スマホで起動確認したら残念なことになった。。。
結局二箇所の対応で済んだ。
contents -> main -> cardsRowの順でネストしている
@media (max-width: 600px) {
.contents {
width: 100%;
margin: 0.5rem;
padding: 0.5rem;
}
.main {
width: 100%;
}
.cardsRow {
max-width: 100rem;
}
}
@media (max-width: 600px) {
.profile {
flex-direction: column;
}
.right {
align-items: center;
}
}
プロフィール部分がスマホの横幅だとどうしても狭かったので、PCと表示を変えることにした。
(flex-direction: column;
のところ)
全体的に要素が収まってなかったので、最初shrinkさせるプロパティをガチャガチャ試したが、
問題はデバイスの横幅以上にFlexboxが領域を取ってしまうことで、特にテキストの量に対して領域をとっているっぽかった。
折り返す指定が効かなくて、なんかそもそもそういうことじゃないっぽかった。
max-widthを指定してみたら制御できたので、これを採用した。
書いてみて思ったけど、なんで上手くいってるのか全然理解できてないな。
ただこれだけじゃPC上ではキレイに出たが、モバイル端末では左にズレて表示された。
これもよくわからなかったが、親Flexboxから順にwidth: 100%;
を指定していったら解消。
上手く調整できていない? これも説明しろと言われたらできない
iPhoneの横向きの画面だと若干左がハミ出るな……
PCのブラウザでも横幅を縮小すると、600px - 900pxぐらいのレンジで表示がハミ出る。
Flexboxを上手く制御できてないなあ
ワークアラウンドで、900px以下のときにmax-widthの調整入れた
プロフィールページ完成!
あとはカスタムドメインとって、ブログ機能をつくりたい。
それは来週。
ブログ機能はどこまでこだわるかもやりながら考えるかな。
別にMarkdownファイル置いて公開するだけでも全然OKだと思うので……