@linhey/rhine-design 0.4.0 → 0.6.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 (69) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +24 -7
  3. package/components/AdvancedForms.tsx +184 -0
  4. package/components/AppShell.tsx +71 -0
  5. package/components/Background.tsx +29 -0
  6. package/components/COMMON_COMPONENTS.md +2 -2
  7. package/components/Charts.tsx +94 -0
  8. package/components/Data.tsx +98 -18
  9. package/components/Forms.tsx +14 -8
  10. package/components/MOTION.md +9 -1
  11. package/components/Media.tsx +82 -0
  12. package/components/OrbitBackdrop.tsx +33 -0
  13. package/components/Overlays.tsx +4 -3
  14. package/components/README.md +12 -1
  15. package/components/StatusTimeline.tsx +50 -12
  16. package/components/background.css +43 -0
  17. package/components/charts.css +27 -0
  18. package/components/common.css +5 -5
  19. package/components/data.css +6 -0
  20. package/components/drawer-placement.css +4 -0
  21. package/components/extensions.css +70 -0
  22. package/components/index.ts +14 -2
  23. package/components/media-parity.css +3 -0
  24. package/components/orbit.css +42 -0
  25. package/components/orbitRenderer.ts +230 -0
  26. package/components/patterns.css +1 -1
  27. package/components/styles.css +5 -1
  28. package/components/theme-arknights.css +10 -5
  29. package/components/theme-neo-brutalism.css +10 -4
  30. package/components/timeline.css +2 -2
  31. package/components/tokens.css +43 -0
  32. package/docs/ANTD_MUI_AUDIT.md +38 -0
  33. package/docs/APPLICATION_COMPONENTS.md +175 -0
  34. package/docs/DESIGN_SYSTEM_AUDIT.md +2 -0
  35. package/docs/ISSUE_4_VALIDATION.md +21 -0
  36. package/docs/ISSUE_VALIDATION.md +52 -0
  37. package/docs/ORBIT_BACKDROP.md +81 -0
  38. package/docs/REFERENCE_RESEARCH.md +2 -0
  39. package/docs/THEMING.md +41 -0
  40. package/docs/assets/application-shell.png +0 -0
  41. package/docs/assets/issue4-shell.png +0 -0
  42. package/docs/assets/media-viewer-mobile.png +0 -0
  43. package/docs/assets/monitoring-desktop.png +0 -0
  44. package/docs/assets/orbit-backdrop.png +0 -0
  45. package/docs/audits/DATA_ANTD_MUI.md +75 -0
  46. package/docs/audits/FORMS_ANTD_MUI.md +88 -0
  47. package/docs/audits/MONITORING_ANTD_MUI.md +44 -0
  48. package/docs/audits/SHELL_MEDIA_ANTD_MUI.md +91 -0
  49. package/package.json +1 -1
  50. package/preview/BackgroundGallery.tsx +46 -0
  51. package/preview/ExtensionsGallery.tsx +95 -0
  52. package/preview/GalleryShell.tsx +4 -3
  53. package/preview/MonitoringGallery.tsx +27 -0
  54. package/preview/OrbitGallery.tsx +47 -0
  55. package/preview/backgrounds.css +10 -0
  56. package/preview/extensions.css +6 -0
  57. package/preview/main.tsx +12 -1
  58. package/skills/rhine-design/SKILL.md +24 -3
  59. package/skills/rhine-design/references/API_MAP.md +6 -0
  60. package/skills/rhine-design/references/TASK_MAP.md +3 -1
  61. package/skills/rhine-design/references/integration.md +4 -2
  62. package/tests/Background.test.tsx +22 -0
  63. package/tests/DataParity.test.tsx +148 -0
  64. package/tests/Extensions.test.tsx +145 -0
  65. package/tests/FormParity.test.tsx +185 -0
  66. package/tests/Monitoring.test.tsx +38 -0
  67. package/tests/MonitoringParity.test.tsx +125 -0
  68. package/tests/OrbitBackdrop.test.tsx +183 -0
  69. package/tests/ShellMediaParity.test.tsx +180 -0
@@ -21,7 +21,7 @@
21
21
  .rhine-table caption { text-align:left; font-size:var(--rhine-text-heading); line-height:var(--rhine-leading-heading); font-weight:var(--rhine-weight-heading); margin-bottom:var(--rhine-space-4); }
22
22
  .rhine-table th,.rhine-table td { padding:12px; border-bottom:1px solid var(--rhine-line); max-width:240px; overflow-wrap:anywhere; }
23
23
  .rhine-table th { color:var(--rhine-muted); font-weight:500; }.rhine-table td { font-variant-numeric:tabular-nums; }
24
- .rhine-table [data-align=right] { text-align:right; }.rhine-table tbody tr:hover { background:var(--rhine-selected); }
24
+ .rhine-table [data-align=right] { text-align:right; }.rhine-table tbody tr:hover { background:var(--rhine-table-hover-bg); }
25
25
  .rhine-table th button { font:inherit; color:inherit; background:none; border:0; min-height:44px; cursor:pointer; padding:0; }
26
26
  .rhine-table th button:disabled { cursor:default; }
27
27
  .rhine-pagination { display:flex; flex-wrap:wrap; justify-content:flex-end; align-items:center; gap:12px; }
@@ -43,13 +43,13 @@
43
43
  .rhine-combobox { position:relative; }
44
44
  .rhine-combobox__input { position:relative; }.rhine-combobox__input input { padding-right:40px; }
45
45
  .rhine-combobox__arrow { position:absolute; right:12px; top:50%; transform:translateY(-50%); pointer-events:none; }
