😸

HTML, CSS, Vanilla JSでポートフォリオサイト

2024/05/22に公開

はじめに

未経験からエンジニアを目指して、現在Udemyで以下のブートキャンプコースを受講しています。

https://www.udemy.com/course/the-complete-web-development-bootcamp

HTML, CSS, JSのセクションを終えた(18セクション/全44セクション)ので、ここまでの知識を定着させるために一度何か作りたいなと思い、この機会にずっと作りたいと思っていたポートフォリオサイトをつくることにしました。出来上がったサイトはこちら(Github Pagesでホストしています)↓

https://folio.haru0u0.com/

pc mobile

お気に入りの点やこだわった点について、デザイン編(デザイン決定プロセス)と、コーディング編(言語切り替え、Spotify再生状況の表示、アクセシビリティチェックと、Git/Githubの使い方について)に分けて記載します。

デザイン編

実装に入る前に、要件整理 → ブレスト → より具体的な要件整理 → モックアップ作成 という手順でデザインを決定しました。

1. 要件整理

まずは学習教材として、ポートフォリオとして、それぞれの視点から要件を整理しました。
※ものすごく読みにくい表ですみません(特にスマホだと)。流し見でお願いいたします、、、。

学習教材として ポートフォリオとして
なぜつくるのか(概要) HTML,CSS,JSの基礎固め 人柄、考え方、職業上の姿勢など、履歴書で伝わらない部分を伝えるため(not 職学歴など履歴書に含む情報)
なぜつくるのか(詳細) いままで開発した際には「作る」ことだけが目的だったので雰囲気で書いていたが、エンジニアへの転職を考えるにあたり、きちんと基礎理解を固めたいと思った。今回は「HTML,CSS,JSを理解する/身に着ける」を目的として、そのための「手段」として作る 具体的な伝えたいこと:【職業上の姿勢】責任感がある / 誠実【人柄】パッと見はバシバシよりはポワポワしている / まあまあ元気 / 人が好き【仕事とプライベート】趣味はあって、仕事外でもそれなりに楽しく暮らしている(=仕事でのストレス対応可)/ XXXに関心がある(=これらに関連する仕事で最高パフォーマンスがだせる)/ 同じ趣味関心があったらお話ししましょう(アイスブレイクの材料の提供)
誰に向けてつくるのか 自分 Primary: イギリスのHiring Manager, Secondary: 日本の採用担当者[1]
上記を充足するために設定される、本サイトへの要件 現在の私にとって、HTML, CSS, JSの勉強になるちょうどよい難易度 / フレームワークやライブラリを使用せずに素のHTML, CSS, JSでつくる(いろいろなフレームワークやライブラリを理解するために、まずはオリジナルへの理解を深める) 【見た目】キリっとしているよりはポワっとした感じで(自分の人間的な雰囲気を伝えるため)、手触り感?があって(人を遠ざけるのではなく人に近い感じを感じさせたい)、誠実で信頼感のある感じ(職業上の姿勢を伝えるため)【中身】趣味や関心など、ソフトな部分についての記述を含めたい

2. 要件を頭に置きながら、具体的なデザインのブレスト

①いろんなポートフォリオサイトをみたり、②デザイントレンドを調べたりして、具体的にどんなデザインがいいかブレストしました。

2.1. いろんなポートフォリオサイトをみる

いろいろなポートフォリオサイトをみて、いいなと思うデザインや要素を(逆にこれはnot for meだなという要素やデザインも)収集しました。ポートフォリオ探しは、ふつうにgoogle検索したり、以下のようなキュレートサイトをみたりしました。
https://www.bestfolios.com/portfolios

