为网页表单添加样式

在前面的几篇文章中,我们展示了如何在 HTML 中创建网页表单。现在,我们将展示如何在 CSS 中为它们设置样式。

预备知识 HTMLCSS 样式基础有基本的了解。
目标 了解表单样式背后的问题,并学习一些对你有用的基本样式技术。

表单小部件样式设置的挑战

History

1995 年,HTML 2 规范引入了表单控件(又称“表单小部件”或“表单元素”)。但 CSS 直到 1996 年底才发布,并且在几年后才被大多数浏览器支持;因此,在此期间,浏览器依赖底层操作系统来渲染表单小部件。

即使有了 CSS,浏览器厂商最初也不愿让表单元素可样式化,因为用户太习惯于各自浏览器的外观。但情况已经发生了变化,现在表单小部件大多可以样式化,只有少数例外。

小部件类型

易于样式化

  1. <form>
  2. <fieldset><legend>
  3. 单行文本 <input>s(例如,type 为 text、url、email),除了 <input type="search">
  4. 多行 <textarea>
  5. 按钮(<input><button> 均可)
  6. <label>
  7. <output>

较难样式化

文章 高级表单样式 展示了如何为这些设置样式。

内部结构无法仅通过 CSS 样式化

例如,日期选择器日历以及点击时显示选项列表的 <select> 上的按钮无法仅使用 CSS 进行样式设置。

文章 高级表单样式如何构建自定义表单控件 描述了如何为这些设置样式。

注意:一些专有的 CSS 伪元素,例如 ::-moz-range-track,能够为这些内部组件设置样式,但这些在不同浏览器之间不一致,因此不是很可靠。我们稍后会提到这些。

为简单的表单小部件设置样式

上一节中的“易于样式化”小部件可以使用文章 你的第一个表单CSS 构建块 中的技术进行样式设置。还有一些特殊的选择器——UI 伪类——可以根据 UI 的当前状态进行样式设置。

我们将在本文末尾通过一个示例进行演示——但首先,这里有一些值得了解的表单样式方面的特殊之处。

字体和文本

CSS 字体和文本功能可以轻松地与任何小部件一起使用(是的,你可以在表单小部件中使用 @font-face)。然而,浏览器行为通常不一致。默认情况下,某些小部件不会从其父级继承 font-familyfont-size。许多浏览器而是使用系统的默认外观。为了使你的表单外观与内容的其余部分保持一致,你可以在样式表中添加以下规则

css
button,
input,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
}

inherit 属性值使属性值与其父元素的属性的计算值匹配;继承父级的值。

下面的屏幕截图显示了差异。左侧是 macOS 上 Chrome 中 <input type="text"><input type="date"><select><textarea><input type="submit"><button> 的默认渲染,使用了平台的默认字体样式。右侧是相同的元素,应用了我们上面的样式规则。

Form controls with default and inherited font families. By default, some types are serif and others are sans serif. Inheriting should change the fonts of all to the parent's font family - in this case a paragraph. Oddly, input of type submit does not inherit from the parent paragraph.

默认设置在很多方面有所不同。继承应该将它们的字体更改为父级的字体系列——在这种情况下,是父容器的默认衬线字体。它们都这样做了,只有一个奇怪的例外——<input type="submit"> 在 Chrome 中不从父段落继承。相反,它使用 font-family: system-ui。这是使用 <button> 元素而不是其等效输入类型的另一个原因!

关于表单是使用系统默认样式看起来更好,还是使用旨在匹配你内容的自定义样式看起来更好,有很多争论。这个决定由你作为网站或 Web 应用程序的设计师来做出。

盒模型

所有文本字段都完全支持与 CSS 盒模型相关的每个属性,例如 widthheightpaddingmarginborder。然而,与以前一样,浏览器在显示这些小部件时依赖于系统默认样式。如何将它们融入你的内容取决于你。如果你想保持小部件的原生外观和感觉,那么如果你想为它们提供一致的大小,你会遇到一些困难。

这是因为每个小部件都有自己的边框、填充和边距规则。要为几个不同的小部件提供相同的大小,你可以使用 box-sizing 属性以及其他属性的一些一致值

