@heylemon/lemonade 0.1.6 → 0.1.8

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.
@@ -1,37 +1,708 @@
1
1
  ---
2
- name: docx
3
- description: "Create professional, polished .docx documents (reports, memos, briefs, proposals, letters) with consistent formatting and validation. Trigger for requests to create or draft Word documents."
4
- license: Proprietary. LICENSE.txt has complete terms
2
+ name: pro-docs
3
+ description: "Create professional, polished documents reports, memos, proposals, briefs, and letters with consistent formatting, proper typography, and clear structure. Use this skill whenever the user wants to create a document, report, memo, proposal, brief, letter, write-up, analysis, or any structured written deliverable. Also trigger when someone says 'write me a report', 'create a document', 'draft a memo', 'make this look professional', or needs any written output that should look polished and ready to share with stakeholders."
5
4
  ---
6
5
 
7
- # Pro Docs for DOCX
6
+ # Pro Docs Professional Document Creation
8
7
 
9
- This skill uses deterministic scripts to generate polished `.docx` files.
8
+ Create polished, production-ready documents using docx-js, the clean standards-compliant Node.js library for producing .docx files. This skill writes bespoke code for each request rather than using JSON templates — you have full control over the document API.
10
9
 
11
- ## Workflow
10
+ ## Quick Reference
12
11
 
13
- 1. Understand request (document type, audience, goal).
14
- 2. Build a JSON spec.
15
- 3. Run creation script.
16
- 4. Validate output.
17
- 5. Deliver.
12
+ | Task | Approach | Tool |
13
+ |------|----------|------|
14
+ | **Create new document** | Write docx-js code using Document, Packer, Paragraph, TextRun, Table | Node.js + docx-js |
15
+ | **Read/analyze existing** | Unzip → read document.xml or use pandoc for content extraction | unzip or pandoc |
16
+ | **Edit existing file** | Unzip → modify XML → repack into .docx | unzip + JSZip |
17
+ | **Validate output** | Check XML, fonts, structure, paragraphs | `python scripts/validate.py` |
18
+ | **Visual QA** | Convert to PDF then to images | soffice + pdftoppm |
18
19
 
