Apply brand kit colors and fonts to DOCX export

Use antique gold accent, rich black text, Cormorant Garamond
headings, and Lora body text. Remove fill backgrounds, keeping
only the gold left-border scripture callout and section divider.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-06-11 09:18:05 -04:00
parent c41916b3ce
commit 6c4658e244
+49 -17
View File
@@ -513,18 +513,21 @@ export function createParagraphsFromText(text) {
.map((line) => new Paragraph({ children: [new TextRun({ text: line })] })); .map((line) => new Paragraph({ children: [new TextRun({ text: line })] }));
} }
const DOCX_ACCENT = '0F766E'; // teal-700 const DOCX_ACCENT = 'C9A84C'; // antique gold
const DOCX_HEADING_FONT = 'Cormorant Garamond';
const DOCX_BODY_FONT = 'Lora';
export function sectionLabel(text) { export function sectionLabel(text) {
return new Paragraph({ return new Paragraph({
spacing: { before: 240, after: 80 }, spacing: { before: 240, after: 80 },
shading: { type: ShadingType.CLEAR, fill: 'F0FDFA' },
children: [ children: [
new TextRun({ new TextRun({
text: text.toUpperCase(), text: text.toUpperCase(),
bold: true, bold: true,
color: DOCX_ACCENT, color: DOCX_ACCENT,
font: DOCX_BODY_FONT,
size: 20, size: 20,
characterSpacing: 20,
}), }),
], ],
}); });
@@ -2195,7 +2198,7 @@ const App = () => {
new TextRun({ new TextRun({
text: `${project.translation}${buildChapterSummary(project)}`, text: `${project.translation}${buildChapterSummary(project)}`,
italics: true, italics: true,
color: '64748B', color: '7A7060',
}), }),
], ],
spacing: { after: 300 }, spacing: { after: 300 },
@@ -2214,18 +2217,18 @@ const App = () => {
ch.chunks.forEach((chunk) => { ch.chunks.forEach((chunk) => {
const scriptureHeading = formatChunkReference(project, chapterIndex, chunk, '-'); const scriptureHeading = formatChunkReference(project, chapterIndex, chunk, '-');
const headingRuns = [new TextRun({ text: scriptureHeading, bold: true, size: 28, color: DOCX_ACCENT })]; const headingRuns = [new TextRun({ text: scriptureHeading, bold: true, size: 28, color: DOCX_ACCENT, font: DOCX_HEADING_FONT })];
if (chunk.episodeNumber || chunk.episodeTitle) { if (chunk.episodeNumber || chunk.episodeTitle) {
headingRuns.push(new TextRun({ headingRuns.push(new TextRun({
text: ` (Ep. ${chunk.episodeNumber || '—'}${chunk.episodeTitle ? `: ${chunk.episodeTitle}` : ''})`, text: ` (Ep. ${chunk.episodeNumber || '—'}${chunk.episodeTitle ? `: ${chunk.episodeTitle}` : ''})`,
italics: true, italics: true,
size: 22, size: 22,
color: '64748B', color: '7A7060',
})); }));
} }
children.push(new Paragraph({ children: headingRuns, spacing: { before: 320, after: 120 } })); children.push(new Paragraph({ children: headingRuns, spacing: { before: 320, after: 120 } }));
// Scripture text in a shaded, left-bordered table cell for a "callout" look // Scripture text in a left-bordered table cell for a "callout" look (no fill)
const scriptureParas = getChunkVerseEntries(project, chapterIndex, chunk).map((verse) => new Paragraph({ const scriptureParas = getChunkVerseEntries(project, chapterIndex, chunk).map((verse) => new Paragraph({
children: [ children: [
new TextRun({ text: `${verse.chapter}:${verse.number} `, bold: true, color: DOCX_ACCENT }), new TextRun({ text: `${verse.chapter}:${verse.number} `, bold: true, color: DOCX_ACCENT }),
@@ -2239,7 +2242,6 @@ const App = () => {
new TableRow({ new TableRow({
children: [ children: [
new TableCell({ new TableCell({
shading: { type: ShadingType.CLEAR, fill: 'F8FAFC' },
margins: { top: 120, bottom: 120, left: 180, right: 180 }, margins: { top: 120, bottom: 120, left: 180, right: 180 },
borders: { borders: {
...{ top: noBorder, right: noBorder, bottom: noBorder }, ...{ top: noBorder, right: noBorder, bottom: noBorder },
@@ -2285,21 +2287,21 @@ const App = () => {
children.push(sectionLabel('Word Studies')); children.push(sectionLabel('Word Studies'));
const headerCellStyle = (label) => new TableCell({ const headerCellStyle = (label) => new TableCell({
width: { size: 20, type: WidthType.PERCENTAGE }, width: { size: 20, type: WidthType.PERCENTAGE },
shading: { type: ShadingType.CLEAR, fill: DOCX_ACCENT }, borders: { bottom: { style: BorderStyle.SINGLE, size: 8, color: DOCX_ACCENT } },
children: [new Paragraph({ children: [new TextRun({ text: label, bold: true, color: 'FFFFFF' })] })], children: [new Paragraph({ children: [new TextRun({ text: label, bold: true, color: DOCX_ACCENT })] })],
}); });
const tableRows = [ const tableRows = [
new TableRow({ new TableRow({
tableHeader: true, tableHeader: true,
children: ['Strong', 'Greek', 'Transliteration', 'Part of Speech', 'Short Definition'].map(headerCellStyle), children: ['Strong', 'Greek', 'Transliteration', 'Part of Speech', 'Short Definition'].map(headerCellStyle),
}), }),
...chunk.greekWords.map((word, i) => new TableRow({ ...chunk.greekWords.map((word) => new TableRow({
children: [ children: [
new TableCell({ shading: i % 2 ? { type: ShadingType.CLEAR, fill: 'F8FAFC' } : undefined, children: [new Paragraph(word.strongNumber || '')] }), new TableCell({ children: [new Paragraph(word.strongNumber || '')] }),
new TableCell({ shading: i % 2 ? { type: ShadingType.CLEAR, fill: 'F8FAFC' } : undefined, children: [new Paragraph(word.lexeme || '')] }), new TableCell({ children: [new Paragraph(word.lexeme || '')] }),
new TableCell({ shading: i % 2 ? { type: ShadingType.CLEAR, fill: 'F8FAFC' } : undefined, children: [new Paragraph(word.transliteration || '')] }), new TableCell({ children: [new Paragraph(word.transliteration || '')] }),
new TableCell({ shading: i % 2 ? { type: ShadingType.CLEAR, fill: 'F8FAFC' } : undefined, children: [new Paragraph(word.partOfSpeech || '')] }), new TableCell({ children: [new Paragraph(word.partOfSpeech || '')] }),
new TableCell({ shading: i % 2 ? { type: ShadingType.CLEAR, fill: 'F8FAFC' } : undefined, children: [new Paragraph(word.shortDefinition || '')] }), new TableCell({ children: [new Paragraph(word.shortDefinition || '')] }),
], ],
})), })),
]; ];
@@ -2323,12 +2325,42 @@ const App = () => {
children.push(new Paragraph({ children.push(new Paragraph({
text: '', text: '',
spacing: { after: 200 }, spacing: { after: 200 },
border: { bottom: { style: BorderStyle.SINGLE, size: 4, color: 'E2E8F0', space: 8 } }, border: { bottom: { style: BorderStyle.SINGLE, size: 4, color: '2A2518', space: 8 } },
})); }));
}); });
}); });
const doc = new Document({ sections: [{ children }] }); const doc = new Document({
styles: {
default: {
document: { run: { font: DOCX_BODY_FONT, color: '0A0A08' } },
},
paragraphStyles: [
{
id: 'Title',
name: 'Title',
basedOn: 'Normal',
next: 'Normal',
run: { font: DOCX_HEADING_FONT, size: 56, bold: true, color: '0A0A08' },
},
{
id: 'Heading1',
name: 'Heading 1',
basedOn: 'Normal',
next: 'Normal',
run: { font: DOCX_HEADING_FONT, size: 36, bold: true, color: '0A0A08' },
},
{
id: 'Heading2',
name: 'Heading 2',
basedOn: 'Normal',
next: 'Normal',
run: { font: DOCX_HEADING_FONT, size: 30, bold: true, color: '0A0A08' },
},
],
},
sections: [{ children }],
});
const blob = await Packer.toBlob(doc); const blob = await Packer.toBlob(doc);
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');