java1234_小锋头像
【技术专题】CSS3 网页技术 - CSS3 工程化与最佳实践封面图

【技术专题】CSS3 网页技术 - CSS3 工程化与最佳实践

大家好,我是Java1234_小锋老师,最近更新 《一天学会 CSS3 视频教程(无废话版) 》专辑,感谢大家支持。

在这里插入图片描述

本课程主要介绍和讲解 CSS3简介,CSS3 入门,CSS3 选择器与优先级,CSS3 单位、颜色、字体 与文本,CSS3 盒模型 与基础样式,CSS3 布局基础,CSS3 布局进阶,CSS3 视觉 增强,CSS3 变换与动画,CSS3 函数与变量,CSS3 响应式 与移动端适配,CSS3 工程化与最佳实践。

视频教程+课件+源码 打包下载 :

链接:https://pan.baidu.com/s/1o-zRfndo1HHrS_uFroOiCw?pwd=1234
提取码:1234

CSS3(在第二阶段目录里)

CSS3 工程化与最佳实践

CSS3 工程化的目标,是让样式在多人协作、长期迭代、多端适配中保持可维护、可复用、可扩展、高性能、可访问。

命名规范:BEM

BEM 即 Block__Element--Modifier,适合组件化开发。

  • Block:独立组件,如 .card
  • Element:组件内部元素,如 .card__title
  • Modifier:状态或变体,如 .card--featured、.card__title--large
  • 原则:低耦合,不依赖标签和层级,避免 .header .nav ul li a
  • 状态类可配合 is-、has-,如 .is-active

css

.card {}
.card__title {}
.card__title--highlight {}
.card--disabled {}

优点是可读、可预测、易复用;缺点是类名可能较长,可结合工具类使用。

代码组织与模块化

常见目录结构:

text

styles/
  base/        # reset、变量、字体、全局样式
  layout/      # 布局:header、footer、grid
  components/  # 组件:button、card、modal
  pages/       # 页面级样式
  themes/      # 主题变量
  utils/       # 工具类、mixin、函数
  vendors/     # 第三方样式

常用方法论:

  • ITCSS:按层组织,从通用到具体
  • 7-1 模式:7 个目录 + 1 个入口文件
  • SMACSS:按基础、布局、模块、状态、主题分类
  • Atomic CSS / Tailwind:原子类优先
  • CSS Modules / styled-components:局部作用域,组件级样式

原则:单一职责、减少全局污染、避免深层嵌套,推荐嵌套不超过 3 层;可用 @layer 管理层叠优先级。

CSS 变量与主题切换

CSS 变量适合做设计令牌和主题系统。

css

:root {
  --color-primary: #2563eb;
  --color-bg: #ffffff;
  --color-text: #111827;
}

[data-theme="dark"] {
  --color-primary: #60a5fa;
  --color-bg: #0f172a;
  --color-text: #f8fafc;
}

body {
  background: var(--color-bg);
  color: var(--color-text);
}

切换方式:

js

document.documentElement.dataset.theme = "dark";

也可跟随系统:

css

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0f172a;
    --color-text: #f8fafc;
  }
}

建议区分基础变量和语义变量,例如 --blue-500 与 --color-primary。

reset.css 与 normalize.css
  • reset.css:清除浏览器默认样式,如 margin、padding、列表样式,统一性强,但可能丢失语义默认表现。
  • normalize.css:保留有用默认样式,修复跨浏览器差异,更温和。
  • 现代项目常用:normalize.css + 局部 reset,或使用现代 reset。

css

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

img,
picture,
video {
  max-width: 100%;
  display: block;
}

不建议无脑同时全量引入 reset 和 normalize,按项目需要选择即可。

浏览器兼容与 Autoprefixer

使用 browserslist 定义目标浏览器,再通过 PostCSS + Autoprefixer 自动添加前缀。

.browserslistrc:

text

> 0.5%
last 2 versions
not dead

postcss.config.js:

js

module.exports = {
  plugins: {
    autoprefixer: {}
  }
};

实践建议:

  • 用 caniuse 查询特性支持
  • 用 @supports 做渐进增强
  • 不依赖已废弃特性
  • 必要时提供回退样式
  • 构建流程中自动处理前缀,而不是手写