19
- ## Build
20
+ ---
21
+
22
+ ## Creating New Documents with docx-js
23
+
24
+ ### Setup & Imports
25
+
26
+ ```javascript
27
+ const { Document, Packer, Paragraph, TextRun, Table, TableCell, WidthType,
28
+ BorderStyle, UnderlineType, PageBreak, Header, Footer } = require("docx");
29
+ const fs = require("fs");
30
+
31
+ // Create a new document
32
+ const doc = new Document({
33
+ sections: [
34
+ {
35
+ children: [
36
+ // Add paragraphs, tables, etc. here
37
+ ]
38
+ }
39
+ ]
40
+ });
41
+
42
+ // Write to file
43
+ Packer.toBuffer(doc).then(buffer => {
44
+ fs.writeFileSync("output.docx", buffer);
45
+ console.log("Document created: output.docx");
46
+ });
47
+ ```
48
+
49
+ ### Page Size & Margins
50
+
51
+ Use US Letter (8.5 x 11 inches). docx-js works in **DXA units** (twentieths of a point):
52
+ - 1 inch = 1440 DXA
53
+ - US Letter = 12240 x 15840 DXA
54
+ - Standard margins: 1 inch (1440 DXA) on sides, 0.8 inches (1152 DXA) top/bottom
55
+
56
+ ```javascript
57
+ const doc = new Document({
58
+ sections: [
59
+ {
60
+ properties: {
61
+ page: {
62
+ margins: {
63
+ top: 1152, // 0.8 inches
64
+ bottom: 1152,
65
+ left: 1440, // 1 inch
66
+ right: 1440
67
+ }
68
+ }
69
+ },
70
+ children: [ /* content */ ]
71
+ }
72
+ ]
73
+ });
74
+ ```
75
+
76
+ For landscape, swap width/height but pass the document normally — docx-js handles orientation internally.
77
+
78
+ ### Typography Scale
79
+
80
+ Establish a consistent hierarchy. All sizes in half-points (pt * 2):
81
+
82
+ ```javascript
83
+ const TYPOGRAPHY = {
84
+ title: 52, // 26pt — document title
85
+ subtitle: 28, // 14pt — subtitle
86
+ h1: 36, // 18pt — major sections
87
+ h2: 28, // 14pt — subsections
88
+ h3: 24, // 12pt — sub-subsections
89
+ body: 22, // 11pt — normal text
90
+ small: 18, // 9pt — captions, footers
91
+ caption: 16 // 8pt — figure captions
92
+ };
93
+
94
+ const COLORS = {
95
+ primary: "1B3A5C", // Dark navy for headings
96
+ accent: "2E86AB", // Teal for highlights
97
+ lightAccent: "E8F4F8", // Light teal for backgrounds
98
+ text: "2C3E50", // Dark gray for body
99
+ muted: "7F8C8D" // Light gray for secondary text
100
+ };
101
+ ```
102
+
103
+ ### Creating Headings with Styles
104
+
105
+ Override built-in heading styles to ensure they appear in TOC. Use `outlineLevel` to control Table of Contents depth:
106
+
107
+ ```javascript
108
+ new Paragraph({
109
+ text: "Section Title",
110
+ heading: HeadingLevel.HEADING_1,
111
+ outlineLevel: 0, // Appears in TOC
112
+ style: "Heading1",
113
+ run: {
114
+ bold: true,
115
+ size: TYPOGRAPHY.h1,
116
+ color: COLORS.primary,
117
+ font: "Arial"
118
+ },
119
+ spacing: { before: 480, after: 160 } // 12pt before, 4pt after
120
+ });
121
+
122
+ new Paragraph({
123
+ text: "Subsection",
124
+ heading: HeadingLevel.HEADING_2,
125
+ outlineLevel: 1,
126
+ style: "Heading2",
127
+ run: { size: TYPOGRAPHY.h2, color: COLORS.primary },
128
+ spacing: { before: 360, after: 120 }
129
+ });
130
+ ```
131
+
132
+ ### Lists: Never Use Unicode Bullets
133
+
134
+ CRITICAL: Never manually insert bullet characters like •, ◦, ▪. Use the list APIs:
135
+
136
+ ```javascript
137
+ // Bullet list (unordered)
138
+ new Paragraph({
139
+ text: "First bullet point",
140
+ bullet: {
141
+ level: 0
142
+ },
143
+ run: { size: TYPOGRAPHY.body }
144
+ }),
145
+ new Paragraph({
146
+ text: "Second bullet point",
147
+ bullet: { level: 0 },
148
+ run: { size: TYPOGRAPHY.body }
149
+ })
150
+
151
+ // Numbered list
152
+ new Paragraph({
153
+ text: "First step",
154
+ numbering: {
155
+ level: 0,
156
+ instance: 0 // Different instance for each numbered list
157
+ },
158
+ run: { size: TYPOGRAPHY.body }
159
+ }),
160
+ new Paragraph({
161
+ text: "Second step",
162
+ numbering: { level: 0, instance: 0 },
163
+ run: { size: TYPOGRAPHY.body }
164
+ })
165
+ ```
166
+
167
+ ### Tables: Dual Width Rules (CRITICAL)
168
+
169
+ Tables MUST specify width in two places. Always use `WidthType.DXA`, never PERCENTAGE:
170
+
171
+ ```javascript
172
+ new Table({
173
+ width: {
174
+ size: 100,
175
+ type: WidthType.PERCENTAGE // Table width = 100% of content area
176
+ },
177
+ rows: [
178
+ new TableRow({
179
+ children: [
180
+ new TableCell({
181
+ width: { size: 2000, type: WidthType.DXA }, // Individual cell width in DXA
182
+ children: [ new Paragraph("Header 1") ]
183
+ }),
184
+ new TableCell({
185
+ width: { size: 2500, type: WidthType.DXA },
186
+ children: [ new Paragraph("Header 2") ]
187
+ }),
188
+ new TableCell({
189
+ width: { size: 2000, type: WidthType.DXA },
190
+ children: [ new Paragraph("Header 3") ]
191
+ })
192
+ ]
193
+ }),
194
+ // Data rows follow
195
+ new TableRow({
196
+ children: [
197
+ new TableCell({
198
+ width: { size: 2000, type: WidthType.DXA },
199
+ shading: { fill: "E8F4F8", type: ShadingType.CLEAR },
200
+ children: [ new Paragraph("Data 1") ]
201
+ }),
202
+ // ... more cells
203
+ ]
204
+ })
205
+ ]
206
+ });
207
+ ```
208
+
209
+ Cell widths must sum to total table width. 6.5 inches of content (8.5 - 2 margins) = ~9360 DXA.
210
+
211
+ ### Cell Shading (Background Colors)
212
+
213
+ Use `ShadingType.CLEAR`, never SOLID:
214
+
215
+ ```javascript
216
+ new TableCell({
217
+ shading: {
218
+ fill: "1B3A5C", // Hex color
219
+ type: ShadingType.CLEAR // CRITICAL: use CLEAR
220
+ },
221
+ children: [ /* cell content */ ]
222
+ })
223
+ ```
224
+
225
+ ### Headers & Footers with Page Numbers
226
+
227
+ ```javascript
228
+ const doc = new Document({
229
+ sections: [
230
+ {
231
+ properties: {
232
+ page: { /* margin config */ }
233
+ },
234
+ children: [ /* content */ ],
235
+ header: {
236
+ children: [
237
+ new Paragraph({
238
+ text: "Company Name",
239
+ alignment: AlignmentType.RIGHT,
240
+ run: { size: TYPOGRAPHY.small, color: COLORS.muted }
241
+ })
242
+ ]
243
+ },
244
+ footer: {
245
+ children: [
246
+ new Paragraph({
247
+ text: "Page ",
248
+ alignment: AlignmentType.CENTER,
249
+ children: [
250
+ new PageNumber(), // Inserts page number
251
+ new TextRun(" of "),
252
+ new PageBreakCount()
253
+ ]
254
+ })
255
+ ]
256
+ }
257
+ }
258
+ ]
259
+ });
260
+ ```
261
+
262
+ ### Images (type parameter is REQUIRED)
263
+
264
+ Always include `type` when adding images:
265
+
266
+ ```javascript
267
+ new Paragraph({
268
+ children: [
269
+ new ImageRun({
270
+ data: fs.readFileSync("image.png"),
271
+ transformation: {
272
+ width: 400,
273
+ height: 300
274
+ },
275
+ type: "image/png" // CRITICAL: PNG, JPG, GIF, etc.
276
+ })
277
+ ]
278
+ })
279
+ ```
280
+
281
+ ### Page Breaks (Must Be Inside Paragraph)
282
+
283
+ CRITICAL: PageBreak must always be inside a Paragraph, never standalone:
284
+
285
+ ```javascript
286
+ // Correct
287
+ new Paragraph({
288
+ children: [ new PageBreak() ]
289
+ }),
290
+
291
+ // Wrong — this will fail
292
+ new PageBreak()
293
+ ```
294
+
295
+ ### Table of Contents
296
+
297
+ docx-js can't generate TOC fields directly, but you can add placeholder text that users update in Word:
298
+
299
+ ```javascript
300
+ new Paragraph({
301
+ text: "Table of Contents",
302
+ style: "Heading2",
303
+ run: { bold: true, size: TYPOGRAPHY.h2, color: COLORS.primary }
304
+ }),
305
+ new Paragraph({
306
+ text: "[Right-click to update Table of Contents in Word]",
307
+ run: { italic: true, color: COLORS.muted }
308
+ }),
309
+ new Paragraph({ children: [new PageBreak()] })
310
+ ```
311
+
312
+ Or use a more sophisticated approach with field codes (requires XML manipulation).
313
+
314
+ ---
315
+
316
+ ## Critical Rules for docx-js
317
+
318
+ **DO:**
319
+ - Set page size explicitly (use US Letter dimensions)
320
+ - For landscape: still pass standard margins, docx-js handles rotation
321
+ - Create separate Paragraph objects for each paragraph (never use \n)
322
+ - Use List APIs for bullets/numbers (never hardcode • or -)
323
+ - Always include `type` parameter on ImageRun
324
+ - Set both table width AND individual cell widths in DXA
325
+ - Use `ShadingType.CLEAR` for cell backgrounds
326
+ - Use `HeadingLevel.HEADING_1`, etc., with `outlineLevel` for TOC
327
+ - Override heading styles with explicit `style: "Heading1"` properties
328
+ - Set `spacing.before` and `spacing.after` explicitly on headings
329
+
330
+ **DON'T:**
331
+ - Use newlines (\n) — create separate Paragraphs instead
332
+ - Mix WidthType.PERCENTAGE and WidthType.DXA on different table cells
333
+ - Omit `type` on ImageRun (causes runtime errors)
334
+ - Use unicode bullet characters manually
335
+ - Put PageBreak outside a Paragraph
336
+ - Forget cell width specifications (columns collapse)
337
+ - Use `ShadingType.SOLID` (use CLEAR)
338
+ - Assume A4 page size (always set explicitly to US Letter)
339
+ - Mix font styles — stick to 2 fonts (heading + body)
340
+
341
+ ---
342
+
343
+ ## Writing Quality Rules
344
+
345
+ Even if the code is perfect, content matters. Follow these principles:
346
+
347
+ ### Structure
348
+ - **Answer first, support second.** Executive summaries at the top with conclusions. Don't bury decisions in 10 pages of analysis.
349
+ - **One idea per paragraph.** If you see two points in one paragraph, split them.
350
+ - **Action headings, not topic headings.** Not "Q3 Revenue" but "Q3 Revenue Missed Target by 22%." Headings should complete the thought.
351
+ - **Three key points.** Group arguments into sets of three — it's how working memory works.
352
+
353
+ ### Clarity
354
+ - **Short words over long.** Use "help" not "facilitate," "about" not "approximately," "use" not "utilize."
355
+ - **Active voice.** "We recommend" not "it is recommended." "Sales grew" not "growth was achieved."
356
+ - **Quantify.** "43% growth" not "significant growth." "3 weeks" not "soon."
357
+ - **Cut filler.** "In order to" → "to." "At this point in time" → "now." "Due to the fact that" → "because."
358
+ - **No weasel words.** Remove "arguably," "somewhat," "it could be said that."
359
+
360
+ ### Tables
361
+ - Use tables for comparisons, timelines, structured data — not for prose.
362
+ - Every table needs clear headers.
363
+ - Keep tables under 7 columns (split wider tables).
364
+ - Include units in headers ("Revenue ($M)") not in every cell.
365
+
366
+ ### Callouts
367
+ - Use callouts for key decisions, critical warnings, or bottom-line takeaways.
368
+ - Maximum one per major section — if everything is highlighted, nothing stands out.
369
+ - **Always use paragraph borders** (left border + shading), never narrow table cells as accent stripes. Narrow table cells break in Apple Pages. See "Cross-Platform Compatibility" section.
370
+
371
+ ---
372
+
373
+ ## Design Guidance
374
+
375
+ ### Color Palettes
376
+
377
+ **Professional (Default):**
378
+ - Primary: #1B3A5C (dark navy)
379
+ - Accent: #2E86AB (teal)
380
+ - Light accent: #E8F4F8 (pale teal)
381
+ - Text: #2C3E50 (charcoal)
382
+
383
+ **Modern Tech:**
384
+ - Primary: #0F3460 (deep blue)
385
+ - Accent: #16213E (dark blue)
386
+ - Light accent: #E0E7FF (pale blue)
387
+
388
+ **Corporate:**
389
+ - Primary: #1a1a2e (very dark blue)
390
+ - Accent: #d62828 (red accent)
391
+ - Light accent: #f7f7f7 (light gray)
392
+
393
+ ### Font Pairing
394
+
395
+ Stick to **two fonts maximum**:
396
+ - **Headings & body:** Arial or Calibri (clean, professional)
397
+ - **Luxury documents:** Georgia (headings) + Garamond (body)
398
+ - **Technical:** Consolas or Courier for code samples, Arial for prose
399
+
400
+ Always verify fonts are installed on target systems. Arial and Calibri render everywhere.
401
+
402
+ ### When to Use Callouts
403
+
404
+ - Executive summary conclusions
405
+ - Budget figures in proposals
406
+ - Critical deadlines or warnings
407
+ - Key decisions needed from stakeholders
408
+ - Not: every recommendation (overuse kills impact)
409
+
410
+ ---
411
+
412
+ ## QA & Validation Process
413
+
414
+ ### 1. Create & Validate
20
415
 
