@linhey/rhine-design 0.3.0 → 0.5.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.
Files changed (89) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +63 -14
  3. package/components/Actions.tsx +2 -0
  4. package/components/AdvancedForms.tsx +184 -0
  5. package/components/AppShell.tsx +71 -0
  6. package/components/Background.tsx +29 -0
  7. package/components/COMMON_COMPONENTS.md +5 -1
  8. package/components/Charts.tsx +94 -0
  9. package/components/Data.tsx +98 -18
  10. package/components/FormWorkflow.tsx +25 -0
  11. package/components/Forms.tsx +14 -8
  12. package/components/MOTION.md +6 -2
  13. package/components/Media.tsx +82 -0
  14. package/components/Navigation.tsx +42 -0
  15. package/components/Overlays.tsx +10 -3
  16. package/components/README.md +20 -5
  17. package/components/SPACING.md +82 -0
  18. package/components/StatusTimeline.tsx +67 -0
  19. package/components/TYPOGRAPHY.md +34 -2
  20. package/components/background.css +43 -0
  21. package/components/charts.css +27 -0
  22. package/components/common.css +17 -8
  23. package/components/data.css +6 -0
  24. package/components/drawer-placement.css +4 -0
  25. package/components/extensions.css +70 -0
  26. package/components/index.ts +18 -2
  27. package/components/media-parity.css +3 -0
  28. package/components/patterns.css +39 -0
  29. package/components/styles.css +15 -8
  30. package/components/theme-arknights.css +306 -0
  31. package/components/theme-neo-brutalism.css +90 -0
  32. package/components/theme.tsx +58 -0
  33. package/components/timeline.css +23 -0
  34. package/components/tokens.css +49 -1
  35. package/docs/ANTD_MUI_AUDIT.md +38 -0
  36. package/docs/APPLICATION_COMPONENTS.md +175 -0
  37. package/docs/DESIGN_SYSTEM.md +12 -2
  38. package/docs/DESIGN_SYSTEM_AUDIT.md +6 -4
  39. package/docs/INTERACTION_PATTERNS.md +129 -0
  40. package/docs/ISSUE_4_VALIDATION.md +21 -0
  41. package/docs/ISSUE_VALIDATION.md +52 -0
  42. package/docs/REFERENCE_RESEARCH.md +38 -0
  43. package/docs/THEME_NEO_BRUTALISM.md +37 -0
  44. package/docs/THEMING.md +145 -0
  45. package/docs/assets/application-shell.png +0 -0
  46. package/docs/assets/issue4-shell.png +0 -0
  47. package/docs/assets/media-viewer-mobile.png +0 -0
  48. package/docs/assets/monitoring-desktop.png +0 -0
  49. package/docs/audits/DATA_ANTD_MUI.md +75 -0
  50. package/docs/audits/FORMS_ANTD_MUI.md +88 -0
  51. package/docs/audits/MONITORING_ANTD_MUI.md +44 -0
  52. package/docs/audits/SHELL_MEDIA_ANTD_MUI.md +91 -0
  53. package/package.json +5 -1
  54. package/preview/BackgroundGallery.tsx +46 -0
  55. package/preview/CommonGallery.tsx +8 -8
  56. package/preview/ExtensionsGallery.tsx +95 -0
  57. package/preview/GalleryShell.tsx +107 -0
  58. package/preview/LineHeightGallery.tsx +27 -0
  59. package/preview/MonitoringGallery.tsx +27 -0
  60. package/preview/MotionGallery.tsx +6 -2
  61. package/preview/PatternGallery.tsx +52 -0
  62. package/preview/SpacingGallery.tsx +32 -0
  63. package/preview/SubscriptionGallery.tsx +84 -0
  64. package/preview/backgrounds.css +10 -0
  65. package/preview/extensions.css +6 -0
  66. package/preview/line-height.css +2 -0
  67. package/preview/main.tsx +48 -25
  68. package/preview/patterns.css +7 -0
  69. package/preview/preview.css +2 -8
  70. package/preview/shell.css +35 -0
  71. package/preview/spacing.css +2 -0
  72. package/preview/subscriptions.css +27 -0
  73. package/preview/theme-arknights.css +234 -0
  74. package/preview/theme-neo-brutalism.css +11 -0
  75. package/skills/rhine-design/SKILL.md +26 -3
  76. package/skills/rhine-design/references/API_MAP.md +11 -3
  77. package/skills/rhine-design/references/TASK_MAP.md +5 -1
  78. package/skills/rhine-design/references/integration.md +7 -3
  79. package/tests/Background.test.tsx +22 -0
  80. package/tests/DataParity.test.tsx +148 -0
  81. package/tests/Extensions.test.tsx +145 -0
  82. package/tests/FormParity.test.tsx +185 -0
  83. package/tests/GalleryShell.test.tsx +98 -0
  84. package/tests/Monitoring.test.tsx +38 -0
  85. package/tests/MonitoringParity.test.tsx +125 -0
  86. package/tests/Patterns.test.tsx +75 -0
  87. package/tests/ShellMediaParity.test.tsx +180 -0
  88. package/tests/StatusTimeline.test.tsx +15 -0
  89. package/tests/Theme.test.tsx +112 -0
