@vegastack/design 0.7.99 → 0.7.101
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/bin/verify-registry-item.mjs +24 -0
- package/dist/{chunk-4YF3V35A.js → chunk-R6IZOGT5.js} +24 -6
- package/dist/create-animated-icon.js +1 -1
- package/dist/icons/index.js +1 -1
- package/dist/index.cjs +24 -6
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/skills/vegastack-consume/SKILL.md +52 -50
- package/skills/vegastack-design-audit/SKILL.md +1 -1
- package/skills/vegastack-design-system/SKILL.md +14 -12
- package/skills/vegastack-design-system/references/components.md +5 -1
|
@@ -64,6 +64,7 @@ import {
|
|
|
64
64
|
writeFileSync,
|
|
65
65
|
} from "node:fs";
|
|
66
66
|
import { tmpdir } from "node:os";
|
|
67
|
+
import { parseEnv } from "node:util";
|
|
67
68
|
import { pathToFileURL } from "node:url";
|
|
68
69
|
import { join, resolve, basename, relative, isAbsolute, sep } from "node:path";
|
|
69
70
|
import { createHash } from "node:crypto";
|
|
@@ -71,6 +72,28 @@ import tsconfigPaths from "tsconfig-paths";
|
|
|
71
72
|
|
|
72
73
|
const { loadConfig: loadTsconfigPaths } = tsconfigPaths;
|
|
73
74
|
|
|
75
|
+
/** Load credential values only; checkout-local files never control trust anchors. */
|
|
76
|
+
export function loadRegistryCredentials(cwd, env = process.env) {
|
|
77
|
+
const loaded = {};
|
|
78
|
+
for (const name of [".env", ".env.local"]) {
|
|
79
|
+
let contents;
|
|
80
|
+
try {
|
|
81
|
+
contents = readFileSync(join(cwd, name), "utf8");
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error.code === "ENOENT") continue;
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
const values = parseEnv(contents);
|
|
87
|
+
for (const key of ["CF_ACCESS_CLIENT_ID", "CF_ACCESS_CLIENT_SECRET"]) {
|
|
88
|
+
if (values[key] !== undefined) loaded[key] = values[key];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const [key, value] of Object.entries(loaded)) {
|
|
92
|
+
if (env[key] === undefined) env[key] = value;
|
|
93
|
+
}
|
|
94
|
+
return env;
|
|
95
|
+
}
|
|
96
|
+
|
|
74
97
|
// ── canonical item hash (INLINED from tooling/registry-hash.mjs — keep IDENTICAL) ──
|
|
75
98
|
// Covers the WHOLE item (not just file content); meta.integrity is excluded so the hash
|
|
76
99
|
// is self-describing and stable. The provenance header (`// @vegastack <name>@<version>
|
|
@@ -947,6 +970,7 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
|
947
970
|
/\/$/,
|
|
948
971
|
"",
|
|
949
972
|
);
|
|
973
|
+
loadRegistryCredentials(process.cwd());
|
|
950
974
|
const headers = requestHeaders();
|
|
951
975
|
|
|
952
976
|
const tempDir = mkdtempSync(join(tmpdir(), "vegastack-verify-"));
|
|
@@ -134,14 +134,32 @@ var FLOATING = {
|
|
|
134
134
|
};
|
|
135
135
|
function mergeRefs(...refs) {
|
|
136
136
|
return (node) => {
|
|
137
|
-
|
|
138
|
-
if (ref == null)
|
|
137
|
+
const cleanups = refs.map((ref) => {
|
|
138
|
+
if (ref == null) return void 0;
|
|
139
139
|
if (typeof ref === "function") {
|
|
140
|
-
ref(node);
|
|
141
|
-
|
|
142
|
-
ref.current = node;
|
|
140
|
+
const cleanup = ref(node);
|
|
141
|
+
return typeof cleanup === "function" ? cleanup : () => ref(null);
|
|
143
142
|
}
|
|
144
|
-
|
|
143
|
+
ref.current = node;
|
|
144
|
+
return () => {
|
|
145
|
+
ref.current = null;
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
let cleaned = false;
|
|
149
|
+
return () => {
|
|
150
|
+
if (cleaned) return;
|
|
151
|
+
cleaned = true;
|
|
152
|
+
const errors = [];
|
|
153
|
+
for (const cleanup of cleanups) {
|
|
154
|
+
try {
|
|
155
|
+
cleanup?.();
|
|
156
|
+
} catch (error) {
|
|
157
|
+
errors.push(error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (errors.length)
|
|
161
|
+
throw new AggregateError(errors, "Merged ref cleanup failed");
|
|
162
|
+
};
|
|
145
163
|
};
|
|
146
164
|
}
|
|
147
165
|
|
package/dist/icons/index.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -163,14 +163,32 @@ var FLOATING = {
|
|
|
163
163
|
};
|
|
164
164
|
function mergeRefs(...refs) {
|
|
165
165
|
return (node) => {
|
|
166
|
-
|
|
167
|
-
if (ref == null)
|
|
166
|
+
const cleanups = refs.map((ref) => {
|
|
167
|
+
if (ref == null) return void 0;
|
|
168
168
|
if (typeof ref === "function") {
|
|
169
|
-
ref(node);
|
|
170
|
-
|
|
171
|
-
ref.current = node;
|
|
169
|
+
const cleanup = ref(node);
|
|
170
|
+
return typeof cleanup === "function" ? cleanup : () => ref(null);
|
|
172
171
|
}
|
|
173
|
-
|
|
172
|
+
ref.current = node;
|
|
173
|
+
return () => {
|
|
174
|
+
ref.current = null;
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
let cleaned = false;
|
|
178
|
+
return () => {
|
|
179
|
+
if (cleaned) return;
|
|
180
|
+
cleaned = true;
|
|
181
|
+
const errors = [];
|
|
182
|
+
for (const cleanup of cleanups) {
|
|
183
|
+
try {
|
|
184
|
+
cleanup?.();
|
|
185
|
+
} catch (error) {
|
|
186
|
+
errors.push(error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (errors.length)
|
|
190
|
+
throw new AggregateError(errors, "Merged ref cleanup failed");
|
|
191
|
+
};
|
|
174
192
|
};
|
|
175
193
|
}
|
|
176
194
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -193,7 +193,7 @@ declare const FLOATING: {
|
|
|
193
193
|
* (only ref objects the caller already holds), so it is server-safe like `cn`.
|
|
194
194
|
*
|
|
195
195
|
* **Not memoized.** Calling it produces a NEW function every time, and React detaches a
|
|
196
|
-
* changed ref callback (
|
|
196
|
+
* changed ref callback (runs its cleanup; legacy callbacks receive `null`) and reattaches it on every render. Wrap the
|
|
197
197
|
* CALL at the call site when the inputs are stable:
|
|
198
198
|
*
|
|
199
199
|
* @example
|
package/dist/index.d.ts
CHANGED
|
@@ -193,7 +193,7 @@ declare const FLOATING: {
|
|
|
193
193
|
* (only ref objects the caller already holds), so it is server-safe like `cn`.
|
|
194
194
|
*
|
|
195
195
|
* **Not memoized.** Calling it produces a NEW function every time, and React detaches a
|
|
196
|
-
* changed ref callback (
|
|
196
|
+
* changed ref callback (runs its cleanup; legacy callbacks receive `null`) and reattaches it on every render. Wrap the
|
|
197
197
|
* CALL at the call site when the inputs are stable:
|
|
198
198
|
*
|
|
199
199
|
* @example
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vegastack/design",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.101",
|
|
4
4
|
"description": "VegaStack design system — cn utility, icon runtime, Tailwind v4 preset, and the vegastack-design CLI (tokens ship separately as @vegastack/design-tokens)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"lint": "eslint . && node ../../tooling/design-lint.mjs src && pnpm run verify",
|
|
68
68
|
"verify": "node ../../tooling/verify-preset-source.mjs",
|
|
69
69
|
"typecheck": "tsc --noEmit",
|
|
70
|
-
"test": "node test/compare.test.mjs && node test/check-updates.test.mjs && node test/skills-install.test.mjs && node test/doctor.test.mjs"
|
|
70
|
+
"test": "node test/compare.test.mjs && node test/check-updates.test.mjs && node test/skills-install.test.mjs && node test/doctor.test.mjs && node --test test/merge-refs.test.mjs test/registry-credentials.test.mjs"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@vegastack/design-tokens": "^0.7.80",
|
|
@@ -18,15 +18,16 @@ the icon runtime, the Tailwind v4 preset, and the `vegastack-design` CLI.
|
|
|
18
18
|
|
|
19
19
|
**Invoking the CLI.** The bin is `vegastack-design` but the package is `@vegastack/design`, so a
|
|
20
20
|
bare `npx vegastack-design …` in a project that does not already have it installed would try to
|
|
21
|
-
fetch an unrelated, unscoped package from npm. Use
|
|
22
|
-
|
|
23
|
-
standalone one-off:
|
|
21
|
+
fetch an unrelated, unscoped package from npm. Use the installed CLI so the project's lockfile
|
|
22
|
+
selects its approved version:
|
|
24
23
|
|
|
25
24
|
```bash
|
|
26
25
|
pnpm exec vegastack-design <command>
|
|
27
|
-
npx --package=@vegastack/design vegastack-design <command>
|
|
28
26
|
```
|
|
29
27
|
|
|
28
|
+
A standalone invocation must explicitly pin an approved `@vegastack/design` version; do not
|
|
29
|
+
resolve a different verifier version during the preflight/install/post-write sequence.
|
|
30
|
+
|
|
30
31
|
`@vegastack/ui` is private and is never installed downstream — components arrive by copy-in.
|
|
31
32
|
|
|
32
33
|
## 2. Tailwind v4 CSS
|
|
@@ -52,12 +53,51 @@ inside the published builds — the icon runtime, and the compiled Toaster (its
|
|
|
52
53
|
and status-tint classes live in `@vegastack/ui/dist`, and Tailwind has to be told to generate them).
|
|
53
54
|
Your own application and component source is scanned by Tailwind as usual.
|
|
54
55
|
|
|
55
|
-
## 3.
|
|
56
|
+
## 3. Configure registry access
|
|
57
|
+
|
|
58
|
+
Components come from a private registry behind Cloudflare Access service tokens.
|
|
59
|
+
|
|
60
|
+
If the project has no `components.json` yet, create one first — pick the **base** style, never
|
|
61
|
+
`radix`, because VegaStack components are Base UI:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pnpm dlx shadcn@4.21.0 init --base base
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then add the registry block to `components.json`:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"registries": {
|
|
72
|
+
"@vegastack": {
|
|
73
|
+
"url": "https://design.vegastack.com/r/{name}.json",
|
|
74
|
+
"headers": {
|
|
75
|
+
"CF-Access-Client-Id": "${CF_ACCESS_CLIENT_ID}",
|
|
76
|
+
"CF-Access-Client-Secret": "${CF_ACCESS_CLIENT_SECRET}"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Store credentials in your secret manager or ignored `.env.local`. The installed verifier loads
|
|
84
|
+
only `CF_ACCESS_CLIENT_ID` and `CF_ACCESS_CLIENT_SECRET` from `.env` then `.env.local`; an existing
|
|
85
|
+
process value wins. Registry origin and signer overrides are accepted only from the operator's
|
|
86
|
+
process environment, never a checkout-local dotenv file. Do not source the whole dotenv file.
|
|
87
|
+
|
|
88
|
+
Configure this registry before the first provider/component add. Inspect before writing anything:
|
|
56
89
|
|
|
57
90
|
```bash
|
|
58
|
-
|
|
91
|
+
pnpm dlx shadcn@4.21.0 add @vegastack/button --dry-run
|
|
59
92
|
```
|
|
60
93
|
|
|
94
|
+
External and client projects are **tokenless** — components are copied in during development, and
|
|
95
|
+
the shipped application holds zero VegaStack credentials.
|
|
96
|
+
|
|
97
|
+
## 4. Wrap the app root in the provider
|
|
98
|
+
|
|
99
|
+
Install `provider` using the complete fail-closed procedure in step 5 before adding this wrapper.
|
|
100
|
+
|
|
61
101
|
This copies `VegaStackProvider` and `useVegaStackTheme` into your project, composing the `toast`
|
|
62
102
|
Toaster item.
|
|
63
103
|
|
|
@@ -80,8 +120,7 @@ putting `className="isolate"` on your `<body>` yourself. Without it, popups can
|
|
|
80
120
|
chrome or mis-position in stacking-context-heavy layouts.
|
|
81
121
|
|
|
82
122
|
**Stop iOS focus zoom.** iOS Safari zooms into any focused field under 16px. `base.css` renders
|
|
83
|
-
text-entry controls at 16px on touch pointers
|
|
84
|
-
stops focus zoom while pinch-zoom keeps working on iOS. In Next.js, in the root layout:
|
|
123
|
+
text-entry controls at 16px on touch pointers. Keep zoom unrestricted; do not add a maximum-scale cap. In Next.js, in the root layout:
|
|
85
124
|
|
|
86
125
|
```tsx
|
|
87
126
|
import type { Viewport } from "next";
|
|
@@ -89,46 +128,9 @@ import type { Viewport } from "next";
|
|
|
89
128
|
export const viewport: Viewport = {
|
|
90
129
|
width: "device-width",
|
|
91
130
|
initialScale: 1,
|
|
92
|
-
maximumScale: 1,
|
|
93
131
|
};
|
|
94
132
|
```
|
|
95
133
|
|
|
96
|
-
## 4. Configure registry access
|
|
97
|
-
|
|
98
|
-
Components come from a private registry behind Cloudflare Access service tokens.
|
|
99
|
-
|
|
100
|
-
If the project has no `components.json` yet, create one first — pick the **base** style, never
|
|
101
|
-
`radix`, because VegaStack components are Base UI:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
pnpm dlx shadcn@latest init --base base
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Then add the registry block to `components.json`:
|
|
108
|
-
|
|
109
|
-
```json
|
|
110
|
-
{
|
|
111
|
-
"registries": {
|
|
112
|
-
"@vegastack": {
|
|
113
|
-
"url": "https://design.vegastack.com/r/{name}.json",
|
|
114
|
-
"headers": {
|
|
115
|
-
"CF-Access-Client-Id": "${CF_ACCESS_CLIENT_ID}",
|
|
116
|
-
"CF-Access-Client-Secret": "${CF_ACCESS_CLIENT_SECRET}"
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Put the credentials in `.env.local`. Inspect before writing anything:
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
pnpm dlx shadcn@latest add @vegastack/button --dry-run
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
External and client projects are **tokenless** — components are copied in during development, and
|
|
130
|
-
the shipped application holds zero VegaStack credentials.
|
|
131
|
-
|
|
132
134
|
## 5. Add a component (fail-closed, three steps)
|
|
133
135
|
|
|
134
136
|
This is the only safe way to add a component. Do not collapse it to a bare `shadcn add`: that leaves
|
|
@@ -140,16 +142,16 @@ a time-of-check/time-of-use gap, because `shadcn add` re-fetches the item after
|
|
|
140
142
|
DIR="$(mktemp -d "${TMPDIR:-/tmp}/vegastack-verify.XXXXXX")"; ITEM="$DIR/button.json"
|
|
141
143
|
|
|
142
144
|
# 1) PRE-WRITE — verify signature + hash, and save the trusted bytes for step 3.
|
|
143
|
-
|
|
145
|
+
pnpm exec vegastack-design verify --save "$ITEM" button
|
|
144
146
|
|
|
145
147
|
# Retain the verified digest in the parent shell BEFORE shadcn or dependency code runs.
|
|
146
148
|
EXPECTED="$(node -e 'process.stdout.write(JSON.parse(require("node:fs").readFileSync(process.argv[1],"utf8")).meta.integrity)' "$ITEM")"
|
|
147
149
|
|
|
148
150
|
# 2) COPY-IN — shadcn writes the files, rewriting import aliases per your components.json.
|
|
149
|
-
pnpm dlx shadcn@
|
|
151
|
+
pnpm dlx shadcn@4.21.0 add @vegastack/button
|
|
150
152
|
|
|
151
153
|
# 3) POST-WRITE — prove the copied files match the SAVED item. Exits 1 on any tampering.
|
|
152
|
-
|
|
154
|
+
pnpm exec vegastack-design verify \
|
|
153
155
|
--post-write --item "$ITEM" --expected-integrity "$EXPECTED" --target-dir .
|
|
154
156
|
```
|
|
155
157
|
|
|
@@ -176,7 +178,7 @@ Never override a `--color-*` variable — that is the build-inlined bridge, not
|
|
|
176
178
|
Copy-in means no automatic updates.
|
|
177
179
|
|
|
178
180
|
```bash
|
|
179
|
-
|
|
181
|
+
pnpm exec vegastack-design check-updates # ⬆ update · ≈ drift · ✓ up to date
|
|
180
182
|
```
|
|
181
183
|
|
|
182
184
|
Then per stale component: `shadcn add @vegastack/<name> --diff` to review, `--overwrite` to apply,
|
|
@@ -190,7 +192,7 @@ Add the Renovate preset `github>VegaStack/renovate-config` so additive token bum
|
|
|
190
192
|
If this project's agents do not already have them:
|
|
191
193
|
|
|
192
194
|
```bash
|
|
193
|
-
|
|
195
|
+
pnpm exec vegastack-design skills install
|
|
194
196
|
```
|
|
195
197
|
|
|
196
198
|
Writes the public VegaStack skills into `.claude/skills/` and `.agents/skills/`.
|
|
@@ -83,7 +83,7 @@ rg -n '(focus|focus-visible|focus-within|data-focused|popup-open)[^ "]*:border-(
|
|
|
83
83
|
- **a focus border** — a border colour under a focus or open-popup variant (`focus:border-ring/70`,
|
|
84
84
|
`focus-within:border-ring`, `data-popup-open:border-input`, `not-focus:aria-invalid:…`) in app
|
|
85
85
|
code. A border never changes colour on focus or while active (FOC-14) — the DS's own text-entry
|
|
86
|
-
components are the one exception (a subtle `border-ring/
|
|
86
|
+
components are the one exception (a subtle `border-ring/50` focus border, no fill) and carry it
|
|
87
87
|
themselves; only an invalid field's destructive border shows, in every state. **error**
|
|
88
88
|
- a status FILL used as a text ink on that family's own tint — `bg-destructive/10 text-destructive`
|
|
89
89
|
measures 3.98:1. The readable half is `text-destructive-text`. **error**
|
|
@@ -79,12 +79,10 @@ link` (upstream's set, verbatim). `destructive` is a soft tint, not a solid red
|
|
|
79
79
|
need a search field (it is the preset `country-select` and `region-select` are built from — reach
|
|
80
80
|
for it before composing `combobox` by hand); **`combobox`** directly only for free text,
|
|
81
81
|
suggestions or multi-select chips.
|
|
82
|
-
- **One view-switch rule.**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
**`tabs`**; moving between URLs is navigation — links, not `tabs` (a route-tabs recipe is
|
|
87
|
-
not shipped yet).
|
|
82
|
+
- **One view-switch rule.** Form choices use `radio-group`; toolbar scope switches use default
|
|
83
|
+
`Tabs`, and Grid | List | Board uses `ViewToggle` (which composes Tabs). `ToggleGroup` is for
|
|
84
|
+
pressed action/value choices. URL navigation uses links and the route-tabs recipe.
|
|
85
|
+
|
|
88
86
|
- **Empty is tiered** — nothing yet, no matches ("Clear filters"), couldn't load (`role="alert"`,
|
|
89
87
|
"Try again"), blocked. Pick the tier from the empty-state foundation
|
|
90
88
|
(<https://design.vegastack.com/docs/foundations/empty-states>); never leave a region blank.
|
|
@@ -243,7 +241,7 @@ on shadcn's `neutral` base. Always use the utility, never a raw value.
|
|
|
243
241
|
| Fill | `bg-primary` (solid action, every checked control) · `bg-secondary` (soft) · `bg-muted` (well, track, skeleton) · `bg-accent` (hover) |
|
|
244
242
|
| Text | `text-foreground` · `text-muted-foreground` · `text-{primary,secondary,accent,card,popover}-foreground` |
|
|
245
243
|
| Status | `bg-{destructive,success,warning,info}` · `-foreground` (ink ON the fill) · `-text` (ink on the page or on the family's own tint) |
|
|
246
|
-
| Border | `border-border` · `border-input` — there are no rings; focus is a global background tint (Tabs
|
|
244
|
+
| Border | `border-border` · `border-input` — there are no rings; focus is a global background tint (Tabs included) |
|
|
247
245
|
| Radius | `rounded-{sm,md,lg,xl,2xl}` — all derived from the single `--radius` |
|
|
248
246
|
| Type | Tailwind's own `text-{xs…7xl}`. `text-sm` is 14px, `text-base` is 16px. Line-height and letter-spacing above `text-base` come from the theme — never write `tracking-*`, an arbitrary `text-[13px]`, or `uppercase` |
|
|
249
247
|
| Font | `font-sans` `font-mono` `font-serif` `font-heading` |
|
|
@@ -290,10 +288,10 @@ heading down `n` levels (capped at h6) so the outline never skips back up.
|
|
|
290
288
|
|
|
291
289
|
**Markdown is read with `MarkdownView` and edited with `TextEdit format="markdown"`** — never a
|
|
292
290
|
textarea, a local prose class, or a hand-rolled editor. For in-place editing, swap the view for
|
|
293
|
-
`<TextEdit format="markdown" variant="
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
291
|
+
`<TextEdit format="markdown" variant="document" onCommit={save} />` for inline document editing.
|
|
292
|
+
Use `variant="boxed"` inside forms and `variant="composer"` for submission composers.
|
|
293
|
+
Commit through `onCommit`; `onRevert` observes a revert. The editor owns its contextual toolbar;
|
|
294
|
+
there are no `toolbar`, `onSave` or `onCancel` props.
|
|
297
295
|
|
|
298
296
|
`muted`, `accent` and `secondary` share one value in this base, and all three are kept: name the one
|
|
299
297
|
whose ROLE you mean, so a consumer can retune one without moving the others.
|
|
@@ -357,7 +355,11 @@ contract.
|
|
|
357
355
|
players); schedules → `formatDateRange` / `formatTimeOfDay`; feed/inbox headers → `groupByDay`.
|
|
358
356
|
Pass the viewer's `timeZone` (the `tz` cookie via `getTimeZone(cookie, orgZone)` on the server,
|
|
359
357
|
`useTimeZone()` on the client; render `TimeZoneScript` + `TimeZoneProvider` once at the root).
|
|
360
|
-
|
|
358
|
+
Pass a serialized request timestamp as `TimeZoneProvider.referenceNow`; groups, due badges and
|
|
359
|
+
picker presets read the same live epoch clock via `useDateTimeNow()` from `relative-time`.
|
|
360
|
+
`DateTime` and `DueLabel` share it too; explicit `DateTime.options.now` stays controlled.
|
|
361
|
+
It is `undefined` during SSR without that reference; render affected date content pending rather
|
|
362
|
+
than reading a separate render-time clock. API/JSON output stays ISO.
|
|
361
363
|
|
|
362
364
|
## Do / Don't
|
|
363
365
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<!-- GENERATED — do not hand-edit. Regenerated from the design system's component contract,
|
|
4
4
|
which is the authority for membership and counts. -->
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**149 components**, plus 467 animated-icon items, 13 hooks (`use-animation-replay`, `use-announcer`, `use-async-search`, `use-drag-reorder`, `use-file-drop`, `use-inline-edit`, `use-list-nav`, `use-media-query`, `use-mobile`, `use-modal-inert`, `use-overflow`, `use-platform`, `use-tabs-swipe`), 14 starter blocks (`app-shell-01`, `board-01`, `issue-detail-01`, `page-editor-01`, `command-search-01`, `list-page-01`, `library-01`, `login-01`, `notifications-01`, `review-split-01`, `settings-01`, `settings-02`, `status-pages-01`, `share-01`), 68 chart blocks across 7 families, and 11 data libs (`code-highlight`, `date-time`, `geo-data`, `emoji-data`, `drag-item`, `text-anchor`, `text-anchor-doc`, `file-kind`, `media-probe`, `page-layout`, `tile-overlay`) — 722 registry items in total.
|
|
7
7
|
|
|
8
8
|
Install any of them with `shadcn add @vegastack/<name>`. Animated icons install as
|
|
9
9
|
`@vegastack/icon-<name>`; the bare name is reserved for components, so a component whose name
|
|
@@ -23,6 +23,7 @@ starts with `icon-` is a component and never an icon.
|
|
|
23
23
|
- **`calendar`** — A date-field calendar on React DayPicker — single, multiple and range selection.
|
|
24
24
|
- **`checkbox`** — A binary (or indeterminate) toggle on Base UI Checkbox, with a 24px invisible hit area (A11Y-2).
|
|
25
25
|
- **`chip-input`** — Free-token entry field — Enter/comma/paste commits chips, Backspace removes, per-chip validation marks invalid entries instead of dropping them. InputGroup field chrome + real Tag chips.
|
|
26
|
+
- **`choice-card`** — A radio option drawn as a card — icon, title, description and the radio — in a group that is two across from @sm and stacked below. A thin wrapper over the Field choice-card pattern.
|
|
26
27
|
- **`color-picker`** — A swatch-triggered popover presenting a grid of preset colors — pick one, fire onValueChange, mark the selection.
|
|
27
28
|
- **`combobox`** — A filterable listbox behind a text input — grouped items, an announced empty state and a chips mode.
|
|
28
29
|
- **`country-select`** — A searchable country picker returning the ISO 3166-1 alpha-2 code, with flag + name. A thin wrapper over SearchableSelect fed by the geo-data item.
|
|
@@ -47,6 +48,7 @@ starts with `icon-` is a component and never an icon.
|
|
|
47
48
|
- **`searchable-select`** — The Select-shaped Combobox preset: a full-width trigger, an in-panel search field, a tick on the selected row (matched by key, so async options tick too), a standard person option (name plus muted email, both searched) and an optional clear control. Single or multiple, controlled through value/onValueChange.
|
|
48
49
|
- **`select`** — A dropdown for one value — trigger, grouped scrollable popup and item-aligned positioning.
|
|
49
50
|
- **`slider`** — A number or range over a continuous track — horizontal or vertical, any number of thumbs.
|
|
51
|
+
- **`space-picker`** — SpaceChip — a space as a quiet property chip (tile, name, chevron) — and SpacePicker, that chip opening a searchable list of spaces with My space first, a check on the current one and disabled rows that say why.
|
|
50
52
|
- **`switch`** — An on/off toggle for instant settings — two sizes and a 24px invisible hit area (A11Y-2).
|
|
51
53
|
- **`textarea`** — A styled native textarea that grows with its content; focus darkens its border subtly with an ease and paints no fill.
|
|
52
54
|
|
|
@@ -108,6 +110,7 @@ starts with `icon-` is a component and never an icon.
|
|
|
108
110
|
|
|
109
111
|
## Overlay
|
|
110
112
|
|
|
113
|
+
- **`access-chip`** — How far one record reaches as one focusable chip: the space tile and name, a lock and Only invited, My space, or a space the viewer cannot see (someone's My space, a private space) — with a globe when published and the full sentence in a tooltip. Clicking opens Share.
|
|
111
114
|
- **`alert-dialog`** — A modal that interrupts for a decision — an optional media slot, two sizes and an action/cancel footer.
|
|
112
115
|
- **`context-menu`** — The same menu vocabulary opened by right-click, positioned at the pointer.
|
|
113
116
|
- **`dialog`** — A modal overlay — backdrop, centred popup, a plain right-aligned footer and an optional close button; opens onto the first field or the popup, never the close ×.
|
|
@@ -117,6 +120,7 @@ starts with `icon-` is a component and never an icon.
|
|
|
117
120
|
- **`panel-search`** — The sticky, box-free search row a filtering popup puts at the top of its panel (decision OVL-11) — shared by EmojiPicker and ShortcutOverlay.
|
|
118
121
|
- **`permission-menu`** — An access level on a sharing row — a ghost trigger reading the level, a menu of levels with one-line descriptions and a check on the current one, an optional destructive Remove access, and a read-only text mode for built-in rows.
|
|
119
122
|
- **`popover`** — An anchored, dismissible surface for secondary content, with a header, title and description.
|
|
123
|
+
- **`responsive-dialog`** — One overlay API that is a centred Dialog from 768px up and a bottom Sheet with a drag handle and a safe-area footer below it — the same header, body and footer in both.
|
|
120
124
|
- **`sheet`** — A panel that slides in from any edge — Dialog semantics with a side, a header and a footer; opens onto the first field, never the close ×; a left sheet can dock beside the sidebar rail (beside).
|
|
121
125
|
- **`shortcut-overlay`** — The ?-triggered dialog listing keyboard shortcuts, rendered from a declaration registry (keys, label, category, when) — grouped, filterable, platform-aware via use-platform + Kbd.
|
|
122
126
|
- **`tooltip`** — A floating label on hover or focus, portaled inside the theme scope (OVL-13).
|