渲染性能与优化
  • 避免复杂选择器和过深嵌套,减少匹配成本
  • 动画优先使用 transform、opacity,避免触发 layout
  • 减少重排重绘,避免频繁读取和修改布局属性
  • will-change 谨慎使用,过多会消耗内存
  • 使用 contain: layout paint; 限制渲染范围
  • 使用 content-visibility: auto; 优化长列表
  • 关键 CSS 内联,非关键 CSS 延迟加载
  • 字体使用 font-display: swap,减少 FOIT
  • 图片设置宽高,减少 CLS
  • 控制 CSS 体积,拆分公共样式和页面样式

css

.animate {
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.card {
  contain: layout paint;
}
可访问性与语义化
  • 优先使用语义化 HTML,如 button、nav、main、label
  • 颜色对比满足 WCAG AA,正文通常至少 4.5:1
  • 保留可见焦点,使用 :focus-visible
  • 尊重 prefers-reduced-motion
  • 表单必须有 label,错误提示不能只靠颜色
  • 键盘可操作,避免直接 outline: none 且无替代
  • 屏幕阅读器专用类:

css

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

css

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
综合实例
<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="color-scheme" content="light dark" />
  <title>CSS3 工程化综合实例</title>

  <!--
    ============================================================
    生产环境目录结构(对应下方 <style> 中的分区注释)
    ============================================================
    styles/
      main.css                 # 入口,按顺序 @import / @use
      base/_reset.css          # 第 1 节
      base/_tokens.css         # 第 2 节
      themes/_dark.css         # 第 3 节
      layout/_header.css       # 第 4 节
      layout/_footer.css
      components/_button.css   # 第 5 节
      components/_card.css     # 第 6 节
      components/_field.css    # 第 7 节
      components/_task-list.css# 第 8 节
      utils/_utilities.css     # 第 9 节
      utils/_a11y.css          # 第 10 节

    .browserslistrc
      > 0.5%
      last 2 versions
      not dead

    postcss.config.js
      module.exports = {
        plugins: {
          'postcss-import': {},
          autoprefixer: {},
          'cssnano': {}          // 生产环境压缩
        }
      }
    —— 前缀(-webkit- 等)由 Autoprefixer 自动生成,源码里不手写。
    ============================================================
  -->

  <!-- 提前应用主题,避免首屏闪烁(FOUC) -->
  <script>
    (function () {
      try {
        var saved = localStorage.getItem('demo-theme');
        var theme = saved || (window.matchMedia &&
          window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
        document.documentElement.setAttribute('data-theme', theme);
      } catch (e) {
        document.documentElement.setAttribute('data-theme', 'light');
      }
    })();
  </script>

  <style>
    /* ==================================================================
       1. base/_reset.css —— 现代 reset(normalize 的“温和”替代方案)
       ================================================================== */
    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }

    html {
      -webkit-text-size-adjust: 100%;   /* Autoprefixer 会保留该前缀 */
      scroll-behavior: smooth;
    }

    body {
      margin: 0;
      font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC",
        "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
      line-height: 1.6;
      background-color: var(--color-bg);
      color: var(--color-text);
      transition: background-color var(--transition-base),
        color var(--transition-base);
    }

    h1, h2, h3, h4, p, ul, ol, figure {
      margin: 0;
    }

    img, svg, video {
      display: block;
      max-width: 100%;
    }

    button, input, select, textarea {
      font: inherit;
      color: inherit;
    }

    /* ==================================================================
       2. base/_tokens.css —— 设计令牌(Design Tokens)
          组件只允许消费“语义令牌”,不直接写死十六进制色值
       ================================================================== */
    :root {
      color-scheme: light;

      /* 2.1 基础令牌(原始色板,仅用于定义语义令牌) */
      --blue-500: #2563eb;
      --blue-600: #1d4ed8;
      --blue-100: #dbeafe;
      --slate-900: #0f172a;
      --slate-500: #64748b;
      --slate-200: #e2e8f0;
      --slate-100: #f1f5f9;
      --red-600: #dc2626;
      --green-600: #16a34a;

      /* 2.2 语义令牌 */
      --color-bg: #f6f8fc;
      --color-surface: #ffffff;
      --color-header-bg: rgba(255, 255, 255, 0.85);
      --color-border: var(--slate-200);
      --color-text: var(--slate-900);
      --color-text-muted: var(--slate-500);
      --color-primary: var(--blue-500);
      --color-primary-hover: var(--blue-600);
      --color-on-primary: #ffffff;
      --color-focus: var(--blue-600);
      --color-danger: var(--red-600);
      --color-success: var(--green-600);
      --color-badge-bg: var(--blue-100);
      --color-badge-text: var(--blue-600);
      --color-code-bg: var(--slate-100);

      /* 2.3 间距 / 圆角 / 字号 */
      --space-1: 0.25rem;
      --space-2: 0.5rem;
      --space-3: 0.75rem;
      --space-4: 1rem;
      --space-5: 1.5rem;
      --space-6: 2rem;
      --space-7: 3rem;

      --radius-sm: 6px;
      --radius-md: 10px;
      --radius-lg: 16px;
      --radius-full: 999px;

      --font-sm: 0.875rem;
      --font-lg: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
      --font-2xl: clamp(1.6rem, 1.3rem + 1.4vw, 2.25rem);

      /* 2.4 阴影与动效 */
      --shadow-card: 0 1px 2px rgba(15, 23, 42, 0.06),
        0 8px 24px rgba(15, 23, 42, 0.06);
      --shadow-focus: 0 0 0 3px rgba(37, 99, 235, 0.35);
      --transition-base: 160ms ease;

      /* 2.5 布局 */
      --container: 68rem;
      --header-h: 4rem;
    }

    /* ==================================================================
       3. themes/_dark.css —— 主题切换:只覆盖语义令牌,不动组件代码
       ================================================================== */
    :root[data-theme="dark"] {
      color-scheme: dark;

      --color-bg: #0b1220;
      --color-surface: #131c2e;
      --color-header-bg: rgba(19, 28, 46, 0.85);
      --color-border: #26334a;
      --color-text: #e8eefc;
      --color-text-muted: #9aa8c0;
      --color-primary: #6ba1ff;
      --color-primary-hover: #8ab6ff;
      --color-on-primary: #08111f;
      --color-focus: #8ab6ff;
      --color-danger: #ff8b8b;
      --color-success: #6ee7a8;
      --color-badge-bg: rgba(107, 161, 255, 0.16);
      --color-badge-text: #9cc2ff;
      --color-code-bg: rgba(255, 255, 255, 0.08);
      --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4),
        0 8px 24px rgba(0, 0, 0, 0.35);
      --shadow-focus: 0 0 0 3px rgba(107, 161, 255, 0.35);
    }

    /* ==================================================================
       4. layout/_header.css + _footer.css
       ================================================================== */
    .container {
      width: 100%;
      max-width: var(--container);
      margin-inline: auto;
      padding-inline: var(--space-4);
    }

    .site-header {
      position: sticky;
      top: 0;
      z-index: 10;
      background-color: var(--color-surface);
      border-bottom: 1px solid var(--color-border);
      transition: background-color var(--transition-base);
    }

    /* 渐进增强:支持 backdrop-filter 时才启用毛玻璃,否则回退为不透明背景 */
    @supports ((backdrop-filter: blur(8px)) or (-webkit-backdrop-filter: blur(8px))) {
      .site-header {
        background-color: var(--color-header-bg);
        -webkit-backdrop-filter: blur(8px);
        backdrop-filter: blur(8px);
      }
    }

    .site-header__inner {
      display: flex;
      align-items: center;
      gap: var(--space-4);
      min-height: var(--header-h);
    }

    .site-header__brand {
      font-weight: 700;
      color: var(--color-text);
      text-decoration: none;
      white-space: nowrap;
    }

    .site-nav__list {
      display: flex;
      gap: var(--space-4);
      margin: 0;
      padding: 0;
      list-style: none;
    }

    .site-nav__link {
      display: inline-block;
      padding: var(--space-2) var(--space-1);
      border-radius: var(--radius-sm);
      color: var(--color-text-muted);
      text-decoration: none;
      transition: color var(--transition-base);
    }

    .site-nav__link:hover {
      color: var(--color-primary);
    }

    /* BEM 修饰符表示当前状态 */
    .site-nav__link--active {
      color: var(--color-text);
      font-weight: 600;
    }

    .theme-toggle {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 2.75rem;
      height: 2.75rem;
      margin-left: auto;
      padding: 0;
      border: 1px solid var(--color-border);
      border-radius: var(--radius-full);
      background-color: var(--color-surface);
      color: var(--color-text);
      cursor: pointer;
      transition: border-color var(--transition-base),
        background-color var(--transition-base);
    }

    .theme-toggle:hover {
      border-color: var(--color-primary);
    }

    /* 图标切换:浅色显示月亮,深色显示太阳 */
    .theme-toggle__icon--sun {
      display: none;
    }

    :root[data-theme="dark"] .theme-toggle__icon--sun {
      display: block;
    }

    :root[data-theme="dark"] .theme-toggle__icon--moon {
      display: none;
    }

    .page {
      padding-block: var(--space-7);
    }

    .section {
      scroll-margin-top: calc(var(--header-h) + var(--space-4));
    }

    .section + .section {
      margin-top: var(--space-7);
      padding-top: var(--space-7);
      border-top: 1px solid var(--color-border);
    }

    .section__title {
      font-size: var(--font-lg);
      line-height: 1.35;
    }

    .section__desc {
      max-width: 62ch;
      margin-top: var(--space-2);
      margin-bottom: var(--space-5);
      color: var(--color-text-muted);
      font-size: var(--font-sm);
    }

    .site-footer {
      padding-block: var(--space-6);
      border-top: 1px solid var(--color-border);
      color: var(--color-text-muted);
      font-size: var(--font-sm);
    }

    /* ==================================================================
       5. components/_button.css —— BEM + 局部 CSS 变量修饰符
       ================================================================== */
    .btn {
      /* 局部变量做“可配置默认值”,修饰符只覆盖变量 */
      --btn-bg: transparent;
      --btn-fg: var(--color-text);
      --btn-border: var(--color-border);

      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-2);
      min-height: 2.75rem;              /* 触控目标 ≥ 44px */
      padding: var(--space-2) var(--space-4);
      border: 1px solid var(--btn-border);
      border-radius: var(--radius-md);
      background-color: var(--btn-bg);
      color: var(--btn-fg);
      font-size: var(--font-sm);
      font-weight: 600;
      text-decoration: none;
      cursor: pointer;
      /* 只过渡可合成 / 低开销属性,避免触发 layout */
      transition: background-color var(--transition-base),
        border-color var(--transition-base), transform var(--transition-base);
    }

    .btn:hover {
      transform: translateY(-1px);
    }

    .btn:active {
      transform: translateY(0);
    }

    .btn--primary {
      --btn-bg: var(--color-primary);
      --btn-fg: var(--color-on-primary);
      --btn-border: var(--color-primary);
    }

    .btn--primary:hover {
      --btn-bg: var(--color-primary-hover);
      --btn-border: var(--color-primary-hover);
    }

    .btn--ghost {
      --btn-bg: transparent;
      --btn-border: transparent;
    }

    .btn--ghost:hover {
      --btn-bg: var(--color-code-bg);
    }

    .btn--lg {
      min-height: 3rem;
      padding: var(--space-3) var(--space-5);
      font-size: 1rem;
    }

    .btn[disabled],
    .btn--disabled {
      opacity: 0.55;
      cursor: not-allowed;
      transform: none;
    }

    /* ==================================================================
       6. components/_card.css
       ================================================================== */
    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
      gap: var(--space-4);
    }

    .card {
      display: flex;
      flex-direction: column;
      gap: var(--space-3);
      padding: var(--space-5);
      border: 1px solid var(--color-border);
      border-radius: var(--radius-lg);
      background-color: var(--color-surface);
      box-shadow: var(--shadow-card);
      /* 性能:把重排 / 重绘影响限制在本元素内部 */
      contain: layout paint;
      transition: transform var(--transition-base),
        box-shadow var(--transition-base), border-color var(--transition-base);
    }

    .card:hover {
      transform: translateY(-2px);
    }

    .card--featured {
      border-color: var(--color-primary);
      box-shadow: 0 0 0 1px var(--color-primary), var(--shadow-card);
    }

    .card__badge {
      align-self: flex-start;
      padding: var(--space-1) var(--space-3);
      border-radius: var(--radius-full);
      background-color: var(--color-badge-bg);
      color: var(--color-badge-text);
      font-size: 0.75rem;
      font-weight: 600;
    }

    .card__title {
      font-size: 1.0625rem;
      line-height: 1.4;
    }

    .card__text {
      color: var(--color-text-muted);
      font-size: var(--font-sm);
    }

    .card__footer {
      display: flex;
      align-items: center;
      gap: var(--space-3);
      margin-top: auto;                 /* 底部对齐 */
      padding-top: var(--space-2);
    }

    /* ==================================================================
       7. components/_field.css —— 表单可访问性
       ================================================================== */
    .form {
      display: grid;
      gap: var(--space-4);
      max-width: 32rem;
    }

    .field {
      display: grid;
      gap: var(--space-2);
    }

    .field__label {
      font-size: var(--font-sm);
      font-weight: 600;
    }

    .field__input {
      padding: var(--space-3);
      border: 1px solid var(--color-border);
      border-radius: var(--radius-md);
      background-color: var(--color-surface);
      color: var(--color-text);
      transition: border-color var(--transition-base);
    }

    .field__input:hover {
      border-color: var(--color-text-muted);
    }

    .field__hint {
      color: var(--color-text-muted);
      font-size: var(--font-sm);
    }

    /* 错误提示:图标 + 文案,不依赖颜色单独传达信息 */
    .field__error {
      display: flex;
      align-items: center;
      gap: var(--space-2);
      color: var(--color-danger);
      font-size: var(--font-sm);
    }

    .field--invalid .field__input {
      border-color: var(--color-danger);
    }

    /* ==================================================================
       8. components/_task-list.css —— content-visibility 性能示例
       ================================================================== */
    .task-list {
      display: grid;
      gap: var(--space-2);
      margin: 0;
      padding: 0;
      list-style: none;
    }

    .task-list__item {
      padding: var(--space-3) var(--space-4);
      border: 1px solid var(--color-border);
      border-radius: var(--radius-md);
      background-color: var(--color-surface);
      font-size: var(--font-sm);
      /* 性能:视口外的列表项跳过渲染,长列表滚动更顺滑 */
      content-visibility: auto;
      contain-intrinsic-size: auto 3.25rem;
    }

    /* ==================================================================
       9. utils/_utilities.css
       ================================================================== */
    .stack {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: var(--space-3);
    }

    code {
      padding: 0.1em 0.35em;
      border-radius: 4px;
      background-color: var(--color-code-bg);
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 0.9em;
    }

    .icon {
      flex: 0 0 auto;
    }

    /* ==================================================================
       10. utils/_a11y.css —— 可访问性
       ================================================================== */

    /* 10.1 屏幕阅读器专用文本 */
    .sr-only {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
    }

    /* 10.2 跳过导航(键盘用户第一个可聚焦元素) */
    .skip-link {
      position: absolute;
      top: var(--space-2);
      left: var(--space-4);
      z-index: 100;
      padding: var(--space-2) var(--space-4);
      border-radius: var(--radius-md);
      background-color: var(--color-primary);
      color: var(--color-on-primary);
      text-decoration: none;
      transform: translateY(-200%);
      transition: transform var(--transition-base);
    }

    .skip-link:focus-visible {
      transform: translateY(0);
    }

    /* 10.3 统一可见焦点,绝不直接 outline: none */
    :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
      outline: 2px solid var(--color-focus);
      outline-offset: 2px;
    }

    /* 10.4 尊重用户的“减少动态效果”设置 */
    @media (prefers-reduced-motion: reduce) {
      *,
      *::before,
      *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
      }
    }

    /* 10.5 窄屏适配 */
    @media (max-width: 40rem) {
      .site-nav {
        display: none;               /* 实际项目可换成汉堡菜单 */
      }

      .page {
        padding-block: var(--space-6);
      }
    }
  </style>
