create-shopify-firebase-app 1.3.0 → 2.0.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/lib/index.js +672 -456
- package/package.json +2 -2
- package/templates/js/functions/package.json +24 -0
- package/templates/js/functions/src/admin-api.js +292 -0
- package/templates/js/functions/src/auth.js +147 -0
- package/templates/js/functions/src/config.js +14 -0
- package/templates/js/functions/src/firebase.js +12 -0
- package/templates/js/functions/src/index.js +93 -0
- package/templates/js/functions/src/proxy.js +60 -0
- package/templates/js/functions/src/verify-token.js +39 -0
- package/templates/js/functions/src/webhooks.js +111 -0
- package/templates/{firebase.json → shared/firebase.json} +1 -0
- package/templates/shopify.app.toml +1 -1
- package/templates/ts/functions/src/admin-api.ts +290 -0
- package/templates/web/css/app.css +1287 -47
- package/templates/web/index.html +84 -49
- package/templates/web/js/app.js +177 -0
- package/templates/web/js/pages/home.js +90 -0
- package/templates/web/js/pages/polaris-demo.js +190 -0
- package/templates/web/js/pages/products.js +319 -0
- package/templates/web/js/pages/settings.js +241 -0
- package/templates/web/polaris.html +1149 -0
- package/templates/web/products.html +86 -0
- package/templates/web/settings.html +40 -0
- package/templates/functions/src/admin-api.ts +0 -125
- package/templates/web/js/bridge.js +0 -98
- /package/templates/{env.example → shared/env.example} +0 -0
- /package/templates/{extensions → shared/extensions}/theme-block/assets/app-block.css +0 -0
- /package/templates/{extensions → shared/extensions}/theme-block/assets/app-block.js +0 -0
- /package/templates/{extensions → shared/extensions}/theme-block/blocks/app-block.liquid +0 -0
- /package/templates/{extensions → shared/extensions}/theme-block/locales/en.default.json +0 -0
- /package/templates/{extensions → shared/extensions}/theme-block/shopify.extension.toml +0 -0
- /package/templates/{firestore.indexes.json → shared/firestore.indexes.json} +0 -0
- /package/templates/{firestore.rules → shared/firestore.rules} +0 -0
- /package/templates/{gitignore → shared/gitignore} +0 -0
- /package/templates/{functions → ts/functions}/package.json +0 -0
- /package/templates/{functions → ts/functions}/src/auth.ts +0 -0
- /package/templates/{functions → ts/functions}/src/config.ts +0 -0
- /package/templates/{functions → ts/functions}/src/firebase.ts +0 -0
- /package/templates/{functions → ts/functions}/src/index.ts +0 -0
- /package/templates/{functions → ts/functions}/src/proxy.ts +0 -0
- /package/templates/{functions → ts/functions}/src/verify-token.ts +0 -0
- /package/templates/{functions → ts/functions}/src/webhooks.ts +0 -0
- /package/templates/{functions → ts/functions}/tsconfig.json +0 -0
|
@@ -0,0 +1,1149 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Components - {{APP_NAME}}</title>
|
|
7
|
+
<link rel="stylesheet" href="/css/app.css">
|
|
8
|
+
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<ui-nav-menu>
|
|
12
|
+
<a href="/" rel="home">Dashboard</a>
|
|
13
|
+
<a href="/products">Products</a>
|
|
14
|
+
<a href="/settings">Settings</a>
|
|
15
|
+
<a href="/polaris">Components</a>
|
|
16
|
+
</ui-nav-menu>
|
|
17
|
+
|
|
18
|
+
<ui-title-bar title="Component Reference"></ui-title-bar>
|
|
19
|
+
|
|
20
|
+
<div class="page page-wide">
|
|
21
|
+
<!-- Intro banner -->
|
|
22
|
+
<div class="card" style="border-left: 4px solid var(--p-color-primary);">
|
|
23
|
+
<h2>UI Component Reference</h2>
|
|
24
|
+
<p>A catalog of all available UI components styled to match the Shopify admin. Copy code snippets directly into your pages. Each component uses the Polaris-inspired CSS classes from <code>app.css</code>.</p>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<!-- Table of contents -->
|
|
28
|
+
<div class="card">
|
|
29
|
+
<h2>Quick Navigation</h2>
|
|
30
|
+
<div class="flex flex-wrap gap-2 mt-3">
|
|
31
|
+
<a href="#section-buttons" class="btn btn-sm">Buttons</a>
|
|
32
|
+
<a href="#section-badges" class="btn btn-sm">Badges</a>
|
|
33
|
+
<a href="#section-cards" class="btn btn-sm">Cards</a>
|
|
34
|
+
<a href="#section-forms" class="btn btn-sm">Forms</a>
|
|
35
|
+
<a href="#section-tables" class="btn btn-sm">Tables</a>
|
|
36
|
+
<a href="#section-banners" class="btn btn-sm">Banners</a>
|
|
37
|
+
<a href="#section-empty-state" class="btn btn-sm">Empty State</a>
|
|
38
|
+
<a href="#section-loading" class="btn btn-sm">Loading</a>
|
|
39
|
+
<a href="#section-modals" class="btn btn-sm">Modals</a>
|
|
40
|
+
<a href="#section-toasts" class="btn btn-sm">Toasts</a>
|
|
41
|
+
<a href="#section-layout" class="btn btn-sm">Layout</a>
|
|
42
|
+
<a href="#section-resource-picker" class="btn btn-sm">Resource Picker</a>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
48
|
+
<!-- SECTION 1: BUTTONS -->
|
|
49
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
50
|
+
<div class="demo-section" id="section-buttons">
|
|
51
|
+
<h2 class="demo-section-title">Buttons</h2>
|
|
52
|
+
|
|
53
|
+
<!-- Primary, Secondary, Destructive -->
|
|
54
|
+
<div class="demo-block">
|
|
55
|
+
<div class="demo-block-title">Button Variants</div>
|
|
56
|
+
<p class="demo-description mt-3">Use different button styles to indicate hierarchy and intent.</p>
|
|
57
|
+
<div class="demo-preview">
|
|
58
|
+
<div class="btn-group">
|
|
59
|
+
<button class="btn btn-primary">Primary</button>
|
|
60
|
+
<button class="btn">Secondary</button>
|
|
61
|
+
<button class="btn btn-destructive">Destructive</button>
|
|
62
|
+
<button class="btn btn-outline">Outline</button>
|
|
63
|
+
<button class="btn btn-plain">Plain</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="demo-code">
|
|
67
|
+
<div class="code-block-header">
|
|
68
|
+
<span>HTML</span>
|
|
69
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
70
|
+
</div>
|
|
71
|
+
<pre><code><button class="btn btn-primary">Primary</button>
|
|
72
|
+
<button class="btn">Secondary</button>
|
|
73
|
+
<button class="btn btn-destructive">Destructive</button>
|
|
74
|
+
<button class="btn btn-outline">Outline</button>
|
|
75
|
+
<button class="btn btn-plain">Plain</button></code></pre>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Sizes -->
|
|
80
|
+
<div class="demo-block">
|
|
81
|
+
<div class="demo-block-title">Button Sizes</div>
|
|
82
|
+
<div class="demo-preview">
|
|
83
|
+
<div class="btn-group items-center">
|
|
84
|
+
<button class="btn btn-primary btn-sm">Small</button>
|
|
85
|
+
<button class="btn btn-primary">Default</button>
|
|
86
|
+
<button class="btn btn-primary btn-lg">Large</button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="demo-code">
|
|
90
|
+
<div class="code-block-header">
|
|
91
|
+
<span>HTML</span>
|
|
92
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
93
|
+
</div>
|
|
94
|
+
<pre><code><button class="btn btn-primary btn-sm">Small</button>
|
|
95
|
+
<button class="btn btn-primary">Default</button>
|
|
96
|
+
<button class="btn btn-primary btn-lg">Large</button></code></pre>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- Disabled & Loading -->
|
|
101
|
+
<div class="demo-block">
|
|
102
|
+
<div class="demo-block-title">Disabled & Loading</div>
|
|
103
|
+
<div class="demo-preview">
|
|
104
|
+
<div class="btn-group">
|
|
105
|
+
<button class="btn btn-primary" disabled>Disabled</button>
|
|
106
|
+
<button class="btn btn-primary btn-loading">Loading</button>
|
|
107
|
+
<button class="btn btn-loading">Loading</button>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="demo-code">
|
|
111
|
+
<div class="code-block-header">
|
|
112
|
+
<span>HTML</span>
|
|
113
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
114
|
+
</div>
|
|
115
|
+
<pre><code><button class="btn btn-primary" disabled>Disabled</button>
|
|
116
|
+
<button class="btn btn-primary btn-loading">Loading</button>
|
|
117
|
+
<button class="btn btn-loading">Loading</button></code></pre>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
124
|
+
<!-- SECTION 2: BADGES -->
|
|
125
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
126
|
+
<div class="demo-section" id="section-badges">
|
|
127
|
+
<h2 class="demo-section-title">Badges</h2>
|
|
128
|
+
|
|
129
|
+
<div class="demo-block">
|
|
130
|
+
<div class="demo-block-title">Badge Variants</div>
|
|
131
|
+
<p class="demo-description mt-3">Badges communicate status, labels, and metadata in a compact format.</p>
|
|
132
|
+
<div class="demo-preview">
|
|
133
|
+
<div class="flex flex-wrap gap-3 items-center">
|
|
134
|
+
<span class="badge badge-default">Default</span>
|
|
135
|
+
<span class="badge badge-success">Success</span>
|
|
136
|
+
<span class="badge badge-warning">Warning</span>
|
|
137
|
+
<span class="badge badge-critical">Critical</span>
|
|
138
|
+
<span class="badge badge-info">Info</span>
|
|
139
|
+
<span class="badge badge-attention">Attention</span>
|
|
140
|
+
<span class="badge badge-new">New</span>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="demo-code">
|
|
144
|
+
<div class="code-block-header">
|
|
145
|
+
<span>HTML</span>
|
|
146
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
147
|
+
</div>
|
|
148
|
+
<pre><code><span class="badge badge-default">Default</span>
|
|
149
|
+
<span class="badge badge-success">Success</span>
|
|
150
|
+
<span class="badge badge-warning">Warning</span>
|
|
151
|
+
<span class="badge badge-critical">Critical</span>
|
|
152
|
+
<span class="badge badge-info">Info</span>
|
|
153
|
+
<span class="badge badge-attention">Attention</span>
|
|
154
|
+
<span class="badge badge-new">New</span></code></pre>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="demo-block">
|
|
159
|
+
<div class="demo-block-title">Badges in Context</div>
|
|
160
|
+
<div class="demo-preview">
|
|
161
|
+
<ul class="info-list">
|
|
162
|
+
<li>
|
|
163
|
+
<span class="info-list-label">Order #1001</span>
|
|
164
|
+
<span class="badge badge-success">Fulfilled</span>
|
|
165
|
+
</li>
|
|
166
|
+
<li>
|
|
167
|
+
<span class="info-list-label">Order #1002</span>
|
|
168
|
+
<span class="badge badge-warning">Pending</span>
|
|
169
|
+
</li>
|
|
170
|
+
<li>
|
|
171
|
+
<span class="info-list-label">Order #1003</span>
|
|
172
|
+
<span class="badge badge-critical">Cancelled</span>
|
|
173
|
+
</li>
|
|
174
|
+
</ul>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="demo-code">
|
|
177
|
+
<div class="code-block-header">
|
|
178
|
+
<span>HTML</span>
|
|
179
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
180
|
+
</div>
|
|
181
|
+
<pre><code><ul class="info-list">
|
|
182
|
+
<li>
|
|
183
|
+
<span class="info-list-label">Order #1001</span>
|
|
184
|
+
<span class="badge badge-success">Fulfilled</span>
|
|
185
|
+
</li>
|
|
186
|
+
<li>
|
|
187
|
+
<span class="info-list-label">Order #1002</span>
|
|
188
|
+
<span class="badge badge-warning">Pending</span>
|
|
189
|
+
</li>
|
|
190
|
+
</ul></code></pre>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
197
|
+
<!-- SECTION 3: CARDS -->
|
|
198
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
199
|
+
<div class="demo-section" id="section-cards">
|
|
200
|
+
<h2 class="demo-section-title">Cards</h2>
|
|
201
|
+
|
|
202
|
+
<!-- Basic card -->
|
|
203
|
+
<div class="demo-block">
|
|
204
|
+
<div class="demo-block-title">Basic Card</div>
|
|
205
|
+
<p class="demo-description mt-3">Cards are the primary container for content. Use them to group related information.</p>
|
|
206
|
+
<div class="demo-preview">
|
|
207
|
+
<div class="card" style="margin-bottom:0;">
|
|
208
|
+
<h2>Card Title</h2>
|
|
209
|
+
<p>This is a basic card with a title and body text. Cards automatically get padding, a border, and a subtle shadow.</p>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
<div class="demo-code">
|
|
213
|
+
<div class="code-block-header">
|
|
214
|
+
<span>HTML</span>
|
|
215
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
216
|
+
</div>
|
|
217
|
+
<pre><code><div class="card">
|
|
218
|
+
<h2>Card Title</h2>
|
|
219
|
+
<p>Card body text goes here.</p>
|
|
220
|
+
</div></code></pre>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<!-- Card with sections -->
|
|
225
|
+
<div class="demo-block">
|
|
226
|
+
<div class="demo-block-title">Card with Sections</div>
|
|
227
|
+
<div class="demo-preview">
|
|
228
|
+
<div class="card card-flush" style="margin-bottom:0;">
|
|
229
|
+
<div class="card-section">
|
|
230
|
+
<h3>Section One</h3>
|
|
231
|
+
<p>First section content with separated borders.</p>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="card-section">
|
|
234
|
+
<h3>Section Two</h3>
|
|
235
|
+
<p>Second section, visually separated from the first.</p>
|
|
236
|
+
</div>
|
|
237
|
+
<div class="card-section">
|
|
238
|
+
<h3>Section Three</h3>
|
|
239
|
+
<p>Third section with its own content area.</p>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
<div class="demo-code">
|
|
244
|
+
<div class="code-block-header">
|
|
245
|
+
<span>HTML</span>
|
|
246
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
247
|
+
</div>
|
|
248
|
+
<pre><code><div class="card card-flush">
|
|
249
|
+
<div class="card-section">
|
|
250
|
+
<h3>Section One</h3>
|
|
251
|
+
<p>First section content.</p>
|
|
252
|
+
</div>
|
|
253
|
+
<div class="card-section">
|
|
254
|
+
<h3>Section Two</h3>
|
|
255
|
+
<p>Second section content.</p>
|
|
256
|
+
</div>
|
|
257
|
+
</div></code></pre>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<!-- Card with header actions -->
|
|
262
|
+
<div class="demo-block">
|
|
263
|
+
<div class="demo-block-title">Card with Header Actions</div>
|
|
264
|
+
<div class="demo-preview">
|
|
265
|
+
<div class="card" style="margin-bottom:0;">
|
|
266
|
+
<div class="card-header">
|
|
267
|
+
<h2>Recent Orders</h2>
|
|
268
|
+
<button class="btn btn-sm btn-plain">View all</button>
|
|
269
|
+
</div>
|
|
270
|
+
<ul class="info-list">
|
|
271
|
+
<li>
|
|
272
|
+
<span class="info-list-label">Order #1234</span>
|
|
273
|
+
<span class="info-list-value">$149.00</span>
|
|
274
|
+
</li>
|
|
275
|
+
<li>
|
|
276
|
+
<span class="info-list-label">Order #1235</span>
|
|
277
|
+
<span class="info-list-value">$89.50</span>
|
|
278
|
+
</li>
|
|
279
|
+
</ul>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
<div class="demo-code">
|
|
283
|
+
<div class="code-block-header">
|
|
284
|
+
<span>HTML</span>
|
|
285
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
286
|
+
</div>
|
|
287
|
+
<pre><code><div class="card">
|
|
288
|
+
<div class="card-header">
|
|
289
|
+
<h2>Recent Orders</h2>
|
|
290
|
+
<button class="btn btn-sm btn-plain">View all</button>
|
|
291
|
+
</div>
|
|
292
|
+
<ul class="info-list">
|
|
293
|
+
<li>
|
|
294
|
+
<span class="info-list-label">Order #1234</span>
|
|
295
|
+
<span class="info-list-value">$149.00</span>
|
|
296
|
+
</li>
|
|
297
|
+
</ul>
|
|
298
|
+
</div></code></pre>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<!-- Card grid -->
|
|
303
|
+
<div class="demo-block">
|
|
304
|
+
<div class="demo-block-title">Card Grid</div>
|
|
305
|
+
<div class="demo-preview">
|
|
306
|
+
<div class="card-grid">
|
|
307
|
+
<div class="card" style="margin-bottom:0;">
|
|
308
|
+
<h3>Card A</h3>
|
|
309
|
+
<p>Responsive grid layout for multiple cards.</p>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="card" style="margin-bottom:0;">
|
|
312
|
+
<h3>Card B</h3>
|
|
313
|
+
<p>Cards automatically wrap on smaller screens.</p>
|
|
314
|
+
</div>
|
|
315
|
+
<div class="card" style="margin-bottom:0;">
|
|
316
|
+
<h3>Card C</h3>
|
|
317
|
+
<p>Use <code>card-grid</code> for equal-width columns.</p>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="demo-code">
|
|
322
|
+
<div class="code-block-header">
|
|
323
|
+
<span>HTML</span>
|
|
324
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
325
|
+
</div>
|
|
326
|
+
<pre><code><div class="card-grid">
|
|
327
|
+
<div class="card">
|
|
328
|
+
<h3>Card A</h3>
|
|
329
|
+
<p>Content here.</p>
|
|
330
|
+
</div>
|
|
331
|
+
<div class="card">
|
|
332
|
+
<h3>Card B</h3>
|
|
333
|
+
<p>Content here.</p>
|
|
334
|
+
</div>
|
|
335
|
+
</div></code></pre>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
342
|
+
<!-- SECTION 4: FORMS -->
|
|
343
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
344
|
+
<div class="demo-section" id="section-forms">
|
|
345
|
+
<h2 class="demo-section-title">Forms</h2>
|
|
346
|
+
|
|
347
|
+
<!-- Text input -->
|
|
348
|
+
<div class="demo-block">
|
|
349
|
+
<div class="demo-block-title">Text Inputs</div>
|
|
350
|
+
<p class="demo-description mt-3">Standard form inputs with labels, hints, and validation states.</p>
|
|
351
|
+
<div class="demo-preview">
|
|
352
|
+
<div class="form-group">
|
|
353
|
+
<label for="demo-name">Store name</label>
|
|
354
|
+
<input type="text" class="form-input" id="demo-name" placeholder="My Awesome Store" value="">
|
|
355
|
+
<p class="form-hint">The public-facing name of your store.</p>
|
|
356
|
+
</div>
|
|
357
|
+
<div class="form-group">
|
|
358
|
+
<label for="demo-email">Email (error state)</label>
|
|
359
|
+
<input type="email" class="form-input form-input-error" id="demo-email" value="not-valid">
|
|
360
|
+
<p class="form-error">Please enter a valid email address.</p>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
<div class="demo-code">
|
|
364
|
+
<div class="code-block-header">
|
|
365
|
+
<span>HTML</span>
|
|
366
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
367
|
+
</div>
|
|
368
|
+
<pre><code><div class="form-group">
|
|
369
|
+
<label for="name">Store name</label>
|
|
370
|
+
<input type="text" class="form-input" id="name"
|
|
371
|
+
placeholder="My Awesome Store">
|
|
372
|
+
<p class="form-hint">Help text here.</p>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
<!-- Error state -->
|
|
376
|
+
<div class="form-group">
|
|
377
|
+
<label for="email">Email</label>
|
|
378
|
+
<input type="email" class="form-input form-input-error"
|
|
379
|
+
id="email" value="not-valid">
|
|
380
|
+
<p class="form-error">Please enter a valid email.</p>
|
|
381
|
+
</div></code></pre>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
<!-- Select -->
|
|
386
|
+
<div class="demo-block">
|
|
387
|
+
<div class="demo-block-title">Select</div>
|
|
388
|
+
<div class="demo-preview">
|
|
389
|
+
<div class="form-group">
|
|
390
|
+
<label for="demo-country">Country</label>
|
|
391
|
+
<select class="form-select" id="demo-country">
|
|
392
|
+
<option value="">Select a country</option>
|
|
393
|
+
<option value="us">United States</option>
|
|
394
|
+
<option value="ca">Canada</option>
|
|
395
|
+
<option value="gb">United Kingdom</option>
|
|
396
|
+
<option value="au">Australia</option>
|
|
397
|
+
</select>
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
<div class="demo-code">
|
|
401
|
+
<div class="code-block-header">
|
|
402
|
+
<span>HTML</span>
|
|
403
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
404
|
+
</div>
|
|
405
|
+
<pre><code><div class="form-group">
|
|
406
|
+
<label for="country">Country</label>
|
|
407
|
+
<select class="form-select" id="country">
|
|
408
|
+
<option value="">Select a country</option>
|
|
409
|
+
<option value="us">United States</option>
|
|
410
|
+
<option value="ca">Canada</option>
|
|
411
|
+
</select>
|
|
412
|
+
</div></code></pre>
|
|
413
|
+
</div>
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
<!-- Checkbox & Radio -->
|
|
417
|
+
<div class="demo-block">
|
|
418
|
+
<div class="demo-block-title">Checkboxes & Radios</div>
|
|
419
|
+
<div class="demo-preview">
|
|
420
|
+
<div class="form-group">
|
|
421
|
+
<label class="form-checkbox">
|
|
422
|
+
<input type="checkbox" checked>
|
|
423
|
+
<span>Enable email notifications</span>
|
|
424
|
+
</label>
|
|
425
|
+
</div>
|
|
426
|
+
<div class="form-group">
|
|
427
|
+
<label class="form-checkbox">
|
|
428
|
+
<input type="checkbox">
|
|
429
|
+
<span>Subscribe to newsletter</span>
|
|
430
|
+
</label>
|
|
431
|
+
</div>
|
|
432
|
+
<hr class="divider">
|
|
433
|
+
<div class="form-group">
|
|
434
|
+
<label class="form-checkbox">
|
|
435
|
+
<input type="radio" name="demo-radio" value="option1" checked>
|
|
436
|
+
<span>Option A</span>
|
|
437
|
+
</label>
|
|
438
|
+
</div>
|
|
439
|
+
<div class="form-group">
|
|
440
|
+
<label class="form-checkbox">
|
|
441
|
+
<input type="radio" name="demo-radio" value="option2">
|
|
442
|
+
<span>Option B</span>
|
|
443
|
+
</label>
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
<div class="demo-code">
|
|
447
|
+
<div class="code-block-header">
|
|
448
|
+
<span>HTML</span>
|
|
449
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
450
|
+
</div>
|
|
451
|
+
<pre><code><label class="form-checkbox">
|
|
452
|
+
<input type="checkbox" checked>
|
|
453
|
+
<span>Enable notifications</span>
|
|
454
|
+
</label>
|
|
455
|
+
|
|
456
|
+
<label class="form-checkbox">
|
|
457
|
+
<input type="radio" name="group" value="a" checked>
|
|
458
|
+
<span>Option A</span>
|
|
459
|
+
</label></code></pre>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
<!-- Textarea -->
|
|
464
|
+
<div class="demo-block">
|
|
465
|
+
<div class="demo-block-title">Textarea</div>
|
|
466
|
+
<div class="demo-preview">
|
|
467
|
+
<div class="form-group">
|
|
468
|
+
<label for="demo-notes">Order notes</label>
|
|
469
|
+
<textarea class="form-textarea" id="demo-notes" rows="4" placeholder="Add any notes about this order..."></textarea>
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
<div class="demo-code">
|
|
473
|
+
<div class="code-block-header">
|
|
474
|
+
<span>HTML</span>
|
|
475
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
476
|
+
</div>
|
|
477
|
+
<pre><code><div class="form-group">
|
|
478
|
+
<label for="notes">Order notes</label>
|
|
479
|
+
<textarea class="form-textarea" id="notes"
|
|
480
|
+
rows="4" placeholder="Add notes..."></textarea>
|
|
481
|
+
</div></code></pre>
|
|
482
|
+
</div>
|
|
483
|
+
</div>
|
|
484
|
+
</div>
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
488
|
+
<!-- SECTION 5: TABLES -->
|
|
489
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
490
|
+
<div class="demo-section" id="section-tables">
|
|
491
|
+
<h2 class="demo-section-title">Tables</h2>
|
|
492
|
+
|
|
493
|
+
<div class="demo-block">
|
|
494
|
+
<div class="demo-block-title">Data Table</div>
|
|
495
|
+
<p class="demo-description mt-3">Tables display structured data with alternating row hover and action buttons.</p>
|
|
496
|
+
<div class="demo-preview">
|
|
497
|
+
<div class="table-container">
|
|
498
|
+
<table>
|
|
499
|
+
<thead>
|
|
500
|
+
<tr>
|
|
501
|
+
<th>Product</th>
|
|
502
|
+
<th>SKU</th>
|
|
503
|
+
<th>Price</th>
|
|
504
|
+
<th>Status</th>
|
|
505
|
+
<th>Actions</th>
|
|
506
|
+
</tr>
|
|
507
|
+
</thead>
|
|
508
|
+
<tbody>
|
|
509
|
+
<tr>
|
|
510
|
+
<td><strong>Classic Leather Jacket</strong></td>
|
|
511
|
+
<td><code>CLJ-001</code></td>
|
|
512
|
+
<td>$249.00</td>
|
|
513
|
+
<td><span class="badge badge-success">Active</span></td>
|
|
514
|
+
<td><div class="table-actions"><button class="btn btn-sm">Edit</button><button class="btn btn-sm btn-plain">Delete</button></div></td>
|
|
515
|
+
</tr>
|
|
516
|
+
<tr>
|
|
517
|
+
<td><strong>Wool Scarf</strong></td>
|
|
518
|
+
<td><code>WS-042</code></td>
|
|
519
|
+
<td>$59.00</td>
|
|
520
|
+
<td><span class="badge badge-success">Active</span></td>
|
|
521
|
+
<td><div class="table-actions"><button class="btn btn-sm">Edit</button><button class="btn btn-sm btn-plain">Delete</button></div></td>
|
|
522
|
+
</tr>
|
|
523
|
+
<tr>
|
|
524
|
+
<td><strong>Running Shoes</strong></td>
|
|
525
|
+
<td><code>RS-118</code></td>
|
|
526
|
+
<td>$129.00</td>
|
|
527
|
+
<td><span class="badge badge-warning">Draft</span></td>
|
|
528
|
+
<td><div class="table-actions"><button class="btn btn-sm">Edit</button><button class="btn btn-sm btn-plain">Delete</button></div></td>
|
|
529
|
+
</tr>
|
|
530
|
+
</tbody>
|
|
531
|
+
</table>
|
|
532
|
+
</div>
|
|
533
|
+
</div>
|
|
534
|
+
<div class="demo-code">
|
|
535
|
+
<div class="code-block-header">
|
|
536
|
+
<span>HTML</span>
|
|
537
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
538
|
+
</div>
|
|
539
|
+
<pre><code><div class="table-container">
|
|
540
|
+
<table>
|
|
541
|
+
<thead>
|
|
542
|
+
<tr>
|
|
543
|
+
<th>Product</th>
|
|
544
|
+
<th>SKU</th>
|
|
545
|
+
<th>Price</th>
|
|
546
|
+
<th>Status</th>
|
|
547
|
+
<th>Actions</th>
|
|
548
|
+
</tr>
|
|
549
|
+
</thead>
|
|
550
|
+
<tbody>
|
|
551
|
+
<tr>
|
|
552
|
+
<td><strong>Product Name</strong></td>
|
|
553
|
+
<td><code>SKU-001</code></td>
|
|
554
|
+
<td>$99.00</td>
|
|
555
|
+
<td><span class="badge badge-success">Active</span></td>
|
|
556
|
+
<td>
|
|
557
|
+
<div class="table-actions">
|
|
558
|
+
<button class="btn btn-sm">Edit</button>
|
|
559
|
+
</div>
|
|
560
|
+
</td>
|
|
561
|
+
</tr>
|
|
562
|
+
</tbody>
|
|
563
|
+
</table>
|
|
564
|
+
</div></code></pre>
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
|
|
568
|
+
<!-- Sortable headers -->
|
|
569
|
+
<div class="demo-block">
|
|
570
|
+
<div class="demo-block-title">Sortable Table Headers</div>
|
|
571
|
+
<div class="demo-preview">
|
|
572
|
+
<div class="table-container">
|
|
573
|
+
<table>
|
|
574
|
+
<thead>
|
|
575
|
+
<tr>
|
|
576
|
+
<th class="sortable">Name</th>
|
|
577
|
+
<th class="sortable">Date</th>
|
|
578
|
+
<th class="sortable">Amount</th>
|
|
579
|
+
</tr>
|
|
580
|
+
</thead>
|
|
581
|
+
<tbody>
|
|
582
|
+
<tr><td>Alice Johnson</td><td>Mar 15, 2026</td><td>$1,240.00</td></tr>
|
|
583
|
+
<tr><td>Bob Smith</td><td>Mar 12, 2026</td><td>$890.50</td></tr>
|
|
584
|
+
<tr><td>Carol White</td><td>Mar 10, 2026</td><td>$2,100.00</td></tr>
|
|
585
|
+
</tbody>
|
|
586
|
+
</table>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
589
|
+
<div class="demo-code">
|
|
590
|
+
<div class="code-block-header">
|
|
591
|
+
<span>HTML</span>
|
|
592
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
593
|
+
</div>
|
|
594
|
+
<pre><code><th class="sortable">Name</th>
|
|
595
|
+
<th class="sortable">Date</th>
|
|
596
|
+
<th class="sortable">Amount</th></code></pre>
|
|
597
|
+
</div>
|
|
598
|
+
</div>
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
603
|
+
<!-- SECTION 6: BANNERS -->
|
|
604
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
605
|
+
<div class="demo-section" id="section-banners">
|
|
606
|
+
<h2 class="demo-section-title">Banners</h2>
|
|
607
|
+
|
|
608
|
+
<div class="demo-block">
|
|
609
|
+
<div class="demo-block-title">Banner Variants</div>
|
|
610
|
+
<p class="demo-description mt-3">Use banners to communicate important information, status, or errors.</p>
|
|
611
|
+
<div class="demo-preview" id="banners-preview">
|
|
612
|
+
<div class="banner banner-info">
|
|
613
|
+
<div class="banner-icon">ℹ</div>
|
|
614
|
+
<div class="banner-content">
|
|
615
|
+
<p><strong>Information</strong></p>
|
|
616
|
+
<p>This is an informational banner. Use it for tips or neutral messages.</p>
|
|
617
|
+
</div>
|
|
618
|
+
<button class="banner-dismiss" onclick="this.closest('.banner').remove()">×</button>
|
|
619
|
+
</div>
|
|
620
|
+
|
|
621
|
+
<div class="banner banner-success">
|
|
622
|
+
<div class="banner-icon">✓</div>
|
|
623
|
+
<div class="banner-content">
|
|
624
|
+
<p><strong>Success</strong></p>
|
|
625
|
+
<p>The operation completed successfully. Your changes have been saved.</p>
|
|
626
|
+
</div>
|
|
627
|
+
<button class="banner-dismiss" onclick="this.closest('.banner').remove()">×</button>
|
|
628
|
+
</div>
|
|
629
|
+
|
|
630
|
+
<div class="banner banner-warning">
|
|
631
|
+
<div class="banner-icon">⚠</div>
|
|
632
|
+
<div class="banner-content">
|
|
633
|
+
<p><strong>Warning</strong></p>
|
|
634
|
+
<p>This action may have unintended consequences. Please review before proceeding.</p>
|
|
635
|
+
</div>
|
|
636
|
+
<button class="banner-dismiss" onclick="this.closest('.banner').remove()">×</button>
|
|
637
|
+
</div>
|
|
638
|
+
|
|
639
|
+
<div class="banner banner-critical">
|
|
640
|
+
<div class="banner-icon">✖</div>
|
|
641
|
+
<div class="banner-content">
|
|
642
|
+
<p><strong>Error</strong></p>
|
|
643
|
+
<p>Something went wrong. Please try again or contact support.</p>
|
|
644
|
+
</div>
|
|
645
|
+
<button class="banner-dismiss" onclick="this.closest('.banner').remove()">×</button>
|
|
646
|
+
</div>
|
|
647
|
+
|
|
648
|
+
<button class="btn btn-sm mt-3" onclick="resetBanners()">Reset banners</button>
|
|
649
|
+
</div>
|
|
650
|
+
<div class="demo-code">
|
|
651
|
+
<div class="code-block-header">
|
|
652
|
+
<span>HTML</span>
|
|
653
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
654
|
+
</div>
|
|
655
|
+
<pre><code><div class="banner banner-info">
|
|
656
|
+
<div class="banner-icon">&#8505;</div>
|
|
657
|
+
<div class="banner-content">
|
|
658
|
+
<p><strong>Information</strong></p>
|
|
659
|
+
<p>Your message here.</p>
|
|
660
|
+
</div>
|
|
661
|
+
<button class="banner-dismiss"
|
|
662
|
+
onclick="this.closest('.banner').remove()">&times;</button>
|
|
663
|
+
</div>
|
|
664
|
+
|
|
665
|
+
<!-- Variants: banner-info, banner-success,
|
|
666
|
+
banner-warning, banner-critical --></code></pre>
|
|
667
|
+
</div>
|
|
668
|
+
</div>
|
|
669
|
+
</div>
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
673
|
+
<!-- SECTION 7: EMPTY STATE -->
|
|
674
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
675
|
+
<div class="demo-section" id="section-empty-state">
|
|
676
|
+
<h2 class="demo-section-title">Empty State</h2>
|
|
677
|
+
|
|
678
|
+
<div class="demo-block">
|
|
679
|
+
<div class="demo-block-title">Empty State with CTA</div>
|
|
680
|
+
<p class="demo-description mt-3">Show an empty state when there is no data to display. Include a clear call to action.</p>
|
|
681
|
+
<div class="demo-preview">
|
|
682
|
+
<div class="empty-state">
|
|
683
|
+
<div class="empty-state-icon">📦</div>
|
|
684
|
+
<h3>No orders yet</h3>
|
|
685
|
+
<p>When customers place orders, they will appear here. Start by adding products to your store.</p>
|
|
686
|
+
<button class="btn btn-primary">Add product</button>
|
|
687
|
+
</div>
|
|
688
|
+
</div>
|
|
689
|
+
<div class="demo-code">
|
|
690
|
+
<div class="code-block-header">
|
|
691
|
+
<span>HTML</span>
|
|
692
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
693
|
+
</div>
|
|
694
|
+
<pre><code><div class="empty-state">
|
|
695
|
+
<div class="empty-state-icon">&#128230;</div>
|
|
696
|
+
<h3>No orders yet</h3>
|
|
697
|
+
<p>Your description text here.</p>
|
|
698
|
+
<button class="btn btn-primary">Add product</button>
|
|
699
|
+
</div></code></pre>
|
|
700
|
+
</div>
|
|
701
|
+
</div>
|
|
702
|
+
</div>
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
706
|
+
<!-- SECTION 8: LOADING -->
|
|
707
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
708
|
+
<div class="demo-section" id="section-loading">
|
|
709
|
+
<h2 class="demo-section-title">Loading</h2>
|
|
710
|
+
|
|
711
|
+
<!-- Spinner -->
|
|
712
|
+
<div class="demo-block">
|
|
713
|
+
<div class="demo-block-title">Spinners</div>
|
|
714
|
+
<p class="demo-description mt-3">Animated spinners indicate an ongoing process.</p>
|
|
715
|
+
<div class="demo-preview">
|
|
716
|
+
<div class="flex items-center gap-5">
|
|
717
|
+
<div>
|
|
718
|
+
<div class="spinner"></div>
|
|
719
|
+
<p class="text-sm text-secondary mt-2">Default</p>
|
|
720
|
+
</div>
|
|
721
|
+
<div>
|
|
722
|
+
<div class="spinner spinner-lg"></div>
|
|
723
|
+
<p class="text-sm text-secondary mt-2">Large</p>
|
|
724
|
+
</div>
|
|
725
|
+
<div class="loading-state" style="padding: 20px;">
|
|
726
|
+
<div class="spinner"></div>
|
|
727
|
+
<p>Loading content...</p>
|
|
728
|
+
</div>
|
|
729
|
+
</div>
|
|
730
|
+
</div>
|
|
731
|
+
<div class="demo-code">
|
|
732
|
+
<div class="code-block-header">
|
|
733
|
+
<span>HTML</span>
|
|
734
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
735
|
+
</div>
|
|
736
|
+
<pre><code><div class="spinner"></div>
|
|
737
|
+
<div class="spinner spinner-lg"></div>
|
|
738
|
+
|
|
739
|
+
<!-- Full loading state -->
|
|
740
|
+
<div class="loading-state">
|
|
741
|
+
<div class="spinner"></div>
|
|
742
|
+
<p>Loading content...</p>
|
|
743
|
+
</div></code></pre>
|
|
744
|
+
</div>
|
|
745
|
+
</div>
|
|
746
|
+
|
|
747
|
+
<!-- Skeleton -->
|
|
748
|
+
<div class="demo-block">
|
|
749
|
+
<div class="demo-block-title">Skeleton Loaders</div>
|
|
750
|
+
<p class="demo-description mt-3">Skeleton loaders provide a placeholder preview of content while it loads.</p>
|
|
751
|
+
<div class="demo-preview">
|
|
752
|
+
<div class="card" style="margin-bottom:0;">
|
|
753
|
+
<div class="skeleton skeleton-heading"></div>
|
|
754
|
+
<div class="skeleton skeleton-text"></div>
|
|
755
|
+
<div class="skeleton skeleton-text"></div>
|
|
756
|
+
<div class="skeleton skeleton-text" style="width: 40%;"></div>
|
|
757
|
+
</div>
|
|
758
|
+
</div>
|
|
759
|
+
<div class="demo-code">
|
|
760
|
+
<div class="code-block-header">
|
|
761
|
+
<span>HTML</span>
|
|
762
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
763
|
+
</div>
|
|
764
|
+
<pre><code><div class="skeleton skeleton-heading"></div>
|
|
765
|
+
<div class="skeleton skeleton-text"></div>
|
|
766
|
+
<div class="skeleton skeleton-text"></div>
|
|
767
|
+
<div class="skeleton skeleton-text"
|
|
768
|
+
style="width: 40%;"></div>
|
|
769
|
+
|
|
770
|
+
<!-- Image skeleton -->
|
|
771
|
+
<div class="skeleton skeleton-image"></div></code></pre>
|
|
772
|
+
</div>
|
|
773
|
+
</div>
|
|
774
|
+
</div>
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
778
|
+
<!-- SECTION 9: MODALS -->
|
|
779
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
780
|
+
<div class="demo-section" id="section-modals">
|
|
781
|
+
<h2 class="demo-section-title">Modals</h2>
|
|
782
|
+
|
|
783
|
+
<div class="demo-block">
|
|
784
|
+
<div class="demo-block-title">Basic Modal</div>
|
|
785
|
+
<p class="demo-description mt-3">App Bridge modals are rendered by Shopify outside your iframe for a native feel. Use <code><ui-modal></code>.</p>
|
|
786
|
+
<div class="demo-preview">
|
|
787
|
+
<button class="btn btn-primary" onclick="document.getElementById('demo-modal-basic').show()">Open Modal</button>
|
|
788
|
+
</div>
|
|
789
|
+
<div class="demo-code">
|
|
790
|
+
<div class="code-block-header">
|
|
791
|
+
<span>HTML</span>
|
|
792
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
793
|
+
</div>
|
|
794
|
+
<pre><code><ui-modal id="my-modal">
|
|
795
|
+
<div style="padding: 16px;">
|
|
796
|
+
<p>Modal content goes here.</p>
|
|
797
|
+
</div>
|
|
798
|
+
<ui-title-bar title="Modal Title">
|
|
799
|
+
<button variant="primary"
|
|
800
|
+
onclick="document.getElementById('my-modal').hide()">
|
|
801
|
+
Done
|
|
802
|
+
</button>
|
|
803
|
+
</ui-title-bar>
|
|
804
|
+
</ui-modal>
|
|
805
|
+
|
|
806
|
+
<!-- Trigger -->
|
|
807
|
+
<button onclick="document.getElementById('my-modal').show()">
|
|
808
|
+
Open Modal
|
|
809
|
+
</button></code></pre>
|
|
810
|
+
</div>
|
|
811
|
+
</div>
|
|
812
|
+
|
|
813
|
+
<!-- Confirmation dialog -->
|
|
814
|
+
<div class="demo-block">
|
|
815
|
+
<div class="demo-block-title">Confirmation Dialog</div>
|
|
816
|
+
<p class="demo-description mt-3">Use a confirmation modal before destructive actions like deleting records.</p>
|
|
817
|
+
<div class="demo-preview">
|
|
818
|
+
<button class="btn btn-destructive" onclick="document.getElementById('demo-modal-confirm').show()">Delete Item</button>
|
|
819
|
+
</div>
|
|
820
|
+
<div class="demo-code">
|
|
821
|
+
<div class="code-block-header">
|
|
822
|
+
<span>HTML</span>
|
|
823
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
824
|
+
</div>
|
|
825
|
+
<pre><code><ui-modal id="confirm-delete">
|
|
826
|
+
<div style="padding: 16px;">
|
|
827
|
+
<p>Are you sure you want to delete this item?
|
|
828
|
+
This action cannot be undone.</p>
|
|
829
|
+
</div>
|
|
830
|
+
<ui-title-bar title="Confirm Deletion">
|
|
831
|
+
<button onclick="document.getElementById('confirm-delete').hide()">
|
|
832
|
+
Cancel
|
|
833
|
+
</button>
|
|
834
|
+
<button variant="primary" tone="critical"
|
|
835
|
+
onclick="handleDelete()">
|
|
836
|
+
Delete
|
|
837
|
+
</button>
|
|
838
|
+
</ui-title-bar>
|
|
839
|
+
</ui-modal></code></pre>
|
|
840
|
+
</div>
|
|
841
|
+
</div>
|
|
842
|
+
</div>
|
|
843
|
+
|
|
844
|
+
<!-- Demo modals (rendered outside the demo blocks) -->
|
|
845
|
+
<ui-modal id="demo-modal-basic">
|
|
846
|
+
<div style="padding: 16px;">
|
|
847
|
+
<h3 style="margin-bottom: 12px;">Hello from a modal!</h3>
|
|
848
|
+
<p>This is an App Bridge modal rendered by Shopify. It appears outside your app's iframe for a seamless, native experience.</p>
|
|
849
|
+
<p style="margin-top: 12px; color: var(--p-color-text-secondary);">Modals are great for forms, confirmations, and displaying details without navigating away.</p>
|
|
850
|
+
</div>
|
|
851
|
+
<ui-title-bar title="Basic Modal">
|
|
852
|
+
<button variant="primary" onclick="document.getElementById('demo-modal-basic').hide()">Done</button>
|
|
853
|
+
</ui-title-bar>
|
|
854
|
+
</ui-modal>
|
|
855
|
+
|
|
856
|
+
<ui-modal id="demo-modal-confirm">
|
|
857
|
+
<div style="padding: 16px;">
|
|
858
|
+
<p>Are you sure you want to delete this item? This action cannot be undone.</p>
|
|
859
|
+
</div>
|
|
860
|
+
<ui-title-bar title="Confirm Deletion">
|
|
861
|
+
<button onclick="document.getElementById('demo-modal-confirm').hide()">Cancel</button>
|
|
862
|
+
<button variant="primary" tone="critical" onclick="demoConfirmDelete()">Delete</button>
|
|
863
|
+
</ui-title-bar>
|
|
864
|
+
</ui-modal>
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
868
|
+
<!-- SECTION 10: TOASTS -->
|
|
869
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
870
|
+
<div class="demo-section" id="section-toasts">
|
|
871
|
+
<h2 class="demo-section-title">Toasts</h2>
|
|
872
|
+
|
|
873
|
+
<div class="demo-block">
|
|
874
|
+
<div class="demo-block-title">Toast Notifications</div>
|
|
875
|
+
<p class="demo-description mt-3">Toasts provide brief, non-blocking feedback. In embedded admin, they use App Bridge. Outside, a CSS fallback renders.</p>
|
|
876
|
+
<div class="demo-preview">
|
|
877
|
+
<div class="btn-group">
|
|
878
|
+
<button class="btn btn-primary" onclick="showToast('Product saved successfully')">Success Toast</button>
|
|
879
|
+
<button class="btn btn-destructive" onclick="showToast('Failed to save product', true)">Error Toast</button>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
<div class="demo-code">
|
|
883
|
+
<div class="code-block-header">
|
|
884
|
+
<span>JavaScript</span>
|
|
885
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
886
|
+
</div>
|
|
887
|
+
<pre><code>// Success toast
|
|
888
|
+
showToast("Product saved successfully");
|
|
889
|
+
|
|
890
|
+
// Error toast
|
|
891
|
+
showToast("Failed to save product", true);
|
|
892
|
+
|
|
893
|
+
// App Bridge native API (also works)
|
|
894
|
+
shopify.toast.show("Message", { duration: 5000 });</code></pre>
|
|
895
|
+
</div>
|
|
896
|
+
</div>
|
|
897
|
+
</div>
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
901
|
+
<!-- SECTION 11: LAYOUT -->
|
|
902
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
903
|
+
<div class="demo-section" id="section-layout">
|
|
904
|
+
<h2 class="demo-section-title">Layout</h2>
|
|
905
|
+
|
|
906
|
+
<!-- Page layout -->
|
|
907
|
+
<div class="demo-block">
|
|
908
|
+
<div class="demo-block-title">Page Widths</div>
|
|
909
|
+
<p class="demo-description mt-3">Three page width options to match your content needs.</p>
|
|
910
|
+
<div class="demo-preview">
|
|
911
|
+
<div style="font-size: 13px; display: flex; flex-direction: column; gap: 8px;">
|
|
912
|
+
<div style="background: var(--p-color-bg-info); padding: 8px 12px; border-radius: 6px; max-width: 720px; margin: 0 auto; width: 100%; text-align: center;">
|
|
913
|
+
<code>.page-narrow</code> - 720px max
|
|
914
|
+
</div>
|
|
915
|
+
<div style="background: var(--p-color-bg-success); padding: 8px 12px; border-radius: 6px; max-width: 960px; margin: 0 auto; width: 100%; text-align: center;">
|
|
916
|
+
<code>.page</code> - 960px max (default)
|
|
917
|
+
</div>
|
|
918
|
+
<div style="background: var(--p-color-bg-warning); padding: 8px 12px; border-radius: 6px; max-width: 100%; text-align: center;">
|
|
919
|
+
<code>.page-wide</code> - 1200px max
|
|
920
|
+
</div>
|
|
921
|
+
</div>
|
|
922
|
+
</div>
|
|
923
|
+
<div class="demo-code">
|
|
924
|
+
<div class="code-block-header">
|
|
925
|
+
<span>HTML</span>
|
|
926
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
927
|
+
</div>
|
|
928
|
+
<pre><code><div class="page">Default width (960px)</div>
|
|
929
|
+
<div class="page-narrow">Narrow (720px)</div>
|
|
930
|
+
<div class="page-wide">Wide (1200px)</div></code></pre>
|
|
931
|
+
</div>
|
|
932
|
+
</div>
|
|
933
|
+
|
|
934
|
+
<!-- Stats grid -->
|
|
935
|
+
<div class="demo-block">
|
|
936
|
+
<div class="demo-block-title">Stats Grid</div>
|
|
937
|
+
<div class="demo-preview">
|
|
938
|
+
<div class="stats-grid" style="margin-bottom: 0;">
|
|
939
|
+
<div class="stat-card" style="margin-bottom:0;">
|
|
940
|
+
<div class="stat-label">Total Sales</div>
|
|
941
|
+
<div class="stat-value">$12,450</div>
|
|
942
|
+
<div class="stat-change stat-change-positive">+12.5%</div>
|
|
943
|
+
</div>
|
|
944
|
+
<div class="stat-card" style="margin-bottom:0;">
|
|
945
|
+
<div class="stat-label">Orders</div>
|
|
946
|
+
<div class="stat-value">156</div>
|
|
947
|
+
<div class="stat-change stat-change-positive">+8.2%</div>
|
|
948
|
+
</div>
|
|
949
|
+
<div class="stat-card" style="margin-bottom:0;">
|
|
950
|
+
<div class="stat-label">Visitors</div>
|
|
951
|
+
<div class="stat-value">3,842</div>
|
|
952
|
+
<div class="stat-change stat-change-negative">-2.1%</div>
|
|
953
|
+
</div>
|
|
954
|
+
<div class="stat-card" style="margin-bottom:0;">
|
|
955
|
+
<div class="stat-label">Conversion</div>
|
|
956
|
+
<div class="stat-value">4.1%</div>
|
|
957
|
+
<div class="stat-change stat-change-positive">+0.3%</div>
|
|
958
|
+
</div>
|
|
959
|
+
</div>
|
|
960
|
+
</div>
|
|
961
|
+
<div class="demo-code">
|
|
962
|
+
<div class="code-block-header">
|
|
963
|
+
<span>HTML</span>
|
|
964
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
965
|
+
</div>
|
|
966
|
+
<pre><code><div class="stats-grid">
|
|
967
|
+
<div class="stat-card">
|
|
968
|
+
<div class="stat-label">Total Sales</div>
|
|
969
|
+
<div class="stat-value">$12,450</div>
|
|
970
|
+
<div class="stat-change stat-change-positive">
|
|
971
|
+
+12.5%
|
|
972
|
+
</div>
|
|
973
|
+
</div>
|
|
974
|
+
<!-- More stat cards... -->
|
|
975
|
+
</div></code></pre>
|
|
976
|
+
</div>
|
|
977
|
+
</div>
|
|
978
|
+
|
|
979
|
+
<!-- Flex utilities -->
|
|
980
|
+
<div class="demo-block">
|
|
981
|
+
<div class="demo-block-title">Flex Utilities</div>
|
|
982
|
+
<div class="demo-preview">
|
|
983
|
+
<div class="flex items-center justify-between p-3" style="background: var(--p-color-bg-surface-hover); border-radius: 8px; margin-bottom: 8px;">
|
|
984
|
+
<span>justify-between</span>
|
|
985
|
+
<button class="btn btn-sm">Action</button>
|
|
986
|
+
</div>
|
|
987
|
+
<div class="flex items-center justify-center gap-3 p-3" style="background: var(--p-color-bg-surface-hover); border-radius: 8px; margin-bottom: 8px;">
|
|
988
|
+
<span>justify-center + gap-3</span>
|
|
989
|
+
<span class="badge badge-info">Centered</span>
|
|
990
|
+
</div>
|
|
991
|
+
<div class="flex flex-wrap gap-2 p-3" style="background: var(--p-color-bg-surface-hover); border-radius: 8px;">
|
|
992
|
+
<span class="badge badge-default">flex-wrap</span>
|
|
993
|
+
<span class="badge badge-default">gap-2</span>
|
|
994
|
+
<span class="badge badge-default">wraps</span>
|
|
995
|
+
<span class="badge badge-default">on</span>
|
|
996
|
+
<span class="badge badge-default">smaller</span>
|
|
997
|
+
<span class="badge badge-default">screens</span>
|
|
998
|
+
</div>
|
|
999
|
+
</div>
|
|
1000
|
+
<div class="demo-code">
|
|
1001
|
+
<div class="code-block-header">
|
|
1002
|
+
<span>HTML</span>
|
|
1003
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
1004
|
+
</div>
|
|
1005
|
+
<pre><code><div class="flex items-center justify-between">
|
|
1006
|
+
<span>Label</span>
|
|
1007
|
+
<button class="btn btn-sm">Action</button>
|
|
1008
|
+
</div>
|
|
1009
|
+
|
|
1010
|
+
<!-- Available: flex, flex-col, flex-wrap,
|
|
1011
|
+
items-center, items-start,
|
|
1012
|
+
justify-between, justify-center, justify-end,
|
|
1013
|
+
gap-1 through gap-5 --></code></pre>
|
|
1014
|
+
</div>
|
|
1015
|
+
</div>
|
|
1016
|
+
|
|
1017
|
+
<!-- Quick Links -->
|
|
1018
|
+
<div class="demo-block">
|
|
1019
|
+
<div class="demo-block-title">Quick Links</div>
|
|
1020
|
+
<div class="demo-preview">
|
|
1021
|
+
<div class="quick-links">
|
|
1022
|
+
<a class="quick-link" href="#">
|
|
1023
|
+
<div class="quick-link-icon">🛒</div>
|
|
1024
|
+
<div class="quick-link-text">
|
|
1025
|
+
<h4>Products</h4>
|
|
1026
|
+
<p>Manage inventory</p>
|
|
1027
|
+
</div>
|
|
1028
|
+
</a>
|
|
1029
|
+
<a class="quick-link" href="#">
|
|
1030
|
+
<div class="quick-link-icon">💰</div>
|
|
1031
|
+
<div class="quick-link-text">
|
|
1032
|
+
<h4>Orders</h4>
|
|
1033
|
+
<p>View recent orders</p>
|
|
1034
|
+
</div>
|
|
1035
|
+
</a>
|
|
1036
|
+
<a class="quick-link" href="#">
|
|
1037
|
+
<div class="quick-link-icon">⚙</div>
|
|
1038
|
+
<div class="quick-link-text">
|
|
1039
|
+
<h4>Settings</h4>
|
|
1040
|
+
<p>Configure your app</p>
|
|
1041
|
+
</div>
|
|
1042
|
+
</a>
|
|
1043
|
+
</div>
|
|
1044
|
+
</div>
|
|
1045
|
+
<div class="demo-code">
|
|
1046
|
+
<div class="code-block-header">
|
|
1047
|
+
<span>HTML</span>
|
|
1048
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
1049
|
+
</div>
|
|
1050
|
+
<pre><code><div class="quick-links">
|
|
1051
|
+
<a class="quick-link" href="/products">
|
|
1052
|
+
<div class="quick-link-icon">&#128722;</div>
|
|
1053
|
+
<div class="quick-link-text">
|
|
1054
|
+
<h4>Products</h4>
|
|
1055
|
+
<p>Manage inventory</p>
|
|
1056
|
+
</div>
|
|
1057
|
+
</a>
|
|
1058
|
+
</div></code></pre>
|
|
1059
|
+
</div>
|
|
1060
|
+
</div>
|
|
1061
|
+
</div>
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
1065
|
+
<!-- SECTION 12: RESOURCE PICKER -->
|
|
1066
|
+
<!-- ═══════════════════════════════════════════════════════════ -->
|
|
1067
|
+
<div class="demo-section" id="section-resource-picker">
|
|
1068
|
+
<h2 class="demo-section-title">Resource Picker</h2>
|
|
1069
|
+
|
|
1070
|
+
<div class="demo-block">
|
|
1071
|
+
<div class="demo-block-title">Product Resource Picker</div>
|
|
1072
|
+
<p class="demo-description mt-3">App Bridge provides a native product picker. Only available when running inside the Shopify admin.</p>
|
|
1073
|
+
<div class="demo-preview">
|
|
1074
|
+
<button class="btn btn-primary" onclick="demoResourcePicker()">Open Product Picker</button>
|
|
1075
|
+
<div id="picker-demo-result" class="mt-4"></div>
|
|
1076
|
+
</div>
|
|
1077
|
+
<div class="demo-code">
|
|
1078
|
+
<div class="code-block-header">
|
|
1079
|
+
<span>JavaScript</span>
|
|
1080
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
1081
|
+
</div>
|
|
1082
|
+
<pre><code>// Open the product resource picker
|
|
1083
|
+
const selected = await shopify.resourcePicker({
|
|
1084
|
+
type: "product",
|
|
1085
|
+
// Optional: allow multiple selection
|
|
1086
|
+
// multiple: true,
|
|
1087
|
+
// Optional: filter
|
|
1088
|
+
// filter: { query: "shirt" },
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
if (selected && selected.length > 0) {
|
|
1092
|
+
console.log("Selected:", selected);
|
|
1093
|
+
selected.forEach(product => {
|
|
1094
|
+
console.log(product.title, product.id);
|
|
1095
|
+
});
|
|
1096
|
+
}</code></pre>
|
|
1097
|
+
</div>
|
|
1098
|
+
</div>
|
|
1099
|
+
|
|
1100
|
+
<!-- API fetch pattern -->
|
|
1101
|
+
<div class="demo-block">
|
|
1102
|
+
<div class="demo-block-title">API Fetch Pattern</div>
|
|
1103
|
+
<p class="demo-description mt-3">Use the <code>apiFetch()</code> helper for all API calls. It handles session tokens automatically.</p>
|
|
1104
|
+
<div class="demo-preview">
|
|
1105
|
+
<button class="btn" onclick="demoApiFetch()">Test API Call</button>
|
|
1106
|
+
</div>
|
|
1107
|
+
<div class="demo-code">
|
|
1108
|
+
<div class="code-block-header">
|
|
1109
|
+
<span>JavaScript</span>
|
|
1110
|
+
<button class="btn-copy" onclick="copyCode(this)">Copy</button>
|
|
1111
|
+
</div>
|
|
1112
|
+
<pre><code>// GET request
|
|
1113
|
+
const shopData = await apiFetch("/api/shop");
|
|
1114
|
+
|
|
1115
|
+
// POST request with body
|
|
1116
|
+
await apiFetch("/api/settings", {
|
|
1117
|
+
method: "POST",
|
|
1118
|
+
body: JSON.stringify({
|
|
1119
|
+
settings: { greeting: "Hello!" }
|
|
1120
|
+
}),
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
// Search with query params
|
|
1124
|
+
const results = await apiFetch(
|
|
1125
|
+
"/api/products/search?q=" + encodeURIComponent(term)
|
|
1126
|
+
);
|
|
1127
|
+
|
|
1128
|
+
// Error handling
|
|
1129
|
+
try {
|
|
1130
|
+
const data = await apiFetch("/api/shop");
|
|
1131
|
+
showToast("Success!");
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
showToast("Error: " + err.message, true);
|
|
1134
|
+
}</code></pre>
|
|
1135
|
+
</div>
|
|
1136
|
+
</div>
|
|
1137
|
+
</div>
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
<!-- Footer -->
|
|
1141
|
+
<div class="card text-center" style="margin-top: 32px;">
|
|
1142
|
+
<p class="text-secondary text-sm">Built with the Shopify App Bridge + Firebase Hosting scaffold. Edit <code>web/css/app.css</code> to customize these components.</p>
|
|
1143
|
+
</div>
|
|
1144
|
+
</div>
|
|
1145
|
+
|
|
1146
|
+
<script src="/js/app.js"></script>
|
|
1147
|
+
<script src="/js/pages/polaris-demo.js"></script>
|
|
1148
|
+
</body>
|
|
1149
|
+
</html>
|