css
input,
textarea,
select,
button {
  width: 150px;
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

在下面的屏幕截图中,左栏显示了 <input type="radio"><input type="checkbox"><input type="range"><input type="text"><input type="date"><select><textarea><input type="submit"><button> 的默认渲染。右栏则显示了应用了我们上面规则的相同元素。请注意,这如何让我们确保所有元素占据相同的空间,尽管平台对每种类型的小部件都有默认规则。

box model properties effect most input types.

屏幕截图可能不明显的是,单选按钮和复选框控件看起来仍然相同,但它们居中在 width 属性提供的 150 像素水平空间中。其他浏览器可能不会使小部件居中,但它们确实遵守了分配的空间。

图例放置

<legend> 元素可以设置样式,但控制其位置可能有点棘手。默认情况下,它总是位于其 <fieldset> 父级的顶部边框上方,靠近左上角。要将其放置在其他位置,例如在字段集内部的某个位置,或靠近左下角,你需要依赖定位。

请看以下示例

为了以这种方式定位图例,我们使用了以下 CSS(为简洁起见,省略了其他声明)

css
fieldset {
  position: relative;
}

legend {
  position: absolute;
  bottom: 0;
  right: 0;
}

<fieldset> 也需要定位,以便 <legend> 相对于它定位(否则 <legend> 将相对于 <body> 定位)。

<legend> 元素对于可访问性非常重要——辅助技术会将其作为字段集内每个表单元素的标签的一部分进行朗读——但使用上述技术是没问题的。图例内容仍然会以相同的方式朗读;只是视觉位置发生了变化。

注意:你还可以使用 transform 属性来帮助你定位 <legend>。但是,当你使用例如 transform: translateY(); 定位它时,它会移动,但在 <fieldset> 边框中留下一个难看的间隙,这不容易去除。

一个具体的样式示例

让我们看一个如何为 HTML 表单设置样式的具体示例。我们将构建一个看起来很漂亮的“明信片”联系表单;点击此处查看完成版本

如果你想跟着这个示例操作,请下载我们的 postcard-start.html 文件的本地副本,并按照以下说明操作。

HTML

HTML 只比我们在 你的第一个表单 中使用的示例稍微复杂一点;它只多了一些 ID 和一个标题。

html
<form>
  <h1>to: Mozilla</h1>

  <div id="from">
    <label for="name">from:</label>
    <input type="text" id="name" name="user_name" />
  </div>

  <div id="reply">
    <label for="mail">reply:</label>
    <input type="email" id="mail" name="user_email" />
  </div>

  <div id="message">
    <label for="msg">Your message:</label>
    <textarea id="msg" name="user_message"></textarea>
  </div>

  <div class="button">
    <button type="submit">Send your message</button>
  </div>
</form>

将上述代码添加到 HTML 的 body 中。

组织你的资产

这才是乐趣的开始!在开始编码之前,我们需要另外三个资产

  1. 明信片背景 — 下载此图片并将其保存在与你的 HTML 工作文件相同的目录中。
  2. 一种打字机字体:来自 dafont.com 的“Mom's Typewriter”字体 — 将 TTF 文件下载到与上面相同的目录中。
  3. 一种手绘字体:来自 dafont.com 的“Journal”字体 — 将 TTF 文件下载到与上面相同的目录中。

你的字体在开始之前还需要一些额外的处理

  1. 访问 fontsquirrel.com Webfont Generator
  2. 使用表单上传你的字体文件并生成一个 webfont kit。将 kit 下载到你的计算机。
  3. 解压缩提供的 zip 文件。
  4. 在解压缩的内容中,你会找到一些字体文件(在撰写本文时,是两个 .woff 文件和两个 .woff2 文件;将来可能会有所不同)。将这些文件复制到一个名为 fonts 的目录中,与之前在同一目录中。我们为每种字体使用两个不同的文件以最大限度地提高浏览器兼容性;有关更多信息,请参阅我们的 Web 字体 文章。

CSS

现在我们可以深入研究示例的 CSS。将下面显示的所有代码块一个接一个地添加到 <style> 元素中。

整体布局

首先,我们通过定义 @font-face 规则以及 <body><form> 元素上设置的所有基本样式来准备。如果 fontsquirrel 的输出与我们上面描述的不同,你可以在你下载的 webfont kit 中的 stylesheet.css 文件中找到正确的 @font-face 块(你需要用它们替换下面的 @font-face 块,并更新字体文件的路径)

css
@font-face {
  font-family: "handwriting";
  src:
    url("fonts/journal-webfont.woff2") format("woff2"),
    url("fonts/journal-webfont.woff") format("woff");
  font-weight: normal;
  font-style: normal;
}

@font-face {
  font-family: "typewriter";
  src:
    url("fonts/momot___-webfont.woff2") format("woff2"),
    url("fonts/momot___-webfont.woff") format("woff");
  font-weight: normal;
  font-style: normal;
}

body {
  font: 1.3rem sans-serif;
  padding: 0.5em;
  margin: 0;
  background: #222222;
}

form {
  position: relative;
  width: 740px;
  height: 498px;
  margin: 0 auto;
  padding: 1em;
  box-sizing: border-box;
  background: white url("background.jpg");

  /* we create our grid */
  display: grid;
  grid-gap: 20px;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: 10em 1em 1em 1em;
}

请注意,我们使用了一些 CSS 网格Flexbox 来布局表单。通过这种方式,我们可以轻松定位我们的元素,包括标题和所有表单元素

css
h1 {
  font:
    1em "typewriter",
    monospace;
  align-self: end;
}

#message {
  grid-row: 1 / 5;
}

