design26 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +57 -0
- package/COMPONENTS.md +325 -0
- package/README.md +99 -2
- package/dist/index.d.ts +15 -0
- package/dist/index.js +6 -0
- package/dist/scientific/parameter-control.d.ts +15 -0
- package/dist/scientific/parameter-control.js +10 -0
- package/dist/scientific/patterns.d.ts +37 -0
- package/dist/scientific/patterns.js +10 -0
- package/dist/scientific.d.ts +4 -0
- package/dist/scientific.js +2 -0
- package/dist/styles.css +185 -0
- package/dist/tokens.css +165 -0
- package/dist/ui/controls.d.ts +15 -0
- package/dist/ui/controls.js +16 -0
- package/dist/ui/dialog.d.ts +8 -0
- package/dist/ui/dialog.js +13 -0
- package/dist/ui/primitives.d.ts +59 -0
- package/dist/ui/primitives.js +13 -0
- package/dist/ui/tabs.d.ts +15 -0
- package/dist/ui/tabs.js +32 -0
- package/dist/ui/technical-grid.d.ts +7 -0
- package/dist/ui/technical-grid.js +79 -0
- package/package.json +38 -3
package/dist/styles.css
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
@import "./tokens.css";
|
|
2
|
+
|
|
3
|
+
/* Styles are opt-in and scoped. No global body, heading or control reset. */
|
|
4
|
+
.d26-theme, [data-design26-theme="light"] { box-sizing: border-box; color: var(--color-text-primary); font-family: var(--font-body); font-weight: var(--font-weight-body); line-height: var(--line-height-base); -webkit-font-smoothing: antialiased; }
|
|
5
|
+
:where(.d26-theme, [data-design26-theme="light"]) *, :where(.d26-theme, [data-design26-theme="light"]) *::before, :where(.d26-theme, [data-design26-theme="light"]) *::after { box-sizing: border-box; }
|
|
6
|
+
:where(.d26-theme, [data-design26-theme="light"]) :is(button, a, input, select, summary, [tabindex]):focus-visible { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
|
|
7
|
+
:where(.d26-theme, [data-design26-theme="light"]) ::selection { background: var(--color-selection); color: var(--color-text-primary); }
|
|
8
|
+
.d26-frame { background: var(--color-background); min-height: 100svh; padding: 0 var(--space-page-gutter); }
|
|
9
|
+
.d26-frame > * { max-width: calc(var(--size-content-max) - var(--space-page-gutter) * 2); margin-inline: auto; }
|
|
10
|
+
.d26-panel { min-width: 0; box-shadow: var(--shadow-none); }
|
|
11
|
+
.d26-panel--sheet { background: var(--color-surface); border-radius: var(--radius-sheet); }
|
|
12
|
+
.d26-panel--padded { padding: var(--space-panel-block) var(--space-panel-inline); }
|
|
13
|
+
.d26-panel--control { display: grid; grid-template-columns: minmax(0, 1fr); gap: 36px; background: var(--color-surface-control); border: var(--border-width) solid var(--color-border-control); border-radius: var(--radius-panel); padding: 24px 26px 20px; }
|
|
14
|
+
.d26-panel--toolbar { background: var(--color-surface-toolbar); border-top: var(--border-rule); padding: 21px 32px; }
|
|
15
|
+
.d26-eyebrow { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; color: var(--color-accent); font: var(--font-weight-heading) var(--font-size-label)/1.5 var(--font-mono); letter-spacing: var(--letter-spacing-eyebrow); }
|
|
16
|
+
.d26-eyebrow--compact { font-size: var(--font-size-metadata); letter-spacing: 1.2px; gap: 8px; }
|
|
17
|
+
.d26-status-dot { display: inline-block; width: 6px; height: 6px; flex: 0 0 6px; border-radius: var(--radius-round); background: var(--color-accent); }
|
|
18
|
+
.d26-heading { font-family: var(--font-body); color: var(--color-text-primary); font-weight: var(--font-weight-heading); }
|
|
19
|
+
.d26-heading em { font-style: normal; color: var(--color-accent); }
|
|
20
|
+
.d26-heading--display { font-size: var(--font-size-display); line-height: var(--line-height-display); letter-spacing: var(--letter-spacing-display); margin: 30px 0 26px; }
|
|
21
|
+
.d26-heading--chapter { font-size: var(--font-size-chapter); line-height: var(--line-height-chapter); letter-spacing: var(--letter-spacing-display); margin: 27px 0 22px; }
|
|
22
|
+
.d26-heading--inspector { font-size: var(--font-size-inspector); line-height: 1.12; letter-spacing: -1.2px; margin: 18px 0 16px; }
|
|
23
|
+
.d26-heading--figure { font-size: var(--font-size-figure-title); letter-spacing: -.65px; margin: 0 0 10px; }
|
|
24
|
+
.d26-button, .d26-icon-button { cursor: pointer; font-family: var(--font-body); border: 0; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
|
|
25
|
+
.d26-button { line-height: var(--line-height-base); display: inline-flex; align-items: center; justify-content: space-between; font-size: var(--font-size-control); font-weight: var(--font-weight-body); }
|
|
26
|
+
.d26-button--primary { background: var(--color-accent); color: var(--color-text-on-accent); border-radius: var(--radius-button); padding: 17px 20px; gap: 32px; transition: background var(--motion-interaction), transform var(--motion-interaction); }
|
|
27
|
+
.d26-button--primary:hover:not(:disabled) { background: var(--color-accent-hover); transform: translateY(var(--distance-hover)); }
|
|
28
|
+
.d26-button--text { background: transparent; color: var(--color-accent); padding: 8px 0; gap: 16px; font-size: 11px; }
|
|
29
|
+
.d26-button--text:hover:not(:disabled) { color: var(--color-text-primary); }
|
|
30
|
+
.d26-button--playback { color: var(--color-accent); background: var(--color-surface-control); border: var(--border-width) solid var(--color-border-disclosure); padding: 9px 12px; border-radius: var(--radius-pill); font-size: 11px; gap: 9px; }
|
|
31
|
+
.d26-button-icon, .d26-icon-button > span { display: inline-flex; align-items: center; justify-content: center; }
|
|
32
|
+
.d26-button-icon svg { width: var(--size-icon); height: var(--size-icon); }
|
|
33
|
+
.d26-button:disabled, .d26-icon-button:disabled { opacity: .6; cursor: default; }
|
|
34
|
+
.d26-button[aria-busy="true"] { cursor: progress; }
|
|
35
|
+
.d26-text-link { display: inline-flex; align-items: center; gap: 8px; color: inherit; text-decoration: none; border-bottom: var(--border-width) solid var(--color-border-link); font-size: 11px; padding: 7px 0; }
|
|
36
|
+
.d26-text-link:hover { color: var(--color-accent); border-color: var(--color-accent); }
|
|
37
|
+
.d26-icon-button { display: inline-grid; place-items: center; flex-shrink: 0; color: var(--color-text-primary); background: transparent; padding: 0; width: var(--size-icon-control); height: var(--size-icon-control); border: var(--border-width) solid var(--color-border-interactive); border-radius: var(--radius-round); }
|
|
38
|
+
.d26-icon-button:hover:not(:disabled) { color: var(--color-accent); }
|
|
39
|
+
.d26-icon-button svg { width: 19px; height: 19px; }
|
|
40
|
+
.d26-icon-button--step { width: var(--size-step-control); height: var(--size-step-control); }
|
|
41
|
+
.d26-icon-button--step:hover:not(:disabled) { background: var(--color-accent-soft); border-color: var(--color-border-interactive-hover); }
|
|
42
|
+
.d26-icon-button--plain { width: 25px; height: 26px; border: 0; border-radius: 0; }
|
|
43
|
+
.d26-icon-button--plain svg { width: 14px; height: 14px; }
|
|
44
|
+
.d26-navigation { display: flex; align-items: center; min-height: 105px; color: var(--color-text-on-accent); gap: 36px; }
|
|
45
|
+
.d26-brand { font-size: 39px; font-weight: 700; letter-spacing: -2px; line-height: 1; }
|
|
46
|
+
.d26-brand a { color: inherit; text-decoration: none; }
|
|
47
|
+
.d26-navigation-description { font-size: 12px; letter-spacing: .1px; color: var(--color-text-on-frame); border-left: var(--border-width) solid var(--color-border-on-frame); padding-left: 28px; }
|
|
48
|
+
.d26-navigation-actions { margin-left: auto; display: flex; align-items: center; gap: 22px; font-size: 12px; }
|
|
49
|
+
.d26-navigation-actions a { color: inherit; text-decoration: none; }
|
|
50
|
+
.d26-navigation-actions a:hover { color: var(--color-text-on-frame); }
|
|
51
|
+
.d26-steps { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); padding: 0 36px; border-bottom: var(--border-rule); }
|
|
52
|
+
.d26-steps button { position: relative; display: flex; align-items: center; gap: 16px; min-height: 60px; padding: 12px 24px; text-align: left; background: transparent; border: 0; color: var(--color-step-inactive); font: 12px/1.5 var(--font-body); cursor: pointer; transition: color var(--motion-interaction); }
|
|
53
|
+
.d26-steps button + button::before { content: ""; position: absolute; left: 0; width: 1px; height: 22px; background: var(--color-border); }
|
|
54
|
+
.d26-steps button::after { content: ""; position: absolute; bottom: -1px; left: 24px; right: 24px; height: var(--border-width-active); background: transparent; }
|
|
55
|
+
.d26-steps button[aria-current="step"]::after { background: var(--color-accent); }
|
|
56
|
+
.d26-steps button:hover:not(:disabled), .d26-steps button[aria-current="step"] { color: var(--color-text-primary); }
|
|
57
|
+
.d26-step-number { font: 10px/1.5 var(--font-mono); color: var(--color-step-number); }
|
|
58
|
+
.d26-steps button[aria-current="step"] .d26-step-number { color: var(--color-accent); }
|
|
59
|
+
.d26-step-dot { margin-left: auto; width: 5px; height: 5px; border-radius: var(--radius-round); background: var(--color-step-dot); }
|
|
60
|
+
.d26-steps button[aria-current="step"] .d26-step-dot { background: var(--color-accent); box-shadow: var(--shadow-status); }
|
|
61
|
+
.d26-tab-list { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0,1fr); border-bottom: var(--border-width) solid var(--color-border-tabs); }
|
|
62
|
+
.d26-tab-list button { display: flex; align-items: center; justify-content: center; gap: 13px; background: transparent; color: var(--color-tab-inactive); padding: 17px 8px; font: 13px/1.5 var(--font-body); border: 0; border-bottom: var(--border-width-active) solid transparent; margin-bottom: -1px; cursor: pointer; }
|
|
63
|
+
.d26-tab-list button > span { font: 9px/1.5 var(--font-mono); color: var(--color-tab-number); }
|
|
64
|
+
.d26-tab-list button[aria-selected="true"], .d26-tab-list button[aria-selected="true"] > span { color: var(--color-accent); border-bottom-color: var(--color-accent); }
|
|
65
|
+
.d26-tab-list button:hover:not(:disabled) { color: var(--color-text-primary); }
|
|
66
|
+
.d26-tab-panel { padding-top: 16px; }
|
|
67
|
+
.d26-tab-panel[hidden] { display: none; }
|
|
68
|
+
.d26-tab-list button:disabled, .d26-steps button:disabled { opacity: .5; cursor: default; }
|
|
69
|
+
.d26-slider { appearance: none; -webkit-appearance: none; display: block; width: 100%; height: var(--size-track); background: var(--color-track); border: 0; border-radius: 2px; margin: 10px 0; padding: 0; cursor: pointer; accent-color: var(--color-accent); }
|
|
70
|
+
.d26-slider::-webkit-slider-thumb { appearance: none; width: var(--size-thumb); height: var(--size-thumb); background: var(--color-accent); border: 3px solid var(--color-surface); border-radius: var(--radius-round); box-shadow: 0 0 0 1px var(--color-accent); }
|
|
71
|
+
.d26-slider::-moz-range-thumb { box-sizing: border-box; width: var(--size-thumb); height: var(--size-thumb); background: var(--color-accent); border: 3px solid var(--color-surface); border-radius: var(--radius-round); box-shadow: 0 0 0 1px var(--color-accent); }
|
|
72
|
+
.d26-slider--timeline { height: 3px; border-radius: 3px; background: linear-gradient(to right, var(--color-accent) var(--d26-progress, 0%), var(--color-track-timeline) var(--d26-progress, 0%)); }
|
|
73
|
+
.d26-slider--timeline::-webkit-slider-thumb { width: 9px; height: 9px; border: 0; box-shadow: 0 0 0 3px var(--color-surface-toolbar); }
|
|
74
|
+
.d26-slider--timeline::-moz-range-thumb { width: 9px; height: 9px; border: 0; box-shadow: 0 0 0 3px var(--color-surface-toolbar); }
|
|
75
|
+
.d26-slider:disabled, .d26-select:disabled, .d26-checkbox input:disabled { cursor: default; opacity: .5; }
|
|
76
|
+
.d26-field { display: flex; flex-direction: column; gap: 8px; }
|
|
77
|
+
.d26-field > label { font-size: 9px; color: var(--color-text-field); }
|
|
78
|
+
.d26-select { appearance: auto; width: 100%; background: var(--color-surface-input); border: var(--border-width) solid var(--color-border-input); border-radius: var(--radius-input); padding: 10px 12px; color: var(--color-text-primary); font: 11px/1.5 var(--font-body); cursor: pointer; }
|
|
79
|
+
.d26-field-description { display: block; color: var(--color-text-secondary); font: 10px/1.8 var(--font-body); margin: 8px 0 0; }
|
|
80
|
+
.d26-checkbox { display: inline-flex; align-items: center; gap: 6px; font-size: 9px; color: var(--color-text-secondary); cursor: pointer; }
|
|
81
|
+
.d26-checkbox input { accent-color: var(--color-accent); cursor: pointer; width: 12px; height: 12px; margin: 0; }
|
|
82
|
+
.d26-disclosure { border: var(--border-width) solid var(--color-border-disclosure); border-radius: var(--radius-input); background: var(--color-surface-disclosure); }
|
|
83
|
+
.d26-disclosure summary { display: flex; justify-content: space-between; align-items: center; gap: 20px; padding: 12px 14px; color: var(--color-accent); font-size: 11px; list-style: none; cursor: pointer; }
|
|
84
|
+
.d26-disclosure summary::-webkit-details-marker { display: none; }
|
|
85
|
+
.d26-disclosure summary > span { font-size: 17px; }
|
|
86
|
+
.d26-disclosure[open] summary > span { transform: rotate(180deg); }
|
|
87
|
+
.d26-disclosure > div { padding: 15px; border-top: var(--border-width) solid var(--color-border-disclosure); }
|
|
88
|
+
.d26-dialog { padding: 0; border: 0; border-radius: var(--radius-dialog); background: var(--color-surface); color: var(--color-text-primary); font-family: var(--font-body); width: min(var(--size-dialog-max), calc(100% - 32px)); max-height: calc(100svh - 48px); margin: auto; box-shadow: var(--shadow-dialog); }
|
|
89
|
+
.d26-dialog::backdrop { background: var(--color-overlay); backdrop-filter: blur(var(--backdrop-blur)); }
|
|
90
|
+
.d26-dialog-content { padding: 34px 38px; }
|
|
91
|
+
.d26-dialog-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
|
|
92
|
+
.d26-dialog-header h2 { font-size: 43px; line-height: 1.05; letter-spacing: -2px; font-weight: var(--font-weight-heading); margin: 0 0 20px; }
|
|
93
|
+
.d26-dialog-header .d26-icon-button { font-size: 27px; }
|
|
94
|
+
.d26-note { color: var(--color-text-secondary); background: var(--color-accent-soft); padding: 9px 11px; border-radius: var(--radius-note); font: 10px/1.8 var(--font-body); margin: 16px 0 0; }
|
|
95
|
+
.d26-note--positive { color: var(--color-positive); background: var(--color-positive-surface); }
|
|
96
|
+
.d26-note--warning { color: var(--color-warning); background: var(--color-warning-surface); }
|
|
97
|
+
.d26-note--error { color: var(--color-error); background: var(--color-warning-surface); }
|
|
98
|
+
.d26-parameter { min-width: 0; }
|
|
99
|
+
.d26-parameter > label { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-control); color: var(--color-text-control); font-size: 12px; margin-bottom: 17px; }
|
|
100
|
+
.d26-parameter output { color: var(--color-accent); font: 17px/1.5 var(--font-mono); white-space: nowrap; }
|
|
101
|
+
.d26-parameter output > span { color: var(--color-text-parameter-unit); font-size: 10px; }
|
|
102
|
+
.d26-parameter-range { display: flex; justify-content: space-between; color: var(--color-text-range); font: 8px/1.5 var(--font-mono); margin-top: 11px; }
|
|
103
|
+
.d26-data { margin: 0; }
|
|
104
|
+
.d26-data > div { display: flex; justify-content: space-between; align-items: baseline; gap: 15px; border-bottom: var(--border-width) solid var(--color-border-data); padding: var(--space-data-row) 0; font-size: 10px; }
|
|
105
|
+
.d26-data dt { color: var(--color-text-data-label); }
|
|
106
|
+
.d26-data dd { margin: 0; font: 10px/1.5 var(--font-mono); text-align: right; color: var(--color-text-data); overflow-wrap: anywhere; }
|
|
107
|
+
.d26-data[aria-busy="true"] { opacity: .5; transition: opacity var(--motion-busy) var(--ease-interaction); }
|
|
108
|
+
.d26-metric { min-width: 0; }
|
|
109
|
+
.d26-metric > span { font-size: 10px; color: var(--color-text-label); display: block; margin-bottom: 12px; }
|
|
110
|
+
.d26-metric strong { color: var(--color-accent); font-size: var(--font-size-metric); line-height: 1; letter-spacing: -1.2px; font-weight: var(--font-weight-body); }
|
|
111
|
+
.d26-metric small { font-size: 13px; color: var(--color-text-metric-unit); letter-spacing: 0; }
|
|
112
|
+
.d26-metric p { color: var(--color-text-secondary); font: 12px/1.8 var(--font-body); margin-bottom: 0; }
|
|
113
|
+
.d26-metric--result strong { font-size: var(--font-size-result); letter-spacing: -1.5px; }
|
|
114
|
+
.d26-metric--result small { font-size: 15px; }
|
|
115
|
+
.d26-figure { min-width: 0; margin: 0; }
|
|
116
|
+
.d26-figure figcaption { padding-bottom: var(--space-figure-caption); }
|
|
117
|
+
.d26-figure--caption-after figcaption { padding: var(--space-figure-caption) 0 0; }
|
|
118
|
+
.d26-figure-number { display: block; font: 7px/1.5 var(--font-mono); letter-spacing: .7px; color: var(--color-text-figure-number); margin-bottom: 12px; }
|
|
119
|
+
.d26-figure h3 { font-size: var(--font-size-figure-title); font-weight: var(--font-weight-heading); letter-spacing: -.65px; margin: 0 0 10px; }
|
|
120
|
+
.d26-figure figcaption p { color: var(--color-text-caption); font: 11px/1.8 var(--font-body); margin: 0; max-width: 340px; }
|
|
121
|
+
.d26-plot { min-width: 0; width: 100%; }
|
|
122
|
+
.d26-plot--framed { border: var(--border-width) solid var(--color-plot-border); border-radius: var(--radius-panel); background: var(--color-surface-elevated); overflow: hidden; }
|
|
123
|
+
.d26-plot > :is(svg, canvas, img) { display: block; width: 100%; height: auto; }
|
|
124
|
+
.d26-plot-grid { stroke: var(--color-plot-grid); stroke-width: 1; }
|
|
125
|
+
.d26-plot-axis { stroke: var(--color-plot-axis); stroke-width: 1; }
|
|
126
|
+
.d26-plot-tick { fill: var(--color-plot-tick); font: 11px/1.5 var(--font-mono); }
|
|
127
|
+
.d26-plot-label { fill: var(--color-plot-label); font: 12px/1.5 var(--font-body); }
|
|
128
|
+
.d26-legend { list-style: none; display: flex; flex-wrap: wrap; gap: 8px 12px; padding: 12px 0 0 15px; margin: 0; font-size: 8px; color: var(--color-text-caption); }
|
|
129
|
+
.d26-legend li { display: inline-flex; align-items: center; gap: 5px; }
|
|
130
|
+
.d26-legend i { width: 5px; height: 5px; border-radius: var(--radius-round); background: var(--color-plot-series); }
|
|
131
|
+
.d26-legend .d26-legend--accent { background: var(--color-accent); }
|
|
132
|
+
.d26-legend .d26-legend--highlight { background: var(--color-highlight); }
|
|
133
|
+
.d26-legend .d26-legend--reference { background: var(--color-plot-reference); }
|
|
134
|
+
.d26-legend .d26-legend--dashed { width: 13px; height: 0; border-radius: 0; background: transparent; border-top: 2px dashed var(--color-highlight); }
|
|
135
|
+
.d26-reveal { animation: d26-enter var(--motion-enter) var(--ease-enter) both; }
|
|
136
|
+
.d26-pulse { animation: d26-pulse var(--motion-pulse) ease-in-out infinite; }
|
|
137
|
+
@keyframes d26-enter { from { opacity: 0; transform: translateY(var(--distance-enter)); } to { opacity: 1; transform: translateY(0); } }
|
|
138
|
+
@keyframes d26-pulse { 50% { opacity: .35; } }
|
|
139
|
+
@media (min-width: 1500px) { .d26-navigation { min-height: 118px; } }
|
|
140
|
+
@media (max-width: 1100px) {
|
|
141
|
+
.d26-navigation { min-height: 86px; }
|
|
142
|
+
.d26-button--primary { padding: 15px 16px; gap: 20px; }
|
|
143
|
+
.d26-steps { padding: 0 18px; }
|
|
144
|
+
.d26-steps button { padding: 12px 15px; gap: 10px; font-size: 11px; }
|
|
145
|
+
.d26-steps button::after { left: 15px; right: 15px; }
|
|
146
|
+
.d26-tab-list button { font-size: 11px; gap: 8px; }
|
|
147
|
+
}
|
|
148
|
+
@media (max-width: 760px) {
|
|
149
|
+
.d26-navigation { min-height: 79px; gap: 15px; padding: 0 7px; }
|
|
150
|
+
.d26-brand { font-size: 32px; }
|
|
151
|
+
.d26-navigation-description { display: none; }
|
|
152
|
+
.d26-navigation-actions { font-size: 10px; gap: 13px; }
|
|
153
|
+
.d26-heading--display { margin: 23px 0 21px; }
|
|
154
|
+
.d26-heading--chapter { margin: 22px 0 18px; }
|
|
155
|
+
.d26-eyebrow { font-size: 8px; letter-spacing: 1.5px; }
|
|
156
|
+
.d26-eyebrow--compact { font-size: 7px; letter-spacing: .8px; }
|
|
157
|
+
.d26-button--primary { font-size: 11px; padding: 15px 17px; gap: 22px; }
|
|
158
|
+
.d26-panel--control { padding: 20px; gap: 25px; }
|
|
159
|
+
.d26-metric > span { font-size: 9px; line-height: 1.6; }
|
|
160
|
+
.d26-panel--toolbar { padding: 17px 20px; }
|
|
161
|
+
.d26-steps { padding: 0 12px; }
|
|
162
|
+
.d26-steps button { min-height: 62px; padding: 11px 10px; gap: 4px; align-items: flex-start; flex-direction: column; font-size: 9px; line-height: 1.4; }
|
|
163
|
+
.d26-step-number { font-size: 8px; }
|
|
164
|
+
.d26-steps button::after { left: 10px; right: 10px; }
|
|
165
|
+
.d26-steps button + button::before { top: 21px; }
|
|
166
|
+
.d26-step-dot { display: none; }
|
|
167
|
+
.d26-tab-list button { flex-direction: column; justify-content: flex-start; padding: 13px 5px; gap: 8px; font-size: 10px; line-height: 1.5; }
|
|
168
|
+
.d26-tab-list button > span { font-size: 8px; }
|
|
169
|
+
.d26-select { min-height: 40px; }
|
|
170
|
+
.d26-dialog-content { padding: 26px; }
|
|
171
|
+
.d26-dialog-header h2 { font-size: 38px; }
|
|
172
|
+
.d26-figure-number { font-size: 8px; }
|
|
173
|
+
.d26-figure figcaption p { font-size: 12px; max-width: none; }
|
|
174
|
+
.d26-plot-tick { font-size: 18px; }
|
|
175
|
+
.d26-plot-label { font-size: 16px; }
|
|
176
|
+
.d26-legend { font-size: 9px; }
|
|
177
|
+
}
|
|
178
|
+
@media (prefers-reduced-motion: reduce) {
|
|
179
|
+
:where(.d26-theme, [data-design26-theme="light"]) *, :where(.d26-theme, [data-design26-theme="light"]) *::before, :where(.d26-theme, [data-design26-theme="light"]) *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
|
|
180
|
+
}
|
|
181
|
+
/* Optional backdrop extracted from the reference's concurrent grid refinement. */
|
|
182
|
+
.d26-technical-grid { position: absolute; inset: 0; z-index: 0; pointer-events: none; mask-image: radial-gradient(ellipse 38% 62% at 74% 46%, #000 8%, #000c 30%, #0003 72%, #0001 100%); }
|
|
183
|
+
.d26-technical-grid::before { content: ""; position: absolute; inset: 0; background-image: linear-gradient(var(--color-grid) .8px, transparent .8px), linear-gradient(90deg, var(--color-grid) .8px, transparent .8px); background-size: var(--size-grid-spacing) var(--size-grid-spacing); }
|
|
184
|
+
.d26-technical-grid:has(canvas[data-ready="true"])::before { display: none; }
|
|
185
|
+
.d26-technical-grid canvas { display: block; width: 100%; height: 100%; }
|
package/dist/tokens.css
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* One reference theme. Override semantic roles on the same theme wrapper. */
|
|
2
|
+
.d26-theme, [data-design26-theme="light"] {
|
|
3
|
+
--d26-blue: #075afa;
|
|
4
|
+
--d26-navy: #072754;
|
|
5
|
+
--d26-paper: #f7f9fc;
|
|
6
|
+
--color-background: var(--d26-blue);
|
|
7
|
+
--color-brand: var(--d26-blue);
|
|
8
|
+
--color-surface: var(--d26-paper);
|
|
9
|
+
--color-surface-elevated: #fbfcfe;
|
|
10
|
+
--color-surface-toolbar: #f3f6fb;
|
|
11
|
+
--color-surface-control: #edf3fc;
|
|
12
|
+
--color-surface-input: #eff4fc;
|
|
13
|
+
--color-surface-disclosure: #f0f5fd;
|
|
14
|
+
--color-text-primary: var(--d26-navy);
|
|
15
|
+
--color-text-secondary: #61718a;
|
|
16
|
+
--color-text-description: #5c6d84;
|
|
17
|
+
--color-text-subtitle: #355274;
|
|
18
|
+
--color-text-caption: #7b90ac;
|
|
19
|
+
--color-text-label: #7d91ac;
|
|
20
|
+
--color-text-field: #7d90a8;
|
|
21
|
+
--color-text-metric-unit: #8397b3;
|
|
22
|
+
--color-text-parameter-unit: #91a3bb;
|
|
23
|
+
--color-text-range: #96a7bd;
|
|
24
|
+
--color-text-figure-number: #8da0b9;
|
|
25
|
+
--color-text-data-label: #7488a2;
|
|
26
|
+
--color-step-inactive: #7d8ca1;
|
|
27
|
+
--color-step-number: #91a0b4;
|
|
28
|
+
--color-step-dot: #d3ddeb;
|
|
29
|
+
--color-border-tabs: #d8e2ee;
|
|
30
|
+
--color-text-data: #284d7b;
|
|
31
|
+
--color-text-control: #35577f;
|
|
32
|
+
--color-text-on-accent: #fff;
|
|
33
|
+
--color-text-on-frame: #deebff;
|
|
34
|
+
--color-text-footer: #c7dcff;
|
|
35
|
+
--color-text-disabled: #a7b5c8;
|
|
36
|
+
--color-accent: var(--d26-blue);
|
|
37
|
+
--color-accent-hover: #004ad7;
|
|
38
|
+
--color-accent-soft: #eaf1ff;
|
|
39
|
+
--color-highlight: #ed9019;
|
|
40
|
+
--color-focus: var(--color-highlight);
|
|
41
|
+
--color-border: #dee5ee;
|
|
42
|
+
--color-border-data: #e0e7f1;
|
|
43
|
+
--color-border-control: #dce6f5;
|
|
44
|
+
--color-border-input: #d8e3f1;
|
|
45
|
+
--color-border-link: #b8c8dd;
|
|
46
|
+
--color-border-interactive-hover: #a4badd;
|
|
47
|
+
--color-border-interactive: #cfdae9;
|
|
48
|
+
--color-border-disclosure: #d7e2f1;
|
|
49
|
+
--color-border-on-frame: #ffffff45;
|
|
50
|
+
--color-tab-inactive: #7a8da6;
|
|
51
|
+
--color-tab-number: #9aacbf;
|
|
52
|
+
--color-track: #ccdaf0;
|
|
53
|
+
--color-track-timeline: #d6e0ed;
|
|
54
|
+
--color-positive: #306b72;
|
|
55
|
+
--color-positive-surface: #edf5f4;
|
|
56
|
+
--color-warning: #9a6a20;
|
|
57
|
+
--color-warning-surface: #fbf0de;
|
|
58
|
+
--color-error: #9d6b24;
|
|
59
|
+
--color-selection: #bdd6ff;
|
|
60
|
+
--color-overlay: #021b4b80;
|
|
61
|
+
--color-status-halo: #e7efff;
|
|
62
|
+
--color-grid: rgba(150, 174, 207, .32);
|
|
63
|
+
--size-grid-spacing: 38px;
|
|
64
|
+
--color-plot-grid: #e0e8f2;
|
|
65
|
+
--color-plot-axis: #9bb0cb;
|
|
66
|
+
--color-plot-tick: #8a9db5;
|
|
67
|
+
--color-plot-label: #6681a5;
|
|
68
|
+
--color-plot-border: #dce5f0;
|
|
69
|
+
--color-plot-series: #7eaafb;
|
|
70
|
+
--color-plot-point-muted: #768ca8;
|
|
71
|
+
--color-plot-reference: #c5b568;
|
|
72
|
+
--color-plot-heat-low: #eff4fc;
|
|
73
|
+
--color-plot-heat-mid: #9cc1ff;
|
|
74
|
+
--font-body: Arial, Helvetica, sans-serif;
|
|
75
|
+
--font-mono: var(--font-geist-mono, "Geist Mono"), monospace;
|
|
76
|
+
--font-weight-body: 400;
|
|
77
|
+
--font-weight-heading: 500;
|
|
78
|
+
--font-weight-strong: 600;
|
|
79
|
+
--font-size-display: clamp(48px, 5.1vw, 82px);
|
|
80
|
+
--font-size-chapter: clamp(56px, 5.6vw, 88px);
|
|
81
|
+
--font-size-inspector: clamp(25px, 2.3vw, 36px);
|
|
82
|
+
--font-size-subtitle: clamp(17px, 1.5vw, 22px);
|
|
83
|
+
--font-size-hero-copy: clamp(13px, 1.12vw, 17px);
|
|
84
|
+
--font-size-body: 13px;
|
|
85
|
+
--font-size-copy-compact: 12px;
|
|
86
|
+
--font-size-control: 12px;
|
|
87
|
+
--font-size-label: 10px;
|
|
88
|
+
--font-size-metadata: 8px;
|
|
89
|
+
--font-size-figure-title: 20px;
|
|
90
|
+
--font-size-metric: 34px;
|
|
91
|
+
--font-size-result: 42px;
|
|
92
|
+
--line-height-base: 1.5;
|
|
93
|
+
--line-height-display: .995;
|
|
94
|
+
--line-height-chapter: 1.02;
|
|
95
|
+
--line-height-copy: 1.9;
|
|
96
|
+
--line-height-compact: 1.85;
|
|
97
|
+
--letter-spacing-display: -.065em;
|
|
98
|
+
--letter-spacing-eyebrow: 1.8px;
|
|
99
|
+
--space-page-gutter: 32px;
|
|
100
|
+
--space-panel-block: 40px;
|
|
101
|
+
--space-panel-inline: clamp(35px, 5vw, 80px);
|
|
102
|
+
--space-section: 36px;
|
|
103
|
+
--space-grid: 30px;
|
|
104
|
+
--space-stack: 22px;
|
|
105
|
+
--space-control: 14px;
|
|
106
|
+
--space-data-row: 10px;
|
|
107
|
+
--space-figure-caption: 17px;
|
|
108
|
+
--size-content-max: 1920px;
|
|
109
|
+
--size-media-max: 465px;
|
|
110
|
+
--size-dialog-max: 540px;
|
|
111
|
+
--size-icon: 18px;
|
|
112
|
+
--size-icon-control: 34px;
|
|
113
|
+
--size-step-control: 44px;
|
|
114
|
+
--size-track: 4px;
|
|
115
|
+
--size-thumb: 15px;
|
|
116
|
+
--radius-sheet: 20px;
|
|
117
|
+
--radius-dialog: 16px;
|
|
118
|
+
--radius-panel: 8px;
|
|
119
|
+
--radius-media: 10px;
|
|
120
|
+
--radius-button: 6px;
|
|
121
|
+
--radius-input: 5px;
|
|
122
|
+
--radius-note: 4px;
|
|
123
|
+
--radius-tag: 3px;
|
|
124
|
+
--radius-pill: 24px;
|
|
125
|
+
--radius-round: 50%;
|
|
126
|
+
--border-width: 1px;
|
|
127
|
+
--border-width-active: 2px;
|
|
128
|
+
--border-rule: var(--border-width) solid var(--color-border);
|
|
129
|
+
--shadow-dialog: 0 30px 100px #001e6a45;
|
|
130
|
+
--shadow-none: none;
|
|
131
|
+
--shadow-status: 0 0 0 4px var(--color-status-halo);
|
|
132
|
+
--focus-width: 3px;
|
|
133
|
+
--focus-offset: 5px;
|
|
134
|
+
--motion-interaction: 200ms;
|
|
135
|
+
--motion-busy: 180ms;
|
|
136
|
+
--motion-enter: 420ms;
|
|
137
|
+
--motion-surface: 500ms;
|
|
138
|
+
--motion-pulse: 1500ms;
|
|
139
|
+
--ease-interaction: ease;
|
|
140
|
+
--ease-enter: cubic-bezier(.25, .46, .45, .94);
|
|
141
|
+
--distance-hover: -2px;
|
|
142
|
+
--distance-enter: 12px;
|
|
143
|
+
--backdrop-blur: 7px;
|
|
144
|
+
/* Documentation mirrors. Native media-query conditions below use literals. */
|
|
145
|
+
--breakpoint-mobile: 760px;
|
|
146
|
+
--breakpoint-tablet: 1100px;
|
|
147
|
+
--breakpoint-wide: 1500px;
|
|
148
|
+
}
|
|
149
|
+
@media (min-width: 1500px) {
|
|
150
|
+
.d26-theme, [data-design26-theme="light"] { --space-page-gutter: 44px; --size-media-max: 525px; }
|
|
151
|
+
}
|
|
152
|
+
@media (max-width: 1100px) {
|
|
153
|
+
.d26-theme, [data-design26-theme="light"] { --space-page-gutter: 24px; --space-panel-block: 35px; --space-panel-inline: 35px; --font-size-chapter: 56px; }
|
|
154
|
+
}
|
|
155
|
+
@media (max-width: 760px) {
|
|
156
|
+
.d26-theme, [data-design26-theme="light"] {
|
|
157
|
+
--space-page-gutter: 14px; --space-panel-block: 32px; --space-panel-inline: 25px;
|
|
158
|
+
--radius-sheet: 14px; --font-size-display: clamp(32px, 8.9vw, 60px);
|
|
159
|
+
--line-height-base: 1.5;
|
|
160
|
+
--line-height-display: .99; --font-size-chapter: 49px; --font-size-inspector: 28px;
|
|
161
|
+
--font-size-subtitle: 17px; --font-size-hero-copy: 13px; --font-size-metric: 29px;
|
|
162
|
+
--font-size-figure-title: 22px; --space-section: 27px; --space-grid: 26px;
|
|
163
|
+
--size-media-max: 465px;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type InputHTMLAttributes, type ReactNode, type SelectHTMLAttributes } from "react";
|
|
2
|
+
export type SliderProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> & {
|
|
3
|
+
variant?: "parameter" | "timeline";
|
|
4
|
+
"aria-label"?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function Slider({ variant, min, max, value, defaultValue, style, className, ...props }: SliderProps): import("react").JSX.Element;
|
|
7
|
+
export type SelectProps = SelectHTMLAttributes<HTMLSelectElement> & {
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
description?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function Select({ label, description, id, className, children, ...props }: SelectProps): import("react").JSX.Element;
|
|
12
|
+
export type CheckboxProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> & {
|
|
13
|
+
label: ReactNode;
|
|
14
|
+
};
|
|
15
|
+
export declare function Checkbox({ label, className, id, ...props }: CheckboxProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
import { cx } from "./primitives.js";
|
|
5
|
+
export function Slider({ variant = "parameter", min = 0, max = 100, value, defaultValue, style, className, ...props }) {
|
|
6
|
+
const lower = Number(min), upper = Number(max);
|
|
7
|
+
const numeric = Number(value ?? defaultValue ?? (lower + upper) / 2);
|
|
8
|
+
const progress = upper > lower ? Math.max(0, Math.min(100, (numeric - lower) / (upper - lower) * 100)) : 0;
|
|
9
|
+
return _jsx("input", { ...props, type: "range", min: min, max: max, value: value, defaultValue: defaultValue, style: { "--d26-progress": `${progress}%`, ...style }, className: cx("d26-slider", `d26-slider--${variant}`, className), onInput: event => { const input = event.currentTarget; input.style.setProperty("--d26-progress", `${(Number(input.value) - lower) / (upper - lower || 1) * 100}%`); props.onInput?.(event); } });
|
|
10
|
+
}
|
|
11
|
+
export function Select({ label, description, id, className, children, ...props }) {
|
|
12
|
+
const generated = useId(), inputId = id ?? generated;
|
|
13
|
+
const describedBy = [props["aria-describedby"], description ? `${inputId}-description` : undefined].filter(Boolean).join(" ") || undefined;
|
|
14
|
+
return _jsxs("div", { className: "d26-field", children: [_jsx("label", { htmlFor: inputId, children: label }), _jsx("select", { ...props, id: inputId, "aria-describedby": describedBy, className: cx("d26-select", className), children: children }), description && _jsx("span", { id: `${inputId}-description`, className: "d26-field-description", children: description })] });
|
|
15
|
+
}
|
|
16
|
+
export function Checkbox({ label, className, id, ...props }) { const generated = useId(); return _jsxs("label", { className: cx("d26-checkbox", className), htmlFor: id ?? generated, children: [_jsx("input", { ...props, id: id ?? generated, type: "checkbox" }), _jsx("span", { children: label })] }); }
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type DialogHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type DialogProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, "open" | "title" | "onClose"> & {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
title: ReactNode;
|
|
6
|
+
closeLabel?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function Dialog({ open, onOpenChange, title, closeLabel, className, children, ...props }: DialogProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useRef } from "react";
|
|
4
|
+
import { cx, IconButton } from "./primitives.js";
|
|
5
|
+
export function Dialog({ open, onOpenChange, title, closeLabel = "Close dialog", className, children, ...props }) {
|
|
6
|
+
const ref = useRef(null), titleId = useId();
|
|
7
|
+
useEffect(() => { const dialog = ref.current; if (!dialog)
|
|
8
|
+
return; if (open && !dialog.open)
|
|
9
|
+
dialog.showModal(); if (!open && dialog.open)
|
|
10
|
+
dialog.close(); }, [open]);
|
|
11
|
+
return _jsx("dialog", { ...props, ref: ref, "aria-labelledby": titleId, className: cx("d26-dialog", className), onClose: () => onOpenChange(false), onCancel: event => { props.onCancel?.(event); if (!event.defaultPrevented)
|
|
12
|
+
onOpenChange(false); }, children: _jsxs("div", { className: "d26-dialog-content", children: [_jsxs("div", { className: "d26-dialog-header", children: [_jsx("h2", { id: titleId, children: title }), _jsx(IconButton, { variant: "plain", "aria-label": closeLabel, onClick: () => onOpenChange(false), children: "\u00D7" })] }), children] }) });
|
|
13
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
export declare const cx: (...values: Array<string | undefined | false>) => string;
|
|
3
|
+
export type PageFrameProps = HTMLAttributes<HTMLDivElement>;
|
|
4
|
+
export declare function PageFrame({ className, ...props }: PageFrameProps): import("react").JSX.Element;
|
|
5
|
+
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
variant?: "sheet" | "control" | "toolbar";
|
|
7
|
+
padding?: "none" | "content";
|
|
8
|
+
};
|
|
9
|
+
export declare function Panel({ variant, padding, className, ...props }: PanelProps): import("react").JSX.Element;
|
|
10
|
+
export type EyebrowProps = HTMLAttributes<HTMLDivElement> & {
|
|
11
|
+
dot?: boolean;
|
|
12
|
+
compact?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function Eyebrow({ dot, compact, className, children, ...props }: EyebrowProps): import("react").JSX.Element;
|
|
15
|
+
export type SectionHeadingProps = HTMLAttributes<HTMLHeadingElement> & {
|
|
16
|
+
as?: "h1" | "h2" | "h3" | "h4";
|
|
17
|
+
size?: "display" | "chapter" | "inspector" | "figure";
|
|
18
|
+
};
|
|
19
|
+
export declare function SectionHeading({ as: Tag, size, className, ...props }: SectionHeadingProps): import("react").JSX.Element;
|
|
20
|
+
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
21
|
+
variant?: "primary" | "text" | "playback";
|
|
22
|
+
endIcon?: ReactNode;
|
|
23
|
+
busy?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export declare function Button({ variant, type, busy, endIcon, className, children, disabled, ...props }: ButtonProps): import("react").JSX.Element;
|
|
26
|
+
export type TextLinkProps = AnchorHTMLAttributes<HTMLAnchorElement>;
|
|
27
|
+
export declare function TextLink({ className, ...props }: TextLinkProps): import("react").JSX.Element;
|
|
28
|
+
export type IconButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "aria-label"> & {
|
|
29
|
+
"aria-label": string;
|
|
30
|
+
variant?: "circle" | "step" | "plain";
|
|
31
|
+
};
|
|
32
|
+
export declare function IconButton({ variant, type, className, children, ...props }: IconButtonProps): import("react").JSX.Element;
|
|
33
|
+
export type NavigationProps = HTMLAttributes<HTMLElement> & {
|
|
34
|
+
brand: ReactNode;
|
|
35
|
+
description?: ReactNode;
|
|
36
|
+
actions?: ReactNode;
|
|
37
|
+
};
|
|
38
|
+
export declare function Navigation({ brand, description, actions, className, ...props }: NavigationProps): import("react").JSX.Element;
|
|
39
|
+
export type StepNavigationProps = Omit<HTMLAttributes<HTMLElement>, "onChange"> & {
|
|
40
|
+
items: {
|
|
41
|
+
id: string;
|
|
42
|
+
label: string;
|
|
43
|
+
disabled?: boolean;
|
|
44
|
+
}[];
|
|
45
|
+
value: string;
|
|
46
|
+
onValueChange: (value: string) => void;
|
|
47
|
+
"aria-label": string;
|
|
48
|
+
};
|
|
49
|
+
export declare function StepNavigation({ items, value, onValueChange, className, ...props }: StepNavigationProps): import("react").JSX.Element;
|
|
50
|
+
export type DisclosureProps = Omit<HTMLAttributes<HTMLDetailsElement>, "title"> & {
|
|
51
|
+
title: ReactNode;
|
|
52
|
+
open?: boolean;
|
|
53
|
+
name?: string;
|
|
54
|
+
};
|
|
55
|
+
export declare function Disclosure({ title, className, children, ...props }: DisclosureProps): import("react").JSX.Element;
|
|
56
|
+
export type StatusNoteProps = HTMLAttributes<HTMLParagraphElement> & {
|
|
57
|
+
tone?: "info" | "positive" | "warning" | "error";
|
|
58
|
+
};
|
|
59
|
+
export declare function StatusNote({ tone, className, ...props }: StatusNoteProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export const cx = (...values) => values.filter(Boolean).join(" ");
|
|
3
|
+
export function PageFrame({ className, ...props }) { return _jsx("div", { ...props, className: cx("d26-theme d26-frame", className) }); }
|
|
4
|
+
export function Panel({ variant = "sheet", padding = "content", className, ...props }) { return _jsx("div", { ...props, className: cx("d26-panel", `d26-panel--${variant}`, padding === "content" && "d26-panel--padded", className) }); }
|
|
5
|
+
export function Eyebrow({ dot = false, compact = false, className, children, ...props }) { return _jsxs("div", { ...props, className: cx("d26-eyebrow", compact && "d26-eyebrow--compact", className), children: [dot && _jsx("span", { className: "d26-status-dot", "aria-hidden": "true" }), children] }); }
|
|
6
|
+
export function SectionHeading({ as: Tag = "h2", size = "chapter", className, ...props }) { return _jsx(Tag, { ...props, className: cx("d26-heading", `d26-heading--${size}`, className) }); }
|
|
7
|
+
export function Button({ variant = "primary", type = "button", busy, endIcon, className, children, disabled, ...props }) { return _jsxs("button", { ...props, type: type, disabled: disabled || busy, "aria-busy": busy || undefined, className: cx("d26-button", `d26-button--${variant}`, className), children: [children, endIcon && _jsx("span", { className: "d26-button-icon", "aria-hidden": "true", children: endIcon })] }); }
|
|
8
|
+
export function TextLink({ className, ...props }) { return _jsx("a", { ...props, className: cx("d26-text-link", className) }); }
|
|
9
|
+
export function IconButton({ variant = "circle", type = "button", className, children, ...props }) { return _jsx("button", { ...props, type: type, className: cx("d26-icon-button", `d26-icon-button--${variant}`, className), children: _jsx("span", { "aria-hidden": "true", children: children }) }); }
|
|
10
|
+
export function Navigation({ brand, description, actions, className, ...props }) { return _jsxs("header", { ...props, className: cx("d26-navigation", className), children: [_jsx("div", { className: "d26-brand", children: brand }), description && _jsx("div", { className: "d26-navigation-description", children: description }), actions && _jsx("nav", { "aria-label": "Main", className: "d26-navigation-actions", children: actions })] }); }
|
|
11
|
+
export function StepNavigation({ items, value, onValueChange, className, ...props }) { return _jsx("nav", { ...props, className: cx("d26-steps", className), children: items.map((item, i) => _jsxs("button", { type: "button", disabled: item.disabled, "aria-current": value === item.id ? "step" : undefined, onClick: () => onValueChange(item.id), children: [_jsx("span", { className: "d26-step-number", children: String(i + 1).padStart(2, "0") }), _jsx("span", { children: item.label }), _jsx("span", { className: "d26-step-dot", "aria-hidden": "true" })] }, item.id)) }); }
|
|
12
|
+
export function Disclosure({ title, className, children, ...props }) { return _jsxs("details", { ...props, className: cx("d26-disclosure", className), children: [_jsxs("summary", { children: [title, _jsx("span", { "aria-hidden": "true", children: "\u2304" })] }), _jsx("div", { children: children })] }); }
|
|
13
|
+
export function StatusNote({ tone = "info", className, ...props }) { return _jsx("p", { ...props, className: cx("d26-note", `d26-note--${tone}`, className) }); }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type TabItem = {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
content: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type TabsProps = Omit<HTMLAttributes<HTMLDivElement>, "onChange"> & {
|
|
9
|
+
items: TabItem[];
|
|
10
|
+
value?: string;
|
|
11
|
+
defaultValue?: string;
|
|
12
|
+
onValueChange?: (value: string) => void;
|
|
13
|
+
"aria-label": string;
|
|
14
|
+
};
|
|
15
|
+
export declare function Tabs({ items, value, defaultValue, onValueChange, className, "aria-label": label, ...props }: TabsProps): import("react").JSX.Element;
|
package/dist/ui/tabs.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "./primitives.js";
|
|
5
|
+
export function Tabs({ items, value, defaultValue, onValueChange, className, "aria-label": label, ...props }) {
|
|
6
|
+
const [internal, setInternal] = useState(defaultValue ?? items.find(item => !item.disabled)?.value);
|
|
7
|
+
const active = value ?? internal;
|
|
8
|
+
const selected = items.findIndex(item => item.value === active && !item.disabled);
|
|
9
|
+
const current = selected >= 0 ? selected : items.findIndex(item => !item.disabled);
|
|
10
|
+
const uid = useId(), refs = useRef([]);
|
|
11
|
+
function choose(index) { const item = items[index]; if (!item || item.disabled)
|
|
12
|
+
return; setInternal(item.value); onValueChange?.(item.value); }
|
|
13
|
+
return _jsxs("div", { ...props, className: cx("d26-tabs", className), children: [_jsx("div", { role: "tablist", "aria-label": label, className: "d26-tab-list", children: items.map((item, i) => _jsxs("button", { type: "button", ref: element => { refs.current[i] = element; }, role: "tab", disabled: item.disabled, tabIndex: i === current ? 0 : -1, "aria-selected": i === current, "aria-controls": `${uid}-panel-${i}`, id: `${uid}-tab-${i}`, onClick: () => choose(i), onKeyDown: event => {
|
|
14
|
+
const enabled = items.map((entry, index) => entry.disabled ? -1 : index).filter(index => index >= 0);
|
|
15
|
+
const position = enabled.indexOf(i);
|
|
16
|
+
let next;
|
|
17
|
+
if (event.key === "ArrowRight")
|
|
18
|
+
next = enabled[(position + 1) % enabled.length];
|
|
19
|
+
if (event.key === "ArrowLeft")
|
|
20
|
+
next = enabled[(position - 1 + enabled.length) % enabled.length];
|
|
21
|
+
if (event.key === "Home")
|
|
22
|
+
next = enabled[0];
|
|
23
|
+
if (event.key === "End")
|
|
24
|
+
next = enabled[enabled.length - 1];
|
|
25
|
+
if (next === undefined)
|
|
26
|
+
return;
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
event.stopPropagation();
|
|
29
|
+
choose(next);
|
|
30
|
+
refs.current[next]?.focus();
|
|
31
|
+
}, children: [_jsx("span", { children: String(i + 1).padStart(2, "0") }), item.label] }, item.value)) }), items.map((item, i) => _jsx("div", { role: "tabpanel", hidden: i !== current, tabIndex: 0, id: `${uid}-panel-${i}`, "aria-labelledby": `${uid}-tab-${i}`, className: "d26-tab-panel", children: item.content }, item.value))] });
|
|
32
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "react";
|
|
2
|
+
export type TechnicalGridProps = HTMLAttributes<HTMLDivElement> & {
|
|
3
|
+
active?: boolean;
|
|
4
|
+
interactive?: boolean;
|
|
5
|
+
};
|
|
6
|
+
/** Decorative, self-contained grid. Place inside a positioned figure/panel. */
|
|
7
|
+
export declare function TechnicalGrid({ active, interactive, className, ...props }: TechnicalGridProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { cx } from "./primitives.js";
|
|
5
|
+
/** Decorative, self-contained grid. Place inside a positioned figure/panel. */
|
|
6
|
+
export function TechnicalGrid({ active = true, interactive = true, className, ...props }) {
|
|
7
|
+
const ref = useRef(null);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const canvas = ref.current, host = canvas?.parentElement?.parentElement;
|
|
10
|
+
const context = canvas?.getContext("2d");
|
|
11
|
+
if (!canvas || !host || !context)
|
|
12
|
+
return;
|
|
13
|
+
const motion = matchMedia("(prefers-reduced-motion: reduce)"), fine = matchMedia("(any-hover: hover) and (any-pointer: fine)");
|
|
14
|
+
const pointer = { x: 0, y: 0, targetX: 0, targetY: 0, strength: 0, targetStrength: 0 };
|
|
15
|
+
let width = 0, height = 0, frame = 0, last = 0, drift = 0, visible = true;
|
|
16
|
+
const spacing = Number.parseFloat(getComputedStyle(canvas).getPropertyValue("--size-grid-spacing")) || 38;
|
|
17
|
+
const stroke = getComputedStyle(canvas).getPropertyValue("--color-grid").trim() || "rgba(150,174,207,.32)";
|
|
18
|
+
function draw() {
|
|
19
|
+
if (!context)
|
|
20
|
+
return;
|
|
21
|
+
context.clearRect(0, 0, width, height);
|
|
22
|
+
context.strokeStyle = stroke;
|
|
23
|
+
context.lineWidth = .8;
|
|
24
|
+
context.beginPath();
|
|
25
|
+
function point(x, y, first) {
|
|
26
|
+
const dx = x - pointer.x, dy = y - pointer.y;
|
|
27
|
+
const force = Math.exp(-(dx * dx + dy * dy) / (2 * 110 * 110)) * .3 * pointer.strength;
|
|
28
|
+
if (first)
|
|
29
|
+
context.moveTo(x + dx * force, y + dy * force);
|
|
30
|
+
else
|
|
31
|
+
context.lineTo(x + dx * force, y + dy * force);
|
|
32
|
+
}
|
|
33
|
+
for (let x = drift * 2 % spacing - spacing; x <= width + spacing; x += spacing)
|
|
34
|
+
for (let y = -spacing; y <= height + spacing; y += 12)
|
|
35
|
+
point(x, y, y === -spacing);
|
|
36
|
+
for (let y = drift * 1.2 % spacing - spacing; y <= height + spacing; y += spacing)
|
|
37
|
+
for (let x = -spacing; x <= width + spacing; x += 12)
|
|
38
|
+
point(x, y, x === -spacing);
|
|
39
|
+
context.stroke();
|
|
40
|
+
}
|
|
41
|
+
function tick(time) {
|
|
42
|
+
frame = requestAnimationFrame(tick);
|
|
43
|
+
if (time - last < 1000 / 30)
|
|
44
|
+
return;
|
|
45
|
+
const delta = last ? Math.min((time - last) / 1000, .1) : 0;
|
|
46
|
+
last = time;
|
|
47
|
+
drift += delta;
|
|
48
|
+
const easing = 1 - Math.exp(-delta * 9);
|
|
49
|
+
pointer.x += (pointer.targetX - pointer.x) * easing;
|
|
50
|
+
pointer.y += (pointer.targetY - pointer.y) * easing;
|
|
51
|
+
pointer.strength += (pointer.targetStrength - pointer.strength) * easing;
|
|
52
|
+
draw();
|
|
53
|
+
}
|
|
54
|
+
function sync() { cancelAnimationFrame(frame); last = 0; pointer.targetStrength = 0; pointer.strength = 0; draw(); if (active && !motion.matches && !document.hidden && visible)
|
|
55
|
+
frame = requestAnimationFrame(tick); }
|
|
56
|
+
function resize() { if (!canvas || !context)
|
|
57
|
+
return; const rect = canvas.getBoundingClientRect(), scale = Math.min(devicePixelRatio || 1, 2); width = rect.width; height = rect.height; canvas.width = Math.round(width * scale); canvas.height = Math.round(height * scale); context.setTransform(scale, 0, 0, scale, 0, 0); draw(); canvas.dataset.ready = "true"; }
|
|
58
|
+
function move(event) { if (!canvas || !active || !interactive || motion.matches || !fine.matches || event.pointerType === "touch")
|
|
59
|
+
return; const rect = canvas.getBoundingClientRect(); pointer.targetX = event.clientX - rect.left; pointer.targetY = event.clientY - rect.top; if (pointer.targetStrength === 0 && pointer.strength < .01) {
|
|
60
|
+
pointer.x = pointer.targetX;
|
|
61
|
+
pointer.y = pointer.targetY;
|
|
62
|
+
} pointer.targetStrength = 1; }
|
|
63
|
+
function leave() { pointer.targetStrength = 0; }
|
|
64
|
+
const observer = new ResizeObserver(resize), visibility = new IntersectionObserver(([entry]) => { visible = entry.isIntersecting; sync(); });
|
|
65
|
+
observer.observe(canvas);
|
|
66
|
+
visibility.observe(host);
|
|
67
|
+
host.addEventListener("pointermove", move, { passive: true });
|
|
68
|
+
host.addEventListener("pointerleave", leave);
|
|
69
|
+
host.addEventListener("pointercancel", leave);
|
|
70
|
+
window.addEventListener("blur", leave);
|
|
71
|
+
document.addEventListener("visibilitychange", sync);
|
|
72
|
+
motion.addEventListener("change", sync);
|
|
73
|
+
fine.addEventListener("change", leave);
|
|
74
|
+
resize();
|
|
75
|
+
sync();
|
|
76
|
+
return () => { cancelAnimationFrame(frame); observer.disconnect(); visibility.disconnect(); host.removeEventListener("pointermove", move); host.removeEventListener("pointerleave", leave); host.removeEventListener("pointercancel", leave); window.removeEventListener("blur", leave); document.removeEventListener("visibilitychange", sync); motion.removeEventListener("change", sync); fine.removeEventListener("change", leave); };
|
|
77
|
+
}, [active, interactive]);
|
|
78
|
+
return _jsx("div", { ...props, className: cx("d26-technical-grid", className), "aria-hidden": "true", children: _jsx("canvas", { ref: ref }) });
|
|
79
|
+
}
|