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 ADDED
@@ -0,0 +1,57 @@
1
+ # design26 — the personal visual language
2
+
3
+ This repository captures the existing UVID demonstration's visual identity for future projects. Read `docs/DESIGN-AUDIT.md` and `docs/ARCHITECTURE.md` before changing the system. The reference `../uvid-demonstration` is read-only: do not edit, build in, install into, reconfigure or run a dev server from that directory. Do not import its models, datasets, geometry, branding or assets.
4
+
5
+ ## Build with existing parts first
6
+
7
+ Import public APIs from `design26` and `design26/scientific`, and import `design26/styles.css` once at the host application's root. Reuse components and semantic tokens before introducing styling. Never import package source paths from applications. Wrap the page in `PageFrame`, or use `.d26-theme` / `[data-design26-theme="light"]` for an embedded region. Styles must remain independent of Tailwind and Next-specific components.
8
+
9
+ Generic UI: TechnicalGrid, PageFrame, Navigation, Panel, Eyebrow, SectionHeading, Button, TextLink, IconButton, StepNavigation, Tabs, Slider, Select, Checkbox, Disclosure, Dialog, StatusNote.
10
+
11
+ Scientific presentation: ParameterControl, DataPanel, Metric, FigureContainer, PlotContainer, PlotLegend. These present host-supplied data and artwork; they do not calculate models or fetch scientific datasets. Inspect exported prop types and `app/catalogue.ts` before extending an API.
12
+
13
+ ## Identity
14
+
15
+ - Electric blue `#075afa` frames cool paper `#f7f9fc`. Navy `#072754` is the primary ink; muted text is `#61718a`. Amber `#ed9019` is focus, selected data and meaningful emphasis.
16
+ - Use semantic variables: `--color-background`, `--color-surface`, `--color-surface-control`, `--color-text-primary`, `--color-text-secondary`, `--color-accent`, `--color-border`, `--color-highlight`. Do not add arbitrary hex values to component code.
17
+ - Body and headings are Arial, Helvetica, sans-serif. The original application loads Geist Sans but renders its body in Arial. Preserve that fact. Technical labels use Geist Mono via the host's `--font-geist-mono`, with a system monospace fallback. The package does not fetch fonts.
18
+ - Large medium-weight headings use tight tracking (`-.065em`); technical labels are small, uppercase, monospaced and widely tracked. Use SectionHeading size independently of semantic heading level. One main h1 per page. Use an em child for a single blue accent phrase.
19
+ - Use thin 1px rules, flat surfaces and quiet captions. Ordinary panels have no shadows. The dialog is the elevated exception. Avoid making every section a rounded card.
20
+
21
+ ## Layout
22
+
23
+ The frame max-width is1920px. Gutters are32px normally,44px at ≥1500px,24px at ≤1100px and14px at ≤760px. Native breakpoints are760,1100 and1500px. Do not replace them with Tailwind defaults.
24
+
25
+ Use asymmetric text/figure composition (47/53 hero,1.15/.85 scientific workspace), equal paired plots, generous section spacing and compact control spacing. `--space-panel-inline`, `--space-section`, `--space-grid`, `--space-control` and the existing recipes are the starting point. The source uses optical adjustments, not a uniform eight-pixel grid. Preserve measured recipes rather than rounding every value.
26
+
27
+ Use min-width:0, minmax(0,1fr), responsive SVG viewBox and intrinsic media ratios. Stack scientific figure/inspector and paired plots at mobile. Keep labels, units, controls and readings close together. Caption text explains what to notice. Do not hide essential information on narrow screens.
28
+
29
+ ## Interaction and accessibility
30
+
31
+ Use real buttons for actions, anchors for navigation, labelled native inputs and native details/dialog semantics. IconButton requires aria-label. Distinguish disabled from busy; do not use a waiting cursor on all disabled controls. All interactive elements, including summary/select, use visible amber focus. Do not remove outlines without an equivalent indicator.
32
+
33
+ Tabs implement roving focus, arrows/Home/End, disabled-item skipping and associated panels; do not build a second tab implementation. StepNavigation is numbered section navigation, not an ARIA tablist. Do not attach page-wide arrow shortcuts that steal keys from inputs or nested controls.
34
+
35
+ Use the observed200ms interaction timing,420ms/12px entrance and restrained status pulse. Respect prefers-reduced-motion for CSS and any application animation. Pause continuous work offscreen or in background tabs, and clean up listeners/timers. No Anime.js dependency is needed for the generic library. TechnicalGrid is an optional evidenced figure backdrop; pair it with a pause control. Avoid unrelated decorative autoplay, bouncing cards, large hover shifts and unrelated gradients.
36
+
37
+ ## Extending the system
38
+
39
+ 1. Find the closest existing component, token and reference pattern.
40
+ 2. Compose before extending. Extend a typed variant only when a repeated need is real.
41
+ 3. Add a new component only for a distinct reusable responsibility, with evidence or a clearly documented gap. Do not add a Tooltip, NumberInput or Toggle simply to fill a catalogue.
42
+ 4. Keep generic UI separate from scientific presentation. Keep business logic in the consumer.
43
+ 5. Add semantic tokens for genuine new roles. Preserve exact existing values; document canonicalisation in the audit. Theme overrides belong on the existing theme boundary. Do not invent a dark theme implicitly.
44
+ 6. Document purpose, props, variants, a real usage example, accessibility and recommended composition in the catalogue/package guide.
45
+ 7. Run library build, TypeScript, Next build, packed-consumer validation and relevant interaction/viewport checks. Verify that the reference fingerprints remain unchanged. Report any verification limits honestly.
46
+
47
+ Avoid application-specific names in public props, copied UVID calculations/assets, global CSS resets, a mandatory icon/chart framework, runtime CSS-in-JS, broad client boundaries, deep private imports, unexplained colours, exaggerated shadows, pill-shaped everything and inaccessible tiny essential text. Compact metadata is part of the identity; essential instructions and interactive content still need to be readable and usable.
48
+
49
+ ## Commands
50
+
51
+ - `pnpm dev`: build the package, then start the showcase.
52
+ - `pnpm build`: build package and production showcase.
53
+ - `pnpm typecheck`: package and application types.
54
+ - `pnpm verify:package`: pack/install in an isolated consumer, typecheck and server-render it without Tailwind/Next.
55
+ - `pnpm audit:reference`: verify the read-only reference snapshot.
56
+
57
+ When editing package source during an active showcase session, rebuild the package so public imports reflect the change. Do not work around package boundaries with source aliases. Use `docs/VALIDATION.md` for the latest evidence and known limits.
package/COMPONENTS.md ADDED
@@ -0,0 +1,325 @@
1
+ # design26 component catalogue
2
+
3
+ Import generic components from `design26` and scientific components from `design26/scientific`. Import `design26/styles.css` once and use a theme wrapper. All components accept their documented native HTML props and React 19 refs.
4
+
5
+ ## TechnicalGrid
6
+
7
+ A gently drifting 38px technical mesh, refined in the reference during this extraction.
8
+
9
+ - **Entry:** design26
10
+ - **Props:** active?: boolean; interactive?: boolean; native div props
11
+ - **Variants:** Animated / paused; pointer lens optional
12
+ - **Accessibility:** Decorative and hidden from assistive technology. Reduced motion renders a static grid. Animation pauses offscreen and in hidden tabs; pointer response requires a fine hover pointer.
13
+ - **Recommended use:** An optional figure backdrop, with positioned content above it. Keep it away from dense reading and provide a pause control where appropriate.
14
+
15
+ ```tsx
16
+ <div style={{ position: "relative" }}><TechnicalGrid /><Content /></div>
17
+ ```
18
+
19
+ ## PageFrame
20
+
21
+ The blue outer frame and opt-in theme boundary.
22
+
23
+ - **Entry:** design26
24
+ - **Props:** Native div props, children, className, ref
25
+ - **Variants:** One faithful light theme
26
+ - **Accessibility:** Use a main landmark inside the frame and a skip link before navigation.
27
+ - **Recommended use:** One outer frame per application. Use d26-theme alone for embedded UI.
28
+
29
+ ```tsx
30
+ <PageFrame><Navigation brand="Project" /><Panel>Content</Panel></PageFrame>
31
+ ```
32
+
33
+ ## Panel
34
+
35
+ Paper sheets, parameter surfaces and control decks.
36
+
37
+ - **Entry:** design26
38
+ - **Props:** variant?: "sheet" | "control" | "toolbar"; padding?: "content" | "none"; native div props
39
+ - **Variants:** Sheet / control / toolbar
40
+ - **Accessibility:** A visual wrapper has no automatic landmark. Label important sections with semantic HTML.
41
+ - **Recommended use:** Use fine rules to separate content; avoid nesting sheet after sheet.
42
+
43
+ ```tsx
44
+ <Panel variant="control"><ParameterControl {...parameter} /></Panel>
45
+ ```
46
+
47
+ ## Eyebrow
48
+
49
+ Technical context above a heading.
50
+
51
+ - **Entry:** design26
52
+ - **Props:** dot?: boolean; compact?: boolean; native div props
53
+ - **Variants:** Default / compact / status dot
54
+ - **Accessibility:** The dot is decorative. Put the actual status or category in text.
55
+ - **Recommended use:** Short uppercase labels. Do not use letter-spaced metadata for paragraphs.
56
+
57
+ ```tsx
58
+ <Eyebrow dot>RESEARCH / 01</Eyebrow>
59
+ ```
60
+
61
+ ## SectionHeading
62
+
63
+ The large, tightly tracked heading hierarchy.
64
+
65
+ - **Entry:** design26
66
+ - **Props:** as?: "h1" | "h2" | "h3" | "h4"; size?: "display" | "chapter" | "inspector" | "figure"; native heading props
67
+ - **Variants:** Display / chapter / inspector / figure
68
+ - **Accessibility:** Choose heading level by document structure; size is independent of semantics.
69
+ - **Recommended use:** One clear headline, with a single accent phrase when useful.
70
+
71
+ ```tsx
72
+ <SectionHeading as="h1" size="display">A different <em>perspective.</em></SectionHeading>
73
+ ```
74
+
75
+ ## Button
76
+
77
+ A primary action or a quiet supporting action.
78
+
79
+ - **Entry:** design26
80
+ - **Props:** variant?: "primary" | "text" | "playback"; endIcon?: ReactNode; busy?: boolean; native button props
81
+ - **Variants:** Primary / text / playback; disabled / busy
82
+ - **Accessibility:** Native keyboard behaviour; type defaults to button. Use busy for pending actions and give useful loading text.
83
+ - **Recommended use:** One primary action per local group. Links use TextLink.
84
+
85
+ ```tsx
86
+ <Button endIcon={<Arrow />} onClick={start}>Explore the project</Button>
87
+ ```
88
+
89
+ ## TextLink
90
+
91
+ A fine underlined link for supporting destinations.
92
+
93
+ - **Entry:** design26
94
+ - **Props:** Native anchor props including href, target, rel, ref
95
+ - **Variants:** One underlined treatment
96
+ - **Accessibility:** Supply meaningful link text. Indicate a new tab when relevant.
97
+ - **Recommended use:** Use for navigation, downloads and secondary reading.
98
+
99
+ ```tsx
100
+ <TextLink href="/methods">Read the methods</TextLink>
101
+ ```
102
+
103
+ ## IconButton
104
+
105
+ Compact playback and circular paging actions.
106
+
107
+ - **Entry:** design26
108
+ - **Props:** aria-label: string; variant?: "circle" | "step" | "plain"; native button props
109
+ - **Variants:** Circle 34px / step 44px / plain 25×26px
110
+ - **Accessibility:** An accessible name is required. Keep enough space around compact controls for touch users.
111
+ - **Recommended use:** Use the step variant for main figure navigation and plain for inline tools.
112
+
113
+ ```tsx
114
+ <IconButton aria-label="Next figure" variant="step"><Arrow /></IconButton>
115
+ ```
116
+
117
+ ## Navigation
118
+
119
+ White-on-blue wordmark, descriptor and links.
120
+
121
+ - **Entry:** design26
122
+ - **Props:** brand: ReactNode; description?: ReactNode; actions?: ReactNode; native header props
123
+ - **Variants:** Descriptor hides at 760px
124
+ - **Accessibility:** Brand links should identify the home destination. Actions are wrapped in a Main navigation landmark.
125
+ - **Recommended use:** Keep labels short. The header is part of the blue frame.
126
+
127
+ ```tsx
128
+ <Navigation brand={<a href="/">Project</a>} description="RESEARCH NOTES" actions={<a href="#work">Work</a>} />
129
+ ```
130
+
131
+ ## StepNavigation
132
+
133
+ Numbered section navigation with a fine active rule.
134
+
135
+ - **Entry:** design26
136
+ - **Props:** items: {id,label,disabled?}[]; value: string; onValueChange(value); aria-label: string; native nav props
137
+ - **Variants:** Responsive numbered steps
138
+ - **Accessibility:** Uses aria-current=step. Each button is in the normal tab order; content navigation belongs to the host.
139
+ - **Recommended use:** For ordered sections. Use Tabs for switching panels within a section.
140
+
141
+ ```tsx
142
+ <StepNavigation aria-label="Sections" items={sections} value={section} onValueChange={setSection} />
143
+ ```
144
+
145
+ ## Tabs
146
+
147
+ Switch among related views without leaving a section.
148
+
149
+ - **Entry:** design26
150
+ - **Props:** items: {value,label,content,disabled?}[]; value?; defaultValue?; onValueChange?; aria-label: string; native div props
151
+ - **Variants:** Controlled or uncontrolled; disabled items
152
+ - **Accessibility:** Roving focus, automatic activation, arrows/Home/End, disabled skipping and linked panels. Values must be unique.
153
+ - **Recommended use:** Two to four concise labels. Content belongs to the tab item, without global arrow shortcuts.
154
+
155
+ ```tsx
156
+ <Tabs aria-label="View" items={[{ value: "plot", label: "Plot", content: <Plot /> }, { value: "data", label: "Data", content: <Data /> }]} />
157
+ ```
158
+
159
+ ## Slider
160
+
161
+ A native range input in either reference treatment.
162
+
163
+ - **Entry:** design26
164
+ - **Props:** variant?: "parameter" | "timeline"; native range props; provide a label or aria-label
165
+ - **Variants:** Parameter 4px track / timeline 3px track
166
+ - **Accessibility:** Native arrows/Home/End. Supply aria-valuetext for formatted units. Disabled remains distinct from busy.
167
+ - **Recommended use:** Use ParameterControl when a visible label, readout and endpoints belong together.
168
+
169
+ ```tsx
170
+ <Slider variant="timeline" aria-label="Playback position" value={time} onChange={e => setTime(Number(e.target.value))} />
171
+ ```
172
+
173
+ ## Select
174
+
175
+ An understated native picker with an associated label.
176
+
177
+ - **Entry:** design26
178
+ - **Props:** label: ReactNode; description?: string; native select props; option children
179
+ - **Variants:** Native select, disabled
180
+ - **Accessibility:** Generated stable label and description IDs; native keyboard navigation. Do not remove the visible label.
181
+ - **Recommended use:** Use for longer sets of mutually exclusive options.
182
+
183
+ ```tsx
184
+ <Select label="View" defaultValue="overview"><option value="overview">Overview</option></Select>
185
+ ```
186
+
187
+ ## Checkbox
188
+
189
+ A compact native control for figure overlays and options.
190
+
191
+ - **Entry:** design26
192
+ - **Props:** label: ReactNode; native checkbox props
193
+ - **Variants:** Checked / unchecked / disabled
194
+ - **Accessibility:** Clickable label and native Space toggle. Describe actual effects in its label.
195
+ - **Recommended use:** Independent yes/no settings. Keep groups visually close to the affected figure.
196
+
197
+ ```tsx
198
+ <Checkbox label="Show grid" checked={grid} onChange={e => setGrid(e.target.checked)} />
199
+ ```
200
+
201
+ ## Disclosure
202
+
203
+ Secondary settings in a pale bordered details panel.
204
+
205
+ - **Entry:** design26
206
+ - **Props:** title: ReactNode; open?: boolean; name?: string; native details props
207
+ - **Variants:** Native open / closed
208
+ - **Accessibility:** Native summary toggles with Enter or Space and has a visible focus ring.
209
+ - **Recommended use:** Hide secondary complexity; never hide essential errors or primary actions.
210
+
211
+ ```tsx
212
+ <Disclosure title="Advanced settings"><p>Additional controls</p></Disclosure>
213
+ ```
214
+
215
+ ## Dialog
216
+
217
+ A paper modal with navy heading and a blurred backdrop.
218
+
219
+ - **Entry:** design26
220
+ - **Props:** open: boolean; onOpenChange(open); title: ReactNode; closeLabel?: string; native dialog props except open/title/onClose
221
+ - **Variants:** Controlled native modal
222
+ - **Accessibility:** Native modal focus containment, Escape, labelled title and focus return. Keep the trigger mounted.
223
+ - **Recommended use:** For focused supporting information; keep lengthy documentation in the page.
224
+
225
+ ```tsx
226
+ <Dialog open={open} onOpenChange={setOpen} title="About the project"><p>Project details.</p></Dialog>
227
+ ```
228
+
229
+ ## StatusNote
230
+
231
+ A restrained contextual or review note.
232
+
233
+ - **Entry:** design26
234
+ - **Props:** tone?: "info" | "positive" | "warning" | "error"; native paragraph props
235
+ - **Variants:** Info / positive / warning / error
236
+ - **Accessibility:** Write the status in text. Add role=status or role=alert only when the update needs announcement.
237
+ - **Recommended use:** Use observed teal and amber semantics. Avoid inventing a new red/green badge system.
238
+
239
+ ```tsx
240
+ <StatusNote tone="warning">Manual boundary · review required</StatusNote>
241
+ ```
242
+
243
+ ## ParameterControl
244
+
245
+ Label, unit-aware readout, native range and endpoint values.
246
+
247
+ - **Entry:** design26/scientific
248
+ - **Props:** label: string; value,min,max: number; onValueChange(number); step?,unit?,precision?,disabled?,description?,name?; native div props
249
+ - **Variants:** Numeric range; explicit precision and units
250
+ - **Accessibility:** Label and output reference the range ID; aria-valuetext includes units. Supply description for constraints.
251
+ - **Recommended use:** Keep valid ranges and scientific calculations in the consuming application.
252
+
253
+ ```tsx
254
+ <ParameterControl label="Angle" value={angle} min={-10} max={10} step={0.1} unit="°" onValueChange={setAngle} />
255
+ ```
256
+
257
+ ## DataPanel
258
+
259
+ Ruled label/value rows with monospaced readings.
260
+
261
+ - **Entry:** design26/scientific
262
+ - **Props:** items: {label:ReactNode,value:ReactNode}[]; busy?: boolean; native dl props
263
+ - **Variants:** Default / busy
264
+ - **Accessibility:** A semantic description list. Include units and explain pending values in nearby text.
265
+ - **Recommended use:** Metadata and compact readings. Use a real table for multi-column comparisons.
266
+
267
+ ```tsx
268
+ <DataPanel items={[{ label: "Sample rate", value: "20 Hz" }]} />
269
+ ```
270
+
271
+ ## Metric
272
+
273
+ A large blue reading, with quiet label and unit.
274
+
275
+ - **Entry:** design26/scientific
276
+ - **Props:** label,value: ReactNode; unit?: string; variant?: "default" | "result"; description?: ReactNode; native div props
277
+ - **Variants:** Default 34px / result 42px
278
+ - **Accessibility:** Units are text. Pair colour with labels; avoid live-announcing every animation frame.
279
+ - **Recommended use:** One or two key readings. Do not turn every data row into a metric.
280
+
281
+ ```tsx
282
+ <Metric label="Response time" value="24.8" unit="ms" />
283
+ ```
284
+
285
+ ## FigureContainer
286
+
287
+ A numbered figure, title and caption around supplied artwork.
288
+
289
+ - **Entry:** design26/scientific
290
+ - **Props:** title: ReactNode; number?,caption?: ReactNode; captionPosition?: "before" | "after"; native figure props except title
291
+ - **Variants:** Caption before / after
292
+ - **Accessibility:** Uses figure/figcaption; the child graphic still needs its own accessible title or description.
293
+ - **Recommended use:** Explain what to notice. Plotting algorithms remain with the host application.
294
+
295
+ ```tsx
296
+ <FigureContainer number="01 / RESPONSE" title="Measured response" caption="Illustrative data."><Plot /></FigureContainer>
297
+ ```
298
+
299
+ ## PlotContainer
300
+
301
+ A neutral, responsive frame for SVG, canvas or images.
302
+
303
+ - **Entry:** design26/scientific
304
+ - **Props:** aria-label: string; framed?: boolean; native div props
305
+ - **Variants:** Framed / unframed
306
+ - **Accessibility:** Describe what the figure shows; expose data textually when it matters. Interactive points need keyboard support.
307
+ - **Recommended use:** Use plot colour and axis classes. It is a presentation container, not a charting engine.
308
+
309
+ ```tsx
310
+ <PlotContainer aria-label="Response over time"><svg viewBox="0 0 620 450" role="img" aria-label="Illustrative response">…</svg></PlotContainer>
311
+ ```
312
+
313
+ ## PlotLegend
314
+
315
+ A small inline key for figure series.
316
+
317
+ - **Entry:** design26/scientific
318
+ - **Props:** items: {label:string,tone?:"series"|"accent"|"highlight"|"reference",dashed?:boolean}[]; native ul props
319
+ - **Variants:** Blue / accent / amber / reference; dashed key
320
+ - **Accessibility:** Text accompanies each marker. Use different line patterns or labels when series must be distinguished without colour.
321
+ - **Recommended use:** Keep keys adjacent to their figure and consistent with the actual marks.
322
+
323
+ ```tsx
324
+ <PlotLegend items={[{ label: "Observation", tone: "accent" }, { label: "Reference", tone: "highlight", dashed: true }]} />
325
+ ```
package/README.md CHANGED
@@ -1,3 +1,100 @@
1
- # Temporary Holding Version
1
+ # design26
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A personal visual language extracted from a scientific demonstration: electric blue framing, cool paper, navy type, precise technical labels and figure-led explanation.
4
+
5
+ ## Install
6
+
7
+ Within a pnpm workspace, include `packages/*` in `pnpm-workspace.yaml`, then add `design26: workspace:*` to the consuming application's dependencies. Build the package before starting the app:
8
+
9
+ ```sh
10
+ pnpm --filter design26 build
11
+ pnpm add design26@workspace:*
12
+ ```
13
+
14
+ For a separate repository, run `pnpm pack` from this package and install the resulting tarball with `pnpm add /absolute/path/design26-0.1.0.tgz`. Publication is not required. The package has ESM exports and TypeScript declarations and requires React19 and React DOM19 as peers. It includes no Tailwind, Next.js, chart or animation runtime dependencies. The npm name has not been reserved or published. License: UNLICENSED; choose an appropriate distribution license before public publication.
15
+
16
+ ## Use
17
+
18
+ Import CSS once in the host root layout or entry file. Interactive examples belong in a client component in Next.js.
19
+
20
+ ```tsx
21
+ import 'design26/styles.css';
22
+ import { PageFrame, Navigation, Panel, SectionHeading, TextLink } from 'design26';
23
+ import { DataPanel, FigureContainer } from 'design26/scientific';
24
+
25
+ export default function Page() {
26
+ return (
27
+ <PageFrame>
28
+ <Navigation brand={<a href="/">My research</a>} description="WORKING NOTES" />
29
+ <main>
30
+ <Panel>
31
+ <SectionHeading as="h1" size="display">A new <em>perspective.</em></SectionHeading>
32
+ <FigureContainer number="01 / OVERVIEW" title="What we observed" caption="Explain the important relationship.">
33
+ <DataPanel items={[{ label: 'Sample rate', value: '20 Hz' }]} />
34
+ </FigureContainer>
35
+ <TextLink href="/methods">Read the methods</TextLink>
36
+ </Panel>
37
+ </main>
38
+ </PageFrame>
39
+ );
40
+ }
41
+ ```
42
+
43
+ ```tsx
44
+ 'use client';
45
+ import { useState } from 'react';
46
+ import { Panel } from 'design26';
47
+ import { ParameterControl } from 'design26/scientific';
48
+
49
+ export function Controls() {
50
+ const [angle, setAngle] = useState(2);
51
+ return <Panel variant="control"><ParameterControl label="Angle" min={-10} max={10}
52
+ step={0.1} unit="°" value={angle} onValueChange={setAngle} /></Panel>;
53
+ }
54
+ ```
55
+
56
+ Static components do not require a client boundary. Tabs, form wrappers, Dialog and ParameterControl retain their own `use client` modules in the build. Event-handling application compositions need a host client boundary as usual. CSS imports are explicit and marked as side effects. Package imports use public `.js` ESM files with `.d.ts` definitions.
57
+
58
+ ## Theme and fonts
59
+
60
+ PageFrame supplies `.d26-theme`. For embedded components use a div with that class or `data-design26-theme="light"`. Nothing styles an unwrapped document. Component classes use the `d26-` prefix; styles do not reset global body/headings. All supplied components need the stylesheet and a theme ancestor.
61
+
62
+ Body/heading typography is Arial/Helvetica. For exact technical typography, load Geist Mono in the host and expose `--font-geist-mono`, e.g. using `next/font/google` in a Next application. Without it, technical labels use system monospace. Font files are not bundled and the library makes no network requests.
63
+
64
+ Override semantic tokens on the same wrapper. CSS properties inherit; defaults live at the theme boundary. Place custom overrides after the imported stylesheet.
65
+
66
+ ```css
67
+ .my-theme {
68
+ --color-accent: #075afa;
69
+ --color-background: #075afa;
70
+ --space-section: 36px;
71
+ }
72
+ ```
73
+
74
+ ```tsx
75
+ <PageFrame className="my-theme">...</PageFrame>
76
+ ```
77
+
78
+ Use `design26/tokens.css` alone when building a non-React composition. `design26/styles.css` already imports tokens. One faithful light theme is shipped; a dark theme is not invented. Exported `breakpoints` matches native CSS thresholds: mobile760px, tablet1100px, wide1500px. CSS variables cannot be used in native media-query conditions, so those literal thresholds are intentionally mirrored.
79
+
80
+ ## Components
81
+
82
+ Generic: TechnicalGrid, PageFrame, Navigation, Panel, Eyebrow, SectionHeading, Button, TextLink, IconButton, StepNavigation, Tabs, Slider, Select, Checkbox, Disclosure, Dialog, StatusNote.
83
+
84
+ Scientific: ParameterControl, DataPanel, Metric, FigureContainer, PlotContainer, PlotLegend. These receive data/children from the host and contain no UVID scientific logic.
85
+
86
+ See [COMPONENTS.md](COMPONENTS.md) for every component contract and [AGENTS.md](AGENTS.md) for future coding agents. These guides are included in the tarball.
87
+
88
+ Every component exports a matching `*Props` type. Native HTML attributes, including React19 refs, are accepted. Tabs also exports TabItem. The repository's live catalogue documents every prop contract, state, usage example and accessibility note. Source declarations are the authoritative complete native-plus-custom API.
89
+
90
+ ## Interaction contracts
91
+
92
+ - Button defaults to type=button; use TextLink for navigation. IconButton requires aria-label.
93
+ - Tabs values must be unique. It supports controlled/uncontrolled selection, arrows/Home/End and disabled skipping; current panel is keyboard focusable.
94
+ - Slider needs an associated label or aria-label. Use ParameterControl for labelled unit-aware ranges. Native min/max/step and meaningful limits are the host's responsibility. Avoid min=max.
95
+ - Select/Checkbox associate labels automatically. Supply descriptions for additional constraints.
96
+ - Dialog is controlled by open/onOpenChange. Keep its trigger mounted so native focus return can work. Give it a concise title; Escape closes it.
97
+ - DataPanel is a description list, not a multi-column table. Figure graphics need their own accessible description; PlotContainer does not make arbitrary child graphics accessible automatically.
98
+ - Focus uses an amber outline. Animation helpers d26-reveal and d26-pulse respect reduced motion. Do not infer scientific validity from presentation.
99
+
100
+ See the root audit and agent instructions before extending the system. Retain component recipes and optical spacing rather than imposing a new generic scale.
@@ -0,0 +1,15 @@
1
+ export { PageFrame, Panel, Eyebrow, SectionHeading, Button, TextLink, IconButton, Navigation, StepNavigation, Disclosure, StatusNote } from "./ui/primitives.js";
2
+ export type { PageFrameProps, PanelProps, EyebrowProps, SectionHeadingProps, ButtonProps, TextLinkProps, IconButtonProps, NavigationProps, StepNavigationProps, DisclosureProps, StatusNoteProps } from "./ui/primitives.js";
3
+ export { Slider, Select, Checkbox } from "./ui/controls.js";
4
+ export type { SliderProps, SelectProps, CheckboxProps } from "./ui/controls.js";
5
+ export { Tabs } from "./ui/tabs.js";
6
+ export type { TabsProps, TabItem } from "./ui/tabs.js";
7
+ export { Dialog } from "./ui/dialog.js";
8
+ export type { DialogProps } from "./ui/dialog.js";
9
+ export declare const breakpoints: {
10
+ readonly mobile: 760;
11
+ readonly tablet: 1100;
12
+ readonly wide: 1500;
13
+ };
14
+ export { TechnicalGrid } from "./ui/technical-grid.js";
15
+ export type { TechnicalGridProps } from "./ui/technical-grid.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { PageFrame, Panel, Eyebrow, SectionHeading, Button, TextLink, IconButton, Navigation, StepNavigation, Disclosure, StatusNote } from "./ui/primitives.js";
2
+ export { Slider, Select, Checkbox } from "./ui/controls.js";
3
+ export { Tabs } from "./ui/tabs.js";
4
+ export { Dialog } from "./ui/dialog.js";
5
+ export const breakpoints = { mobile: 760, tablet: 1100, wide: 1500 };
6
+ export { TechnicalGrid } from "./ui/technical-grid.js";
@@ -0,0 +1,15 @@
1
+ import { type HTMLAttributes } from "react";
2
+ export type ParameterControlProps = Omit<HTMLAttributes<HTMLDivElement>, "onChange"> & {
3
+ label: string;
4
+ value: number;
5
+ min: number;
6
+ max: number;
7
+ step?: number;
8
+ unit?: string;
9
+ precision?: number;
10
+ onValueChange: (value: number) => void;
11
+ disabled?: boolean;
12
+ description?: string;
13
+ name?: string;
14
+ };
15
+ export declare function ParameterControl({ label, value, min, max, step, unit, precision, onValueChange, disabled, description, name, id, className, ...props }: ParameterControlProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ import { Slider } from "../ui/controls.js";
5
+ import { cx } from "../ui/primitives.js";
6
+ export function ParameterControl({ label, value, min, max, step = 1, unit = "", precision = step < 1 ? 1 : 0, onValueChange, disabled, description, name, id, className, ...props }) {
7
+ const generated = useId(), uid = id ?? generated;
8
+ const formatted = value.toFixed(precision);
9
+ return _jsxs("div", { ...props, className: cx("d26-parameter", className), children: [_jsxs("label", { htmlFor: uid, children: [label, _jsxs("output", { htmlFor: uid, children: [formatted, _jsxs("span", { children: [" ", unit] })] })] }), _jsx(Slider, { id: uid, name: name, min: min, max: max, step: step, value: value, disabled: disabled, onChange: event => onValueChange(Number(event.target.value)), "aria-valuetext": `${formatted} ${unit}`.trim(), "aria-describedby": description ? `${uid}-description` : undefined }), _jsxs("div", { className: "d26-parameter-range", "aria-hidden": "true", children: [_jsxs("span", { children: [min, unit === "°" ? "°" : ""] }), _jsxs("span", { children: [max, unit === "°" ? "°" : ""] })] }), description && _jsx("p", { id: `${uid}-description`, className: "d26-field-description", children: description })] });
10
+ }
@@ -0,0 +1,37 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export type DataPanelProps = HTMLAttributes<HTMLDListElement> & {
3
+ items: {
4
+ label: ReactNode;
5
+ value: ReactNode;
6
+ }[];
7
+ busy?: boolean;
8
+ };
9
+ export declare function DataPanel({ items, busy, className, ...props }: DataPanelProps): import("react").JSX.Element;
10
+ export type MetricProps = HTMLAttributes<HTMLDivElement> & {
11
+ label: ReactNode;
12
+ value: ReactNode;
13
+ unit?: string;
14
+ variant?: "default" | "result";
15
+ description?: ReactNode;
16
+ };
17
+ export declare function Metric({ label, value, unit, description, variant, className, ...props }: MetricProps): import("react").JSX.Element;
18
+ export type FigureContainerProps = Omit<HTMLAttributes<HTMLElement>, "title"> & {
19
+ number?: string;
20
+ title: ReactNode;
21
+ caption?: ReactNode;
22
+ captionPosition?: "before" | "after";
23
+ };
24
+ export declare function FigureContainer({ number, title, caption, captionPosition, className, children, ...props }: FigureContainerProps): import("react").JSX.Element;
25
+ export type PlotContainerProps = HTMLAttributes<HTMLDivElement> & {
26
+ "aria-label": string;
27
+ framed?: boolean;
28
+ };
29
+ export declare function PlotContainer({ framed, className, ...props }: PlotContainerProps): import("react").JSX.Element;
30
+ export type PlotLegendProps = HTMLAttributes<HTMLUListElement> & {
31
+ items: {
32
+ label: string;
33
+ tone?: "series" | "accent" | "highlight" | "reference";
34
+ dashed?: boolean;
35
+ }[];
36
+ };
37
+ export declare function PlotLegend({ items, className, ...props }: PlotLegendProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../ui/primitives.js";
3
+ export function DataPanel({ items, busy, className, ...props }) { return _jsx("dl", { ...props, "aria-busy": busy || undefined, className: cx("d26-data", className), children: items.map((item, index) => _jsxs("div", { children: [_jsx("dt", { children: item.label }), _jsx("dd", { children: item.value })] }, index)) }); }
4
+ export function Metric({ label, value, unit, description, variant = "default", className, ...props }) { return _jsxs("div", { ...props, className: cx("d26-metric", `d26-metric--${variant}`, className), children: [_jsx("span", { children: label }), _jsxs("strong", { children: [value, unit && _jsxs("small", { children: [" ", unit] })] }), description && _jsx("p", { children: description })] }); }
5
+ export function FigureContainer({ number, title, caption, captionPosition = "before", className, children, ...props }) {
6
+ const text = _jsxs("figcaption", { children: [number && _jsx("span", { className: "d26-figure-number", children: number }), _jsx("h3", { children: title }), caption && _jsx("p", { children: caption })] });
7
+ return _jsxs("figure", { ...props, className: cx("d26-figure", captionPosition === "after" && "d26-figure--caption-after", className), children: [captionPosition === "before" && text, children, captionPosition === "after" && text] });
8
+ }
9
+ export function PlotContainer({ framed = true, className, ...props }) { return _jsx("div", { ...props, role: props.role ?? "group", className: cx("d26-plot", framed && "d26-plot--framed", className) }); }
10
+ export function PlotLegend({ items, className, ...props }) { return _jsx("ul", { ...props, className: cx("d26-legend", className), children: items.map(item => _jsxs("li", { children: [_jsx("i", { "aria-hidden": "true", className: cx(`d26-legend--${item.tone ?? "series"}`, item.dashed && "d26-legend--dashed") }), item.label] }, item.label)) }); }
@@ -0,0 +1,4 @@
1
+ export { DataPanel, Metric, FigureContainer, PlotContainer, PlotLegend } from "./scientific/patterns.js";
2
+ export type { DataPanelProps, MetricProps, FigureContainerProps, PlotContainerProps, PlotLegendProps } from "./scientific/patterns.js";
3
+ export { ParameterControl } from "./scientific/parameter-control.js";
4
+ export type { ParameterControlProps } from "./scientific/parameter-control.js";
@@ -0,0 +1,2 @@
1
+ export { DataPanel, Metric, FigureContainer, PlotContainer, PlotLegend } from "./scientific/patterns.js";
2
+ export { ParameterControl } from "./scientific/parameter-control.js";