@heyramzi/marketing 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +2 -1
  2. package/src/brand/deck.ts +110 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyramzi/marketing",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Umami analytics for client, server, and Svelte, plus a satori-based HTML-to-image pipeline.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,6 +25,7 @@
25
25
  "./analytics/unified": "./src/analytics/unified.ts",
26
26
  "./imagegen": "./src/imagegen/index.ts",
27
27
  "./brand": "./src/brand/index.ts",
28
+ "./brand/deck": "./src/brand/deck.ts",
28
29
  "./testimonials/types": "./src/testimonials/types.ts",
29
30
  "./testimonials/server": "./src/testimonials/server.ts",
30
31
  "./testimonials/render-content": "./src/testimonials/render-content.ts",
@@ -0,0 +1,110 @@
1
+ // The one source of UpSys deck tokens. The app's Svelte layouts and the
2
+ // standalone proposal deck both read these, so the two cannot drift.
3
+
4
+ import { BRAND_HEX } from "#src/brand/index.js";
5
+
6
+ // Flat hex, not oklch: Chromium's print pipeline shifts oklch on the way to PDF.
7
+ // A colour the brand palette already holds comes from BRAND_HEX, never a second literal.
8
+ export const DECK_COLORS = {
9
+ /** Page ground for every slide except the cover and the close. */
10
+ ink: BRAND_HEX.ink,
11
+ /** The cover and the close sit a step lighter so the deck bookends. */
12
+ night: BRAND_HEX.night,
13
+ /** Hairlines and the rules under a section. */
14
+ veil: BRAND_HEX.veil,
15
+ /** Row separators, quieter than veil. */
16
+ dusk: "#0d1a32",
17
+ /** Body text on ink. */
18
+ cream: BRAND_HEX.cream,
19
+ /** Headlines and anything that has to read first. */
20
+ linen: BRAND_HEX.linen,
21
+ indigo: BRAND_HEX.indigo,
22
+ /** Links and the sub-line under a cover title. */
23
+ indigo200: "#b2c2f9",
24
+ /** Lede paragraphs and the say column beside a screenshot. */
25
+ indigo100: "#dbe4ff",
26
+ /** Prices, and the heading of the left column in a two-column slide. */
27
+ sand: BRAND_HEX.sand,
28
+ /** The accent rule. One per slide, never decoration. */
29
+ terra: BRAND_HEX.terra,
30
+ terra200: "#feac9e",
31
+ /** The positive green, from `--success-600`, within 0.3 of terra's contrast on ink. */
32
+ moss: "#2f9f3d",
33
+ /** Captions. */
34
+ dim: "#8fa0c0",
35
+ /** Source lines, the smallest type on any slide. */
36
+ dimmer: "#5d6d8c",
37
+ } as const;
38
+
39
+ /**
40
+ * The type ramp, in design pixels on a 960x540 slide. The standalone deck uses
41
+ * these times 5/3. Keep the ratios; don't invent a size.
42
+ */
43
+ export const DECK_TYPE = {
44
+ coverTitle: { size: 66, weight: 800, tracking: "-0.03em", leading: 1.02 },
45
+ coverSub: { size: 20, weight: 600, tracking: "-0.02em", leading: 1.2 },
46
+ statement: { size: 42, weight: 800, tracking: "-0.03em", leading: 1.05 },
47
+ slideTitle: { size: 32, weight: 800, tracking: "-0.03em", leading: 1.05 },
48
+ sayTitle: { size: 22, weight: 700, tracking: "-0.02em", leading: 1.2 },
49
+ figure: { size: 35, weight: 800, tracking: "-0.04em", leading: 1 },
50
+ price: { size: 40, weight: 800, tracking: "-0.04em", leading: 1 },
51
+ /** One sentence that is the slide. `body` stays at 14 because narrow boxes are measured against it. */
52
+ claim: { size: 18, weight: 400, tracking: "0", leading: 1.45 },
53
+ lede: { size: 17, weight: 400, tracking: "0", leading: 1.5 },
54
+ body: { size: 14, weight: 400, tracking: "0", leading: 1.5 },
55
+ caption: { size: 11, weight: 400, tracking: "0", leading: 1.45 },
56
+ label: { size: 10, weight: 400, tracking: "0.08em", leading: 1.4 },
57
+ } as const;
58
+
59
+ export type DeckTypeToken = keyof typeof DECK_TYPE;
60
+
61
+ /**
62
+ * The cover lockup: the client's mark beside the UpSys logo. Design pixels on a
63
+ * 960x540 slide.
64
+ */
65
+ export const DECK_LOCKUP = {
66
+ /** The tile under the client mark. Client logos ship on white; night eats them. */
67
+ clientTile: 96,
68
+ clientPad: 14,
69
+ clientRadius: 16,
70
+ /** Wordmark height. The mark's own ratio makes the width 4.3x this. */
71
+ agencyLogo: 30,
72
+ /** Between the tile, the rule, and the wordmark. */
73
+ gap: 26,
74
+ /** The hairline that separates the two marks. */
75
+ ruleHeight: 52,
76
+ } as const;
77
+
78
+ /**
79
+ * Slide margins in design pixels on a 960x540 slide. One gutter for every
80
+ * slide, so headlines sit on the same vertical line across the deck.
81
+ */
82
+ export const DECK_SPACE = {
83
+ gutter: 78,
84
+ top: 53,
85
+ bottom: 35,
86
+ /** The accent rule above a slide title. One per slide, never decoration. */
87
+ ruleWidth: 72,
88
+ ruleHeight: 3,
89
+ } as const;
90
+
91
+ /** The UpSys wordmark's ink on the cover ground. The in-app cover and the PDF both draw it. */
92
+ export const DECK_AGENCY_LOGO_FILL = "#F3F3F3";
93
+
94
+ export const DECK_FONTS = {
95
+ heading: "Manrope, system-ui, sans-serif",
96
+ body: "Satoshi, system-ui, sans-serif",
97
+ mono: '"Space Grotesk", ui-monospace, monospace',
98
+ } as const;
99
+
100
+ /** Inline style string for a type token, for `style={...}` on an element. */
101
+ export function deckType(token: DeckTypeToken, family: keyof typeof DECK_FONTS) {
102
+ const t = DECK_TYPE[token];
103
+ return [
104
+ `font-family:${DECK_FONTS[family]}`,
105
+ `font-size:${t.size}px`,
106
+ `font-weight:${t.weight}`,
107
+ `letter-spacing:${t.tracking}`,
108
+ `line-height:${t.leading}`,
109
+ ].join(";");
110
+ }