特にお気に入りで、参考にさせていただいたポートフォリオサイト:

  • https://nevflynn.com/ | Nev Flynnさん
    • bentoグリッドがシンプルでおしゃれ。今聴いてる曲がかわいい。
  • https://www.seanhalpin.xyz/ | Seán Halpinさん
    • フォントの使い方がおしゃれ。背景のもやもやグラデが可愛い。とってもおしゃれな緑だ!これもよくみるとbentoグリッド。これも今聴いてる曲がかわいい。おしゃれだとにかく。
  • https://www.ayushwanjari.com/ | Ayush Wanjariさん
    • 手触り感があってかわいい。優しい人なんだなあ~と伝わってくる感じがいい。シンプルなのに遊び心がある感じがいい!
  • https://theeugene.com/ | Eugene Lazebnyさん
    • シンプルだから、プロジェクト達が強調されてていい。各プロジェクトのモーダルのなかみのデザインもすき。各プロジェクトのイメージに動きがあると、よりプロジェクトのイメージが湧きやすくていい。
  • https://www.dejan.works/ | Dejan Markovicさん
    • クリーンでいい。本人の写真がとても素敵でインパクトある。写真のクオリティと、写真のテイストがサイト全体のテイストとマッチしていることって、とっても大事なんだなと感じた。こちらもすごく誠実な人柄が伝わってくる。

2.2. デザイントレンド/色について調べる

以下の理由から、ビジュアルについてはトレンドに乗ろう(?)と決め、「2024 design trend」などのワードでgoogle検索して、ふんふんこれがいいかな~あれがいいかな~とブレストしました。

  • 私が視覚的に魅力的(?visually attractive)なものをいちから生み出すのが苦手なので、トレンドデザインに乗った方が視覚的に魅力的なもの=読み手を引き付けるものを作れる可能性が高い
  • トレンドデザイン=みんな見慣れているデザイン なので、読み手に優しい
  • トレンドデザイン=今後も仕事でつくることがあるかもしれないデザイン なので、学習教材として適している

色については、色が与える印象を調べて、ふんふんこれがいいかな~あれがいいかな~とブレストしました。

3. ブレストをもとに、具体的なデザイン要件整理

3.1. デザイン

【検討した候補】

  • bento grid
    • CSSの勉強にベストすぎる。
    • レスポンシブ対応も、デザイン面であまり考えなくていいから気軽にできそう。
    • 日本のものが評価されているのはいつだってうれしいよね~。
  • レトロ系 (Y2K, neobrutalism etc.)
    • 超かわいくてタイプだけど、professional感、誠実感を出しにくそう
    • 暖かい手触り感もない
    • 自身のブログですでに採用したので、表現の幅を広げるための学習教材として不向き
  • スクロールで遊ぶ系 (Horizontal scrolling, Parallax scrolling etc.)
    • スムーズに動けばかっこいいのだと思うのだが、自分がユーザとしてあまり好きではない(かっこいいスクロール表現をしようとして変な動きになっているサイトが多い気がする=今の私にはこれをきちんと動かすのは難易度が高いのかも)
    • ダイナミック感が私のイメージには合わない
  • Glassmorphism
    • 「透明感」はprofessionalな文脈に合うし、いいですね
    • ちょっとド定番すぎて、ポートフォリオに採用するのはなんか若干照れる(?)
    • やさしい感じ、近い感じ、私の出したい手触り感のある感じ(texture感はあるけど冷たそう、私は暖かいtexture感がだしたい!)は出にくいかも
  • Neumorphism
    • 手触り感はいい。私が求めているのはこういう暖かい触れる感じ!
    • 過去の一発屋って感じがする(ごめんなさい)(glassmorphismも古いけど彼は一発屋ではない)
    • accessibleにするには色の調整が難しそうなイメージ(今の私には難易度が高い)。
  • Claymorphism
    • かわいい(遊び心を感じる)、あたたかい、やさいしい、触れそう、いい!
    • Neumorphismに似てるけど、サイト背景色と各エレメントの背景色ははっきりコントラスト持たせられるので、accessiblityの懸念も低そう。一発屋感あんまない(個人の感想です)。ド定番過ぎないのでトレンドだけどある程度個性出る(個人の感想です)。

【決定事項】

  • bentoグリッド + claymorphism をベースにする
    • bentoグリッド:CSSの勉強にちょうどいいし、proud to be Japaneseなので
    • Claymorphism:私の伝えたい私のイメージにちょうどよいと思ったので

3.2. いろ

  • メインカラーはなにいろにする?
    • 以下のような各色が与える印象についてのweb記事をいくつか読み、与えたい印象を基にまずはオレンジか青にしぼりました。claymorphismの丸々とした質感で遊び心がでるので、色はすこし落ち着いた感じにすることでバランスが取れる(claymorphism+オレンジだと子どもっぽくなりすぎる?)と思い、青にしようと決めました。