21
416
  ```bash
22
- pip install python-docx --break-system-packages 2>/dev/null
23
- python scripts/create_doc.py spec.json output.docx
417
+ # Write docx-js code, run it
418
+ node create-report.js
419
+
420
+ # Validate the output
421
+ python scripts/validate.py report.docx
24
422
  ```
25
423
 
26
- ## Validate
424
+ The validator checks:
425
+ - XML well-formedness (can be opened in Word)
426
+ - Font consistency (warns if > 3 fonts)
427
+ - Paragraph length (warns if > 500 chars)
428
+ - Empty headings
429
+ - Manual bullet characters (should use List APIs)
430
+
431
+ ### 2. Fix Tables for Cross-Platform (MANDATORY if document contains tables)
432
+
433
+ docx-js has a bug where table grid definitions (`tblGrid`) don't match cell widths. This causes tables to render as 1-character-wide columns in Apple Pages, Google Docs, and LibreOffice. Run this fix on every document that contains tables:
434
+
435
+ ```bash
436
+ python scripts/fix_tables.py report.docx
437
+ ```
438
+
439
+ This script:
440
+ - Reads each table's first-row cell widths (`tcW`)
441
+ - Patches the `tblGrid` column definitions to match
442
+ - Adds `tblLayout type="fixed"` for consistent rendering
443
+ - Overwrites the file in place (or pass a second arg for a new file)
444
+
445
+ **Always run this after generating any document with tables.** Without it, tables will look correct in Word but break in Pages and Google Docs.
446
+
447
+ ### 3. Visual Check (PDF Conversion)
448
+
449
+ ```bash
450
+ # Convert to PDF
451
+ soffice --headless --convert-to pdf report.docx
452
+
453
+ # Convert to images for inspection (150 DPI, JPEG)
454
+ pdftoppm -jpeg -r 150 report.pdf page
455
+
456
+ # View: page-1.jpg, page-2.jpg, etc.
457
+ ```
458
+
459
+ ### 4. Fix & Re-validate
460
+
461
+ If the validator reports issues or the PDF looks off:
462
+ 1. Fix the docx-js code
463
+ 2. Re-run to regenerate .docx
464
+ 3. Run `fix_tables.py` again (step 2)
465
+ 4. Re-validate with `validate.py`
466
+ 5. Re-convert to PDF if visual issues
467
+
468
+ Iterate until both validation passes and visual output looks polished.
469
+
470
+ ---
471
+
472
+ ## Template Patterns by Document Type
473
+
474
+ ### Report Structure (Most Flexible)
475
+
476
+ ```javascript
477
+ [
478
+ titleSection(), // Title, date, author
479
+ tocSection(), // Table of Contents
480
+ execSummary(), // 2-3 paragraphs: findings, conclusions, recs upfront
481
+ background(), // Brief context only
482
+ keyFindings(), // 3 major findings with supporting data
483
+ recommendations(), // Numbered action items with owners + timeline
484
+ nextSteps(), // Action table with owners and due dates
485
+ appendices() // Supporting details, raw data
486
+ ]
487
+ ```
488
+
489
+ ### Memo (1-2 pages max)
490
+
491
+ ```javascript
492
+ [
493
+ titleSection(),
494
+ purpose(), // One sentence: why this memo exists
495
+ background(), // 2-3 sentences of context
496
+ recommendation(), // Proposal + rationale + callout with ask
497
+ supportingDetails(), // Evidence
498
+ nextSteps() // Bulleted actions with owners
499
+ ]
500
+ ```
501
+
502
+ ### Proposal (Pitch to client)
503
+
504
+ ```javascript
505
+ [
506
+ titleSection(), // With client name
507
+ executiveSummary(), // Problem + solution + why us
508
+ understandingChallenge(), // Show you understand their pain
509
+ proposedSolution(), // What you'll do + how it's different
510
+ approachTimeline(), // Table: phases, activities, dates
511
+ investment(), // Pricing table
512
+ whyUs(), // Credentials, relevant projects
513
+ nextSteps() // Call to action
514
+ ]
515
+ ```
516
+
517
+ ### Brief (1-pager)
518
+
519
+ ```javascript
520
+ [
521
+ titleSection(),
522
+ objective(), // One sentence
523
+ context(), // 2-3 sentences
524
+ keyPoints(), // Bulleted (3 max)
525
+ constraints(), // Budget, timeline, scope
526
+ successCriteria(), // How we measure success
527
+ decisionNeeded() // Callout: specific ask + deadline
528
+ ]
529
+ ```
530
+
531
+ ---
532
+
533
+ ## Common Customizations
534
+
535
+ ### Landscape Orientation
536
+
537
+ Swap dimensions but keep same margin structure:
538
+
539
+ ```javascript
540
+ const doc = new Document({
541
+ sections: [
542
+ {
543
+ properties: {
544
+ page: {
545
+ // Landscape = wide table spreads, timelines, etc.
546
+ margins: {
547
+ top: 1152, bottom: 1152, left: 1440, right: 1440
548
+ }
549
+ }
550
+ },
551
+ children: [ /* wide tables */ ]
552
+ }
553
+ ]
554
+ });
555
+ ```
556
+
557
+ ### Cover Page
558
+
559
+ Add a dedicated section with large title, logo, date:
560
+
561
+ ```javascript
562
+ new Paragraph({
563
+ text: "PROJECT PROPOSAL",
564
+ alignment: AlignmentType.CENTER,
565
+ spacing: { before: 2880, after: 240 }, // 2 inches before
566
+ run: { size: TYPOGRAPHY.title, bold: true, color: COLORS.primary }
567
+ }),
568
+ new Paragraph({
569
+ text: "Prepared for Acme Corp",
570
+ alignment: AlignmentType.CENTER,
571
+ run: { size: TYPOGRAPHY.subtitle, color: COLORS.muted }
572
+ }),
573
+ new Paragraph({ children: [new PageBreak()] })
574
+ ```
575
+
576
+ ### Header with Company Branding
577
+
578
+ ```javascript
579
+ section.header = {
580
+ children: [
581
+ new Paragraph({
582
+ text: "Company Name — Confidential",
583
+ alignment: AlignmentType.RIGHT,
584
+ run: { size: TYPOGRAPHY.small, color: COLORS.muted }
585
+ })
586
+ ]
587
+ };
588
+ ```
589
+
590
+ ---
591
+
592
+ ## Dependencies
27
593
 
