create-shibumi 0.2.3 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-shibumi",
3
- "version": "0.2.3",
3
+ "version": "0.2.7",
4
4
  "description": "Scaffold a Shibumi Stack project: Bun, Hono, Zod, Drizzle, SQLite, Alpine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,7 +27,8 @@
27
27
  "check": "tsc --noEmit",
28
28
  "sync:ship": "bun scripts/sync-ship.ts",
29
29
  "sync:extensions": "bun scripts/sync-extensions.ts",
30
- "verify:packed": "bun scripts/verify-packed.ts"
30
+ "verify:packed": "bun scripts/verify-packed.ts",
31
+ "sync:css": "bun scripts/sync-shibumi-css.ts"
31
32
  },
32
33
  "keywords": [
33
34
  "shibumi",
@@ -1,4 +1,4 @@
1
1
  {
2
- "url": "https://shibumistack.dev/ship/v45.ts",
3
- "sha256": "d9bad68786b288d027bb00b2183583905e266069f4506c34e364d3e93ce77cb5"
2
+ "url": "https://shibumistack.dev/ship/v46.ts",
3
+ "sha256": "bf1a6efb177a8a5e20e36b7e2c0bcec6bc5a8598dfd4f86e7ff129af92a0e648"
4
4
  }
@@ -28,7 +28,7 @@ bun run shibumi add <name> # install an extension (auth, email); --dry-run prev
28
28
 
29
29
  - Environment goes through `src/env.ts`; request input through Zod at each route.
30
30
  - The `SECURITY_HEADERS` set in `src/app.ts` is asserted verbatim by tests on every response class. The CSP allows no inline or eval'd script.
31
- - Add no unauthenticated mutation endpoints (POST/PUT/PATCH/DELETE); tests fail if a route registers one. Write examples live in tests, which insert through `db` directly.
31
+ - Add no unauthenticated mutation endpoints (POST/PUT/PATCH/DELETE); tests fail if a route registers one. The single exception is the demo `POST /api/counter`: one shared clamped row, no body read, per-IP rate limit. Real write examples live in tests, which insert through `db` directly; anything touching user data needs auth first (`bun shi add auth`).
32
32
 
33
33
  ## Database
34
34
 
@@ -4,8 +4,13 @@
4
4
  document.addEventListener("alpine:init", () => {
5
5
  Alpine.data("counter", () => ({
6
6
  count: 0,
7
- inc() {
8
- this.count++;
7
+ async init() {
8
+ const res = await fetch("/api/counter");
9
+ if (res.ok) this.count = (await res.json()).count;
10
+ },
11
+ async inc() {
12
+ const res = await fetch("/api/counter", { method: "POST" });
13
+ if (res.ok) this.count = (await res.json()).count;
9
14
  },
10
15
  }));
11
16
  });
@@ -1,60 +1,144 @@
1
- :root {
2
- color-scheme: light dark;
3
- --paper: #f5f0e4;
4
- --ink: #1b130f;
5
- --accent: #e95f19;
6
- }
1
+ /* App styles on top of the vendored shibumi.css tokens (public/vendor/).
2
+ Edit freely; this file is yours. Tokens: --paper --ink --muted --line
3
+ --accent, type scale --text-xs..--text-2xl, --font-sans/serif/mono. */
7
4
 
8
- @media (prefers-color-scheme: dark) {
9
- :root {
10
- --paper: #1b130f;
11
- --ink: #f5f0e4;
12
- --accent: #ff8648;
13
- }
5
+ .scaffold {
6
+ max-width: 40.625rem;
7
+ margin: 0 auto;
8
+ padding: clamp(3rem, 9vh, 6.5rem) 1.5rem 4rem;
14
9
  }
15
10
 
16
- body {
17
- margin: 0;
18
- background: var(--paper);
19
- color: var(--ink);
20
- font-family: system-ui, sans-serif;
21
- font-size: 1.125rem;
22
- line-height: 1.6;
11
+ .masthead {
12
+ display: flex;
13
+ align-items: baseline;
14
+ gap: 0.6rem;
15
+ margin: 0 0 3.5rem;
16
+ padding-bottom: 1rem;
17
+ border-bottom: 1px solid var(--line);
18
+ color: var(--muted);
19
+ font-size: var(--text-sm);
20
+ letter-spacing: 0.04em;
23
21
  }
24
22
 
25
- main {
26
- max-width: 40rem;
27
- margin: 0 auto;
28
- padding: 4rem 1.5rem;
23
+ .masthead-mark {
24
+ color: var(--accent);
25
+ font-size: var(--text-md);
29
26
  }
30
27
 
31
- h1 {
32
- font-family: Georgia, "Times New Roman", serif;
33
- font-weight: 500;
28
+ .scaffold h1 {
29
+ margin: 0 0 0.75rem;
30
+ font-size: clamp(2.6rem, 7vw, 3.8rem);
31
+ font-weight: 450;
32
+ letter-spacing: -0.03em;
33
+ line-height: 1.05;
34
34
  }
35
35
 
36
- a {
37
- color: inherit;
38
- text-decoration: underline;
39
- text-decoration-color: color-mix(in srgb, var(--accent) 30%, transparent);
40
- text-underline-offset: 0.18em;
36
+ .lede {
37
+ margin: 0 0 3rem;
38
+ max-width: 34rem;
39
+ color: var(--muted);
40
+ font-size: var(--text-lg);
41
+ line-height: 1.55;
41
42
  }
42
43
 
43
- a:hover {
44
- text-decoration-color: var(--accent);
44
+ .demo {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 1rem;
48
+ margin-bottom: 3.25rem;
45
49
  }
46
50
 
47
- button {
48
- font: inherit;
49
- padding: 0.4rem 1rem;
51
+ .demo button {
52
+ font: 500 var(--text-md) var(--font-sans);
53
+ padding: 0.5rem 1.3rem;
50
54
  border: 1px solid var(--ink);
55
+ border-radius: 0.35rem;
51
56
  background: transparent;
52
57
  color: inherit;
53
- border-radius: 4px;
54
58
  cursor: pointer;
59
+ transition: border-color 160ms ease, color 160ms ease;
60
+ }
61
+
62
+ .demo button:hover,
63
+ .demo button:focus-visible {
64
+ border-color: var(--accent);
65
+ color: var(--accent);
55
66
  }
56
67
 
57
- output {
58
- margin-left: 0.75rem;
68
+ .demo output {
69
+ min-width: 2ch;
70
+ font-size: var(--text-lg);
59
71
  font-variant-numeric: tabular-nums;
60
72
  }
73
+
74
+ .demo-hint {
75
+ color: var(--muted);
76
+ font-size: var(--text-sm);
77
+ }
78
+
79
+ .endpoints {
80
+ margin: 0;
81
+ padding: 0;
82
+ list-style: none;
83
+ border-top: 1px solid var(--line);
84
+ }
85
+
86
+ main .endpoints a {
87
+ display: flex;
88
+ align-items: baseline;
89
+ justify-content: space-between;
90
+ gap: 1.5rem;
91
+ padding: 0.9rem 0.25rem;
92
+ border-bottom: 1px solid var(--line);
93
+ text-decoration: none;
94
+ text-decoration-color: transparent;
95
+ transition: background 160ms ease;
96
+ }
97
+
98
+ main .endpoints a:hover,
99
+ main .endpoints a:focus-visible {
100
+ background: var(--faint);
101
+ }
102
+
103
+ .endpoint-path {
104
+ font-family: var(--font-mono);
105
+ font-size: var(--text-sm);
106
+ }
107
+ /* Inline code as rounded chips on the faint layer. */
108
+ .scaffold code {
109
+ padding: 0.12em 0.42em;
110
+ border-radius: 0.35rem;
111
+ background: var(--faint);
112
+ font-family: var(--font-mono);
113
+ font-size: 0.85em;
114
+ }
115
+
116
+
117
+ main .endpoints a:hover .endpoint-path {
118
+ color: var(--accent);
119
+ }
120
+
121
+ .endpoints span {
122
+ color: var(--muted);
123
+ font-size: var(--text-sm);
124
+ text-align: right;
125
+ }
126
+
127
+ .colophon {
128
+ margin-top: 3.5rem;
129
+ }
130
+
131
+ .colophon p {
132
+ margin: 0 0 0.5rem;
133
+ color: var(--muted);
134
+ font-size: var(--text-sm);
135
+ line-height: 1.7;
136
+ }
137
+
138
+ .colophon code {
139
+ color: var(--ink);
140
+ }
141
+
142
+ .colophon-links {
143
+ font-size: var(--text-sm);
144
+ }
@@ -0,0 +1,418 @@
1
+ /* shibumi.css — canonical shared layer for shibumistack.dev, shibumi-server, shibumi-forms.
2
+ Source of truth: shibumistack.dev repo public/shibumi.css. Do not edit vendored copies. */
3
+
4
+ /* shared.css: common styles for all pages */
5
+
6
+ :root {
7
+ color-scheme: light dark;
8
+ /* palette copied from shibumi-server site/styles.css */
9
+ --paper: light-dark(#f5f0e4, #1b130f);
10
+ --ink: light-dark(#272016, #eee5d7);
11
+ --muted: light-dark(#746b5d, #aaa092);
12
+ --faint: light-dark(rgba(39, 32, 22, 0.055), rgba(238, 229, 215, 0.07));
13
+ --line: light-dark(rgba(39, 32, 22, 0.14), rgba(238, 229, 215, 0.16));
14
+ --orange: light-dark(#e95f19, #ff8648);
15
+ --orange-soft: light-dark(#f8dfcd, #3c2117);
16
+ --green: light-dark(#26723e, #75d58a);
17
+ --danger: light-dark(#a13b2c, #e36c59);
18
+ --code: light-dark(#201c16, #120e0b);
19
+ --code-ink: #e9e2d5;
20
+ /* aliases so existing components pick up the server palette */
21
+ --bg: var(--paper);
22
+ --text: var(--ink);
23
+ --quiet: var(--faint);
24
+ --accent: var(--orange);
25
+ --accent-soft: var(--orange-soft);
26
+ --accent-hover: light-dark(#d15515, #ff9a66);
27
+ --success: var(--green);
28
+ --success-text: var(--green);
29
+ --success-ink: #17331f;
30
+ --terminal-success: light-dark(#3d8f50, #78e88f);
31
+ --terminal-active: light-dark(#16839a, #62dff0);
32
+ --terminal-info: light-dark(#397fc0, #74b6ff);
33
+ --max: 72.5rem;
34
+ --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
35
+ --font-serif: ui-serif, Georgia, "Iowan Old Style", "Apple Garamond", Cambria, serif;
36
+ --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
37
+ --text-xs: 0.75rem; /* 12px tiny */
38
+ --text-sm: 0.875rem; /* 14px small */
39
+ --text-md: 1rem; /* 16px normal / UI */
40
+ --text-base: 1.125rem; /* 18px body */
41
+ --text-lg: 1.3125rem; /* 21px */
42
+ --text-xl: 1.563rem; /* 25px */
43
+ --text-2xl: 1.953rem;
44
+ }
45
+
46
+ [data-theme="dark"] { color-scheme: dark; }
47
+ [data-theme="light"] { color-scheme: light; }
48
+
49
+ *, *::before, *::after { box-sizing: border-box; }
50
+ html { min-height: 100%; }
51
+
52
+ body {
53
+ margin: 0;
54
+ min-height: 100svh;
55
+ font-family: var(--font-sans);
56
+ font-size: var(--text-base);
57
+ font-weight: 400;
58
+ line-height: 1.65;
59
+ color: var(--text);
60
+ background: var(--bg);
61
+ }
62
+
63
+ h1, h2, h3 {
64
+ font-family: var(--font-serif);
65
+ }
66
+
67
+ /* theme-independent noise texture, copied from shibumi-server */
68
+ body::before {
69
+ content: "";
70
+ position: fixed;
71
+ inset: 0;
72
+ z-index: -1;
73
+ pointer-events: none;
74
+ opacity: 0.33;
75
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.88' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.08'/%3E%3C/svg%3E");
76
+ }
77
+
78
+ a { color: inherit; }
79
+
80
+ main a:not(.btn):not(.button) {
81
+ text-decoration: underline;
82
+ text-decoration-color: color-mix(in srgb, var(--accent) 30%, transparent);
83
+ text-underline-offset: 0.18em;
84
+ transition: text-decoration-color 160ms ease;
85
+ }
86
+
87
+ main a:not(.btn):not(.button):hover,
88
+ main a:not(.btn):not(.button):focus-visible {
89
+ text-decoration-color: var(--accent);
90
+ }
91
+
92
+ .shell {
93
+ width: min(var(--max), calc(100% - 2rem));
94
+ margin: 0 auto;
95
+ padding: 5.5rem 0 0;
96
+ position: relative;
97
+ }
98
+
99
+ /* ---- header / nav ---- */
100
+
101
+ .shell > header,
102
+ .site-header {
103
+ position: fixed;
104
+ top: 0;
105
+ left: 50%;
106
+ z-index: 20;
107
+ width: min(var(--max), calc(100% - 2rem));
108
+ display: flex;
109
+ justify-content: space-between;
110
+ align-items: center;
111
+ gap: 1rem;
112
+ padding: 1rem 0;
113
+ transform: translateX(-50%);
114
+ }
115
+
116
+ .shell > header::before,
117
+ .site-header::before {
118
+ content: "";
119
+ position: fixed;
120
+ top: 0;
121
+ left: 50%;
122
+ z-index: -1;
123
+ width: 100vw;
124
+ height: 100%;
125
+ transform: translateX(-50%);
126
+ background: light-dark(rgb(245 240 228 / 78%), rgb(27 19 15 / 72%));
127
+ backdrop-filter: blur(1.125rem);
128
+ -webkit-backdrop-filter: blur(1.125rem);
129
+ }
130
+
131
+ .mark {
132
+ display: inline-flex;
133
+ align-items: center;
134
+ gap: 0.75rem;
135
+ color: var(--muted);
136
+ text-decoration: none;
137
+ font-family: var(--font-sans);
138
+ font-size: 1.125rem;
139
+ font-weight: 400;
140
+ letter-spacing: 0.03rem;
141
+ }
142
+
143
+ .mark-tld {
144
+ color: light-dark(rgba(92, 88, 82, 0.5), rgba(189, 183, 173, 0.62));
145
+ font-weight: 300;
146
+ }
147
+
148
+ .mark img,
149
+ img.mark-logo {
150
+ width: 2.125rem;
151
+ height: 2.125rem;
152
+ border-radius: 0.5rem;
153
+ }
154
+
155
+ [data-theme="dark"] .mark .logo-light,
156
+ [data-theme="light"] .mark .logo-dark { display: none; }
157
+
158
+ main, .page { min-width: 0; }
159
+
160
+ .eyebrow {
161
+ margin: 0 0 1rem;
162
+ color: var(--accent);
163
+ font: 300 var(--text-md)/1.2 var(--font-mono);
164
+ letter-spacing: 0.14em;
165
+ text-transform: uppercase;
166
+ }
167
+
168
+ nav {
169
+ display: flex;
170
+ align-items: center;
171
+ gap: 1rem;
172
+ font-family: var(--font-sans);
173
+ font-size: var(--text-md);
174
+ color: var(--muted);
175
+ }
176
+
177
+ nav a {
178
+ text-decoration: none;
179
+ border-bottom: 1px solid transparent;
180
+ }
181
+
182
+ nav a:hover,
183
+ nav a[aria-current="page"] {
184
+ border-bottom-color: var(--accent);
185
+ color: var(--text);
186
+ }
187
+
188
+ .nav-links {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 1rem;
192
+ }
193
+
194
+ .nav-actions {
195
+ display: flex;
196
+ align-items: center;
197
+ gap: 1rem;
198
+ }
199
+
200
+ .nav-icons {
201
+ display: flex;
202
+ align-items: center;
203
+ gap: 0.75rem;
204
+ }
205
+
206
+ .github-link {
207
+ display: inline-flex;
208
+ align-items: center;
209
+ border-bottom: 0;
210
+ color: var(--muted);
211
+ }
212
+
213
+ .github-link svg {
214
+ width: 1.05rem;
215
+ height: 1.05rem;
216
+ fill: currentColor;
217
+ }
218
+
219
+ .github-link:hover {
220
+ color: var(--text);
221
+ border-bottom: 0;
222
+ }
223
+
224
+ .stack-menu { position: relative; }
225
+
226
+ .stack-menu summary {
227
+ cursor: pointer;
228
+ list-style: none;
229
+ transition: color 160ms ease;
230
+ }
231
+
232
+ .stack-menu summary::-webkit-details-marker { display: none; }
233
+ .stack-menu summary:hover,
234
+ .stack-menu summary:focus-visible,
235
+ .stack-menu[open] summary,
236
+ .stack-menu:has(a[aria-current="page"]) summary { color: var(--text); }
237
+
238
+ .stack-menu summary:focus-visible {
239
+ border-radius: 0.2rem;
240
+ outline: 2px solid var(--accent);
241
+ outline-offset: 4px;
242
+ }
243
+
244
+ .stack-popover {
245
+ position: absolute;
246
+ top: calc(100% + 0.75rem);
247
+ right: 0;
248
+ z-index: 30;
249
+ width: 14rem;
250
+ padding: 0.45rem;
251
+ border-radius: 0.5rem;
252
+ background: light-dark(rgb(255 250 240 / 78%), rgb(48 35 29 / 72%));
253
+ -webkit-backdrop-filter: blur(0.5rem);
254
+ backdrop-filter: blur(0.5rem);
255
+ box-shadow: 0 0.8rem 2.5rem light-dark(rgb(20 14 9 / 18%), rgb(0 0 0 / 58%));
256
+ }
257
+
258
+ .stack-popover a {
259
+ display: block;
260
+ padding: 0.65rem 0.75rem;
261
+ border: 0;
262
+ border-radius: 0.3rem;
263
+ color: var(--muted);
264
+ }
265
+
266
+ .stack-popover a:hover,
267
+ .stack-popover a:focus-visible {
268
+ border: 0;
269
+ outline: none;
270
+ background: var(--quiet);
271
+ color: var(--text);
272
+ }
273
+
274
+ .stack-popover a[aria-current="page"] { color: var(--accent); }
275
+
276
+ /* first menu in nav (About) opens aligned to its left edge */
277
+ .nav-links > .stack-menu:first-child .stack-popover { left: 0; right: auto; }
278
+ .stack-popover span,
279
+ .stack-popover small { display: block; }
280
+ .stack-popover span { color: inherit; font-weight: 700; }
281
+ .stack-popover small { margin-top: 0.1rem; color: var(--muted); font-size: var(--text-xs); }
282
+
283
+ /* ---- theme toggle ---- */
284
+
285
+ .theme-toggle {
286
+ appearance: none;
287
+ display: inline-grid;
288
+ place-items: center;
289
+ width: 1.7rem;
290
+ height: 1.7rem;
291
+ border: 0;
292
+ background: transparent;
293
+ color: var(--muted);
294
+ cursor: pointer;
295
+ padding: 0;
296
+ }
297
+
298
+ .theme-toggle:hover { color: var(--text); }
299
+
300
+ .theme-toggle svg {
301
+ width: 1.1rem;
302
+ height: 1.1rem;
303
+ fill: none;
304
+ stroke: currentColor;
305
+ stroke-width: 1.8;
306
+ }
307
+
308
+ [data-theme="dark"] .theme-toggle .icon-sun,
309
+ [data-theme="light"] .theme-toggle .icon-moon { display: none; }
310
+ [data-theme="dark"] .theme-toggle .icon-moon,
311
+ [data-theme="light"] .theme-toggle .icon-sun { display: block; }
312
+ /* alias var names used by server/forms stylesheets */
313
+ :root {
314
+ --sans: var(--font-sans);
315
+ --serif: var(--font-serif);
316
+ --mono: var(--font-mono);
317
+ --fs-xs: var(--text-xs);
318
+ --fs-sm: var(--text-sm);
319
+ --fs-md: var(--text-md);
320
+ --fs-base: var(--text-base);
321
+ --fs-lg: var(--text-lg);
322
+ }
323
+
324
+ /* ---- footer ---- */
325
+
326
+ .site-footer {
327
+ margin-top: clamp(4rem, 8vw, 6rem);
328
+ padding: 1.5rem 0;
329
+ border-top: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
330
+ font-family: var(--font-sans);
331
+ font-size: var(--text-sm);
332
+ color: var(--muted);
333
+ }
334
+
335
+ .footer-line {
336
+ display: flex;
337
+ align-items: center;
338
+ justify-content: space-between;
339
+ gap: 1rem;
340
+ }
341
+
342
+ .footer-line nav {
343
+ display: flex;
344
+ align-items: center;
345
+ gap: 1.25rem;
346
+ font: inherit;
347
+ }
348
+
349
+ .site-footer .stack-popover {
350
+ top: auto;
351
+ right: auto;
352
+ bottom: calc(100% + 0.75rem);
353
+ left: 0;
354
+ }
355
+
356
+ .footer-meta {
357
+ display: flex;
358
+ align-items: center;
359
+ gap: 0.3rem;
360
+ margin: 0.75rem 0 0;
361
+ font-size: var(--text-xs);
362
+ opacity: 0.75;
363
+ }
364
+
365
+ .site-footer a {
366
+ text-decoration: none;
367
+ color: var(--muted);
368
+ }
369
+
370
+ .site-footer a:hover {
371
+ color: var(--accent);
372
+ }
373
+
374
+ .footer-heart {
375
+ width: 0.875rem;
376
+ height: 0.875rem;
377
+ fill: currentColor;
378
+ color: var(--danger);
379
+ opacity: 0.5;
380
+ transition: opacity 150ms;
381
+ }
382
+
383
+ .footer-meta:hover .footer-heart {
384
+ opacity: 1;
385
+ animation: heart-pulse 1s ease-in-out infinite;
386
+ }
387
+
388
+ @keyframes heart-pulse {
389
+ 0%, 100% { transform: scale(1); }
390
+ 50% { transform: scale(1.3); }
391
+ }
392
+
393
+ .sr-only {
394
+ position: absolute;
395
+ width: 1px;
396
+ height: 1px;
397
+ margin: -1px;
398
+ padding: 0;
399
+ overflow: hidden;
400
+ clip: rect(0 0 0 0);
401
+ white-space: nowrap;
402
+ border: 0;
403
+ }
404
+
405
+
406
+ /* green pulse on the box around a just-used copy button */
407
+ @keyframes copy-glow {
408
+ 0%, 100% { box-shadow: 0 0 0 0 transparent; }
409
+ 35% {
410
+ box-shadow:
411
+ 0 0 0 3px color-mix(in srgb, var(--success) 30%, transparent),
412
+ 0 0 1.5rem color-mix(in srgb, var(--success) 40%, transparent);
413
+ }
414
+ }
415
+
416
+ :has(> .copied), :has(> .is-copied) {
417
+ animation: copy-glow 1.2s ease-in-out;
418
+ }