赤: 危険、重要、愛 - 赤は活力に関わる色で、見た人の心拍数や代謝を向上させることが知られています。最も強調したい箇所に注意を向けるのに適しています。
オレンジ: 元気、楽観、愉快 - オレンジはポジティブな感情を与えます。また、商品の割安感とも関連があり、e コマースサイトでお買い得商品を目立たせるのに適しています。
黄: 幸福、注意、暖かさ - 黄は明るい性質を表します。また、黒と組み合わせると、素早く人の目を引きつけます。たとえばタクシーの色としてよく使われます。
緑: 成長、成功、自然 - 緑は自然由来であることを表すのに最適です。その他には、UI でユーザーに成功を伝える印としても一般的です。
青: 信頼、快適、平穏 - 青はくつろぎと心地よさの色です。顧客に信頼できる企業という印象を与えたいブランドに人気です。
紫: 豪華、創造、叡智 - 紫は気高さや高級さを表したい場面でよく使われます。
黒: 力、洗練、神秘 - 多くのブランドは黒をテキストとアクセントだけに使用していますが、ファッションサイトなどでは高級感を出すためにメインの色として選択する場合があります。
白: 清潔、健康、純真 - 白は健康や清潔感を連想させます。医療やテクノロジーの安全性を伝える色として選ばれるのは、その典型的な例です。

色彩理論の基本と効果的な色の選び方 | アドビ UX 道場 #UXDojo

  • メインカラーが決まったけど、全体的な色のバランスはどうする?
    • DribbleやFigmaなどでClaymorphismのデザインを検索して眺めてみたところ、同じ色の薄い濃いで2色+白の3色でまとめているものが多かったので、薄い青色(全体背景)、濃い青色(ボタン)、白(各グリッドの背景)に決めました。

dribbleでのclaymorphismの検索結果の一部
Dribbleでのclaymorphismの検索結果より

フォント

  • 本文:sans-serif
    • 威厳があるというよりは身近な感じ、暖かな感じを感じさせたかったので、全体の大きな部分をしめる本文のフォントにはsans-serifを採用しようと思いました。(→ コーディング後にいろいろ試してみて、とりあえずNunito Sans, sans-serifになりました。)
  • 見出し:serif
    • 上記のような親しみやすさと同時に、professionalな場における信頼感を出したかったのと、ビジュアル上の引き締め(メリハリ?)もほしいなと思ったため、見出しはserifにしようと思いました。(→ コーディング後にいろいろ試してみて、とりあえずBioRhyme, serifになりました。)

フォントの心理学(Sans-Serifはfriendlyな印象、など)
Clover Creative Group: Fonts and Their Psychological Associations: Unveiling the Hidden Meanings

コンテンツ

いろいろなポートフォリオを拝見したことで、一般的にポートフォリオに求められるコンテンツがわかってきました。世のポートフォリオサイト内での出現頻度と、序盤で整理した自分のポートフォリオで伝えたいことを考慮し、自分のポートフォリオに載せるコンテンツを決定しました。

コンテンツ 私がみたポートフォリオ内での出現頻度感覚値 自身のポートフォリオに載せるか
自分のフルネーム 100%
これまでのプロジェクトについて(タイトル、役割、期間、種別(Personal/Work, Individual/Team)、使用技術、機能概要、デザインプロセス、ビジュアル情報(画像、動画、gif)、githubリンク) 100%
連絡先(各種SNSアカウントリンクとメールアドレス) 100%
仕事上のモットーみたいな語り 80%
居住国/都市 60%
自分の写真 60%
今聴いている音楽/よく聴く音楽を自分のSpotifyから引っ張ってくるやつ 30%
趣味について 30%
学歴職歴について 20% ×
△と×の理由

自分のフルネーム:こわいので一旦フルネームはやめとく
仕事上のモットーみたいな語り:文章力が問われるので今すぐではないかも。のちのち足したい
自分の写真:こわいので一旦顔はやめとく。自分の雰囲気が伝わるような後ろ姿にする
学歴職歴について:履歴書でわかる

4. figmaでざっくりとモックアップ作成