28
594
  ```bash
29
- python scripts/validate_doc.py output.docx
595
+ npm install docx
596
+ npm install fs # Built-in Node.js
30
597
  ```
31
598
 
32
- Use `references/templates.md` for template structures and choose the closest format:
33
- - report
34
- - memo
35
- - proposal
36
- - brief
37
- - letter
599
+ For validation:
600
+ ```bash
601
+ pip install python-docx --break-system-packages # For validate.py
602
+ ```
603
+
604
+ For visual QA:
605
+ ```bash
606
+ sudo apt-get install libreoffice poppler-utils # soffice, pdftoppm
607
+ ```
608
+
609
+ ---
610
+
611
+ ## Cross-Platform Compatibility (Pages, Google Docs, LibreOffice)
612
+
613
+ Documents must look correct in Apple Pages, Google Docs, and LibreOffice — not just Microsoft Word. These apps interpret .docx features differently and many advanced Word features break silently.
614
+
615
+ ### Features That Break in Pages
616
+
617
+ **Narrow decorative table cells (< 200 DXA):**
618
+ Pages collapses very narrow cells, causing adjacent text to render vertically (one character per line). This is the most common Pages rendering bug.
619
+
620
+ - ❌ **Never:** Use a 120 DXA cell as a colored accent stripe next to a content cell
621
+ - ❌ **Never:** Use single-cell tables as horizontal rule/divider decorations
622
+ - ✅ **Instead:** Use paragraph borders for accent effects:
623
+
624
+ ```javascript
625
+ // Callout box — cross-platform safe
626
+ // Uses left border on the paragraph itself, not a narrow table cell
627
+ new Paragraph({
628
+ spacing: { before: 200, after: 200 },
629
+ indent: { left: 360 },
630
+ border: {
631
+ left: { style: BorderStyle.SINGLE, size: 12, color: "2E86AB", space: 10 }
632
+ },
633
+ shading: { fill: "E8F4F8", type: ShadingType.CLEAR },
634
+ children: [
635
+ new TextRun({ text: "Decision needed: ", bold: true, size: 22, color: "1B3A5C" }),
636
+ new TextRun({ text: "Approve the $400K budget by February 28.", size: 22, color: "2C3E50" })
637
+ ]
638
+ })
639
+ ```
640
+
641
+ **Decorative tables as horizontal lines:**
642
+ ```javascript
643
+ // ❌ BREAKS in Pages — tiny table used as accent line
644
+ new Table({
645
+ width: { size: 2880, type: WidthType.DXA },
646
+ rows: [new TableRow({ children: [new TableCell({
647
+ width: { size: 2880, type: WidthType.DXA },
648
+ shading: { fill: "2E86AB", type: ShadingType.CLEAR },
649
+ children: [new Paragraph({ children: [new TextRun({ text: " ", size: 4 })] })]
650
+ })] })]
651
+ })
652
+
653
+ // ✅ WORKS everywhere — paragraph bottom border as accent line
654
+ new Paragraph({
655
+ spacing: { after: 200 },
656
+ border: {
657
+ bottom: { style: BorderStyle.SINGLE, size: 6, color: "2E86AB", space: 8 }
658
+ },
659
+ children: [] // Empty paragraph with just a bottom border
660
+ })
661
+ ```
662
+
663
+ ### Other Cross-Platform Pitfalls
664
+
665
+ - **Text boxes / floating elements**: Pages and Google Docs don't support them — content disappears or overlaps
666
+ - **Custom XML / content controls**: Ignored by non-Word apps
667
+ - **Complex nested tables**: Keep nesting to 1 level max; Pages handles deeply nested tables poorly
668
+ - **Tab stops for alignment**: Use tables instead — tab rendering varies between apps
669
+ - **Form fields**: Only work in Word
670
+ - **Watermarks**: Render differently or not at all in Pages
671
+ - **SmartArt / ActiveX**: Not supported outside Word
672
+
673
+ ### Safe Cross-Platform Features
674
+
675
+ These features render consistently across all apps:
676
+ - Simple tables with explicit cell widths (minimum 400 DXA per cell)
677
+ - Paragraph borders (left, bottom, top — great for callouts and dividers)
678
+ - Paragraph shading/highlighting
679
+ - Bold, italic, underline, font color, font size
680
+ - Heading styles (Heading 1-6)
681
+ - Bullet and numbered lists
682
+ - Page breaks
683
+ - Headers and footers with page numbers
684
+ - Images (inline, not floating)
685
+
686
+ ### The 400 DXA Rule
687
+
688
+ **Never create a table cell narrower than 400 DXA (roughly 0.28 inches).** Cells below this width are rendered unpredictably in Pages and LibreOffice. If you need a thin accent stripe, use a paragraph left border instead.
689
+
690
+ ## Troubleshooting
691
+
692
+ **Document won't open in Word:** XML malformed. Check for unclosed tags, invalid characters. Run `validate.py` to check structure.
693
+
694
+ **Tables look misaligned:** Missing cell widths or inconsistent width types. Ensure every cell has `width: { size: X, type: WidthType.DXA }`.
695
+
696
+ **Page breaks in wrong place:** PageBreak must be inside Paragraph. `new Paragraph({ children: [new PageBreak()] })`.
697
+
698
+ **Fonts not rendering:** Arial/Calibri are safest. If using specialty fonts, ensure they're installed on target system.
699
+
700
+ **TOC not generating:** docx-js doesn't support field codes natively. Use placeholder text or manually update in Word after opening.
701
+
702
+ **Text renders vertically in Pages:** A table cell is too narrow (< 200 DXA). Replace narrow decorative cells with paragraph borders. See "Cross-Platform Compatibility" section above.
703
+
704
+ ---
705
+
706
+ ## Example: Simple Report
707
+
708
+ See `references/templates.md` for full template examples with code.