Skip to content

Releases: Tsdsj/liquid-glass-react

Release list

v0.0.2

Choose a tag to compare

@github-actions github-actions released this 22 Sep 20:25

两件事凑成了这一版:把 0.0.3 的桌面计划提前做完,以及一个外部使用者装上这个包、照着文档搭了一个报销审批控制台之后报回来的一串缺陷。后者有个共同点——没有一个是从库内部看得见的:要么是两个组件碰面的地方,要么是一个只在没人看演示的那个平台上才错的默认值。

升级前先看这两条:

  • TabBarItem.href 改成可选之后,onSelect 的事件类型变成 MouseEvent<HTMLAnchorElement | HTMLButtonElement>。如果你手写过 MouseEvent<HTMLAnchorElement> 的标注,会编译不过,改成新类型或者去掉标注即可。这是从 0.0.1 升上来唯一需要动手的地方。
  • 有几处外观和行为会变:GlassProvider 现在把解析出的主题写到 <html>(你自己补过这段的话可以删了)、SearchField 在桌面上从 24px 变成 36px、轻提示会让开 Screen 的底栏、玻璃里的列表不再画自己的底色。如果你为这些写过绕过代码,现在应该删掉——留着会叠加。

修复(theme="system" 下,内容层不跟随系统深色)

README 首页的写法就是 theme="system"。系统是深色时,栏和按钮变深,卡片、列表、表单留在浅色——黑字白卡压在深色页面上。

深色取值只挂在 [data-lg-theme="dark"] 上,整份样式表里没有一条 prefers-color-scheme——这是故意的,主题是按元素声明的,浅色页面里才放得下一条深色工具栏。但 GlassProvider 只把主题写在玻璃表面上,从不写到 <html>,于是页面本身成了唯一没人声明主题的那个表面。现在最外层的 Provider 也写根节点;每个表面仍然各声明各的。

顺带:[data-lg-theme] 现在同时设 color-scheme,深色表面里的滚动条、光标和原生控件跟着变。

修复(SearchField 在桌面上只有 24px 高)

它拿 --lg-hit-min 当自己的高度——那是 WCAG 2.2 给命中区的下限(指针 24px),不是控件高。旁边的 TextField 用的是 --lg-height-comfortable,36px。17px 的字塞进 24px 的胶囊里像被裁了。触摸档两个数都是 44,所以这个错只在有鼠标的时候看得见。

修复(Screen 给底部标签栏留的空间少 36–56px)

.lg-tabbar 无条件 position: fixed。放进 Screen 的 bottom 插槽后它被定位了两遍、为home指示条留白两遍,而且——因为脱离了文档流——插槽量出来是 0。Screen 于是给一条 60px 的栏留了 24px,每一页最后一截都压在栏底下。现在插槽里的标签栏由插槽定位;宽屏下它变成侧栏的那一档不受影响。

修复(轻提示整块盖住标签栏,把主导航点没了)

.lg-toast-region 钉死在视口底边,不认 Screen 已经发布出来的栏高。提示和标签栏落在同一排像素上,而提示是 pointer-events: auto——每弹一次提示,整条主导航有几秒点不动;而把鼠标移向标签栏这个动作本身会悬停到提示上,计时暂停,它连自己走都不走。现在 Screen 把量到的栏高也发布到文档根上,提示读它——轻提示是渲染在 portal 里的,根节点是它唯一够得着的地方。

修复(ListRow 在窄栏里把标题压成一列单字)

除了标题,每个槽位都是 flex: 0 0 auto。800px 窗口下三栏分栏自己分出来的 193px 内容栏里,前导图标、金额、徽标和箭头吃掉全部宽度,标题分到 41px,overflow-wrap: anywhere 把它排成一列单字,行高 598px。现在 400px 以下(按列表自己的宽度算,不是窗口)value 换到第二行。规则本身值得写下来:装饰不压过它装饰的那个内容。

修复(同时有 hint 和 error 时,aria-describedby 指向不存在的节点)

出错时提示文字被从 DOM 里拿掉了,id 还留在 aria-describedby 里。悬空的 IDREF 在各家读屏下行为不一致;而且它拿走的正是「怎么才算填对」那句——用户最需要它的时刻。现在两句一起留。

