@ asinative/ 主题
驱动的主题层 @basenative/components。 。 。 .
一个图书馆,很多主题. DuganLabs的每个财产都有自己的身份 停止重新解析对比度、 符号级、 图标网格和 CSS 密接 刮伤。 这个包装的是机器,而不是外观 在这里,不会有一个.
零生产依赖. 手写JS,手写类型, node --test。 。 。 .
@basenative/theme defineTheme, validateTheme, ThemeError, toCss, bridgeToCti
@basenative/theme/contrast relativeLuminance, contrastRatio, parseCustomProperties,
resolveColor, AA_TEXT, AA_LARGE, AA_NONTEXT
@basenative/theme/audit auditPairings, assertPairings, defaultPairings,
assertThemeContrast, ContrastError
@basenative/theme/css minifyCss
@basenative/theme/icons defineIconSet, renderIcon, validateIconSet, IconSetError
@basenative/theme/bridge.css the CTI+ bridge, as a stylesheet
开始新属性
1. 定义
一个主题是四层。 没有东西可以跳过一个, 只有一级包含一个六分之一.
| 级别 | 你写 | 示例 |
|---|---|---|
1. 联合国. ramps | 字义 #rrggbb 比额表 | neutral, (中文(简体) ), primary,加上你需要的信号 |
2. 联合国. surfaces 编号 : ink | 什么东西坐 打开上面写着什么 | surfaces.sunk: 'neutral.100' |
3个. roles | 什么样的元素 实值 | roles.borderControl: 'neutral.500' |
4.四. signals | 家庭状况 词汇, 分配给五个组件槽 | families.stalled, (中文(简体) ), slots.warning: 'stalled' |
2 -4级从不携带螺旋. 他们携带 '<ramp>.<step>',作为
var() 链并解回由校验器解回文字。 换个斜坡
台阶和上面的一切都会移动.
两道坡道是强制性的,因为 @basenative/components 直接读取:
primary (第50-900步)和 neutral (步骤50-950).
2. 写出主题
这是开始者。 没错 @basenative/components' 自己的默认调色板
译名而非新相貌
两道坡道是你第一件事 它不是从此软件包导出
目的(见 为什么没有默认主题(原始内容存档于2018-03-26) (英语). copy it.
import { defineTheme } from '@basenative/theme';
export const theme = defineTheme({
name: 'starter',
scheme: 'light',
// Tier 1 — the only hexes in the file.
ramps: {
neutral: {
50: '#fafafa',
100: '#f4f4f5',
200: '#e4e4e7',
300: '#d4d4d8',
400: '#a1a1aa',
500: '#71717a',
600: '#52525b',
700: '#3f3f46',
800: '#27272a',
900: '#18181b',
950: '#09090b',
},
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
success: { 100: '#dcfce7', 600: '#16a34a', 700: '#15803d', 800: '#166534' },
warning: { 100: '#fef3c7', 600: '#d97706', 700: '#b45309', 800: '#92400e' },
danger: { 100: '#fee2e2', 500: '#ef4444', 600: '#dc2626', 800: '#991b1b' },
paper: { white: '#ffffff' },
},
// Tier 2 — surfaces, and the ink that goes on them.
surfaces: {
base: 'paper.white',
subtle: 'neutral.50',
sunk: 'neutral.100',
raised: 'paper.white',
inverse: 'neutral.900',
},
ink: {
base: 'neutral.900',
// Upstream's muted ink is gray-500, which is 4.40:1 on the gray-100 sunk
// surface. The harness refused it; one step darker clears at 7.03:1.
muted: 'neutral.600',
subtle: 'neutral.600',
inverse: 'paper.white',
link: 'primary.600',
},
// Tier 3 — roles. Every one of these is painted by a component.
roles: {
primary: 'primary.600',
primaryHover: 'primary.700',
onPrimary: 'paper.white',
primaryTint: 'primary.50',
border: 'neutral.300',
borderStrong: 'neutral.400',
borderControl: 'neutral.500',
focus: 'primary.500',
focusOnInverse: 'primary.300',
selectedBg: 'primary.50',
selectedInk: 'primary.700',
},
// Tier 4 — signal families, then the five slots they drive.
signals: {
families: {
neutral: { bg: 'neutral.300', ink: 'neutral.900', line: 'neutral.500', solid: 'neutral.600', onSolid: 'paper.white' },
info: { bg: 'primary.100', ink: 'primary.800', line: 'primary.500', solid: 'primary.600', onSolid: 'paper.white' },
success: { bg: 'success.100', ink: 'success.800', line: 'success.600', solid: 'success.700', onSolid: 'paper.white' },
warning: { bg: 'warning.100', ink: 'warning.800', line: 'warning.600', solid: 'warning.700', onSolid: 'paper.white' },
danger: { bg: 'danger.100', ink: 'danger.800', line: 'danger.500', solid: 'danger.600', onSolid: 'paper.white' },
},
slots: {
default: 'neutral',
primary: 'info',
success: 'success',
warning: 'warning',
error: 'danger',
},
},
});
几何是可选和默认的—— font, (中文(简体) ), radius, (中文(简体) ), control, (中文(简体) ), motion编辑
默认为 @basenative/components' 自己的价值, 主张字节相同
这个包的测试, 所以上游的变化 失败了建设,而不是悄悄地
复置六所地业. 田间应用提升了水龙头目标:
control: { tapMin: '2.75rem' }, // 44px — WCAG 2.2 SC 2.5.5, for gloves
3. 为它服务
import { toCss } from '@basenative/theme';
import { minifyCss } from '@basenative/theme/css';
import { theme } from './theme.js';
// Emits @layer tokens { … } plus the CTI+ bridge. Comments carry the
// rationale for whoever opens the file; strip them at the point of serving.
export const THEME_CSS = minifyCss(toCss(theme));
装入顺序, 匹配已声明的层顺序
(单位:千美元)@layer reset, tokens, layout, components, states:
<link rel="stylesheet" href="/@basenative/components/css">
<style>/* THEME_CSS */</style>
toCss 默认附加桥。 如果你宁愿把它连起来,
toCss(theme, { bridge: false }) 加号 @basenative/theme/bridge.css。 。 。 .
4. 打开门
一行,在您自己的测试文件中:
import { test } from 'node:test';
import { assertThemeContrast } from '@basenative/theme/audit';
import { theme } from '../src/theme.js';
test('every shipped colour pairing clears WCAG AA', () => {
assertThemeContrast(theme);
});
assertThemeContrast 从主题本身的形状写出配对表 — 55
启动器的行——测量真实的相对亮度,并投掷列表
每个失败的行及其实际比率。 它运行一次每个方案 您的主题
因此,一个双重主题要测量两次.
添加此软件包所无法知道的任何内容的行 :
import { assertPairings, defaultPairings, AA_NONTEXT } from '@basenative/theme/audit';
import { toCss } from '@basenative/theme';
assertPairings(toCss(theme, { only: 'light', bridge: false }), [
...defaultPairings(theme),
['logo on the header band', '--color-border-theme-signal-info', '--color-bkg-theme-surface-inverse', AA_NONTEXT],
]);
CI会失败的
| 失败 | 被丢弃 | 这是什么意思 |
|---|---|---|
scheme is "undefined" | defineTheme | 你没有承诺 光明,黑暗或双重。 见下文——这个不是可选的. |
theme.roles is missing | defineTheme | 跳过了一层 这四者皆有需要. |
theme.ink.link references "brand.600", which the theme does not declare | defineTheme | 细细的参考文献. |
ramps.primary.600 is "var(--x)" — a ramp step must be a literal | defineTheme | 永不以颜色终止的链条 . |
theme.signals.slots.warning is unassigned | defineTheme | renderBadge({ variant: 'warning' }) 没有颜色. |
muted text on sunk surface: … is 4.40:1, below 4.5:1 | assertThemeContrast | 一个真正的WCAG失手, 与真实的数字. |
既不是符号名称 CTI_BRIDGED 也没有 UPSTREAM_OWNED | 这个包是自己的 bridge.test.js | @basenative/components 发展出可主题符号 桥没有赶上. |
一揽子计划背后的四项决定
为什么没有默认主题
出口中的参考主题成为了de-facto org 外观,即
正好相反的一点。 所以没有 上面的启动器是
文档而非进口: 伪造是作者行为。 这是保存的
老实说 starter-guide.test.js,此文件的哪个解析,运行 defineTheme 编辑
块,并声称 neutral 财务报告和审定财务报表 primary 斜坡的字节与
@basenative/components/src/tokens-legacy.css。文件无法从代码中漂移
启动器不能悄悄地变成品牌.
颜色从不默认 。 几何学是——半径阶梯是ergonomics,不是 身份证明.
信号家庭对五个插槽
@basenative/components 宣布五个警徽/警示变式:
default | primary | success | warning | error。真正的域没有五个
状态叫这个。 格林普特队是 idle | move | good | dead; 仓库
应用可能是 receiving | staged | short | damaged。 。 。 .
所以主题宣布 家庭名下的家庭,则 指定 改为 五个插槽中每个插槽:
signals: {
families: {
idle: { … }, move: { … }, good: { … }, dead: { … },
},
slots: { default: 'idle', primary: 'idle', success: 'good', warning: 'move', error: 'dead' },
}
家庭不限于五个,两个名额可以共用一个名额. 每个家庭都有
其本身 --color-*-theme-signal-<name> 用于您自己的 CSS 的属性,和 - 这是
关键部分—— 无论是否是,每个家庭都有对照行
分配给一个槽。无法添加一个从来就没有过的状态颜色
测量.
每个家庭有五个渠道: bg (警徽坐着), ink (中文(简体) )
{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}那条线 财务报告和审定财务报表 在页面上, line (家族统治,为SC3:1门)
1.4.11,(a), solid (已填入的文本)和 onSolid (文字在填报上).
写这个时测量: 白色
--palette-success-600(# 16a34a) 为 3:30:1和开通--palette-warning-600(#d97706)为3.19:1,而--palette-error-600为 4.83: 1. 上游只画过错误的文字 填充,所以它载运 没有破碎的—— 只是一个绿色的"证明"按钮-600会的 启动器的成功和警告固体 一步更暗.
黑暗主题:可选,但承诺不是
validateTheme 已经 没有 需要一个深层。 这需要 scheme,则它
实值 'light' | 'dark' | 'dual' 没有默认.
单一策略主题出自 一个街区,没有条件 没有媒体
查询,无 data-theme 变相. 这就是失败的整件事
软件包建于: 待定business.com是一个从未设置过的黑暗站点
data-theme,因此,一个读取器,其OS在光模式下坠入
@media (prefers-color-scheme: dark) 并获得了 --color-bkg-error: #fef2f2 a 包括:
一个近黑页面上的近白警报.
与 scheme: 'dark' 没有什么可掉下来的。 黑暗的价值观是
在每一个方面, color-scheme: dark 被钉住 data-theme
属性停止为负载。 运送这个虫子现在需要申报
scheme: 'dual' 并故意写出一个灯光调色板——此时
控制它.
双重主题镜像 上游的激活模式 在匹配的特异性,所以它的
自己的深色调色板获胜,和 validateTheme 拒绝半主题暗块
他们的家族名单和光线不同.
为什么桥住在这里
components 拥有组件。 内容 --bn-* 阶梯 (tokens-legacy.css)为
已经是一个完全中立的默认, 它应该保持无知的主题
已存在。 成本——这个包裹
名称列表可能落在组件发布后- 是 测量,未假定数字 :
CTI_BRIDGED是桥出( 今天有93个名字) .UPSTREAM_OWNED名单是故意留下的 每一个都有它的理由.bridge.test.js读取每个样式表packages/components/src失败 如果一个var(--*)两个列表中都没有显示.
添加可主题符号的组件发布会打破此软件包的构建
同样的CI运行, 而不是默默无声地去 不主题在六个属性.
validateBridge(css, { exclude }) 被导出, 这样消费者就可以运行相同的
检查它安装的任何版本; exclude 携带
组件级和关键词符号名称,从符号源计算,由
文件( 编号tokens/components/*.json 加号 tokens/keyword.json页:1
关于消费者的组件版本 而不是这个包.
这座桥留下了什么 类型尺度、字体权重、行高, 空间阶梯、阴影、松动曲线、z指数和每英寸几何 no. 这些是应用决定,不是调色板决定——在你自己身上压倒它们 CSS (英语). 颜色、 字体家族、 半径阶梯、 控制高度、 水龙头目标 三个运动持续时间都是桥接的.
如何是桥取胜. 生成的符号表在 components 激活
其暗值为 :root:not([data-theme="light"]) 在媒体查询和
时间 :root[data-theme="dark"] ——特异性(0,2,0). 在平原上的一座桥 :root
(0,1,0)会输给两者,这就是上游的暗值如何通过
主题。 双桥选择器为 (0,2,1):
html:root[data-theme],
html:root:not([data-theme="light"]) { … }
二者之间,它们都符合 data-theme,包括其缺席,
他们从上游打来 该桥也是UNLAYERED,目的:
上游宣布其核心符号未被分层,层层声明会丢失
任何未分层的 任何特异性 —— 刻度符号是不分层的,所以
内接桥 @layer tokens 永远无法重指它。 没有分开
用于中和的暗名设置: 暗是在同一个同位符上携带的一种模式, 所以
在主题和主题上重新指定一个名称
决定黑暗的意义.
图标
不是图标库——使一行图标读作一集的学科.
import { defineIconSet, renderIcon } from '@basenative/theme/icons';
export const icons = defineIconSet({
grid: 24,
strokeWidth: 1.75,
icons: {
activity: '<path d="M3 12h4l3 7 4-14 3 7h4"/>',
check: '<path d="m4.5 12.5 5 5 10-11"/>',
},
});
renderIcon(icons, 'activity'); // aria-hidden — it sits beside a label
renderIcon(icons, 'check', { title: 'Verified' }); // role="img" — it IS the label
defineIconSet 抛出任何打破 设定的均衡:一个填充,一个
被烤入中风的颜色或字形六分肌,每英寸 stroke-width中线
样式, 嵌入式 <svg>本身 viewBox一个网格,一个重量,
currentColor,默认为装饰.
一个身体是 目录 一、导 言 <svg>这样 width 财务报告和审定财务报表 height 内在的
普通几何并允许—— <rect x="3" y="5" width="18" height="16" rx="2"/> 是日历。 自我放大是外在元素的一个属性
renderIcon 写作和身体不能有.
对比度, 直接
import { contrastRatio, relativeLuminance, AA_TEXT } from '@basenative/theme/contrast';
contrastRatio('#166534', '#dcfce7'); // 6.49
parseCustomProperties(css) 财务报告和审定财务报表 resolveColor(name, props) 碎片是
审计工具的构建来自: var() 链到文字
#rrggbb 并投出一个细长的参考物,一个循环,或者一个非文学终端.
parseCustomProperties 将样式表读作平面图,从而测量双向
主题意味着一次发布一个方案—— toCss(theme, { only: 'dark' })。 。 。 .
assertThemeContrast 这样做是为了你.
线性
对比引擎,对接绳, minifyCss 和各种图标
证明在 libs/shared/design 在绿地竞技场, 配对
表格已硬化并连接到 --gp-* 名称空间。 这里的桌子是
参数,每个符号名称都是参数,格林普特的调色板,它的四
承包商的信号家庭,它的网点和标记 留下来,在那里他们
属于.
许可证
阿帕奇 - 2.0