Skip to content

Commit 59dc03e

Browse files
authored
Add CenteredHeadline CV v2 preset (#57)
* Add CenteredHeadline CV v2 preset * Document CenteredHeadline CV v2 migration
1 parent dc05e66 commit 59dc03e

23 files changed

Lines changed: 527 additions & 43 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
All notable changes to GraphCompose are documented here. Versions
44
follow semantic versioning; release dates are ISO 8601.
55

6+
## v1.6.5 — Planned
7+
8+
### Templates v2
9+
10+
- Added the `CenteredHeadline` CV preset to the `cv/v2` layered
11+
template surface, including its isolated theme tokens, visual
12+
regression baselines, and reusable `Subheadline` /
13+
`SectionHeader.flatSpacedCaps` widget support.
14+
615
## v1.6.4 — 2026-05-22
716

817
Bug fix + structured-block patch. Adds two new public Block types —

‎docs/templates/v2-layered/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ template family from empty folder to merged PR.
7171
presets/ composition: data + theme + widgets → DocumentTemplate
7272
│ compose from
7373
▼
74-
widgets/ LEGO bricks: Headline, ContactLine, SectionHeader, …
74+
widgets/ LEGO bricks: Headline, Subheadline, ContactLine, SectionHeader, …
7575
│ delegate to read tokens from
7676
▼ ▼
7777
components/ internal renderers + primitives theme/ palette

‎docs/templates/v2-layered/authoring-presets.md‎

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Authoring Presets — write your own visual style
22

3-
You like the layered architecture, but the three shipped presets
4-
(`BoxedSections`, `MinimalUnderlined`, `ModernProfessional`) don't
5-
match the design you want. This doc walks you through writing a
6-
new preset from scratch — **without subclassing, without duplicating
7-
rendering code**.
3+
You like the layered architecture, but the shipped presets
4+
(`BoxedSections`, `MinimalUnderlined`, `ModernProfessional`,
5+
`CenteredHeadline`) don't match the design you want. This doc walks
6+
you through writing a new preset from scratch — **without subclassing,
7+
without duplicating rendering code**.
88

99
If you haven't read [quickstart.md](quickstart.md) and
1010
[using-templates.md](using-templates.md), do those first.
@@ -59,9 +59,9 @@ visual decision you can read like a recipe.
5959
<a id="the-widget-catalog"></a>
6060
## The widget catalog
6161

62-
Today, three widget classes live in
63-
`com.demcha.compose.document.templates.cv.v2.widgets`. Each has 2-3
64-
named variants.
62+
Today, four widget classes live in
63+
`com.demcha.compose.document.templates.cv.v2.widgets`. Each has a
64+
small set of named variants.
6565

6666
### `Headline` — top-of-document name
6767

@@ -71,12 +71,19 @@ named variants.
7171
| `Headline.rightAligned(host, name, theme)` | Right-aligned plain bold (`Jane Doe`) |
7272
| `Headline.render(host, name, theme, align, spacedCaps)` | Low-level — any (alignment, transform) combo |
7373

74+
### `Subheadline` — secondary tagline under the name
75+
76+
| Variant | Visual |
77+
|---|---|
78+
| `Subheadline.centeredSpacedCaps(host, text, style)` | Centred letter-spaced uppercase tagline (`P R O F E S S I O N A L T I T L E`) |
79+
7480
### `ContactLine` — phone / email / address / links row
7581

7682
| Variant | Visual |
7783
|---|---|
7884
| `ContactLine.centered(host, identity, theme)` | Centred, phone → email → address → links |
7985
| `ContactLine.rightAligned(host, identity, theme)` | Right-aligned, address → phone → email → links |
86+
| `ContactLine.twoRowRightAligned(host, identity, theme, bodyStyle, linkStyle, separatorStyle)` | Right-aligned address/phone row plus email/link row |
8087
| `ContactLine.render(host, identity, theme, align, order)` | Low-level — any alignment + field-order combo |
8188

8289
### `SectionHeader` — title above each section body
@@ -86,6 +93,7 @@ named variants.
8693
| `SectionHeader.banner(host, title, theme)` | Pale-grey panel + centred spaced-caps inside |
8794
| `SectionHeader.underlined(host, title, theme)` | Small left spaced-caps + thin rule below |
8895
| `SectionHeader.flat(host, title, color, theme)` | Large bold title in a given colour, no panel |
96+
| `SectionHeader.flatSpacedCaps(host, title, color, theme, titleStyle)` | Small left spaced-caps title in a soft colour, no panel |
8997

9098
The separator glyph used by `ContactLine`, the bullet glyph used by
9199
`RowRenderer`, and other character-level choices come from
@@ -303,9 +311,10 @@ When you do add a new widget:
303311

304312
1. **One file per widget** in `cv/v2/widgets/`.
305313
2. **`public final class`** with a private constructor.
306-
3. **2-3 named factories** + a lower-level `.render(...)`.
314+
3. **1-3 named factories** + a lower-level `.render(...)` when useful.
307315
4. **First parameter is always `SectionBuilder host`**.
308-
5. **Last parameter is always `CvTheme theme`**.
316+
5. **Pass `CvTheme theme` when the widget reads shared tokens**;
317+
pass an explicit style only when the preset owns that unique style.
309318
6. **No instance state** — all static, all stateless.
310319
7. **JavaDoc the visual** — what does this look like? Who uses it?
311320
8. **Add to `WidgetSmokeTest`** with a basic "renders without

‎docs/templates/v2-layered/quickstart.md‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ GraphCompose's templates v2 (layered) gives you:
1414
- **Themes describing visuals** — `CvTheme` (palette + typography +
1515
spacing + decoration). Swap a theme to change colours, fonts,
1616
bullet glyphs without touching renderers.
17-
- **Widgets as visual LEGO bricks** — `Headline`, `ContactLine`,
18-
`SectionHeader`. Each one is a named visual decision you can drop
19-
into a preset.
17+
- **Widgets as visual LEGO bricks** — `Headline`, `Subheadline`,
18+
`ContactLine`, `SectionHeader`. Each one is a named visual decision
19+
you can drop into a preset.
2020
- **Presets as compositions** — a preset orchestrates widgets in a
21-
page flow. `BoxedSections`, `MinimalUnderlined`, `ModernProfessional`
22-
ship today; writing your own is ~150 lines.
21+
page flow. `BoxedSections`, `MinimalUnderlined`,
22+
`ModernProfessional`, `CenteredHeadline` ship today; writing your
23+
own is ~150 lines.
2324

2425
You hand a `CvDocument` to a preset, you get a PDF. The preset
2526
internally composes widgets that read theme tokens that ultimately
@@ -87,13 +88,14 @@ Same data, different visual. That's the layering.
8788
```
8889
┌─────────────────────────────────────────────────────────────┐
8990
│ presets/ BoxedSections, MinimalUnderlined, │
90-
│ ModernProfessional │
91+
│ ModernProfessional, CenteredHeadline │
9192
│ — composition of widgets in a page flow │
9293
└─────────────────────────────────────────────────────────────┘
9394
│ compose from widgets
9495
▼
9596
┌─────────────────────────────────────────────────────────────┐
96-
│ widgets/ Headline, ContactLine, SectionHeader │
97+
│ widgets/ Headline, Subheadline, ContactLine, │
98+
│ SectionHeader │
9799
│ — named visual LEGO bricks │
98100
└─────────────────────────────────────────────────────────────┘
99101
│ delegate to ↓ │ read tokens from ↓

‎docs/templates/v2-layered/using-templates.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,9 @@ CvDocument doc = CvDocument.builder()
159159
```
160160

161161
**Single-column presets** (`BoxedSections`, `MinimalUnderlined`,
162-
`ModernProfessional`) render only `Slot.MAIN`. Sidebar content is
163-
silently dropped — switch to a multi-column preset to render it.
162+
`ModernProfessional`, `CenteredHeadline`) render only `Slot.MAIN`.
163+
Sidebar content is silently dropped — switch to a multi-column preset
164+
to render it.
164165