修复(命令面板重开会恢复上次的高亮行)

关闭时只重置了输入内容。命令列表没变,按列表内容触发的那个重置就不会跑,于是面板带着空输入框和上次那一行回来——⌘K 然后 Enter 跑的不是第一行。文档写的是「它不记任何东西」,现在是了。

修复(换品牌色,焦点环不跟着走)

docs/api.md 的表里写着焦点环由 --lg-accent 驱动,代码里是 --lg-focus: var(--lg-blue),写死。默认品牌色就是系统蓝,所以只有换了品牌色的人会撞上——然后全站焦点环还是 iOS 蓝。

改进

  • TabBarItem.href 改为可选:没有 URL 的区段渲染成 <button>,不用再编一个假地址再 preventDefault。onSelect 的事件类型相应变成 HTMLAnchorElement | HTMLButtonElement——如果你手写过 MouseEvent<HTMLAnchorElement> 的标注,需要跟着改。
  • PaletteCommand.keywords 接受数组,不再只有空格分隔的单串。
  • FormSection 有了 headingLevel,和 ListSection 一样。之前一个 h1 页题下面的四个表单分组只能是 h3,中间跳一级。
  • tint 传给用不上它的变体时,开发模式会出声。tint 会在按钮上写下 --lg-accent 和 --lg-accent-fill,但只有 glassProminent / primary / tinted / plain 会读它——传给默认的 glass,按钮和没传时长得一模一样,而且原先一声不吭。

修复(列表放进玻璃里会砸出一个方角黑块)

把导航列表放进 Sidebar,材质上会出现一个不透明的黑色方块。两个变体都声明了不透明底色——这在页面上是对的——而 plain 声明的是 --lg-bg,页面底色,深色下是纯黑。于是它既比所在的面板更黑,又用 0 圆角顶着面板自己的 26px 圆角。

内容块去重画它所在的那个表面,和玻璃套玻璃是同一个错误的两面。现在玻璃表面里的列表不画底色,圆角由容器算出来——用的是 Card 和 Concentric 本来就在用的 --lg-radius-container / --lg-concentric-inset,所以给 Sidebar 传了自己的 radius 它也跟得上。GlassSheet 和 GlassAlert 不在此列:它们是被模态呈现的一页,满屏时本来就是实色。

顺带,--lg-radius-container 现在由每一块玻璃发布,所以 Concentric 放进玻璃里和放进 Card 里一样好使。

新增(ListRow 的 selected)

split-views.md 要求「每一栏都要持续高亮当前选中项」,而 ListRow 此前没有任何办法说出这件事——没有东西发出 aria-current,样式表也只给标签链接和路径层级画过 [aria-current]。一个当作分栏中间列用的列表,看不出选中的是哪一条。(本站自己的侧栏示例因此一直是手写的 <a> 加自定义类名;现在换回了真组件。)

selected 落成 aria-current:链接是 "page",按钮是 "true"。颜色和 OutlineView 的选中透镜同一对——同一个界面里不该有两种「选中」——强制配色下换成系统的 Highlight / HighlightText。

文档

  • docs/api.md 第 5 行教人引入 styles.css,这个路径不存在,照抄会得到 ERR_PACKAGE_PATH_NOT_EXPORTED。正确的是 style.css,而且引它一个就够了。
  • SplitView 在紧凑档只渲染 sidebar 和 compact,children 和 inspector 不在 DOM 里。之前任何一处文档都没有写。
  • ListRow 的箭头是自动出现的(能点的行有,带 accessory 的行没有),disclosure 是用来推翻这个默认的。之前只有 .d.ts 的 TSDoc 里有。
  • 「不要让主题闪一下」那节原来的说法暗示 React 后面会写这个属性——它当时不会。现在会了,那段 head 脚本的作用改回它本来的样子:把同一件事提前到首帧。

新增(强调色拆成品牌色和底色)

--lg-accent 配 --lg-accent-contrast(白)量出来是浅色 3.52:1、深色 3.23:1——过大号文字的 3:1,过不了 4.5,而这一对被画在 15px 标签底下的地方不止按钮一处。任何还认得出是这个蓝的颜色配白字都到不了 4.5,所以拆:

  • --lg-accent 仍然是品牌色,图形用它——焦点环、滑块已填的轨道、单选点、进度条。图形只需要 3:1。
  • --lg-accent-fill 是白字压在上面的那块底色,由品牌色压暗 82% 得到,白字量到 4.98:1。--lg-red-fill 同理:通知徽标和危险主操作从 3.57 到 5.1。