@@ -0,0 +1,84 @@
1
+ import { useState } from 'react';
2
+ import { Badge, Button, DataTable, Dialog, Grid, Inline, KeyValue, Menu, Metric, Select, Stack, Surface, Text, TextField } from '../components';
3
+ import './subscriptions.css';
4
+
5
+ const initialPlans = [
6
+ { id: 'gpt100', name: 'GPT100', color: 'green', count: 12, price: 100, description: '日常创作与轻量任务', status: '运行中' },
7
+ { id: 'gpt200', name: 'GPT200', color: 'orange', count: 8, price: 200, description: '复杂推理与高频工作', status: '运行中' },
8
+ { id: 'grok', name: 'Grok', color: 'violet', count: 4, price: 80, description: '探索灵感与多样表达', status: '运行中' },
9
+ ];
10
+ const money = (value: number) => `¥${value.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
11
+ type Plan = typeof initialPlans[number];
12
+ function TypeTag({ plan }: { plan: Plan }) {
13
+ return <span className="rhine-type-tag" data-color={plan.color}>{plan.name}</span>;
14
+ }
15
+
16
+ export function SubscriptionGallery() {
17
+ const [plans, setPlans] = useState(initialPlans);
18
+ const [createOpen, setCreateOpen] = useState(false);
19
+ const [planId, setPlanId] = useState('gpt100');
20
+ const [quantity, setQuantity] = useState('1');
21
+ const [error, setError] = useState('');
22
+ const [message, setMessage] = useState('');
23
+ const [showPrices, setShowPrices] = useState(true);
24
+ const total = plans.reduce((sum, plan) => sum + plan.count, 0);
25
+ const cost = plans.reduce((sum, plan) => sum + plan.count * plan.price, 0);
26
+ const displayMoney = (value: number) => showPrices ? money(value) : '••••';
27
+ const selectedPlan = plans.find(plan => plan.id === planId)!;
28
+
29
+ return <section id="subscriptions" className="ds-subscriptions">
30
+ <div className="ds-subscription-heading">
31
+ <div><Text variant="overline" tone="muted" lang="en">YOUR SUBSCRIPTIONS, AT A GLANCE</Text><h1>订阅,一目了然<span aria-hidden="true">.</span></h1><Text tone="muted">数量、支出与状态,在这里轻松掌握。</Text></div>
32
+ <Inline gap={3}>
33
+ <Dialog title="新增订阅" description="选择类型与数量,预览更新后的订阅概览。仅使用演示数据,不产生实际扣费。" open={createOpen} onOpenChange={open => { setCreateOpen(open); setError(''); }} trigger={<Button>+ 新增订阅</Button>}>
34
+ <form onSubmit={event => {
35
+ event.preventDefault();
36
+ const count = Number(quantity);
37
+ if (!Number.isInteger(count) || count < 1 || count > 99) { setError('请输入 1–99 之间的整数。'); return; }
38
+ setPlans(current => current.map(plan => plan.id === planId ? { ...plan, count: plan.count + count } : plan));
39
+ setMessage(`已添加 ${count} 个 ${selectedPlan.name} 演示订阅。`);
40
+ setCreateOpen(false); setQuantity('1');
41
+ }} noValidate>
42
+ <Stack gap={4}>
43
+ <Select label="订阅类型" value={planId} onChange={event => setPlanId(event.target.value)}>{plans.map(plan => <option value={plan.id} key={plan.id}>{plan.name}</option>)}</Select>
44
+ <TextField label="订阅数量" type="number" inputMode="numeric" min={1} max={99} value={quantity} onChange={event => setQuantity(event.target.value)} error={error} hint="每次可添加 1–99 个演示订阅。" />
45
+ <KeyValue items={[{ key: 'price', label: '单价 / 月', value: displayMoney(selectedPlan.price) }]} />
46
+ <Inline gap={3}><Button type="submit">添加到概览</Button><Button variant="secondary" onClick={() => setCreateOpen(false)}>取消</Button></Inline>
47
+ </Stack>
48
+ </form>
49
+ </Dialog>
50
+ <Menu label="订阅管理" trigger={<Button variant="secondary" aria-label="管理订阅">管理 ⋯</Button>} items={[
51
+ { id: 'amounts', label: showPrices ? '隐藏金额' : '显示金额', onSelect: () => setShowPrices(value => !value) },
52
+ { id: 'reset', label: '重置演示数据', onSelect: () => { setPlans(initialPlans); setMessage('演示数据已重置。'); } },
53
+ ]} />
54
+ </Inline>
55
+ </div>
56
+ <div className="ds-subscription-notice"><span>主题场景 / 订阅管理</span><span>示例数量与价格 · 无实际交易</span></div>
57
+ <div role="status" className="ds-subscription-status">{message}</div>
58
+ <Surface className="ds-subscription-summary"><Grid minWidth={180} gap={6}>
59
+ <Metric label="订阅总数" value={total} unit="个" description="所有订阅均在正常运行" />
60
+ <Metric label="每月支出" value={displayMoney(cost)} description="按当前演示订阅计算" />
61
+ <Metric label="订阅类型" value="03" unit="类" description="GPT100 / GPT200 / Grok" />
62
+ </Grid></Surface>
63
+ <div className="ds-subscription-section-head"><Text as="h2" variant="section">我的订阅</Text><Text tone="muted" variant="caption">点击卡片,查看明细 ↗</Text></div>
64
+ <div className="ds-subscription-cards">{plans.map(plan => <Dialog key={plan.id} title={`${plan.name} 订阅明细`} description={plan.description} trigger={
65
+ <button type="button" className="rhine-surface ds-subscription-card" aria-label={`查看 ${plan.name} 订阅明细`}>
66
+ <span className="ds-subscription-card-top"><TypeTag plan={plan} /><span className="ds-subscription-arrow" aria-hidden="true">↗</span></span>
67
+ <span className="ds-subscription-count">{plan.count}<small>个订阅</small></span>
68
+ <span className="ds-subscription-description">{plan.description}</span>
69
+ <span className="ds-subscription-card-bottom"><span className="ds-subscription-running">● {plan.status}</span><strong>{displayMoney(plan.count * plan.price)}<small> / 月</small></strong></span>
70
+ </button>
71
+ }><Stack gap={6}><Inline><TypeTag plan={plan} /><Badge tone="success">{plan.status}</Badge></Inline><Metric label="当前订阅数量" value={plan.count} unit="个" /><KeyValue items={[
72
+ { key: 'price', label: '单价 / 月', value: displayMoney(plan.price) },
73
+ { key: 'total', label: '每月合计', value: displayMoney(plan.count * plan.price) },
74
+ { key: 'cycle', label: '计费周期', value: '按月 · 演示数据' },
75
+ ]} /><Text variant="caption" tone="muted">这是交互示例,价格不代表真实产品报价。</Text></Stack></Dialog>)}</div>
76
+ <Surface className="ds-subscription-table"><DataTable caption="每月费用明细" rows={plans} rowKey={plan => plan.id} columns={[
77
+ { key: 'type', header: '订阅类型', render: plan => <TypeTag plan={plan} /> },
78
+ { key: 'count', header: '数量', align: 'right', render: plan => `${plan.count} 个` },
79
+ { key: 'price', header: '单价 / 月', align: 'right', render: plan => displayMoney(plan.price) },
80
+ { key: 'total', header: '合计 / 月', align: 'right', render: plan => <strong>{displayMoney(plan.count * plan.price)}</strong> },
81
+ { key: 'status', header: '状态', render: plan => <Badge tone="success">{plan.status}</Badge> },
82
+ ]} /><div className="ds-subscription-total"><Text tone="muted">每月合计</Text><Text variant="heading" numeric>{displayMoney(cost)}</Text></div></Surface>
83
+ </section>;
84
+ }
@@ -0,0 +1,10 @@
1
+ .ds-background-controls { display:flex; flex-wrap:wrap; align-items:end; gap:16px; margin:24px 0; }
2
+ .ds-background-controls .rhine-select { width:min(100%,260px); }
3
+ .ds-background-scenes { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr)); gap:24px; }
4
+ .ds-background-example { min-width:0; }
5
+ .ds-background-label { display:flex; flex-wrap:wrap; justify-content:space-between; gap:8px; margin-bottom:8px; }
6
+ .ds-background-label code { font:12px/20px var(--rhine-mono); color:var(--rhine-muted); }
7
+ .ds-background-sample { display:grid; align-content:center; gap:8px; min-height:136px; padding:24px; border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card); overflow-wrap:anywhere; }
8
+ .ds-background-backdrop { padding:16px; background:var(--rhine-backdrop-bg); border-radius:var(--rhine-radius-card); }
9
+ .ds-background-backdrop .ds-background-sample { min-height:104px; padding:16px; }
10
+ .ds-background-local { margin-top:24px; }
@@ -0,0 +1,6 @@
1
+ .ds-extension-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,360px),1fr)); gap:24px; margin-top:24px; }
2
+ .ds-extension-grid>* { min-width:0; }
3
+ .ds-extension-controls { display:flex; flex-wrap:wrap; align-items:center; gap:16px; margin-bottom:24px; }
4
+ .ds-extension-controls .rhine-select { width:auto; max-width:100%; }
5
+ .ds-shell-stage { height:680px; max-height:85dvh; min-height:380px; min-width:0; }
6
+ .ds-background-swatch { display:grid; gap:24px; padding:24px; border:1px solid var(--rhine-line); }
@@ -0,0 +1,2 @@
1
+ .ds-leading-sample[data-guides=true] { background-image:repeating-linear-gradient(to bottom,var(--rhine-selected) 0,var(--rhine-selected) calc(var(--ds-leading) - 1px),var(--rhine-line) calc(var(--ds-leading) - 1px),var(--rhine-line) var(--ds-leading)); }
2
+ .ds-leading-paragraphs { max-width:44ch; }
package/preview/main.tsx CHANGED
@@ -5,6 +5,14 @@ import './preview.css';
5
5
  import { CommonGallery } from './CommonGallery';
6
6
  import { MotionGallery } from './MotionGallery';
7
7
  import { StartupGallery } from './StartupGallery';
8
+ import { PatternGallery } from './PatternGallery';
9
+ import { GalleryShell } from './GalleryShell';
10
+ import { SpacingGallery } from './SpacingGallery';
11
+ import { LineHeightGallery } from './LineHeightGallery';
12
+ import { SubscriptionGallery } from './SubscriptionGallery';
13
+ import { BackgroundGallery } from './BackgroundGallery';
14
+ import { AdvancedDataGallery, AdvancedFormsGallery, MediaGallery, ShellGallery } from './ExtensionsGallery';
15
+ import { MonitoringGallery } from './MonitoringGallery';
8
16
 
9
17
  function Gallery() {
10
18
  const [motion, setMotion] = useState('system');
@@ -22,15 +30,30 @@ function Gallery() {
22
30
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
23
31
  useEffect(() => () => { if (timer.current) clearTimeout(timer.current); }, []);
24
32
  const simulate = () => { setBusy(true); timer.current = setTimeout(() => { setBusy(false); setNotice(true); }, 900); };
25
- return <div className="rhine-theme ds-gallery" data-rhine-motion={motion}>
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
- <main id="top">
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>
29
- <nav className="ds-nav" aria-label="组件目录"><a href="#startup">启动动画</a><a href="#motion">动效实验室</a><a href="#brand">品牌标志</a><a href="#foundation">视觉基础</a><a href="#typography">字体与文字</a><a href="#actions">按钮与输入</a><a href="#selection">选择与切换</a><a href="#feedback">状态与加载</a><a href="#disclosure">展开与组合</a><a href="#data">数据与分页</a><a href="#forms">表单与选择</a><a href="#utilities">操作与布局</a><a href="#overlays">浮层与反馈</a></nav>
33
+ return <GalleryShell motion={motion} onMotionChange={setMotion}>{section => <>
34
+ {section === 'monitoring' && <MonitoringGallery />}
35
+ {section === 'backgrounds' && <BackgroundGallery />}
36
+ {section === 'app-shell' && <ShellGallery />}
37
+ {section === 'advanced-forms' && <AdvancedFormsGallery />}
38
+ {section === 'advanced-data' && <AdvancedDataGallery />}
39
+ {section === 'media' && <MediaGallery />}
40
+ {section === 'subscriptions' && <SubscriptionGallery />}
41
+ {section === 'top' && <>
42
+ <div className="ds-hero"><div><p className="ds-kicker">FOUNDATIONS / COMPONENTS / MOTION</p><h1>精密,有序。<br /><span>每一次交互都有回应。</span></h1><p>59 个通用组件,共用一套视觉变量和动效规则。<br />这里运行的就是后续业务页面可直接导入的 React 组件。</p></div></div>
43
+ <div className="ds-overview-links">{[
44
+ ['foundation','视觉基础','从色彩、间距与材质开始。'],
45
+ ['typography','字体与文字','清晰的层级,稳定的阅读节奏。'],
46
+ ['patterns','场景与交互指南','先明确任务,再选择组件和动效。'],
47
+ ['motion','动效实验室','播放、重播,观察交互的连续性。'],
48
+ ].map(([id,title,description]) => <a key={id} href={`#${id}`}><strong>{title}</strong><span aria-hidden="true">↗</span><p>{description}</p></a>)}</div>
49
+ </>}
30
50
 
