挑战:移动优先布局

本挑战通过要求你更新现有的移动布局,使其在桌面浏览器上也能很好地工作,从而为 CSS 布局模块画上句号。在此过程中,你还将测试响应式布局功能,例如媒体查询、CSS 网格、弹性盒和响应式图像。

起始点

我们将让你在本地开发环境中解决此挑战;理想情况下,你将需要在完整的浏览器窗口中查看示例,以确保布局功能按预期工作。

  1. 在你的计算机上创建一个名为 mobile-first-challenge 的新文件夹。

  2. 在该文件夹中,创建一个 index.html 文件,并将以下内容粘贴到其中。

    html
    <!DOCTYPE html>
    <html lang="en-US">
      <head>
        <meta charset="utf-8" />
        <title>RWD Task</title>
        <link href="style.css" rel="stylesheet" type="text/css" />
        <script defer src="script.js"></script>
      </head>
    
      <body>
        <header>
          <div class="logo">My exciting website!</div>
          <button aria-label="Open menu">☰</button>
        </header>
    
        <main class="grid">
          <nav>
            <ul>
              <li><a href="#">Home</a></li>
              <li><a href="#">Blog</a></li>
              <li><a href="#">About us</a></li>
              <li><a href="#">Our history</a></li>
              <li><a href="#">Contacts</a></li>
            </ul>
          </nav>
          <article>
            <h1>An Exciting Blog Post</h1>
            <img src="images/square6.jpg" alt="placeholder" class="feature" />
            <p>
              Veggies es bonus vobis, proinde vos postulo essum magis kohlrabi
              welsh onion daikon amaranth tatsoi tomatillo melon azuki bean
              garlic.
            </p>
    
            <p>
              Turnip greens yarrow ricebean rutabaga endive cauliflower sea
              lettuce kohlrabi amaranth water spinach avocado daikon napa
              asparagus winter purslane kale. Celery potato scallion desert
              raisin horseradish spinach carrot soko. Lotus root water spinach
              fennel kombu maize bamboo shoot green bean swiss chard seakale
              pumpkin onion chickpea gram corn pea. Brussels sprout coriander
              water chestnut gourd swiss chard wakame kohlrabi beetroot carrot
              watercress. Corn amaranth salsify bunya nuts nori azuki bean
              chickweed potato bell pepper artichoke.
            </p>
    
            <p>
              Gumbo beet greens corn soko endive gumbo gourd. Parsley shallot
              courgette tatsoi pea sprouts fava bean collard greens dandelion
              okra wakame tomato. Dandelion cucumber earthnut pea peanut soko
              zucchini.
            </p>
    
            <p>
              Nori grape silver beet broccoli kombu beet greens fava bean potato
              quandong celery. Bunya nuts black-eyed pea prairie turnip leek
              lentil turnip greens parsnip. Sea lettuce lettuce water chestnut
              eggplant winter purslane fennel azuki bean earthnut pea sierra
              leone bologi leek soko chicory celtuce parsley jícama salsify.
            </p>
    
            <p>
              Celery quandong swiss chard chicory earthnut pea potato. Salsify
              taro garlic gram celery wattle seed collard greens nori. Grape
              wattle seed kombu beetroot horseradish carrot squash brussels
              sprout chard.
            </p>
    
            <p>
              Veggies es bonus vobis, proinde vos postulo essum magis kohlrabi
              welsh onion daikon amaranth tatsoi tomatillo melon azuki bean
              garlic.
            </p>
    
            <p>
              Turnip greens yarrow ricebean rutabaga endive cauliflower sea
              lettuce kohlrabi amaranth water spinach avocado daikon napa
              asparagus winter purslane kale. Celery potato scallion desert
              raisin horseradish spinach carrot soko. Lotus root water spinach
              fennel kombu maize bamboo shoot green bean swiss chard seakale
              pumpkin onion chickpea gram corn pea. Brussels sprout coriander
              water chestnut gourd swiss chard wakame kohlrabi beetroot carrot
              watercress. Corn amaranth salsify bunya nuts nori azuki bean
              chickweed potato bell pepper artichoke.
            </p>
          </article>
    
          <aside>
            <h2>Photography</h2>
            <ul class="photos">
              <li><img src="images/square1.jpg" alt="placeholder" /></li>
              <li><img src="images/square2.jpg" alt="placeholder" /></li>
              <li><img src="images/square3.jpg" alt="placeholder" /></li>
              <li><img src="images/square4.jpg" alt="placeholder" /></li>
              <li><img src="images/square5.jpg" alt="placeholder" /></li>
            </ul>
          </aside>
        </main>
      </body>
    </html>
    
  3. 在该文件夹中,创建一个 style.css 文件,并将以下内容粘贴到其中。

    css
    /* General styles */
    
    * {
      box-sizing: border-box;
    }
    
    body {
      background-color: white;
      color: #333333;
      margin: 0;
      font: 1.2em / 1.6 sans-serif;
      padding: 0 20px 20px 20px;
    }
    
    img {
      display: block;
      border: 1px solid black;
    }
    
    /* Mobile layout */
    
    header {
      padding: 50px 0;
      display: flex;
      gap: 20px;
      justify-content: space-between;
      align-items: center;
    }
    
    .logo {
      font-size: 200%;
    }
    
    button {
      font-size: 250%;
      border: 0;
      background: none;
      cursor: pointer;
    }
    
    button:hover,
    button:focus {
      text-shadow: 0 0 2px black;
    }
    
    nav {
      position: fixed;
      inset: 10%;
      background-color: white;
      display: none;
    }
    
    nav ul {
      margin: 0;
      padding: 0;
      list-style: none;
      text-align: center;
      height: 100%;
      display: flex;
      gap: 10px;
      flex-direction: column;
    }
    
    nav li {
      flex: 1;
    }
    
    nav a {
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 150%;
      width: 100%;
      height: 100%;
      background-color: black;
      color: white;
      text-decoration: none;
    }
    
    nav a:hover,
    nav a:focus {
      font-weight: bold;
    }
    
    .photos {
      list-style: none;
      margin: 0;
      padding: 0;
      display: grid;
      gap: 5px;
      grid-template-columns: 1fr 1fr;
    }
    
    .feature {
      width: 200px;
      float: left;
      margin: 8px 30px 20px 0;
    }
    
  4. 在该文件夹中,创建一个 script.js 文件,并将以下内容粘贴到其中。

    js
    const btn = document.querySelector("button");
    const nav = document.querySelector("nav");
    
    function showNav() {
      nav.style.display = "block";
    }
    
    function hideNav() {
      nav.style.display = "none";
    }
    
    function hideNavEsc(e) {
      if (e.key === "Escape") {
        nav.style.display = "none";
      }
    }
    
    function handleEventListeners() {
      if (matchMedia("(width > 800px)").matches) {
        btn.removeEventListener("click", showNav);
        nav.removeEventListener("click", hideNav);
        document.body.removeEventListener("keydown", hideNavEsc);
        if (nav.style.display === "none") {
          nav.style.display = "block";
        }
      } else {
        btn.addEventListener("click", showNav);
        nav.addEventListener("click", hideNav);
        document.body.addEventListener("keydown", hideNavEsc);
        if (nav.style.display === "block") {
          nav.style.display = "none";
        }
      }
    }
    
    handleEventListeners();
    
    window.addEventListener("resize", handleEventListeners);
    
  5. 在该文件夹中,创建一个名为 images 的子文件夹,并将以下图像文件保存到其中。

  6. 保存你的文件并在浏览器中加载 index.html,准备进行测试。在狭窄的视口中查看时,页面的起始点应该看起来像这样:

    Starting point of the mobile-first task. A single column layout with a logo at the top and a hamburger menu icon, followed by a top-level heading, followed by text content with a floated image.

