Skip to content

feat(templates): add the NorthlineProposal structured proposal preset - #610

Merged
DemchaAV merged 1 commit into
feature/template-promotionfrom
feat/proposal-northline-preset
Aug 30, 2026
Merged

DemchaAV merged 1 commit into
feature/template-promotionfrom
feat/proposal-northline-preset

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

The proposal family renders one look — ModernProposal on the narrative model. The structured proposal model landed in #609 with no consumer; this PR adds the first one: the two-page teal-and-navy "Northline" business proposal look, previously available only as a standalone published template pinned to graph-compose:2.2.0, with its icons on the filesystem and its own spec.

What changed

  • proposal/presets/NorthlineProposalDocumentTemplate<StructuredProposalDocumentSpec> via create(). Page one: brand header (logo mark, wordmark, tracked document label via TextOrnaments.spacedUpper), three title lines stacked at the reference's own pitch (tighter than the font's line height, unreachable through additive leading — so the pitch is an anchor, not text layout), the meta line, the executive summary beside the glance fact card, flattened icon goal cells (the engine refuses a row inside a row cell), and the numbered scope list. Page two: deliverable bullet columns, the four-column phase grid (the one region with vertical rules and therefore the one real table), the investment / terms band, and the signing card.
  • The page chrome is not flow content: the navy footer band and the teal page-number block are PageBackgroundFills, the brand line and 0{page} numbers a FOOTER-zone DocumentHeaderFooter, and the bottom page margin equals the band height — so the preset owns its session geometry (size, margins, page background), documented on RECOMMENDED_MARGIN. Like the ported template it issues no explicit page break (a break after the natural advance leaves a blank page); the two-page distribution is a property of reference-volume content and the class Javadoc says so.
  • Split for the 500-LOC rule into package-private NorthlineStyles (measured geometry + Spectral/Lato text roles; preset-local — the look has no second consumer yet, the orange variant parameterises the colours when it lands), NorthlineWidgets (header, icon-badged section heading, teal bullet), NorthlinePageOne / NorthlinePageTwo (the bands), and NorthlineIcons (classpath loader with a per-token cache).
  • The 16-icon set moves into the templates artifact under templates/proposal/northline/icons/; icon tokens are data, so a blank token degrades gracefully (plain badge disc / column-preserving spacer) and an unknown one throws IllegalArgumentException naming the token — the same class the phase-grid header contract throws when columnHeaders does not carry one label per rendered column.
  • qa: NorthlineProposalFixtures (the reference content, kept in lockstep with the examples module's sample data by cross-reference) feeds all three gates — NorthlineProposalSmokeTest (7 tests: identity, canonical render through toPdfBytes proving the packaged icons resolve, the empty document through the section guards, rendered-text assertions on the phase grid and investment table — both are single leaf nodes in the layout snapshot, the recorded composed-cell blind spot — and the three data-error contracts), NorthlineProposalLayoutSnapshotTest (exact two-page geometry, page count asserted), NorthlineProposalVisualParityTest (both pages vs checked-in baselines, standard budget).
  • Examples: NorthlineProposalV2Example + StructuredProposalSampleData, registered in GenerateAllExamples and the showcase metadata; the proposal-northline-v2 preview is committed beside the other previews (web/examples.json picks the row up at its release-time regeneration). CHANGELOG entry under v2.2.3 — Planned / Templates.

Verification

Full reactor gate (the eight CI modules) → BUILD SUCCESS; proposal qa suite 18/18 green (-f qa/pom.xml); examples suite 75/75 green including CommittedAssetDriftTest with the new preview; javadoc:javadoc clean.

Port parity, verified on one machine against the published template itself: the preset rendered with the template's own example data is pixel-identical to the standalone template's render on the current engine — 0 / 2,173,720 differing pixels on each of the two pages — and the resolved layout snapshots are node-identical (175/175 paths, zero geometry deltas > 0.01pt). Against the template's approved 2.2.0 preview the only delta is the released ligature fix (#601, v2.2.1): geometry is identical and the differing pixels are exactly the letter-drawn ti pairs.

Notes

  • Content noticeably lighter than the reference volume lets the page-two bands climb onto page one — the ported template has the same property (its own notes record why an explicit break cannot fix it). A robust-pagination variant is follow-up material, not part of the port.
  • An empty section keeps its badge disc and heading; only structures that cannot compose empty (goal cells, phase grid, signature row) are skipped — documented in the class Javadoc.

Lane: canonical (templates.proposal + qa + examples) — no engine changes. Stacks on #609; merge that first.

Comment on lines +157 to +158
row.verticalAlign(RowVerticalAlign.TOP)
.gap(SUMMARY_GAP)
double separatorHeight = GOAL_TEXT_LINES * (TABLE_SIZE * LATO_LINE + BODY_LEADING);
page.addRow("GoalCells", row -> {
row.margin(DocumentInsets.top(BAND_TOP));
row.verticalAlign(RowVerticalAlign.TOP).gap(GOAL_GAP);
Comment on lines +320 to +321
row.verticalAlign(RowVerticalAlign.TOP)
.gap(SCOPE_GAP)
Comment on lines +94 to +95
row.verticalAlign(RowVerticalAlign.TOP)
.gap(0)
Comment on lines +198 to +199
row.verticalAlign(RowVerticalAlign.TOP)
.gap(MONEY_GAP)
Comment on lines +312 to +313
row.verticalAlign(RowVerticalAlign.BOTTOM)
.gap(6)
Comment on lines +67 to +68
row.verticalAlign(RowVerticalAlign.CENTER)
.gap(LOGO_SIZE * 0.25)
@DemchaAV
DemchaAV changed the base branch from feat/proposal-structured-data to feature/template-promotion August 30, 2026 17:57
The proposal family had one preset on the narrative model. Northline
Proposal is the first preset on the structured model: a two-page
teal-and-navy business proposal on the Spectral/Lato pair - brand
header with logo mark and wordmark, three title lines stacked on the
reference's own pitch, the executive summary beside an at-a-glance
fact card, icon goal cells, a numbered scope list, deliverable
columns, the four-column phase grid, an investment table with
subtotal / optional / total row styling, and a signing card. The navy
footer band, the teal page-number block and the 01/02 page numbers are
page chrome (PageBackgroundFill + DocumentHeaderFooter), not flow
content, so the preset owns its session geometry. The icon set ships
inside the templates artifact and resolves by the tokens the data
carries; a blank token degrades gracefully and an unknown one is
reported as a data error by name, as is a phase-grid header count that
does not match the four rendered columns.

qa: one fixtures class feeds the smoke, the exact two-page layout
snapshot, and the pixel gate; the smoke drives full layout and render
(empty document included) and asserts the table text on the rendered
text layer, where the snapshot cannot see. Examples gain
NorthlineProposalV2Example (proposal-northline-v2 in the showcase)
with its committed preview.
@DemchaAV
DemchaAV force-pushed the feat/proposal-northline-preset branch from 686a95e to c176f66 Compare August 30, 2026 19:07
@DemchaAV
DemchaAV merged commit 6e34071 into feature/template-promotion Aug 30, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the feat/proposal-northline-preset branch August 30, 2026 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants