iT邦幫忙

2025 iThome 鐵人賽

DAY 5
0
Modern Web

Angular、React、Vue 三框架實戰養成:從零打造高品質前端履歷網站系列 第 5

Day 5 RWD 響應式網頁設計 – 讓自我介紹頁在各裝置都好看

  • 分享至 

  • xImage
  •  

今日目標

  • 了解 RWD 的核心觀念:手機先行、流體排版、彈性圖片、斷點(breakpoints)
  • 在原本的履歷網站套上 mobile-first 的版面規劃
  • 完成 Header 導覽Hero 區塊Skills/Projects 卡片格線 的響應式排版

基礎概念(先懂再做)

1) 手機先行(Mobile-first)

  • 預設樣式寫給「最小螢幕」,之後再用 @media (min-width: …) 逐步增強到平板、桌機。
  • 好處:程式碼更精簡、效能更友善、需求自然聚焦重要內容。

2) 流體排版(Fluid layout)

  • 少用固定寬度 px,多用 百分比remchmin()max() 等相對單位。
  • 內容容器用「最大寬度 + 兩側自動置中」控制行寬。

3) 斷點(Breakpoints)

  • 沒有「標準答案」,跟著版面「何時擠壓」來放。
  • 本文採用三段:
    • base(手機)
    • @media (min-width: 768px) → 平板
    • @media (min-width: 1024px) → 桌機

4) 圖片與排版可讀性

  • 圖片預設 max-width: 100% 防止溢出。
  • 文字行寬控制在 45–75 字元最易讀(用容器寬度+字級達成)。

實作:把 Day1 的頁面做成 RWD

我們保留你 Day1 的 HTML 結構(語義化標籤),只補上 viewport 與 class 鉤子,把重點放在 CSS。

檔案結構(和昨天的差不多):

day5-rwd/
├─ index.html
└─ styles/
   └─ style.css

1) index.html(重點:加入 viewport 與容器類別)

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Chiayu · 前端工程師 · 履歷網站</title>
  <link rel="stylesheet" href="styles/style.css" />
</head>
<body>
  <header class="site-header">
    <div class="container header-inner">
      <a class="brand" href="#home">Chiayu</a>
      <nav class="site-nav" aria-label="主選單">
        <button id="nav-toggle" class="nav-toggle" aria-expanded="false" aria-controls="nav-list">選單</button>
        <ul id="nav-list" class="nav-list">
          <li><a href="#about">關於我</a></li>
          <li><a href="#skills">技能</a></li>
          <li><a href="#projects">作品</a></li>
          <li><a href="#contact">聯絡</a></li>
        </ul>
      </nav>
    </div>
  </header>

  <main id="home">
    <!-- Hero -->
    <section class="hero container">
      <div class="hero-text">
        <h1>哈囉,我是 Chiayu</h1>
        <p>前端工程師|專長 Angular & TypeScript</p>
        <div class="hero-cta">
          <a class="btn" href="#projects">看作品</a>
          <a class="btn btn-outline" href="#contact">聯絡我</a>
        </div>
      </div>
      <div class="hero-photo">
        <img src="assets/me-formal.jpg" alt="Chiayu 的正式照片" width="240" height="240" />
      </div>
    </section>

    <!-- About -->
    <section id="about" class="container section">
      <h2>關於我</h2>
      <p>我是一名前端工程師,喜歡把想法做成能上線的產品。近期專注於 Angular、TypeScript 與效能最佳化。</p>
    </section>

    <!-- Skills -->
    <section id="skills" class="container section">
      <h2>技能 Skillset</h2>
      <ul class="grid grid-2 md:grid-3 lg:grid-4">
        <li>HTML / CSS / SCSS</li>
        <li>TypeScript</li>
        <li>Angular / React / Vue</li>
        <li>Node.js / Express</li>
        <li>Git / GitHub / Docker</li>
        <li>Vite / Webpack</li>
      </ul>
    </section>

    <!-- Projects -->
    <section id="projects" class="container section">
      <h2>作品集 Projects</h2>
      <div class="grid grid-1 md:grid-2">
        <article class="card">
          <h3>毛毛購物(寵物電商)</h3>
          <p class="muted">Angular + Node.js|購物車、結帳、RWD</p>
          <p>主導前端架構與購物流程,完成商品列表到訂單頁。</p>
          <a class="btn small" href="#">Live Demo</a>
        </article>
        <article class="card">
          <h3>LINE Bot 預約系統</h3>
          <p class="muted">Cloud Functions + LINE API|時段預約</p>
          <p>整合聊天介面與雲端排程,完成會員預約流程。</p>
          <a class="btn small" href="#">Live Demo</a>
        </article>
      </div>
    </section>

    <!-- Contact -->
    <section id="contact" class="container section">
      <h2>聯絡我</h2>
      <address>
        Email:<a href="mailto:hotdanton08@hotmail.com">hotdanton08@hotmail.com</a><br />
        GitHub:<a href="https://github.com/你的帳號">github.com/你的帳號</a><br />
        LinkedIn:<a href="https://linkedin.com/in/你的帳號">linkedin.com/in/你的帳號</a>
      </address>
    </section>
  </main>

  <footer class="site-footer">
    <div class="container">
      <p>&copy; 2025 Chiayu Lee · All rights reserved.</p>
    </div>
  </footer>

  <!-- (可選)一行 JS 讓手機上「選單」按鈕能開合 -->
  <script>
    const btn = document.getElementById('nav-toggle');
    const list = document.getElementById('nav-list');
    btn?.addEventListener('click', () => {
      const open = btn.getAttribute('aria-expanded') === 'true';
      btn.setAttribute('aria-expanded', String(!open));
      list?.classList.toggle('open', !open);
    });
  </script>
