1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React + TypeScript】文系出身者がこだわった、なめらかに動くポートフォリオサイトの作り方

Posted at

【React + TypeScript】文系出身者がこだわった、なめらかに動くポートフォリオサイトの作り方

こんにちは!文系出身でプログラミング歴2年の駆け出しエンジニアです。この記事では、自分の技術力のアウトプットとして作成したポートフォリオサイトについて、特にこだわったデザインや滑らかなアニメーションの実装方法を中心に解説します。

はじめに:なぜポートフォリオサイトを作ったのか

法学部出身の私がプログラミングを始めたのは2年前。独学と仕事を通じて技術を身につけてきましたが、「自分の技術力を一度きちんとアウトプットしたい」という思いが強くなり、ポートフォリオサイト制作に挑戦しました。

特に重視したのは以下の点です:

  • モダンな技術スタックの採用
  • ユーザーが心地よいと感じるスムーズな動き
  • レスポンシブ対応
  • 直感的なUI/UX

技術スタック

  • フロントエンド
    • React 19.0.0
    • TypeScript 5.7.2
    • Vite 6.2.1
  • スタイリング
    • CSS Variables(カスタムプロパティ)
  • デプロイ
    • Firebase Hosting

サイト構成

基本的なセクション構成は以下の通りです:

  1. ヒーローセクション
  2. 自己紹介セクション
  3. スキルセクション
  4. プロジェクトセクション
  5. コンタクトセクション
  6. フッター

こだわり①:CSS変数を活用した一貫したデザインシステム

デザインの一貫性を保つために、CSS変数(カスタムプロパティ)を活用しました。これにより、色やサイズ、アニメーション速度などを簡単に統一でき、変更も容易になります。

:root {
  /* メインカラーパレット */
  --primary-color: #5e60ce;
  --primary-light: #7a7de3;
  --primary-dark: #4c4da3;
  --secondary-color: #64dfdf;
  --accent-color: #ff7eee;

  /* テキストカラー */
  --text-primary: #2b2c34;
  --text-secondary: #555;
  --text-light: #777;
  --text-on-dark: #f8f9fa;

  /* 背景カラー */
  --bg-primary: #ffffff;
  --bg-secondary: #f8f9fa;
  --bg-tertiary: #edf2f7;
  --bg-dark: #2b2c34;

  /* トランジション */
  --transition-fast: all 0.2s ease;
  --transition-normal: all 0.3s ease;
  --transition-slow: all 0.5s ease;
  
  /* 他にもスペーシングやボーダーラディウスなど */
}

これらの変数を使うことで、例えばボタンのスタイルを以下のように簡潔に定義できます:

.btn {
  padding: var(--space-3) var(--space-6);
  border-radius: var(--border-radius-full);
  transition: var(--transition-normal);
  /* 他のスタイル */
}

こだわり②:スムーズなスクロールと遷移アニメーション

ユーザーエクスペリエンスで最も重視したのが、サイト内の動きの滑らかさです。

スムーズスクロール

HTML全体にスムーズスクロールを適用:

html {
  scroll-behavior: smooth;
}

アニメーションのタイミング調整

特に気をつけたのは、アニメーションのタイミングとイージング(加速・減速)の調整です。例えば、ヒーローセクションの要素をフェードインさせる際には:

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.hero-text {
  animation: fadeIn 0.8s ease forwards;
}

.hero-image-container {
  animation: fadeIn 1s ease 0.3s forwards;
  opacity: 0;
}

ここでポイントなのは、テキストを先に表示し(0.8秒)、少し遅れて(0.3秒後)画像を表示する演出です。こうすることで、ユーザーの視線の流れをコントロールし、自然な印象を与えることができます。

浮動するカードエフェクト

ヒーローセクションでは「経験年数」や「プロジェクト数」を示すカードが浮遊するアニメーションを実装しました:

.floating-card {
  position: absolute;
  display: flex;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--border-radius-md);
  background-color: var(--bg-primary);
  box-shadow: var(--box-shadow-md);
  z-index: 2;
}

.card-experience {
  top: 10%;
  left: -15%;
  animation: floatCard 3s ease-in-out infinite;
}

.card-projects {
  bottom: 15%;
  right: -10%;
  animation: floatCard 3s ease-in-out 1.5s infinite;
}