两个都在 :root 上由 --lg-accent / --lg-red 算出来,所以改掉品牌色,底色自动跟着变。GlassButton 的 tint 是唯一同时设这两个、并且设成同一个值的地方:传 tint 的人已经指定了颜色和压在上面的文字色,开发模式量的也正是这一对,再压暗一次就是画一个没人要的颜色。

改动可见的地方:主操作按钮、强调色与通知徽标、危险主操作、菜单栏打开着的标题、复选框与单选钮的选中态、大纲视图的选中高亮。

修复(按住弹层里的滑块,旋钮会消失)

按下去那一刻旋钮不见了。「按住」的规则是抬起来变成玻璃——background: transparent,让底下的玻璃层透出来——而共享表面上的控件已经把自己那层玻璃交出去了,渲染出来是平的,压根没有那几层。填色被拿走,手交给了一层从来没被画出来的东西。

报的是 Panel,但气泡、底部面板、工具栏分组里全都一样。玻璃叠玻璃仍然不行,所以按住改用剩下的两样说:留住填色,放大,抬起。

修复(一打开,边缘就是高亮的)

命令面板一开,整块面板就戴着强调色的环,在任何人碰它之前;对话框里的输入框同理。输入框不管焦点怎么来的都匹配 :focus-visible,规范就是这么定的——可当焦点是被塞给读者的时候,这个环什么也没说。

现在浮层开的时候会记住焦点是它给的,读者第一次真的移动焦点(Tab、点别处)才把环点亮。在那之前说明「打字落在哪」的是光标。

顺带修掉一条更重的:命令面板里按 Tab,焦点会掉到页面 <body> 上——面板里只有一个焦点位,而模态对话框外面全是 inert,Tab 走到头就走出去了。环灭了,键也不进输入框,只能用鼠标救回来。现在 Tab 留在原地,并把环点亮。

修复(颜色被当成墨水用)

按 Apple-Style-Review 的清单把 66 个组件全部重过一遍:62 个页面 × 六种状态(浅色、深色、390、最大字号、从右到左、强制颜色)× 十个问题。整轮的过程和读数在 reports/hig-review.md。

  • tone="accent" 3.03:1、tone="destructive" 3.14:1,打开「增强对比度」也只到 3.94 / 4.01。系统色是底色,不是墨水——这条规则连同它的解法(--lg-ink-toward / --lg-ink-amount)早就写在 tokens.css 里,但只用在了 variant="tinted" 一个控件上。现在 Text 的两种色调、variant="plain" 和 variant="destructive" 的标签、菜单和操作表里的危险项、页面栈的返回按钮,全都走同一条配方,浅色下量到 5.9–6.0:1。一条只在写它的那个控件上成立的规则,还不是规则。
  • 窄窗口配鼠标时,小图标按钮的命中区是 32×44。 --lg-hit-min 问的是窗口宽度,--lg-hit-width 问的是指针种类,于是同一个控件的两条轴拿到了两个下限——而横幅页上印着「关闭按钮有 44×44 的点击范围」。两条现在问同一个问题,并且问的是 any-pointer: coarse:带触摸屏的笔记本和带触控板的平板都答得对,窗口宽度只继续决定控件高度。
  • 页码点的间距差 2px。 点是 18px,比一个目标该有的 24px 小,救它的是 WCAG 2.2 的间距豁免——而豁免是算术:以每个点为心画 24px 的圆,不能碰到邻居的。原来中心距 22px,圆是相交的,所以豁免不成立。现在 24px。
  • 路径栏上两个字的一级只有 42px 宽,手指够不着。有 min-height 没有 min-width。
  • GroupBox / OutlineView / Form 等处的无障碍说明写成了属性名。 文案那一轮一起改了,见下。