165166
If you don't use slots at all, your sections go to `MAIN` and every
166167
preset renders them. The slot model is opt-in.
@@ -170,13 +171,14 @@ preset renders them. The slot model is opt-in.
170171
<a id="picking-a-preset"></a>
171172
## Picking a preset
172173

173-
Three shipped today:
174+
Four shipped today:
174175

175176
| Preset | Visual signature |
176177
|---|---|
177178
| `BoxedSections.create()` | Centred letter-spaced name, pale-grey panel section banners, two-page friendly |
178179
| `MinimalUnderlined.create()` | Centred name with thin rule, small spaced-caps section titles with accent rule, single page |
179180
| `ModernProfessional.create()` | Right-aligned big slate-blue name, flat bright-blue bold section titles, dense single page |
181+
| `CenteredHeadline.create()` | Centred spaced-caps name, small subheadline, full-width rules around contact and modules |
180182

181183
Each factory has a no-arg form (uses a sensible default theme) and
182184
a `create(CvTheme)` form (custom theme).

‎examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,17 +504,17 @@ public static CvDocument sampleCvDocumentV2() {
504504
.builder("Education & Certifications")
505505
.entry("MSc Computer Science",
506506
"University of Manchester",
507-
"2021",
507+
"2020-2021",
508508
"Distinction. Thesis: *Composable layout primitives "
509509
+ "for deterministic document rendering*.")
510510
.entry("BSc Software Engineering",
511511
"Imperial College London",
512-
"2019",
512+
"2016-2019",
513513
"First-class honours. Specialisation in compilers and "
514514
+ "static analysis.")
515515
.entry("Oracle Java Certification",
516516
"Professional track",
517-
"2023",
517+
"2023-2024",
518518
"Java 17 platform deep-dive: records, sealed types, "
519519
+ "pattern matching, virtual threads.")
520520
.build();

‎src/main/java/com/demcha/compose/document/templates/cv/v2/AUTHORS.md‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,16 +324,23 @@ as DSL plumbing. Below is the current catalog.
324324

325325
| Variant | Visual | Used in |
326326
|---|---|---|
327-
| `Headline.spacedCentered(host, name, theme)` | centred letter-spaced uppercase (`J A N E D O E`) | BoxedSections, MinimalUnderlined |
327+
| `Headline.spacedCentered(host, name, theme)` | centred letter-spaced uppercase (`J A N E D O E`) | BoxedSections, MinimalUnderlined, CenteredHeadline |
328328
| `Headline.rightAligned(host, name, theme)` | right-aligned plain bold (`Jane Doe`) | ModernProfessional |
329329
| `Headline.render(host, name, theme, align, spacedCaps)` | low-level: pick any alignment + transform | — |
330330

331+
### `Subheadline` — secondary tagline under the name
332+
333+
| Variant | Visual | Used in |
334+
|---|---|---|
335+
| `Subheadline.centeredSpacedCaps(host, text, style)` | centred letter-spaced uppercase tagline | CenteredHeadline |
336+
331337
### `ContactLine` — phone / email / address / links row
332338

333339
| Variant | Visual | Used in |
334340
|---|---|---|
335-
| `ContactLine.centered(host, identity, theme)` | centred, phone → email → address → links | BoxedSections, MinimalUnderlined |
341+
| `ContactLine.centered(host, identity, theme)` | centred, phone → email → address → links | BoxedSections, MinimalUnderlined, CenteredHeadline |
336342
| `ContactLine.rightAligned(host, identity, theme)` | right-aligned, address → phone → email → links | ModernProfessional |
343+
| `ContactLine.twoRowRightAligned(host, identity, theme, bodyStyle, linkStyle, separatorStyle)` | right-aligned address/phone row plus email/link row | ModernProfessional |
337344
| `ContactLine.render(host, identity, theme, align, order)` | low-level: pick alignment + field order | — |
338345

339346
The separator glyph comes from
@@ -347,11 +354,12 @@ change ` | ` to ` · ` or anything else.
347354
| `SectionHeader.banner(host, title, theme)` | pale-grey panel with centred spaced-caps inside | BoxedSections |
348355
| `SectionHeader.underlined(host, title, theme)` | small spaced-caps left-aligned, thin rule below | MinimalUnderlined |
349356
| `SectionHeader.flat(host, title, color, theme)` | large bold title in a given colour, no panel | ModernProfessional |
357+
| `SectionHeader.flatSpacedCaps(host, title, color, theme, titleStyle)` | small spaced-caps title in a soft colour, no panel | CenteredHeadline |
350358

351-
Note that `flat` takes a `DocumentColor` argument — the section
352-
title colour is the preset's signature accent, and the widget
353-
deliberately surfaces it as a parameter rather than burying it in
354-
the theme.
359+
Note that `flat` and `flatSpacedCaps` take a `DocumentColor`
360+
argument — the section title colour is the preset's signature
361+
accent, and the widget deliberately surfaces it as a parameter
362+
rather than burying it in the theme.
355363

356364
### Composing a preset from widgets
357365

‎src/main/java/com/demcha/compose/document/templates/cv/v2/package-info.java‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,19 @@
2020
* │ presets/ │
2121
* │ BoxedSections ← composition: data + theme + render │
2222
* │ MinimalUnderlined ← another composition, same pieces │
23-
* │ ModernProfessional ← third preset, partial widget use │
23+
* │ ModernProfessional ← corporate composition variant │
24+
* │ CenteredHeadline ← classic centred headline variant │
2425
* └─────────────────────────────────────────────────────────────┘
2526
* │ compose from
2627
* ▼
2728
* ┌─────────────────────────────────────────────────────────────┐
2829
* │ widgets/ ← named visual building blocks (LEGO bricks) │
2930
* │ Headline .spacedCentered | .rightAligned │
30-
* │ ContactLine .centered | .rightAligned │
31+
* │ Subheadline .centeredSpacedCaps │
32+
* │ ContactLine .centered | .rightAligned │
33+
* │ .twoRowRightAligned │
3134
* │ SectionHeader .banner | .underlined | .flat │
35+
* │ .flatSpacedCaps │
3236
* └─────────────────────────────────────────────────────────────┘
3337
* │ delegate to │ read tokens from
3438
* ▼ ▼
@@ -70,8 +74,9 @@
7074
* <dt><b>{@code widgets/}</b></dt>
7175
* <dd>The LEGO bricks. <em>"Which visual building block do I
7276
* want here — a banner, an underlined title, a right-aligned
73-
* headline?"</em> Each widget has 2-3 named variants and a
74-
* lower-level entry for ad-hoc parameter combinations. This
77+
* headline?"</em> Each widget has a small set of named
78+
* variants and, where useful, a lower-level entry for ad-hoc
79+
* parameter combinations. This
7580
* is where most preset code lives — picking widgets and
7681
* composing them.</dd>
7782
*

0 commit comments

Comments
 (0)