</head>

<body>
  <!-- 跳过导航:键盘 / 屏幕阅读器用户可直达主内容 -->
  <a class="skip-link" href="#main">跳到主要内容</a>

  <!-- ================= 页头:BEM 命名 + 主题切换 ================= -->
  <header class="site-header">
    <div class="container site-header__inner">
      <a class="site-header__brand" href="#top">CSS3 工程化示例</a>

      <nav class="site-nav" aria-label="主导航">
        <ul class="site-nav__list">
          <li><a class="site-nav__link site-nav__link--active" href="#components" aria-current="page">组件</a></li>
          <li><a class="site-nav__link" href="#form">表单</a></li>
          <li><a class="site-nav__link" href="#perf">性能</a></li>
        </ul>
      </nav>

      <button class="theme-toggle" type="button" aria-pressed="false" aria-label="切换到深色主题">
        <svg class="theme-toggle__icon theme-toggle__icon--moon" width="20" height="20"
             viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
             stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
          <path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z" />
        </svg>
        <svg class="theme-toggle__icon theme-toggle__icon--sun" width="20" height="20"
             viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
             stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
          <circle cx="12" cy="12" r="4" />
          <path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
        </svg>
      </button>
    </div>
  </header>

  <!-- ================= 主内容 ================= -->
  <main class="page container" id="main">

    <!-- ---------- 1. 按钮:BEM ---------- -->
    <section class="section" id="components" aria-labelledby="components-title">
      <h2 class="section__title" id="components-title">按钮:BEM 命名 + CSS 变量修饰符</h2>
      <p class="section__desc">
        基础块 <code>.btn</code> 只暴露三个局部变量
        <code>--btn-bg</code> / <code>--btn-fg</code> / <code>--btn-border</code>,
        修饰符 <code>.btn--primary</code>、<code>.btn--ghost</code> 仅覆盖变量,
        组件内部规则零改动,天然可扩展。
      </p>

      <div class="stack">
        <button class="btn btn--primary" type="button">主要按钮</button>
        <button class="btn" type="button">次要按钮</button>
        <button class="btn btn--ghost" type="button">幽灵按钮</button>
        <button class="btn btn--primary btn--lg" type="button">大号按钮</button>
        <button class="btn btn--primary" type="button" disabled>禁用状态</button>
      </div>
    </section>

    <!-- ---------- 2. 卡片:模块化 + 修饰符 ---------- -->
    <section class="section" aria-labelledby="cards-title">
      <h2 class="section__title" id="cards-title">卡片:模块化布局与状态修饰符</h2>
      <p class="section__desc">
        卡片是独立 Block,不依赖父级选择器;<code>.card--featured</code> 表达“推荐”状态;
        <code>.card</code> 上的 <code>contain: layout paint</code> 将重排重绘限制在卡片内部。
      </p>

      <div class="grid">
        <article class="card">
          <span class="card__badge">基础</span>
          <h3 class="card__title">标准卡片</h3>
          <p class="card__text">
            使用语义令牌着色,切换主题时无需改动任何组件代码,只换令牌即可。
          </p>
          <div class="card__footer">
            <button class="btn" type="button">查看详情</button>
          </div>
        </article>

        <article class="card card--featured">
          <span class="card__badge">推荐</span>
          <h3 class="card__title">推荐卡片</h3>
          <p class="card__text">
            修饰符只负责“状态”,不重复定义布局与排版,避免样式漂移。
          </p>
          <div class="card__footer">
            <button class="btn btn--primary" type="button">立即使用</button>
          </div>
        </article>

        <article class="card">
          <span class="card__badge">性能</span>
          <h3 class="card__title">合成层动画</h3>
          <p class="card__text">
            悬停只改 <code>transform</code> 与 <code>box-shadow</code>,
            不触碰 <code>width</code> / <code>top</code> 等触发重排的属性。
          </p>
          <div class="card__footer">
            <button class="btn" type="button">了解详情</button>
          </div>
        </article>
      </div>
    </section>

    <!-- ---------- 3. 表单:可访问性与语义化 ---------- -->
    <section class="section" id="form" aria-labelledby="form-title">
      <h2 class="section__title" id="form-title">表单:语义化标签与可访问性</h2>
      <p class="section__desc">
        <code>label</code> 通过 <code>for</code> 显式绑定;错误提示用
        <code>aria-describedby</code> 关联、用 <code>aria-invalid</code> 标记状态;
        错误信息同时具备图标与文字,不只依赖颜色。
      </p>

      <form class="form" novalidate>
        <div class="field">
          <label class="field__label" for="email">
            邮箱地址 <span aria-hidden="true">*</span>
          </label>
          <input
            class="field__input"
            id="email"
            name="email"
            type="email"
            required
            autocomplete="email"
            placeholder="you@example.com"
            aria-describedby="email-hint" />
          <p class="field__hint" id="email-hint">用于接收通知,不会公开显示。</p>
        </div>

        <div class="field field--invalid">
          <label class="field__label" for="username">
            用户名 <span aria-hidden="true">*</span>
          </label>
          <input
            class="field__input"
            id="username"
            name="username"
            type="text"
            required
            value="a"
            aria-invalid="true"
            aria-describedby="username-error" />
          <p class="field__error" id="username-error">
            <svg class="icon" width="16" height="16" viewBox="0 0 24 24"
                 fill="none" stroke="currentColor" stroke-width="2"
                 stroke-linecap="round" stroke-linejoin="round"
                 aria-hidden="true" focusable="false">
              <circle cx="12" cy="12" r="10" />
              <path d="M12 8v4M12 16h.01" />
            </svg>
            用户名至少需要 3 个字符
          </p>
        </div>

        <div>
          <button class="btn btn--primary btn--lg" type="submit">提交表单</button>
        </div>
      </form>
    </section>

    <!-- ---------- 4. 性能 ---------- -->
    <section class="section" id="perf" aria-labelledby="perf-title">
      <h2 class="section__title" id="perf-title">性能:content-visibility 与 contain</h2>
      <p class="section__desc">
        下面每个列表项都带有 <code>content-visibility: auto</code> 与
        <code>contain-intrinsic-size</code>,视口外的元素会被浏览器跳过渲染,
        长列表首屏渲染成本显著降低。
      </p>

      <ul class="task-list">
        <li class="task-list__item">01 · 使用 <code>transform</code> / <code>opacity</code> 做动画</li>
        <li class="task-list__item">02 · 避免深层嵌套选择器,降低匹配成本</li>
        <li class="task-list__item">03 · 关键 CSS 内联,非关键 CSS 延迟加载</li>
        <li class="task-list__item">04 · 字体使用 <code>font-display: swap</code></li>
        <li class="task-list__item">05 · 图片设置宽高,减少 CLS</li>
        <li class="task-list__item">06 · <code>will-change</code> 慎用,用完即撤</li>
        <li class="task-list__item">07 · 用 <code>@layer</code> 管理优先级,减少 <code>!important</code></li>
        <li class="task-list__item">08 · 拆分公共样式与页面样式,控制单文件体积</li>
        <li class="task-list__item">09 · 用 Stylelint + Prettier 保证团队一致性</li>
        <li class="task-list__item">10 · 用 PostCSS + Autoprefixer 处理浏览器前缀</li>
        <li class="task-list__item">11 · 用 browserslist 明确目标浏览器范围</li>
        <li class="task-list__item">12 · 用 <code>@supports</code> 做渐进增强与降级</li>
      </ul>
    </section>
  </main>

  <!-- ================= 页脚 ================= -->
  <footer class="site-footer">
    <div class="container">
      <p>
        覆盖内容:BEM 命名 · 代码组织与模块化 · CSS 变量与主题切换 · reset / normalize ·
        Autoprefixer 兼容策略 · 渲染性能优化 · 可访问性与语义化
      </p>
    </div>
  </footer>

  <!-- ================= 交互脚本:主题切换 ================= -->
  <script>
    (function () {
      var root = document.documentElement;
      var STORAGE_KEY = 'demo-theme';
      var mq = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;

      function applyTheme(theme) {
        root.setAttribute('data-theme', theme);

        var btn = document.querySelector('.theme-toggle');
        if (!btn) return;

        var isDark = theme === 'dark';
        btn.setAttribute('aria-pressed', String(isDark));
        btn.setAttribute('aria-label', isDark ? '切换到浅色主题' : '切换到深色主题');
      }

      function readSaved() {
        try { return localStorage.getItem(STORAGE_KEY); } catch (e) { return null; }
      }

      function save(theme) {
        try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) { /* 隐私模式忽略 */ }
      }

      // 同步按钮的无障碍属性(主题本身已在 <head> 中提前应用)
      applyTheme(root.getAttribute('data-theme') || 'light');

      // 用户未手动选择时,跟随系统主题变化
      if (mq) {
        var onSystemChange = function (e) {
          if (!readSaved()) applyTheme(e.matches ? 'dark' : 'light');
        };
        if (mq.addEventListener) {
          mq.addEventListener('change', onSystemChange);
        } else if (mq.addListener) {
          mq.addListener(onSystemChange);   // 兼容旧版 Safari / Edge
        }
      }

      // 手动切换
      document.addEventListener('click', function (e) {
        var btn = e.target.closest ? e.target.closest('.theme-toggle') : null;
        if (!btn) return;

        var next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
        applyTheme(next);
        save(next);
      });
    })();
  </script>
</body>
</html>

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/caoli201314/article/details/170145889

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--