@voltro/ui-shadcn 0.67.0 → 0.69.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/CHANGELOG.md +437 -0
- package/THIRD-PARTY-NOTICES.md +1 -113
- package/dist/index.d.ts +36 -3
- package/dist/index.js +200 -140
- package/dist/signal.css +105 -2
- package/dist/tokens.css +9 -0
- package/package.json +2 -2
- package/src/compositions/signalWorkspace.tsx +63 -0
- package/src/index.ts +1 -0
- package/src/primitives/checkbox.tsx +2 -2
- package/src/primitives/dialog.tsx +2 -2
- package/src/signal.css +105 -2
- package/src/tokens.css +9 -0
- package/src/widgets.tsx +4 -5
package/dist/signal.css
CHANGED
|
@@ -103,6 +103,46 @@
|
|
|
103
103
|
|
|
104
104
|
.signal-stage .signal-atmosphere { mask-image: linear-gradient(#000 0%, #000 85%, transparent 100%); }
|
|
105
105
|
.signal-shell { width: min(100% - 88px, 1536px); margin-inline: auto; }
|
|
106
|
+
/* One opening rhythm for Framework, Cloud and the documentation index. */
|
|
107
|
+
.signal-opener { padding-top: 50px; }
|
|
108
|
+
.signal-opener-title { margin: 0; position: relative; z-index: 1; font-size: clamp(48px, 6.15vw, 96px); font-weight: 700; line-height: 1.06; letter-spacing: -.04em; }
|
|
109
|
+
.signal-opener-title > span { display: block; }
|
|
110
|
+
.signal-opener-accent { color: var(--signal-light); margin-top: 9px; }
|
|
111
|
+
.signal-opener-description { position: relative; z-index: 1; max-width: 75ch; margin: 27px 0 0; font-size: clamp(18px, 1.64vw, 25px); line-height: 1.48; letter-spacing: -.023em; color: var(--signal-ink); }
|
|
112
|
+
.signal-opener-description > span { display: block; }
|
|
113
|
+
.signal-opener-actions { position: relative; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 16px 39px; width: fit-content; margin-top: 29px; }
|
|
114
|
+
.signal-opener-secondary { display: inline-flex; align-items: center; gap: 12px; min-height: 44px; font-size: 15px; color: var(--signal-ink); }
|
|
115
|
+
.signal-opener-secondary > span { position: relative; padding-block: 7px; }
|
|
116
|
+
.signal-opener-secondary > span::after { content: ''; position: absolute; left: 0; right: 0; bottom: 1px; height: 1px; background: var(--signal-light); transform-origin: left; transform: scaleX(.22); transition: transform 220ms var(--signal-ease); }
|
|
117
|
+
.signal-opener-actions .signal-opener-secondary svg { width: 16px; height: 16px; color: var(--signal-light); transition: transform 200ms var(--signal-ease); }
|
|
118
|
+
.signal-opener-secondary:hover > span::after, .signal-opener-secondary:focus-visible > span::after { transform: scaleX(1); }
|
|
119
|
+
.signal-opener-secondary:hover svg, .signal-opener-secondary:focus-visible svg { transform: translate(2px, -2px); }
|
|
120
|
+
.signal-opener-actions .signal-primary { position: relative; isolation: isolate; }
|
|
121
|
+
.signal-opener-actions .signal-arrow svg { width: 22px; height: 22px; }
|
|
122
|
+
.signal-opener-actions .signal-primary::after { content: ''; position: absolute; height: 2px; bottom: 0; left: 0; right: 0; background: #d5beff; transform: scaleX(0); transform-origin: left; transition: transform 280ms var(--signal-ease); }
|
|
123
|
+
.signal-opener-actions .signal-primary:hover::after, .signal-opener-actions .signal-primary:focus-visible::after { transform: scaleX(1); }
|
|
124
|
+
.signal-opener-actions .signal-primary:active svg { transform: translateX(2px); }
|
|
125
|
+
@media (min-width: 1600px) { .signal-opener { padding-top: 70px; } }
|
|
126
|
+
@media (max-width: 1150px) {
|
|
127
|
+
.signal-opener { padding-top: 40px; }
|
|
128
|
+
.signal-opener-title { font-size: clamp(48px, 6.1vw, 75px); }
|
|
129
|
+
}
|
|
130
|
+
@media (max-width: 760px) {
|
|
131
|
+
.signal-opener { padding-top: 29px; }
|
|
132
|
+
.signal-opener-title { font-size: clamp(37px, 7.5vw, 57px); line-height: 1.1; letter-spacing: -.035em; }
|
|
133
|
+
.signal-opener-accent { margin-top: 5px; }
|
|
134
|
+
.signal-opener-description { max-width: 40ch; margin-top: 22px; font-size: 16px; line-height: 1.5; }
|
|
135
|
+
.signal-opener-description > span { display: inline; }
|
|
136
|
+
.signal-opener-description > span + span::before { content: ' '; }
|
|
137
|
+
.signal-opener-actions { width: 100%; gap: 12px 22px; margin-top: 24px; }
|
|
138
|
+
.signal-opener-secondary { gap: 5px; font-size: 13px; }
|
|
139
|
+
.signal-opener-actions .signal-opener-secondary svg { width: 14px; height: 14px; }
|
|
140
|
+
}
|
|
141
|
+
@media (prefers-reduced-motion: reduce) {
|
|
142
|
+
.signal-opener-secondary > span::after, .signal-opener-actions .signal-primary::after { transition: none; }
|
|
143
|
+
.signal-opener-actions svg { transition: none; }
|
|
144
|
+
.signal-opener-actions a:hover svg, .signal-opener-actions a:focus-visible svg, .signal-opener-actions a:active svg { transform: none; }
|
|
145
|
+
}
|
|
106
146
|
.signal-primary {
|
|
107
147
|
display: inline-flex; align-items: stretch; min-height: 54px; width: fit-content;
|
|
108
148
|
background: #7c3aed linear-gradient(115deg, #6d28d9, #7c3aed 48%, #8a49ed);
|
|
@@ -164,8 +204,8 @@
|
|
|
164
204
|
.signal-control:hover, .signal-control[data-state=open] { border-color: var(--input); background: var(--secondary); }
|
|
165
205
|
.signal-preferences { padding-inline: 12px; border-color: transparent; background: transparent; color: var(--muted-foreground); font-size: 12px; }
|
|
166
206
|
.signal-preferences:hover, .signal-preferences[data-state=open] { color: var(--foreground); }
|
|
167
|
-
.signal-header-action {
|
|
168
|
-
.signal-header-action > svg { color: var(--signal-accent-ink); transition: transform 180ms var(--signal-ease); }
|
|
207
|
+
.signal-header-action { line-height: 1.5; }
|
|
208
|
+
.signal-header-action > svg { width: 16px; height: 16px; color: var(--signal-accent-ink); transition: transform 180ms var(--signal-ease); }
|
|
169
209
|
.signal-header-action:hover > svg { transform: translateX(3px); }
|
|
170
210
|
.signal-search { width: 224px; justify-content: flex-start; color: var(--muted-foreground); font-weight: 400; }
|
|
171
211
|
.signal-search kbd { margin-left: auto; padding: 2px 5px; border: 1px solid var(--border); border-radius: 3px; font-family: inherit; font-size: 11px; line-height: 1.4; }
|
|
@@ -221,3 +261,66 @@
|
|
|
221
261
|
.signal-marketing-close::before { background: radial-gradient(ellipse at 20% 30%, #7c3aed29, transparent 65%); }
|
|
222
262
|
.signal-marketing-close .group { background: linear-gradient(115deg, #6d28d9, #7c3aed 48%, #8a49ed); color: #fff; border-radius: 4px; box-shadow: 0 5px 20px -9px #7c3aed66; }
|
|
223
263
|
@media(max-width:760px) { .signal-marketing-close { padding: 40px 24px; } }
|
|
264
|
+
|
|
265
|
+
/* Shared operating workspace: Cloud and local dashboards use the same shell. */
|
|
266
|
+
.signal-workspace { position: relative; isolation: isolate; }
|
|
267
|
+
.signal-workspace .signal-workspace-field { height: 600px; bottom: auto; opacity: .65; mask-image: linear-gradient(#000, #000 35%, transparent); }
|
|
268
|
+
.signal-workspace-field .field-contours { top: -220px; opacity: .6; }
|
|
269
|
+
.signal-workspace-header { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 40px; }
|
|
270
|
+
.signal-workspace-heading { min-width: 0; }
|
|
271
|
+
.signal-workspace-title { display: flex; align-items: center; flex-wrap: wrap; gap: 16px; }
|
|
272
|
+
.signal-workspace-title h1 { font-size: 3rem; line-height: 1.12; font-weight: 650; letter-spacing: -.035em; overflow-wrap: anywhere; }
|
|
273
|
+
.signal-workspace-description { max-width: 70ch; margin-top: 16px; color: var(--muted-foreground); font-size: 16px; line-height: 1.65; }
|
|
274
|
+
.signal-workspace-description code { color: var(--foreground); font-size: .875em; }
|
|
275
|
+
.signal-workspace-action { flex-shrink: 0; }
|
|
276
|
+
.signal-workspace [data-slot="card"] { border-radius: 6px; box-shadow: none; }
|
|
277
|
+
.signal-workspace [data-slot="button"] { box-shadow: none; border-radius: 4px; }
|
|
278
|
+
.signal-workspace [data-slot="button"].bg-primary { background: var(--signal-violet) linear-gradient(115deg, #6d28d9, #7c3aed 48%, #8a49ed); }
|
|
279
|
+
.signal-workspace-page { padding-block: 40px 80px; }
|
|
280
|
+
@media (max-width: 760px) {
|
|
281
|
+
.signal-workspace-page { padding-block: 28px 48px; }
|
|
282
|
+
.signal-workspace-header { align-items: flex-start; flex-direction: column; gap: 22px; margin-bottom: 30px; }
|
|
283
|
+
.signal-workspace-title h1 { font-size: 2.25rem; }
|
|
284
|
+
.signal-workspace-description { font-size: 14px; margin-top: 12px; }
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.signal-workspace-item { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto auto; align-items: center; gap: 24px; min-height: 116px; padding: 24px 4px; border-bottom: 1px solid var(--border); transition: background-color 180ms, border-color 180ms; }
|
|
288
|
+
.signal-workspace-item:hover { background: color-mix(in srgb, var(--accent) 40%, transparent); border-bottom-color: var(--signal-line); }
|
|
289
|
+
.signal-workspace-item-icon { width: 44px; height: 44px; border: 1px solid var(--signal-line); border-radius: 6px; display: grid; place-items: center; color: var(--signal-accent-ink); background: var(--background); }
|
|
290
|
+
.signal-workspace-item-icon svg { width: 20px; height: 20px; }
|
|
291
|
+
.signal-workspace-item-copy { display: grid; gap: 8px; min-width: 0; }
|
|
292
|
+
.signal-workspace-item-title { font-size: 20px; line-height: 1.3; font-weight: 550; letter-spacing: -.02em; overflow-wrap: anywhere; }
|
|
293
|
+
.signal-workspace-item-description { font-size: 13px; color: var(--muted-foreground); overflow-wrap: anywhere; line-height: 1.6; }
|
|
294
|
+
.signal-workspace-item-detail { display: flex; align-items: center; gap: 20px; color: var(--muted-foreground); font-size: 12px; }
|
|
295
|
+
.signal-workspace-item-open { display: flex; align-items: center; justify-content: flex-end; gap: 14px; min-height: 44px; margin-left: 24px; font-size: 13px; color: var(--signal-accent-ink); }
|
|
296
|
+
.signal-workspace-item-open svg { height: 18px; width: 18px; transition: transform 180ms var(--signal-ease); }
|
|
297
|
+
.signal-workspace-item:hover .signal-workspace-item-open svg { transform: translateX(4px); }
|
|
298
|
+
.signal-workspace-item { width: 100%; text-align: left; cursor: pointer; }
|
|
299
|
+
@media (max-width: 1000px) { .signal-workspace-item-detail { flex-direction: column; align-items: flex-start; gap: 8px; } }
|
|
300
|
+
@media (max-width: 760px) {
|
|
301
|
+
.signal-workspace-item { grid-template-columns: 36px minmax(0, 1fr) 24px; gap: 12px; padding-block: 20px; }
|
|
302
|
+
.signal-workspace-item-icon { width: 36px; height: 36px; align-self: start; }
|
|
303
|
+
.signal-workspace-item-title { font-size: 18px; }
|
|
304
|
+
.signal-workspace-item-detail { grid-column: 2; grid-row: 2; flex-direction: row; flex-wrap: wrap; }
|
|
305
|
+
.signal-workspace-item-open { grid-column: 3; grid-row: 1 / span 2; margin-left: 0; }
|
|
306
|
+
.signal-workspace-item-open > span { display: none; }
|
|
307
|
+
}
|
|
308
|
+
@media (prefers-reduced-motion: reduce) {
|
|
309
|
+
.signal-workspace-item, .signal-workspace-item-open svg { transition: none; }
|
|
310
|
+
.signal-workspace-item:hover .signal-workspace-item-open svg { transform: none; }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.signal-workspace-tabs { display: flex; gap: 28px; overflow-x: auto; border-bottom: 1px solid var(--border); margin-bottom: 32px; }
|
|
314
|
+
.signal-workspace-tabs > :is(a, span) { flex-shrink: 0; display: inline-flex; min-height: 48px; align-items: center; padding: 0 1px; border-bottom: 2px solid transparent; color: var(--muted-foreground); font-size: 14px; }
|
|
315
|
+
.signal-workspace-tabs > [aria-current="page"] { color: var(--foreground); border-bottom-color: var(--signal-light); background: transparent; }
|
|
316
|
+
.signal-workspace-tabs > a:not([aria-current="page"]):hover { color: var(--foreground); border-bottom-color: var(--signal-line); }
|
|
317
|
+
|
|
318
|
+
/* Operating chrome keeps navigation close to the task surface. */
|
|
319
|
+
.signal-workspace-compact { --signal-header-height: 88px; }
|
|
320
|
+
.signal-workspace-compact .signal-workspace-page { padding-top: 8px; }
|
|
321
|
+
.signal-workspace-compact .signal-workspace-tabs { margin-bottom: 16px; }
|
|
322
|
+
@media (max-width: 760px) {
|
|
323
|
+
.signal-workspace-compact { --signal-header-height: 138px; }
|
|
324
|
+
.signal-workspace-compact .signal-header { padding-block: 12px; gap: 12px; }
|
|
325
|
+
.signal-workspace-compact .signal-workspace-tabs { gap: 24px; margin-bottom: 12px; }
|
|
326
|
+
}
|
package/dist/tokens.css
CHANGED
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
--accent-foreground: oklch(0 0 0);
|
|
52
52
|
--destructive: oklch(0.63 0.19 23.03);
|
|
53
53
|
--destructive-foreground: oklch(1 0 0);
|
|
54
|
+
--success: #167252;
|
|
55
|
+
--warning: #805e00;
|
|
56
|
+
--info: #2563b4;
|
|
54
57
|
--border: oklch(0.92 0 0);
|
|
55
58
|
--input: oklch(0.94 0 0);
|
|
56
59
|
--ring: oklch(0 0 0);
|
|
@@ -111,6 +114,9 @@
|
|
|
111
114
|
--accent-foreground: oklch(1 0 0);
|
|
112
115
|
--destructive: oklch(0.69 0.2 23.91);
|
|
113
116
|
--destructive-foreground: oklch(0 0 0);
|
|
117
|
+
--success: #62cda1;
|
|
118
|
+
--warning: #e4ba65;
|
|
119
|
+
--info: #86b7f2;
|
|
114
120
|
--border: oklch(0.26 0 0);
|
|
115
121
|
--input: oklch(0.32 0 0);
|
|
116
122
|
--ring: oklch(0.72 0 0);
|
|
@@ -173,6 +179,9 @@
|
|
|
173
179
|
--color-accent-foreground: var(--accent-foreground);
|
|
174
180
|
--color-destructive: var(--destructive);
|
|
175
181
|
--color-destructive-foreground: var(--destructive-foreground);
|
|
182
|
+
--color-success: var(--success);
|
|
183
|
+
--color-warning: var(--warning);
|
|
184
|
+
--color-info: var(--info);
|
|
176
185
|
--color-border: var(--border);
|
|
177
186
|
--color-input: var(--input);
|
|
178
187
|
--color-ring: var(--ring);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/ui-shadcn",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.0",
|
|
4
4
|
"description": "Voltro's first-party shadcn/ui kit: Tailwind v4 design tokens (light + dark), 30+ primitives, layout compositions, styled widgets for the @voltro/ui seam, and the canonical theme/language preference-cookie helpers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@radix-ui/react-toggle-group": "^1.1.19",
|
|
53
53
|
"@shikijs/langs": "^4.4.3",
|
|
54
54
|
"@shikijs/themes": "^4.4.3",
|
|
55
|
-
"@voltro/ui": "0.
|
|
55
|
+
"@voltro/ui": "0.69.0",
|
|
56
56
|
"class-variance-authority": "^0.7.1",
|
|
57
57
|
"clsx": "^2.1.1",
|
|
58
58
|
"shiki": "^4.4.3",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { cn } from '../cn'
|
|
3
|
+
import { AppShell } from './appShell'
|
|
4
|
+
import { SignalField } from './signalField'
|
|
5
|
+
import { PackageIcon } from '../primitives/docIcons'
|
|
6
|
+
|
|
7
|
+
export interface SignalWorkspaceProps {
|
|
8
|
+
readonly brand: ReactNode
|
|
9
|
+
readonly header: ReactNode
|
|
10
|
+
readonly children: ReactNode
|
|
11
|
+
readonly className?: string
|
|
12
|
+
/** Signed-out pages can supply their own full-height, animated field. */
|
|
13
|
+
readonly field?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Shared Cloud/local operating shell. Import `@voltro/ui-shadcn/signal.css`. */
|
|
17
|
+
export function SignalWorkspace({ brand, header, children, className, field = true }: SignalWorkspaceProps): ReactNode {
|
|
18
|
+
return <AppShell brand={brand} header={header} className={cn('signal-operate signal-stage signal-workspace', className)}>
|
|
19
|
+
{field && <SignalField className="signal-workspace-field" />}
|
|
20
|
+
{children}
|
|
21
|
+
</AppShell>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SignalWorkspaceHeaderProps {
|
|
25
|
+
readonly title: ReactNode
|
|
26
|
+
readonly description?: ReactNode
|
|
27
|
+
readonly badge?: ReactNode
|
|
28
|
+
readonly action?: ReactNode
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** One heading, description and action rhythm for workspace pages in either host. */
|
|
32
|
+
export function SignalWorkspaceHeader({ title, description, badge, action }: SignalWorkspaceHeaderProps): ReactNode {
|
|
33
|
+
return <div className="signal-workspace-header">
|
|
34
|
+
<div className="signal-workspace-heading">
|
|
35
|
+
<div className="signal-workspace-title"><h1>{title}</h1>{badge}</div>
|
|
36
|
+
{description && <p className="signal-workspace-description">{description}</p>}
|
|
37
|
+
</div>
|
|
38
|
+
{action && <div className="signal-workspace-action">{action}</div>}
|
|
39
|
+
</div>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface SignalWorkspaceItemProps {
|
|
43
|
+
readonly title: string
|
|
44
|
+
readonly description: ReactNode
|
|
45
|
+
readonly detail?: ReactNode
|
|
46
|
+
readonly actionLabel: string
|
|
47
|
+
readonly renderLink: (className: string, children: ReactNode) => ReactNode
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Open workspace row; the host supplies its typed link or selection button. */
|
|
51
|
+
export function SignalWorkspaceItem({ title, description, detail, actionLabel, renderLink }: SignalWorkspaceItemProps): ReactNode {
|
|
52
|
+
return renderLink('signal-workspace-item', <>
|
|
53
|
+
<span className="signal-workspace-item-icon" aria-hidden="true"><PackageIcon /></span>
|
|
54
|
+
<span className="signal-workspace-item-copy">
|
|
55
|
+
<span className="signal-workspace-item-title">{title}</span>
|
|
56
|
+
<span className="signal-workspace-item-description">{description}</span>
|
|
57
|
+
</span>
|
|
58
|
+
<span className="signal-workspace-item-detail">{detail}</span>
|
|
59
|
+
<span className="signal-workspace-item-open"><span>{actionLabel}</span>
|
|
60
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true"><path d="M4 12h16m-6-6 6 6-6 6" /></svg>
|
|
61
|
+
</span>
|
|
62
|
+
</>)
|
|
63
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -160,5 +160,6 @@ export { SearchModal, SearchTrigger } from './primitives/searchModal'
|
|
|
160
160
|
export type { SearchModalProps, SearchModalLabels } from './primitives/searchModal'
|
|
161
161
|
|
|
162
162
|
export { SignalField, type SignalFieldProps } from './compositions/signalField'
|
|
163
|
+
export { SignalWorkspace, SignalWorkspaceHeader, SignalWorkspaceItem, type SignalWorkspaceProps, type SignalWorkspaceHeaderProps, type SignalWorkspaceItemProps } from './compositions/signalWorkspace'
|
|
163
164
|
export { SignalPreferences, SignalSearch, SignalHeaderAction, type SignalPreferencesProps, type SignalSearchProps } from './compositions/signalControls'
|
|
164
165
|
export { SignalHeader, type SignalHeaderProps, type SignalNavigationLink } from './compositions/signalHeader'
|
|
@@ -26,8 +26,8 @@ export const Checkbox = ({ className, indeterminate, ...props }: CheckboxProps):
|
|
|
26
26
|
className={cn(
|
|
27
27
|
'peer size-4 appearance-none rounded-[4px] border shadow-xs outline-none',
|
|
28
28
|
'border-input bg-transparent dark:bg-input/30',
|
|
29
|
-
'checked:bg-primary checked:border-primary',
|
|
30
|
-
'indeterminate:bg-primary indeterminate:border-primary',
|
|
29
|
+
'checked:bg-primary checked:border-primary dark:checked:bg-primary',
|
|
30
|
+
'indeterminate:bg-primary indeterminate:border-primary dark:indeterminate:bg-primary',
|
|
31
31
|
'transition-[color,box-shadow]',
|
|
32
32
|
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
|
33
33
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
// imperative API (`dialogRef.current?.showModal()`) is the native
|
|
8
8
|
// path; we surface it as a uncontrolled wrapper for now.
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type { ComponentPropsWithRef, HTMLAttributes, ReactNode } from 'react'
|
|
11
11
|
import { cn } from '../cn'
|
|
12
12
|
|
|
13
|
-
export const Dialog = ({ className, children, ...props }:
|
|
13
|
+
export const Dialog = ({ className, children, ...props }: ComponentPropsWithRef<'dialog'>): ReactNode => (
|
|
14
14
|
<dialog
|
|
15
15
|
data-slot="dialog"
|
|
16
16
|
className={cn(
|
package/src/signal.css
CHANGED
|
@@ -103,6 +103,46 @@
|
|
|
103
103
|
|
|
104
104
|
.signal-stage .signal-atmosphere { mask-image: linear-gradient(#000 0%, #000 85%, transparent 100%); }
|
|
105
105
|
.signal-shell { width: min(100% - 88px, 1536px); margin-inline: auto; }
|
|
106
|
+
/* One opening rhythm for Framework, Cloud and the documentation index. */
|
|
107
|
+
.signal-opener { padding-top: 50px; }
|
|
108
|
+
.signal-opener-title { margin: 0; position: relative; z-index: 1; font-size: clamp(48px, 6.15vw, 96px); font-weight: 700; line-height: 1.06; letter-spacing: -.04em; }
|
|
109
|
+
.signal-opener-title > span { display: block; }
|
|
110
|
+
.signal-opener-accent { color: var(--signal-light); margin-top: 9px; }
|
|
111
|
+
.signal-opener-description { position: relative; z-index: 1; max-width: 75ch; margin: 27px 0 0; font-size: clamp(18px, 1.64vw, 25px); line-height: 1.48; letter-spacing: -.023em; color: var(--signal-ink); }
|
|
112
|
+
.signal-opener-description > span { display: block; }
|
|
113
|
+
.signal-opener-actions { position: relative; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 16px 39px; width: fit-content; margin-top: 29px; }
|
|
114
|
+
.signal-opener-secondary { display: inline-flex; align-items: center; gap: 12px; min-height: 44px; font-size: 15px; color: var(--signal-ink); }
|
|
115
|
+
.signal-opener-secondary > span { position: relative; padding-block: 7px; }
|
|
116
|
+
.signal-opener-secondary > span::after { content: ''; position: absolute; left: 0; right: 0; bottom: 1px; height: 1px; background: var(--signal-light); transform-origin: left; transform: scaleX(.22); transition: transform 220ms var(--signal-ease); }
|
|
117
|
+
.signal-opener-actions .signal-opener-secondary svg { width: 16px; height: 16px; color: var(--signal-light); transition: transform 200ms var(--signal-ease); }
|
|
118
|
+
.signal-opener-secondary:hover > span::after, .signal-opener-secondary:focus-visible > span::after { transform: scaleX(1); }
|
|
119
|
+
.signal-opener-secondary:hover svg, .signal-opener-secondary:focus-visible svg { transform: translate(2px, -2px); }
|
|
120
|
+
.signal-opener-actions .signal-primary { position: relative; isolation: isolate; }
|
|
121
|
+
.signal-opener-actions .signal-arrow svg { width: 22px; height: 22px; }
|
|
122
|
+
.signal-opener-actions .signal-primary::after { content: ''; position: absolute; height: 2px; bottom: 0; left: 0; right: 0; background: #d5beff; transform: scaleX(0); transform-origin: left; transition: transform 280ms var(--signal-ease); }
|
|
123
|
+
.signal-opener-actions .signal-primary:hover::after, .signal-opener-actions .signal-primary:focus-visible::after { transform: scaleX(1); }
|
|
124
|
+
.signal-opener-actions .signal-primary:active svg { transform: translateX(2px); }
|
|
125
|
+
@media (min-width: 1600px) { .signal-opener { padding-top: 70px; } }
|
|
126
|
+
@media (max-width: 1150px) {
|
|
127
|
+
.signal-opener { padding-top: 40px; }
|
|
128
|
+
.signal-opener-title { font-size: clamp(48px, 6.1vw, 75px); }
|
|
129
|
+
}
|
|
130
|
+
@media (max-width: 760px) {
|
|
131
|
+
.signal-opener { padding-top: 29px; }
|
|
132
|
+
.signal-opener-title { font-size: clamp(37px, 7.5vw, 57px); line-height: 1.1; letter-spacing: -.035em; }
|
|
133
|
+
.signal-opener-accent { margin-top: 5px; }
|
|
134
|
+
.signal-opener-description { max-width: 40ch; margin-top: 22px; font-size: 16px; line-height: 1.5; }
|
|
135
|
+
.signal-opener-description > span { display: inline; }
|
|
136
|
+
.signal-opener-description > span + span::before { content: ' '; }
|
|
137
|
+
.signal-opener-actions { width: 100%; gap: 12px 22px; margin-top: 24px; }
|
|
138
|
+
.signal-opener-secondary { gap: 5px; font-size: 13px; }
|
|
139
|
+
.signal-opener-actions .signal-opener-secondary svg { width: 14px; height: 14px; }
|
|
140
|
+
}
|
|
141
|
+
@media (prefers-reduced-motion: reduce) {
|
|
142
|
+
.signal-opener-secondary > span::after, .signal-opener-actions .signal-primary::after { transition: none; }
|
|
143
|
+
.signal-opener-actions svg { transition: none; }
|
|
144
|
+
.signal-opener-actions a:hover svg, .signal-opener-actions a:focus-visible svg, .signal-opener-actions a:active svg { transform: none; }
|
|
145
|
+
}
|
|
106
146
|
.signal-primary {
|
|
107
147
|
display: inline-flex; align-items: stretch; min-height: 54px; width: fit-content;
|
|
108
148
|
background: #7c3aed linear-gradient(115deg, #6d28d9, #7c3aed 48%, #8a49ed);
|
|
@@ -164,8 +204,8 @@
|
|
|
164
204
|
.signal-control:hover, .signal-control[data-state=open] { border-color: var(--input); background: var(--secondary); }
|
|
165
205
|
.signal-preferences { padding-inline: 12px; border-color: transparent; background: transparent; color: var(--muted-foreground); font-size: 12px; }
|
|
166
206
|
.signal-preferences:hover, .signal-preferences[data-state=open] { color: var(--foreground); }
|
|
167
|
-
.signal-header-action {
|
|
168
|
-
.signal-header-action > svg { color: var(--signal-accent-ink); transition: transform 180ms var(--signal-ease); }
|
|
207
|
+
.signal-header-action { line-height: 1.5; }
|
|
208
|
+
.signal-header-action > svg { width: 16px; height: 16px; color: var(--signal-accent-ink); transition: transform 180ms var(--signal-ease); }
|
|
169
209
|
.signal-header-action:hover > svg { transform: translateX(3px); }
|
|
170
210
|
.signal-search { width: 224px; justify-content: flex-start; color: var(--muted-foreground); font-weight: 400; }
|
|
171
211
|
.signal-search kbd { margin-left: auto; padding: 2px 5px; border: 1px solid var(--border); border-radius: 3px; font-family: inherit; font-size: 11px; line-height: 1.4; }
|
|
@@ -221,3 +261,66 @@
|
|
|
221
261
|
.signal-marketing-close::before { background: radial-gradient(ellipse at 20% 30%, #7c3aed29, transparent 65%); }
|
|
222
262
|
.signal-marketing-close .group { background: linear-gradient(115deg, #6d28d9, #7c3aed 48%, #8a49ed); color: #fff; border-radius: 4px; box-shadow: 0 5px 20px -9px #7c3aed66; }
|
|
223
263
|
@media(max-width:760px) { .signal-marketing-close { padding: 40px 24px; } }
|
|
264
|
+
|
|
265
|
+
/* Shared operating workspace: Cloud and local dashboards use the same shell. */
|
|
266
|
+
.signal-workspace { position: relative; isolation: isolate; }
|
|
267
|
+
.signal-workspace .signal-workspace-field { height: 600px; bottom: auto; opacity: .65; mask-image: linear-gradient(#000, #000 35%, transparent); }
|
|
268
|
+
.signal-workspace-field .field-contours { top: -220px; opacity: .6; }
|
|
269
|
+
.signal-workspace-header { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 40px; }
|
|
270
|
+
.signal-workspace-heading { min-width: 0; }
|
|
271
|
+
.signal-workspace-title { display: flex; align-items: center; flex-wrap: wrap; gap: 16px; }
|
|
272
|
+
.signal-workspace-title h1 { font-size: 3rem; line-height: 1.12; font-weight: 650; letter-spacing: -.035em; overflow-wrap: anywhere; }
|
|
273
|
+
.signal-workspace-description { max-width: 70ch; margin-top: 16px; color: var(--muted-foreground); font-size: 16px; line-height: 1.65; }
|
|
274
|
+
.signal-workspace-description code { color: var(--foreground); font-size: .875em; }
|
|
275
|
+
.signal-workspace-action { flex-shrink: 0; }
|
|
276
|
+
.signal-workspace [data-slot="card"] { border-radius: 6px; box-shadow: none; }
|
|
277
|
+
.signal-workspace [data-slot="button"] { box-shadow: none; border-radius: 4px; }
|
|
278
|
+
.signal-workspace [data-slot="button"].bg-primary { background: var(--signal-violet) linear-gradient(115deg, #6d28d9, #7c3aed 48%, #8a49ed); }
|
|
279
|
+
.signal-workspace-page { padding-block: 40px 80px; }
|
|
280
|
+
@media (max-width: 760px) {
|
|
281
|
+
.signal-workspace-page { padding-block: 28px 48px; }
|
|
282
|
+
.signal-workspace-header { align-items: flex-start; flex-direction: column; gap: 22px; margin-bottom: 30px; }
|
|
283
|
+
.signal-workspace-title h1 { font-size: 2.25rem; }
|
|
284
|
+
.signal-workspace-description { font-size: 14px; margin-top: 12px; }
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.signal-workspace-item { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto auto; align-items: center; gap: 24px; min-height: 116px; padding: 24px 4px; border-bottom: 1px solid var(--border); transition: background-color 180ms, border-color 180ms; }
|
|
288
|
+
.signal-workspace-item:hover { background: color-mix(in srgb, var(--accent) 40%, transparent); border-bottom-color: var(--signal-line); }
|
|
289
|
+
.signal-workspace-item-icon { width: 44px; height: 44px; border: 1px solid var(--signal-line); border-radius: 6px; display: grid; place-items: center; color: var(--signal-accent-ink); background: var(--background); }
|
|
290
|
+
.signal-workspace-item-icon svg { width: 20px; height: 20px; }
|
|
291
|
+
.signal-workspace-item-copy { display: grid; gap: 8px; min-width: 0; }
|
|
292
|
+
.signal-workspace-item-title { font-size: 20px; line-height: 1.3; font-weight: 550; letter-spacing: -.02em; overflow-wrap: anywhere; }
|
|
293
|
+
.signal-workspace-item-description { font-size: 13px; color: var(--muted-foreground); overflow-wrap: anywhere; line-height: 1.6; }
|
|
294
|
+
.signal-workspace-item-detail { display: flex; align-items: center; gap: 20px; color: var(--muted-foreground); font-size: 12px; }
|
|
295
|
+
.signal-workspace-item-open { display: flex; align-items: center; justify-content: flex-end; gap: 14px; min-height: 44px; margin-left: 24px; font-size: 13px; color: var(--signal-accent-ink); }
|
|
296
|
+
.signal-workspace-item-open svg { height: 18px; width: 18px; transition: transform 180ms var(--signal-ease); }
|
|
297
|
+
.signal-workspace-item:hover .signal-workspace-item-open svg { transform: translateX(4px); }
|
|
298
|
+
.signal-workspace-item { width: 100%; text-align: left; cursor: pointer; }
|
|
299
|
+
@media (max-width: 1000px) { .signal-workspace-item-detail { flex-direction: column; align-items: flex-start; gap: 8px; } }
|
|
300
|
+
@media (max-width: 760px) {
|
|
301
|
+
.signal-workspace-item { grid-template-columns: 36px minmax(0, 1fr) 24px; gap: 12px; padding-block: 20px; }
|
|
302
|
+
.signal-workspace-item-icon { width: 36px; height: 36px; align-self: start; }
|
|
303
|
+
.signal-workspace-item-title { font-size: 18px; }
|
|
304
|
+
.signal-workspace-item-detail { grid-column: 2; grid-row: 2; flex-direction: row; flex-wrap: wrap; }
|
|
305
|
+
.signal-workspace-item-open { grid-column: 3; grid-row: 1 / span 2; margin-left: 0; }
|
|
306
|
+
.signal-workspace-item-open > span { display: none; }
|
|
307
|
+
}
|
|
308
|
+
@media (prefers-reduced-motion: reduce) {
|
|
309
|
+
.signal-workspace-item, .signal-workspace-item-open svg { transition: none; }
|
|
310
|
+
.signal-workspace-item:hover .signal-workspace-item-open svg { transform: none; }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.signal-workspace-tabs { display: flex; gap: 28px; overflow-x: auto; border-bottom: 1px solid var(--border); margin-bottom: 32px; }
|
|
314
|
+
.signal-workspace-tabs > :is(a, span) { flex-shrink: 0; display: inline-flex; min-height: 48px; align-items: center; padding: 0 1px; border-bottom: 2px solid transparent; color: var(--muted-foreground); font-size: 14px; }
|
|
315
|
+
.signal-workspace-tabs > [aria-current="page"] { color: var(--foreground); border-bottom-color: var(--signal-light); background: transparent; }
|
|
316
|
+
.signal-workspace-tabs > a:not([aria-current="page"]):hover { color: var(--foreground); border-bottom-color: var(--signal-line); }
|
|
317
|
+
|
|
318
|
+
/* Operating chrome keeps navigation close to the task surface. */
|
|
319
|
+
.signal-workspace-compact { --signal-header-height: 88px; }
|
|
320
|
+
.signal-workspace-compact .signal-workspace-page { padding-top: 8px; }
|
|
321
|
+
.signal-workspace-compact .signal-workspace-tabs { margin-bottom: 16px; }
|
|
322
|
+
@media (max-width: 760px) {
|
|
323
|
+
.signal-workspace-compact { --signal-header-height: 138px; }
|
|
324
|
+
.signal-workspace-compact .signal-header { padding-block: 12px; gap: 12px; }
|
|
325
|
+
.signal-workspace-compact .signal-workspace-tabs { gap: 24px; margin-bottom: 12px; }
|
|
326
|
+
}
|
package/src/tokens.css
CHANGED
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
--accent-foreground: oklch(0 0 0);
|
|
52
52
|
--destructive: oklch(0.63 0.19 23.03);
|
|
53
53
|
--destructive-foreground: oklch(1 0 0);
|
|
54
|
+
--success: #167252;
|
|
55
|
+
--warning: #805e00;
|
|
56
|
+
--info: #2563b4;
|
|
54
57
|
--border: oklch(0.92 0 0);
|
|
55
58
|
--input: oklch(0.94 0 0);
|
|
56
59
|
--ring: oklch(0 0 0);
|
|
@@ -111,6 +114,9 @@
|
|
|
111
114
|
--accent-foreground: oklch(1 0 0);
|
|
112
115
|
--destructive: oklch(0.69 0.2 23.91);
|
|
113
116
|
--destructive-foreground: oklch(0 0 0);
|
|
117
|
+
--success: #62cda1;
|
|
118
|
+
--warning: #e4ba65;
|
|
119
|
+
--info: #86b7f2;
|
|
114
120
|
--border: oklch(0.26 0 0);
|
|
115
121
|
--input: oklch(0.32 0 0);
|
|
116
122
|
--ring: oklch(0.72 0 0);
|
|
@@ -173,6 +179,9 @@
|
|
|
173
179
|
--color-accent-foreground: var(--accent-foreground);
|
|
174
180
|
--color-destructive: var(--destructive);
|
|
175
181
|
--color-destructive-foreground: var(--destructive-foreground);
|
|
182
|
+
--color-success: var(--success);
|
|
183
|
+
--color-warning: var(--warning);
|
|
184
|
+
--color-info: var(--info);
|
|
176
185
|
--color-border: var(--border);
|
|
177
186
|
--color-input: var(--input);
|
|
178
187
|
--color-ring: var(--ring);
|
package/src/widgets.tsx
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// the seam falls back to the plain default for any kind not overridden here.
|
|
11
11
|
|
|
12
12
|
import { useId, type ReactNode } from 'react'
|
|
13
|
+
import { Checkbox } from './primitives/checkbox'
|
|
13
14
|
import type { Widget, WidgetProps, WidgetRegistry } from '@voltro/ui'
|
|
14
15
|
|
|
15
16
|
const cx = {
|
|
@@ -18,7 +19,7 @@ const cx = {
|
|
|
18
19
|
control:
|
|
19
20
|
'block w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm ' +
|
|
20
21
|
'focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
|
21
|
-
|
|
22
|
+
radio: 'h-4 w-4 rounded border-input text-primary focus:ring-2 focus:ring-ring',
|
|
22
23
|
error: 'text-sm text-destructive',
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -93,11 +94,9 @@ const CheckboxWidget: Widget = (props) => {
|
|
|
93
94
|
const id = useId()
|
|
94
95
|
return (
|
|
95
96
|
<div className="voltro-field flex items-center gap-2">
|
|
96
|
-
<
|
|
97
|
+
<Checkbox
|
|
97
98
|
id={id}
|
|
98
|
-
type="checkbox"
|
|
99
99
|
name={props.name}
|
|
100
|
-
className={cx.checkbox}
|
|
101
100
|
checked={props.value === true}
|
|
102
101
|
onChange={(e) => props.onChange(e.target.checked)}
|
|
103
102
|
disabled={props.disabled}
|
|
@@ -203,7 +202,7 @@ const RadioWidget: Widget = (props) => {
|
|
|
203
202
|
type="radio"
|
|
204
203
|
name={props.name}
|
|
205
204
|
value={o.value}
|
|
206
|
-
className={cx.
|
|
205
|
+
className={cx.radio}
|
|
207
206
|
checked={asString(props.value) === o.value}
|
|
208
207
|
onChange={() => props.onChange(o.value)}
|
|
209
208
|
disabled={props.disabled}
|