@typecad/ui 1.0.0-alpha.11 → 1.0.0-alpha.13
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/README.md +21 -1
- package/assets/fonts/dejavu/DejaVuSans-Bold.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSans-Oblique.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSans.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSansMono-Bold.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSansMono.ttf +0 -0
- package/assets/fonts/dejavu/LICENSE +187 -0
- package/assets/fonts/dejavu/README.md +49 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +74 -0
- package/dist/engine-index.js +2 -0
- package/dist/preview/build-program.d.ts +7 -1
- package/dist/preview/build-program.js +94 -15
- package/dist/preview/host-gfx.d.ts +3 -0
- package/dist/preview/host-gfx.js +30 -3
- package/dist/preview/host-ui-runtime.d.ts +115 -0
- package/dist/preview/host-ui-runtime.js +928 -46
- package/dist/ui-engine/block-layout.js +25 -11
- package/dist/ui-engine/color.d.ts +6 -0
- package/dist/ui-engine/color.js +80 -2
- package/dist/ui-engine/compat-report.d.ts +8 -0
- package/dist/ui-engine/compat-report.js +124 -0
- package/dist/ui-engine/css-imports.d.ts +4 -0
- package/dist/ui-engine/css-imports.js +70 -0
- package/dist/ui-engine/css-parser.js +58 -14
- package/dist/ui-engine/default-font.d.ts +25 -0
- package/dist/ui-engine/default-font.js +80 -0
- package/dist/ui-engine/font-assets.d.ts +2 -2
- package/dist/ui-engine/font-assets.js +22 -5
- package/dist/ui-engine/html-parser.d.ts +4 -0
- package/dist/ui-engine/html-parser.js +241 -26
- package/dist/ui-engine/image-assets.d.ts +8 -1
- package/dist/ui-engine/image-assets.js +35 -2
- package/dist/ui-engine/image-decode.d.ts +22 -0
- package/dist/ui-engine/image-decode.js +194 -0
- package/dist/ui-engine/inline-parser.js +1 -1
- package/dist/ui-engine/layout-engine.d.ts +19 -0
- package/dist/ui-engine/layout-engine.js +59 -3
- package/dist/ui-engine/model.d.ts +18 -0
- package/dist/ui-engine/model.js +76 -8
- package/dist/ui-engine/runtime-header/blend-bodies.js +29 -9
- package/dist/ui-engine/runtime-header/canvas-helpers.js +5 -2
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -111
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +21 -0
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -179
- package/dist/ui-engine/runtime-header/forward-decls.js +215 -167
- package/dist/ui-engine/runtime-header/init-press-input.js +138 -135
- package/dist/ui-engine/runtime-header/keyboard.js +682 -380
- package/dist/ui-engine/runtime-header/node-draw-body.js +1656 -1377
- package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -214
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -568
- package/dist/ui-engine/runtime-header/scroll-physics.js +25 -1
- package/dist/ui-engine/runtime-header/state-bindings-nav.js +9 -0
- package/dist/ui-engine/runtime-header/structs.js +228 -216
- package/dist/ui-engine/runtime-header/text-rendering.js +888 -770
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -116
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +897 -633
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -25
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +79 -23
- package/dist/ui-engine/runtime-header/types-defines.js +110 -93
- package/dist/ui-engine/shadcn-kit.d.ts +1 -0
- package/dist/ui-engine/shadcn-kit.js +502 -0
- package/dist/ui-engine/style-resolver.d.ts +4 -0
- package/dist/ui-engine/style-resolver.js +58 -6
- package/dist/ui-engine/transpile-ui.js +12 -5
- package/dist/ui-engine/ua-stylesheet.d.ts +22 -1
- package/dist/ui-engine/ua-stylesheet.js +119 -32
- package/dist/ui-engine/ui-lowering.js +37 -3
- package/dist/ui-engine/ui-registry.js +32 -4
- package/dist/ui-engine/yoga-layout.js +57 -52
- package/dist/wizard/config-writer.d.ts +49 -0
- package/dist/wizard/config-writer.js +366 -0
- package/dist/wizard/display-catalog.d.ts +75 -0
- package/dist/wizard/display-catalog.js +207 -0
- package/dist/wizard/index.d.ts +7 -0
- package/dist/wizard/index.js +10 -0
- package/dist/wizard/integration-wizard.d.ts +15 -0
- package/dist/wizard/integration-wizard.js +486 -0
- package/dist/wizard/prompts.d.ts +32 -0
- package/dist/wizard/prompts.js +95 -0
- package/dist/wizard/starter-ui.d.ts +1 -0
- package/dist/wizard/starter-ui.js +56 -0
- package/package.json +18 -5
- package/src/cli.ts +87 -0
- package/src/engine-index.ts +53 -51
- package/src/preview/build-program.ts +810 -734
- package/src/preview/host-gfx.ts +30 -3
- package/src/preview/host-ui-runtime.ts +4133 -3289
- package/src/ui-engine/block-layout.ts +24 -11
- package/src/ui-engine/color.ts +77 -2
- package/src/ui-engine/compat-report.ts +139 -0
- package/src/ui-engine/css-imports.ts +69 -0
- package/src/ui-engine/css-parser.ts +64 -15
- package/src/ui-engine/default-font.ts +95 -0
- package/src/ui-engine/font-assets.ts +545 -525
- package/src/ui-engine/html-parser.ts +615 -397
- package/src/ui-engine/image-assets.ts +37 -2
- package/src/ui-engine/image-decode.ts +244 -0
- package/src/ui-engine/inline-parser.ts +1 -1
- package/src/ui-engine/layout-engine.ts +61 -3
- package/src/ui-engine/model.ts +1317 -1230
- package/src/ui-engine/runtime-header/blend-bodies.ts +29 -9
- package/src/ui-engine/runtime-header/canvas-helpers.ts +5 -2
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -117
- package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +21 -0
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -185
- package/src/ui-engine/runtime-header/forward-decls.ts +227 -179
- package/src/ui-engine/runtime-header/init-press-input.ts +144 -141
- package/src/ui-engine/runtime-header/keyboard.ts +689 -386
- package/src/ui-engine/runtime-header/node-draw-body.ts +1681 -1402
- package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -220
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -574
- package/src/ui-engine/runtime-header/scroll-physics.ts +25 -1
- package/src/ui-engine/runtime-header/state-bindings-nav.ts +9 -0
- package/src/ui-engine/runtime-header/structs.ts +234 -222
- package/src/ui-engine/runtime-header/text-rendering.ts +894 -776
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -122
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -638
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -31
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +79 -23
- package/src/ui-engine/runtime-header/types-defines.ts +116 -99
- package/src/ui-engine/shadcn-kit.ts +503 -0
- package/src/ui-engine/style-resolver.ts +471 -416
- package/src/ui-engine/transpile-ui.ts +12 -5
- package/src/ui-engine/ua-stylesheet.ts +137 -31
- package/src/ui-engine/ui-lowering.ts +486 -452
- package/src/ui-engine/ui-registry.ts +556 -524
- package/src/ui-engine/yoga-layout.ts +307 -309
- package/src/wizard/config-writer.ts +404 -0
- package/src/wizard/display-catalog.ts +273 -0
- package/src/wizard/index.ts +38 -0
- package/src/wizard/integration-wizard.ts +619 -0
- package/src/wizard/prompts.ts +145 -0
- package/src/wizard/starter-ui.ts +58 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// The built-in shadcn-style kit stylesheet (tokens + class recipes). Always
|
|
3
|
+
// prepended to the CSS chain by BOTH pipelines (ui-registry for device
|
|
4
|
+
// builds, buildPreviewSnapshot for the preview) ahead of the user's CSS, so
|
|
5
|
+
// user rules and theme token blocks override it by cascade order. Themes are
|
|
6
|
+
// plain CSS files the user @imports — no registry, no splicing.
|
|
7
|
+
//
|
|
8
|
+
// Source of truth: this string. Edit here; the demos rely on it verbatim.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
export const SHADCN_KIT_CSS = `/* ---------------------------------------------------------------------------
|
|
12
|
+
* shadcn-style component kit — BUILT IN and always included.
|
|
13
|
+
*
|
|
14
|
+
* These tokens + class recipes are prepended to every build and preview
|
|
15
|
+
* automatically, BEFORE your stylesheets, so anything you write overrides
|
|
16
|
+
* them by normal cascade order. No scaffolding, no imports needed: put the
|
|
17
|
+
* classes on native elements and they work.
|
|
18
|
+
*
|
|
19
|
+
* THEMES: a theme is any CSS file. Pre-packaged ones ship with @typecad/ui —
|
|
20
|
+
* import by bare specifier from a <style> block (or the sidecar .ui.css):
|
|
21
|
+
*
|
|
22
|
+
* @import "@typecad/ui/themes/blue.css"; (zinc slate stone gray neutral
|
|
23
|
+
* blue green red)
|
|
24
|
+
*
|
|
25
|
+
* Your own: save a ui.shadcn.com / tweakcn export into the project and
|
|
26
|
+
* @import it by path — its :root/.dark token blocks override the kit
|
|
27
|
+
* defaults (later definitions win). Both dialects parse — classic HSL channel triplets and
|
|
28
|
+
* Tailwind-v4 oklch(); alpha in colors is ignored (no blending on bare
|
|
29
|
+
* metal). Dark mode activates with themeClass: 'dark' in the config's
|
|
30
|
+
* display block.
|
|
31
|
+
*
|
|
32
|
+
* Overriding recipes: redefine any class in your own stylesheet; your
|
|
33
|
+
* definition wins.
|
|
34
|
+
*
|
|
35
|
+
* Components (classes over native elements):
|
|
36
|
+
* Button <button class="btn btn-primary">Save</button>
|
|
37
|
+
* variants: -secondary -outline -ghost -destructive
|
|
38
|
+
* sizes: .btn-sm .btn-lg full width: .btn-block
|
|
39
|
+
* Badge <text class="badge badge-secondary">new</text>
|
|
40
|
+
* variants: -default(omit) -destructive -outline
|
|
41
|
+
* Card <view class="card">
|
|
42
|
+
* <view class="card-header">
|
|
43
|
+
* <text class="card-title">Title</text>
|
|
44
|
+
* <text class="card-description">Subtitle</text>
|
|
45
|
+
* </view>
|
|
46
|
+
* <view class="card-content">...</view>
|
|
47
|
+
* <view class="card-footer">...buttons...</view>
|
|
48
|
+
* </view>
|
|
49
|
+
* Input <input class="input" placeholder="..."/>
|
|
50
|
+
* Label <label class="form-label">Name</label>
|
|
51
|
+
* Row (kit) <view class="row"> ... </view> (flex row + gap, for composing)
|
|
52
|
+
* Validation .input-error (destructive border) + .field-error hint;
|
|
53
|
+
* runtime-driven: bind borderColor/visible to a signal
|
|
54
|
+
* Separator <hr class="separator"/> (horizontal) or
|
|
55
|
+
* <hr class="vseparator"/> (vertical; stretches to the row height)
|
|
56
|
+
* Alert <view class="alert alert-destructive">
|
|
57
|
+
* <text class="alert-title">...</text>
|
|
58
|
+
* <text class="alert-description">...</text>
|
|
59
|
+
* </view>
|
|
60
|
+
* Skeleton <view class="skeleton"/> (pulse while loading)
|
|
61
|
+
* Spinner <view class="spinner"><view class="spinner-dot"/></view>
|
|
62
|
+
* (indeterminate loading: a dot orbiting a ring via pure
|
|
63
|
+
* transform keyframes — translate lerps smoothly, unlike
|
|
64
|
+
* rotate which only renders exact quarter turns)
|
|
65
|
+
* Dialog <dialog id="d"><view class="dialog-scrim" on:click={...}/>
|
|
66
|
+
* <view class="dialog-card"> ... <view class="dialog-footer">
|
|
67
|
+
* </view></dialog>
|
|
68
|
+
* (centered modal: programmatic ui.dialog.open(id), scrim tap
|
|
69
|
+
* closes; same slot machinery as the drawer)
|
|
70
|
+
* Toast <toast id="t" side="bottom" duration="2500" class="toast">
|
|
71
|
+
* ...</toast> (ui.toast(id) shows it; auto-closes after the
|
|
72
|
+
* duration; stacks as authored siblings)
|
|
73
|
+
* Progress <progress class="progress" value="40"/>
|
|
74
|
+
* Avatar <img class="avatar" src="face.bmp"/>
|
|
75
|
+
* Switch <check class="switch"/> (pill container; the
|
|
76
|
+
* 16px indicator is runtime-drawn at the top-left — best-effort)
|
|
77
|
+
* Tabs <view class="tabs-list"><button class="tabs-trigger">..</button>..
|
|
78
|
+
* <view class="tabs-content-area"><view class="tabs-content">..
|
|
79
|
+
* (panes toggle via ui.bind(x, 'visible', ...) on a signal)
|
|
80
|
+
* Accordion <view class="accordion"><view class="accordion-item">..
|
|
81
|
+
* (content toggles via ui.bind visible; chevron via text bind)
|
|
82
|
+
* Table <table> ... </table> (UA styles apply; see the
|
|
83
|
+
* html-table-approximation build note)
|
|
84
|
+
* ------------------------------------------------------------------------- */
|
|
85
|
+
|
|
86
|
+
:root {
|
|
87
|
+
--background: #ffffff;
|
|
88
|
+
--foreground: #09090b;
|
|
89
|
+
--card: #ffffff;
|
|
90
|
+
--card-foreground: #09090b;
|
|
91
|
+
--primary: #18181b;
|
|
92
|
+
--primary-foreground: #fafafa;
|
|
93
|
+
--secondary: #f4f4f5;
|
|
94
|
+
--secondary-foreground: #18181b;
|
|
95
|
+
--muted: #f4f4f5;
|
|
96
|
+
--muted-foreground: #71717a;
|
|
97
|
+
--accent: #f4f4f5;
|
|
98
|
+
--accent-foreground: #18181b;
|
|
99
|
+
--destructive: #dc2626;
|
|
100
|
+
--destructive-foreground: #fafafa;
|
|
101
|
+
--destructive-background: #fee2e2;
|
|
102
|
+
--border: #e4e4e7;
|
|
103
|
+
--input: #e4e4e7;
|
|
104
|
+
--radius: 8px;
|
|
105
|
+
--shadow-sm: 0 1px 3px rgb(0 0 0 / 0.1);
|
|
106
|
+
/* Solid scrim for dialogs — alpha blending needs a canvas underneath, so
|
|
107
|
+
the kit uses an opaque near-black that reads as a dimmed backdrop. */
|
|
108
|
+
--scrim: #101014;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.dark {
|
|
112
|
+
--background: #09090b;
|
|
113
|
+
--foreground: #fafafa;
|
|
114
|
+
--card: #18181b;
|
|
115
|
+
--card-foreground: #fafafa;
|
|
116
|
+
--primary: #fafafa;
|
|
117
|
+
--primary-foreground: #18181b;
|
|
118
|
+
--secondary: #27272a;
|
|
119
|
+
--secondary-foreground: #fafafa;
|
|
120
|
+
--muted: #27272a;
|
|
121
|
+
--muted-foreground: #a1a1aa;
|
|
122
|
+
--accent: #27272a;
|
|
123
|
+
--accent-foreground: #fafafa;
|
|
124
|
+
--destructive: #ef4444;
|
|
125
|
+
--destructive-foreground: #09090b;
|
|
126
|
+
--destructive-background: #451a1a;
|
|
127
|
+
--border: #27272a;
|
|
128
|
+
--input: #3f3f46;
|
|
129
|
+
--radius: 8px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* ---- Button ---------------------------------------------------------------- */
|
|
133
|
+
|
|
134
|
+
.btn {
|
|
135
|
+
border-radius: var(--radius);
|
|
136
|
+
padding: 10px 16px;
|
|
137
|
+
font-weight: bold;
|
|
138
|
+
transition: background 80ms;
|
|
139
|
+
}
|
|
140
|
+
.btn:pressed { transform: translateY(1px); }
|
|
141
|
+
.btn-primary { background: var(--primary); color: var(--primary-foreground); }
|
|
142
|
+
.btn-secondary { background: var(--secondary); color: var(--secondary-foreground); }
|
|
143
|
+
.btn-outline { background: var(--background); color: var(--foreground); border: 1px solid var(--border); }
|
|
144
|
+
.btn-ghost { color: var(--foreground); }
|
|
145
|
+
.btn-destructive { background: var(--destructive); color: var(--destructive-foreground); }
|
|
146
|
+
.btn-sm { padding: 6px 10px; min-height: 32px; font-size: 12px; }
|
|
147
|
+
.btn-lg { padding: 14px 22px; min-height: 52px; font-size: 18px; }
|
|
148
|
+
.btn-block { align-self: stretch; }
|
|
149
|
+
|
|
150
|
+
/* ---- Badge ------------------------------------------------------------------ */
|
|
151
|
+
|
|
152
|
+
.badge {
|
|
153
|
+
background: var(--primary);
|
|
154
|
+
color: var(--primary-foreground);
|
|
155
|
+
padding: 2px 8px;
|
|
156
|
+
border-radius: 999px;
|
|
157
|
+
font-size: 12px;
|
|
158
|
+
text-align: center;
|
|
159
|
+
}
|
|
160
|
+
.badge-secondary { background: var(--secondary); color: var(--secondary-foreground); }
|
|
161
|
+
.badge-destructive { background: var(--destructive); color: var(--destructive-foreground); }
|
|
162
|
+
.badge-outline { background: var(--background); color: var(--foreground); border: 1px solid var(--border); }
|
|
163
|
+
|
|
164
|
+
/* ---- Layout utility -------------------------------------------------------------
|
|
165
|
+
Not a shadcn component — a kit convenience for composing recipes. */
|
|
166
|
+
|
|
167
|
+
.row { flex-direction: row; align-items: center; gap: 8px; }
|
|
168
|
+
|
|
169
|
+
/* ---- Card ------------------------------------------------------------------- */
|
|
170
|
+
|
|
171
|
+
.card {
|
|
172
|
+
background: var(--card);
|
|
173
|
+
color: var(--card-foreground);
|
|
174
|
+
border: 1px solid var(--border);
|
|
175
|
+
border-radius: var(--radius);
|
|
176
|
+
box-shadow: var(--shadow-sm);
|
|
177
|
+
padding: 16px;
|
|
178
|
+
gap: 8px;
|
|
179
|
+
align-self: stretch;
|
|
180
|
+
}
|
|
181
|
+
.card-header { gap: 4px; }
|
|
182
|
+
.card-title { font-size: 18px; font-weight: bold; }
|
|
183
|
+
.card-description { font-size: 14px; color: var(--muted-foreground); }
|
|
184
|
+
.card-content { gap: 8px; }
|
|
185
|
+
.card-footer { flex-direction: row; gap: 8px; }
|
|
186
|
+
|
|
187
|
+
/* ---- Tabs ---------------------------------------------------------------- */
|
|
188
|
+
|
|
189
|
+
/* Segmented trigger row (shadcn TabsList). Active-trigger styling is driven
|
|
190
|
+
at runtime by ui.bind background/color bindings in author code — runtime
|
|
191
|
+
color swaps are literal hex (compile-time tokens), so pin them to the
|
|
192
|
+
active theme the way native_demo accents do. */
|
|
193
|
+
.tabs-list {
|
|
194
|
+
flex-direction: row;
|
|
195
|
+
gap: 4px;
|
|
196
|
+
align-self: stretch;
|
|
197
|
+
background: var(--muted);
|
|
198
|
+
border-radius: var(--radius);
|
|
199
|
+
padding: 4px;
|
|
200
|
+
}
|
|
201
|
+
.tabs-trigger {
|
|
202
|
+
flex-grow: 1;
|
|
203
|
+
border-radius: calc(var(--radius) - 2px);
|
|
204
|
+
padding: 6px 10px;
|
|
205
|
+
min-height: 32px;
|
|
206
|
+
font-size: 13px;
|
|
207
|
+
text-align: center;
|
|
208
|
+
}
|
|
209
|
+
/* Fixed-height content region: panes are absolutely stacked inside it and
|
|
210
|
+
toggled via ui.bind(x, 'visible', ...) — layout keeps every pane's box,
|
|
211
|
+
so switching never re-flows. */
|
|
212
|
+
.tabs-content-area {
|
|
213
|
+
position: relative;
|
|
214
|
+
align-self: stretch;
|
|
215
|
+
height: 150px;
|
|
216
|
+
}
|
|
217
|
+
.tabs-content {
|
|
218
|
+
position: absolute;
|
|
219
|
+
left: 0;
|
|
220
|
+
top: 0;
|
|
221
|
+
right: 0;
|
|
222
|
+
bottom: 0;
|
|
223
|
+
gap: 8px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* ---- Accordion (single-open) --------------------------------------------- */
|
|
227
|
+
|
|
228
|
+
/* Stacked collapsible sections. The trigger is a button + chevron row; the
|
|
229
|
+
content pane toggles via ui.bind(x, 'visible', () => signal === i) — the
|
|
230
|
+
same signal pattern as Tabs. The chevron swaps v/^ via a text binding. */
|
|
231
|
+
.accordion {
|
|
232
|
+
gap: 8px;
|
|
233
|
+
align-self: stretch;
|
|
234
|
+
}
|
|
235
|
+
.accordion-item {
|
|
236
|
+
background: var(--card);
|
|
237
|
+
border: 1px solid var(--border);
|
|
238
|
+
border-radius: var(--radius);
|
|
239
|
+
}
|
|
240
|
+
.accordion-trigger-row {
|
|
241
|
+
flex-direction: row;
|
|
242
|
+
align-items: center;
|
|
243
|
+
gap: 8px;
|
|
244
|
+
padding: 10px 12px;
|
|
245
|
+
}
|
|
246
|
+
.accordion-trigger {
|
|
247
|
+
flex-grow: 1;
|
|
248
|
+
text-align: left;
|
|
249
|
+
font-weight: bold;
|
|
250
|
+
padding: 0;
|
|
251
|
+
min-height: 20px;
|
|
252
|
+
/* The UA sheet gives every button a 1px border (border: 1px solid, colored
|
|
253
|
+
by the foreground token). A trigger is a plain text row on the card —
|
|
254
|
+
cancel it or each one renders with a bright outline. */
|
|
255
|
+
border: none;
|
|
256
|
+
}
|
|
257
|
+
.accordion-chevron {
|
|
258
|
+
color: var(--muted-foreground);
|
|
259
|
+
font-size: 14px;
|
|
260
|
+
/* Wide enough for the '^' glyph (advance 12px at 14px size) — a single
|
|
261
|
+
char wider than the box wraps to nothing. */
|
|
262
|
+
width: 16px;
|
|
263
|
+
text-align: center;
|
|
264
|
+
}
|
|
265
|
+
.accordion-content {
|
|
266
|
+
padding: 0 12px 10px 12px;
|
|
267
|
+
gap: 8px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* ---- Form controls ----------------------------------------------------------- */
|
|
271
|
+
|
|
272
|
+
.input {
|
|
273
|
+
background: var(--background);
|
|
274
|
+
color: var(--foreground);
|
|
275
|
+
border: 1px solid var(--input);
|
|
276
|
+
border-radius: var(--radius);
|
|
277
|
+
}
|
|
278
|
+
.form-label { color: var(--foreground); font-size: 14px; }
|
|
279
|
+
|
|
280
|
+
/* Switch: pill container. The runtime draws a 16px circular knob that
|
|
281
|
+
slides with state — hollow at the left when off, solid at the right when
|
|
282
|
+
on (a static jump, no travel animation). min-height overrides the UA
|
|
283
|
+
touch target so the pill stays 26px tall. */
|
|
284
|
+
.switch {
|
|
285
|
+
width: 44px;
|
|
286
|
+
height: 26px;
|
|
287
|
+
/* Override the UA's touch-target min-height (42px on small panels) — the
|
|
288
|
+
pill must stay 26px tall to read as a switch, not a checkbox. */
|
|
289
|
+
min-height: 26px;
|
|
290
|
+
border-radius: 999px;
|
|
291
|
+
border: 1px solid var(--border);
|
|
292
|
+
/* --card, not --muted: on a bare (near-black) page background a --muted
|
|
293
|
+
track reads as a stray gray box; --card keeps the pill affordance while
|
|
294
|
+
staying quiet. Swap back to var(--muted) inside lighter containers. */
|
|
295
|
+
background: var(--card);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* ---- Checked-state pairs (shadcn data-[state=checked]) ------------------------
|
|
299
|
+
The ON control carries the primary pair: the switch's TRACK turns primary
|
|
300
|
+
with a primary-foreground knob, the checkbox face and the radio's selected
|
|
301
|
+
ring do the same. The select's pair themes the OPTION LIST's selected row
|
|
302
|
+
with the accent pair (shadcn's SelectItem selected state). These bake at
|
|
303
|
+
build time from :checked rules (a separate style bucket, like :pressed);
|
|
304
|
+
the device runtime swaps the pair onto the indicator when the value flips,
|
|
305
|
+
and the preview draws through the same fields. */
|
|
306
|
+
.switch:checked {
|
|
307
|
+
background: var(--primary);
|
|
308
|
+
border-color: var(--primary);
|
|
309
|
+
color: var(--primary-foreground);
|
|
310
|
+
}
|
|
311
|
+
check:checked {
|
|
312
|
+
background: var(--primary);
|
|
313
|
+
color: var(--primary-foreground);
|
|
314
|
+
}
|
|
315
|
+
radio:checked {
|
|
316
|
+
background: var(--primary);
|
|
317
|
+
color: var(--primary-foreground);
|
|
318
|
+
}
|
|
319
|
+
.select:checked {
|
|
320
|
+
background: var(--accent);
|
|
321
|
+
color: var(--accent-foreground);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* ---- Form validation states -------------------------------------------------- */
|
|
325
|
+
|
|
326
|
+
/* Static hook: always-invalid styling for hardcoded markup. Runtime-driven
|
|
327
|
+
validation (recommended) binds the input's borderColor + a .field-error
|
|
328
|
+
hint's visible/text to a signal — see the demo's Forms screen. */
|
|
329
|
+
.input-error {
|
|
330
|
+
border-color: var(--destructive);
|
|
331
|
+
}
|
|
332
|
+
.field-error {
|
|
333
|
+
color: var(--destructive);
|
|
334
|
+
font-size: 12px;
|
|
335
|
+
}
|
|
336
|
+
.field-success {
|
|
337
|
+
color: var(--muted-foreground);
|
|
338
|
+
font-size: 12px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* ---- Separator ---------------------------------------------------------------- */
|
|
342
|
+
|
|
343
|
+
.separator {
|
|
344
|
+
height: 1px;
|
|
345
|
+
background: var(--border);
|
|
346
|
+
margin: 8px 0;
|
|
347
|
+
}
|
|
348
|
+
/* Vertical: 1px wide, stretches to the row's cross height (shadcn's
|
|
349
|
+
<Separator orientation="vertical" /> — flex column + align-self: stretch). */
|
|
350
|
+
.vseparator {
|
|
351
|
+
width: 1px;
|
|
352
|
+
height: 100%;
|
|
353
|
+
align-self: stretch;
|
|
354
|
+
background: var(--border);
|
|
355
|
+
margin: 0 8px;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* ---- Alert -------------------------------------------------------------------- */
|
|
359
|
+
|
|
360
|
+
.alert {
|
|
361
|
+
background: var(--background);
|
|
362
|
+
color: var(--foreground);
|
|
363
|
+
border: 1px solid var(--border);
|
|
364
|
+
border-radius: var(--radius);
|
|
365
|
+
padding: 12px;
|
|
366
|
+
gap: 4px;
|
|
367
|
+
align-self: stretch;
|
|
368
|
+
}
|
|
369
|
+
.alert-destructive {
|
|
370
|
+
background: var(--destructive-background);
|
|
371
|
+
border: 1px solid var(--destructive);
|
|
372
|
+
color: var(--destructive);
|
|
373
|
+
}
|
|
374
|
+
.alert-title { font-weight: bold; }
|
|
375
|
+
.alert-description { font-size: 14px; color: var(--muted-foreground); }
|
|
376
|
+
|
|
377
|
+
/* ---- Skeleton (loading placeholder) --------------------------------------------- */
|
|
378
|
+
|
|
379
|
+
/* Opacity pulse. If the target display's keyframe support skips opacity,
|
|
380
|
+
the block still renders as a static muted placeholder. */
|
|
381
|
+
.skeleton {
|
|
382
|
+
background: var(--muted);
|
|
383
|
+
border-radius: var(--radius);
|
|
384
|
+
height: 16px;
|
|
385
|
+
align-self: stretch;
|
|
386
|
+
animation: ui-skeleton-pulse 1.2s ease-in-out infinite;
|
|
387
|
+
}
|
|
388
|
+
@keyframes ui-skeleton-pulse {
|
|
389
|
+
0% { opacity: 1; }
|
|
390
|
+
50% { opacity: 0.55; }
|
|
391
|
+
100% { opacity: 1; }
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* ---- Spinner ---------------------------------------------------------------- */
|
|
395
|
+
|
|
396
|
+
/* Indeterminate loading indicator: a dot orbiting inside a ring, pure
|
|
397
|
+
transform keyframes. translate() lerps CONTINUOUSLY between stops (unlike
|
|
398
|
+
rotate(), which only renders exact quarter turns), so the orbit is smooth.
|
|
399
|
+
The dot is absolute + out of flow; the ring is a fixed square so border
|
|
400
|
+
clipping stays symmetric. Sizes: change .spinner's width/height and keep
|
|
401
|
+
the dot inset consistent (orbit travel = inner - dot). */
|
|
402
|
+
.spinner {
|
|
403
|
+
position: relative;
|
|
404
|
+
width: 22px;
|
|
405
|
+
height: 22px;
|
|
406
|
+
border: 2px solid var(--muted);
|
|
407
|
+
border-radius: 999px;
|
|
408
|
+
}
|
|
409
|
+
.spinner-dot {
|
|
410
|
+
position: absolute;
|
|
411
|
+
/* The dot's BASE position is the orbit's TOP-LEFT corner, not the ring
|
|
412
|
+
center: the keyframes translate 0..8px from here, and the path only
|
|
413
|
+
centers when base + travel/2 == ring center (with the 2px border,
|
|
414
|
+
top/left 2px + border lands the 6px dot at 4,4; its center travels
|
|
415
|
+
7..15 around the 22px ring's center at 11,11). */
|
|
416
|
+
top: 2px;
|
|
417
|
+
left: 2px;
|
|
418
|
+
width: 6px;
|
|
419
|
+
height: 6px;
|
|
420
|
+
background: var(--primary);
|
|
421
|
+
border-radius: 999px;
|
|
422
|
+
animation: ui-spinner-orbit 1000ms linear infinite;
|
|
423
|
+
}
|
|
424
|
+
@keyframes ui-spinner-orbit {
|
|
425
|
+
0% { transform: translate(0px, 0px); }
|
|
426
|
+
25% { transform: translate(8px, 0px); }
|
|
427
|
+
50% { transform: translate(8px, 8px); }
|
|
428
|
+
75% { transform: translate(0px, 8px); }
|
|
429
|
+
100% { transform: translate(0px, 0px); }
|
|
430
|
+
}
|
|
431
|
+
/* ---- Dialog ---------------------------------------------------------------- */
|
|
432
|
+
|
|
433
|
+
/* Centered modal: <dialog> is a centered drawer (side-free) — programmatic
|
|
434
|
+
open/close via ui.dialog.open(id)/close(id?), hidden while closed by the
|
|
435
|
+
runtime (visibility gate for centered panels; offsets can't hide them).
|
|
436
|
+
The scrim is part of the markup: an absolute full-area view whose tap
|
|
437
|
+
closes the dialog; the card sits above it. z-index keeps both over page
|
|
438
|
+
content. */
|
|
439
|
+
.dialog-scrim {
|
|
440
|
+
position: absolute;
|
|
441
|
+
left: 0;
|
|
442
|
+
right: 0;
|
|
443
|
+
top: 0;
|
|
444
|
+
bottom: 0;
|
|
445
|
+
background: var(--scrim);
|
|
446
|
+
z-index: 30;
|
|
447
|
+
}
|
|
448
|
+
.dialog-card {
|
|
449
|
+
position: absolute;
|
|
450
|
+
left: 24px;
|
|
451
|
+
right: 24px;
|
|
452
|
+
top: 64px;
|
|
453
|
+
background: var(--card);
|
|
454
|
+
color: var(--card-foreground);
|
|
455
|
+
border: 1px solid var(--border);
|
|
456
|
+
border-radius: var(--radius);
|
|
457
|
+
box-shadow: var(--shadow-lg);
|
|
458
|
+
padding: 16px;
|
|
459
|
+
gap: 8px;
|
|
460
|
+
z-index: 31;
|
|
461
|
+
}
|
|
462
|
+
.dialog-footer {
|
|
463
|
+
flex-direction: row;
|
|
464
|
+
justify-content: flex-end;
|
|
465
|
+
gap: 8px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/* ---- Toast ----------------------------------------------------------------- */
|
|
469
|
+
|
|
470
|
+
/* Transient notification: <toast side="bottom" duration="2500"> slides from
|
|
471
|
+
the bottom edge (author-positioned, flush to the edge so the slide fully
|
|
472
|
+
hides it) and auto-closes after duration ms. Show with ui.toast(id).
|
|
473
|
+
Sibling toasts stack by author layout; each closes on its own timer. */
|
|
474
|
+
.toast {
|
|
475
|
+
position: absolute;
|
|
476
|
+
left: 12px;
|
|
477
|
+
right: 12px;
|
|
478
|
+
bottom: 0;
|
|
479
|
+
background: var(--foreground);
|
|
480
|
+
color: var(--background);
|
|
481
|
+
border-radius: var(--radius);
|
|
482
|
+
padding: 12px;
|
|
483
|
+
gap: 4px;
|
|
484
|
+
z-index: 40;
|
|
485
|
+
}
|
|
486
|
+
.toast-title { font-weight: bold; font-size: 14px; }
|
|
487
|
+
.toast-description { font-size: 12px; color: var(--muted-foreground); }
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
/* ---- Progress / Avatar ----------------------------------------------------------- */
|
|
492
|
+
|
|
493
|
+
.progress {
|
|
494
|
+
height: 12px;
|
|
495
|
+
border-radius: 999px;
|
|
496
|
+
background: var(--secondary);
|
|
497
|
+
border: 1px solid var(--border);
|
|
498
|
+
color: var(--primary);
|
|
499
|
+
}
|
|
500
|
+
/* Avatar: plain image element. The runtime draws images rectangular — no
|
|
501
|
+
rounded clipping — so no border/radius here (a border would just draw
|
|
502
|
+
over the image). Wrap in a sized view if you want a frame. */
|
|
503
|
+
`;
|