修复(文档站:能读的字,和人话)

  • 代码块的语法配色在浅色下读不了:字符串 1.95:1、属性 1.90:1、注释 2.37:1、标签 3.10:1、关键字 3.67:1。原因和上面同一条——直接拿系统调色板当了墨水。现在浅色是同样五种色相压暗到过线的版本(5.0–6.5:1),深色仍然用系统色,因为那里本来就够。
  • 页脚、左栏分组标题、「本页内容」用的是 tertiary,2.32:1。库自己的文本页写着 tertiary 是占位符和停用态的颜色,站点却拿它写了要读的字。
  • 正文次级灰在站点画布上 4.47:1:那个灰的透明度是按库的分组背景调到刚好过 4.5 的,而站点画布比它暗。画布调亮五个单位。
  • 首页图上的两行字直接压在插画上,「FIELD NOTES」3.28:1、右下角脚注 4.34:1。文字阴影让它们看起来能读,量出来是零;改成一层压暗的底片之后是 5.6 和 7.4。
  • 示例卡片的标题写在示例后面,于是自带标题的示例(文本、列表、导航栏、页面栈)把页面的标题层级从 h2 跳到了 h4。标题现在写在前面,用 order 摆到下面。
  • menu-button 页上一个 id 出现了两次,大纲里指向那个示例的锚点有两个落点。
  • 全站文案重写:把「aria-describedby」「role="tree"」「content-visibility: hidden」「auto-fill + minmax」「可辨识联合」这类实现术语,换成它们对使用者意味着什么;把几段解释「我们当初为什么这么定」的长注脚压成一句话;去掉「排在 0.3.0」这类只有维护者关心的说法;引号统一。

修复