3でまとめた要件を基に、実際のデザインを描き起こしていきました。サイトの目的上、pcで見る方が圧倒的に多いと思うので、デスクトップファーストでデザインしました(開発も)。実際にコーディングをすすめていく中で、どんどんああしようこうしようが出てくるだろうなと思っていたので、「まあ大体こんな感じだな~」レベルで描き起こせた段階で、コーディングに移行しました。実際現在リリースされているサイトは↓とは異なる点も多々ありつつ、大まかなエッセンスはこのままな感じになっています。

この進め方をするのは、現時点でfigmaの扱いがそこまで上手ではないので、figma上で思っていることを表現するのに無駄に時間がかかってしまってもったいないという理由もあります。練習します。

figmaで描いたモックアップの画像

コーディング編

上記モックアップをもとに、粛々とコーディングしていきました。ここでは、言語切り替えと、Spotify再生状況表示の実装、アクセシビリティチェック、Git/Githubの使い方の4点について振り返っておこうと思います。

言語切り替え

転職活動の都合上、日本語英語どちらでも読めることが必須だったので、言語切り替え機能を実装しました。

プロジェクトの紹介文がはじめは英語で表示されているが、トグル操作で日本語になる

jsonに格納したテキストを、選択されている言語に合わせて出しわけする形で実装しています。以下はトップページの言語切り替えの様子です。

  1. ほぼもぬけの殻のHTMLをつくる(jsでテキストを差し込むのでHTMLにテキストはほぼ記載されていない状態)。文字が入る部分には、2のテキスト管理用jsonと対応するidをつけておく。
index.html
index.html
<div role="main" id="main" class="grid-container">
  <!--------------中略-------------------->
        <div id="libft" class="grid-item one-by-one work withtext">
            <button type="button" class="open" aria-label="詳細をみる, read more about the project">
                <i class="fa-solid fa-arrow-up-right-from-square fa-lg"></i>
            </button>
            <div class="content visually-hidden">
                <h2>Libft</h2>
                <p class="tags">C</p>
                <p class="overview"></p>
            </div>
        </div>
        <div id="folio" class="grid-item one-by-one work withtext">
            <button type="button" class="open" aria-label="詳細をみる, read more about the project">
                <i class="fa-solid fa-arrow-up-right-from-square fa-lg"></i>
            </button>
            <div class="content visually-hidden">
                <h2>Portfolio</h2>
                <p class="tags">HTML / CSS / Vanilla JS</p>
                <p class="overview"></p>
            </div>
        </div>
        <!--------------中略-------------------->
  1. HTML要素ごとに、対応するHTML要素を識別できるようなid、英語テキスト、日本語テキストを記載したjsonファイルを作成する。

※jsonのidと、HTMLのidが対応しています。