46
- .rhine-combobox__list { z-index:50; width:var(--radix-popover-trigger-width); max-width:calc(100vw - 24px); max-height:min(240px,var(--radix-popover-content-available-height)); overflow:auto; background:var(--rhine-surface); color:var(--rhine-ink); border:1px solid var(--rhine-control); border-radius:4px; box-shadow:0 8px 24px #242b2818; }
46
+ .rhine-combobox__list { z-index:50; width:var(--radix-popover-trigger-width); max-width:calc(100vw - 24px); max-height:min(240px,var(--radix-popover-content-available-height)); overflow:auto; background:var(--rhine-overlay-bg); color:var(--rhine-overlay-text); border:1px solid var(--rhine-control); border-radius:4px; box-shadow:0 8px 24px #242b2818; }
47
47
  .rhine-combobox__list [role=option],.rhine-combobox__empty { padding:12px; min-height:44px; cursor:pointer; overflow-wrap:anywhere; }
48
48
  .rhine-combobox__list [role=option][data-active=true],.rhine-combobox__list [role=option][aria-selected=true] { background:var(--rhine-selected); }
49
49
  .rhine-combobox__list [role=option][aria-disabled=true],.rhine-combobox__empty { color:var(--rhine-muted); cursor:default; }
50
50
  .rhine-overlay-anchor { display:inline-block; vertical-align:middle; max-width:100%; }
51
- .rhine-overlay { position:fixed; inset:0; background:#242b2870; z-index:40; }
52
- .rhine-dialog { position:fixed; z-index:41; top:50%; left:50%; transform:translate(-50%,-50%); width:min(480px,calc(100vw - 32px)); max-height:calc(100dvh - 32px); overflow:auto; padding:24px; border:1px solid var(--rhine-line); border-radius:4px; background:var(--rhine-surface); color:var(--rhine-ink); box-shadow:0 16px 48px #242b2826; }
51
+ .rhine-overlay { position:fixed; inset:0; background:var(--rhine-backdrop-bg); z-index:40; }
52
+ .rhine-dialog { position:fixed; z-index:41; top:50%; left:50%; transform:translate(-50%,-50%); width:min(480px,calc(100vw - 32px)); max-height:calc(100dvh - 32px); overflow:auto; padding:24px; border:1px solid var(--rhine-line); border-radius:4px; background:var(--rhine-overlay-bg); color:var(--rhine-overlay-text); box-shadow:0 16px 48px #242b2826; }
53
53
  .rhine-dialog>p,.rhine-dialog__main>p { margin-top:12px; }.rhine-dialog>.rhine-notice { margin-top:16px; }
54
54
  .rhine-dialog__main { min-width:0; }
55
55
  .rhine-dialog--split { width:min(960px,calc(100vw - 32px)); display:grid; grid-template-columns:minmax(240px,0.8fr) minmax(0,1.2fr); gap:24px; }
@@ -63,7 +63,7 @@
63
63
  .rhine-dialog__head { display:flex; align-items:center; justify-content:space-between; gap:12px; }
64
64
  .rhine-dialog__head>:first-child { min-width:0; overflow-wrap:anywhere; }
65
65
  .rhine-dialog__body { margin-top:24px; }.rhine-dialog__actions { display:flex; justify-content:flex-end; flex-wrap:wrap; gap:12px; margin-top:24px; }
66
- .rhine-popover,.rhine-menu { z-index:50; background:var(--rhine-surface); color:var(--rhine-ink); border:1px solid var(--rhine-control); border-radius:4px; box-shadow:0 8px 24px #242b2820; max-width:calc(100vw - 24px); overflow:auto; }
66
+ .rhine-popover,.rhine-menu { z-index:50; background:var(--rhine-overlay-bg); color:var(--rhine-overlay-text); border:1px solid var(--rhine-control); border-radius:4px; box-shadow:0 8px 24px #242b2820; max-width:calc(100vw - 24px); overflow:auto; }
67
67
  .rhine-popover { width:300px; padding:16px; max-height:var(--radix-popover-content-available-height); }
68
68
  .rhine-menu { min-width:180px; max-height:var(--radix-dropdown-menu-content-available-height); padding:4px; }
69
69
  .rhine-menu__item { min-height:44px; padding:10px 12px; cursor:pointer; outline:none; overflow-wrap:anywhere; }
@@ -0,0 +1,6 @@
1
+ .rhine-data .rhine-table tbody th[scope=row] { position:static; top:auto; z-index:auto; color:var(--rhine-ink); background:var(--rhine-surface); font-weight:var(--rhine-weight-label); }
2
+ .rhine-data .rhine-table tbody th[scope=row][data-sticky] { position:sticky; z-index:1; }
3
+ .rhine-data .rhine-table tr[data-selected=true] th[scope=row] { background:var(--rhine-table-selected-bg); }
4
+ .rhine-table__radio { display:flex; align-items:center; gap:8px; min-height:44px; cursor:pointer; }
5
+ .rhine-table__radio input { flex:none; width:18px; height:18px; accent-color:var(--rhine-green); }
6
+ .rhine-table__radio:has(input:disabled) { color:var(--rhine-muted); cursor:default; }
@@ -0,0 +1,4 @@
1
+ .rhine-dialog.rhine-drawer[data-side=left] { left:0; right:auto; animation-name:rhine-drawer-enter-left; }
2
+ .rhine-dialog.rhine-drawer[data-side=right] { left:auto; right:0; }
3
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-drawer[data-side=left] { border-radius:0 24px 24px 0; box-shadow:5px 0 0 var(--rhine-ink); }
4
+ @keyframes rhine-drawer-enter-left { from { opacity:0; transform:translateX(-3px); } to { opacity:1; transform:translateX(0); } }
@@ -0,0 +1,70 @@
1
+ .rhine-shell { height:100%; min-height:0; min-width:0; display:flex; flex-direction:column; border:1px solid var(--rhine-line); background:var(--rhine-canvas-bg); }
2
+ .rhine-shell__header { flex:none; display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding:12px; border-bottom:1px solid var(--rhine-line); background:var(--rhine-surface); }
3
+ .rhine-shell__body { display:flex; flex:1; min-height:0; min-width:0; }
4
+ .rhine-shell__nav, .rhine-shell__secondary { flex:0 0 224px; min-width:0; overflow:auto; padding:16px; background:var(--rhine-sidebar-bg); border-right:1px solid var(--rhine-line); overflow-wrap:anywhere; }
5
+ .rhine-shell__secondary { flex-basis:256px; }
6
+ .rhine-shell[data-layout=left-right] .rhine-shell__secondary { order:2; border-right:0; border-left:1px solid var(--rhine-line); }
7
+ .rhine-shell__content { flex:1; min-width:0; min-height:0; overflow:auto; padding:24px; overflow-wrap:anywhere; }
8
+ .rhine-date-range { margin:0; min-width:0; padding:16px; border:1px solid var(--rhine-line); background:var(--rhine-field-group-bg); }
9
+ .rhine-date-range legend { padding:0 4px; font-weight:600; }
10
+ .rhine-date-range__fields { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr)); gap:16px; margin-bottom:16px; }
11
+ .rhine-date-range input { max-width:100%; min-width:0; }
12
+ .rhine-number-field { display:grid; gap:8px; min-width:0; }
13
+ .rhine-multi-select { min-width:0; }
14
+ .rhine-multi-select__tags { display:flex; flex-wrap:wrap; gap:8px; list-style:none; padding:0; margin:8px 0; }
15
+ .rhine-multi-select__tags li { display:flex; align-items:center; max-width:100%; padding-left:12px; border:1px solid var(--rhine-line); background:var(--rhine-selected); border-radius:var(--rhine-radius-control); }
16
+ .rhine-multi-select__tags li>span { min-width:0; overflow-wrap:anywhere; }
17
+ .rhine-multi-select__tags .rhine-button { flex:none; }
18
+ .rhine-tree { min-width:0; padding:8px; border:1px solid var(--rhine-line); background:var(--rhine-field-group-bg); }
19
+ .rhine-tree [role=group] { padding-left:var(--rhine-tree-indent); }
20
+ .rhine-tree__row { display:flex; align-items:center; gap:8px; min-height:44px; padding:4px; overflow-wrap:anywhere; cursor:pointer; }
21
+ .rhine-tree__row>span:last-of-type { min-width:0; flex:1; }
22
+ .rhine-tree__row[data-selected=true] { background:var(--rhine-selected); }
23
+ .rhine-tree [aria-disabled=true]>.rhine-tree__row { color:var(--rhine-muted); cursor:default; }
24
+ .rhine-tree__row button { min-height:44px; min-width:44px; border:1px solid var(--rhine-line); background:var(--rhine-surface); color:var(--rhine-ink); font:inherit; cursor:pointer; }
25
+ .rhine-tree__spacer { flex:0 0 12px; }
26
+ .rhine-data[data-density=compact] { --rhine-table-cell-padding:6px 12px; }
27
+ .rhine-data .rhine-table-scroll { max-width:100%; overflow:auto; scroll-padding:48px; }
28
+ .rhine-data .rhine-table :is(th,td) { padding:var(--rhine-table-cell-padding); overflow-wrap:anywhere; font-size:14px; }
29
+ .rhine-table th { background:var(--rhine-table-header-bg); }
30
+ .rhine-table td { background:var(--rhine-surface); }
31
+ .rhine-table tbody tr:hover:not([data-selected=true]) > :is(td,th[scope=row]) { background:var(--rhine-table-hover-bg); }
32
+ .rhine-table tr[data-selected=true] td { background:var(--rhine-table-selected-bg); }
33
+ .rhine-table[data-sticky-header=true] th { position:sticky; top:0; z-index:2; }
34
+ .rhine-table :is(th,td)[data-sticky] { position:sticky; z-index:1; box-shadow:0 0 0 1px var(--rhine-line); }
35
+ .rhine-table[data-sticky-header=true] th[data-sticky] { z-index:3; }
36
+ .rhine-table__depth { display:block; white-space:nowrap; color:var(--rhine-muted); font-size:12px; }
37
+ .rhine-table .rhine-check label { min-height:44px; }
38
+ .rhine-pagination .rhine-select { width:auto; }
39
+ .rhine-media-card { min-width:0; border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card); background:var(--rhine-surface); }
40
+ .rhine-media-card[data-selected=true] { border:2px solid var(--rhine-green); }
41
+ .rhine-media-card__open { display:block; width:100%; min-width:0; padding:0; border:0; background:none; color:inherit; font:inherit; text-align:left; cursor:pointer; }
42
+ .rhine-media-card__open:disabled { cursor:default; opacity:.6; }
43
+ .rhine-media-card__preview { position:relative; display:flex; align-items:center; justify-content:center; width:100%; overflow:hidden; background:var(--rhine-media-canvas); color:var(--rhine-media-text); border-radius:var(--rhine-radius-card) var(--rhine-radius-card) 0 0; }
44
+ .rhine-media-card__preview :is(img,video) { width:100%; height:100%; object-fit:contain; }
45
+ .rhine-media-card__kind { position:absolute; top:8px; left:8px; padding:4px 8px; background:var(--rhine-media-overlay); color:var(--rhine-media-text); border:1px solid var(--rhine-media-border); font-size:12px; }
46
+ .rhine-media-card__caption { display:grid; gap:4px; padding:16px; overflow-wrap:anywhere; }
47
+ .rhine-media-card__caption>span { font-size:12px; color:var(--rhine-muted); }
48
+ .rhine-media-card>.rhine-check { padding:0 16px 12px; overflow-wrap:anywhere; }
49
+ .rhine-media-card>.rhine-check label { min-height:44px; }
50
+ .rhine-media-viewer { position:fixed; inset:0; z-index:41; width:100%; height:100dvh; display:flex; flex-direction:column; background:var(--rhine-media-canvas); color:var(--rhine-media-text); color-scheme:dark; overflow:auto; }
51
+ .rhine-media-viewer__header { flex:none; display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:16px; padding:16px; background:var(--rhine-media-overlay); border-bottom:1px solid var(--rhine-media-border); }
52
+ .rhine-media-viewer__header>div { min-width:0; }
53
+ .rhine-media-viewer__header h2 { font-size:18px; line-height:26px; overflow-wrap:anywhere; margin:0; }
54
+ .rhine-media-viewer__header p { color:var(--rhine-media-muted); font-size:12px; margin:4px 0 0; }
55
+ .rhine-media-viewer__actions { display:flex; flex-wrap:wrap; gap:8px; }
56
+ .rhine-media-viewer :is(button,select,a) { min-height:44px; min-width:44px; max-width:100%; padding:8px 12px; border:1px solid var(--rhine-media-border); border-radius:4px; background:var(--rhine-media-overlay); color:var(--rhine-media-text); font:inherit; overflow-wrap:anywhere; }
57
+ .rhine-media-viewer button { cursor:pointer; }
58
+ .rhine-media-viewer button:disabled { color:var(--rhine-media-muted); opacity:.5; cursor:default; }
59
+ .rhine-theme .rhine-media-viewer.rhine-media-viewer :is(button,input,select,textarea,summary,a,[tabindex]):focus-visible { outline:3px solid var(--rhine-media-focus); outline-offset:-3px; }
60
+ .rhine-media-viewer__body { display:grid; grid-template-columns:var(--rhine-media-columns); flex:1; min-height:0; min-width:0; }
61
+ .rhine-media-viewer__canvas { display:flex; align-items:center; justify-content:center; min-height:0; min-width:0; overflow:auto; padding:16px; }
62
+ .rhine-media-viewer__canvas :is(img,video) { display:block; max-width:100%; max-height:100%; object-fit:contain; }
63
+ .rhine-media-viewer__metadata { min-width:0; overflow:auto; padding:24px; border-left:1px solid var(--rhine-media-border); background:var(--rhine-media-overlay); overflow-wrap:anywhere; }
64
+ @media(max-width:760px) {
65
+ .rhine-media-viewer__body { display:flex; flex-direction:column; overflow:auto; }
66
+ .rhine-media-viewer__canvas { flex:1 0 220px; }
67
+ .rhine-media-viewer__canvas :is(img,video) { max-height:50dvh; }
68
+ .rhine-media-viewer__metadata { flex:none; overflow:visible; border-left:0; border-top:1px solid var(--rhine-media-border); }
69
+ .rhine-shell__content { padding:16px; }
70
+ }
@@ -1,6 +1,18 @@
1
1
  import './styles.css';
2
+ export { OrbitBackdrop } from './OrbitBackdrop';
3
+ export type { OrbitBackdropProps } from './OrbitBackdrop';
4
+ export { Background } from './Background';
5
+ export type { BackgroundProps, BackgroundRole, BackgroundDecoration, BackgroundAppearance } from './Background';
6
+ export { AppShell } from './AppShell';
7
+ export type { AppShellProps, ShellPanel } from './AppShell';
8
+ export { MediaCard, MediaViewer } from './Media';
9
+ export type { MediaCardProps, MediaViewerProps } from './Media';
10
+ export { DateField, DateRangePicker, NumberField, RemoteMultiSelect, TreeSelect } from './AdvancedForms';
11
+ export type { DateFieldProps, DateRange, DateRangePickerProps, NumberFieldProps, RemoteMultiSelectProps, TreeNode, TreeSelectProps } from './AdvancedForms';
2
12
  export { StatusTimeline, TimelineLegend } from './StatusTimeline';
3
- export type { TimelineSegment, TimelineStatus } from './StatusTimeline';
13
+ export type { TimelineSegment, TimelineStatus, TimelineDetail, StatusTimelineProps } from './StatusTimeline';
14
+ export { TrendChart, StackedBar } from './Charts';
15
+ export type { ChartPoint, ChartPart, TrendChartProps, StackedBarProps } from './Charts';
4
16
 
5
17
  export { Logo } from './Logo';
6
18
  export type { LogoProps } from './Logo';
@@ -15,7 +27,7 @@ export type { IconName } from './Actions';
15
27
  export { Divider, Stack, Inline, Grid } from './Layout';
16
28
  export type { Spacing } from './Layout';
17
29
  export { Metric, KeyValue, DataTable, Pagination } from './Data';
18
- export type { DataColumn, DataTableProps, TableSort } from './Data';
30
+ export type { DataColumn, DataTableProps, TableSort, PaginationProps } from './Data';
19
31
  export { Checkbox, RadioGroup, Switch, Textarea, SearchField, Combobox } from './Forms';
20
32
  export type { ComboboxProps } from './Forms';
21
33
  export { Dialog, Drawer, ConfirmDialog, Popover, Menu, Tooltip } from './Overlays';
@@ -0,0 +1,3 @@
1
+ .rhine-media-card__actions { display:flex; flex-wrap:wrap; gap:8px; padding:0 16px 12px; }
2
+ .rhine-media-card__asset { position:absolute; inset:0; }
3
+ .rhine-media-card__asset:not([hidden]) { display:flex; align-items:center; justify-content:center; }
@@ -0,0 +1,42 @@
1
+ .rhine-orbit {
2
+ position:relative; isolation:isolate; display:flex; flex-direction:column;
3
+ min-width:0; min-height:280px; overflow:hidden;
4
+ border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card);
5
+ background:
6
+ radial-gradient(ellipse at 90% 78%, #15508066 0, transparent 43%),
7
+ radial-gradient(ellipse at 8% 54%, #84603626 0, transparent 35%),
8
+ radial-gradient(ellipse at 62% 18%, #213b653b 0, transparent 48%),
9
+ linear-gradient(155deg, #03070f 12%, #081426 58%, #030a13);
10
+ }
11
+ .rhine-orbit::before, .rhine-orbit::after { content:''; position:absolute; inset:0; pointer-events:none; }
12
+ .rhine-orbit::before {
13
+ background-image:
14
+ radial-gradient(circle at 17px 37px, #d7eaffb3 .7px, transparent 1.4px),
15
+ radial-gradient(circle at 73px 19px, #8bb8e780 .6px, transparent 1.2px),
16
+ radial-gradient(circle at 41px 89px, #d7ba8f80 .7px, transparent 1.3px),
17
+ radial-gradient(circle at 113px 157px, #c4dcff66 .5px, transparent 1.2px);
18
+ background-size:173px 211px, 127px 181px, 251px 239px, 97px 139px;
19
+ opacity:.55;
20
+ }
21
+ .rhine-orbit::after {
22
+ background:
23
+ radial-gradient(ellipse at 77% 66%, #52a4f21a 0, transparent 28%),
24
+ radial-gradient(ellipse at 24% 84%, #b8874220 0, transparent 30%),
25
+ linear-gradient(125deg, transparent 40%, #6295d109 49%, transparent 58%);
26
+ }
27
+ .rhine-orbit-canvas { position:absolute; inset:0; display:block; width:100%; height:100%; pointer-events:none; }
28
+ /* Keep host control tokens intact; only unpainted content inherits the artwork foreground. */
29
+ .rhine-orbit-content {
30
+ position:relative; z-index:1; flex:1; min-width:0;
31
+ display:flex; flex-direction:column; justify-content:space-between; gap:var(--rhine-space-6);
32
+ padding:var(--rhine-space-6); padding-bottom:calc(44px + var(--rhine-space-6) * 2);
33
+ color:#f2f7ff;
34
+ }
35
+ .rhine-orbit-content :is(.rhine-text--muted, .rhine-field__hint) { color:inherit; opacity:.8; }
36
+ .rhine-orbit-content .rhine-logo:not(.rhine-logo--icon) { color:inherit; }
37
+ /* Transparent actions follow their backdrop; painted hover/disabled states retain the host palette. */
38
+ .rhine-orbit-content .rhine-button--quiet:not(:disabled):not(:hover),
39
+ .rhine-orbit-content .rhine-button--quiet[aria-disabled=true]:not(:disabled) { color:inherit; }
40
+ .rhine-orbit .rhine-orbit-content .rhine-button--quiet:focus-visible { outline:none; box-shadow:inset 0 0 0 2px currentColor; }
41
+ .rhine-orbit-controls { position:absolute; right:var(--rhine-space-4); bottom:var(--rhine-space-4); z-index:2; max-width:calc(100% - var(--rhine-space-4) * 2); }
42
+ .rhine-orbit-controls .rhine-button { max-width:100%; }
@@ -0,0 +1,230 @@
1
+ type Point = { x: number; y: number; depth: number; perspective: number };
2
+ type Edge = { a: number; sweep: number; phase: number; waist: number; lift: number; color: number; speed: number };
3
+ type Layer =
4
+ | { kind: 'void'; points: Point[] }
5
+ | { kind: 'line'; p: Point; q: Point; color: number }
6
+ | { kind: 'particle'; p: Point; color: number }
7
+ | { kind: 'spike'; p: Point; q: Point; i: number; pulse: number };
8
+ type CanvasScene = { el: HTMLCanvasElement; ctx: CanvasRenderingContext2D; w: number; h: number };
9
+ export type OrbitState = 'playing' | 'paused' | 'reduced' | 'compact' | 'hidden' | 'offscreen' | 'empty' | 'unavailable';
10
+ export interface OrbitRenderer { setPaused(value: boolean): void; setReducedMotion(value: boolean): void; dispose(): void }
11
+
12
+ /** The seeded artwork is stable across mounts; only elapsed visible time advances it. */
13
+ export function mountOrbit(canvas: HTMLCanvasElement, onState: (state: OrbitState) => void): OrbitRenderer {
14
+ let ctx: CanvasRenderingContext2D | null = null;
15
+ try { ctx = canvas.getContext('2d'); } catch { /* Canvas may be disabled by the host. */ }
16
+ if (!ctx) {
17
+ onState('unavailable');
18
+ return { setPaused() {}, setReducedMotion() {}, dispose() {} };
19
+ }
20
+ const TAU = Math.PI * 2, colors = ["#51b7f6", "#e5b94d", "#2ec7a1", "#bdcbe1"];
21
+ let seed = 1823;
22
+ function rand() {
23
+ seed = seed * 1664525 + 1013904223 >>> 0;
24
+ return seed / 4294967296;
25
+ }
26
+ const nodes = Array.from({ length: 420 }, (_, i) => ({ a: i / 420 * TAU, r: 1.025 + rand() * 0.07, z: (rand() - 0.5) * 0.34, length: 0.035 + rand() * 0.19, color: i % 7 === 0 ? 1 : i % 11 === 0 ? 2 : 3 }));
27
+ const emissions = nodes.map(() => ({ phase: rand(), rate: 0.28 + rand() * 0.3 }));
28
+ const edges = Array.from({ length: 185 }, () => ({ a: rand() * TAU, sweep: 2.1 + rand() * 3.8, phase: rand() * TAU, waist: 0.5 + rand() * 0.32, lift: (rand() - 0.3) * 0.6, color: rand() < 0.32 ? 1 : rand() < 0.1 ? 2 : 0, speed: 0.026 + rand() * 0.024 }));
29
+ function edgePoint(e: Edge, t: number) {
30
+ const a = e.a + t * e.sweep, envelope = Math.sin(Math.PI * t);
31
+ return { a, r: 1.035 - (1.035 - e.waist) * Math.pow(envelope, 0.72), z: e.lift * envelope + 0.06 * Math.sin(e.phase + t * TAU) * envelope };
32
+ }
33
+ const paths = edges.map((e) => Array.from({ length: 81 }, (_, i) => edgePoint(e, i / 80)));
34
+ const canvases = { network: { el: canvas, ctx, w: 0, h: 0 } };
35
+ let time = 0, orbit = 0, projected: Point[] = [], last: number | null = null, frame = 0;
36
+ const yaw = -0.72, tilt = 0.22;
37
+ let paused = false, manualReduced = false, disposed = false, visible = true;
38
+ let previousState: OrbitState | undefined;
39
+ function resize() {
40
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
41
+ // Layout CSS pixels exclude CSS zoom; visual scaling must not change stroke proportions.
42
+ const c = canvases.network;
43
+ c.w = c.el.clientWidth;
44
+ c.h = c.el.clientHeight;
45
+ c.el.width = Math.round(c.w * dpr);
46
+ c.el.height = Math.round(c.h * dpr);
47
+ c.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
48
+ draw();
49
+ }
50
+ function line(ctx2: CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number, color: string | CanvasGradient, width = 1) {
51
+ ctx2.strokeStyle = color;
52
+ ctx2.lineWidth = width;
53
+ ctx2.beginPath();
54
+ ctx2.moveTo(x1, y1);
55
+ ctx2.lineTo(x2, y2);
56
+ ctx2.stroke();
57
+ }
58
+ function dot(ctx2: CanvasRenderingContext2D, x: number, y: number, r: number, color: string | CanvasGradient) {
59
+ ctx2.fillStyle = color;
60
+ ctx2.beginPath();
61
+ ctx2.arc(x, y, r, 0, TAU);
62
+ ctx2.fill();
63
+ }
64
+ function clear(c: CanvasScene) {
65
+ c.ctx.clearRect(0, 0, c.w, c.h);
66
+ }
67
+ function project(a: number, r = 1, z = 0) {
68
+ const c = canvases.network, scale = Math.min(c.w * 0.66, c.h * 0.39), x = Math.cos(a) * r, y = Math.sin(a) * r;
69
+ const yy = y * Math.cos(tilt) - z * Math.sin(tilt), zz = y * Math.sin(tilt) + z * Math.cos(tilt);
70
+ const xx = x * Math.cos(yaw) + zz * Math.sin(yaw), depth = -x * Math.sin(yaw) + zz * Math.cos(yaw);
71
+ const spin = 0.34, px = xx * Math.cos(spin) - yy * Math.sin(spin), py = xx * Math.sin(spin) + yy * Math.cos(spin), perspective = 3.6 / (3.6 - depth);
72
+ return { x: c.w * 0.62 + px * scale * perspective, y: c.h * 0.57 + py * scale * perspective * 1.08, depth, perspective };
73
+ }
74
+ function network() {
75
+ const c = canvases.network, { ctx: ctx2 } = c;
76
+ clear(c);
77
+ const buckets: Layer[][] = Array.from({ length: 80 }, () => []), offset = orbit;
78
+ function add(item: Layer, depth: number) {
79
+ buckets[Math.max(0, Math.min(79, Math.floor((depth + 1.5) / 3 * 80)))].push(item);
80
+ }
81
+ const horizon = Array.from({ length: 101 }, (_, i) => project(i / 100 * TAU, 0.46, -0.06));
82
+ add({ kind: "void", points: horizon }, -0.04);
83
+ edges.forEach((e, index) => {
84
+ const pts = paths[index].map((p) => project(p.a + offset, p.r, p.z));
85
+ for (let i = 1; i < pts.length; i++) add({ kind: "line", p: pts[i - 1], q: pts[i], color: e.color }, (pts[i - 1].depth + pts[i].depth) / 2 + 7e-3);
86
+ if (index % 3 === 0) {
87
+ const t = (time * e.speed + e.phase / TAU) % 1, p3 = edgePoint(e, t), p = project(p3.a + offset, p3.r, p3.z);
88
+ add({ kind: "particle", p, color: e.color }, p.depth + 0.012);
89
+ }
90
+ });
91
+ projected = nodes.map((n) => project(n.a + offset, n.r, n.z));
92
+ nodes.forEach((n, i) => {
93
+ const emission = emissions[i], pulse = (time * emission.rate + emission.phase) % 1, breath = 1 + 0.18 * Math.sin(time * 1.7 + n.a * 5), p = projected[i], q = project(n.a + offset, n.r + n.length * breath, n.z * 1.1);
94
+ add({ kind: "spike", p, q, i, pulse }, (p.depth + q.depth) / 2);
95
+ });
96
+ const palette = ["104,178,244", "223,187,92", "72,182,179"];
97
+ for (const bucket of buckets) for (const item of bucket) {
98
+ if (item.kind === "void") {
99
+ ctx2.beginPath();
100
+ item.points.forEach((p2, i) => i ? ctx2.lineTo(p2.x, p2.y) : ctx2.moveTo(p2.x, p2.y));
101
+ ctx2.closePath();
102
+ ctx2.fillStyle = "#010205";
103
+ ctx2.fill();
104
+ continue;
105
+ }
106
+ const p = item.p, near = Math.max(0, Math.min(1, (p.depth + 1) / 2));
107
+ if (item.kind === "line") {
108
+ ctx2.strokeStyle = "rgba(" + palette[item.color] + "," + (0.07 + near * near * 0.57) + ")";
109
+ ctx2.lineWidth = 0.35 + near * 0.65;
110
+ ctx2.beginPath();
111
+ ctx2.moveTo(p.x, p.y);
112
+ ctx2.lineTo(item.q.x, item.q.y);
113
+ ctx2.stroke();
114
+ } else if (item.kind === "spike") {
115
+ const q = item.q, bright = near > 0.48, rgb = bright ? "173,218,255" : "225,161,71", dx = q.x - p.x, dy = q.y - p.y, len = Math.hypot(dx, dy) || 1, ux = dx / len, uy = dy / len;
116
+ line(ctx2, p.x, p.y, q.x, q.y, bright ? "rgba(213,224,241," + (0.16 + near * 0.35) + ")" : "rgba(181,118,53," + (0.12 + near * 0.3) + ")", 0.4 + near * 0.4);
117
+ const t = item.pulse, fade = Math.sin(Math.PI * t), alpha = fade * (0.25 + near * 0.75), x = p.x + dx * t, y = p.y + dy * t, tail = Math.max(0, t - 0.38), tx = p.x + dx * tail, ty = p.y + dy * tail;
118
+ const trail = ctx2.createLinearGradient(tx, ty, x + 1e-3, y + 1e-3);
119
+ trail.addColorStop(0, "rgba(" + rgb + ",0)");
120
+ trail.addColorStop(1, "rgba(" + rgb + "," + alpha + ")");
121
+ line(ctx2, tx, ty, x, y, trail, 0.7 + near * 0.9);
122
+ const size = (1.6 + near * 1.5) * q.perspective;
123
+ ctx2.strokeStyle = "rgba(" + rgb + "," + alpha + ")";
124
+ ctx2.lineWidth = 0.65 + near * 0.5;
125
+ ctx2.beginPath();
126
+ ctx2.moveTo(x - ux * size - uy * size * 0.55, y - uy * size + ux * size * 0.55);
127
+ ctx2.lineTo(x, y);
128
+ ctx2.lineTo(x - ux * size + uy * size * 0.55, y - uy * size - ux * size * 0.55);
129
+ ctx2.stroke();
130
+ if (item.i % 3 === 0) {
131
+ const radius = (4 + near * 4) * q.perspective, glow = ctx2.createRadialGradient(x, y, 0, x, y, radius);
132
+ glow.addColorStop(0, "rgba(" + rgb + "," + alpha * 0.45 + ")");
133
+ glow.addColorStop(1, "rgba(" + rgb + ",0)");
134
+ dot(ctx2, x, y, radius, glow);
135
+ dot(ctx2, x, y, 0.65 + near * 0.45, "rgba(242,247,255," + alpha + ")");
136
+ }
137
+ const flash = Math.pow(Math.max(0, 1 - Math.abs(t - 0.88) / 0.12), 2);
138
+ dot(ctx2, q.x, q.y, (0.55 + flash * 1.1) * q.perspective, "rgba(" + rgb + "," + (0.18 + near * 0.25 + flash * 0.35) + ")");
139
+ } else {
140
+ ctx2.shadowBlur = 5;
141
+ ctx2.shadowColor = colors[item.color];
142
+ dot(ctx2, p.x, p.y, 1.1 * p.perspective, colors[item.color]);
143
+ ctx2.shadowBlur = 0;
144
+ }
145
+ }
146
+ for (let i = 0; i < 8; i++) {
147
+ const e = edges[i * 19], v = edgePoint(e, 0.4), p = project(v.a + offset, v.r, v.z);
148
+ if (p.depth < -0.1) continue;
149
+ ctx2.shadowColor = "#f25380";
150
+ ctx2.shadowBlur = 10;
151
+ dot(ctx2, p.x, p.y, 2.2 * p.perspective, "#ff658c");
152
+ ctx2.shadowBlur = 0;
153
+ }
154
+ }
155
+ const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)');
156
+ const compact = window.matchMedia?.('(max-width: 760px)');
157
+ function draw() {
158
+ if (canvases.network.w && canvases.network.h) network();
159
+ }
160
+ function state(): OrbitState {
161
+ if (manualReduced || reduced?.matches || canvas.closest('[data-rhine-motion="reduced"]')) return 'reduced';
162
+ if (compact?.matches) return 'compact';
163
+ if (document.hidden) return 'hidden';
164
+ if (!visible) return 'offscreen';
165
+ if (canvases.network.w <= 0 || canvases.network.h <= 0) return 'empty';
166
+ return paused ? 'paused' : 'playing';
167
+ }
168
+ function animate(now: number) {
169
+ frame = 0;
170
+ if (disposed) return;
171
+ // A frame can observe a media change before its change event is delivered.
172
+ if (state() !== 'playing') { sync(); return; }
173
+ const dt = last === null ? 0 : Math.min((now - last) / 1e3, 0.1);
174
+ last = now;
175
+ time += dt;
176
+ orbit += dt * 0.028;
177
+ draw();
178
+ frame = requestAnimationFrame(animate);
179
+ }
180
+ function sync() {
181
+ if (disposed) return;
182
+ cancelAnimationFrame(frame);
183
+ frame = 0;
184
+ last = null;
185
+ resize();
186
+ const next = state();
187
+ if (next !== previousState) { previousState = next; onState(next); }
188
+ if (next === 'playing') frame = requestAnimationFrame(animate);
189
+ }
190
+ const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(sync);
191
+ observer?.observe(canvas);
192
+ const intersection = typeof IntersectionObserver === 'undefined' ? null : new IntersectionObserver(entries => {
193
+ visible = entries.some(entry => entry.target === canvas && entry.isIntersecting);
194
+ sync();
195
+ });
196
+ intersection?.observe(canvas);
197
+ const motion = new MutationObserver(sync);
198
+ // Observe ancestors only: canvas sizing and React state writes must not retrigger drawing.
199
+ for (let ancestor = canvas.parentElement; ancestor; ancestor = ancestor.parentElement) {
200
+ motion.observe(ancestor, { attributes: true, attributeFilter: ['data-rhine-motion'] });
201
+ }
202
+ reduced?.addEventListener?.('change', sync);
203
+ compact?.addEventListener?.('change', sync);
204
+ window.addEventListener('resize', sync);
205
+ document.addEventListener("visibilitychange", sync);
206
+ sync();
207
+ return {
208
+ setPaused(value) {
209
+ if (paused === value) return;
210
+ paused = value;
211
+ sync();
212
+ },
213
+ setReducedMotion(value) {
214
+ if (manualReduced === value) return;
215
+ manualReduced = value;
216
+ sync();
217
+ },
218
+ dispose() {
219
+ disposed = true;
220
+ cancelAnimationFrame(frame);
221
+ observer?.disconnect();
222
+ intersection?.disconnect();
223
+ motion.disconnect();
224
+ reduced?.removeEventListener?.('change', sync);
225
+ compact?.removeEventListener?.('change', sync);
226
+ window.removeEventListener('resize', sync);
227
+ document.removeEventListener("visibilitychange", sync);
228
+ }
229
+ };
230
+ }
@@ -26,7 +26,7 @@
26
26
  .rhine-steps li[data-state=current]>div { background:var(--rhine-selected); color:var(--rhine-green); animation:rhine-pattern-enter var(--rhine-duration-panel) var(--rhine-ease-standard); }
27
27
  .rhine-steps li[data-state=error] { border-color:var(--rhine-error); }
28
28
  .rhine-steps li[data-state=error]>div { color:var(--rhine-error); background:var(--rhine-error-bg); }
29
- .rhine-fieldset { min-width:0; margin:0; padding:20px; border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card); }
29
+ .rhine-fieldset { background:var(--rhine-surface-bg); color:var(--rhine-surface-text); min-width:0; margin:0; padding:20px; border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card); }
30
30
  .rhine-fieldset legend { padding:0 8px; font-weight:600; }
31
31
  .rhine-fieldset>p { margin:0 0 20px; color:var(--rhine-muted); font-size:12px; }
32
32
  .rhine-fieldset__body { display:grid; gap:16px; }
@@ -3,6 +3,10 @@
3
3
  @import './common.css';
4
4
  @import './patterns.css';
5
5
  @import './timeline.css';
6
+ @import './extensions.css';
7
+ @import './background.css';
8
+ @import './orbit.css';
9
+ @import './charts.css';
6
10
 
7
11
  .rhine-theme { color:var(--rhine-ink); background:var(--rhine-bg); font:var(--rhine-text-body)/var(--rhine-leading-body) var(--rhine-font); font-synthesis:none; }
8
12
  .rhine-theme[data-rhine-theme="rhine"] { color-scheme:light; }
@@ -28,7 +32,7 @@
28
32
  .rhine-tone-warning { --rhine-tone:var(--rhine-warning); --rhine-tone-bg:var(--rhine-warning-bg); }
29
33
  .rhine-tone-error { --rhine-tone:var(--rhine-error); --rhine-tone-bg:var(--rhine-error-bg); }
30
34
  .rhine-badge { display:inline-block; padding:2px 8px; border-radius:2px; background:var(--rhine-tone-bg); color:var(--rhine-tone); font:500 12px/20px var(--rhine-font); }
31
- .rhine-surface { padding:var(--rhine-space-6); border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card); background:var(--rhine-surface); min-width:0; }
35
+ .rhine-surface { padding:var(--rhine-space-6); border:1px solid var(--rhine-line); border-radius:var(--rhine-radius-card); background:var(--rhine-surface-bg); color:var(--rhine-surface-text); min-width:0; }
32
36
  .rhine-selection { display:block; border:1px solid var(--rhine-control); background:var(--rhine-surface); border-radius:var(--rhine-radius-card); padding:var(--rhine-space-6); cursor:pointer; min-width:0; transition:background-color var(--rhine-duration-hover) var(--rhine-ease-standard),border-color var(--rhine-duration-hover) var(--rhine-ease-standard),box-shadow var(--rhine-duration-hover) var(--rhine-ease-standard); }
33
37
  .rhine-selection:hover:not([data-disabled=true]) { border-color:var(--rhine-green); background:#f4f7ef; }
34
38
  .rhine-selection[data-selected=true] { border-color:var(--rhine-green); background:var(--rhine-selected); box-shadow:inset 0 0 0 1px var(--rhine-green); }
@@ -2,12 +2,18 @@
2
2
  .rhine-theme[data-rhine-theme="arknights"] {
3
3
  /* ---- 色彩:暗舱底 + 信号黄 + 警戒橙 ---- */
4
4
  --rhine-bg: #0b0d10;
5
+ --rhine-overlay-bg: #1a1d23;
6
+ --rhine-backdrop-bg: rgba(4, 5, 7, .72);
7
+ --rhine-background-mark: #20242a;
8
+ --rhine-background-texture: linear-gradient(var(--rhine-background-mark) 1px, transparent 1px), linear-gradient(90deg, var(--rhine-background-mark) 1px, transparent 1px);
9
+ --rhine-background-scale: 32px 32px;
5
10
  --rhine-surface: #14161b;
6
11
  --rhine-ink: #eef0ec;
7
12
  --rhine-muted: #878f96;
8
13
  --rhine-line: #272b31;
9
14
  --rhine-control: #454c54;
10
15
  --rhine-selected: #1c2026;
16
+ --rhine-table-hover-bg: color-mix(in srgb, #ffd400 6%, var(--rhine-surface));
11
17
  --rhine-green: #ffd400;
12
18
  --rhine-signal: #ff6a1a;
13
19
  --rhine-warning: #ffb020;
@@ -77,7 +83,7 @@
77
83
  .rhine-theme[data-rhine-theme="arknights"] .rhine-surface,
78
84
  .rhine-theme[data-rhine-theme="arknights"] .rhine-empty,
79
85
  .rhine-theme[data-rhine-theme="arknights"] .rhine-fieldset {
80
- background: var(--ak-panel);
86
+ background: var(--rhine-surface-bg);
81
87
  clip-path: polygon(0 0, calc(100% - var(--ak-cut)) 0, 100% var(--ak-cut), 100% 100%, 0 100%);
82
88
  }
83
89
  /* 左下角小角标点缀 */
@@ -173,7 +179,7 @@
173
179
  .rhine-theme[data-rhine-theme="arknights"] .rhine-combobox__list,
174
180
  .rhine-theme[data-rhine-theme="arknights"] .rhine-popover,
175
181
  .rhine-theme[data-rhine-theme="arknights"] .rhine-menu {
176
- background: var(--ak-panel-hi);
182
+ background: var(--rhine-overlay-bg);
177
183
  border-color: var(--rhine-control);
178
184
  border-radius: 0;
179
185
  box-shadow: 0 12px 32px rgba(0, 0, 0, .55);
@@ -223,7 +229,6 @@
223
229
  letter-spacing: .08em;
224
230
  border-bottom: 2px solid var(--ak-accent);
225
231
  }
226
- .rhine-theme[data-rhine-theme="arknights"] .rhine-table tbody tr:hover { background: rgba(255, 212, 0, .06); }
227
232
  .rhine-theme[data-rhine-theme="arknights"] .rhine-metric { font-variant-numeric: tabular-nums; }
228
233
 
229
234
  /* ================= 反馈 ================= */
@@ -240,9 +245,9 @@
240
245
  .rhine-theme[data-rhine-theme="arknights"] .rhine-disclosure summary>span { color: var(--ak-accent); }
241
246
 
242
247
  /* ================= 浮层 / 对话框 ================= */
243
- .rhine-theme[data-rhine-theme="arknights"] .rhine-overlay { background: rgba(4, 5, 7, .72); }
248
+ .rhine-theme[data-rhine-theme="arknights"] .rhine-overlay { background: var(--rhine-backdrop-bg); }
244
249
  .rhine-theme[data-rhine-theme="arknights"] .rhine-dialog {
245
- background: var(--ak-panel);
250
+ background: var(--rhine-overlay-bg);
246
251
  border-color: var(--rhine-control);
247
252
  border-radius: 0;
248
253
  box-shadow: 0 24px 64px rgba(0, 0, 0, .6);
@@ -2,12 +2,18 @@
2
2
  .rhine-theme[data-rhine-theme="neo-brutalism"] {
3
3
  color-scheme: light;
4
4
  --rhine-bg: #eaf0f4;
5
+ --rhine-backdrop-bg: #20232360;
6
+ --rhine-background-mark: #b7c2cc;
7
+ --rhine-background-texture: radial-gradient(circle, var(--rhine-background-mark) .8px, transparent .9px);
8
+ --rhine-background-scale: 14px 14px;
5
9
  --rhine-surface: #fffdf5;
6
10
  --rhine-ink: #202323;
7
11
  --rhine-muted: #59616a;
8
12
  --rhine-line: #b9c1c7;
9
13
  --rhine-control: #202323;
10
14
  --rhine-selected: #fce4cc;
15
+ --rhine-table-header-bg: #edf0e9;
16
+ --rhine-table-cell-padding: 16px 12px;
11
17
  --rhine-green: #286043;
12
18
  --rhine-signal: #ff8b3d;
13
19
  --rhine-warning: #854216;
@@ -24,7 +30,7 @@
24
30
  --rhine-tag-green: #dcebd7;
25
31
  --rhine-tag-orange: #fce0c4;
26
32
  --rhine-tag-violet: #e3e0f7;
27
- background: radial-gradient(circle, #b7c2cc .8px, transparent .9px) 0 0 / 14px 14px, var(--rhine-bg);
33
+ background: var(--rhine-background-texture) 0 0 / var(--rhine-background-scale), var(--rhine-bg);
28
34
  }
29
35
 
30
36
  .rhine-theme[data-rhine-theme="neo-brutalism"] :is(.rhine-surface, .rhine-selection, .rhine-empty, .rhine-fieldset) {
@@ -67,13 +73,13 @@
67
73
  .rhine-theme[data-rhine-theme="neo-brutalism"] :is(.rhine-dialog, .rhine-popover, .rhine-menu, .rhine-combobox__list) { border: 2px solid var(--rhine-ink); border-radius: 18px; box-shadow: var(--rhine-hard-shadow); }
68
74
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-dialog { border-radius: 24px; box-shadow: 8px 8px 0 var(--rhine-ink); }
69
75
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-drawer { border-radius: 24px 0 0 24px; box-shadow: -5px 0 0 var(--rhine-ink); }
70
- .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-overlay { background: #20232360; backdrop-filter: none; }
76
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-overlay { background: var(--rhine-backdrop-bg); backdrop-filter: none; }
71
77
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-menu__item { border-radius: 10px; }
72
78
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-menu__item[data-highlighted] { box-shadow: inset 0 0 0 2px var(--rhine-ink); }
73
79
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-tooltip { border-radius: 8px; }
74
80
  .rhine-theme[data-rhine-theme="neo-brutalism"] :is(.rhine-notice, .rhine-toast, .rhine-error-summary) { border: 2px solid var(--rhine-tone, var(--rhine-error)); border-radius: 14px; }
75
- .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-table th { color: var(--rhine-ink); background: #edf0e9; font-weight: 700; border-bottom: 2px solid var(--rhine-ink); }
76
- .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-table :is(th, td) { padding: 16px 12px; }
81
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-table th { color: var(--rhine-ink); background: var(--rhine-table-header-bg); font-weight: 700; border-bottom: 2px solid var(--rhine-ink); }
82
+ .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-table :is(th, td) { padding: var(--rhine-table-cell-padding); }
77
83
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-table td { font-size: 14px; }
78
84
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-progress__track { height: 10px; border: 1.5px solid var(--rhine-ink); border-radius: 6px; background: #e7ebee; }
79
85
  .rhine-theme[data-rhine-theme="neo-brutalism"] .rhine-progress__track>span { background: var(--rhine-signal); }
@@ -18,6 +18,6 @@
18
18
  .rhine-timeline--compact { position:relative; gap:0; }
19
19
  .rhine-timeline--compact .rhine-timeline__track { height:20px; }
20
20
  .rhine-timeline--compact .rhine-timeline__track input { top:-12px; bottom:auto; }
21
- .rhine-timeline--compact .rhine-timeline__detail { position:absolute; z-index:2; top:100%; inset-inline:0; padding:4px 8px; border:1px solid var(--rhine-line); background:var(--rhine-surface); box-shadow:0 4px 12px #242b2814; pointer-events:none; visibility:hidden; }
22
- .rhine-timeline--compact:hover .rhine-timeline__detail,.rhine-timeline--compact:focus-within .rhine-timeline__detail { visibility:visible; }
21
+ .rhine-timeline-popover { z-index:50; width:min(360px,calc(100vw - 24px)); max-height:var(--radix-popover-content-available-height); overflow:auto; padding:16px; border:1px solid var(--rhine-control); border-radius:var(--rhine-radius-card); background:var(--rhine-overlay-bg); color:var(--rhine-overlay-text); box-shadow:0 4px 12px #242b2814; overflow-wrap:anywhere; }
22
+ .rhine-timeline-popover .rhine-key-value { margin-block:16px; }
23
23
  .rhine-timeline--compact:not(:hover):not(:focus-within) .rhine-timeline__bars [data-selected=true] { height:20px; outline:none; }
@@ -1,6 +1,25 @@
1
1
  /* Canonical tokens for the component library and design previews. */
2
2
  :root, :where(.rhine-theme[data-rhine-theme]) {
3
3
  --rhine-bg: #f2f1e9;
4
+ --rhine-backdrop-bg: #242b2870;
5
+ --rhine-content-texture: none;
6
+ --rhine-sidebar-texture: none;
7
+ --rhine-surface-texture: none;
8
+ --rhine-overlay-texture: none;
9
+ --rhine-media-texture: none;
10
+ --rhine-background-texture: none;
11
+ --rhine-background-scale: 24px 24px;
12
+ --rhine-background-mark: #c5ccc1;
13
+ --rhine-table-cell-padding: 12px 16px;
14
+ --rhine-tree-indent: 20px;
15
+ --rhine-chart-series-3: #477aa8;
16
+ --rhine-chart-series-4: #8763a7;
17
+ --rhine-media-canvas: #101214;
18
+ --rhine-media-overlay: #202428;
19
+ --rhine-media-text: #f6f7f8;
20
+ --rhine-media-muted: #bcc3ca;
21
+ --rhine-media-border: #68727c;
22
+ --rhine-media-focus: #9bceff;
4
23
  --rhine-surface: #fcfcf8;
5
24
  --rhine-ink: #242b28;
6
25
  --rhine-muted: #59645d;
@@ -61,3 +80,27 @@
61
80
  --rhine-ease-exit: cubic-bezier(.4, 0, 1, 1);
62
81
  --rhine-motion-distance: 3px;
63
82
  }
83
+
84
+ /* Resolve palette aliases at CSS-only roots as well as ThemeProvider roots. */
85
+ :root, :where(.rhine-theme) {
86
+ --rhine-canvas-bg: var(--rhine-bg);
87
+ --rhine-content-bg: var(--rhine-bg);
88
+ --rhine-sidebar-bg: var(--rhine-surface);
89
+ --rhine-surface-bg: var(--rhine-surface);
90
+ --rhine-overlay-bg: var(--rhine-surface);
91
+ --rhine-media-bg: var(--rhine-media-canvas);
92
+ --rhine-canvas-text: var(--rhine-ink);
93
+ --rhine-content-text: var(--rhine-ink);
94
+ --rhine-sidebar-text: var(--rhine-ink);
95
+ --rhine-surface-text: var(--rhine-ink);
96
+ --rhine-overlay-text: var(--rhine-ink);
97
+ --rhine-canvas-texture: var(--rhine-background-texture);
98
+ --rhine-table-header-bg: var(--rhine-surface);
99
+ --rhine-table-hover-bg: var(--rhine-selected);
100
+ --rhine-table-selected-bg: var(--rhine-selected);
101
+ --rhine-field-group-bg: var(--rhine-surface);
102
+ --rhine-chart-series-1: var(--rhine-green);
103
+ --rhine-chart-series-2: var(--rhine-signal);
104
+ --rhine-chart-grid: var(--rhine-line);
105
+ --rhine-chart-missing: var(--rhine-muted);
106
+ }