外观这一组是在真实屏幕上看出来的,不是推出来的。每一条都换算成了一个可以量的数,tests/browser/appearance.spec.ts 守住它们。

  • 深色下指针高光太亮。玻璃的光晕和按压闪光是白光,而白光落在什么上,结果完全不同:浅色面板上 26% 是一层柔和的反光,深色面板上就是整块板被点亮,指针走到哪里拖着一团亮斑。现在这两个强度是按外观分开的 token,深色约为浅色的三分之一。

  • 浅色白得刺眼,很多文字看不清。顶层表面原来是满亮度的纯白——一块 #fff 面板在亮屏上就是整面板在反光,而它上面的分组背景为了能被区分还得更暗。现在顶层降三个点(仍然是系统里最亮的那一层,「白」本来也只是这个意思),分组背景跟着落一档。次级文字的灰从 60% 提到 74%:不是拍脑袋,那是这个灰压在分组表面上测出来刚过 4.5:1 的位置,用例量的是合成后的那一对而不是 token 本身——只看 token 正是「它过了」和「你读不清」同时成立的原因。

  • 折叠区没有高度动画。组件一直在量内容高度并写出 --lg-disclosure-height,而没有任何一条规则读它:箭头转了,内容一帧就出来了。现在用 interpolate-size + ::details-content 让浏览器做这件事,量出来的高度是它的兜底目标。

  • 分段控件和标签页看不出选中哪一个。白色胶囊压在白色玻璃轨道上——实测选中项与周围只差 5/255,加阴影再多三个点。光靠影子修不好:轨道得是凹的。现在轨道是一道压暗的凹槽,胶囊从里面抬起来,实测差值 20/255。这也是系统自己的画法:灰色沟槽 + 抬起的白色滑块。

  • 步进器按住不会连续加减,也没有 Shift 大步。从 1 到 40 只能点 40 次,而那正是用户放弃、改去找一个并不存在的输入框的时刻。现在按住 0.4 秒后每 90ms 一步,松手、指针取消、到达上下限都会停;Shift + 点击走 10 倍。

  • 轻提示只能等它自己消失。没有关闭按钮,不理 Escape——唯一的办法是等六秒,或者把指针悬在上面暂停它。键盘用户两样都没有。现在有关闭按钮,Escape 关最新一条。

  • 徽标在增强对比度下没有边框。白字压红色本身是 4.6:1、过 AA,所以这是一致性问题:其他表面在这个设置下都会长出边线,唯独它没有,看起来像被这个设置漏掉了。

  • RTL 下所有浮层都对错了边。align 写的是 start / end(前缘 / 后缘),锚定代码算的却是 rect.right,不看方向——实测 dir="rtl" 下触发器左缘 556,菜单左缘 432:对齐的是物理右边,而那在 RTL 里是前缘。菜单、气泡、操作表全中。现在按触发器自己的 direction 解析,所以 LTR 页面里的一小块 RTL 子树也对。

  • 面板只有顶部那条横条能拖。系统的面板是整块可拖的,判据是滚动位置而不是碰到了哪个元素。实测从内容区拖 8 步,--lg-sheet-offset 50% → 50%,纹丝不动。现在内容滚到顶时往下拖就是收起,往上拖在还有更高一档时长高、到顶了就让位给滚动;横向拖不接管;方向定下来之前不 preventDefault,所以面板里的按钮照常能按。

  • 气泡面板没有箭头,在手机上也不变形。HIG 的 popovers 页要求箭头尽量直指打开它的元素,样式表里一处都没有;同一页还说手机上应该换成面板,组件自己的注释也这么写着,然后在任何宽度都渲染一块 300px 的锚定面板。现在箭头贴在朝向触发器的那条边上、跟着触发器中心滑;紧凑尺寸下换成通栏贴底的面板,箭头一并去掉——钉在屏幕底部之后它已经没有东西可指了。

  • 分栏视图会溢出它自己的盒子。栏宽用的是刚性 flex 基准,容器比「侧栏 + 检查器」还窄时,多出来的部分直接从盒子里漫出去——实测 320px 的容器里溢出 161px,而它的父级完全不知道这些栏存在。这是布局容器唯一不能做的事。现在栏可以收缩,内容栏先让。

  • 分栏视图会报出一个没有人拥有的宽度。视图比自己声明的最大值还窄时,按 End 把 aria-valuenow 设成 400,画出来是 304。读屏用户听到的是一个不存在的数字。现在范围会被「实际有多少地方」二次收紧,aria-valuemax、aria-valuenow 和画面永远是同一个数。

  • 标签栏在最大字号下把整份文档推歪。AX5 下 390px 屏幕上四个标签是 400px 的文字,而标签栏是 inset-inline: 0——文档横移 69px。Dynamic Type 要的是重排,所以标签行现在会换行:只把栏宽封顶的话,标签会被挤成一列一个字,而那正好挤给了要求大字号的那些人。

  • 页面栈的压栈动画把自己撑宽。进入的那一页从侧面 16px 滑入,期间栈比自己宽 16px;一个满屏的栈会让页面每次导航都闪一次横向滚动条。现在这段位移被收在栈内。

  • 搜索建议的高亮会被调用方的重渲染清掉。「列表变了就清掉高亮」这个 effect 依赖的是 suggestions 数组的引用,而筛选归调用方,所以它几乎总是每次渲染新建——按下 ↓、高亮出现、下一帧换成新数组、effect 又把它清了。是一场竞态,所以它看起来大部分时候是好的。改为依赖列表的内容。

  • 颜色快捷色之间只有 4px,低于「控件之间至少 8pt」,两个焦点环会贴在一起。

  • disabled 的跳转行照样能跳走。ListRow 在 disabled 时仍渲染 <a href>,只多加一个 aria-disabled——回车、点击、Tab 停靠全都不受影响。aria-disabled 是播报,不是实现。现在改渲染 <button disabled>。

  • 37 个会渲染元素的组件里,25 个拿不到 ref,其中大半还只收 className。这是在真实项目里第一天就会撞上的墙:量不了尺寸、ScrollEdge.targetRef 指不到自己的列表、第三方库锚不上去、给 <label for> 设不了 id。现在每个组件都交还它渲染的那个元素,并透传 HTML 属性;TextField 和 SearchField 的 ref 落在 <input> 上,因为那才是你要聚焦、要读值的东西。四个纯 context 的 Provider 没有 ref,这是写下来的决定,不是漏掉的。

  • TabBar 的 ref 落错了元素:它混在被当成玻璃选项的 rest 里,被透传到内层第一个 GlassSurface,而不是整条 <nav>。

  • SearchField 收了 id 又用自己生成的覆盖掉,<label for> 和 aria-controls 指不过去。

  • 开发告警一直在生产环境里响。守卫读的是 globalThis.process?.env——打包器只替换字面量 process.env.NODE_ENV,所以浏览器产...

Read more