@linhey/rhine-design 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/CONTRIBUTING.md +6 -0
- package/README.md +19 -14
- package/components/Actions.tsx +4 -4
- package/components/COMMON_COMPONENTS.md +1 -1
- package/components/Data.tsx +8 -8
- package/components/Forms.tsx +11 -11
- package/components/Layout.tsx +4 -4
- package/components/Logo.tsx +7 -7
- package/components/MOTION.md +1 -1
- package/components/Overlays.tsx +9 -9
- package/components/README.md +6 -6
- package/components/Select.tsx +5 -5
- package/components/TYPOGRAPHY.md +1 -1
- package/components/Toast.tsx +2 -2
- package/components/Toolbar.tsx +3 -3
- package/components/Typography.tsx +1 -1
- package/components/common.css +69 -69
- package/components/components.tsx +28 -28
- package/components/styles.css +101 -101
- package/components/tokens.css +55 -55
- package/components/typography.css +31 -31
- package/docs/DESIGN_SYSTEM.md +12 -11
- package/package.json +1 -1
- package/preview/MotionGallery.tsx +3 -3
- package/preview/StartupGallery.tsx +1 -1
- package/preview/brand-assembly.css +8 -8
- package/preview/main.tsx +1 -1
- package/preview/motion.css +14 -14
- package/preview/preview.css +19 -19
- package/preview/startup.css +15 -15
- package/scripts/check-build.mjs +1 -1
- package/skills/rhine-design/SKILL.md +3 -3
- package/skills/rhine-design/references/integration.md +4 -4
- package/tests/CommonComponents.test.tsx +1 -1
- package/tests/StartupGallery.test.tsx +3 -3
package/components/tokens.css
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
1
|
/* Canonical tokens for the component library and design previews. */
|
|
2
2
|
:root {
|
|
3
|
-
--
|
|
4
|
-
--
|
|
5
|
-
--
|
|
6
|
-
--
|
|
7
|
-
--
|
|
8
|
-
--
|
|
9
|
-
--
|
|
10
|
-
--
|
|
11
|
-
--
|
|
12
|
-
--
|
|
13
|
-
--
|
|
14
|
-
--
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
--
|
|
30
|
-
--
|
|
31
|
-
--
|
|
32
|
-
--
|
|
33
|
-
--
|
|
34
|
-
--
|
|
35
|
-
--
|
|
36
|
-
--
|
|
37
|
-
--
|
|
38
|
-
--
|
|
39
|
-
--
|
|
40
|
-
--
|
|
41
|
-
--
|
|
42
|
-
--
|
|
43
|
-
--
|
|
44
|
-
--
|
|
45
|
-
--
|
|
46
|
-
--
|
|
47
|
-
--
|
|
48
|
-
--
|
|
49
|
-
--
|
|
50
|
-
--
|
|
51
|
-
--
|
|
52
|
-
--
|
|
53
|
-
--
|
|
54
|
-
--
|
|
55
|
-
--
|
|
56
|
-
--
|
|
57
|
-
--
|
|
3
|
+
--rhine-bg: #f2f1e9;
|
|
4
|
+
--rhine-surface: #fcfcf8;
|
|
5
|
+
--rhine-ink: #242b28;
|
|
6
|
+
--rhine-muted: #59645d;
|
|
7
|
+
--rhine-line: #c5ccc1;
|
|
8
|
+
--rhine-control: #78857b;
|
|
9
|
+
--rhine-selected: #e5ece3;
|
|
10
|
+
--rhine-green: #425c4b;
|
|
11
|
+
--rhine-signal: #c65c34;
|
|
12
|
+
--rhine-warning: #8a472c;
|
|
13
|
+
--rhine-error: #a13e35;
|
|
14
|
+
--rhine-warning-bg: #f4e8dc;
|
|
15
|
+
--rhine-error-bg: #f6e9e4;
|
|
16
|
+
--rhine-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans CJK SC', sans-serif;
|
|
17
|
+
--rhine-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
|
|
18
|
+
--rhine-text-title: 30px;
|
|
19
|
+
--rhine-leading-title: 38px;
|
|
20
|
+
--rhine-text-section: 18px;
|
|
21
|
+
--rhine-leading-section: 26px;
|
|
22
|
+
--rhine-text-heading: 16px;
|
|
23
|
+
--rhine-leading-heading: 24px;
|
|
24
|
+
--rhine-text-body: 14px;
|
|
25
|
+
--rhine-leading-body: 22px;
|
|
26
|
+
--rhine-text-caption: 12px;
|
|
27
|
+
--rhine-leading-caption: 18px;
|
|
28
|
+
--rhine-text-metric: 26px;
|
|
29
|
+
--rhine-leading-metric: 32px;
|
|
30
|
+
--rhine-text-metric-compact: 20px;
|
|
31
|
+
--rhine-leading-metric-compact: 26px;
|
|
32
|
+
--rhine-text-code: 12px;
|
|
33
|
+
--rhine-leading-code: 20px;
|
|
34
|
+
--rhine-weight-regular: 400;
|
|
35
|
+
--rhine-weight-label: 500;
|
|
36
|
+
--rhine-weight-heading: 600;
|
|
37
|
+
--rhine-space-1: 4px;
|
|
38
|
+
--rhine-space-2: 8px;
|
|
39
|
+
--rhine-space-3: 12px;
|
|
40
|
+
--rhine-space-4: 16px;
|
|
41
|
+
--rhine-space-6: 24px;
|
|
42
|
+
--rhine-space-8: 32px;
|
|
43
|
+
--rhine-space-12: 48px;
|
|
44
|
+
--rhine-radius-card: 4px;
|
|
45
|
+
--rhine-radius-control: 3px;
|
|
46
|
+
--rhine-duration-hover: 120ms;
|
|
47
|
+
--rhine-duration-press: 80ms;
|
|
48
|
+
--rhine-duration-panel: 180ms;
|
|
49
|
+
--rhine-duration-notice: 200ms;
|
|
50
|
+
--rhine-duration-progress: 240ms;
|
|
51
|
+
--rhine-duration-brand: 240ms;
|
|
52
|
+
--rhine-duration-overlay: 160ms;
|
|
53
|
+
--rhine-duration-loading: 1000ms;
|
|
54
|
+
--rhine-duration-skeleton: 1400ms;
|
|
55
|
+
--rhine-ease-standard: cubic-bezier(.2, 0, 0, 1);
|
|
56
|
+
--rhine-ease-exit: cubic-bezier(.4, 0, 1, 1);
|
|
57
|
+
--rhine-motion-distance: 3px;
|
|
58
58
|
}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
.
|
|
2
|
-
.
|
|
3
|
-
.
|
|
4
|
-
.
|
|
5
|
-
.
|
|
6
|
-
.
|
|
7
|
-
.
|
|
8
|
-
.
|
|
9
|
-
.
|
|
10
|
-
.
|
|
11
|
-
.
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
.
|
|
20
|
-
.
|
|
21
|
-
.
|
|
22
|
-
.
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
.
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
.
|
|
31
|
-
@media(forced-colors:active) { .
|
|
1
|
+
.rhine-text { margin:0; font-family:var(--rhine-font); font-weight:var(--rhine-weight-regular); letter-spacing:0; overflow-wrap:anywhere; }
|
|
2
|
+
.rhine-text--title { font-size:var(--rhine-text-title); line-height:var(--rhine-leading-title); font-weight:var(--rhine-weight-heading); }
|
|
3
|
+
.rhine-text--section { font-size:var(--rhine-text-section); line-height:var(--rhine-leading-section); font-weight:var(--rhine-weight-heading); }
|
|
4
|
+
.rhine-text--heading { font-size:var(--rhine-text-heading); line-height:var(--rhine-leading-heading); font-weight:var(--rhine-weight-heading); }
|
|
5
|
+
.rhine-text--body,.rhine-text--label { font-size:var(--rhine-text-body); line-height:var(--rhine-leading-body); }
|
|
6
|
+
.rhine-text--label { font-weight:var(--rhine-weight-label); }
|
|
7
|
+
.rhine-text--caption,.rhine-text--overline { font-size:var(--rhine-text-caption); line-height:var(--rhine-leading-caption); }
|
|
8
|
+
.rhine-text--overline { font-weight:var(--rhine-weight-label); }
|
|
9
|
+
.rhine-text--overline:lang(en) { letter-spacing:.08em; text-transform:uppercase; }
|
|
10
|
+
.rhine-text--metric { font-size:var(--rhine-text-metric); line-height:var(--rhine-leading-metric); font-weight:var(--rhine-weight-heading); font-variant-numeric:tabular-nums lining-nums; }
|
|
11
|
+
.rhine-text--code { font:var(--rhine-text-code)/var(--rhine-leading-code) var(--rhine-mono); }
|
|
12
|
+
.rhine-text--numeric { font-variant-numeric:tabular-nums lining-nums; }
|
|
13
|
+
.rhine-text--default { color:inherit; }
|
|
14
|
+
.rhine-text--muted { color:var(--rhine-muted); }
|
|
15
|
+
.rhine-text--success { color:var(--rhine-green); }
|
|
16
|
+
.rhine-text--warning { color:var(--rhine-warning); }
|
|
17
|
+
.rhine-text--error { color:var(--rhine-error); }
|
|
18
|
+
.rhine-select { position:relative; min-width:0; color:var(--rhine-ink); }
|
|
19
|
+
.rhine-select select { appearance:none; -webkit-appearance:none; display:block; width:100%; min-width:0; min-height:44px; padding:10px 40px 10px 12px; border:1px solid var(--rhine-control); border-radius:var(--rhine-radius-control); background:var(--rhine-surface); color:var(--rhine-ink); font:var(--rhine-text-body)/var(--rhine-leading-body) var(--rhine-font); cursor:pointer; transition:border-color var(--rhine-duration-hover) var(--rhine-ease-standard),background-color var(--rhine-duration-hover) var(--rhine-ease-standard); }
|
|
20
|
+
.rhine-select select:hover:not(:disabled) { border-color:var(--rhine-green); }
|
|
21
|
+
.rhine-select select[aria-invalid=true] { border-color:var(--rhine-error); }
|
|
22
|
+
.rhine-select select:disabled { color:var(--rhine-muted); background:var(--rhine-selected); cursor:default; }
|
|
23
|
+
.rhine-select__arrow { position:absolute; width:16px; height:16px; right:12px; top:50%; transform:translateY(-50%); pointer-events:none; fill:none; stroke:currentColor; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; }
|
|
24
|
+
.rhine-select select:disabled+.rhine-select__arrow { color:var(--rhine-muted); }
|
|
25
|
+
.rhine-toolbar { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:20px 32px; padding:20px 24px; color:var(--rhine-ink); background:var(--rhine-surface); border-bottom:1px solid var(--rhine-line); }
|
|
26
|
+
.rhine-toolbar__intro { min-width:0; flex:1 1 220px; }
|
|
27
|
+
.rhine-toolbar__intro>p { margin-top:4px; }
|
|
28
|
+
.rhine-toolbar__controls { display:flex; flex-wrap:wrap; align-items:flex-start; gap:12px; flex:2 1 440px; min-width:0; }
|
|
29
|
+
.rhine-toolbar__controls>.rhine-field { flex:1 1 150px; min-width:0; }
|
|
30
|
+
.rhine-toolbar .rhine-field>label { font-size:var(--rhine-text-caption); line-height:var(--rhine-leading-caption); }
|
|
31
|
+
@media(forced-colors:active) { .rhine-select select { appearance:auto; -webkit-appearance:auto; } .rhine-select__arrow { display:none; } }
|
package/docs/DESIGN_SYSTEM.md
CHANGED
|
@@ -16,22 +16,23 @@
|
|
|
16
16
|
|
|
17
17
|
## 3. 视觉变量
|
|
18
18
|
|
|
19
|
+
公开样式接口统一使用 `rhine-`:主题根 `.rhine-theme`、变量 `--rhine-*`、组件类与关键帧 `rhine-*`、减少动态属性 `data-rhine-motion="reduced"`。消费项目的品牌名称只通过内容与组件参数传入,不进入共享命名空间。
|
|
19
20
|
|
|
20
21
|
### 3.1 色彩
|
|
21
22
|
|
|
22
23
|
| Token | 值 | 用途 |
|
|
23
24
|
| --- | --- | --- |
|
|
24
|
-
| `--
|
|
25
|
-
| `--
|
|
26
|
-
| `--
|
|
27
|
-
| `--
|
|
28
|
-
| `--
|
|
29
|
-
| `--
|
|
30
|
-
| `--
|
|
31
|
-
| `--
|
|
32
|
-
| `--
|
|
33
|
-
| `--
|
|
34
|
-
| `--
|
|
25
|
+
| `--rhine-bg` | `#F2F1E9` | 窗口底色 |
|
|
26
|
+
| `--rhine-surface` | `#FCFCF8` | 卡片、输入、浮层 |
|
|
27
|
+
| `--rhine-ink` | `#242B28` | 正文、主按钮 |
|
|
28
|
+
| `--rhine-muted` | `#59645D` | 次级文字 |
|
|
29
|
+
| `--rhine-line` | `#C5CCC1` | 装饰分隔线,不独立承担控件识别 |
|
|
30
|
+
| `--rhine-control` | `#78857B` | 必须可识别的控件边界 |
|
|
31
|
+
| `--rhine-selected` | `#E5ECE3` | 选中背景 |
|
|
32
|
+
| `--rhine-green` | `#425C4B` | 选中标记、成功文字、焦点环 |
|
|
33
|
+
| `--rhine-signal` | `#C65C34` | 少量品牌定位色,不用于小字 |
|
|
34
|
+
| `--rhine-warning` | `#8A472C` | 待处理、冲突、覆盖不完整 |
|
|
35
|
+
| `--rhine-error` | `#A13E35` | 操作失败、需要恢复 |
|
|
35
36
|
|
|
36
37
|
本稿测算:正文 / 底色 12.77:1,次级文字 / 底色 5.44:1;品牌橙 / 底色仅 3.72:1,因此只用于非文字标记或符合大字要求的展示文字。重要状态同时有文字。数据未知显示「— / 暂不可用」,不显示 0%,未知进度不画百分比。
|
|
37
38
|
|
package/package.json
CHANGED
|
@@ -20,8 +20,8 @@ type Side = 'top' | 'right' | 'bottom' | 'left';
|
|
|
20
20
|
|
|
21
21
|
// Restart only visual animations: never remount the modal or replay a business action.
|
|
22
22
|
function replayModal(button: HTMLButtonElement) {
|
|
23
|
-
const dialog = button.closest('.
|
|
24
|
-
const overlay = dialog?.parentElement?.querySelector('.
|
|
23
|
+
const dialog = button.closest('.rhine-dialog');
|
|
24
|
+
const overlay = dialog?.parentElement?.querySelector('.rhine-overlay');
|
|
25
25
|
for (const element of [dialog, overlay]) {
|
|
26
26
|
if (!element) continue;
|
|
27
27
|
for (const animation of element.getAnimations()) { animation.currentTime = 0; animation.play(); }
|
|
@@ -70,7 +70,7 @@ function MotionCard({ recipe, slow, reduced }: { recipe: typeof recipes[number];
|
|
|
70
70
|
const [generation, setGeneration] = useState(0), [active, setActive] = useState(false), [still, setStill] = useState(false), [side, setSide] = useState<Side>('bottom');
|
|
71
71
|
const [kind, title, duration, flow, status] = recipe;
|
|
72
72
|
const run = () => { setGeneration(value=>value+1); setActive(true); };
|
|
73
|
-
return <article className="
|
|
73
|
+
return <article className="rhine-theme ds-motion-card" aria-label={title} data-kind={kind} data-slow={slow} data-rhine-motion={reduced || still ? 'reduced' : 'system'}>
|
|
74
74
|
<header><Text variant="code">{duration}</Text><Badge>{status}</Badge></header>
|
|
75
75
|
<div className="ds-motion-stage"><Scene key={generation} kind={kind} active={active} side={side}/></div>
|
|
76
76
|
<div className="ds-motion-description"><h3>{title}</h3><Text variant="caption" tone="muted">{flow}</Text></div>
|
|
@@ -34,7 +34,7 @@ export function StartupGallery({ reduced }: { reduced: boolean }) {
|
|
|
34
34
|
const play = () => { setLoginPreview(false); setGeneration(value => value + 1); setState('playing'); };
|
|
35
35
|
const reset = () => { setLoginPreview(false); setGeneration(value => value + 1); setState('cover'); };
|
|
36
36
|
|
|
37
|
-
return <section id="startup" className="ds-startup" aria-labelledby="startup-title" data-
|
|
37
|
+
return <section id="startup" className="ds-startup" aria-labelledby="startup-title" data-rhine-motion={reduce ? 'reduced' : 'system'} style={{ '--startup-speed': slow ? 4 : 1, '--ds-brand-speed': slow ? 4 : 1, '--ds-brand-offset': '180ms' } as CSSProperties}>
|
|
38
38
|
<div className="ds-section-title"><span>START / 01</span><h2 id="startup-title">构架归位,品牌留下。</h2></div>
|
|
39
39
|
<p className="ds-caption">启动动画设计候选 · 品牌分镜 1.6 秒,560ms 连续收拢到登录页。就绪即刻可操作,减少动态时直接呈现。</p>
|
|
40
40
|
<div className="ds-startup-layout">
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/* Brand-only preview recipe; the canonical SVG and generic Logo entry stay unchanged. */
|
|
2
|
-
.
|
|
3
|
-
.
|
|
2
|
+
.rhine-logo.ds-brand-assembly .rhine-logo__symbol { overflow:visible; }
|
|
3
|
+
.rhine-logo.ds-brand-assembly[data-animate=true] .rhine-logo__plate {
|
|
4
4
|
transform-box:fill-box;
|
|
5
5
|
transform-origin:center;
|
|
6
|
-
animation:ds-brand-assemble calc(720ms * var(--ds-brand-speed,1)) var(--
|
|
6
|
+
animation:ds-brand-assemble calc(720ms * var(--ds-brand-speed,1)) var(--rhine-ease-standard) both;
|
|
7
7
|
animation-delay:calc((var(--ds-brand-offset,0ms) + var(--ds-brand-stagger)) * var(--ds-brand-speed,1));
|
|
8
8
|
}
|
|
9
|
-
.ds-brand-assembly .
|
|
10
|
-
.ds-brand-assembly .
|
|
11
|
-
.ds-brand-assembly .
|
|
9
|
+
.ds-brand-assembly .rhine-logo__plate--left { --ds-brand-stagger:0ms; --ds-brand-turn:-120deg; }
|
|
10
|
+
.ds-brand-assembly .rhine-logo__plate--right { --ds-brand-stagger:80ms; --ds-brand-turn:120deg; }
|
|
11
|
+
.ds-brand-assembly .rhine-logo__plate--base { --ds-brand-stagger:160ms; --ds-brand-turn:60deg; }
|
|
12
12
|
@keyframes ds-brand-assemble {
|
|
13
|
-
0% { opacity:0; transform:translate(calc(var(--
|
|
13
|
+
0% { opacity:0; transform:translate(calc(var(--rhine-logo-x) * 6),calc(var(--rhine-logo-y) * 6)) rotate(var(--ds-brand-turn)) scale(.22,1.08); }
|
|
14
14
|
24% { opacity:1; }
|
|
15
15
|
70% { opacity:1; transform:translate(0,0) rotate(calc(var(--ds-brand-turn) * .06)) scale(.94,1); }
|
|
16
16
|
100% { opacity:1; transform:translate(0,0) rotate(0deg) scale(1); }
|
|
17
17
|
}
|
|
18
|
-
@media(prefers-reduced-motion:reduce) { .
|
|
18
|
+
@media(prefers-reduced-motion:reduce) { .rhine-logo.ds-brand-assembly[data-animate=true] .rhine-logo__plate { animation:none; } }
|
package/preview/main.tsx
CHANGED
|
@@ -22,7 +22,7 @@ function Gallery() {
|
|
|
22
22
|
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
23
23
|
useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []);
|
|
24
24
|
const simulate = () => { setBusy(true); timer.current = setTimeout(() => { setBusy(false); setNotice(true); }, 900); };
|
|
25
|
-
return <div className="
|
|
25
|
+
return <div className="rhine-theme ds-gallery" data-rhine-motion={motion}>
|
|
26
26
|
<header className="ds-header"><a href="#top" className="ds-brand"><Logo variant="lockup" size={36} /><span>DESIGN SYSTEM</span></a><Badge>V1.4 / COMPONENTS</Badge></header>
|
|
27
27
|
<main id="top">
|
|
28
28
|
<div className="ds-hero"><div><p className="ds-kicker">FOUNDATIONS / COMPONENTS / MOTION</p><h1>精密,有序。<br /><span>每一次交互都有回应。</span></h1><p>40 个通用组件,共用一套视觉变量和动效规则。<br />这里运行的就是后续业务页面可直接导入的 React 组件。</p></div><div className="ds-motion-control"><span className="ds-caption">动效偏好 · 仅预览</span><SegmentedControl label="动效偏好" items={[{ value:'system', label:'跟随系统' },{ value:'reduced', label:'减少动态' }]} value={motion} onValueChange={setMotion} /><p>系统减少动态始终优先。<br />动画不延迟操作,也不代替业务结果。</p></div></div>
|
package/preview/motion.css
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
.ds-motion-toolbar { display:flex; align-items:center; justify-content:space-between; gap:24px; flex-wrap:wrap; padding:20px 0 28px; }
|
|
2
2
|
.ds-motion-toolbar>div:first-child { flex:1 1 320px; }
|
|
3
3
|
.ds-motion-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr)); gap:24px; align-items:start; }
|
|
4
|
-
.ds-motion-card { min-width:0; padding:24px; border:1px solid var(--
|
|
4
|
+
.ds-motion-card { min-width:0; padding:24px; border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card); background:var(--rhine-surface); }
|
|
5
5
|
.ds-motion-card>header { display:flex; justify-content:space-between; gap:12px; flex-wrap:wrap; align-items:center; }
|
|
6
|
-
.ds-motion-stage { display:flex; align-items:center; justify-content:center; min-height:240px; margin:20px 0; padding:24px; border:1px solid var(--
|
|
6
|
+
.ds-motion-stage { display:flex; align-items:center; justify-content:center; min-height:240px; margin:20px 0; padding:24px; border:1px solid var(--rhine-line); background:var(--rhine-bg); }
|
|
7
7
|
.ds-motion-description { min-height:88px; }.ds-motion-description h3 { font-size:16px; margin:0 0 8px; }
|
|
8
|
-
.ds-motion-buttons { display:flex; flex-wrap:wrap; gap:8px; margin-top:20px; }.ds-motion-card>.
|
|
8
|
+
.ds-motion-buttons { display:flex; flex-wrap:wrap; gap:8px; margin-top:20px; }.ds-motion-card>.rhine-check { margin-top:12px; }
|
|
9
9
|
.ds-motion-fill,.ds-motion-content { width:100%; min-width:0; }.ds-motion-content { min-height:112px; }
|
|
10
|
-
.ds-content-reveal { display:grid; gap:10px; justify-items:start; animation:
|
|
10
|
+
.ds-content-reveal { display:grid; gap:10px; justify-items:start; animation:rhine-overlay-fade var(--rhine-duration-panel) var(--rhine-ease-standard) both; }
|
|
11
11
|
.ds-result-demo { display:grid; justify-items:center; gap:20px; }.ds-result-slot { min-height:44px; display:flex; align-items:center; }
|
|
12
|
-
.ds-result-reveal { display:inline-flex; gap:8px; align-items:center; color:var(--
|
|
12
|
+
.ds-result-reveal { display:inline-flex; gap:8px; align-items:center; color:var(--rhine-green); animation:rhine-overlay-fade var(--rhine-duration-hover) var(--rhine-ease-standard) both; }
|
|
13
13
|
.ds-still * { animation:none!important; }
|
|
14
|
-
.ds-motion-card :is(.
|
|
15
|
-
.ds-motion-card :is(.
|
|
16
|
-
.ds-motion-card [data-side=bottom] { --ds-from-y:calc(-1 * var(--
|
|
17
|
-
.ds-motion-card [data-side=top] { --ds-from-y:var(--
|
|
18
|
-
.ds-motion-card [data-side=right] { --ds-from-x:calc(-1 * var(--
|
|
19
|
-
.ds-motion-card [data-side=left] { --ds-from-x:var(--
|
|
14
|
+
.ds-motion-card :is(.rhine-dialog,.rhine-overlay)[data-state=open] { animation-fill-mode:both; }
|
|
15
|
+
.ds-motion-card :is(.rhine-popover,.rhine-menu,.rhine-combobox__list)[data-state=open] { animation:ds-origin-enter var(--rhine-duration-overlay) var(--rhine-ease-standard) both; --ds-from-x:0px; --ds-from-y:0px; }
|
|
16
|
+
.ds-motion-card [data-side=bottom] { --ds-from-y:calc(-1 * var(--rhine-motion-distance))!important; }
|
|
17
|
+
.ds-motion-card [data-side=top] { --ds-from-y:var(--rhine-motion-distance)!important; }
|
|
18
|
+
.ds-motion-card [data-side=right] { --ds-from-x:calc(-1 * var(--rhine-motion-distance))!important; }
|
|
19
|
+
.ds-motion-card [data-side=left] { --ds-from-x:var(--rhine-motion-distance)!important; }
|
|
20
20
|
@keyframes ds-origin-enter { from { opacity:0; transform:translate(var(--ds-from-x),var(--ds-from-y)); } to { opacity:1; transform:translate(0,0); } }
|
|
21
|
-
.ds-motion-card[data-slow=true] { --
|
|
21
|
+
.ds-motion-card[data-slow=true] { --rhine-duration-hover:480ms; --rhine-duration-panel:720ms; --rhine-duration-notice:800ms; --rhine-duration-overlay:640ms; --rhine-duration-brand:960ms; }
|
|
22
22
|
.ds-motion-card[data-slow=true] { --ds-brand-speed:4; }
|
|
23
|
-
.
|
|
24
|
-
@media(prefers-reduced-motion:reduce) { .ds-motion-card { --
|
|
23
|
+
.rhine-theme[data-rhine-motion=reduced] .ds-motion-card,.ds-motion-card[data-rhine-motion=reduced] { --rhine-duration-notice:0ms; }
|
|
24
|
+
@media(prefers-reduced-motion:reduce) { .ds-motion-card { --rhine-duration-notice:0ms!important; } }
|
|
25
25
|
@media(max-width:520px) { .ds-motion-card { padding:16px; }.ds-motion-stage { padding:16px; }.ds-motion-description { min-height:0; } }
|
package/preview/preview.css
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
body { margin:0; }
|
|
2
|
-
.ds-type-row { display:grid; grid-template-columns:180px minmax(0,1fr); align-items:baseline; gap:12px 24px; padding:16px 0; border-bottom:1px solid var(--
|
|
2
|
+
.ds-type-row { display:grid; grid-template-columns:180px minmax(0,1fr); align-items:baseline; gap:12px 24px; padding:16px 0; border-bottom:1px solid var(--rhine-line); }
|
|
3
3
|
.ds-type-row:last-child { border:0; }
|
|
4
4
|
.ds-type-copy { display:grid; gap:12px; margin-top:16px; max-width:60ch; }
|
|
5
5
|
@media(max-width:600px) { .ds-type-row { grid-template-columns:1fr; } }
|
|
6
|
-
.ds-header { display:flex; justify-content:space-between; align-items:center; gap:16px; border-bottom:1px solid var(--
|
|
7
|
-
.ds-brand { display:inline-flex; align-items:center; color:var(--
|
|
8
|
-
.ds-brand>span:not(.
|
|
6
|
+
.ds-header { display:flex; justify-content:space-between; align-items:center; gap:16px; border-bottom:1px solid var(--rhine-line); padding:20px max(24px,calc((100vw - 1080px)/2)); }
|
|
7
|
+
.ds-brand { display:inline-flex; align-items:center; color:var(--rhine-ink); text-decoration:none; }
|
|
8
|
+
.ds-brand>span:not(.rhine-logo) { font:12px var(--rhine-mono); letter-spacing:1px; margin-left:16px; }
|
|
9
9
|
.ds-logo-pair { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; }
|
|
10
|
-
.ds-logo-stage { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:24px; padding:32px 12px; background:var(--
|
|
11
|
-
.ds-logo-stage>span:last-child { font:12px var(--
|
|
12
|
-
.ds-logo-stage--dark { background:var(--
|
|
10
|
+
.ds-logo-stage { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:24px; padding:32px 12px; background:var(--rhine-surface); border:1px solid var(--rhine-line); }
|
|
11
|
+
.ds-logo-stage>span:last-child { font:12px var(--rhine-mono); letter-spacing:2px; }
|
|
12
|
+
.ds-logo-stage--dark { background:var(--rhine-ink); color:var(--rhine-bg); border-color:var(--rhine-ink); }
|
|
13
13
|
@media(max-width:600px) { .ds-logo-pair { grid-template-columns:1fr; } }
|
|
14
14
|
.ds-gallery main { max-width:1128px; padding:0 24px; margin:auto; }
|
|
15
15
|
.ds-hero { display:grid; grid-template-columns:1.5fr 1fr; gap:48px; padding:56px 0 40px; align-items:end; }
|
|
16
|
-
.ds-kicker { font:12px var(--
|
|
17
|
-
.ds-hero h1 { font-size:44px; line-height:1.35; letter-spacing:-2px; margin:24px 0; font-weight:600; }.ds-hero h1>span { color:var(--
|
|
18
|
-
.ds-hero p { color:var(--
|
|
19
|
-
.ds-motion-control { border-left:2px solid var(--
|
|
20
|
-
.ds-nav { display:flex; flex-wrap:wrap; gap:16px 28px; padding:16px 0; border-block:1px solid var(--
|
|
21
|
-
.ds-gallery section { padding:32px 0; scroll-margin-top:24px; }.ds-section-title { display:flex; align-items:baseline; gap:16px; margin-bottom:24px; }.ds-section-title>span { font:12px var(--
|
|
22
|
-
.ds-palette { display:grid; grid-template-columns:repeat(5,1fr); gap:16px; }.ds-palette>div>div { height:82px; border:1px solid var(--
|
|
23
|
-
.ds-notes { display:flex; flex-wrap:wrap; gap:12px 32px; margin-top:24px; padding-top:16px; border-top:1px solid var(--
|
|
24
|
-
.ds-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; align-items:start; }.ds-grid>* { min-width:0; }.ds-component-title { display:flex; align-items:baseline; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-bottom:20px; }.ds-component-title h3 { font:600 16px var(--
|
|
25
|
-
.ds-row { display:flex; flex-wrap:wrap; gap:12px; align-items:center; }.ds-caption { font-size:12px; line-height:1.8; color:var(--
|
|
26
|
-
.ds-footer { border-top:1px solid var(--
|
|
27
|
-
@media(max-width:760px) { .ds-hero,.ds-grid { grid-template-columns:1fr; gap:24px; }.ds-hero { padding-top:32px; }.ds-hero h1 { font-size:32px; letter-spacing:-1px; }.ds-brand>span:not(.
|
|
16
|
+
.ds-kicker { font:12px var(--rhine-mono); letter-spacing:1px; color:var(--rhine-muted); }
|
|
17
|
+
.ds-hero h1 { font-size:44px; line-height:1.35; letter-spacing:-2px; margin:24px 0; font-weight:600; }.ds-hero h1>span { color:var(--rhine-green); }
|
|
18
|
+
.ds-hero p { color:var(--rhine-muted); }
|
|
19
|
+
.ds-motion-control { border-left:2px solid var(--rhine-signal); padding-left:24px; }.ds-motion-control>.ds-caption { display:block; margin-bottom:12px; }.ds-motion-control p { font-size:12px; }
|
|
20
|
+
.ds-nav { display:flex; flex-wrap:wrap; gap:16px 28px; padding:16px 0; border-block:1px solid var(--rhine-ink); }.ds-nav a { color:var(--rhine-green); text-decoration:none; min-height:28px; display:flex; align-items:center; }
|
|
21
|
+
.ds-gallery section { padding:32px 0; scroll-margin-top:24px; }.ds-section-title { display:flex; align-items:baseline; gap:16px; margin-bottom:24px; }.ds-section-title>span { font:12px var(--rhine-mono); color:var(--rhine-warning); }.ds-section-title h2 { font-size:22px; font-weight:600; margin:0; }
|
|
22
|
+
.ds-palette { display:grid; grid-template-columns:repeat(5,1fr); gap:16px; }.ds-palette>div>div { height:82px; border:1px solid var(--rhine-line); }.ds-palette strong,.ds-palette code { display:block; font:12px/1.8 var(--rhine-mono); }.ds-palette strong { margin-top:10px; }.ds-palette code { color:var(--rhine-muted); }
|
|
23
|
+
.ds-notes { display:flex; flex-wrap:wrap; gap:12px 32px; margin-top:24px; padding-top:16px; border-top:1px solid var(--rhine-line); font-size:12px; color:var(--rhine-muted); }
|
|
24
|
+
.ds-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; align-items:start; }.ds-grid>* { min-width:0; }.ds-component-title { display:flex; align-items:baseline; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-bottom:20px; }.ds-component-title h3 { font:600 16px var(--rhine-mono); margin:0; }.ds-component-title>span { color:var(--rhine-muted); font-size:12px; }
|
|
25
|
+
.ds-row { display:flex; flex-wrap:wrap; gap:12px; align-items:center; }.ds-caption { font-size:12px; line-height:1.8; color:var(--rhine-muted); }.ds-gap-top { margin-top:20px; }.ds-badges { margin-bottom:24px; }.ds-loaded { min-height:72px; margin:0; }
|
|
26
|
+
.ds-footer { border-top:1px solid var(--rhine-ink); display:flex; justify-content:space-between; gap:16px; flex-wrap:wrap; padding:24px 0; color:var(--rhine-muted); font-size:12px; }
|
|
27
|
+
@media(max-width:760px) { .ds-hero,.ds-grid { grid-template-columns:1fr; gap:24px; }.ds-hero { padding-top:32px; }.ds-hero h1 { font-size:32px; letter-spacing:-1px; }.ds-brand>span:not(.rhine-logo) { display:none; }.ds-palette { grid-template-columns:repeat(auto-fit,minmax(min(100%,90px),1fr)); }.ds-palette>div>div { height:64px; }.ds-gallery main { padding:0 20px; }.ds-header { padding:16px 20px; flex-wrap:wrap; }.ds-section-title h2 { font-size:19px; } }
|
package/preview/startup.css
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
.ds-startup-layout { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(240px,1fr); gap:40px; margin:24px 0; align-items:center; }
|
|
2
|
-
.ds-startup-window { border:1px solid var(--
|
|
3
|
-
.ds-startup-chrome { display:flex; justify-content:space-between; gap:12px; padding:12px 20px; border-bottom:1px solid var(--
|
|
2
|
+
.ds-startup-window { border:1px solid var(--rhine-line); background:var(--rhine-bg); min-width:0; }
|
|
3
|
+
.ds-startup-chrome { display:flex; justify-content:space-between; gap:12px; padding:12px 20px; border-bottom:1px solid var(--rhine-line); font:12px/18px var(--rhine-font); color:var(--rhine-muted); }
|
|
4
4
|
.ds-startup-stage { min-height:540px; padding:40px 24px 24px; display:flex; flex-direction:column; align-items:center; justify-content:flex-start; position:relative; isolation:isolate; }
|
|
5
5
|
.ds-startup-emblem { width:240px; height:220px; position:relative; box-sizing:border-box; flex:none; }
|
|
6
6
|
.ds-startup-logo-anchor { position:absolute; left:32px; top:32px; width:176px; height:176px; transform-origin:center; transition:transform calc(560ms * var(--startup-speed)) cubic-bezier(.22,1,.36,1); }
|
|
7
7
|
.ds-startup-construction { position:absolute; inset:0; width:240px; height:240px; pointer-events:none; transition:opacity calc(180ms * var(--startup-speed)) ease-out; }
|
|
8
|
-
.ds-startup-guide { stroke:var(--
|
|
9
|
-
.ds-startup-ticks { stroke:var(--
|
|
10
|
-
.ds-startup-signal { fill:var(--
|
|
11
|
-
.ds-startup-wordmark { position:absolute; top:246px; width:100%; text-align:center; color:var(--
|
|
12
|
-
.ds-startup-status { font:12px/18px var(--
|
|
13
|
-
.ds-startup-footnote { display:flex; justify-content:space-between; flex-wrap:wrap; gap:16px; width:100%; margin-top:auto; padding-top:32px; font:12px/18px var(--
|
|
14
|
-
.ds-startup-score h3 { font:600 26px/1.4 var(--
|
|
15
|
-
.ds-startup-score ol { list-style:none; padding:0; margin:28px 0; }.ds-startup-score li { padding:16px 0; border-top:1px solid var(--
|
|
16
|
-
.ds-startup-controls { display:flex; align-items:center; gap:24px; flex-wrap:wrap; margin-bottom:20px; }.ds-startup-controls>.
|
|
8
|
+
.ds-startup-guide { stroke:var(--rhine-control); stroke-width:.75; opacity:0; }
|
|
9
|
+
.ds-startup-ticks { stroke:var(--rhine-line); stroke-width:1; }
|
|
10
|
+
.ds-startup-signal { fill:var(--rhine-signal); opacity:0; }
|
|
11
|
+
.ds-startup-wordmark { position:absolute; top:246px; width:100%; text-align:center; color:var(--rhine-ink); font:600 30px/38px var(--rhine-font); letter-spacing:.04em; transition:transform calc(560ms * var(--startup-speed)) cubic-bezier(.22,1,.36,1); }
|
|
12
|
+
.ds-startup-status { font:12px/18px var(--rhine-font); color:var(--rhine-muted); margin:100px 0 24px; }
|
|
13
|
+
.ds-startup-footnote { display:flex; justify-content:space-between; flex-wrap:wrap; gap:16px; width:100%; margin-top:auto; padding-top:32px; font:12px/18px var(--rhine-mono); letter-spacing:.06em; color:var(--rhine-muted); }
|
|
14
|
+
.ds-startup-score h3 { font:600 26px/1.4 var(--rhine-font); margin:20px 0 12px; }.ds-startup-score>p { color:var(--rhine-muted); }
|
|
15
|
+
.ds-startup-score ol { list-style:none; padding:0; margin:28px 0; }.ds-startup-score li { padding:16px 0; border-top:1px solid var(--rhine-line); }.ds-startup-score li>span { display:block; color:var(--rhine-muted); font:12px/18px var(--rhine-mono); margin-bottom:8px; }.ds-startup-score li strong { font-size:16px; font-weight:600; }.ds-startup-score li p { margin:4px 0 0; font-size:12px; color:var(--rhine-muted); }
|
|
16
|
+
.ds-startup-controls { display:flex; align-items:center; gap:24px; flex-wrap:wrap; margin-bottom:20px; }.ds-startup-controls>.rhine-field { flex:1; min-width:180px; }
|
|
17
17
|
.ds-startup-destination { display:flex; flex-direction:column; gap:16px; align-items:center; text-align:center; width:100%; max-width:320px; animation:startup-arrival calc(420ms * var(--startup-speed)) ease-out both; }
|
|
18
|
-
.ds-startup-destination>.
|
|
18
|
+
.ds-startup-destination>.rhine-button { margin-top:8px; width:100%; max-width:260px; }
|
|
19
19
|
.ds-startup-destination:focus-within { animation:none; }
|
|
20
20
|
.ds-startup-stage[data-state=ready] .ds-startup-logo-anchor { transform:translateY(-16px) scale(.5); }
|
|
21
21
|
.ds-startup-stage[data-state=ready] .ds-startup-wordmark { transform:translateY(-84px); }
|
|
22
22
|
.ds-startup-stage[data-state=ready] .ds-startup-construction { opacity:0; }
|
|
23
23
|
@keyframes startup-arrival { from { opacity:.12; transform:translateY(3px); } to { opacity:1; transform:translateY(0); } }
|
|
24
|
-
.ds-startup-stage[data-state=playing] .ds-startup-guide { animation:startup-guide calc(1340ms * var(--startup-speed)) var(--
|
|
24
|
+
.ds-startup-stage[data-state=playing] .ds-startup-guide { animation:startup-guide calc(1340ms * var(--startup-speed)) var(--rhine-ease-standard) both; }
|
|
25
25
|
.ds-startup-stage[data-state=playing] .ds-startup-signal { animation:startup-signal calc(280ms * var(--startup-speed)) linear calc(1060ms * var(--startup-speed)) both; }
|
|
26
|
-
.ds-startup-stage[data-state=playing] .ds-startup-wordmark { animation:startup-destination calc(240ms * var(--startup-speed)) var(--
|
|
26
|
+
.ds-startup-stage[data-state=playing] .ds-startup-wordmark { animation:startup-destination calc(240ms * var(--startup-speed)) var(--rhine-ease-standard) calc(1080ms * var(--startup-speed)) both; }
|
|
27
27
|
@keyframes startup-guide { 0% { stroke-dasharray:1; stroke-dashoffset:1; opacity:0; } 24%,79% { stroke-dasharray:1; stroke-dashoffset:0; opacity:.65; } 100% { stroke-dasharray:1; stroke-dashoffset:0; opacity:0; } }
|
|
28
28
|
@keyframes startup-signal { 0%,100% { opacity:0; } 20%,65% { opacity:1; } }
|
|
29
29
|
@keyframes startup-destination { from { opacity:0; } to { opacity:1; } }
|
|
30
|
-
.ds-startup[data-
|
|
30
|
+
.ds-startup[data-rhine-motion=reduced] * { animation:none!important; transition:none!important; }
|
|
31
31
|
@media(prefers-reduced-motion:reduce) { .ds-startup * { animation:none!important; transition:none!important; } }
|
|
32
32
|
@media(max-width:760px) { .ds-startup-layout { grid-template-columns:minmax(0,1fr); gap:24px; }.ds-startup-stage { padding-inline:16px; }.ds-startup-score ol { margin:20px 0; } }
|
|
33
33
|
@media(max-width:360px) { .ds-startup-chrome { padding-inline:12px; }.ds-startup-footnote { flex-wrap:wrap; } }
|
package/scripts/check-build.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { join } from 'node:path';
|
|
|
4
4
|
const assets = new URL('../dist/assets/', import.meta.url);
|
|
5
5
|
const files = await readdir(assets);
|
|
6
6
|
const css = (await Promise.all(files.filter(file => file.endsWith('.css')).map(file => readFile(new URL(file, assets), 'utf8')))).join('\n');
|
|
7
|
-
for (const required of ['--
|
|
7
|
+
for (const required of ['--rhine-bg:', '.rhine-theme', '.rhine-button', '.ds-startup']) {
|
|
8
8
|
if (!css.includes(required)) throw new Error(`Design kit build lost required stylesheet content: ${required}`);
|
|
9
9
|
}
|
|
10
10
|
if (!files.some(file => /^logo-.*\.svg$/.test(file))) throw new Error('Design kit build lost the SVG master asset');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: rhine-design
|
|
3
3
|
description: Use Rhine Design components and specifications to design, implement, or review React UI, typography, themes, and motion. Apply when a project uses Rhine Design or the user requests its visual language or component system.
|
|
4
4
|
metadata:
|
|
5
|
-
version: "0.
|
|
5
|
+
version: "0.3.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Rhine Design · 莱茵设计
|
|
@@ -11,7 +11,7 @@ Use the installed version's components and contracts to build consistent interfa
|
|
|
11
11
|
|
|
12
12
|
## Locate the matching source
|
|
13
13
|
|
|
14
|
-
Package: `@linhey/rhine-design`. GitHub: `linhay/rhine-design` (different account spelling). This skill targets 0.
|
|
14
|
+
Package: `@linhey/rhine-design`. GitHub: `linhay/rhine-design` (different account spelling). This skill targets 0.3.0.
|
|
15
15
|
|
|
16
16
|
Find the package in the host's dependencies, normally `node_modules/@linhey/rhine-design/`, or its registered source checkout. Verify `package.json` name/version before using the paths below. Paths in references are relative to that package root, not the copied skill directory. Read installed types and source when versions differ; do not infer a new API from this skill. If the package is unavailable, use the task's supplied design sources and identify the missing implementation evidence.
|
|
17
17
|
|
|
@@ -30,7 +30,7 @@ Find the package in the host's dependencies, normally `node_modules/@linhey/rhin
|
|
|
30
30
|
|
|
31
31
|
## Preserve these contracts
|
|
32
32
|
|
|
33
|
-
- Use public exports, the `.
|
|
33
|
+
- Use public exports, the `.rhine-theme` root and existing tokens. Use the `rhine-` namespace for CSS variables, classes, animation names and motion data attributes; do not introduce host-product prefixes.
|
|
34
34
|
- Read the actual component signature before using it. Keep semantic headings separate from visual text variants.
|
|
35
35
|
- Host state determines data, selection and operation outcomes. Search and pagination remain host/server responsibilities; busy indicators never imply success.
|
|
36
36
|
- Motion explains a change. Respect reduced motion, cancellation and replay; startup decoration never blocks real readiness. Preview-only experiments are not exported components.
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Supported baseline
|
|
4
4
|
|
|
5
|
-
Rhine Design 0.
|
|
5
|
+
Rhine Design 0.3.0 ships TypeScript/TSX, CSS and SVG source for React 19 + Vite. Install `@linhey/rhine-design`; GitHub remains `linhay/rhine-design`. Check the installed package's engine and peer dependencies. Node 24 is the development baseline. Do not assume precompiled JavaScript, arbitrary SSR support or a complete dark theme.
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
import { Button, Surface, Text } from '@linhey/rhine-design';
|
|
9
9
|
|
|
10
10
|
export function Example() {
|
|
11
11
|
return (
|
|
12
|
-
<div className="
|
|
12
|
+
<div className="rhine-theme">
|
|
13
13
|
<Surface>
|
|
14
14
|
<Text as="h2" variant="section">工作空间</Text>
|
|
15
15
|
<Button onClick={() => { /* host action */ }}>继续</Button>
|
|
@@ -22,10 +22,10 @@ export function Example() {
|
|
|
22
22
|
## Integration invariants
|
|
23
23
|
|
|
24
24
|
- The entry imports CSS automatically. Keep the package's entry and CSS `sideEffects` declarations so production tree shaking retains styles.
|
|
25
|
-
- Put `.
|
|
25
|
+
- Put `.rhine-theme` at the page root. Do not transform, clip or apply containment to it: overlays portal to the nearest theme root and need its variables and reduced-motion settings.
|
|
26
26
|
- For a locally linked checkout, use Vite `resolve.dedupe: ['react', 'react-dom']`. Duplicate React can break hooks; do not patch component state to mask this.
|
|
27
27
|
- Preserve SVG `?no-inline` handling. Logo uses a shared geometric master; missing symbols in production warrant checking the emitted SVG asset before replacing the logo.
|
|
28
|
-
- Reuse tokens for layout and state styling. Full dark theme is not exported in 0.
|
|
28
|
+
- Reuse tokens for layout and state styling. Full dark theme is not exported in 0.3.0; the logo's dark variant alone does not establish dark-mode support.
|
|
29
29
|
|
|
30
30
|
## Host responsibilities
|
|
31
31
|
|
|
@@ -65,7 +65,7 @@ describe('common design components', () => {
|
|
|
65
65
|
});
|
|
66
66
|
it('keeps a failed confirmation open and closes only after successful retry', async () => {
|
|
67
67
|
const confirm=vi.fn().mockRejectedValueOnce(new Error('demo')).mockResolvedValue(undefined);
|
|
68
|
-
render(<div className="
|
|
68
|
+
render(<div className="rhine-theme"><ConfirmDialog trigger={<button>打开</button>} title="确认任务" description="演示" onConfirm={confirm}/></div>);
|
|
69
69
|
fireEvent.click(screen.getByRole('button',{name:'打开'}));expect(await screen.findByRole('alertdialog')).toBeVisible();expect(screen.getByRole('button',{name:'取消'})).toHaveFocus();fireEvent.click(screen.getByRole('button',{name:'确认操作'}));expect(await screen.findByRole('alert')).toHaveTextContent('操作未完成');fireEvent.click(screen.getByRole('button',{name:'确认操作'}));await waitFor(()=>expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument());
|
|
70
70
|
});
|
|
71
71
|
});
|
|
@@ -44,11 +44,11 @@ it('keeps the same brand node when readiness arrives and makes the login preview
|
|
|
44
44
|
vi.useFakeTimers();
|
|
45
45
|
const { container } = render(<StartupGallery reduced={false} />);
|
|
46
46
|
fireEvent.click(screen.getByRole('button', { name: '播放启动动画' }));
|
|
47
|
-
const logo = container.querySelector('.ds-startup-logo-anchor .
|
|
47
|
+
const logo = container.querySelector('.ds-startup-logo-anchor .rhine-logo');
|
|
48
48
|
act(() => vi.advanceTimersByTime(300));
|
|
49
49
|
fireEvent.click(screen.getByRole('button', { name: '模拟就绪 · 立即进入' }));
|
|
50
|
-
expect(container.querySelector('.ds-startup-logo-anchor .
|
|
51
|
-
expect(container.querySelectorAll('.ds-startup-stage .
|
|
50
|
+
expect(container.querySelector('.ds-startup-logo-anchor .rhine-logo')).toBe(logo);
|
|
51
|
+
expect(container.querySelectorAll('.ds-startup-stage .rhine-logo')).toHaveLength(1);
|
|
52
52
|
fireEvent.click(screen.getByRole('button', { name: '在浏览器中登录' }));
|
|
53
53
|
expect(screen.getByText('仅演示登录入口,不会打开授权页面。')).toBeInTheDocument();
|
|
54
54
|
expect(vi.getTimerCount()).toBe(0);
|