</body>
</html>

2) styles/style.css(mobile-first + 兩個斷點)

重點:先寫手機版,再用 @media (min-width: …) 擴充到平板、桌機。

/* ===== Design Tokens(可日後抽成 SCSS 變數) ===== */
:root{
  --bg:#fff; --fg:#1f2937; --muted:#6b7280; --primary:#2563eb;
  --border:#e5e7eb; --container:1100px; --radius:12px; --space:16px;
  --lh:1.7; --base:16px; /* 基底字級(方便用 rem) */
}

/* ===== Base(手機先行) ===== */
*{ box-sizing:border-box; }
html{ font-size: var(--base); }
body{
  margin:0; background:var(--bg); color:var(--fg);
  font-family: system-ui, -apple-system, "Noto Sans TC", Arial, sans-serif;
  line-height:var(--lh);
}
img{ max-width:100%; height:auto; display:block; }
a{ color:inherit; }

.container{ max-width:var(--container); margin:0 auto; padding:0 20px; }
.section{ padding:56px 0 40px; }
.muted{ color:var(--muted); }
.btn{
  display:inline-block; text-decoration:none;
  background:var(--primary); color:#fff; border:1px solid var(--primary);
  padding:.625rem 1rem; border-radius:8px;
}
.btn:hover{ opacity:.92; }
.btn-outline{ background:transparent; color:var(--primary); }
.btn.small{ padding:.5rem .75rem; font-size:.9rem; }

/* ===== Header(手機:品牌 + 漢堡選單 + 下拉清單) ===== */
.site-header{
  position:sticky; top:0; background:var(--bg); border-bottom:1px solid var(--border); z-index:10;
}
.header-inner{
  display:flex; align-items:center; gap:12px; padding:12px 0;
}
.brand{ font-weight:700; text-decoration:none; }
.site-nav{ margin-left:auto; }
.nav-toggle{
  border:1px solid var(--border); background:transparent; padding:.5rem .75rem; border-radius:8px;
}
.nav-list{
  list-style:none; margin:.5rem 0 0; padding:0; display:none; /* 手機預設收起 */
}
.nav-list.open{ display:block; }
.nav-list li{ border-top:1px solid var(--border); }
.nav-list a{ display:block; padding:.75rem 0; text-decoration:none; }

/* ===== Hero(手機:上下排列) ===== */
.hero{ display:grid; gap:20px; padding:40px 0; }
.hero-text h1{ margin:.2rem 0 .4rem; font-size: clamp(1.5rem, 5vw, 2.2rem); }
.hero-cta{ display:flex; gap:12px; margin-top:.5rem; }
.hero-photo{ max-width:240px; }
.hero-photo img{ border-radius:50%; border:4px solid var(--border); }

/* ===== Grid 工具:手機預設 1 欄 ===== */
.grid{ display:grid; gap:12px; }
.grid-1{ grid-template-columns:1fr; }
.grid-2{ grid-template-columns:1fr; } /* 先 1 欄,等中斷點再擴張 */
.md\:grid-2, .md\:grid-3, .lg\:grid-4{ grid-template-columns:1fr; } /* 較大螢幕才改 */

