@rubytech/create-maxy 1.0.656 → 1.0.657

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": "@rubytech/create-maxy",
3
- "version": "1.0.656",
3
+ "version": "1.0.657",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -0,0 +1,418 @@
1
+ ---
2
+ name: deck-pages
3
+ description: >
4
+ Constraints for producing presentation deck pages as HTML slide sequences that
5
+ download as pixel-perfect PDFs via client-side html2canvas + jsPDF rendering.
6
+ Use when creating or modifying any slide deck, pitch deck, presentation, or
7
+ multi-slide document intended for PDF download. Also use when the output is a
8
+ set of fixed-dimension landscape pages viewed on screen and exported as a PDF.
9
+ Trigger phrases: "deck", "pitch deck", "slides", "presentation", "slide deck",
10
+ "investor deck", "deck PDF", "download deck", or any request to create a
11
+ multi-page slide-based document with a PDF download button.
12
+ ---
13
+
14
+ # Deck Pages — Slide-Based PDF Documents
15
+
16
+ Constraints for producing HTML slide decks that render correctly on screen and export as pixel-perfect PDFs via client-side `html2canvas-pro` + `jsPDF`. Every rule exists because violating it caused a visible rendering problem in exported decks.
17
+
18
+ ## How this differs from A4 print documents
19
+
20
+ A4 documents use the browser's native print pipeline (`@page` rules, Playwright `browser_pdf_save`). Deck pages bypass print entirely — each slide is rasterized to a canvas and composited into a PDF on the client. This means:
21
+
22
+ - Glassmorphism and `backdrop-filter` render correctly (no PNG fallbacks needed).
23
+ - Text in the PDF is raster, not vector — so the canvas `scale` factor matters for sharpness.
24
+ - The browser's `@page` margin boxes, `counter(page)`, and `orphans`/`widows` have no effect on the exported PDF. Page structure is controlled entirely by the slide DOM.
25
+
26
+ Print styles are still included as a fallback for users who Cmd+P, but they are not the primary export path.
27
+
28
+ ## Rendering path
29
+
30
+ 1. Build the HTML deck with fixed-dimension `.slide` containers.
31
+ 2. Add a client-side download button that uses `html2canvas-pro` + `jsPDF`.
32
+ 3. Verify each slide visually using Playwright snapshots before declaring the deck complete.
33
+
34
+ ## Slide dimensions
35
+
36
+ A4 landscape: `297mm` wide × `210mm` tall. Every slide uses identical dimensions.
37
+
38
+ ```css
39
+ .slide {
40
+ width: 297mm;
41
+ height: 210mm;
42
+ margin: 20px auto;
43
+ position: relative;
44
+ overflow: hidden;
45
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
46
+ }
47
+ ```
48
+
49
+ The `overflow: hidden` is load-bearing — content that overflows a slide will be silently clipped in the PDF. If content doesn't fit, reduce font sizes or restructure the layout. Never let a slide scroll.
50
+
51
+ ## Slide content container
52
+
53
+ Every slide's content lives inside a `.slide-content` div that provides consistent padding and flex layout:
54
+
55
+ ```css
56
+ .slide-content {
57
+ padding: 36px 48px;
58
+ height: 100%;
59
+ display: flex;
60
+ flex-direction: column;
61
+ }
62
+ ```
63
+
64
+ The outer `.slide` controls dimensions and background. The inner `.slide-content` controls content flow. Typical usage:
65
+
66
+ ```html
67
+ <div class="slide bg-[#1a1c18]">
68
+ <div class="slide-content justify-between relative z-10">
69
+ <!-- slide content here -->
70
+ </div>
71
+ </div>
72
+ ```
73
+
74
+ Use `justify-between`, `justify-center`, or `gap-*` on `.slide-content` to distribute content vertically within the fixed height.
75
+
76
+ ## Deck wrapper
77
+
78
+ The slides sit inside a wrapper that provides the grey background visible between slides on screen:
79
+
80
+ ```css
81
+ .deck-wrapper {
82
+ background: #e8e5e0;
83
+ min-height: 100vh;
84
+ padding: 20px 0;
85
+ }
86
+ ```
87
+
88
+ ## Client-side PDF generation
89
+
90
+ The download function dynamically imports `html2canvas-pro` and `jspdf`, iterates over every `.slide` element, rasterizes each to a canvas, and assembles them into a multi-page PDF.
91
+
92
+ ```tsx
93
+ async function downloadAsPdf() {
94
+ const [{ default: html2canvas }, { jsPDF }] = await Promise.all([
95
+ import("html2canvas-pro"),
96
+ import("jspdf"),
97
+ ]);
98
+
99
+ const slides = document.querySelectorAll(".slide");
100
+ const pdf = new jsPDF({ orientation: "landscape", unit: "mm", format: "a4" });
101
+ const pageW = 297;
102
+ const pageH = 210;
103
+
104
+ for (let i = 0; i < slides.length; i++) {
105
+ const canvas = await html2canvas(slides[i] as HTMLElement, {
106
+ scale: 2,
107
+ useCORS: true,
108
+ backgroundColor: null,
109
+ });
110
+ const imgData = canvas.toDataURL("image/jpeg", 0.92);
111
+ if (i > 0) pdf.addPage();
112
+ pdf.addImage(imgData, "JPEG", 0, 0, pageW, pageH);
113
+ }
114
+
115
+ pdf.save("deck.pdf");
116
+ }
117
+ ```
118
+
119
+ **Critical settings:**
120
+
121
+ - `scale: 2` — renders at 2x resolution for sharp text on retina displays. Lower values produce blurry text. Higher values increase file size with diminishing returns.
122
+ - `useCORS: true` — required for any images loaded from external origins (Google Fonts icon sheets, CDN images). Without this, cross-origin images render as blank rectangles.
123
+ - `backgroundColor: null` — preserves the slide's own background rather than injecting a white fill behind it. Essential for dark slides.
124
+ - JPEG at `0.92` quality — balances file size against visual quality. PNG would be lossless but produces much larger PDFs.
125
+
126
+ **Dependencies:** Add `html2canvas-pro` and `jspdf` to `package.json`:
127
+
128
+ ```json
129
+ {
130
+ "dependencies": {
131
+ "html2canvas-pro": "^2.0.2",
132
+ "jspdf": "^4.2.1"
133
+ }
134
+ }
135
+ ```
136
+
137
+ Use `html2canvas-pro`, not `html2canvas` — the `-pro` fork has better support for modern CSS features (oklch colours, CSS variables, container queries).
138
+
139
+ ## Download button
140
+
141
+ The download button sits in a fixed position overlay and shows a loading state during generation:
142
+
143
+ ```tsx
144
+ const [generating, setGenerating] = useState(false);
145
+
146
+ async function handleDownload() {
147
+ setGenerating(true);
148
+ try {
149
+ await downloadAsPdf();
150
+ } finally {
151
+ setGenerating(false);
152
+ }
153
+ }
154
+ ```
155
+
156
+ Place the button in a `fixed top-5 right-5 z-50` container. Mark it with `className="print-hidden"` so it doesn't appear in Cmd+P output. The button must be disabled during generation to prevent double-clicks queuing multiple exports.
157
+
158
+ ## Dark slides and background colours
159
+
160
+ Dark slides (e.g. title slides, accent slides) use a dark `bg-*` on the `.slide` div. For the PDF export path (html2canvas), dark backgrounds render correctly with no special handling — `backgroundColor: null` preserves them.
161
+
162
+ For the Cmd+P fallback path, dark backgrounds require explicit print colour preservation:
163
+
164
+ ```css
165
+ @media print {
166
+ * {
167
+ -webkit-print-color-adjust: exact !important;
168
+ print-color-adjust: exact !important;
169
+ }
170
+ }
171
+ ```
172
+
173
+ ## Glassmorphism on dark slides
174
+
175
+ Unlike A4 print documents, deck pages do **not** need PNG fallbacks for glassmorphism. The html2canvas rendering path captures `backdrop-filter: blur()` as it appears on screen, because it rasterizes the composited result.
176
+
177
+ Use glassmorphism freely on dark slides for stat cards, overlays, and frosted panels:
178
+
179
+ ```html
180
+ <div class="bg-[rgba(250,250,248,0.06)] backdrop-blur-[16px]
181
+ border border-[rgba(250,250,248,0.12)] rounded-md px-5 py-4">
182
+ <!-- content -->
183
+ </div>
184
+ ```
185
+
186
+ ## Background images and gradients
187
+
188
+ For slides with background images (e.g. title slides), layer the image, gradient overlays, and content using absolute positioning:
189
+
190
+ ```html
191
+ <div class="slide bg-[#1a1c18] relative overflow-hidden">
192
+ <!-- Background image -->
193
+ <div class="absolute inset-0 bg-center bg-cover bg-no-repeat"
194
+ style="backgroundImage: url('/hero-bg.png'), linear-gradient(...)" />
195
+ <!-- Gradient overlay for text readability -->
196
+ <div class="absolute inset-0"
197
+ style="background: linear-gradient(to right, rgba(26,28,24,0.92) 0%, ...)" />
198
+ <!-- Content on top -->
199
+ <div class="slide-content justify-between relative z-10">
200
+ ...
201
+ </div>
202
+ </div>
203
+ ```
204
+
205
+ The background image should have a CSS gradient fallback so the slide still looks correct if the image fails to load. `html2canvas` will capture the gradient fallback automatically.
206
+
207
+ ## Print styles (Cmd+P fallback)
208
+
209
+ Include print styles as a fallback for users who use the browser's native print dialog instead of the download button. These styles are not used by the html2canvas export path.
210
+
211
+ ```css
212
+ @page {
213
+ size: A4 landscape;
214
+ margin: 0;
215
+ }
216
+
217
+ @media print {
218
+ html { font-size: 16px; }
219
+ body { background: white !important; }
220
+
221
+ .deck-wrapper {
222
+ background: white;
223
+ padding: 0;
224
+ }
225
+
226
+ .print-hidden {
227
+ display: none !important;
228
+ }
229
+
230
+ .slide {
231
+ width: auto;
232
+ height: 100vh;
233
+ margin: 0;
234
+ box-shadow: none;
235
+ page-break-after: always;
236
+ }
237
+
238
+ .slide:last-child {
239
+ page-break-after: auto;
240
+ }
241
+
242
+ * {
243
+ -webkit-print-color-adjust: exact !important;
244
+ print-color-adjust: exact !important;
245
+ }
246
+ }
247
+ ```
248
+
249
+ **Key resets:** The screen CSS uses `width: 297mm; margin: 20px auto; box-shadow: ...` for the card-on-grey-background look. The print CSS resets these to `width: auto; margin: 0; box-shadow: none;` — screen layout properties leak into print if not explicitly overridden.
250
+
251
+ ## Slide structure patterns
252
+
253
+ ### Section label
254
+
255
+ A horizontal rule + uppercase label to identify each slide's topic:
256
+
257
+ ```tsx
258
+ function SlideLabel({ children, dark = false }) {
259
+ return (
260
+ <div className="flex items-center gap-3 mb-3">
261
+ <span className={`w-8 h-px ${dark ? "bg-accent" : "bg-primary"}`} />
262
+ <span className={`font-body text-[0.6rem] font-semibold tracking-[0.25em] uppercase
263
+ ${dark ? "text-accent" : "text-primary"}`}>
264
+ {children}
265
+ </span>
266
+ </div>
267
+ );
268
+ }
269
+ ```
270
+
271
+ ### Standard content slide
272
+
273
+ Light background, section label, heading, body text, and a card grid:
274
+
275
+ ```html
276
+ <div class="slide bg-bg">
277
+ <div class="slide-content justify-center gap-8">
278
+ <div>
279
+ <SlideLabel>Section Title</SlideLabel>
280
+ <h2 class="font-display text-[2rem]">Heading</h2>
281
+ <p class="font-body text-[0.82rem] max-w-[80%]">Body text.</p>
282
+ </div>
283
+ <div class="grid grid-cols-2 gap-5">
284
+ <!-- cards -->
285
+ </div>
286
+ <p class="font-body text-[0.68rem] text-text-tertiary pt-3
287
+ border-t border-[rgba(124,140,114,0.12)]">
288
+ Footer note.
289
+ </p>
290
+ </div>
291
+ </div>
292
+ ```
293
+
294
+ ### Title slide (dark, full-bleed)
295
+
296
+ Dark background with image overlay, hero text, stat strip, and attribution footer. See the background images section above for the layering pattern.
297
+
298
+ ### Data table slide
299
+
300
+ Use an HTML `<table>` with tight font sizes (`text-[0.72rem]` for cells, `text-[0.6rem]` for headers). Highlight the "our row" with a subtle background tint so the reader's eye lands on it immediately.
301
+
302
+ ## Typography scale for slides
303
+
304
+ Slides use a compressed typography scale that fits content within the fixed 210mm height:
305
+
306
+ | Element | Size | Weight |
307
+ |---------|------|--------|
308
+ | Hero title (h1) | `text-[3.8rem]` | normal |
309
+ | Slide heading (h2) | `text-[2rem]` | normal |
310
+ | Body paragraph | `text-[0.82rem]` | normal |
311
+ | Card description | `text-[0.68rem]` | normal |
312
+ | Section label | `text-[0.6rem]` | semibold, uppercase, tracked |
313
+ | Footer / attribution | `text-[0.62rem]` – `text-[0.7rem]` | normal |
314
+ | Stat number | `text-[1.5rem]` – `text-[1.8rem]` | semibold |
315
+ | Button text | `text-[0.72rem]` | semibold, uppercase, tracked |
316
+
317
+ These sizes are calibrated for 297mm × 210mm at `scale: 2` — they produce readable text in the exported PDF. Smaller sizes become illegible; larger sizes cause overflow.
318
+
319
+ ## Verifying slides with Playwright snapshots
320
+
321
+ Before declaring a deck complete, verify that every slide renders correctly by taking Playwright screenshots. This catches problems that are invisible in the dev server but appear in the PDF: clipped content, broken images, font loading failures, and layout overflow.
322
+
323
+ ### Verification process
324
+
325
+ 1. **Start a local server** — the deck must be served over HTTP. `file://` protocol blocks font loading and CORS resources. If Next.js, run `npm run dev` and wait for the ready signal before navigating.
326
+
327
+ 2. **Navigate to the deck page** and wait for fonts and images to load. Use `browser_wait_for` or `browser_evaluate` with `document.fonts.ready` to ensure custom fonts are loaded — otherwise screenshots capture system-font fallbacks that look correct in dev but produce the wrong PDF.
328
+
329
+ 3. **Screenshot each slide individually** — take an element-level screenshot of each `.slide` div, not a full-page screenshot. Full-page screenshots compress the entire deck into one image and hide per-slide problems.
330
+
331
+ Pseudocode for the Playwright loop:
332
+ ```
333
+ slides = page.locator(".slide").all()
334
+ for i, slide in enumerate(slides):
335
+ slide.scroll_into_view_if_needed()
336
+ slide.screenshot(path=f"slide-{i+1}.png")
337
+ ```
338
+
339
+ 4. **Verify each screenshot against these checks:**
340
+ - Content fits within the slide bounds (no clipping at edges).
341
+ - Text is readable (custom fonts loaded, not falling back to system fonts).
342
+ - Background images and gradients render (no broken image icons).
343
+ - Dark slides have correct dark backgrounds (not white).
344
+ - Glassmorphism panels show the blur effect.
345
+ - No unexpected scrollbars or overflow indicators.
346
+ - Card grids and tables are evenly spaced.
347
+
348
+ 5. **Test the PDF download** — click the download button via Playwright and verify the generated PDF:
349
+ ```
350
+ page.click("button:has-text('Download PDF')")
351
+ download = page.wait_for_event("download")
352
+ download.save_as("deck.pdf")
353
+ ```
354
+ Then open the PDF and confirm page count matches slide count and no pages are blank.
355
+
356
+ ### Common rendering failures caught by snapshots
357
+
358
+ | Symptom | Cause | Fix |
359
+ |---------|-------|-----|
360
+ | White rectangle where image should be | CORS blocked, `useCORS: true` missing | Add `useCORS: true` to html2canvas options |
361
+ | Blurry text in PDF | `scale` too low | Use `scale: 2` or higher |
362
+ | Content clipped at slide bottom | Too much content for 210mm height | Reduce font sizes, remove content, or split across two slides |
363
+ | System font instead of custom font | Font not loaded before screenshot | Wait for `document.fonts.ready` before capturing |
364
+ | Dark slide renders white | `backgroundColor` not `null` in html2canvas | Set `backgroundColor: null` |
365
+ | Download button visible in PDF | Button inside a `.slide` container | Keep button outside `.slide` divs, in a fixed overlay |
366
+ | Glassmorphism appears flat | Using the print pipeline instead of html2canvas | Use the download button, not Cmd+P |
367
+ | Text re-flows between screen and PDF | Relative font units inside fixed-size container | Use explicit `text-[Xrem]` sizes, not `text-lg` style shorthand |
368
+
369
+ ## Layout component structure (Next.js)
370
+
371
+ The deck uses a dedicated layout that injects the wrapper and print styles:
372
+
373
+ ```tsx
374
+ // app/deck/layout.tsx
375
+ export default function DeckLayout({ children }: { children: React.ReactNode }) {
376
+ return (
377
+ <div className="deck-wrapper">
378
+ <style>{`
379
+ .deck-wrapper { background: #e8e5e0; min-height: 100vh; padding: 20px 0; }
380
+ .slide { width: 297mm; height: 210mm; margin: 20px auto; position: relative; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.12); }
381
+ .slide:first-of-type { margin-top: 20px; }
382
+ .slide-content { padding: 36px 48px; height: 100%; display: flex; flex-direction: column; }
383
+
384
+ @page { size: A4 landscape; margin: 0; }
385
+ @media print {
386
+ html { font-size: 16px; }
387
+ body { background: white !important; }
388
+ .deck-wrapper { background: white; padding: 0; }
389
+ .print-hidden { display: none !important; }
390
+ .slide { width: auto; height: 100vh; margin: 0; box-shadow: none; page-break-after: always; }
391
+ .slide:last-child { page-break-after: auto; }
392
+ * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
393
+ }
394
+ `}</style>
395
+ {children}
396
+ </div>
397
+ );
398
+ }
399
+ ```
400
+
401
+ The page component (`app/deck/page.tsx`) is a `"use client"` component containing the slides, the download function, and the download button. Keep the layout as a server component for metadata; the "use client" directive belongs on the page, not the layout.
402
+
403
+ ## Reference implementation
404
+
405
+ A complete working deck lives at `/Users/neo/capacity-derivatives/app/deck/` — read `layout.tsx` and `page.tsx` there when building a new deck. The reference shows eight slides covering title, problem, market, solution, differentiation, roadmap, competitive landscape, and team/ask — the standard pitch-deck arc.
406
+
407
+ ## Checklist before shipping
408
+
409
+ - [ ] Every slide is exactly `297mm × 210mm` with `overflow: hidden`.
410
+ - [ ] Content fits within each slide — no clipping at bottom or right edges.
411
+ - [ ] `html2canvas-pro` and `jspdf` are in `package.json` dependencies.
412
+ - [ ] Download button uses loading state and is disabled during generation.
413
+ - [ ] Download button is outside `.slide` containers (in a fixed overlay).
414
+ - [ ] Dark slides use `backgroundColor: null` in html2canvas config.
415
+ - [ ] Print fallback styles included (`@page`, `@media print`).
416
+ - [ ] Playwright screenshots taken of every slide — content verified.
417
+ - [ ] PDF download tested — correct number of pages, no blank pages.
418
+ - [ ] Fonts loaded before any screenshot or export (check `document.fonts.ready`).