31
- <StartupGallery reduced={motion==='reduced'} />
32
- <MotionGallery reduced={motion==='reduced'} />
33
- <section id="brand"><div className="ds-section-title"><span>00</span><h2>三角构架 · Rhine Design</h2></div>
51
+
52
+ {['navigation','form-workflow','patterns'].includes(section) && <PatternGallery section={section} />}
53
+ {section === 'spacing' && <SpacingGallery />}
54
+ {section === 'startup' && <StartupGallery reduced={motion==='reduced'} />}
55
+ {section === 'motion' && <MotionGallery reduced={motion==='reduced'} />}
56
+ {section === 'brand' && <section id="brand"><div className="ds-section-title"><span>00</span><h2>三角构架 · Rhine Design</h2></div>
34
57
  <div className="ds-logo-pair">
35
58
  <div className="ds-logo-stage"><Logo variant="icon" size={192} theme="light" /><span>浅色 / LIGHT</span></div>
36
59
  <div className="ds-logo-stage ds-logo-stage--dark"><Logo variant="icon" size={192} theme="dark" /><span>深色 / DARK</span></div>
@@ -38,14 +61,14 @@ function Gallery() {
38
61
  <p className="ds-caption">浅色与深色共用同一份矢量母版,图形在方形画布内水平、垂直居中。</p>
39
62
  <div className="ds-grid"><Surface><div className="ds-component-title"><h3>Logo · 尺寸与组合</h3><span>16 / 24 / 32 / 48 / 64px</span></div><div className="ds-row">{[16,24,32,48,64].map(size=><Logo key={size} size={size} label={`Rhine Design ${size}px`} />)}</div><div className="ds-gap-top"><Logo variant="lockup" size={48} /></div></Surface>
40
63
  <Surface><div className="ds-component-title"><h3>Logo · 三块归位</h3><span>240ms / 单次入场</span></div><div className="ds-row"><Logo key={logoReplay} size={80} animate /><Button variant="secondary" onClick={()=>setLogoReplay(value=>value+1)}>重播标志动画</Button></div><p className="ds-caption">默认静态;减少动态时直接呈现。标志不表示同步进度。</p></Surface></div>
41
- </section>
64
+ </section>}
42
65
 
43
- <section id="foundation"><div className="ds-section-title"><span>01</span><h2>同一套变量,贯穿所有组件</h2></div>
66
+ {section === 'foundation' && <section id="foundation"><div className="ds-section-title"><span>01</span><h2>同一套变量,贯穿所有组件</h2></div>
44
67
  <div className="ds-palette">{[['PAPER','#F2F1E9'],['INK','#242B28'],['SAGE','#E5ECE3'],['GREEN','#425C4B'],['SIGNAL','#C65C34']].map(([name,color])=><div key={name}><div style={{background:color}} /><strong>{name}</strong><code>{color}</code></div>)}</div>
45
68
  <div className="ds-notes"><span>14px 正文 / 12px 辅助信息</span><span>4px 卡片 / 3px 按钮圆角</span><span>120–240ms 交互过渡</span></div>
46
- </section>
69
+ </section>}
47
70
 
48
- <section id="typography"><div className="ds-section-title"><span>TYPE</span><h2>字体与文字 · 清晰先于装饰</h2></div>
71
+ {section === 'typography' && <section id="typography"><div className="ds-section-title"><span>TYPE</span><h2>字体与文字 · 清晰先于装饰</h2></div>
49
72
  <p className="ds-caption">系统无衬线用于中英文正文;等宽字体用于路径、模型标识和代码。业务字号不低于 12px,中文不增加装饰字距。</p>
50
73
  <Surface><div className="ds-type-scale">{([
51
74
  ['title','30 / 38 · 600','连接你的工作空间'],
@@ -58,6 +81,7 @@ function Gallery() {
58
81
  ['metric','26 / 32 · 600','1,280.00'],
59
82
  ['code','12 / 20 · 400','~/.codex/auth.json'],
60
83
  ] satisfies [TextVariant,string,string][]).map(([variant,spec,sample])=><div className="ds-type-row" key={variant}><div><Text variant="code">{variant}</Text><Text variant="caption" tone="muted">{spec}</Text></div><Text variant={variant}>{sample}</Text></div>)}</div></Surface>
84
+ <LineHeightGallery />
61
85
  <div className="ds-grid ds-gap-top"><Surface><Text as="h3" variant="heading">中英文与数字</Text><div className="ds-type-copy"><Text>已完成 128 次请求,共使用 1.4M Token。</Text><Text numeric>预估费用 $5.44 · 平均耗时 2.4 s</Text><Text tone="muted" variant="caption">2026-10-22 14:32 · 上海(UTC+8)</Text><Text variant="overline" lang="en">Usage / Today</Text><Text>未采集的数据显示「—」,不替换为 0。小数位按指标语义固定,原始值保持可查。</Text></div></Surface>
62
86
  <Surface><Text as="h3" variant="heading">换行与状态文案</Text><div className="ds-type-copy"><Text>长标题、错误原因和下一步操作允许自然换行。</Text><Text variant="code">example-very-long-model-identifier-for-wrapping-preview-2026-10-22</Text><Text tone="error">暂时无法读取列表。请检查连接后重试。</Text><Text variant="caption" tone="muted">文字不缩放、不跑马灯、不逐字播放;复制或展开应能获取完整内容。</Text></div></Surface></div>
63
87
  <div className="ds-gap-top"><Toolbar title="Rhine Design / 设计评审" description="工具栏与静态页面样稿共用一套样式。">
@@ -65,36 +89,35 @@ function Gallery() {
65
89
  <Select label="场景示例" defaultValue="normal"><option value="normal">正常</option><option value="loading">读取中 / 等待授权</option></Select>
66
90
  </Toolbar></div>
67
91
  <div className="ds-grid ds-gap-top"><Select label="错误状态" defaultValue="" error="请选择一个页面后继续。"><option value="" disabled>请选择</option><option value="usage">数据概览</option></Select><Select label="禁用状态" disabled defaultValue="fixed" hint="原生菜单保留键盘操作,箭头不接收点击。"><option value="fixed">暂不可更改</option></Select></div>
68
- </section>
92
+ </section>}
69
93
 
70
- <section id="actions"><div className="ds-section-title"><span>02</span><h2>操作有分量,反馈不打断</h2></div>
94
+ {section === 'actions' && <section id="actions"><div className="ds-section-title"><span>02</span><h2>操作有分量,反馈不打断</h2></div>
71
95
  <div className="ds-grid"><Surface><div className="ds-component-title"><h3>Button</h3><span>悬停 120ms / 按下 80ms</span></div><div className="ds-row"><Button busy={busy} busyLabel="正在保存" onClick={simulate}>保存修改</Button><Button variant="secondary" onClick={()=>setNotice(false)}>取消提示</Button><Button variant="quiet" onClick={()=>setNotice(true)}>查看反馈</Button><Button variant="danger" disabled>删除记录</Button></div><p className="ds-caption">处理中保持尺寸和键盘焦点,阻止重复提交。</p><Button disabled>暂不可用</Button></Surface>
72
96
  <Surface><div className="ds-component-title"><h3>TextField</h3><span>边框反馈 / 不抖动报错</span></div><TextField label="显示名称" value={field} onChange={event=>setField(event.target.value)} hint="此字段仅演示输入状态,不保存数据。" error={showError&&!field.trim()?'请输入显示名称':undefined} placeholder="例如:研究工作区" /><div className="ds-row ds-gap-top"><Button variant="secondary" onClick={()=>setShowError(true)}>验证输入</Button></div></Surface></div>
73
- </section>
97
+ </section>}
74
98
 
75
- <section id="selection"><div className="ds-section-title"><span>03</span><h2>选择清楚,位置稳定</h2></div>
99
+ {section === 'selection' && <section id="selection"><div className="ds-section-title"><span>03</span><h2>选择清楚,位置稳定</h2></div>
76
100
  <div className="ds-grid"><div><SelectionCard label="可选择容器" selected={selected} onSelectedChange={setSelected}><span className="ds-caption">适用于账号、模板或可取消选择项。<br />同一位置切换底色与边框,不抬升卡片。</span><div className="ds-gap-top"><Badge tone={selected?'success':'neutral'}>{selected?'已选择':'未选择'}</Badge></div></SelectionCard><p className="ds-caption">SelectionCard · 背景与边界 120ms</p></div>
77
101
  <Surface><div className="ds-component-title"><h3>SegmentedControl</h3><span>原生单选 / 120ms</span></div><SegmentedControl label="展示指标" items={[{value:'tokens',label:'Token'},{value:'requests',label:'请求数'},{value:'cost',label:'费用',disabled:true}]} value={metric} onValueChange={setMetric} /><p className="ds-caption">当前指标:{metric==='tokens'?'Token':'请求数'}。方向键切换,自动跳过禁用项。</p></Surface></div>
78
102
  <Surface className="ds-gap-top"><div className="ds-component-title"><h3>Tabs</h3><span>下划线与内容进入 180ms</span></div><Tabs label="组件说明" value={tab} onValueChange={setTab} items={[{value:'tokens',label:'视觉变量',content:<p>颜色、间距、字体与动效集中定义。业务页面只消费语义变量。</p>},{value:'disabled',label:'预留示例',disabled:true,content:null},{value:'motion',label:'动效规则',content:<p>内容进入最多移动 3px。保留隐藏面板状态,切换不触发重复业务操作。</p>}]} /></Surface>
79
- </section>
103
+ </section>}
80
104
 
81
- <section id="feedback"><div className="ds-section-title"><span>04</span><h2>告诉用户发生了什么</h2></div>
105
+ {section === 'feedback' && <section id="feedback"><div className="ds-section-title"><span>04</span><h2>告诉用户发生了什么</h2></div>
82
106
  <div className="ds-row ds-badges"><Badge>未开始</Badge><Badge tone="success">已完成</Badge><Badge tone="warning">需要处理</Badge><Badge tone="error">操作失败</Badge></div>
83
107
  <div className="ds-grid"><Surface><div className="ds-component-title"><h3>Progress</h3><span>数值更新 240ms</span></div><Progress label="进度示例" value={progress} /><div className="ds-row ds-gap-top"><Button variant="secondary" onClick={()=>setProgress(value=>value===null?38:Math.min(100,value+20))}>推进进度</Button><Button variant="quiet" onClick={()=>setProgress(value=>value===null?38:null)}>切换未知进度</Button></div><p className="ds-caption">只有真实进度才显示百分比;未知进度单独表达。</p></Surface>
84
108
  <Surface><div className="ds-component-title"><h3>Skeleton</h3><span>1400ms 低幅明暗</span></div>{loading?<Skeleton label="正在读取组件示例" />:<p className="ds-loaded">内容已准备好。加载占位与内容共用容器。</p>}<div className="ds-gap-top"><Button variant="secondary" onClick={()=>setLoading(value=>!value)}>切换加载状态</Button></div></Surface></div>
85
109
  <div className="ds-gap-top"><Button variant="secondary" onClick={()=>setNotice(value=>!value)}>切换提示</Button></div>
86
110
  {notice&&<div className="ds-gap-top"><Notice tone="success" title="修改已保存(演示)" action={<Button variant="quiet" onClick={()=>setNotice(false)}>关闭提示</Button>}>这是组件的本地反馈,没有写入任何业务数据。</Notice></div>}
87
111
  <div className="ds-gap-top"><Notice tone="warning" title="状态文字始终可见">颜色和动画只增强反馈,不承担唯一的状态含义。</Notice></div>
88
- </section>
112
+ </section>}
89
113
 
90
- <section id="disclosure"><div className="ds-section-title"><span>05</span><h2>逐步展开,保持可读</h2></div>
114
+ {section === 'disclosure' && <section id="disclosure"><div className="ds-section-title"><span>05</span><h2>逐步展开,保持可读</h2></div>
91
115
  <div className="ds-grid"><Surface><div className="ds-component-title"><h3>Disclosure</h3><span>实测高度 / 200ms</span></div><Disclosure title="查看组件动画规则"><p>展开和收起都支持中途反向。关闭后内容不参与 Tab 导航。</p><Button variant="secondary" onClick={()=>setNotice(true)}>内容内操作</Button><p>减少动态模式直接到达最终状态,信息不会丢失。</p></Disclosure></Surface>
92
116
  <EmptyState title="暂无记录" description="用一句话说明当前状态,按业务需要提供一个下一步操作。" action={<Button variant="secondary" onClick={()=>setNotice(true)}>演示下一步</Button>} /></div>
93
- </section>
94
- <CommonGallery />
95
- <footer className="ds-footer"><span>Rhine Design · 通用设计组件 V1.4</span><span>合成数据;复制示例仅在点击时写入演示文字。</span></footer>
96
- </main>
97
- </div>;
117
+ </section>}
118
+ {['data','forms','utilities','overlays'].includes(section) && <CommonGallery key={section} section={section} />}
119
+ </>}</GalleryShell>;
120
+
98
121
  }
99
122
 
100
123
  createRoot(document.getElementById('root')!).render(<StrictMode><Gallery /></StrictMode>);
@@ -0,0 +1,7 @@
1
+ .ds-navigation-demo { display:grid; grid-template-columns:180px minmax(0,1fr); gap:24px; margin-top:20px; }
2
+ .ds-navigation-content { border-inline-start:1px solid var(--rhine-line); padding:24px; }
3
+ .ds-pattern-detail { margin-top:28px; padding-top:24px; border-top:1px solid var(--rhine-line); animation:rhine-pattern-enter var(--rhine-duration-panel) var(--rhine-ease-standard); }
4
+ .ds-pattern-detail h3 { font-size:22px; line-height:1.4; margin:8px 0 24px; overflow-wrap:anywhere; }
5
+ .ds-pattern-detail dl { display:grid; grid-template-columns:80px minmax(0,1fr); gap:20px; margin:0; }
6
+ .ds-pattern-detail dt { font-size:12px; color:var(--rhine-muted); }.ds-pattern-detail dd { margin:0; }
7
+ @media(max-width:600px) { .ds-navigation-demo { grid-template-columns:1fr; gap:16px; }.ds-navigation-content { border-inline-start:0; border-top:1px solid var(--rhine-line); padding:20px 0; }.ds-pattern-detail dl { grid-template-columns:1fr; gap:8px; }.ds-pattern-detail dd { margin-bottom:12px; } }
@@ -3,25 +3,19 @@ body { margin:0; }
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(--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
6
  .ds-logo-pair { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; }
10
7
  .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
8
  .ds-logo-stage>span:last-child { font:12px var(--rhine-mono); letter-spacing:2px; }
12
9
  .ds-logo-stage--dark { background:var(--rhine-ink); color:var(--rhine-bg); border-color:var(--rhine-ink); }
13
10
  @media(max-width:600px) { .ds-logo-pair { grid-template-columns:1fr; } }
14
- .ds-gallery main { max-width:1128px; padding:0 24px; margin:auto; }
15
- .ds-hero { display:grid; grid-template-columns:1.5fr 1fr; gap:48px; padding:56px 0 40px; align-items:end; }
11
+ .ds-hero { display:grid; grid-template-columns:1fr; gap:48px; padding:56px 0 40px; align-items:end; }
16
12
  .ds-kicker { font:12px var(--rhine-mono); letter-spacing:1px; color:var(--rhine-muted); }
17
13
  .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
14
  .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
15
  .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
16
  .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
17
  .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
18
  .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
19
  .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
20
  .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; } }