项目简介

本示例提供的内容与上一个挑战(基本布局理解)的内容相同,只是在结构上略有不同。它从一开始就有一个大致完整的布局,尽管你可能已经注意到,它在宽屏视口中看起来很糟糕!

这是因为我们为你提供了一个移动布局作为起点。请注意,导航菜单通过按“汉堡菜单”图标访问,并且可以通过单击菜单项或按 Esc 键关闭。此功能通过 JavaScript 处理,并且仅在视口宽度小于 800px 时才有效,这样它就不会干扰你将要实现的更宽屏幕布局。

具体来说,我们希望你实现两种布局:第一种在宽度超过 800px 时触发,第二种在宽度超过 1300px 时触发。我们还将让你修复现有代码中的几个问题并实现一些附加功能。

修复几个显示问题

首先,你需要解决我们在起始模板中留下的一些问题。

  1. 目前,你的布局无法在移动浏览器中正常显示。在 <html> 文档的 <head> 中添加一个标签来解决此问题。
  2. 将浏览器窗口设置为狭窄宽度时,查看页面底部——你会发现照片库显示不正常,因为照片超出了其容器。在 CSS 文件中添加一个声明来解决此问题。

创建中间布局

中间布局需要应用于视口宽度大于 800px 的页面。按照以下步骤完成布局:

  1. 隐藏菜单 <button> 并显示 <nav>。我们只希望在移动布局中使用隐藏/显示菜单。
  2. 更改 <nav> 的定位,使其不再位于大部分内容上方,而是位于网站顶部,就在“我的精彩网站!”标志下方。我们还希望它在内容滚动到该位置后粘附在视口顶部。
  3. 导航列表项目前以列的形式显示。对于此布局,你希望它们以行跨越整个屏幕的形式显示。
  4. 调整列表项内的 <a> 元素,为它们提供 10px 的上下内边距和更小的字体大小(例如 100%)。
  5. <nav><article><aside> 元素都是 <main> 元素的子元素。我们希望你使用命名网格模板区域以以下结构布局它们:
    ┌----------------------------------------┐
    |                  <nav>                 |
    ├------------------------------┬---------┤
    |           <article>          | <aside> |
    |                              |         |
    
    <article> 元素的宽度应是 <aside> 元素宽度的三倍;两个元素应位于同一行。<nav> 元素应位于另外两个元素上方的单独一行,并跨越所有可用宽度。我们还希望你在不同的网格项之间包含 20px 的间距。

