跳到主要内容

参考约 19 分钟

支持的样式

完整属性清单、颜色文法、渐变与阴影写法,以及 hover / active / focusVisible 的原生行为。

通过 style 属性做类 CSS 的样式设置:

<div style={{
  display: 'flex',
  flexDirection: 'column',
  gap: 8,
  padding: 16,
  backgroundColor: '#3b82f6',
  borderRadius: 8,
}}>
  <div style={{ color: '#ffffff', fontSize: 18 }}>
    Hello GPUI!
  </div>
</div>

属性总览

布局: display("flex" | "grid")、flexDirection、flexWrap、flexGrow、flexShrink、flexBasis、alignItems、alignSelf、alignContent、justifyContent、gap、rowGap、columnGap、gridTemplateColumns、gridTemplateRows、gridColumnMin、gridRowMin

尺寸: width、height、minWidth、minHeight、maxWidth、maxHeight — 接受像素(数字)或百分比(如 "100%" 这样的字符串)

间距: padding、paddingTop/Right/Bottom/Left、margin、marginTop/Right/Bottom/Left

定位: position("relative" | "absolute" | "fixed")、top、right、bottom、left — "fixed" 的布局方式同 "absolute",因为 GPUI 没有可固定其上的滚动文档

视觉: background、backgroundColor、color、opacity、cursor、pointerEvents、borderRadius、borderTopLeftRadius、borderTopRightRadius、borderBottomLeftRadius、borderBottomRightRadius、borderWidth、borderTopWidth、borderRightWidth、borderBottomWidth、borderLeftWidth、borderColor、boxShadow、outlineWidth、outlineColor、outlineOffset

溢出: overflow、overflowX、overflowY — "hidden" 裁剪内容,"scroll" 创建一个带有持久滚动状态的原生可滚动容器

文本: fontSize、fontFamily、fontWeight、textAlign、lineHeight、whiteSpace、textOverflow、lineClamp、textDecoration("underline" | "line-through" | "none")

选区: userSelect("text" | "none")、selectionColor — 两者都会沿树向下继承

焦点: focusVisible

光标

cursor 接受 CSS 关键字。未列出的关键字会被忽略,就像其它无效的样式值一样。

分组 关键字
指向 default、auto、pointer、context-menu、not-allowed、no-drop
文本 text、vertical-text、crosshair
拖拽 grab、grabbing、move、all-scroll、alias、copy
缩放 col-resize、row-resize、ew-resize、ns-resize、nwse-resize、nesw-resize、n-resize、e-resize、s-resize、w-resize、ne-resize、nw-resize、se-resize、sw-resize
<div style={{ cursor: 'grab', active: { cursor: 'grabbing' } }} />
<div style={{ cursor: 'col-resize' }} />

颜色

所有带颜色的样式字段都接受同一套字符串文法。GPUIX 原生使用 csscolorparser 0.8.3,接受:

  • 具名颜色与 transparent;
  • 3/4/6/8 位十六进制,带不带 # 均可;
  • rgb() / rgba()、hsl() / hsla()、hwb() / hwba(),以及 hsv() / hsva();
  • lab()、lch()、oklab(),以及 oklch();
  • none 分量,以及解析器有限的相对颜色 from / calc() 形式。

标准逗号写法与现代空格/斜杠 alpha 写法都可用。GPUI 绘制前会把值转换为硬裁剪的 sRGB。无效字符串只会被该属性忽略,不会拒绝整个样式对象。

线性渐变

background 接受 GPUI 原生的两色标线性渐变。角度遵循 CSS 规则:0 指向上方,数值顺时针增大。色标位置用 0 到 1。

<div
  style={{
    background: {
      type: 'linear-gradient',
      angle: 90,
      stops: [
        { color: '#7c3aed', position: 0 },
        { color: '#06b6d4', position: 1 },
      ],
      colorSpace: 'oklab',
    },
    borderRadius: 12,
  }}
/>

colorSpace 可选,默认为 "srgb"。GPUI 也支持 "oklab"。它不支持径向、锥形、重复渐变,也不支持超过两个色标的渐变。

hsv()、hsva() 与 hwba() 是解析器的扩展,而非 CSS Color 4 标准函数。color()、平台/动态颜色,以及数值形式的颜色整数均不被接受。

现代颜色语法

主题值可以使用同样的现代文法:

