← ブログ一覧に戻る

WordPressオリジナルテーマを一から自作してポートフォリオサイトを作った話

2026.07.24

フリーランスでWeb制作をしている矢野です。

今回、この技術ブログを含むポートフォリオサイト(yanoworks.com)そのものを、購入テーマやページビル ダーを使わず、WordPressのオリジナルテーマとして一から自作しました。

この記事では、「なぜ自作したか」「どう設計したか」「どこでハマったか」を、実際の試行錯誤も含めて 書いておきます。

なぜ購入テーマではなく自作したか

正直、購入テーマやページビルダーを使えば、もっと早く形にできたと思います。

それでもオリジナルテーマにこだわったのには理由があります。

1つ目は、単純に技術力を直接示せるからです。

ポートフォリオサイト自体が購入テーマだと、「このサイト、テンプレートっぽいな」と思われた時点で説 得力が落ちてしまいます。

逆に、サイト自体が自分の実装力の証明になるなら、これほど分かりやすいアピールはありません。

2つ目は、カスタム投稿タイプやカスタムフィールドを使った柔軟な設計が、既製テーマだと窮屈に感じたこ とです。

実績(work)をどう管理するか、フォームの項目をどう増やすか、といった細かい調整を自分の裁量で決め られるのは、自作ならではのメリットだと感じています。

設計の全体像

構成としては、大きく分けて2つの独自実装があります。

1つ目は、実績管理用のカスタム投稿タイプ「work」です。

クライアント名・サイトURL・タグをカスタムフィールドとして持たせ、実績一覧はこのカスタム投稿タイプ をループして表示する形です。

実績が増えても、投稿を追加するだけで一覧・詳細ページが自動的に増えていく設計です。

2つ目は、プラグインを使わない自作のお問い合わせフォームです。

admin-post.phpのフックを使い、nonceによるCSRF対策、ハニーポットによるスパム対策、wp_mail()による メール送信までを自前で実装しています。

プラグインに頼らず自作したのは、「自分で作れる」という一貫性を持たせたかったからです。

デザインで意識したこと:「AI感」を消す試行錯誤

正直、ここが一番時間をかけた部分です。

最初に組んだデザインは、実績カードに枠線・影・ブラウザ風のモックアップフレームをつけた、いわゆる 「よくあるテンプレート的な」見た目でした。

ただ自分で見返すと、生成AIが作ったようなテンプレ感が強く、何度も作り直す羽目になりました。

最終的に実績セクションは、カード型をやめて、連番とセリフ体タイトルを使った編集誌風のリスト表示へ と作り替えています。

枠や影に頼らず、タイポグラフィだけで情報の階層を作る方向です。

もう1つ判断に迷ったのが、スキルセットの見せ方です。

最初はスキルの習熟度をドットやバーで可視化する案を検討していました。

ただ、これは依頼者目線で考えたときにリスクがあると気づいたんです。

「バーが100%じゃないから、この人に頼むのはやめておこうか」と、非技術者の依頼者に余計な不安を与え てしまう可能性があるからです。

信頼構築が目的のセクションでは、定量的な自己評価よりもプレーンな一覧の方が適切だと判断し、最終的 には装飾のない連番リストに落ち着きました。

つまずいたポイント

FAQアコーディオンのアニメーションが不安定だった

開閉のアニメーションを、最初はCSSのgrid-template-rowsを0frから1frに変える方法で実装していました。

BEFORE(CSSのみで実装)

.faq-item .faq-answer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 300ms ease;
}

.faq-item[open] .faq-answer {
  grid-template-rows: 1fr;
}

ただ、閉じるときの動きが不安定だったり、特定の項目だけ挙動がおかしくなったりして、原因の切り分け に時間がかかりました。

最終的には、JavaScriptで開閉前後の実際の高さを測定し、Web Animations APIで直接アニメーションさせる方式に切り替えています。

AFTER(実測した高さをWeb Animations APIでアニメーション)

const toggleFaq = (item) => {
  const answer = item.querySelector('.faq-answer');
  const height = measureOpenHeight(answer);

  item.animate(
    [{ height: '0px' }, { height: height + 'px' }],
    { duration: 300, easing: 'ease' }
  );
};

こちらに切り替えたことで、安定した開閉動作を実現できています。

レスポンシブでCSS Gridの列数が想定より減っていた

スマートフォン表示で、2列になるはずのグリッドが1列になってしまう不具合がありました。

BEFORE(余白を考慮していなかった指定)

.flow-steps {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 2rem;
}

原因を追ったところ、親要素の.sectionと.containerの両方に左右の余白(パディング)が設定されており 、想定より表示エリアが狭くなっていたことが分かりました。

AFTER(スマホ幅では最小値と余白を縮小)

@media (max-width: 900px) {
  .flow-steps {
    grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
    gap: 1rem;
  }
}

grid-template-columns: repeat(auto-fit, minmax(150px, 1fr))のような指定は、余白を含めた実際の表示幅次第で列数が変わるため、レイアウトの入れ子構造まで含め て計算する必要がある、という学びがありました。

まとめ

購入テーマを使えばもっと早く公開できたと思いますが、自作したことで、「自分が何を作れるか」を一番 説得力のある形で示せたと感じています。

特に、「AI感をどう消すか」「依頼者にとって不要な不安を与えない見せ方は何か」といった判断は、実際 に手を動かして初めて気づけたことでした。

これから副業や独立でWeb制作を始めようとしている方の参考になれば嬉しいです。

次回は、このサイトのローカル開発環境をDocker(wp-env)で構築した話を書く予定です。