21
+ @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-palette { grid-template-columns:repeat(auto-fit,minmax(min(100%,90px),1fr)); }.ds-palette>div>div { height:64px; }.ds-section-title h2 { font-size:19px; } }
@@ -0,0 +1,35 @@
1
+ .ds-shell { display:grid; grid-template-columns:232px minmax(0,1fr); height:100dvh; background:var(--rhine-bg); }
2
+ .ds-skip { position:fixed; top:8px; left:16px; z-index:100; padding:12px; background:var(--rhine-ink); color:var(--rhine-surface); transform:translateY(-160%); }
3
+ .ds-skip:focus { transform:none; }
4
+ .ds-sidebar { display:flex; flex-direction:column; min-height:0; border-right:1px solid var(--rhine-line); background:var(--rhine-surface); }
5
+ .ds-sidebar-brand { padding:28px 24px 24px; }
6
+ .ds-sidebar-brand>a { display:flex; gap:12px; align-items:center; color:var(--rhine-ink); text-decoration:none; font-size:18px; font-weight:600; }
7
+ .ds-sidebar-brand small { display:block; margin-top:2px; font:10px/16px var(--rhine-mono); letter-spacing:.08em; color:var(--rhine-muted); }
8
+ .ds-sidebar-nav { flex:1; min-height:0; overflow:auto; padding:0 16px 20px; scrollbar-width:thin; }
9
+ .ds-sidebar-foot { display:grid; gap:6px; padding:20px 28px; border-top:1px solid var(--rhine-line); color:var(--rhine-muted); font:12px/18px var(--rhine-font); }
10
+ .ds-sidebar-foot>span:first-child { font:10px/16px var(--rhine-mono); letter-spacing:.06em; }
11
+ .ds-menu-toggle { display:none; }
12
+ .ds-workspace { min-width:0; min-height:0; overflow:auto; scrollbar-gutter:stable; }
13
+ .ds-workspace:focus { outline:none; }
14
+ .ds-workspace-head { min-height:80px; display:flex; align-items:center; justify-content:space-between; gap:20px; padding:16px 32px; border-bottom:1px solid var(--rhine-line); }
15
+ .ds-head-controls { display:flex; align-items:flex-end; gap:16px; flex-wrap:wrap; }
16
+ .ds-workspace-head>div:first-child { font-size:12px; }.ds-workspace-head>div:first-child>span { color:var(--rhine-muted); }
17
+ .ds-page { max-width:1200px; margin:0 auto; padding:0 32px; animation:rhine-enter var(--rhine-duration-panel) var(--rhine-ease-standard); }
18
+ .ds-overview-links { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; padding-bottom:40px; }
19
+ .ds-overview-links>a { display:grid; grid-template-columns:1fr auto; gap:12px; padding:24px; color:var(--rhine-ink); text-decoration:none; background:var(--rhine-surface); border:1px solid var(--rhine-line); transition:border-color var(--rhine-duration-hover) var(--rhine-ease-standard); }
20
+ .ds-overview-links>a:hover { border-color:var(--rhine-green); }.ds-overview-links strong { font-size:18px; }.ds-overview-links p { grid-column:1/-1; margin:0; color:var(--rhine-muted); }
21
+ @media(min-width:761px) and (max-width:1100px) {
22
+ .ds-shell { grid-template-columns:200px minmax(0,1fr); }.ds-page { padding:0 24px; }
23
+ .ds-page .ds-grid,.ds-page .ds-startup-layout,.ds-page .ds-motion-grid { grid-template-columns:1fr; }
24
+ .ds-page .ds-hero { grid-template-columns:1fr; gap:24px; }.ds-page .ds-hero h1 { font-size:36px; }
25
+ .ds-page .ds-type-row { grid-template-columns:130px minmax(0,1fr); }
26
+ }
27
+ @media(max-width:760px) {
28
+ .ds-shell { grid-template-columns:minmax(0,1fr); grid-template-rows:auto minmax(0,1fr); }
29
+ .ds-sidebar { border-right:0; border-bottom:1px solid var(--rhine-line); }
30
+ .ds-sidebar-brand { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:12px 20px; }
31
+ .ds-menu-toggle { display:inline-flex; }.ds-sidebar-foot { display:none; }
32
+ .ds-sidebar-nav { display:none; flex:none; max-height:45dvh; }.ds-sidebar[data-open=true] .ds-sidebar-nav { display:block; }
33
+ .ds-workspace-head { min-height:0; padding:16px 20px; flex-wrap:wrap; gap:12px; }
34
+ .ds-page { padding:0 20px; }.ds-overview-links { grid-template-columns:1fr; }
35
+ }
@@ -0,0 +1,2 @@
1
+ .ds-spacing-scale { display:grid; grid-template-columns:88px 48px minmax(0,1fr); align-items:center; gap:var(--rhine-space-4); }
2
+ .ds-spacing-bar { display:block; height:var(--rhine-space-3); background:var(--rhine-green); }
@@ -0,0 +1,27 @@
1
+ .ds-subscription-heading { display: flex; justify-content: space-between; align-items: center; gap: 24px; flex-wrap: wrap; }
2
+ .ds-subscription-heading h1 { font-size: clamp(28px, 3vw, 40px); line-height: 1.3; font-weight: 800; letter-spacing: -1px; margin: 12px 0; }
3
+ .ds-subscription-heading h1>span { color: var(--rhine-signal); }
4
+ .ds-subscription-notice { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; margin-top: 24px; font-size: 12px; color: var(--rhine-muted); }
5
+ .ds-subscription-status { color: var(--rhine-green); font-size: 12px; }
6
+ .ds-subscription-status:not(:empty) { margin-top: 16px; }
7
+ .ds-subscription-summary { margin-top: 24px; }
8
+ .ds-subscription-section-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px; margin: 32px 0 20px; }
9
+ .ds-subscription-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); gap: 24px; }
10
+ .ds-subscription-cards>.rhine-overlay-anchor { display: block; min-width: 0; }
11
+ .ds-subscription-card { display: flex; flex-direction: column; width: 100%; height: 100%; text-align: left; font: inherit; color: var(--rhine-ink); cursor: pointer; transition: background-color 120ms; }
12
+ .ds-subscription-card:hover { background: var(--rhine-selected); }
13
+ .ds-subscription-card-top { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
14
+ .ds-subscription-arrow { font-size: 24px; }
15
+ .ds-subscription-count { display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px; font-size: 52px; font-weight: 800; line-height: 1.2; margin: 24px 0 8px; font-variant-numeric: tabular-nums; }
16
+ .ds-subscription-count small, .ds-subscription-card-bottom small { font-size: 12px; color: var(--rhine-muted); font-weight: 400; }
17
+ .ds-subscription-description { font-size: 12px; color: var(--rhine-muted); margin-bottom: 24px; }
18
+ .ds-subscription-card-bottom { display: flex; justify-content: space-between; align-items: baseline; flex-wrap: wrap; gap: 12px; margin-top: auto; padding-top: 16px; border-top: 1px solid var(--rhine-line); }
19
+ .ds-subscription-running { color: var(--rhine-green); font-size: 12px; }
20
+ .ds-subscription-card-bottom strong { font-size: 16px; font-variant-numeric: tabular-nums; }
21
+ .ds-subscription-table { margin-top: 32px; }
22
+ .ds-subscription-total { display: flex; justify-content: space-between; gap: 16px; padding-top: 20px; }
23
+ .ds-subscriptions .rhine-type-tag { font-size: 12px; font-weight: 700; }
24
+ @media(max-width:480px) {
25
+ .ds-subscription-heading h1 { font-size: 28px; }
26
+ .ds-subscription-summary .rhine-grid { grid-template-columns: 1fr; }
27
+ }
@@ -0,0 +1,234 @@
1
+ /* Gallery-only chrome and decoration. Not imported by the public theme entry. */
2
+ /* ================= 全局质感 ================= */
3
+
4
+ /* 坐标网格底纹 + 顶部扫描线氛围 */
5
+ .rhine-theme[data-rhine-theme="arknights"] .ds-workspace {
6
+ background-image:
7
+ linear-gradient(var(--ak-gridline) 1px, transparent 1px),
8
+ linear-gradient(90deg, var(--ak-gridline) 1px, transparent 1px);
9
+ background-size: 56px 56px;
10
+ }
11
+
12
+ /* ================= 字体性格 ================= */
13
+ /* 标签、编号、说明全部等宽化,正文保持无衬线可读性 */
14
+ .rhine-theme[data-rhine-theme="arknights"] .ds-caption,
15
+ .rhine-theme[data-rhine-theme="arknights"] .ds-component-title>span,
16
+ .rhine-theme[data-rhine-theme="arknights"] .ds-notes {
17
+ font-family: var(--rhine-mono);
18
+ letter-spacing: .04em;
19
+ }
20
+
21
+ /* ================= 顶栏 ================= */
22
+ .rhine-theme[data-rhine-theme="arknights"] .ds-workspace-head {
23
+ position: relative;
24
+ background: rgba(11, 13, 16, .85);
25
+ border-bottom-color: var(--rhine-line);
26
+ }
27
+ .rhine-theme[data-rhine-theme="arknights"] .ds-workspace-head::after {
28
+ content: '';
29
+ position: absolute;
30
+ left: 32px;
31
+ bottom: -1px;
32
+ width: 160px;
33
+ height: 4px;
34
+ background: var(--ak-hazard);
35
+ }
36
+
37
+ /* ================= 侧栏:深色舱室 ================= */
38
+ .rhine-theme[data-rhine-theme="arknights"] .ds-sidebar {
39
+ background: #08090c;
40
+ border-right-color: var(--rhine-line);
41
+ }
42
+ .rhine-theme[data-rhine-theme="arknights"] .ds-sidebar-brand {
43
+ border-bottom: 1px solid var(--rhine-line);
44
+ background: repeating-linear-gradient(-45deg, rgba(255, 212, 0, .07) 0 10px, transparent 10px 20px);
45
+ }
46
+ .rhine-theme[data-rhine-theme="arknights"] .ds-sidebar-brand>a { color: var(--rhine-ink); }
47
+ .rhine-theme[data-rhine-theme="arknights"] .ds-sidebar-brand small { color: var(--ak-accent); letter-spacing: .18em; }
48
+ .rhine-theme[data-rhine-theme="arknights"] .ds-sidebar-foot>span:first-child { color: var(--ak-accent); }
49
+
50
+ /* ================= 主视觉 Hero ================= */
51
+ .rhine-theme[data-rhine-theme="arknights"] .ds-kicker {
52
+ color: var(--ak-accent);
53
+ letter-spacing: 3px;
54
+ }
55
+ .rhine-theme[data-rhine-theme="arknights"] .ds-kicker::before { content: '// '; }
56
+ .rhine-theme[data-rhine-theme="arknights"] .ds-hero h1 {
57
+ font-size: 56px;
58
+ font-weight: 700;
59
+ letter-spacing: 0;
60
+ line-height: 1.2;
61
+ }
62
+ .rhine-theme[data-rhine-theme="arknights"] .ds-hero h1>span {
63
+ color: var(--ak-accent);
64
+ /* 黄色高光下划线块 */
65
+ background: linear-gradient(transparent 62%, rgba(255, 212, 0, .22) 62%);
66
+ }
67
+ @media(max-width:760px) {
68
+ .rhine-theme[data-rhine-theme="arknights"] .ds-hero h1 { font-size: 34px; }
69
+ }
70
+
71
+ /* 总览卡片:大号幽灵编号 + 斜切 */
72
+ .rhine-theme[data-rhine-theme="arknights"] .ds-overview-links { counter-reset: ak-link; }
73
+ .rhine-theme[data-rhine-theme="arknights"] .ds-overview-links>a {
74
+ counter-increment: ak-link;
75
+ position: relative;
76
+ background: var(--ak-panel);
77
+ clip-path: polygon(0 0, calc(100% - var(--ak-cut)) 0, 100% var(--ak-cut), 100% 100%, 0 100%);
78
+ overflow: hidden;
79
+ }
80
+ .rhine-theme[data-rhine-theme="arknights"] .ds-overview-links>a::after {
81
+ content: counter(ak-link, decimal-leading-zero);
82
+ position: absolute;
83
+ right: 12px;
84
+ bottom: -14px;
85
+ font: 700 72px/1 var(--rhine-mono);
86
+ color: rgba(238, 240, 236, .05);
87
+ pointer-events: none;
88
+ }
89
+ .rhine-theme[data-rhine-theme="arknights"] .ds-overview-links>a:hover {
90
+ border-color: var(--ak-accent);
91
+ background: var(--ak-panel-hi);
92
+ }
93
+ .rhine-theme[data-rhine-theme="arknights"] .ds-overview-links>a:hover>span { color: var(--ak-accent); }
94
+
95
+ /* ================= 章节标题 ================= */
96
+ .rhine-theme[data-rhine-theme="arknights"] .ds-section-title { align-items:center; }
97
+ .rhine-theme[data-rhine-theme="arknights"] .ds-section-title>span {
98
+ background: var(--ak-accent);
99
+ color: var(--ak-accent-ink);
100
+ font: 700 12px/1 var(--rhine-mono);
101
+ padding: 5px 8px;
102
+ clip-path: polygon(0 0, 100% 0, calc(100% - 6px) 100%, 0 100%);
103
+ }
104
+ .rhine-theme[data-rhine-theme="arknights"] .ds-section-title h2 { letter-spacing: .02em; }
105
+
106
+ /* 组件标题等宽化 */
107
+ .rhine-theme[data-rhine-theme="arknights"] .ds-component-title h3 { letter-spacing: .06em; }
108
+
109
+ /* ================= 面板与容器:斜切 + 角标框架 ================= */
110
+ .rhine-theme[data-rhine-theme="arknights"] .ds-logo-stage {
111
+ background: var(--ak-panel);
112
+ clip-path: polygon(0 0, calc(100% - var(--ak-cut)) 0, 100% var(--ak-cut), 100% 100%, 0 100%);
113
+ }
114
+
115
+ /* ================= Logo 展台 ================= */
116
+ .rhine-theme[data-rhine-theme="arknights"] .ds-logo-stage--dark { background: #08090c; border-color: var(--rhine-line); }
117
+ .rhine-theme[data-rhine-theme="arknights"] .ds-logo-stage>span:last-child { letter-spacing: 3px; color: var(--rhine-muted); }
118
+
119
+ /* ================= 页脚 ================= */
120
+ .rhine-theme[data-rhine-theme="arknights"] .ds-footer {
121
+ border-top: 0;
122
+ position: relative;
123
+ font-family: var(--rhine-mono);
124
+ letter-spacing: .06em;
125
+ }
126
+ .rhine-theme[data-rhine-theme="arknights"] .ds-footer::before {
127
+ content: '';
128
+ position: absolute;
129
+ top: 0;
130
+ left: 0;
131
+ width: 200px;
132
+ height: 4px;
133
+ background: var(--ak-hazard);
134
+ }
135
+
136
+ /* ================= 调色板展示 ================= */
137
+ .rhine-theme[data-rhine-theme="arknights"] .ds-palette>div>div { border-color: var(--rhine-control); }
138
+
139
+ /* ================= 进阶:HUD 视框 / 扫描 / 开机 ================= */
140
+
141
+ /* 终端读数(默认隐藏,仅本主题显示) */
142
+ .ak-readout { display: none; }
143
+ .rhine-theme[data-rhine-theme="arknights"] .ak-readout {
144
+ display: block;
145
+ margin-top: 8px;
146
+ padding-top: 8px;
147
+ border-top: 1px dashed var(--rhine-line);
148
+ color: var(--ak-accent);
149
+ font: 11px/18px var(--rhine-mono);
150
+ letter-spacing: .08em;
151
+ font-variant-numeric: tabular-nums;
152
+ }
153
+
154
+ /* 四角 HUD 取景框 */
155
+ .ak-hud { display: none; }
156
+ .rhine-theme[data-rhine-theme="arknights"] .ak-hud {
157
+ display: block;
158
+ position: fixed;
159
+ inset: 10px;
160
+ z-index: 90;
161
+ pointer-events: none;
162
+ }
163
+ .rhine-theme[data-rhine-theme="arknights"] .ak-hud i {
164
+ position: absolute;
165
+ width: 26px;
166
+ height: 26px;
167
+ border: 2px solid rgba(255, 212, 0, .5);
168
+ }
169
+ .rhine-theme[data-rhine-theme="arknights"] .ak-hud i:nth-child(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; }
170
+ .rhine-theme[data-rhine-theme="arknights"] .ak-hud i:nth-child(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; }
171
+ .rhine-theme[data-rhine-theme="arknights"] .ak-hud i:nth-child(3) { bottom: 0; left: 0; border-right: 0; border-top: 0; }
172
+ .rhine-theme[data-rhine-theme="arknights"] .ak-hud i:nth-child(4) { bottom: 0; right: 0; border-left: 0; border-top: 0; }
173
+
174
+ /* 主题切换开机闪屏 */
175
+ .rhine-theme[data-rhine-theme="arknights"].ds-shell::after {
176
+ content: '';
177
+ position: fixed;
178
+ inset: 0;
179
+ z-index: 200;
180
+ pointer-events: none;
181
+ background:
182
+ repeating-linear-gradient(0deg, rgba(255, 212, 0, .06) 0 2px, transparent 2px 4px),
183
+ #050608;
184
+ animation: ak-boot 520ms steps(3, end) both;
185
+ }
186
+ @keyframes ak-boot {
187
+ 0% { opacity: 1; }
188
+ 55% { opacity: .55; }
189
+ 100% { opacity: 0; visibility: hidden; }
190
+ }
191
+
192
+ /* 屏幕右缘竖排标识 */
193
+ .rhine-theme[data-rhine-theme="arknights"].ds-shell::before {
194
+ content: 'RHINE // TACTICAL TERMINAL';
195
+ position: fixed;
196
+ right: 6px;
197
+ top: 50%;
198
+ transform: translateY(-50%);
199
+ writing-mode: vertical-rl;
200
+ font: 10px/1 var(--rhine-mono);
201
+ letter-spacing: 5px;
202
+ color: rgba(238, 240, 236, .16);
203
+ z-index: 50;
204
+ pointer-events: none;
205
+ }
206
+ @media(max-width:760px) {
207
+ .rhine-theme[data-rhine-theme="arknights"].ds-shell::before { content: none; }
208
+ }
209
+
210
+ /* Hero 扫描线 */
211
+ .rhine-theme[data-rhine-theme="arknights"] .ds-hero { position: relative; overflow: hidden; }
212
+ .rhine-theme[data-rhine-theme="arknights"] .ds-hero::after {
213
+ content: '';
214
+ position: absolute;
215
+ left: 0;
216
+ right: 0;
217
+ top: -120px;
218
+ height: 120px;
219
+ background: linear-gradient(180deg, transparent, rgba(255, 212, 0, .07), transparent);
220
+ pointer-events: none;
221
+ animation: ak-scan 4.5s linear infinite;
222
+ }
223
+ @keyframes ak-scan {
224
+ from { top: -120px; }
225
+ to { top: 100%; }
226
+ }
227
+
228
+ /* 减少动态:关闭扫描与开机闪屏 */
229
+ @media(prefers-reduced-motion:reduce) {
230
+ .rhine-theme[data-rhine-theme="arknights"] .ds-hero::after { animation: none; content: none; }
231
+ .rhine-theme[data-rhine-theme="arknights"].ds-shell::after { animation: none; content: none; }
232
+ }
233
+ .rhine-theme[data-rhine-theme="arknights"][data-rhine-motion=reduced] .ds-hero::after,
234
+ .rhine-theme[data-rhine-theme="arknights"][data-rhine-motion=reduced].ds-shell::after { animation: none; content: none; }
@@ -0,0 +1,11 @@
1
+ /* Gallery-only chrome and decoration. Not imported by the public theme entry. */
2
+ /* Gallery chrome uses the same theme; application layout stays with its host. */
3
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .ds-sidebar { border-right-width: 2px; border-color: var(--rhine-ink); }
4
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .ds-workspace-head { background: var(--rhine-surface); border-bottom: 2px solid var(--rhine-ink); flex-wrap: wrap; }
5
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .ds-overview-links>a { border: 2px solid var(--rhine-ink); border-radius: 22px; box-shadow: var(--rhine-hard-shadow); }
6
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .ds-hero h1 { font-weight: 800; }
7
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .ds-hero h1>span { color: var(--rhine-ink); text-decoration: underline; text-decoration-color: var(--rhine-signal); text-decoration-thickness: 6px; text-underline-offset: 8px; }
8
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .ds-logo-stage { border: 2px solid var(--rhine-ink); border-radius: 22px; }
9
+ @media(max-width:760px) {
10
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .ds-sidebar { border-right: 0; border-bottom: 2px solid var(--rhine-ink); }
11
+ }