创建宽屏布局

宽屏布局需要应用于视口宽度大于 1300px 的页面。按照以下步骤完成布局:

  1. 将你为中间布局实现的网格布局更改为不同的布局,再次使用命名网格模板区域。这次,结构应如下所示:
    ┌--------┬------------------------------┬---------┐
    | <nav>  |           <article>          | <aside> |
    |        |                              |         |
    
    这次,所有三个元素都在同一行。<nav><aside> 元素的宽度应相同;<article> 元素的宽度应是另外两个元素宽度的三倍。
  2. 由于中间布局,导航列表项以行的形式显示;为了使宽屏布局工作,你需要调整列表样式,使列表项再次以列的形式显示,就像它们在移动布局中一样。
  3. 列表项目前具有 flex1,这意味着它们将拉伸以填充列的整个高度。调整此属性值,使导航项的高度仅与其内容和设置的 padding 一样高。

实现响应式排版

我们希望你调整 <h1><h2> 元素的样式,使它们:

  1. 移除它们的上下 margin,使其更紧密地与上方和下方的内容配合。
  2. 在视口变宽或变窄时响应式地改变其大小,同时仍可缩放。你应该选择合适的单位,使标题很好地填充可用空间,而不会断开成多行。

调整打印布局

添加一个样式块,在打印页面时从布局中删除 <button><nav> 元素。