#from,
#reply {
  display: flex;
}

标签和控件

现在我们可以开始处理表单元素本身了。首先,让我们确保 <label> 获得正确的字体

css
label {
  font:
    0.8em "typewriter",
    sans-serif;
}

文本字段需要一些通用规则。换句话说,我们删除它们的 bordersbackgrounds,并重新定义它们的 paddingmargin

css
input,
textarea {
  font:
    1.4em/1.5em "handwriting",
    cursive,
    sans-serif;
  border: none;
  padding: 0 10px;
  margin: 0;
  width: 80%;
  background: none;
}

当这些字段之一获得焦点时,我们用浅灰色透明背景突出显示它们(为提高可用性和键盘可访问性,始终拥有焦点样式很重要)

css
input:focus,
textarea:focus {
  background: rgb(0 0 0 / 10%);
  border-radius: 5px;
}

现在我们的文本字段已完成,我们需要调整单行和多行文本字段的显示以匹配,因为它们通常在默认情况下看起来不一样。

调整文本区域

<textarea> 元素默认渲染为行内块元素。这里重要的两件事是 resizeoverflow 属性。虽然我们的设计是固定大小的设计,我们可以使用 resize 属性来阻止用户调整我们的多行文本字段的大小,但最好不要阻止用户调整文本区域的大小。 overflow 属性用于使字段在不同浏览器中渲染更一致。某些浏览器默认值为 auto,而某些浏览器默认值为 scroll。在我们的例子中,最好确保所有人都使用 auto

css
textarea {
  display: block;

  padding: 10px;
  margin: 10px 0 0 -10px;
  width: 100%;
  height: 90%;

  border-right: 1px solid;

  /* resize  : none; */
  overflow: auto;
}

设置提交按钮样式

<button> 元素使用 CSS 设置样式非常方便;你可以做任何你想做的事情,甚至使用 伪元素

css
button {
  padding: 5px;
  font: bold 0.6em sans-serif;
  border: 2px solid #333333;
  border-radius: 5px;
  background: none;
  cursor: pointer;
  transform: rotate(-1.5deg);
}

button::after {
  content: " >>>";
}

button:hover,
button:focus {
  background: black;
  color: white;
}

最终结果

瞧!你的表单现在应该看起来像这样

The final look and layout of the form after applying all styling and tweaking to it as described above

注意:如果你的示例没有达到你的预期,并且你想与我们的版本进行比较,你可以在 GitHub 上找到它——查看实时运行(另请参阅源代码)。

总结

如你所见,只要我们想用纯文本字段和按钮构建表单,使用 CSS 设置它们的样式就很容易。在下一篇文章中,我们将了解如何处理属于“差”和“丑”类别的表单小部件。