@nextop-os/ui-system 0.0.17 → 0.0.18
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 +30 -8
- package/README.md +11 -14
- package/agent/install-skill.mjs +65 -5
- package/agent/nextop-ui-system/SKILL.md +119 -8
- package/agent/nextop-ui-system/references/extract-base-component.md +50 -6
- package/agent/nextop-ui-system/references/maintain-inventory.md +5 -0
- package/agent/nextop-ui-system/references/promote-business-component.md +94 -208
- package/dist/chunk-GE5YVRTV.js +859 -0
- package/dist/chunk-GE5YVRTV.js.map +1 -0
- package/dist/chunk-KJQ366TA.js +70 -0
- package/dist/chunk-KJQ366TA.js.map +1 -0
- package/dist/chunk-LVHEV755.js +2553 -0
- package/dist/chunk-LVHEV755.js.map +1 -0
- package/dist/components/index.d.ts +162 -11
- package/dist/components/index.js +62 -2
- package/dist/date-format.d.ts +6 -0
- package/dist/date-format.js +11 -0
- package/dist/date-format.js.map +1 -0
- package/dist/dev-vite.js +12 -5
- package/dist/dev-vite.js.map +1 -1
- package/dist/icons/index.d.ts +90 -47
- package/dist/icons/index.js +91 -11
- package/dist/index.d.ts +3 -2
- package/dist/index.js +159 -11
- package/dist/metadata/components.json +1320 -265
- package/dist/metadata/components.schema.json +4 -0
- package/dist/metadata/index.d.ts +3 -1
- package/dist/metadata/index.js +1320 -265
- package/dist/metadata/index.js.map +1 -1
- package/dist/styles/base.css +85 -0
- package/dist/styles/index.css +1 -0
- package/dist/styles/semantic.css +7 -0
- package/dist/styles/theme.css +82 -1
- package/package.json +10 -3
- package/ui-system.md +640 -0
- package/UI_SYSTEM_GUIDELINES.md +0 -148
- package/dist/chunk-FT633NLJ.js +0 -1159
- package/dist/chunk-FT633NLJ.js.map +0 -1
- package/dist/chunk-NFSMZKML.js +0 -208
- package/dist/chunk-NFSMZKML.js.map +0 -1
package/ui-system.md
ADDED
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
# UI System
|
|
2
|
+
|
|
3
|
+
This document defines the responsibility boundary for the shared visual-system package under `packages/ui/*`.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The shared UI system package exists to hold product-facing visual foundations that are reused across renderer surfaces.
|
|
8
|
+
|
|
9
|
+
The current package is:
|
|
10
|
+
|
|
11
|
+
- `packages/ui/system`
|
|
12
|
+
|
|
13
|
+
It is organized into two public component layers:
|
|
14
|
+
|
|
15
|
+
- `base`
|
|
16
|
+
Basic visual primitives and foundations such as tokens, icons, Button,
|
|
17
|
+
Input, Dialog, Select, Card, Badge, and Toast.
|
|
18
|
+
- `business`
|
|
19
|
+
Multi-end reusable Nextop business display components. These components may
|
|
20
|
+
expose domain display props such as workspace, file, task, or agent state, and
|
|
21
|
+
should compose `base` primitives instead of recreating them.
|
|
22
|
+
|
|
23
|
+
The package owns:
|
|
24
|
+
|
|
25
|
+
- design tokens
|
|
26
|
+
- shared theme styles
|
|
27
|
+
- icon exports
|
|
28
|
+
- presentation primitives
|
|
29
|
+
- reusable business display components
|
|
30
|
+
|
|
31
|
+
It does not own:
|
|
32
|
+
|
|
33
|
+
- business logic or host-side side effects
|
|
34
|
+
- app-specific workflows
|
|
35
|
+
- daemon, Electron, router, or host adapter calls
|
|
36
|
+
|
|
37
|
+
## Current Package Role
|
|
38
|
+
|
|
39
|
+
`@nextop-os/ui-system` is the single source of truth for:
|
|
40
|
+
|
|
41
|
+
- CSS token definitions
|
|
42
|
+
- structural shared workbench CSS in `packages/workbench/surface/src/styles/workbench.css`
|
|
43
|
+
- structural shared terminal CSS in `packages/workspace/terminal/src/styles/terminal.css`
|
|
44
|
+
- Tailwind-facing semantic theme variables
|
|
45
|
+
- shared SVG and icon APIs
|
|
46
|
+
- shadcn-derived React primitives
|
|
47
|
+
|
|
48
|
+
Desktop renderer code should consume this package instead of defining a second token or primitive layer in `apps/desktop`.
|
|
49
|
+
|
|
50
|
+
The visual language that this package should serve is defined in [Desktop Visual Language](../../../docs/conventions/desktop-visual-language.md).
|
|
51
|
+
|
|
52
|
+
## Public API
|
|
53
|
+
|
|
54
|
+
`@nextop-os/ui-system` should expose a small, stable surface:
|
|
55
|
+
|
|
56
|
+
- `@nextop-os/ui-system`
|
|
57
|
+
Root runtime entry for shared primitives, icon components, and the small set of utility exports that primitives depend on
|
|
58
|
+
- `@nextop-os/ui-system/styles.css`
|
|
59
|
+
Shared stylesheet entry loaded by renderer shells
|
|
60
|
+
- `@nextop-os/ui-system/components`
|
|
61
|
+
Stable component barrel for tooling and rare category-focused imports
|
|
62
|
+
- `@nextop-os/ui-system/icons`
|
|
63
|
+
Stable icon barrel for tooling and rare category-focused imports
|
|
64
|
+
- `@nextop-os/ui-system/utils`
|
|
65
|
+
Stable utility entry for shadcn CLI integration and primitive support code
|
|
66
|
+
- `@nextop-os/ui-system/metadata`
|
|
67
|
+
Tooling entry for component metadata used by storyboard, dev server, and
|
|
68
|
+
agent skills
|
|
69
|
+
- `@nextop-os/ui-system/dev-vite`
|
|
70
|
+
Development-tooling entry for external Vite apps that opt into local source
|
|
71
|
+
sync; this is not a runtime component API
|
|
72
|
+
|
|
73
|
+
Default consumption rules:
|
|
74
|
+
|
|
75
|
+
- application code should prefer importing primitives and icons from `@nextop-os/ui-system`
|
|
76
|
+
- renderer entrypoints should import `@nextop-os/ui-system/styles.css` once
|
|
77
|
+
- shadcn monorepo aliases may target `@nextop-os/ui-system/components` and `@nextop-os/ui-system/utils`
|
|
78
|
+
- application runtime code must not import `@nextop-os/ui-system/dev-vite`
|
|
79
|
+
- consumers must not deep import `@nextop-os/ui-system/src/*` or component file
|
|
80
|
+
paths; use the root package and stable subpaths instead
|
|
81
|
+
|
|
82
|
+
## Automated Enforcement
|
|
83
|
+
|
|
84
|
+
The repository enforces the shared UI boundary with:
|
|
85
|
+
|
|
86
|
+
- `pnpm check:ui-boundaries`
|
|
87
|
+
- `pnpm check:ui-boundaries:staged`
|
|
88
|
+
|
|
89
|
+
Use the script output as the source of truth for the mechanically-checkable rules.
|
|
90
|
+
|
|
91
|
+
- `check:ui-boundaries:staged` is the fast local hook variant for staged files
|
|
92
|
+
- `check:ui-boundaries` is the full-repository variant for `pre-push` and CI
|
|
93
|
+
- `@nextop-os/workbench-surface/styles.css` and `@nextop-os/workspace-terminal/styles.css` are the only non-UI-system package stylesheets allowed by the boundary check; they must remain structural and variable-driven, not product-branded
|
|
94
|
+
|
|
95
|
+
Keep this check aligned with the package exports and file boundary rules:
|
|
96
|
+
|
|
97
|
+
- if a new stable public subpath is intentionally added, update both `packages/ui/system/package.json` and the import-check script
|
|
98
|
+
- do not "fix" the script by broadening the allowed list unless the package boundary itself is intentionally changing
|
|
99
|
+
|
|
100
|
+
## Metadata Rules
|
|
101
|
+
|
|
102
|
+
Every public UI-system metadata entry must have a stable `id`.
|
|
103
|
+
|
|
104
|
+
- `id` is the user-facing tooling identifier for storyboard anchors, dev server
|
|
105
|
+
metadata, and agent skill lookups
|
|
106
|
+
- `id` must be globally unique and use readable kebab-case, for example
|
|
107
|
+
`button`, `dialog-content`, `button-variants`, or `styles-css`
|
|
108
|
+
- `id` should not be derived at runtime by consumers; read it from
|
|
109
|
+
`@nextop-os/ui-system/metadata`
|
|
110
|
+
- `name` and `export` remain the TypeScript API identity, while `id` is the
|
|
111
|
+
stable human-readable inventory identity
|
|
112
|
+
- `layer` must be either `base` or `business`
|
|
113
|
+
- `business` components may use business display nouns in their props, but
|
|
114
|
+
should still receive data, labels, status, and callbacks from the consuming
|
|
115
|
+
host
|
|
116
|
+
|
|
117
|
+
## Component Promotion Protocol
|
|
118
|
+
|
|
119
|
+
Use this protocol when moving UI from an application or business package into
|
|
120
|
+
`@nextop-os/ui-system`.
|
|
121
|
+
|
|
122
|
+
The UI system follows a shadcn-like self-owned source model:
|
|
123
|
+
|
|
124
|
+
- Radix or equivalent headless primitives provide accessible interaction
|
|
125
|
+
behavior where a proven primitive exists
|
|
126
|
+
- CVA-style variant definitions keep component variants explicit and typed
|
|
127
|
+
- Tailwind classes consume shared semantic tokens instead of local palettes
|
|
128
|
+
- Nextop owns the checked-in source, public exports, metadata, storyboard
|
|
129
|
+
examples, and boundary validation
|
|
130
|
+
|
|
131
|
+
Before extracting a component, decide whether the target is `base`, `business`,
|
|
132
|
+
or not suitable for UI-system promotion.
|
|
133
|
+
|
|
134
|
+
Promote to `base` when the component is a foundation primitive:
|
|
135
|
+
|
|
136
|
+
- it has no product workflow or domain noun in its public contract
|
|
137
|
+
- props describe presentation, interaction, accessibility, variants, refs,
|
|
138
|
+
slots, class names, or children
|
|
139
|
+
- the component can be reused by unrelated surfaces without explaining a
|
|
140
|
+
business concept
|
|
141
|
+
- a known shadcn or Radix primitive can provide the starting point, or the
|
|
142
|
+
exception is documented in review
|
|
143
|
+
|
|
144
|
+
Promote to `business` when the component is a reusable business display unit:
|
|
145
|
+
|
|
146
|
+
- it represents a cross-surface Nextop concept such as workspace, file, task,
|
|
147
|
+
agent, run, project, or account display state
|
|
148
|
+
- it receives all business data, labels, statuses, permissions, and callbacks
|
|
149
|
+
from the host through props
|
|
150
|
+
- it receives user-visible copy through props, labels, or children instead of
|
|
151
|
+
introducing new hardcoded product strings inside the shared component body
|
|
152
|
+
- it composes `base` components for buttons, fields, dialogs, cards, icons, and
|
|
153
|
+
overlays instead of rebuilding those primitives locally
|
|
154
|
+
- it can be rendered in storyboard with controlled sample data and no daemon,
|
|
155
|
+
router, store, or host adapter
|
|
156
|
+
|
|
157
|
+
Do not promote a component when it owns:
|
|
158
|
+
|
|
159
|
+
- daemon, Electron, filesystem, router, or host-adapter calls
|
|
160
|
+
- data fetching, cache mutation, persistence, or polling
|
|
161
|
+
- global app store ownership
|
|
162
|
+
- workspace registration, navigation, onboarding, or other app workflow
|
|
163
|
+
orchestration
|
|
164
|
+
- product copy that cannot be supplied by props or children
|
|
165
|
+
|
|
166
|
+
For every promoted component, define this contract before editing code:
|
|
167
|
+
|
|
168
|
+
- component `id` and `layer`
|
|
169
|
+
- source usage being replaced
|
|
170
|
+
- intended reuse surfaces
|
|
171
|
+
- public props and callbacks
|
|
172
|
+
- host-owned state and side effects that remain outside the component
|
|
173
|
+
- stable export path
|
|
174
|
+
- metadata entry
|
|
175
|
+
- storyboard states and examples
|
|
176
|
+
- validation commands
|
|
177
|
+
|
|
178
|
+
The bundled `packages/ui/system/agent/nextop-ui-system/SKILL.md` skill is the
|
|
179
|
+
standard prompt-level workflow for this judgment-heavy promotion step. Boundary
|
|
180
|
+
scripts are still required, but they only catch mechanical violations.
|
|
181
|
+
|
|
182
|
+
## API Shape And Composition
|
|
183
|
+
|
|
184
|
+
The public API must be derived from the source state matrix and intended reuse
|
|
185
|
+
surfaces, not from every conditional branch in the original component.
|
|
186
|
+
|
|
187
|
+
- Avoid boolean prop proliferation for rendering modes. Standard UI booleans
|
|
188
|
+
such as `disabled`, `loading`, `selected`, `open`, `required`, or `invalid`
|
|
189
|
+
are acceptable when they represent real state. Mode switches such as `isFoo`,
|
|
190
|
+
`showBar`, and `withBaz` should usually become a finite variant,
|
|
191
|
+
discriminated union, explicit component variant, slot, or composed child.
|
|
192
|
+
- Use explicit variants when combinations would otherwise create impossible
|
|
193
|
+
states. Prefer a narrow discriminated union or named component variant over a
|
|
194
|
+
component that accepts unrelated mode booleans.
|
|
195
|
+
- Prefer `children` and named slots for caller-owned visual regions. Use render
|
|
196
|
+
props only when the shared component must pass data back to the caller.
|
|
197
|
+
- User-visible copy must stay caller-owned by default. Do not add hardcoded
|
|
198
|
+
product strings inside `@nextop-os/ui-system` components when the text can be
|
|
199
|
+
supplied by `labels`, `title`, `description`, `children`, or other explicit
|
|
200
|
+
props. If a legacy-compatible fallback string must exist temporarily, treat it
|
|
201
|
+
as compatibility debt rather than the preferred API pattern.
|
|
202
|
+
- Use compound components and context only when the component is complex enough
|
|
203
|
+
that consumers need to assemble subparts while sharing state. Do not add a
|
|
204
|
+
provider for a simple card, row, badge, or button wrapper.
|
|
205
|
+
- If shared state is necessary, make the context contract narrow and
|
|
206
|
+
injectable: `state`, `actions`, and `meta`. State implementation remains
|
|
207
|
+
outside the visual subcomponents, and host-owned daemon, Electron, router,
|
|
208
|
+
store, query, persistence, filesystem, or workflow behavior remains outside
|
|
209
|
+
the UI-system package.
|
|
210
|
+
- For new components, follow the repository's React 19 baseline, including
|
|
211
|
+
`ref` as a prop where a ref is part of the public contract. Do not rewrite
|
|
212
|
+
shadcn or Radix-acquired components only to chase API style parity.
|
|
213
|
+
|
|
214
|
+
Document the API shape decision before promotion: which state axes became
|
|
215
|
+
variants, props, slots, children, compound subcomponents, provider state, or
|
|
216
|
+
host-owned caller logic.
|
|
217
|
+
|
|
218
|
+
## Token Rules
|
|
219
|
+
|
|
220
|
+
- CSS variables are the source of truth for theme values
|
|
221
|
+
- Tailwind utilities should consume the same token layer rather than defining a parallel color system
|
|
222
|
+
- prefer semantic token names such as `background`, `foreground`, `primary`, `muted`, and `destructive` over raw palette leakage in public APIs
|
|
223
|
+
- keep nextop-specific token extensions additive and minimal
|
|
224
|
+
- Build primitives for a calm workbench shell, not for marketing-card
|
|
225
|
+
theatrics.
|
|
226
|
+
|
|
227
|
+
For cross-surface stacking, use shared semantic `z-index` tokens instead of
|
|
228
|
+
local magic numbers. Current global layer tokens live in
|
|
229
|
+
`packages/ui/system/src/styles/theme.css` and should be the default source of
|
|
230
|
+
truth for:
|
|
231
|
+
|
|
232
|
+
- workbench chrome overlays
|
|
233
|
+
- popovers and menus
|
|
234
|
+
- toasts
|
|
235
|
+
- full-panel overlays
|
|
236
|
+
- dialogs and their backdrops
|
|
237
|
+
|
|
238
|
+
When a surface needs a new global layer, add a responsibility-named token to the
|
|
239
|
+
shared theme rather than introducing another raw `z-[12345]` value in app code.
|
|
240
|
+
Small component-internal stacking such as `z-1` on a pseudo-element can stay
|
|
241
|
+
local when it does not participate in global overlay ordering.
|
|
242
|
+
|
|
243
|
+
### Z-Index Design Rules
|
|
244
|
+
|
|
245
|
+
Treat `z-index` as an ordering system, not as a per-component escape hatch.
|
|
246
|
+
|
|
247
|
+
Use these rules:
|
|
248
|
+
|
|
249
|
+
- use shared global tokens when a layer can overlap content owned by another component, feature, portal, or renderer surface
|
|
250
|
+
- use package-local or component-local variables when the layer only needs to order parts inside one isolated surface
|
|
251
|
+
- keep local decorative layering simple; values such as `0`, `1`, `2`, or `3` are acceptable when they never compete with global overlays
|
|
252
|
+
- do not introduce new raw high values such as `9999`, `10000`, or `z-[12345]` in app code
|
|
253
|
+
- prefer a new responsibility-named token over “one bigger number” when an existing global layer is not sufficient
|
|
254
|
+
|
|
255
|
+
Questions to ask before adding or changing a layer:
|
|
256
|
+
|
|
257
|
+
1. Can this element ever overlap a portal, popover, toast, dialog, or another feature-owned overlay?
|
|
258
|
+
2. Is this a global interaction layer or only internal ordering inside one component?
|
|
259
|
+
3. Would another engineer understand the layer’s purpose from its token name alone?
|
|
260
|
+
|
|
261
|
+
If the answer to the first question is yes, the layer should almost always use a
|
|
262
|
+
shared global token.
|
|
263
|
+
|
|
264
|
+
### Current Global Layers
|
|
265
|
+
|
|
266
|
+
The current shared global `z-index` tokens are:
|
|
267
|
+
|
|
268
|
+
- `--z-workbench-chrome`
|
|
269
|
+
Top or bottom workbench chrome rendered above window content but below global popovers and dialogs.
|
|
270
|
+
- `--z-workbench-genie`
|
|
271
|
+
The genie animation layer that must stay above ordinary workbench chrome.
|
|
272
|
+
- `--z-popover`
|
|
273
|
+
Cross-feature floating UI such as menus, switchers, and layout popovers.
|
|
274
|
+
- `--z-toast`
|
|
275
|
+
Toast notifications that should stay above popovers.
|
|
276
|
+
- `--z-panel`
|
|
277
|
+
Full-panel overlays such as workspace settings surfaces.
|
|
278
|
+
- `--z-panel-popover`
|
|
279
|
+
Popovers or menus that are portaled from within a full-panel overlay and must stay above the panel but below dialog backdrops.
|
|
280
|
+
- `--z-dialog-overlay`
|
|
281
|
+
Dialog backdrops that should dim or block panel surfaces beneath them.
|
|
282
|
+
- `--z-dialog`
|
|
283
|
+
Dialog content rendered above dialog backdrops.
|
|
284
|
+
|
|
285
|
+
These tokens are intentionally semantic and ordered by responsibility, not by
|
|
286
|
+
visual implementation detail.
|
|
287
|
+
|
|
288
|
+
### Local Layering Rules
|
|
289
|
+
|
|
290
|
+
Local layers should stay local when they only solve ordering inside one bounded
|
|
291
|
+
surface. Good examples:
|
|
292
|
+
|
|
293
|
+
- a selected tile border inside one settings grid
|
|
294
|
+
- a resize handle above adjacent pane content inside one file manager surface
|
|
295
|
+
- a tooltip above its own dock icon inside one workbench dock
|
|
296
|
+
- background decorations behind launcher content
|
|
297
|
+
|
|
298
|
+
For these cases:
|
|
299
|
+
|
|
300
|
+
- prefer package-local variables such as `--workbench-z-dock-tooltip` or `--workspace-file-manager-dialog-overlay-z-index`
|
|
301
|
+
- keep the numeric scale tight and relative to the owning surface
|
|
302
|
+
- do not promote a local layer into the global theme unless another surface needs to reason about it
|
|
303
|
+
|
|
304
|
+
### Migration Rules
|
|
305
|
+
|
|
306
|
+
When touching existing code:
|
|
307
|
+
|
|
308
|
+
- replace raw high `z-index` values with the nearest existing semantic token when the layer is globally meaningful
|
|
309
|
+
- if no existing token matches, add a new one in `packages/ui/system/src/styles/theme.css` and document it here
|
|
310
|
+
- if the layer is local-only, prefer a package or component variable instead of a new global token
|
|
311
|
+
- remove transitional duplicate declarations such as a Tailwind `z-*` class plus an overriding inline `style.zIndex`
|
|
312
|
+
|
|
313
|
+
## Reusable Package Styling Rules
|
|
314
|
+
|
|
315
|
+
Reusable packages outside `packages/ui/*` should not create their own visual
|
|
316
|
+
systems. They should consume `@nextop-os/ui-system` primitives, icons, tokens, and
|
|
317
|
+
token-backed Tailwind utilities as their default styling model.
|
|
318
|
+
|
|
319
|
+
For reusable packages that render Tailwind utility classes, consumers are
|
|
320
|
+
responsible for including the published package output in Tailwind source
|
|
321
|
+
scanning. For example, a consumer of `@nextop-os/workbench-surface` should include
|
|
322
|
+
that package's built output in its Tailwind entrypoint or equivalent build
|
|
323
|
+
configuration:
|
|
324
|
+
|
|
325
|
+
```css
|
|
326
|
+
@source "../node_modules/@nextop-os/workbench-surface/dist";
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Within this monorepo, reusable packages that require consumer Tailwind scanning
|
|
330
|
+
should declare that requirement in their `package.json`:
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"nextop": {
|
|
335
|
+
"tailwindSourceRoot": "src"
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
`pnpm check:ui-boundaries` validates that the desktop renderer entrypoint
|
|
341
|
+
`apps/desktop/src/renderer/src/style.css` includes matching `@source` directives
|
|
342
|
+
for any imported workspace package that declares `nextop.tailwindSourceRoot`.
|
|
343
|
+
It also validates that the path matches the declared source root and reports the
|
|
344
|
+
exact `@source` line to add or replace.
|
|
345
|
+
|
|
346
|
+
Tailwind source troubleshooting checklist:
|
|
347
|
+
|
|
348
|
+
- if a reusable package introduces new utility classes but the desktop UI does not change at runtime, confirm the package declares `nextop.tailwindSourceRoot` when it renders runtime Tailwind classes
|
|
349
|
+
- confirm the desktop renderer Tailwind entrypoint includes the package source path through `@source`
|
|
350
|
+
- re-run `pnpm check:ui-boundaries` before assuming the issue is a hot-reload or build-cache problem
|
|
351
|
+
|
|
352
|
+
Package-local CSS in reusable packages is an exception, not the default. Add it
|
|
353
|
+
only when the package needs selectors, keyframes, or structural behavior that is
|
|
354
|
+
awkward to express through UI system primitives and Tailwind utilities.
|
|
355
|
+
|
|
356
|
+
When package-local CSS is necessary:
|
|
357
|
+
|
|
358
|
+
- use UI system CSS variables or Tailwind-facing semantic tokens
|
|
359
|
+
- keep the CSS structural and package-responsibility-specific
|
|
360
|
+
- do not define raw palette values, a second token layer, or app-specific visual roles
|
|
361
|
+
- do not include product styling, product copy, or app-specific state concepts
|
|
362
|
+
- document the public stylesheet entrypoint in the package README and package release docs
|
|
363
|
+
|
|
364
|
+
If a reusable package repeatedly needs visual primitives or new semantic tokens,
|
|
365
|
+
prefer moving that foundation into `@nextop-os/ui-system` before adding more
|
|
366
|
+
package-local CSS.
|
|
367
|
+
|
|
368
|
+
## Design Foundation Compliance
|
|
369
|
+
|
|
370
|
+
All components promoted into `@nextop-os/ui-system` must fully follow the
|
|
371
|
+
existing design foundations owned by this package. Promotion must align with the
|
|
372
|
+
shared token model, theme variables, spacing rhythm, radius scale, typography,
|
|
373
|
+
surface language, focus states, disabled states, and the existing `base`
|
|
374
|
+
primitive vocabulary.
|
|
375
|
+
|
|
376
|
+
Do not introduce a second visual language during promotion. Avoid raw palette
|
|
377
|
+
values, ad hoc spacing scales, local radius conventions, duplicate button or
|
|
378
|
+
field treatments, or component-specific CSS that should be expressed through
|
|
379
|
+
existing tokens or primitives.
|
|
380
|
+
|
|
381
|
+
After a component is promoted, start an independent design-foundation review
|
|
382
|
+
subagent before reporting completion. Give the subagent the promoted component
|
|
383
|
+
files, source usage, selected states, storyboard entry, metadata entry, and this
|
|
384
|
+
document. The subagent should verify that the component follows the design
|
|
385
|
+
foundation and report any drift. If a subagent cannot be started in the current
|
|
386
|
+
environment, report the verification as blocked instead of claiming full
|
|
387
|
+
design-foundation compliance.
|
|
388
|
+
|
|
389
|
+
## Component Rules
|
|
390
|
+
|
|
391
|
+
- `base` primitives should stay low-level and presentation-focused
|
|
392
|
+
- `business` components may include reusable business display semantics, but
|
|
393
|
+
must stay host-agnostic and side-effect-free
|
|
394
|
+
- `packages/ui/system` is the repository's shared Radix and shadcn host package
|
|
395
|
+
- for primitives that exist in the shadcn registry, start from shadcn CLI output targeted at `packages/ui/system`; do not hand-author a fresh component body when the upstream primitive can be downloaded
|
|
396
|
+
- keep `packages/ui/system/components.json` healthy enough that `pnpm dlx shadcn@latest add <component> -c packages/ui/system` remains the default acquisition path
|
|
397
|
+
- treat CLI-generated source as the canonical starting point; repository-specific edits should stay narrow and mechanical, such as package import aliases, stable barrel exports, icon-layer routing, and token-backed class adjustments required by boundary checks
|
|
398
|
+
- if a desired primitive is not available from shadcn, prefer composing directly from `radix-ui` inside `packages/ui/system` and document that exception in the change review or follow-up docs
|
|
399
|
+
- if the current package structure makes CLI acquisition awkward, fix the host package structure or configuration first instead of silently replacing the workflow with a handwritten primitive
|
|
400
|
+
- keep primitive APIs close to upstream shadcn patterns unless product-specific constraints require a deviation
|
|
401
|
+
- do not place app-specific workflows such as launcher flows, workspace
|
|
402
|
+
registration, or route-owned panels in the shared package
|
|
403
|
+
- do not add new hardcoded user-visible copy inside `@nextop-os/ui-system`
|
|
404
|
+
components when the text can be supplied by props, `labels`, `title`,
|
|
405
|
+
`description`, or `children`; keep translation lookup and copy selection in
|
|
406
|
+
the caller
|
|
407
|
+
- export UI-system components through stable package barrels instead of exposing
|
|
408
|
+
per-file component paths
|
|
409
|
+
- only move a component into `@nextop-os/ui-system` when it is a real
|
|
410
|
+
visual-system primitive or reusable business display component with more than
|
|
411
|
+
one plausible consumer
|
|
412
|
+
- every public component, icon, utility, style entry, or tooling-visible UI
|
|
413
|
+
export must have metadata in
|
|
414
|
+
`packages/ui/system/src/metadata/components.json`
|
|
415
|
+
- metadata `source` paths must point at existing files under `packages/ui/system/src`
|
|
416
|
+
and `from` must use a stable public entrypoint
|
|
417
|
+
- run `node tools/scripts/check-ui-metadata.mjs` or
|
|
418
|
+
`pnpm check:ui-boundaries` after adding, removing, or renaming UI-system
|
|
419
|
+
public exports
|
|
420
|
+
|
|
421
|
+
### Primitive Sourcing Workflow
|
|
422
|
+
|
|
423
|
+
Use this workflow when adding or replacing a shared primitive:
|
|
424
|
+
|
|
425
|
+
1. Run shadcn CLI against `packages/ui/system` when the primitive exists in the registry.
|
|
426
|
+
2. Keep the downloaded component body as the baseline implementation.
|
|
427
|
+
3. Apply only the minimum package-specific adaptation required to satisfy repository rules.
|
|
428
|
+
4. Export the primitive through the stable `@nextop-os/ui-system` barrels.
|
|
429
|
+
5. Re-run `pnpm check:ui-boundaries` after the adaptation pass.
|
|
430
|
+
|
|
431
|
+
Repository-specific adaptation is allowed for:
|
|
432
|
+
|
|
433
|
+
- replacing direct third-party icon imports with `@nextop-os/ui-system` icon exports when the UI boundary check requires it
|
|
434
|
+
- switching import aliases to package-local `#components`, `#icons`, or `#lib` paths
|
|
435
|
+
- aligning classes with shared CSS tokens or other repository-owned boundary rules
|
|
436
|
+
|
|
437
|
+
Repository-specific adaptation is not a reason to skip CLI acquisition. The rule of thumb is:
|
|
438
|
+
|
|
439
|
+
- download first
|
|
440
|
+
- adapt second
|
|
441
|
+
- do not handwrite the primitive body from scratch unless there is no upstream shadcn primitive to start from
|
|
442
|
+
|
|
443
|
+
### Business Component Workflow
|
|
444
|
+
|
|
445
|
+
Use this workflow when promoting reusable business UI:
|
|
446
|
+
|
|
447
|
+
1. Read existing `@nextop-os/ui-system/metadata` and storyboard entries first.
|
|
448
|
+
2. Reuse existing `base` and `business` components when they cover the need.
|
|
449
|
+
3. Keep host state, side effects, data loading, routing, and daemon calls in the
|
|
450
|
+
original app or package.
|
|
451
|
+
4. Extract only the reusable display surface and typed callback contract.
|
|
452
|
+
5. Compose `base` primitives for controls, overlays, cards, icons, and layout
|
|
453
|
+
affordances.
|
|
454
|
+
6. Add metadata with `layer: "business"` and a readable stable `id`.
|
|
455
|
+
7. Add storyboard examples that cover empty, loading, normal, disabled, and
|
|
456
|
+
error-like display states when those states are part of the public contract.
|
|
457
|
+
8. Replace the original duplicated UI with a stable public import from
|
|
458
|
+
`@nextop-os/ui-system`.
|
|
459
|
+
|
|
460
|
+
Business components should look like controlled React components:
|
|
461
|
+
|
|
462
|
+
```tsx
|
|
463
|
+
<WorkspaceSummaryCard
|
|
464
|
+
workspace={workspace}
|
|
465
|
+
status={workspaceStatus}
|
|
466
|
+
disabled={!canOpenWorkspace}
|
|
467
|
+
onOpen={handleOpenWorkspace}
|
|
468
|
+
/>
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
They should not reach back into the host:
|
|
472
|
+
|
|
473
|
+
```tsx
|
|
474
|
+
useWorkspaceStore();
|
|
475
|
+
useNavigate();
|
|
476
|
+
invokeDaemon();
|
|
477
|
+
fetch("/api/workspaces");
|
|
478
|
+
localStorage.setItem("workspace", id);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Promotion Review Gate
|
|
482
|
+
|
|
483
|
+
Use this gate for every base or business component promotion. It adapts
|
|
484
|
+
frontend design review practice to Nextop's product standard; do not import
|
|
485
|
+
general marketing, portfolio, or decorative frontend heuristics into the shared
|
|
486
|
+
workbench system.
|
|
487
|
+
|
|
488
|
+
### Frictionless
|
|
489
|
+
|
|
490
|
+
- The migrated consumer preserves the original task path and interaction count
|
|
491
|
+
unless the user approved a behavior change.
|
|
492
|
+
- Primary, secondary, destructive, cancel, and recovery actions keep their
|
|
493
|
+
visual hierarchy and remain reachable by keyboard.
|
|
494
|
+
- The shared component does not introduce dead ends, hidden required steps, or
|
|
495
|
+
new caller-owned state requirements.
|
|
496
|
+
|
|
497
|
+
### Quality Craft
|
|
498
|
+
|
|
499
|
+
- Selected states have before/after visual parity evidence from the source
|
|
500
|
+
route, storyboard, or smallest reproducible view.
|
|
501
|
+
- The implementation uses existing `@nextop-os/ui-system` primitives and
|
|
502
|
+
canonical tokens before adding component-local CSS.
|
|
503
|
+
- Light, dark, focus-visible, hover, disabled, invalid, selected, loading, and
|
|
504
|
+
reduced-motion states are covered when they exist in the public contract.
|
|
505
|
+
- Layout, density, spacing, radius, typography, icon sizing, border, color,
|
|
506
|
+
opacity, shadow, and responsive behavior have no unapproved drift.
|
|
507
|
+
- Any intentional delta is named, justified, and reported as approved rather
|
|
508
|
+
than hidden inside the promotion.
|
|
509
|
+
|
|
510
|
+
### Trustworthy
|
|
511
|
+
|
|
512
|
+
- Loading, empty, disabled, error-like, and permission-limited states keep clear
|
|
513
|
+
labels and actionable recovery where the source had them.
|
|
514
|
+
- AI-generated or inferred content keeps provenance, confidence, or disclaimer
|
|
515
|
+
treatment host-owned and visible when applicable.
|
|
516
|
+
- Error copy and status labels are supplied by the caller or labels props; the
|
|
517
|
+
shared component must not invent product policy or workflow meaning.
|
|
518
|
+
- New user-visible copy should enter the component boundary through props,
|
|
519
|
+
labels, or children rather than new hardcoded strings inside the shared
|
|
520
|
+
component.
|
|
521
|
+
- The component boundary leaves daemon, Electron, router, persistence, query,
|
|
522
|
+
filesystem, polling, i18n lookup, and workflow orchestration in the host.
|
|
523
|
+
|
|
524
|
+
Report the promotion review in this structure:
|
|
525
|
+
|
|
526
|
+
- context: component id/layer, source usage, user task, selected states, and
|
|
527
|
+
intended reuse surfaces
|
|
528
|
+
- summary: pass, needs work, or blocked
|
|
529
|
+
- pillar assessment: Frictionless, Quality craft, Trustworthy
|
|
530
|
+
- design-system compliance: tokens, primitives, metadata, storyboard, stable
|
|
531
|
+
exports, and public imports
|
|
532
|
+
- issues: blocking, major, and minor, with concrete file references
|
|
533
|
+
- validation: exact commands and results
|
|
534
|
+
- risks: uncovered states, unavailable visual evidence, unresolved subagent
|
|
535
|
+
review, or approved visual deltas.
|
|
536
|
+
|
|
537
|
+
## Agent Skill Rules
|
|
538
|
+
|
|
539
|
+
Use the bundled `nextop-ui-system` skill for prompt-level work involving
|
|
540
|
+
`@nextop-os/ui-system`. The source lives under
|
|
541
|
+
`packages/ui/system/agent/nextop-ui-system/SKILL.md` so it can ship with the UI
|
|
542
|
+
system package.
|
|
543
|
+
|
|
544
|
+
The skill should route internally across these scenarios:
|
|
545
|
+
|
|
546
|
+
- use an existing UI-system component
|
|
547
|
+
- extract a new `base` component
|
|
548
|
+
- extract a new `business` component
|
|
549
|
+
- maintain metadata, ids, exports, or storyboard coverage
|
|
550
|
+
|
|
551
|
+
The skill must treat this document, `packages/ui/AGENTS.md`, metadata, and
|
|
552
|
+
boundary scripts as the source of truth. It should not duplicate long copies of
|
|
553
|
+
the rules; it should point the coding agent to the right files, force the
|
|
554
|
+
base/business decision, and require validation.
|
|
555
|
+
|
|
556
|
+
External business repositories that promote UI into the shared system should add
|
|
557
|
+
a short agent instruction such as:
|
|
558
|
+
|
|
559
|
+
```md
|
|
560
|
+
When promoting business UI into @nextop-os/ui-system, use the
|
|
561
|
+
nextop-ui-system skill and follow packages/ui/system/ui-system.md.
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
After installing `@nextop-os/ui-system`, external repositories can configure the
|
|
565
|
+
bundled skill with:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
pnpm exec nextop-ui-system-install-skill
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
The command copies the bundled skill into `.codex/skills/nextop-ui-system` in
|
|
572
|
+
the current repository. When `.nextop-ui-system-dev/` is present, the installer
|
|
573
|
+
prefers the synced source checkout so the skill and bundled UI-system rules stay
|
|
574
|
+
aligned with the current local UI-system source. It refuses to overwrite local
|
|
575
|
+
changes unless run with `--force`.
|
|
576
|
+
|
|
577
|
+
## Storyboard Rules
|
|
578
|
+
|
|
579
|
+
`apps/ui-storyboard` is the local component inventory and example surface for
|
|
580
|
+
`@nextop-os/ui-system`.
|
|
581
|
+
|
|
582
|
+
- the component list, categories, statuses, and inventory counts must come from
|
|
583
|
+
`@nextop-os/ui-system/metadata`
|
|
584
|
+
- storyboard navigation should group component stories by `layer`
|
|
585
|
+
- examples may stay hand-written so they can show realistic composition and
|
|
586
|
+
edge states
|
|
587
|
+
- visible component stories should display the component `id` prominently and
|
|
588
|
+
support copying it from the UI
|
|
589
|
+
- the storyboard should import public UI-system entrypoints and metadata, not
|
|
590
|
+
private component file paths
|
|
591
|
+
- keep the storyboard as a development surface; it should not become a product
|
|
592
|
+
shell or marketing page
|
|
593
|
+
|
|
594
|
+
## External Dev Server Rules
|
|
595
|
+
|
|
596
|
+
The UI-system dev server exists only for local external development. It lets an
|
|
597
|
+
external app keep normal `@nextop-os/ui-system` imports while temporarily resolving
|
|
598
|
+
the stable entrypoints to a generated local cache.
|
|
599
|
+
|
|
600
|
+
- start it with `pnpm --filter @nextop-os/ui-system dev:server`
|
|
601
|
+
- external Vite apps opt in with `nextopUISystemDev` from
|
|
602
|
+
`@nextop-os/ui-system/dev-vite`
|
|
603
|
+
- when the server is unavailable, external apps must fall back to their
|
|
604
|
+
installed package in `node_modules`
|
|
605
|
+
- the generated `.nextop-ui-system-dev/` cache belongs in the external app's
|
|
606
|
+
`.gitignore`
|
|
607
|
+
- Tailwind consumers must include both the installed package output and the
|
|
608
|
+
generated dev cache in source scanning, for example
|
|
609
|
+
`@source "../node_modules/@nextop-os/ui-system/dist";` and
|
|
610
|
+
`@source "../.nextop-ui-system-dev";`
|
|
611
|
+
- do not make CI, production builds, or package publishing depend on the dev
|
|
612
|
+
server
|
|
613
|
+
- `@nextop-os/ui-system/dev-vite` may be imported only from bundler config or
|
|
614
|
+
tooling files
|
|
615
|
+
|
|
616
|
+
## Icon Rules
|
|
617
|
+
|
|
618
|
+
- renderer-visible icons should be exported through the root package or the stable `@nextop-os/ui-system/icons` barrel
|
|
619
|
+
- generic system icons may wrap a third-party icon set
|
|
620
|
+
- product marks and custom status glyphs should live as local SVG components in the package
|
|
621
|
+
- icons should default to `currentColor` unless a specific token-driven treatment is required
|
|
622
|
+
|
|
623
|
+
## Review Heuristics
|
|
624
|
+
|
|
625
|
+
When reviewing a change under `packages/ui/*`, prefer these checks:
|
|
626
|
+
|
|
627
|
+
- is the new export responsibility-named and stable enough to support for a while
|
|
628
|
+
- could a consumer do the same work through the root package instead of a new subpath
|
|
629
|
+
- is a proposed helper really part of primitive support, or should it stay local to one app
|
|
630
|
+
- does the change make the package easier to consume without exposing its folder layout
|
|
631
|
+
|
|
632
|
+
## Review Questions
|
|
633
|
+
|
|
634
|
+
When reviewing a change under `packages/ui/*`, ask:
|
|
635
|
+
|
|
636
|
+
1. Is this a visual-system concern or an app-specific component?
|
|
637
|
+
2. Does this change preserve CSS tokens as the source of truth?
|
|
638
|
+
3. Is the package API still narrow, stable, and responsibility-named?
|
|
639
|
+
4. Would this be clearer if it stayed local to one app instead?
|
|
640
|
+
5. Are consumers being nudged toward the root package and stable barrels instead of internal paths?
|