content.json
content.json
{
 "overviews": [
  { //中略
    "id":"libft",
    "en": "My own C library replicating frequently used C standard library functions",
    "jp": "C言語の標準関数を複製したCライブラリ"
  }, //中略
  {
    "id":"folio",
    "en": "Yes, this is what you are seeing now🥰 Thank you for visiting!",
    "jp": "本ポートフォリオサイト。訪問ありがとうございます!"
  }
], //中略
  1. jsでjsonファイルを読んでおき、画面ロード時と言語選択トグル操作時に、選択された言語(ロード時はデフォルトのen)のテキストを、対応するHTML要素に差し込む
index.js
index.js
var selected_lang = "en";
var response;
var contents;

//サイト読み込み時にjsonデータを読み、setLang関数を呼ぶ
window.addEventListener("load", async function () {
  response = await fetch("content.json");
  contents = await response.json();
  setLang(selected_lang);
});

//言語変更トグルが操作されるたびに、setLang関数を呼ぶ
document.querySelector("input").addEventListener("change", function () {
  if (selected_lang == "jp") {
    selected_lang = "en";
  } else {
    selected_lang = "jp";
  }
  setLang(selected_lang);
});

//選択された言語のテキストを表示する関数
function setLang(selected_lang) {
  var toggle_lang = document.querySelector("#selected_lang");
  var grid_withtext = document.querySelectorAll(".withtext");
  var i_grid_withtext = 0;

  //トグル上に表示される選択中言語の変更
  toggle_lang.innerHTML = selected_lang.toUpperCase();

  //表示テキストの変更
  while (i_grid_withtext < grid_withtext.length) {
    var title = grid_withtext[i_grid_withtext].getAttribute("id");
    var i_json_content = 0;
    while (i_json_content < grid_withtext.length) {
      if (title == contents.overviews[i_json_content].id) {
        grid_withtext[i_grid_withtext].querySelector(".overview").innerHTML =
          contents.overviews[i_grid_withtext][selected_lang];
      }
      i_json_content++;
    }
    i_grid_withtext++;
  }
}

Spotify再生状況の表示

大変かな~?と若干怯えていましたが、Spotify APIのドキュメントがわかりやすく、特に困らずササっと思い通りのものが実装できました。

サイト主がspotifyで最もよく聴いている曲が表示されている部分のスクリーンショット

Spotifyでは、各ユーザの再生履歴をもとに勝手に生成してくれるプレイリストがいくつかあり、ここではその中のひとつのOn Repeatという最近よく聴いている曲をまとめてくれているプレイリストの上位6曲を表示するようにしています。

On Repeat – The music you’ve been streaming nonstop
Get even more of your current favorite tracks with On Repeat. This playlist helps you keep track of what you’ve been playing most over the past 30 days. On Repeat auto-updates, so you can be sure everything on there is the most up-to-date account of what you’ve been playing nonstop. It’s a great combination of all the music you love, no matter what artist or genre, so each time you tap play, it will sound a little different.

Spotify: Introducing Two New Personalized Playlists: On Repeat and Repeat Rewind

以下の2ページに沿ってプレイリストの情報を取得し、表示しているだけです。
ただ、クレデンシャルが隠せないのが嫌なのですが、これはフロントエンドだけのページではどうしようもないのでしょうか...?とりあえず別にSpotifyなのでいいやと思ってこのまま公開していますが、より良き方法を調べてみようと思います(調べたけど現時点ではわからなかった)。

https://developer.spotify.com/documentation/web-api/tutorials/getting-started

https://developer.spotify.com/documentation/web-api/reference/get-playlist

一応jsを置いておきます。
spotify.js
const xhr = new XMLHttpRequest();
const xhr2 = new XMLHttpRequest();

window.addEventListener("load", async function () {
  xhr.open("POST", "https://accounts.spotify.com/api/token");
  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  xhr.send(
    "grant_type=client_credentials&client_id=XXXXXXXXXXXXXXXXXXXXX&client_secret=XXXXXXXXXXXXXXXXXXXXX"
  );
  xhr.responseType = "json";
});

xhr.onload = () => {
  if (xhr.readyState == 4 && xhr.status == 200) {
    //get access token
    const res_initial = xhr.response;
    //request on-repeat playlist with the access token
    xhr2.open(
      "GET",
      "https://api.spotify.com/v1/playlists/XXXXXXXXXXXXXXXXXXXXX?fields=tracks.items%28track%28album.images.url%2C+artists.name%2C+name%2Cpreview_url%29"
    );
    xhr2.setRequestHeader(
      "Authorization",
      "Bearer " + res_initial.access_token
    );
    xhr2.send();
    xhr2.responseType = "json";
    xhr2.onload = () => {
      if (xhr2.readyState == 4 && xhr2.status == 200) {
        display(xhr2.response);
      } else {
        console.log(`Error: ${xhr2.status}`);
      }
    };
  } else {
    console.log(`Error: ${xhr.status}`);
  }
};

function display(data) {
  //text
  var songs = document.querySelectorAll(".song");
  var song_indx = 0;

  while (song_indx < songs.length) {
    //name
    var song_name = songs[song_indx].querySelector(".song-name");
    song_name.innerHTML = data.tracks.items[song_indx].track.name;
    //artist
    var artist_name = songs[song_indx].querySelector(".artist-name");
    artist_name.innerHTML = data.tracks.items[song_indx].track.artists[0].name;
    //thumnail
    var thm = songs[song_indx].querySelector("img");
    thm.setAttribute(
      "src",
      data.tracks.items[song_indx].track.album.images[0].url
    );
    //button
    var music_button = songs[song_indx].querySelector(".music-button");
    var mp3;
    music_button.addEventListener("click", function () {
      var play_button = this.querySelector(".fa-circle-play");
      var pause_button = this.querySelector(".fa-circle-pause");

      if (play_button.classList.contains("hidden")) {
        mp3.pause();
      } else {
        mp3 = new Audio(
          data.tracks.items[
            Array.from(songs).indexOf(this.closest(".song"))
          ].track.preview_url
        );
        mp3.play();
      }
      play_button.classList.toggle("hidden");
      pause_button.classList.toggle("hidden");
    });
    song_indx++;
  }

アクセシビリティチェック

だいたいコーディングが完了したころに、アクセシビリティチェック拡張機能を走らせ、指摘された改善点や、スクリーンリーダが想定通りに読み上げてくれなかった箇所などを修正しました。

以下の拡張機能はローカル環境でも動き、いろいろな機能があったので使いやすかったです。
https://chromewebstore.google.com/detail/silktide-accessibility-ch/mpobacholfblmnpnfbiomjkecoojakah

Skilltide Accessibility Checkerの利用中スクリーンショット

この拡張機能でスクリーンリーダーを試してみたところ、マウスホバーするまでdisplay:none;で隠していた項目たち(各プロジェクトのタイトルや説明文)が読み上げてもらえず、焦りました。display:none;だとスクリーンリーダーにも飛ばされてしまうことを知らなかったので、勉強になりました。display:none;にしていた箇所は、以下のスニペットをお借りし、見た目のみ隠すように修正しました。
https://qiita.com/randy39/items/fca820d500dfe9ec1a52

このほか、aria-labelを足したり、role="main"を足したり、色のコントラストをより良く改善したりしました。

lighthouseは本番環境でしか動作しない?ようだったので、上記拡張機能に沿って修正した後の最終確認に利用しました。accessibilityは100でしたが、他にいまいちな項目があるようなので、のちのち対応したいです。
https://chromewebstore.google.com/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk

lighthouseの結果 accessibilityは100点

Git/Githubの使い方

Git/Githubをいままでも使っていたのですが、ひたすらmasterブランチにpushするただのバックアップ置き場としての使い方しかできていなかったので、以下ふたつの記事を参考にしながら、Issueを立てたり、ブランチをわけたり、コメントをある程度のルールに沿って書いたりしてみました。これらの利点がわかったのでこれからも継続しつつ、実際なにをしているのかの理解を深めつつ、もう少し慣れてきたら自分に合うIssueやコメントの書き方などを探ってみようと思います。

https://qiita.com/braveryk7/items/5208263cd06a8878f0c2#masterブランチ1本からの進歩

https://qiita.com/itosho/items/9565c6ad2ffc24c09364

おわりです

お読みいただいた方ありがとうございました。まだまだ以下のような残件があるので、引き続き改修していきたいと思います。

残件

*優先順位順

  • それぞれのプロジェクトの紹介モーダルをつくる(現状githubに飛ばしているボタンを、サイト内でそれぞれのプロジェクト紹介のモーダルを開くようにする)
  • 日本語版をきれいにする。英語primaryでつくった影響で、現状の日本語版は、内容もフォントも、怪しい外国のなりすましサイトみたいになってしまっている、気がする。
    • 日本語用フォントの選定、適用
    • 日本語記述内容の書き直し
  • アクセシビリティ改善
    • 今はaria-labelに2言語書いているが、言語切り替えに応じて、JSで差し替えたほうがいい?
    • そもそもaria-labelに頼りすぎないべく、アイコンボタンなどを変更する
  • 自己紹介にもうちょっと写真とかいれたいな~。
  • 英語版もフォントがしっくりきていないので、引き続き探検する

参考

https://chocolat5.com/tips/loop-image-animation/

https://qiita.com/feketerigo222/items/c9acdd7c3493ed6c6ddd

https://hype4.academy/tools/claymorphism-generator

脚注
  1. イギリス, 日本での就職活動のどちらも同程度検討しているのですが、諸々転職体験談等を読んだ結果、日本の未経験エンジニア就職活動でいう「ポートフォリオ」は、自分についてまとめたサイトではなくて、自分の学習成果をつめこんだひとつのwebアプリであり、前者の意のポートフォリオ(=今回作成しているもの)はそこまで重視されないのかもしれない?と感じたので、このポートフォリオサイトのメインターゲットはイギリスのHiring Managerとしました。 ↩︎

GitHubで編集を提案

Discussion