/* 卡片 */
.card{ border:1px solid var(--border); border-radius:var(--radius); padding:16px; }

/* ===== Footer ===== */
.site-footer{ border-top:1px solid var(--border); }
.site-footer .container{ text-align:center; padding:16px 0; color:var(--muted); }

/* ===== 斷點:平板(≥768px) ===== */
@media (min-width:768px){
  .nav-toggle{ display:none; }         /* 平板以上顯示水平選單 */
  .nav-list{ display:flex; gap:16px; margin:0; }
  .nav-list li{ border:0; }
  .nav-list a{ padding:.25rem 0; }

  .hero{
    grid-template-columns: 1.2fr .8fr; align-items:center;
  }

  /* grid 工具在平板擴張 */
  .md\:grid-2{ grid-template-columns: repeat(2, 1fr); }
  .md\:grid-3{ grid-template-columns: repeat(3, 1fr); }
}

/* ===== 斷點:桌機(≥1024px) ===== */
@media (min-width:1024px){
  .lg\:grid-4{ grid-template-columns: repeat(4, 1fr); }
  .section{ padding:72px 0 56px; }
}

你會看到我們大量用 工具類 class(grid-2 / md:grid-3 / lg:grid-4)來快速定義不同斷點下的欄數。這對初學者非常直覺,也好維護。


成果(你可以在文末附圖)

  • 手機版:導覽為下拉清單、Hero 上下排列、Skills/Projects 單欄卡片。
  • 平板:導覽水平排列、Hero 變成 2 欄、Skills 3 欄、Projects 2 欄。
  • 桌機:Spacing 放大、Skills 最多 4 欄。

建議截圖:

  • day5-mobile-hero.pngday5-tablet-grid.pngday5-desktop-overview.png

小心踩雷(常見誤用 → 正確作法)

  1. 沒加 viewport,手機版字超小
  • ❌ 忘記 <meta name="viewport" content="width=device-width, initial-scale=1">
  • ✅ 一定要加,否則手機會用桌機寬度縮放顯示。
  1. 桌機優先寫法,到手機才縮
  • ❌ 先寫桌機固定寬度,再一路覆寫到手機,CSS 爆炸。
  • Mobile-first:預設給手機;用 @media (min-width: …) 向上加。
  1. 固定寬度造成溢出
  • .card{ width: 400px } 在 360px 手機直接爆版。
  • ✅ 用彈性:max-width、百分比、clamp() 文字大小、grid/flex 佈局。
  1. 圖片不設上限導致超框
  • img 無限制;大圖把版面撐破。
  • ✅ 全域 img{ max-width:100%; height:auto },必要時再加容器寬。
  1. 把內容「隱藏」當作行動版
  • ❌ 行動版大量 display:none 直接砍內容。
  • ✅ 行動版優先顯示核心資訊;「同內容不同排列」,不是「縮水內容」。
  1. 點擊區太小
  • ❌ 導覽連結間距過小,手指難點。
  • ✅ 增加 padding,確保點擊目標至少 44×44px(Apple/HIG 建議)。
  1. 斷點跟裝置型號綁死
  • ❌ 「iPhone 用 375、iPad 用 768…」
  • ✅ 以版面擠壓點決定斷點,跟裝置型號解耦。

進一步練習(可選)

  • clamp() 做流體字級:font-size: clamp(1rem, 2.5vw, 1.25rem);

  • Skills 區塊改用 grid-auto-fit

    .grid-auto { display:grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap:12px; }
    
    
  • Hero 圖片在超寬螢幕限制最大寬度,避免過度放大。


下一步(Day 6 預告)

明天開始把同一份資訊架構搬進 Angular

  • 用 Angular CLI 建專案(standalone + routing)
  • 建立頁面骨架與路由,導入今天的 RWD 思維(mobile-first SCSS)
  • 把內容來源資料化(JSON/TS 型別),之後才能在 React/Vue 復用

如果你想,我也可以把今天的 RWD 版本幫你打包成最小可上線的檔案,附一份 GitHub Pages / Vercel 的部署說明。


上一篇
DOM 操作與事件綁定 – 用 TS 操作 HTML 元素
下一篇
Day 6 小專案:純原生 HTML/CSS/TS 的自我介紹頁(可交付 MVP) 今日目標
系列文
Angular、React、Vue 三框架實戰養成:從零打造高品質前端履歷網站26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言