@keyframes floatCard {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-10px);
  }
}

このように時間差を付けて浮遊させることで、自然で有機的な動きを演出しています。

こだわり③:インタラクティブな要素

フィルタリング機能

プロジェクトセクションでは、カテゴリ別にフィルタリングできる機能を実装しました:

// Projects.tsxの一部抜粋
const [filter, setFilter] = useState<FilterType>('all');
const filteredProjects = filter === 'all' 
  ? projects 
  : projects.filter(project => project.category === filter);

// フィルターボタン
<div className="filter-buttons">
  <button 
    className={`filter-btn ${filter === 'all' ? 'active' : ''}`}
    onClick={() => setFilter('all')}
  >
    すべて
    <span className="filter-count">{projects.length}</span>
  </button>
  {/* 他のフィルターボタン */}
</div>

// フィルター適用後のプロジェクト表示
<div className="projects-grid">
  {filteredProjects.map(project => (
    <div className="project-card" key={project.id}>
      {/* プロジェクトカードの内容 */}
    </div>
  ))}
</div>

モーダル表示

プロジェクトカードをクリックすると詳細がモーダルで表示される機能も実装しました:

const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [modalOpen, setModalOpen] = useState<boolean>(false);

const handleProjectClick = (project: Project) => {
  setSelectedProject(project);
  setModalOpen(true);
  document.body.style.overflow = 'hidden'; // スクロール防止
};

const closeModal = () => {
  setModalOpen(false);
  document.body.style.overflow = 'auto'; // スクロール復活
};

モーダルのアニメーションもこだわりました:

.project-modal {
  animation: modalFadeIn 0.3s ease;
}

@keyframes modalFadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

こだわり④:レスポンシブデザイン

様々な画面サイズに対応するため、メディアクエリを活用しました:

/* 基本レイアウト */
.hero-content {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-12);
}

/* タブレットサイズ */
@media screen and (max-width: 992px) {
  .hero-content {
    grid-template-columns: 1fr;
    text-align: center;
  }
  
  .hero-image-container {
    order: 1; /* 画像を上に */
  }
  
  .hero-text {
    order: 2; /* テキストを下に */
  }
}

/* スマートフォンサイズ */
@media screen and (max-width: 576px) {
  html {
    font-size: 12px; /* 基本フォントサイズを小さく */
  }
  
  .section-title {
    font-size: 1.8rem;
  }
  
  /* 他の調整 */
}

苦労した点と学び

State管理の最適化

特にプロジェクトセクションでは、フィルタリングとモーダル表示の状態管理を組み合わせる必要があり、初めは複雑になってしまいました。ですが、適切にステートを分割し、関数をシンプルにすることで可読性を向上させました。

CSSアニメーションのタイミング調整

複数の要素が連動するアニメーションのタイミング調整は、思った以上に時間がかかりました。デバイスによって表示が異なる場合もあり、様々な環境でテストする必要がありました。

フォームのバリデーション

コンタクトフォームのバリデーションとユーザー体験の両立に苦労しました。エラーメッセージの表示タイミングや、フォーカス時の挙動など、細かい部分まで調整しています:

// フォーム入力項目のフォーカス状態管理
const [isFocused, setIsFocused] = useState({
  name: false,
  email: false,
  subject: false,
  message: false
});

const handleFocus = (field) => {
  setIsFocused(prev => ({
    ...prev,
    [field]: true
  }));
};

const handleBlur = (field) => {
  if (!formData[field]) {
    setIsFocused(prev => ({
      ...prev,
      [field]: false
    }));
  }
};

今後の改善点

現在も継続的に改善を行っていますが、主な今後の課題は:

  1. さらなるプロジェクト追加(現在は一部仮データ)
  2. ダークモード実装
  3. 多言語対応
  4. パフォーマンス最適化

まとめ

文系出身のエンジニアとして、デザインと技術の両面からポートフォリオサイトを作り上げる過程は非常に勉強になりました。特にCSSアニメーションと状態管理の組み合わせは、見た目だけでなくユーザー体験という観点からも深く考えるきっかけになりました。

何か質問やフィードバックがあれば、コメントいただけると嬉しいです!

参考リソース

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?