Design: drittes Layout "Sunny" für Anschreiben und Lebenslauf
Plakatives Layout nach Vorlage eines Referenz-Lebenslaufs: weiße Seite, Pastellband im Kopf mit überstehendem Foto, Name groß daneben, Zielposition als solides Badge, überdimensionale Display-Überschriften unter Pastellbalken, die abwechselnd links und rechts stehen, zweispaltige Einträge und Level-Balken für die Sprachen. Die Balkenlänge kommt aus dem im Lebenslauf genannten Niveau (Muttersprache, C1, B1 …) – es wird kein Prozentwert erfunden, der nicht in den Unterlagen steht. Studium, Berufsausbildung und Weiterbildungen laufen unter einer gemeinsamen Überschrift "Ausbildung": drei Überschriften dieser Größe hintereinander würden die Seite auffressen. Neue Akzentfarbe "Beere" als Standard für das Layout. Die Layout-Weiche steckt jetzt in einer Tabelle (CV_LAYOUT / LETTER_LAYOUT) statt in Ternären – ein weiteres Layout ist eine Zeile dort plus seine Palette in lib/design.js. Sidebar und Social bleiben unverändert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+104
-46
@@ -3,7 +3,7 @@
|
||||
// Same contract as lib/prompts.js: the defaults live here, an override is a row
|
||||
// in the `design` table, and deleting the row restores the default.
|
||||
//
|
||||
// Two layouts ship today:
|
||||
// Three layouts ship today:
|
||||
//
|
||||
// sidebar — the classic: neutral greys, tinted sidebar, DIN-5008 letter. The
|
||||
// accent is a single restrained colour; hierarchy comes from weight
|
||||
@@ -13,15 +13,21 @@
|
||||
// headline, a full-page tinted background). Right for creative,
|
||||
// social-media and design-adjacent roles — and deliberately loud.
|
||||
//
|
||||
// sunny — an editorial poster look: white page, oversized display headings
|
||||
// in a deep accent, pastel accent bars that alternate left/right, a
|
||||
// photo breaking out of a tinted hero band, a solid role badge and
|
||||
// level bars for the languages. Bold but tidy — right for marketing,
|
||||
// media and design roles that still want a readable, gridded page.
|
||||
//
|
||||
// The cover letter always follows the résumé's layout, so both documents arrive
|
||||
// as one coherent set — that is the whole point of picking a design here.
|
||||
//
|
||||
// The palette stays closed (no free colour picker): every tone is one that still
|
||||
// works when an HR department prints it in greyscale.
|
||||
|
||||
// Accent colours. In `sidebar` the accent is the *only* colour; in `social` the
|
||||
// whole page palette (background, cards, pills) is derived from it, so the
|
||||
// playful layout works in pink as well as in deep blue.
|
||||
// Accent colours. In `sidebar` the accent is the *only* colour; in `social` and
|
||||
// `sunny` the whole page palette (bands, cards, pills) is derived from it, so
|
||||
// those layouts work in berry as well as in deep blue.
|
||||
const AKZENTE = [
|
||||
{ key: 'anthrazit', label: 'Anthrazit', rgb: [23, 23, 23] },
|
||||
{ key: 'tiefblau', label: 'Tiefblau', rgb: [30, 58, 95] },
|
||||
@@ -29,6 +35,7 @@ const AKZENTE = [
|
||||
{ key: 'waldgruen', label: 'Waldgrün', rgb: [27, 67, 50] },
|
||||
{ key: 'stahlblau', label: 'Stahlblau', rgb: [51, 65, 85] },
|
||||
{ key: 'kupfer', label: 'Kupfer', rgb: [124, 62, 24] },
|
||||
{ key: 'beere', label: 'Beere (Sunny)', rgb: [109, 24, 74] },
|
||||
{ key: 'pink', label: 'Pink (verspielt)', rgb: [232, 54, 143] },
|
||||
{ key: 'violett', label: 'Violett (verspielt)', rgb: [147, 51, 187] },
|
||||
];
|
||||
@@ -36,6 +43,7 @@ const AKZENTE = [
|
||||
const LAYOUTS = [
|
||||
{ key: 'sidebar', label: 'Klassisch mit Sidebar (Standard)' },
|
||||
{ key: 'social', label: 'Social Media (verspielt)' },
|
||||
{ key: 'sunny', label: 'Sunny (plakativ, Farbbänder)' },
|
||||
];
|
||||
|
||||
const SIDEBAR_VARIANTEN = [
|
||||
@@ -63,6 +71,9 @@ const SCHRIFT_STUFEN = [
|
||||
const LAYOUT_DEFAULTS = {
|
||||
sidebar: { akzent: 'anthrazit', foto_form: 'eckig' },
|
||||
social: { akzent: 'pink', foto_form: 'rund' },
|
||||
// Sunny is built around the berry/pastel pairing of its reference; the photo
|
||||
// sits in a square frame that breaks out of the hero band, so square it is.
|
||||
sunny: { akzent: 'beere', foto_form: 'eckig' },
|
||||
};
|
||||
|
||||
const DEFAULTS = {
|
||||
@@ -129,18 +140,36 @@ function settings(overrides = {}) {
|
||||
function resolve(overrides = {}) {
|
||||
const s = settings(overrides);
|
||||
const accent = findOption(AKZENTE, s.akzent, 'anthrazit').rgb;
|
||||
const social = s.layout === 'social';
|
||||
|
||||
let sidebarBg;
|
||||
if (s.sidebar === 'weiss') sidebarBg = [255, 255, 255];
|
||||
else if (s.sidebar === 'getoent') sidebarBg = tint(accent, 0.08);
|
||||
else sidebarBg = NEUTRAL_SIDEBAR;
|
||||
|
||||
// The playful layout derives its whole palette from the accent: a tinted page,
|
||||
// near-white cards with a soft border, pill chips, and a warm dark ink that is
|
||||
// the accent shaded down rather than plain black.
|
||||
const rc = social
|
||||
? {
|
||||
// Résumé palettes, one per layout. Every key exists in every palette so the
|
||||
// renderer can read `t.rc.<x>` without knowing which layout it is drawing.
|
||||
const RC = {
|
||||
sidebar: {
|
||||
ink: [33, 33, 33],
|
||||
sub: [92, 96, 100],
|
||||
hair: [208, 210, 214],
|
||||
accent,
|
||||
pageBg: null, // no page tint in the classic layout
|
||||
cardBg: [255, 255, 255],
|
||||
cardBorder: [208, 210, 214],
|
||||
deko: [208, 210, 214], // the classic layout draws no sparkles; kept for shape parity
|
||||
band: tint(accent, 0.14),
|
||||
pillBg: tint(accent, 0.12),
|
||||
pillInk: accent,
|
||||
sidebarBg,
|
||||
track: [199, 202, 208],
|
||||
onSide: [38, 40, 44],
|
||||
onSideSub: [96, 100, 106],
|
||||
},
|
||||
// The playful layout derives its whole palette from the accent: a tinted
|
||||
// page, near-white cards with a soft border, pill chips, and a warm dark ink
|
||||
// that is the accent shaded down rather than plain black.
|
||||
social: {
|
||||
ink: shade(accent, 0.35),
|
||||
sub: tint(shade(accent, 0.55), 0.75),
|
||||
hair: tint(accent, 0.30),
|
||||
@@ -151,46 +180,41 @@ function resolve(overrides = {}) {
|
||||
// Sparkles/blob sit on the tinted page, so they need more punch than the
|
||||
// card border or they simply vanish.
|
||||
deko: tint(accent, 0.45),
|
||||
band: tint(accent, 0.22),
|
||||
pillBg: tint(accent, 0.18),
|
||||
pillInk: shade(accent, 0.6),
|
||||
sidebarBg, // unused in this layout, kept so the shape stays stable
|
||||
track: tint(accent, 0.45),
|
||||
onSide: shade(accent, 0.35),
|
||||
onSideSub: tint(shade(accent, 0.55), 0.75),
|
||||
}
|
||||
: {
|
||||
ink: [33, 33, 33],
|
||||
sub: [92, 96, 100],
|
||||
hair: [208, 210, 214],
|
||||
},
|
||||
// Sunny keeps the page white and puts the colour into solid blocks: pastel
|
||||
// bands and rails on one side, the full-strength accent in the display
|
||||
// headings and the role badge. Only two tones, used at maximum contrast.
|
||||
sunny: {
|
||||
ink: [40, 36, 42],
|
||||
sub: [124, 118, 126],
|
||||
hair: tint(accent, 0.22),
|
||||
accent,
|
||||
pageBg: null, // no page tint in the classic layout
|
||||
pageBg: null, // white page — the colour lives in the bands
|
||||
cardBg: [255, 255, 255],
|
||||
cardBorder: [208, 210, 214],
|
||||
deko: [208, 210, 214], // the classic layout draws no sparkles; kept for shape parity
|
||||
pillBg: tint(accent, 0.12),
|
||||
pillInk: accent,
|
||||
sidebarBg,
|
||||
track: [199, 202, 208],
|
||||
onSide: [38, 40, 44],
|
||||
onSideSub: [96, 100, 106],
|
||||
};
|
||||
cardBorder: tint(accent, 0.22),
|
||||
deko: tint(accent, 0.30),
|
||||
band: tint(accent, 0.24), // the pastel hero band and section bars
|
||||
pillBg: accent, // solid badge…
|
||||
pillInk: [255, 255, 255], // …with the text knocked out white
|
||||
sidebarBg, // unused
|
||||
track: tint(accent, 0.16), // rail behind a language level bar
|
||||
onSide: [40, 36, 42],
|
||||
onSideSub: [124, 118, 126],
|
||||
},
|
||||
};
|
||||
const rc = RC[s.layout] || RC.sidebar;
|
||||
|
||||
// The cover letter always mirrors the résumé, so the two documents read as one
|
||||
// set — a playful CV never arrives with a stiff monochrome letter.
|
||||
const lc = social
|
||||
? {
|
||||
ink: rc.ink,
|
||||
muted: rc.sub,
|
||||
hair: rc.hair,
|
||||
accent,
|
||||
pageBg: rc.pageBg,
|
||||
cardBg: rc.cardBg,
|
||||
cardBorder: rc.cardBorder,
|
||||
deko: rc.deko,
|
||||
pillBg: rc.pillBg,
|
||||
pillInk: rc.pillInk,
|
||||
}
|
||||
: {
|
||||
const LC = {
|
||||
sidebar: {
|
||||
ink: [26, 26, 26],
|
||||
muted: [92, 96, 100],
|
||||
hair: [206, 208, 212],
|
||||
@@ -199,18 +223,52 @@ function resolve(overrides = {}) {
|
||||
cardBg: [255, 255, 255],
|
||||
cardBorder: [206, 208, 212],
|
||||
deko: [206, 208, 212],
|
||||
band: rc.band,
|
||||
pillBg: tint(accent, 0.12),
|
||||
pillInk: accent,
|
||||
};
|
||||
},
|
||||
social: {
|
||||
ink: rc.ink,
|
||||
muted: rc.sub,
|
||||
hair: rc.hair,
|
||||
accent,
|
||||
pageBg: rc.pageBg,
|
||||
cardBg: rc.cardBg,
|
||||
cardBorder: rc.cardBorder,
|
||||
deko: rc.deko,
|
||||
band: rc.band,
|
||||
pillBg: rc.pillBg,
|
||||
pillInk: rc.pillInk,
|
||||
},
|
||||
sunny: {
|
||||
ink: rc.ink,
|
||||
muted: rc.sub,
|
||||
hair: rc.hair,
|
||||
accent,
|
||||
pageBg: rc.pageBg,
|
||||
cardBg: rc.cardBg,
|
||||
cardBorder: rc.cardBorder,
|
||||
deko: rc.deko,
|
||||
band: rc.band,
|
||||
pillBg: rc.pillBg,
|
||||
pillInk: rc.pillInk,
|
||||
},
|
||||
};
|
||||
const lc = LC[s.layout] || LC.sidebar;
|
||||
|
||||
// Which typefaces the renderer registers and draws with. The playful layout
|
||||
// adds a script face for its "Hallo, ich bin" line; Sunny lives off Poppins
|
||||
// Bold alone (its whole hierarchy is size, not style); the classic one stays
|
||||
// on Lato and embeds nothing extra.
|
||||
const FONTS = {
|
||||
sidebar: { sans: 'Lato', script: null },
|
||||
social: { sans: 'Poppins', script: 'Pacifico' },
|
||||
sunny: { sans: 'Poppins', script: null },
|
||||
};
|
||||
|
||||
return {
|
||||
layout: s.layout,
|
||||
// Which typefaces the renderer registers and draws with. The playful layout
|
||||
// uses a geometric sans plus a script face for the "Hallo, ich bin" line;
|
||||
// the classic one stays on Lato and embeds nothing extra.
|
||||
fonts: social
|
||||
? { sans: 'Poppins', script: 'Pacifico' }
|
||||
: { sans: 'Lato', script: null },
|
||||
fonts: FONTS[s.layout] || FONTS.sidebar,
|
||||
rc,
|
||||
lc,
|
||||
foto: {
|
||||
|
||||
Reference in New Issue
Block a user