Closed76

個人サイトをつくる

蔀

何をつくるか

  • ポートフォリオサイト、ブログ機能、あと実験的に色々動かせるようにしておきたい

技術スタック

  • 基本的にはNext.jsベースで、カスタマイズしていく
  • TypeScriptで開発する
  • ESLintも入れる
  • デプロイはVercel上で行う
  • カスタムドメインも取りたい。AWSかGoogleかで迷い中
蔀

npmとyarnってどっちがいいんだろうか?

蔀
  • 昔はnpmが重かったため、yarnがサードパーティーで開発された
  • サードパーティーあるあるで、いいところが公式にパクられたため、今はnpmも改善した
  • 俺の状況ならnpmで良さそう。速さにこだわりたい、チームメンバーがyarn多い、yarnを応援したい、などがあるならyarnを使ってもいい
蔀

ちなみに各種バージョン

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の導入をしよう

蔀
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]
(以下略)
蔀
npm install --save-dev prettier
npm install --save-dev eslint-config-prettier

インストールの後、.eslintrc.jsonにprettierを追加する

.eslintrc.json
{
  "extends": [
    "some-other-config-you-use",
    "prettier"
  ]
}

で、.prettierrc.jsを追加する

{
  "trailingComma": "es5",
  "tabWidth": 4,
  "semi": false,
  "singleQuote": true
}

公式サンプルそのまま。 YAMLやJSの例は↓

https://prettier.io/docs/en/configuration.html

蔀

プロジェクトディレクトリ直下にtemp.tsをつくって、試す

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 の設定は不要になりました。(設定するとエラーになります。)

とのこと。

https://blog.vivita.io/entry/2021/04/01/070000

蔀

VSCodeにもESLintを適用した。
VSCodeから、ESLint/Prettier - Code formatterの2つの拡張機能をインストール。

設定ファイルを書きのように書きかえる。

setting.json
{
    "[typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode" // フォーマッタをprettierに指定
    },
    "editor.formatOnSave": true, // ファイル保存時にPrettierでフォーマット
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true // ファイル保存時にESLintでフォーマット
    }
}

(setting.jsonの開き方: Ctrl + ,で設定を開いて、右上のドキュメントアイコンを押す)

これで適用完了。
Lintのワーニングがエディタ上に出るし、保存また⌘+Shift+Fでフォーマティング。

蔀

無事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の設定ができたので、いよいよデザイン変更に入っていく

蔀

時すでに遅しなので、↓だけ入れた

module.exports = {
  extends: [
    //...
    'plugin:@next/next/recommended',
  ],
}
蔀

とりあえずまっさらにした。
ここからどう完成形になっていくか

蔀

自分のプロフィール画像、とりあえずTwitterから落としたものをソース内に埋めたが、
Twitterのソース見たら、

https://pbs.twimg.com/profile_images/{id_1}/{id_2}.jpg

という形式で取得していて、取れるっちゃ取れそう。
このIDがなんなのかよくわからなかったので、採用は見送り。

蔀

プロフィール画像を円形にしたいというモチベーションがあって、400 x 400のプロフィール画像(Twitterから落とすもの)を下記で円形にくり抜けた。

.profileImage {
  clip-path: circle(50%);
}

https://developer.mozilla.org/ja/docs/Web/CSS/clip-path

蔀

ブラウザのタブに表示される画像やfaviconも丸くしたいなと思ってたんだけど、あんまりこだわるところでもないかと思いなおした。
faviconはちゃんとやるなら、Webとモバイルで最適化しないといけないっぽい

https://qiita.com/purpleeeee/items/cd9aca1ae735ad678355

蔀

React/CSSのコンポーネント分割が難しいな。
キレイに設計するには、経験がないと厳しそう

蔀

ヘッダ部分は実装できた。
いよいよプロフィール部分のコンテンツに入っていく

蔀

SVGが思ったようにスケーリングできない

https://developer.mozilla.org/ja/docs/Web/SVG

蔀

QiitaもZennと同じところから落として入れたが、ちょっとダサい……

蔀

とりあえず粗々でできた。
明日はCSSでレイアウト整えてみよう

蔀

CSSのデザインはFlexboxが中心になる。
チートシートが便利

https://www.webcreatorbox.com/tech/css-flexbox-cheat-sheet

蔀

デバッグ術。
どの範囲が操作対象なのかわからなくなったとき

.anyElement {
  background-color: antiquewhite; // 背景色を指定する
  border: 1px solid red; // 外枠を指定する
}

蔀

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

これで四隅指定できているらしい

蔀

Flexbox内のテキストが思ったような配置にならなくて悩んでいたら、pタグにデフォルトマージンがあるらしい
(Next.jsは何もしてなくて、たぶんブラウザの仕様……?)

https://seolaboratory.jp/64888/

.workContent p {
  /* line-height: 0px; */
  margin-left: 0;
  margin-top: 0;
  margin-right: 0;
  margin-bottom: 0;
}

無効にしたければこれでよし

蔀

flexboxの子要素を横スクロールにしたいなという気持ちになってきた

https://qiita.com/_katomasa/items/b131b3cf0b78ce3933d5

↑これをやってみたら、ページ全体が横に広がってしまった……

蔀

できた。

親となるFlexboxにoverflow: scroll;を指定する。
(水平方向であれば、overflow-xでも同じ)
子の方にはflex-shrink: 0;を指定する。
min-widthでもたぶんできるとは思うけど、俺の構成だとFlexboxのネストが激しいので、水平スクロールする親の親に影響が出ていたみたい

蔀

これ、こだわり出したら無限に時間溶けるなあ……

蔀

Qiitaのfavicon、公式見たら配ってたので、PNG→SVG変換をかけたら、埋めこむことができた。
ただ緑色のままになってしまったので、そこはCSSで無理矢理黒くすることにした。

https://blitzgate.co.jp/blog/2119/

蔀

マウス乗せたときにリンクがある文字に対して下線が引かれて欲しかったんだけど、作動しなかったのは、どうも↓みたいなCSSがないせいみたい

.title a:hover,
.title a:focus,
.title a:active {
   text-decoration: underline;
}
蔀

globals.cssに下記を入れた

a:hover,
a:focus,
a:active {
  text-decoration: underline;
}
蔀

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だと思うので……

このスクラップは2021/06/18にクローズされました