提示和技巧

  1. 你不需要编辑 JavaScript 来完成此挑战。
  2. 有几种方法可以完成项目简报中的某些任务,而且通常没有单一的正确或错误的方法。尝试几种不同的方法,看看哪种效果最好。在实验时做好笔记。
  3. 有时,为先前布局设置的属性值会导致后续布局出现问题。响应式设计的一些技巧在于知道何时取消设置或覆盖先前设置的属性值。

示例

以下屏幕截图显示了完成的中间布局应该是什么样子:

Finished rwd task website middle layout. A logo at the top, followed by a horizontal nav menu, followed by two columns, text content on the left and a photo gallery on the right.

以下屏幕截图显示了完成的宽屏布局应该是什么样子:

Finished rwd task website widescreen layout. A logo at the top, followed by three columns, vertical nav menu on the left, text content in the center, and a photo gallery on the right.

点击此处显示可能的解决方案

为了使布局在移动浏览器中正常显示,你需要在 HTML 文档的 <head> 中添加一个视口 <meta> 标签。

html
<meta name="viewport" content="width=device-width, initial-scale=1" />

完成的 CSS 应该像这样:

css
/* General styles */

* {
  box-sizing: border-box;
}

body {
  background-color: white;
  color: #333333;
  margin: 0;
  font: 1.2em / 1.6 sans-serif;
  padding: 0 20px 20px 20px;
}

img {
  display: block;
  border: 1px solid black;
  /* Solution: Stop the photographs from breaking out of
  their containers */
  max-width: 100%;
}

/* Mobile layout */

header {
  padding: 50px 0;
  display: flex;
  gap: 20px;
  justify-content: space-between;
  align-items: center;
}

.logo {
  font-size: 200%;
}

button {
  font-size: 250%;
  border: 0;
  background: none;
  cursor: pointer;
}

button:hover,
button:focus {
  text-shadow: 0 0 2px black;
}

nav {
  position: fixed;
  inset: 10%;
  background-color: white;
  display: none;
}

nav ul {
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
  height: 100%;
  display: flex;
  gap: 10px;
  flex-direction: column;
}

nav li {
  flex: 1;
}

nav a {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 150%;
  width: 100%;
  height: 100%;
  background-color: black;
  color: white;
  text-decoration: none;
}

nav a:hover,
nav a:focus {
  font-weight: bold;
}

.photos {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 5px;
  grid-template-columns: 1fr 1fr;
}

.feature {
  width: 200px;
  float: left;
  margin: 8px 30px 20px 0;
}

/* Solution: Creating the middle layout (breakpoint: 800px) */

@media (width > 800px) {
  /* Sort out navigation styling for middle breakpoint */
  button {
    display: none;
  }

  nav {
    display: block;
    inset: unset;
    position: sticky;
    top: 0;
  }

  nav ul {
    flex-direction: row;
  }

  nav a {
    font-size: 100%;
    padding: 10px 0;
  }

  /* Create grid layout for middle breakpoint */

  nav {
    grid-area: nav;
  }

  article {
    grid-area: main;
  }

  aside {
    grid-area: photos;
  }

  .grid {
    display: grid;
    grid-template-columns: 3fr 1fr;
    grid-template-areas:
      "nav nav"
      "main photos";
    gap: 20px;
  }
}

/* Solution: Creating the widescreen layout (breakpoint: 1300px) */

@media (width > 1300px) {
  .grid {
    grid-template-columns: 1fr 3fr 1fr;
    grid-template-areas: "nav main photos";
  }

  nav ul {
    flex-direction: column;
  }

  nav li {
    flex: unset;
  }
}

/* 4. Solution: Implementing responsive typography */

h1 {
  font-size: calc(1.3rem + 3vw);
  margin: 0;
}

h2 {
  font-size: calc(1rem + 2vw);
  margin: 0;
}

/* 5. Solution: Adjusting the layout for print */

@media print {
  nav,
  button {
    display: none;
  }
}