Red Dot Tracker

facet

facet 是一套为一组网站共用的设计语言:用纯 CSS 和少量原生 JavaScript 写成的设计令牌、组件和版式,不需要构建步骤。每个网站只挑选自己的强调色,其余完全一致。

版本 0.1.3 · MIT 许可证

这里有什么

安装:CDN

把样式表和脚本链接到页面里。先加载 facet.css,再加载 cast/cast-layered.css(只有用到吉祥物时才需要)。社交图标要另外加载 icons.css。固定一个标签,网站就不会随主分支变化。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/h1994st/facet@v0.1.3/dist/facet.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/h1994st/facet@v0.1.3/dist/cast/cast-layered.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/h1994st/facet@v0.1.3/dist/icons.css" />
<script type="module" src="https://cdn.jsdelivr.net/gh/h1994st/facet@v0.1.3/dist/js/facet.js"></script>

jsDelivr 在中国大陆可能不稳定,字体也是相对 facet.css 加载的。面向中国大陆的网站,请改用下面的 submodule,把文件和网站放在一起。

安装:Git submodule

git submodule add https://github.com/h1994st/facet vendor/facet
git -C vendor/facet checkout v0.1.3

把 vendor/facet/dist/ 作为静态文件和网站一起发布,然后用相对路径链接。

<link rel="stylesheet" href="/vendor/facet/dist/facet.css" />
<link rel="stylesheet" href="/vendor/facet/dist/cast/cast-layered.css" />
<link rel="stylesheet" href="/vendor/facet/dist/icons.css" />
<script type="module" src="/vendor/facet/dist/js/facet.js"></script>

最小示例

<html lang="en" data-accent="moss">
  <head>
    <link rel="stylesheet" href="…/dist/facet.css" />
    <script type="module" src="…/dist/js/facet.js"></script>
  </head>
  <body>
    <nav class="f-nav">
      <span class="f-nav__brand">Duna Feline</span>
      <a href="/" aria-current="page">Blog</a>
    </nav>
    <main class="f-page f-prose">
      <h1>Hairball</h1>
      <p>Why the red dot always moves.</p>
    </main>
  </body>
</html>

行为

facet 的 JavaScript 是可选的。引入 dist/js/facet.js 就会自动初始化:页面解析完后,每个模块找到用 data-f-* 属性声明的元素并接管它们。再次调用 init() 是安全的:已经初始化过的元素会被跳过;某个模块出错只会在控制台报告,不会挡住其他模块。

