@crewlethq / artifact-kit
Crewlet将系统设计为一个无框架的浏览器包,用于人员和代理将页面作为工件发布。工件页面将它链接起来,并使用与它周围的指示板相同的标记、主题和字体进行绘制,随着阅读器的外观变化而变化,并且可以使用一个元素在其数据存储上绘制可编辑的工作表。
引擎为这个包裹服务 dist/ 目录,未更改,从工件的原始位置 /_crewlet/kit/<version>/,因此页面永远不会从第三方主机获取它,并且在封闭的网络上工作。
多好的网页链接啊
<link rel="stylesheet" href="/_crewlet/kit/<version>/kit.css">
<script src="/_crewlet/kit/<version>/theme.js"></script>
<script type="module" src="/_crewlet/kit/<version>/kit.js"></script>
<version> 是页面所针对的工具包版本(引擎的创作指南列出了它所服务的最新版本);该引擎为它所提供的每个工具包版本提供服务,因此已发布的页面保留自己的版本。链接的前两个 <head>, theme.js 作为一个经典的封锁脚本,在任何油漆之前。 kit.js 只有在绘图时才需要。 content.css,后面有链接 kit.css为页面自己编写的纯HTML设置样式(下面). 然后用符号而不是文字值写出颜色、间距和类型:
.summary {
padding: var(--spacing-3);
border-radius: var(--radius-md);
background: var(--color-surface-elevated);
color: var(--color-text-primary);
}
纯HTML内容
kit.css 绘制标记和一个安静的文档基线,它将标题和段落的边距归零,并将按钮剥离为纯文本:编写自己组件的页面恰恰需要这些。编写纯HTML(报告、摘要、表单)链接的页面 content.css as well, AFTER kit.css,以及它的标题、段落、列表、引用、代码、表格、规则、数字、披露和表单控件在每个主题中都采用设计系统的外观:
<link rel="stylesheet" href="/_crewlet/kit/<version>/kit.css">
<link rel="stylesheet" href="/_crewlet/kit/<version>/content.css">
<script src="/_crewlet/kit/<version>/theme.js"></script>
阅读栏是文档的散文(一页) h1 在标题步,在阅读步和导读步运行文本,块与块之间有一个间隙,标题上方有更多间隙);按钮是次要按钮,提交按钮是主要按钮;文本字段、文本区域和选择是中等大小的输入、文本区域和选择;一个方框、一个点、一个滑块和一个条代表重音;表的标题下有一条更牢固的规则,列中有数字,等等 data-align="right" (或 "center")对于对齐,严格的策略将样式属性排除在外。一个宽的桌子在里面滚动 <div> 或者 <figure> 它被写入而不是移动页面,并且有一长行代码封装。
它是可选择的,它是有范围的,所以它永远不会与页面上的任何其他内容相冲突:
- 没有链接它的页面是不变的,包括在它存在之前发布的每一页:它是一个独立的文件
kit.css不带任何东西。 - 它只对作者写的东西进行排版。 一、
<crewlet-sheet>,任何带a的元素crewlet-类(例如绘制在主体上的工作表的菜单)和其中的所有内容都留给工具包自己的样式表。页面的一个区域,它保留了自己选择的设计data-crewlet-content="off"里面的所有东西都保持基线。 - 页面自己的类总是获胜。 每条规则都有它的元素(
button是0 0 1,不管它的类型或状态),那么.chip { ... }在每个州都是无效的。键盘环是一个被视为伪类的规则,所以控件在聚焦时保持自己的角;重新设计它的页面.chip:focus-visible.
链接到后面 kit.css:对于相同的元素,它的每条规则都以相同的权重出现在后面,从而胜过基线的规则。
一个公司的品牌
一家公司的品牌会重新设计印有球衣标志的每一页:口音和与之混合的一切,场地,正文,面孔和标志。品牌为 输入槽, --brand-* 自定义属性,以及工具包自己的令牌读取它们,因此已经写入的页面 var(--color-brand-accent) 或者 var(--font-family-display) 遵循品牌而不改变线条。在没有输入集的情况下,每个令牌都是主题自己的值,无论是在两种模式下,还是在每个命名的主题下,所以一个链接工具包的页面没有品牌,就像它一直拥有的那样。固定在早期工具包上的页面根本没有读取槽。
该引擎将公司采用的品牌生成到自己的样式表中,并将其链接到工具包旁边。样式表定义了 只有 --brand-* 自定义属性(on) :root), @font-face 规则,没有别的:没有选择,除了 :root,没有其他属性,因此它不能重新设计套件未选择从中读取的任何内容,并且它可以在之前或之后加载 kit.css. 页面也可以自己设置插槽。
| 槽 | 价值 | 怎么读 |
|---|---|---|
--brand-primary | #rrggbb | 亮模式下的重音,暗模式下的重音 --brand-primary-on-dark 不设; --color-brand-mark. |
--brand-primary-on-dark | #rrggbb | 暗模式下的重音。 |
--brand-secondary | #rrggbb | --color-brand-highlight的第二站 --color-brand-gradient. |
--brand-text | #rrggbb | --color-text-primary 在光模式下。暗模式保留主题的文本。 |
--brand-light-ground | #rrggbb | --color-surface-frame 和 --color-surface-background 在光模式下。 |
--brand-dark-ground | #rrggbb | 同样的两个在暗模式下。 |
--brand-font-heading | 一个 font-family 列表 | --font-family-display. 如果省略了,标题就占据了身体的正面。 |
--brand-font-body | 一个 font-family 列表 | --font-family-sans. |
--brand-logo-primary, --brand-logo-on-dark, --brand-logo-mark | url(...),词根relative或absolute | 这个 crewlet-logo 类。 |
--brand-logo-primary-ratio, --brand-logo-on-dark-ratio, --brand-logo-mark-ratio | 一个数字,标志的宽度除以它的高度 | 这个 crewlet-logo 类。 |
一次输入可以在两种模式下重新设计整个家族。 从每个模式的口音,工具包混合,与 color-mix():
| 令牌 | 混合了模特的口音 |
|---|---|
--color-brand-accent-hover, -active | OKLab的90%和80%偏向黑色,暗了一步和两步 |
--color-brand-accent-ink | 文字重音:暗模式下55%偏向白色,亮模式下70%偏向黑色 |
--color-brand-accent-soft | 在黑暗模式下重音为16%,在明亮模式下重音为12.5%,无论它落在哪里 |
--color-brand-accent-soft-strong, --color-border-selected | 32%和60%的口音 |
--color-brand-accent-deep | 70%偏向黑色,渐变的深度停止 |
--color-text-link-quiet | 65%的墨水用在了次要文字上 |
--shadow-glow | 30%的重音,就像辉光一样 |
--color-text-on-accent | 从重音的WCAG亮度中计算出,黑色或白色,以在重音上清除更多对比度的那个 |
两个记号留下主题是故意的。 对焦环 (--color-focus每个主题的比例是3:1,而任意颜色的混合是不可能的,所以键盘阅读器保留了一个他们可以看到的环。 --color-brand-accent-rgb 是一个通道三重,CSS不能从颜色派生;色彩与 color-mix(in srgb, var(--color-brand-accent) 20%, transparent) 而不是 rgba(var(--color-brand-accent-rgb), 0.2),颜色随品牌而变化。每个派生值都是a --crewlet-brand-* 步骤命名它的输入,没有回退,所以输入未设置的步骤是无效的,令牌回落到主题的值;主题的值保存在 --crewlet-theme-<token>,构建将它们移动到品牌层,因此品牌层是声明公共令牌的地方。颜色混合需要 color-mix() 并且,对于重音标签,相对颜色语法(当前Chrome, Edge, Firefox和Safari)。
该工具包不检查自身的对比:该品牌的文本是否能在浅色地面上阅读,其重音是否能在浅色地面上阅读,这是引擎检查管理员是否采用该品牌的方法。
映射并不仅仅是工具包的问题。它的一个来源是 @crewlethq/tokens' stylesheets/brand.css,出版于 @crewlethq/tokens/css/brand,在将主题的值移到一边后,工具包的构建将其捆绑到kit.css中;将选择器移到作用域上的同一源是 @crewlethq/tokens/css/brand-scope,这 @crewlethq/ui的BrandScope用于在仪表板内标记文档或板。工具包中的页面和仪表板的标记区域按照相同的规则绘制,工具包的套件使两者在每种模式下声明相同的步骤。工具包自己的图层部分是logo类(src/brand.css)。
商标
这个 crewlet-logo 类从插槽中绘制品牌的徽标,因此页面永远不会写入URL,其徽标遵循下一个采用的品牌:
<span class="crewlet-logo" role="img" aria-label="Acme"></span>
<span class="crewlet-logo" data-variant="mark" data-size="sm" role="img" aria-label="Acme"></span>
| 属性 | 意义 |
|---|---|
data-variant | primary (默认):浅色模式下浅色地面的标志,深色模式下品牌有深色地面的on-dark标志。 on-dark:暗标志,无论模式如何,对于浅色页面上的暗带。 mark:单独标记。 |
data-size | sm (1.5rem),默认为2rem; lg (3快速眼动) xl (4 rem)高。一个页面自己的类可以设置 --crewlet-logo-size 而是任意长度。 |
宽度遵循徽标的比例。如果品牌没有标识的变体,它没有画,没有占用空间。给它 role="img" 公司的名字是 aria-label,如上所述。它带有一个 crewlet- 类,所以 content.css 留给工具包吧。
的表
在其数据存储集上绘制可编辑的工作表 <crewlet-sheet>,其列声明为 <crewlet-column>:
<crewlet-sheet collection="vendors" sort="-score" frozen="1" label="Vendor scorecard">
<crewlet-column field="vendor" type="text" label="Vendor"></crewlet-column>
<crewlet-column field="score" type="number" label="Score" format="0.0" min="0" max="10"></crewlet-column>
<crewlet-column field="approved" type="checkbox" label="Approved"></crewlet-column>
<crewlet-column field="tier" type="select" label="Tier" options="Gold:amber,Silver:neutral,Bronze:rose"></crewlet-column>
<crewlet-column field="owner" type="person" label="Owner"></crewlet-column>
<crewlet-column field="status" type="status" label="Status" options="Review:info,Blocked:warning,Done:success"></crewlet-column>
</crewlet-sheet>
或者从脚本中:
import { mountSheet } from '/_crewlet/kit/<version>/kit.js';
const sheet = mountSheet(document.querySelector('#grid'), { collection: 'vendors' });
// sheet.update({ ... }) draws it again with changed options; sheet.unmount() takes it down.
没有自己的列的工作表绘制集合的模式:它的隐藏列被忽略,它的主列被冻结,并且这个工具包不知道的列类型被绘制为只读文本。
它是设计系统自己的 SheetGrid,组件仪表板吸引本地表,所以一张页面的外观和行为完全一样:键盘的电子表格(箭头,输入或编辑F2,逃脱,选项卡中,空间在一个复选框,输入替换,国防部+ C和Mod + V范围,删除,国防部+ Z和国防部+ Shift + Z),一个编辑绘制一次随着储蓄到引擎证实它,拒绝表示细胞的同步带才救了读者认为,冲突注意命名谁改变了下一个细胞 编辑,合作者的戒指和名字标志,以及光明和黑暗的主题和每一个调色板。
<crewlet-sheet> 属性
| 属性 | 意义 |
|---|---|
collection | 工作表编辑的数据存储集合。必需的。 |
label | 表的可访问名称。模式的标题,然后是集合的名称,当不存在时。 |
sort | 初始顺序: score 提升, -score 下行。 |
frozen | 当工作表横向滚动时,有多少个前导列保持在视图中。 |
readonly | 无论读者做什么,只读地绘制表格。 |
height | 工作表的最大值,单位为px;它的行滚动过它。工作表的高度与其下面的行一样高,因此在添加行下面没有空白空间。没有它,床单就停在 --crewlet-sheet-max-height (默认为70%的视口)。 |
<crewlet-column> 属性
| 属性 | 意义 |
|---|---|
field | 文档字段。必需的。 |
type | text, longtext, number, checkbox, select, multiselect, status, person, date, url 或者 rating. text 当缺席;未知类型被绘制为只读文本。 |
label | 的头。缺席时的字段。 |
options | 选择、多选和状态选择: Label:color 成对,逗号分隔。选择的颜色是六种节点色调之一(purple, cyan, green, amber, rose, blue)或 neutral;状态是一种语气(neutral, info, success, warning, danger)。 |
format | 数字的形式: plain, 0.0 (小数保留), percent 或者 0.0%, currency:EUR. |
min, max | 界限:数字的界限;评级的星数(max,默认为5)。 |
max-length, max-items | 文本的最长值;multi-select的大多数选项。 |
width | 列的宽度,单位是px。 |
frozen, readonly, required, time | 停留在视野中;无需编辑;拒绝空值;一个有时间的约会。 |
这床单缚得多紧啊
表单只与页面运行时对话, window.crewlet 运行时契约1.0,在引擎的 docs/reference/artifact-runtime.md),因此它完全具有页面的功能,仅此而已:
| 运行时调用 | 床单是怎么用它的 | 能力 |
|---|---|---|
data.schema() | 集合的标题,以及页面声明为none时的列 | db |
data.collection(name).subscribe(callback, onError) | 行,活;被拒绝的订阅将取代网格 | db |
data.collection(name).merge(id, fields), data.batch().merge(…).commit() | 一次编辑,粘贴或清除范围作为一次全有或全无的写入 | db |
data.collection(name).set(id, {}) | 添加一行,在一个id的工具包薄荷 | db |
data.collection(name).delete(id) | 一旦读者确认,就从行操作中删除行 | db |
data.undo(), data.redo() | Mod+Z和Mod+Shift+Z在页面自己的写;有人跨过一步说 | db |
presence.roster(), presence.subscribe(…), presence.set({ activity, focus }) | 合作者的戒指和旗帜,一小堆谁在这张表,和读者自己的细胞 | presence |
identity.me(), identity.people() | 查看器的只读表,以及person单元格提供的人员和姓名 | identity |
connection.subscribe(…) | 同步条带离线 | 无 |
openLink(url) | url单元格的链接,在打开它之前主机名(框架不能打开窗口) | 无 |
写入解决与引擎的裁决(applied, pending, conflict, invalid, forbidden, quota 或者 gone),只在没有决定的情况下拒绝(rate_limited, unavailable, capability_not_declared); 这张纸上写着要么,要么,整个粘贴被拒绝,从来没有部分。失败的添加行显示在带有Retry的工作表下。删除行(在行号上,或者Shift+F10)在工具包自己的确认中询问,从来没有一个浏览器对话框,工件框架拒绝;引擎拒绝的删除操作或运行时拒绝的删除操作将保留该行,并在其第一个单元格上显示,作为拒绝写操作保存。一个观众和一个 readonly 床单都没有提供。运行时拒绝的订阅(forbidden, not_found, unavailable, capability_not_declared)表示取代网格,而不是作为空集合绘制。一个没有声明的页面 db 获取一个显示上述内容的工作表,而不是一个空网格;没有 presence 或者 identity 这张纸没有合作者,也没有名字。一张表格只显示其中的成员:关注其集合的成员在他们所在的单元格上佩戴他们的戒指,网格上方的一个小堆栈为他们命名。它永远不会绘制页面的整个空间,仪表板的工件条为页面绘制一次,因此包含两个表单的页面不会重复两次。一旦运行时与主机的握手完成,就会绘制工作表(crewlet.ready):运行时在它之前安装,但只有在那时才学习版本的功能,所以早先绘制的表将读取声明的页面 db 作为一个没有的人。当工具包在运行时之前加载时,它等待运行时 crewlet:ready 事件 window (出口 RUNTIME_READY_EVENT),运行时在同一时刻调度。一个观众的表,和一个 readonly 第一,没有同步条:读者没有任何东西可以保存。
里面有什么?
| 文件 | 内容 |
|---|---|
kit.css | 规范变量,默认主题的深色和浅色调色板,每个命名主题下 data-palette,密度在下面 data-density,自托管的Geist和Geist Mono面和文档基线,读取的品牌层 --brand-* 槽(以上)及 crewlet-logo 类,然后将表的样式表、它的单元格和存在标记放在一个文件中。 |
content.css | 可选内容样式表:设计系统查找页面自己编写的纯HTML,然后链接 kit.css. 看到 纯HTML内容. |
fonts/ | 幽灵的四个面 kit.css 引用, OFL.txt. |
theme.js | 主题引导,压缩在2 KB以下。 |
kit.js | 这个 crewlet-sheet 和 crewlet-column 元素和 mountSheet,一个ES模块。它携带设计系统的React组件和React本身,所以页面不需要自己的框架。 |
THIRD_PARTY_NOTICES.txt | 第三方代码的license kit.js 包含:MIT下的React、React DOM和调度器,以及ISC下的Lucide图形。 |
theme.js 从框架URL的片段中读取阅读器的外观,仪表板将其放在(#nonce=...&theme=dark&palette=dusk&density=compact),并写道 data-theme, data-palette 和 data-density 在 <html> 在第一次油漆之前,就像仪表板写自己的一样。当仪表板传输桥接端口时,它遵循阅读器在其上的外观,因此更改仪表板中的主题将重新绘制页面而无需重新加载。每个值在写入之前都要根据允许的值进行检查。它说的电线是 FrameProtocol 在 @crewlethq/ui,捆绑而不是重述。
元素在LIGHT DOM中呈现,而不是在shadow根中呈现,因此令牌级联到表中,并且页面自己的样式表可以放在它旁边 var(--color-*).
预算
构建拒绝超过任何预算的工具包,并且套件将构建文件保存到相同的数量(scripts/budgets.mjs):
| 文件 | gzip预算 |
|---|---|
theme.js | 2 KB |
kit.css | 40 KB |
content.css | 6 KB |
kit.js | 160 KB |
建筑
npm run build --workspace @crewlethq/artifact-kit # dist/
npm test --workspace @crewlethq/artifact-kit # the suite over dist/
它从 @crewlethq/tokens 和 @crewlethq/ui 当用户安装它们时,该工具包所携带的内容与发布的包所携带的内容完全相同。表单在页面上的行为(绑定、编辑、拒绝、存在、粘贴、撤消)如何在jdom中通过 apps/ui-tests/src/artifact-kit.test.tsx,包含测试工具的私有工作区。
许可证
代码在 MIT许可证. Geist和Geist Mono字体文件 dist/fonts/ 使用SIL开放字体许可证1.1 (dist/fonts/OFL.txt). 第三方代码 kit.js 她在执照下 dist/THIRD_PARTY_NOTICES.txt. Crewlet的名称、标志和字符不在这些许可范围内;看到 TRADEMARKS.md.