Sunny: Anschreiben dezenter — Farbe nur noch im Kopfband
Das Anschreiben war ein zweites Poster: Konfetti im Band, solides Akzent-Badge, Betreff und Anrede beide in der Akzentfarbe, ein pastellener Marker-Strich hinter dem Namen und eine Farbleiste als Fußzeile. Neben dem Lebenslauf las sich das als Lärm, nicht als Set. Jetzt trägt die Seite genau eine Farbfläche — das Kopfband —, alles darunter ist Typografie: - Kopfband ohne Konfetti, Name 27pt -> 22pt - Rollen-Badge als weiße Pille mit Akzentschrift (der Lebenslauf behält sein solides Badge; die Umstellung sitzt in der Letter-Palette) - Datum als schlichte rechtsbündige Zeile statt Pastell-Chip - Betreff und Anrede in Tinte; die Bar über dem Betreff bleibt der einzige Farbakzent im Textkörper - Signatur ohne Bild: Name über feinem Haarstrich statt Marker-Swipe - Fußzeile: Haarlinie mit gedämpftem Kontakttext statt Farbleiste suKonfetti/suDot hatten damit keinen Aufrufer mehr und fallen weg. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+6
-2
@@ -250,8 +250,12 @@ function resolve(overrides = {}) {
|
|||||||
cardBorder: rc.cardBorder,
|
cardBorder: rc.cardBorder,
|
||||||
deko: rc.deko,
|
deko: rc.deko,
|
||||||
band: rc.band,
|
band: rc.band,
|
||||||
pillBg: rc.pillBg,
|
// The letter's role badge sits on the pastel head band, so a solid accent
|
||||||
pillInk: rc.pillInk,
|
// slug reads too loud next to the résumé's poster. A white pill with the
|
||||||
|
// accent knocked out keeps the motif but softens it — the letter is the
|
||||||
|
// quiet twin, not a second poster.
|
||||||
|
pillBg: rc.cardBg,
|
||||||
|
pillInk: accent,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const lc = LC[s.layout] || LC.sidebar;
|
const lc = LC[s.layout] || LC.sidebar;
|
||||||
|
|||||||
+39
-59
@@ -1573,27 +1573,6 @@ function suBar(doc, p, S, dry, y, align, frac = 0.62) {
|
|||||||
doc.rect(x, y, w, 2.4 * S, 'F');
|
doc.rect(x, y, w, 2.4 * S, 'F');
|
||||||
}
|
}
|
||||||
|
|
||||||
// A filled dot. The confetti scattered through the Sunny documents is built from
|
|
||||||
// these — a designer's flourish, kept to the margins where no text can collide.
|
|
||||||
function suDot(doc, cx, cy, r, color) {
|
|
||||||
doc.setFillColor(color[0], color[1], color[2]);
|
|
||||||
doc.circle(cx, cy, r, 'F');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confetti in the band's gutters — the strips between its edges and the text
|
|
||||||
// column, empty by construction, so the decoration can never run into a word.
|
|
||||||
//
|
|
||||||
// It all lives on the *right*: dots down the left gutter sat directly beside the
|
|
||||||
// name and read as bullet points in front of it, not as decoration. The only
|
|
||||||
// thing on the left is a sparkle low in the band, well clear of the name.
|
|
||||||
function suKonfetti(doc, p, S, bandY, bandH) {
|
|
||||||
const gx = SU.r - 3 * S;
|
|
||||||
suDot(doc, gx, bandY + bandH * 0.26, 1.4 * S, p.accent);
|
|
||||||
suDot(doc, gx, bandY + bandH * 0.48, 0.9 * S, [255, 255, 255]);
|
|
||||||
suDot(doc, gx, bandY + bandH * 0.66, 1.9 * S, [255, 255, 255]);
|
|
||||||
sparkle(doc, SU.mx + 3.4 * S, bandY + bandH - 5.5 * S, 4.4 * S, [255, 255, 255]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display heading: the poster type. Set as large as the layout's rhythm allows
|
// Display heading: the poster type. Set as large as the layout's rhythm allows
|
||||||
// and shrunk only if a long word (Berufsausbildung…) would not otherwise fit.
|
// and shrunk only if a long word (Berufsausbildung…) would not otherwise fit.
|
||||||
function suHeading(doc, t, S, dry, cur, title, align = 'left') {
|
function suHeading(doc, t, S, dry, cur, title, align = 'left') {
|
||||||
@@ -2277,7 +2256,7 @@ function composeLetterSunny(doc, t, S, dry, cur, { letter, header, job, anlagen,
|
|||||||
|
|
||||||
const cl = { y: top };
|
const cl = { y: top };
|
||||||
const name = String(header.name || '');
|
const name = String(header.name || '');
|
||||||
let namePt = 27;
|
let namePt = 22;
|
||||||
doc.setFont(doc.__sans, 'bold');
|
doc.setFont(doc.__sans, 'bold');
|
||||||
for (; namePt > 14; namePt -= 0.5) {
|
for (; namePt > 14; namePt -= 0.5) {
|
||||||
doc.setFontSize(namePt * S);
|
doc.setFontSize(namePt * S);
|
||||||
@@ -2308,24 +2287,27 @@ function composeLetterSunny(doc, t, S, dry, cur, { letter, header, job, anlagen,
|
|||||||
if (!dry) {
|
if (!dry) {
|
||||||
doc.setFillColor(t.lc.band[0], t.lc.band[1], t.lc.band[2]);
|
doc.setFillColor(t.lc.band[0], t.lc.band[1], t.lc.band[2]);
|
||||||
doc.rect(SU.mx, bandY, SU.w, bandH, 'F');
|
doc.rect(SU.mx, bandY, SU.w, bandH, 'F');
|
||||||
suKonfetti(doc, t.lc, S, bandY, bandH);
|
|
||||||
inner({ y: bandY }, false);
|
inner({ y: bandY }, false);
|
||||||
}
|
}
|
||||||
cur.y = bandY + bandH + 9 * S;
|
cur.y = bandY + bandH + 9 * S;
|
||||||
|
|
||||||
// --- Recipient (Anschriftfeld) with the date as a pastel chip on the same
|
// --- Recipient (Anschriftfeld) with the date on the same line — DIN keeps its
|
||||||
// line — DIN keeps its address block, the date just stops being a lonely
|
// address block; the date sits right-aligned in the accent, a quiet line
|
||||||
// right-aligned string and becomes part of the layout's chip language.
|
// rather than a pastel chip, so the letter's head is the only block of
|
||||||
|
// colour on the page.
|
||||||
const emp = letter.empfaenger || {};
|
const emp = letter.empfaenger || {};
|
||||||
const empFirma = emp.firma || job.firma || '';
|
const empFirma = emp.firma || job.firma || '';
|
||||||
const empOrt = emp.ort || job.ort || '';
|
const empOrt = emp.ort || job.ort || '';
|
||||||
const datum = stadt ? `${stadt}, ${today}` : today;
|
const datum = stadt ? `${stadt}, ${today}` : today;
|
||||||
if (!dry) {
|
if (!dry) {
|
||||||
const p = pill(doc, t, S, true, 0, 0, datum, { pt: 8.6, style: 'normal' });
|
doc.setFont(doc.__sans, 'normal');
|
||||||
pill(doc, t, S, false, right - p.w, cur.y - 0.5 * S, datum,
|
doc.setFontSize(8.6 * S);
|
||||||
{ pt: 8.6, style: 'normal', bg: t.lc.band, ink: t.lc.accent });
|
doc.setTextColor(t.lc.accent[0], t.lc.accent[1], t.lc.accent[2]);
|
||||||
|
// Align the date's baseline with the first recipient line so the two read
|
||||||
|
// as one row rather than two floats at different heights.
|
||||||
|
doc.text(datum, right, cur.y + 10.2 * S * PT * 0.76, { align: 'right' });
|
||||||
}
|
}
|
||||||
const empW = width * 0.6; // keep the address clear of the date chip
|
const empW = width * 0.6; // keep the address clear of the date line
|
||||||
if (empFirma) write(doc, S, dry, cur, empFirma, { pt: 10.2, style: 'bold', color: t.lc.ink, x, width: empW, factor: 1.32 });
|
if (empFirma) write(doc, S, dry, cur, empFirma, { pt: 10.2, style: 'bold', color: t.lc.ink, x, width: empW, factor: 1.32 });
|
||||||
if (emp.ansprechpartner) write(doc, S, dry, cur, `z. Hd. ${emp.ansprechpartner}`, { pt: 10.2, color: t.lc.ink, x, width: empW, factor: 1.32 });
|
if (emp.ansprechpartner) write(doc, S, dry, cur, `z. Hd. ${emp.ansprechpartner}`, { pt: 10.2, color: t.lc.ink, x, width: empW, factor: 1.32 });
|
||||||
if (emp.adresse) write(doc, S, dry, cur, emp.adresse, { pt: 10.2, color: t.lc.ink, x, width: empW, factor: 1.32 });
|
if (emp.adresse) write(doc, S, dry, cur, emp.adresse, { pt: 10.2, color: t.lc.ink, x, width: empW, factor: 1.32 });
|
||||||
@@ -2337,13 +2319,13 @@ function composeLetterSunny(doc, t, S, dry, cur, { letter, header, job, anlagen,
|
|||||||
cur.y += 9 * S;
|
cur.y += 9 * S;
|
||||||
suBar(doc, t.lc, S, dry, cur.y, 'left', 0.34);
|
suBar(doc, t.lc, S, dry, cur.y, 'left', 0.34);
|
||||||
cur.y += 2.4 * S + 4.2 * S;
|
cur.y += 2.4 * S + 4.2 * S;
|
||||||
write(doc, S, dry, cur, betreff, { pt: 15, style: 'bold', color: t.lc.accent, x, width, factor: 1.18 });
|
write(doc, S, dry, cur, betreff, { pt: 13.5, style: 'bold', color: t.lc.ink, x, width, factor: 1.18 });
|
||||||
|
|
||||||
// --- Salutation + body. The salutation carries the accent: it is the moment
|
// --- Salutation + body. The salutation is set bold in ink rather than the
|
||||||
// the letter starts speaking, and it echoes the coloured entry titles of
|
// accent — the bar above the subject is the page's one colour cue, and the
|
||||||
// the résumé.
|
// letter's voice stays quiet so the résumé can be the loud twin.
|
||||||
cur.y += 5.5 * S;
|
cur.y += 5.5 * S;
|
||||||
if (letter.anrede) { write(doc, S, dry, cur, letter.anrede, { pt: 10.2, style: 'bold', color: t.lc.accent, x, width, factor: 1.4 }); cur.y += 2.8 * S; }
|
if (letter.anrede) { write(doc, S, dry, cur, letter.anrede, { pt: 10.2, style: 'bold', color: t.lc.ink, x, width, factor: 1.4 }); cur.y += 2.8 * S; }
|
||||||
letter.absaetze.forEach((p, i) => {
|
letter.absaetze.forEach((p, i) => {
|
||||||
if (i > 0) cur.y += 3 * S;
|
if (i > 0) cur.y += 3 * S;
|
||||||
write(doc, S, dry, cur, p, { pt: 10.2, color: t.lc.ink, x, width, factor: 1.52 });
|
write(doc, S, dry, cur, p, { pt: 10.2, color: t.lc.ink, x, width, factor: 1.52 });
|
||||||
@@ -2369,24 +2351,21 @@ function composeLetterSunny(doc, t, S, dry, cur, { letter, header, job, anlagen,
|
|||||||
if (!dry) doc.addImage(signatur.dataUrl, signatur.format || 'PNG', x, cur.y, sigW, sigH);
|
if (!dry) doc.addImage(signatur.dataUrl, signatur.format || 'PNG', x, cur.y, sigW, sigH);
|
||||||
cur.y += sigH;
|
cur.y += sigH;
|
||||||
} else {
|
} else {
|
||||||
// No signature image: the typed name gets a pastel highlight swiped behind
|
// No signature image: the typed name stands alone in the accent with a
|
||||||
// it, like a marker stroke — the flourish that stands in for the missing
|
// short hairline beneath it — a quiet flourish in place of the missing
|
||||||
// handwriting instead of leaving an empty gap.
|
// handwriting, instead of the poster's loud pastel marker swipe.
|
||||||
cur.y += 12 * S;
|
cur.y += 12 * S;
|
||||||
const pt = 10.6;
|
const pt = 10.6;
|
||||||
|
const baseY = cur.y + pt * S * PT * 0.76;
|
||||||
|
write(doc, S, dry, cur, header.name, { pt, style: 'bold', color: t.lc.accent, x, width, factor: 1.25 });
|
||||||
if (!dry) {
|
if (!dry) {
|
||||||
doc.setFont(doc.__sans, 'bold');
|
doc.setFont(doc.__sans, 'bold');
|
||||||
doc.setFontSize(pt * S);
|
doc.setFontSize(pt * S);
|
||||||
const w = doc.getTextWidth(String(header.name || ''));
|
const w = Math.min(doc.getTextWidth(String(header.name || '')), 42 * S);
|
||||||
// Centre the swipe on the glyphs: `write` puts the baseline at
|
doc.setDrawColor(t.lc.accent[0], t.lc.accent[1], t.lc.accent[2]);
|
||||||
// cur.y + pt*PT*0.76, so a box hung off cur.y alone sits too low and the
|
doc.setLineWidth(0.35 * S);
|
||||||
// capitals poke out over its top edge.
|
doc.line(x, baseY + 1.6 * S, x + w, baseY + 1.6 * S);
|
||||||
const h = pt * S * PT * 1.2;
|
|
||||||
const y = cur.y + pt * S * PT * 0.76 - h * 0.72;
|
|
||||||
doc.setFillColor(t.lc.band[0], t.lc.band[1], t.lc.band[2]);
|
|
||||||
doc.roundedRect(x - 1.8 * S, y, w + 3.6 * S, h, 1.4 * S, 1.4 * S, 'F');
|
|
||||||
}
|
}
|
||||||
write(doc, S, dry, cur, header.name, { pt, style: 'bold', color: t.lc.accent, x, width, factor: 1.25 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Enclosures as chips, in the same pill language as the contact details ---
|
// --- Enclosures as chips, in the same pill language as the contact details ---
|
||||||
@@ -2401,7 +2380,10 @@ function composeLetterSunny(doc, t, S, dry, cur, { letter, header, job, anlagen,
|
|||||||
cur.y = c.y;
|
cur.y = c.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Footer: the contact strip on a pastel bar, in the reserved bottom margin ---
|
// --- Footer: a single hairline above a quiet, centred contact line — the
|
||||||
|
// letter's footer stays as contact information, not a second band of
|
||||||
|
// colour echoing the head. The poster résumé can carry the dots; the
|
||||||
|
// letter closes with a rule instead.
|
||||||
if (!dry) {
|
if (!dry) {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
if (header.adresse) {
|
if (header.adresse) {
|
||||||
@@ -2413,17 +2395,15 @@ function composeLetterSunny(doc, t, S, dry, cur, { letter, header, job, anlagen,
|
|||||||
if (parts.length) {
|
if (parts.length) {
|
||||||
const fh = 7.5 * S;
|
const fh = 7.5 * S;
|
||||||
const fy = PAGE.h - SU.bottom - fh;
|
const fy = PAGE.h - SU.bottom - fh;
|
||||||
doc.setFillColor(t.lc.band[0], t.lc.band[1], t.lc.band[2]);
|
doc.setDrawColor(t.lc.hair[0], t.lc.hair[1], t.lc.hair[2]);
|
||||||
doc.rect(SU.mx, fy, SU.w, fh, 'F');
|
doc.setLineWidth(0.4 * S);
|
||||||
// Dots closing the strip at both ends — the head's confetti, answered.
|
doc.line(SU.mx, fy, SU.r, fy);
|
||||||
suDot(doc, SU.mx + 4 * S, fy + fh / 2, 1.1 * S, t.lc.accent);
|
|
||||||
suDot(doc, SU.r - 4 * S, fy + fh / 2, 1.1 * S, t.lc.accent);
|
|
||||||
|
|
||||||
// Fit the strip to the band. A long address plus a long e-mail address
|
// Fit the strip to the line. A long address plus a long e-mail address
|
||||||
// overruns it at a fixed size — the line then slides out over the dots and
|
// overruns it at a fixed size. Shrink to fit; if even the floor is too
|
||||||
// off the band's edge. Shrink to fit; if even the floor is too wide, drop
|
// wide, drop the postal address and keep the two ways to actually reach
|
||||||
// the postal address and keep the two ways to actually reach the person.
|
// the person.
|
||||||
const raum = SU.w - 16 * S; // band minus the dots at both ends
|
const raum = SU.w - 6 * S;
|
||||||
doc.setFont(doc.__sans, 'normal');
|
doc.setFont(doc.__sans, 'normal');
|
||||||
const passt = (text, pt) => {
|
const passt = (text, pt) => {
|
||||||
doc.setFontSize(pt * S);
|
doc.setFontSize(pt * S);
|
||||||
@@ -2442,7 +2422,7 @@ function composeLetterSunny(doc, t, S, dry, cur, { letter, header, job, anlagen,
|
|||||||
if (fpt === null) fpt = 5.4; // Notnagel: lieber klein als über den Rand
|
if (fpt === null) fpt = 5.4; // Notnagel: lieber klein als über den Rand
|
||||||
|
|
||||||
doc.setFontSize(fpt * S);
|
doc.setFontSize(fpt * S);
|
||||||
doc.setTextColor(t.lc.accent[0], t.lc.accent[1], t.lc.accent[2]);
|
doc.setTextColor(t.lc.muted[0], t.lc.muted[1], t.lc.muted[2]);
|
||||||
doc.text(strip, PAGE.w / 2, fy + fh / 2 + fpt * S * PT * 0.35, { align: 'center' });
|
doc.text(strip, PAGE.w / 2, fy + fh / 2 + fpt * S * PT * 0.35, { align: 'center' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user