属性 模块 作用
data-f-ga4 (on <html>), data-f-event, data-f-label analytics.js 可选的 Google Analytics 4:<html data-f-ga4="G-…"> 加载一次 gtag 并发送一次 config(即一次页面浏览)。浏览器发送全局隐私控制(GPC)时什么都不加载。data-f-event="name" 在点击时发送这个事件(链接带 link_url,data-f-label 作为 label);切换主题时发送 theme_change。不提供 Cookie 同意横幅。要用 GA4 的同意模式,请在 facet.js 之前用一个普通(非模块)的 <head> 脚本定义 window.dataLayer 和 gtag,并调用 gtag("consent", "default", …):analytics.js 会沿用已有的 gtag。不要再保留自己的 gtag("config", …) 代码,否则每次浏览都会被计两次。
data-f-theme-toggle theme.js 放在按钮上:在深色、浅色和自动之间切换,把选择存进 localStorage["facet-theme"],并更新按钮的文字和标签。
data-f-liquid liquid.js 放在 .f-card 或装着卡片的容器上:一道跟着鼠标在卡片上移动的光,只对鼠标生效。
data-f-scrollspy scrollspy.js 放在 .f-toc 上:给正在阅读的那一节的链接加上 aria-current="location"。
data-f-nav-cover="#cover" nav.js 放在 .f-nav--over 上:封面在屏幕上时导航保持透明,封面滚走后变成玻璃。
data-f-autoplay, data-f-autoplay-toggle autoplay.js 放在静音循环的 video 上:视频在屏幕上时播放。带 data-f-autoplay-toggle 和 aria-controls 的按钮成为它的暂停按钮(文字取自 data-f-pause 和 data-f-play)。
data-f-story story.js 放在 .f-story 上:固定画面,并显示正在阅读的那一步对应的图片(img[data-step])。
data-f-panels, data-f-panel-toggle panels.js 放在 .f-panels 上:标出停稳的面板,只播放它的视频,并填充空的 nav.f-dots。面板里带 data-f-panel-toggle 的按钮成为视频的暂停按钮。
data-f-deck, data-f-piece, data-f-slide, data-f-from, data-f-deck-prev, data-f-deck-next deck.js 放在 .f-deck 上:把幻灯片变成一个画面,画面里的元素在幻灯片之间移动(数据格式见下文)。
data-f-type typewriter.js 一组短语(JSON 数组):逐字打出、停留、清空,再打下一句,循环。元素原来的文字是静止状态,减少动效时一直保持。速度和停留时间可用 data-f-type-speed 和 data-f-type-hold(毫秒)调整。放在 aria-hidden 的包裹元素里,旁边用视觉上隐藏的文字写出真实内容。
data-f-variants, data-f-variant variants.js data-f-variants="组" 放在 .f-segmented 上,按钮写 data-variant;其父元素内每个 data-f-variant="组:值" 按所选值显示或隐藏。控件和非默认变体先写 hidden,没有 JavaScript 时只显示第一个,也不会出现无效按钮。值没有对应按钮的变体保持隐藏;没有按钮的控件被跳过、保持隐藏;初始化之后才加入的变体不会被处理(再调用一次 init)。

单独使用一个模块

每个模块都可以单独引入,导出 init(root = document),返回一个清理函数,用来移除它加上的监听器和元素。facet.js 也导出 init,以及撤销它自动初始化的 cleanup。

import { init } from "…/dist/js/deck.js";

const stop = init(document.querySelector("#talk"));
// Later, before the deck leaves the page:
stop();

避免主题闪烁

facet.js 在首次绘制之后才运行,保存的主题会晚到一步。把下面这一行放进 ,放在样式表之前:它在页面绘制前应用 localStorage["facet-theme"](只接受 dark、light 和 auto),之后主题按钮只负责显示当前主题。

<script>try{var t=localStorage.getItem("facet-theme");if(/^(dark|light|auto)$/.test(t))document.documentElement.dataset.theme=t}catch(e){}</script>

演示文稿的数据

每个 .f-deck__slide 的 data-f-slide 是一段 JSON,列出这张幻灯片上每个元素(按 data-f-piece 的名字)的状态;没有列出的元素会隐藏(光束缩回起点,再出现时重新长出来)。位置和尺寸都是画板的百分比。

元素 状态
方块 .f-deck__tile {"at": [left, top], "c": color, "lit": true}:lit 用它的颜色填满它。
光束 .f-deck__beam {"beam": [left, top, length]}:加 .f-deck__beam--v 变成竖直方向。
方框 .f-deck__shot {"box": [left, top, width, height], "fade": true}:fade 让它淡出。

元素从它第一次出现的位置开始,或者从它的 data-f-from([left, top] 或一个方框)开始。数据有错(不是 JSON,或某个字段的数字不够)时,演示文稿保持没有 JavaScript 时的样子,并在控制台里指出是哪一张。

<section
  class="f-deck__slide"
  data-f-slide='{"laser": {"at": [20, 50], "c": "var(--f-deck-ink)"},
                 "b1": {"beam": [20, 50, 60]},
                 "target": {"at": [80, 50], "c": "var(--f-accent)", "lit": true}}'
>
  <h2>In straight lines.</h2>
</section>

减少动效与没有 JavaScript