const theme = {
  surface: 'oklch(18% 0.02 260)',
  accent: 'oklch(67.3% 0.182 276.935)',
  text: 'oklch(96% 0 0)',
}

<div style={{ backgroundColor: theme.surface, borderColor: theme.accent }}>
  <text style={{ color: theme.text }}>Hello GPUI!</text>
</div>

有限的相对颜色形式可以从一个基准值派生出新颜色:

<div
  style={{
    backgroundColor: '#bad455',
    borderColor: 'oklch(from #bad455 calc(l - 0.15) calc(c * 0.7) h)',
  }}
/>

阴影

boxShadow 接受单个结构化阴影。其字段为 offsetX、offsetY、blurRadius、spreadRadius 与 color:

<div
  style={{
    boxShadow: {
      offsetX: 0,
      offsetY: 4,
      blurRadius: 12,
      spreadRadius: 0,
      color: '#00000033',
    },
  }}
/>

悬停与激活

hover 与 active 是嵌套的样式对象。当指针悬停在元素上或鼠标按下时,GPUI 会以原生方式应用它们,没有 JavaScript 往返。

<div
  style={{
    backgroundColor: '#313244',
    borderRadius: 8,
    padding: 12,
    hover: { backgroundColor: '#45475a' },
    active: { backgroundColor: '#585b70' },
  }}
>
  Press
</div>

嵌套只有一层深。hover 对象中不能再包含另一个 hover 或 active。

它们对所有元素都有效,包括 <text>、<code>、<markdown>、<diff>、<img>、<svg> 以及编辑器。唯一的例外是 <virtual-list>,它的 style 类型不接受它们:gpui 的列表没有可持有悬停或按下状态的交互身份,所以请把它们放在包裹用的 <div> 上。

焦点样式

focusVisible 是一个嵌套样式对象,类似 hover。它在元素拥有焦点且最后一次输入来自键盘时应用,类似 CSS 的 :focus-visible。GPUI 会以原生方式应用它。

鼠标按下永远不会显示它,文本字段也一样。没有单独的 focus 键。

<div
  tabIndex={0}
  style={{
    borderRadius: 8,
    backgroundColor: '#313244',
    focusVisible: { outlineWidth: 2, outlineColor: '#89b4fa', outlineOffset: 2 },
  }}
/>

它需要一个可聚焦的元素:tabIndex、某个键或焦点监听器、<input>、<textarea>,或 Button 之类的原语。

默认行为:其余一切变暗

GPUIX 不画任何环。当某个控件(Button、设置了 tabIndex 的 div)拥有键盘焦点时,每一个其它可聚焦元素都会以 40% 的不透明度渲染。被聚焦的那个保持原样,于是你能一眼看清 Tab 能到达的所有元素。

Tab  ► [ 保存 ]  (新建变暗)  (搜索变暗)  (输入框变暗)
  • 鼠标按下、拖拽,或鼠标移动超过 8px 会结束变暗。手搭在触控板上产生的更小抖动则保留。
  • 聚焦的 <input> 或 <textarea> 不会让任何元素变暗:打字同样属于键盘输入,且光标已经表明了焦点。
  • 被聚焦元素的祖先永远不会变暗,因为不透明度会覆盖整棵子树。
  • focusVisible 与变暗是相互独立的。一个从 focusVisible 获得环的元素,在另一个控件拥有焦点时仍会变暗。
  • style.keyboardFocusDim: false 会让单个元素保持完全不透明。Select.Content 和 Dialog.Popup 会设置它。
<div tabIndex={0} style={{
  focusVisible: { outlineWidth: 2, outlineColor: '#89b4fa' }, // 获得焦点时
}} />                                                         // 否则变暗
<div tabIndex={0} style={{ keyboardFocusDim: false }} />      // 永不变暗

对整个窗口关闭变暗。 向 render()(或 createTestRoot())传入 keyboardFocusDim: false,然后用 focusVisible 自己设置焦点样式。

render(<App />, { keyboardFocusDim: false })

是描边(outline)不是边框(border)。 outlineWidth、outlineColor 与 outlineOffset 在边框盒外侧画线,类似 CSS 的 outline。它不占布局空间,所以用 focusVisible 加的环不会移动任何东西。负的 offset 会把它画在内侧。它遵循 borderRadius。设置了 overflow: "hidden" 的父级会裁剪它,就像在浏览器里一样。

两处文本注意事项