@loworbitstudio/visor-theme-engine 0.16.0 → 0.17.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.
@@ -1,4 +1,4 @@
1
- import { k as GeneratedPrimitives, r as SemanticTokens, R as ResolvedThemeConfig } from '../types-BstIS9rL.js';
1
+ import { k as GeneratedPrimitives, r as SemanticTokens, R as ResolvedThemeConfig } from '../types-DqsWwFVZ.js';
2
2
 
3
3
  /**
4
4
  * Adapter types for the Visor theme engine.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FontResolveOptions, a as FontResolution, V as VisorTypography, b as FontDisplayStrategy, T as ThemeFontResult, G as GoogleFontEntry, c as VisorBrand, B as BrandSlot, d as BrandSource, e as BrandResolution, f as ThemeBrandResult, g as BrandStrategy, h as BrandStrategyContext, i as BrandStrategyIssue, j as BrandStrategyValidationResult, S as SerializedBrandStrategy, R as ResolvedThemeConfig, k as GeneratedPrimitives, l as ThemeOutput, m as ThemeData, n as VisorThemeConfig, o as FullShadeScale, C as ColorRole, p as SelectiveShadeScale, q as RGB, P as ParsedColor, O as OKLCH, r as SemanticTokens, s as ShadeStep } from './types-BstIS9rL.js';
2
- export { t as BRAND_VARIANTS, u as BRAND_VISIBILITIES, v as BrandArchetype, w as BrandGoverns, x as BrandLexiconEntry, y as BrandPersonalityTrait, z as BrandPillar, A as BrandPositioning, D as BrandStrategyIssueSeverity, E as BrandToneEntry, H as BrandVariant, I as BrandVisibility, J as BrandVoice, K as BrandVoiceTrait, L as ColorFormat, M as DEFAULT_BRAND_STRATEGY_SURFACES, N as DEFAULT_BRAND_STRATEGY_TONE_STATES, Q as FontSource, U as GOVERNS_WILDCARD, W as RGBA, X as SemanticTokenValue } from './types-BstIS9rL.js';
1
+ import { F as FontResolveOptions, a as FontResolution, V as VisorTypography, b as FontDisplayStrategy, T as ThemeFontResult, G as GoogleFontEntry, c as VisorBrand, B as BrandSlot, d as BrandSource, e as BrandResolution, f as ThemeBrandResult, g as BrandStrategy, h as BrandStrategyContext, i as BrandStrategyIssue, j as BrandStrategyValidationResult, S as SerializedBrandStrategy, R as ResolvedThemeConfig, k as GeneratedPrimitives, l as ThemeOutput, m as ThemeData, n as VisorThemeConfig, o as FullShadeScale, C as ColorRole, p as SelectiveShadeScale, q as RGB, P as ParsedColor, O as OKLCH, r as SemanticTokens, s as ShadeStep } from './types-DqsWwFVZ.js';
2
+ export { t as BRAND_VARIANTS, u as BRAND_VISIBILITIES, v as BrandAccessibility, w as BrandArchetype, x as BrandBoilerplate, y as BrandColorPairing, z as BrandColorUsage, A as BrandContrastTarget, D as BrandGoverns, E as BrandLexiconEntry, H as BrandMessaging, I as BrandPersonalityTrait, J as BrandPillar, K as BrandPositioning, L as BrandStrategyIssueSeverity, M as BrandToneEntry, N as BrandVariant, Q as BrandVisibility, U as BrandVoice, W as BrandVoiceTrait, X as ColorFormat, Y as DEFAULT_BRAND_STRATEGY_SURFACES, Z as DEFAULT_BRAND_STRATEGY_TONE_STATES, _ as FontSource, $ as GOVERNS_WILDCARD, a0 as RGBA, a1 as SemanticTokenValue } from './types-DqsWwFVZ.js';
3
3
 
4
4
  /**
5
5
  * Font resolver — maps font family names to loadable font resources.
@@ -291,8 +291,11 @@ declare function validateBrandStrategy(block: unknown, ctx?: BrandStrategyContex
291
291
  /**
292
292
  * Serialize a validated brand strategy for the manifest. Returns `null` when
293
293
  * `visibility: private`. Optional fields (`archetype.secondary`,
294
- * `voice.traits[].example`, absent `governs` lists) are emitted only when
295
- * present, so the agent reads a clean, predictable object.
294
+ * `voice.traits[].example`, absent `governs` lists, the Phase 2 wave-1 fields
295
+ * `pillars[].proof` / `messaging` / `taglines` / `boilerplate` / `colorUsage` /
296
+ * `accessibility`) are emitted only when present, so the agent reads a clean,
297
+ * predictable object. The Phase 2 fields are all PUBLIC — a private record
298
+ * never reaches this point, so visibility is enforced once, up top (D4).
296
299
  */
297
300
  declare function serializeBrandStrategy(strategy: BrandStrategy): SerializedBrandStrategy | null;
298
301
 
@@ -856,6 +859,27 @@ var properties = {
856
859
  "private"
857
860
  ],
858
861
  description: "Client brands are private and are omitted from the public agent manifest."
862
+ },
863
+ messaging: {
864
+ $ref: "#/$defs/brandMessaging"
865
+ },
866
+ taglines: {
867
+ type: "array",
868
+ items: {
869
+ type: "string",
870
+ minLength: 1
871
+ },
872
+ minItems: 1,
873
+ description: "Phase 2 wave-1 (VI-541). Permanent, brand-level signature line(s) — who the brand is, not what it sells this quarter."
874
+ },
875
+ boilerplate: {
876
+ $ref: "#/$defs/brandBoilerplate"
877
+ },
878
+ colorUsage: {
879
+ $ref: "#/$defs/brandColorUsage"
880
+ },
881
+ accessibility: {
882
+ $ref: "#/$defs/brandAccessibility"
859
883
  }
860
884
  }
861
885
  },
@@ -1208,6 +1232,15 @@ var $defs = {
1208
1232
  },
1209
1233
  governs: {
1210
1234
  $ref: "#/$defs/brandGoverns"
1235
+ },
1236
+ proof: {
1237
+ type: "array",
1238
+ items: {
1239
+ type: "string",
1240
+ minLength: 1
1241
+ },
1242
+ minItems: 1,
1243
+ description: "Phase 2 wave-1 (VI-541). Reasons-to-believe — checkable evidence backing this pillar's claim (the message-house foundation)."
1211
1244
  }
1212
1245
  }
1213
1246
  },
@@ -1298,6 +1331,134 @@ var $defs = {
1298
1331
  minLength: 1
1299
1332
  }
1300
1333
  }
1334
+ },
1335
+ brandMessaging: {
1336
+ type: "object",
1337
+ description: "Phase 2 wave-1 (VI-541). Message-house roof — the single umbrella message above the pillars.",
1338
+ additionalProperties: false,
1339
+ required: [
1340
+ "roof"
1341
+ ],
1342
+ properties: {
1343
+ roof: {
1344
+ type: "string",
1345
+ minLength: 1,
1346
+ description: "One overarching statement the pillars support."
1347
+ }
1348
+ }
1349
+ },
1350
+ brandBoilerplate: {
1351
+ type: "object",
1352
+ description: "Phase 2 wave-1 (VI-541). Reusable \"about us\" copy — short and long forms.",
1353
+ additionalProperties: false,
1354
+ required: [
1355
+ "short",
1356
+ "long"
1357
+ ],
1358
+ properties: {
1359
+ short: {
1360
+ type: "string",
1361
+ minLength: 1
1362
+ },
1363
+ long: {
1364
+ type: "string",
1365
+ minLength: 1
1366
+ }
1367
+ }
1368
+ },
1369
+ brandColorPairing: {
1370
+ type: "object",
1371
+ description: "A color-pairing rule expressed as brand intent (not a computed value).",
1372
+ additionalProperties: false,
1373
+ required: [
1374
+ "use",
1375
+ "with",
1376
+ "rule"
1377
+ ],
1378
+ properties: {
1379
+ use: {
1380
+ type: "string",
1381
+ minLength: 1,
1382
+ description: "The token or role being used (e.g. --primary)."
1383
+ },
1384
+ "with": {
1385
+ type: "string",
1386
+ minLength: 1,
1387
+ description: "What it pairs against (token, role, or surface)."
1388
+ },
1389
+ rule: {
1390
+ type: "string",
1391
+ minLength: 1,
1392
+ description: "The intent — when and how the pairing is allowed."
1393
+ }
1394
+ }
1395
+ },
1396
+ brandColorUsage: {
1397
+ type: "object",
1398
+ description: "Phase 2 wave-1 (VI-541). Color-usage intent — the brand's allowed pairings.",
1399
+ additionalProperties: false,
1400
+ required: [
1401
+ "pairings"
1402
+ ],
1403
+ properties: {
1404
+ pairings: {
1405
+ type: "array",
1406
+ items: {
1407
+ $ref: "#/$defs/brandColorPairing"
1408
+ },
1409
+ minItems: 1
1410
+ }
1411
+ }
1412
+ },
1413
+ brandContrastTarget: {
1414
+ type: "object",
1415
+ description: "A contrast target expressed as brand intent (a WCAG 2.1 AA threshold).",
1416
+ additionalProperties: false,
1417
+ required: [
1418
+ "context",
1419
+ "ratio"
1420
+ ],
1421
+ properties: {
1422
+ context: {
1423
+ type: "string",
1424
+ minLength: 1,
1425
+ description: "The text/UI context the target applies to."
1426
+ },
1427
+ ratio: {
1428
+ type: "string",
1429
+ minLength: 1,
1430
+ description: "The minimum contrast ratio (e.g. \"4.5:1\")."
1431
+ }
1432
+ }
1433
+ },
1434
+ brandAccessibility: {
1435
+ type: "object",
1436
+ description: "Phase 2 wave-1 (VI-541). Accessibility intent — the standard and its contrast targets (authored as intent; live computation is the render surface's concern).",
1437
+ additionalProperties: false,
1438
+ required: [
1439
+ "standard",
1440
+ "contrast",
1441
+ "intent"
1442
+ ],
1443
+ properties: {
1444
+ standard: {
1445
+ type: "string",
1446
+ minLength: 1,
1447
+ description: "The conformance standard. Visor targets \"WCAG 2.1 AA\"."
1448
+ },
1449
+ contrast: {
1450
+ type: "array",
1451
+ items: {
1452
+ $ref: "#/$defs/brandContrastTarget"
1453
+ },
1454
+ minItems: 1
1455
+ },
1456
+ intent: {
1457
+ type: "string",
1458
+ minLength: 1,
1459
+ description: "How the brand applies the standard (intent, not computed results)."
1460
+ }
1461
+ }
1301
1462
  }
1302
1463
  };
1303
1464
  var visorTheme_schema = {
package/dist/index.js CHANGED
@@ -248,7 +248,13 @@ var TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
248
248
  "tone",
249
249
  "lexicon",
250
250
  "core",
251
- "visibility"
251
+ "visibility",
252
+ // Phase 2 wave-1 (VI-541) — all optional.
253
+ "messaging",
254
+ "taglines",
255
+ "boilerplate",
256
+ "colorUsage",
257
+ "accessibility"
252
258
  ]);
253
259
  var GOVERNS_KEYS = /* @__PURE__ */ new Set(["tokens", "components", "surfaces"]);
254
260
  function checkBrandStrategyStructure(block) {
@@ -347,6 +353,64 @@ function checkBrandStrategyStructure(block) {
347
353
  if (!isNonEmptyString(block.visibility) || !BRAND_VISIBILITIES.includes(block.visibility)) {
348
354
  errors.push(error("BRAND_STRATEGY_VISIBILITY", `'brand-strategy.visibility' is required and must be one of: ${BRAND_VISIBILITIES.join(", ")}`, "brand-strategy.visibility"));
349
355
  }
356
+ errors.push(...checkPhase2Structure(block));
357
+ return errors;
358
+ }
359
+ function checkPhase2Structure(block) {
360
+ const errors = [];
361
+ if (block.messaging !== void 0) {
362
+ const messaging = block.messaging;
363
+ if (!isObject(messaging) || !isNonEmptyString(messaging.roof)) {
364
+ errors.push(error("BRAND_STRATEGY_MESSAGING", "'brand-strategy.messaging' must be an object with a non-empty 'roof' string", "brand-strategy.messaging"));
365
+ }
366
+ }
367
+ if (block.taglines !== void 0) {
368
+ if (!isStringArray(block.taglines) || block.taglines.length === 0) {
369
+ errors.push(error("BRAND_STRATEGY_TAGLINES", "'brand-strategy.taglines' must be a non-empty array of strings when present", "brand-strategy.taglines"));
370
+ } else if (!block.taglines.every(isNonEmptyString)) {
371
+ errors.push(error("BRAND_STRATEGY_TAGLINES", "'brand-strategy.taglines' entries must be non-empty strings", "brand-strategy.taglines"));
372
+ }
373
+ }
374
+ if (block.boilerplate !== void 0) {
375
+ const boilerplate = block.boilerplate;
376
+ if (!isObject(boilerplate) || !isNonEmptyString(boilerplate.short) || !isNonEmptyString(boilerplate.long)) {
377
+ errors.push(error("BRAND_STRATEGY_BOILERPLATE", "'brand-strategy.boilerplate' must be an object with non-empty 'short' and 'long' strings", "brand-strategy.boilerplate"));
378
+ }
379
+ }
380
+ if (block.colorUsage !== void 0) {
381
+ const colorUsage = block.colorUsage;
382
+ if (!isObject(colorUsage) || !Array.isArray(colorUsage.pairings) || colorUsage.pairings.length === 0) {
383
+ errors.push(error("BRAND_STRATEGY_COLOR_USAGE", "'brand-strategy.colorUsage' must be an object with a non-empty 'pairings' array", "brand-strategy.colorUsage"));
384
+ } else {
385
+ colorUsage.pairings.forEach((pairing, i) => {
386
+ if (!isObject(pairing) || !isNonEmptyString(pairing.use) || !isNonEmptyString(pairing.with) || !isNonEmptyString(pairing.rule)) {
387
+ errors.push(error("BRAND_STRATEGY_COLOR_USAGE", `'brand-strategy.colorUsage.pairings[${i}]' must have non-empty 'use', 'with', and 'rule' strings`, `brand-strategy.colorUsage.pairings[${i}]`));
388
+ }
389
+ });
390
+ }
391
+ }
392
+ if (block.accessibility !== void 0) {
393
+ const accessibility = block.accessibility;
394
+ if (!isObject(accessibility)) {
395
+ errors.push(error("BRAND_STRATEGY_ACCESSIBILITY", "'brand-strategy.accessibility' must be an object", "brand-strategy.accessibility"));
396
+ } else {
397
+ if (!isNonEmptyString(accessibility.standard)) {
398
+ errors.push(error("BRAND_STRATEGY_ACCESSIBILITY", "'brand-strategy.accessibility.standard' is required and must be a non-empty string", "brand-strategy.accessibility.standard"));
399
+ }
400
+ if (!isNonEmptyString(accessibility.intent)) {
401
+ errors.push(error("BRAND_STRATEGY_ACCESSIBILITY", "'brand-strategy.accessibility.intent' is required and must be a non-empty string", "brand-strategy.accessibility.intent"));
402
+ }
403
+ if (!Array.isArray(accessibility.contrast) || accessibility.contrast.length === 0) {
404
+ errors.push(error("BRAND_STRATEGY_ACCESSIBILITY", "'brand-strategy.accessibility.contrast' must be a non-empty array", "brand-strategy.accessibility.contrast"));
405
+ } else {
406
+ accessibility.contrast.forEach((target, i) => {
407
+ if (!isObject(target) || !isNonEmptyString(target.context) || !isNonEmptyString(target.ratio)) {
408
+ errors.push(error("BRAND_STRATEGY_ACCESSIBILITY", `'brand-strategy.accessibility.contrast[${i}]' must have non-empty 'context' and 'ratio' strings`, `brand-strategy.accessibility.contrast[${i}]`));
409
+ }
410
+ });
411
+ }
412
+ }
413
+ }
350
414
  return errors;
351
415
  }
352
416
  function checkPillarStructure(pillar, index) {
@@ -383,6 +447,13 @@ function checkPillarStructure(pillar, index) {
383
447
  errors.push(error("BRAND_STRATEGY_GOVERNS", `'${path}.governs' must declare at least one of: ${[...GOVERNS_KEYS].join(", ")}`, `${path}.governs`));
384
448
  }
385
449
  }
450
+ if (pillar.proof !== void 0) {
451
+ if (!isStringArray(pillar.proof) || pillar.proof.length === 0) {
452
+ errors.push(error("BRAND_STRATEGY_PROOF", `'${path}.proof' must be a non-empty array of strings when present`, `${path}.proof`));
453
+ } else if (!pillar.proof.every(isNonEmptyString)) {
454
+ errors.push(error("BRAND_STRATEGY_PROOF", `'${path}.proof' entries must be non-empty strings`, `${path}.proof`));
455
+ }
456
+ }
386
457
  return errors;
387
458
  }
388
459
  function checkBrandStrategyCoherence(strategy, ctx = {}) {
@@ -442,6 +513,15 @@ function compactGoverns(governs) {
442
513
  if (governs.surfaces) out.surfaces = [...governs.surfaces];
443
514
  return out;
444
515
  }
516
+ function serializePillar(p) {
517
+ const pillar = {
518
+ id: p.id,
519
+ statement: p.statement,
520
+ governs: compactGoverns(p.governs)
521
+ };
522
+ if (p.proof !== void 0) pillar.proof = [...p.proof];
523
+ return pillar;
524
+ }
445
525
  function serializeBrandStrategy(strategy) {
446
526
  if (strategy.visibility === "private") {
447
527
  return null;
@@ -453,7 +533,7 @@ function serializeBrandStrategy(strategy) {
453
533
  for (const [state, entry] of Object.entries(strategy.tone)) {
454
534
  tone[state] = { feeling: entry.feeling, example: entry.example };
455
535
  }
456
- return {
536
+ const serialized = {
457
537
  positioning: {
458
538
  onliness: strategy.positioning.onliness,
459
539
  category: strategy.positioning.category,
@@ -462,11 +542,7 @@ function serializeBrandStrategy(strategy) {
462
542
  essence: [...strategy.essence],
463
543
  personality: strategy.personality.map((p) => ({ trait: p.trait, not: p.not })),
464
544
  archetype,
465
- pillars: strategy.pillars.map((p) => ({
466
- id: p.id,
467
- statement: p.statement,
468
- governs: compactGoverns(p.governs)
469
- })),
545
+ pillars: strategy.pillars.map(serializePillar),
470
546
  voice: {
471
547
  traits: strategy.voice.traits.map((t) => {
472
548
  const trait = {
@@ -483,6 +559,38 @@ function serializeBrandStrategy(strategy) {
483
559
  core: [...strategy.core],
484
560
  visibility: strategy.visibility
485
561
  };
562
+ if (strategy.messaging !== void 0) {
563
+ serialized.messaging = { roof: strategy.messaging.roof };
564
+ }
565
+ if (strategy.taglines !== void 0) {
566
+ serialized.taglines = [...strategy.taglines];
567
+ }
568
+ if (strategy.boilerplate !== void 0) {
569
+ serialized.boilerplate = {
570
+ short: strategy.boilerplate.short,
571
+ long: strategy.boilerplate.long
572
+ };
573
+ }
574
+ if (strategy.colorUsage !== void 0) {
575
+ serialized.colorUsage = {
576
+ pairings: strategy.colorUsage.pairings.map((p) => ({
577
+ use: p.use,
578
+ with: p.with,
579
+ rule: p.rule
580
+ }))
581
+ };
582
+ }
583
+ if (strategy.accessibility !== void 0) {
584
+ serialized.accessibility = {
585
+ standard: strategy.accessibility.standard,
586
+ contrast: strategy.accessibility.contrast.map((c) => ({
587
+ context: c.context,
588
+ ratio: c.ratio
589
+ })),
590
+ intent: strategy.accessibility.intent
591
+ };
592
+ }
593
+ return serialized;
486
594
  }
487
595
 
488
596
  // src/pipeline.ts
@@ -834,7 +942,17 @@ var visor_theme_schema_default = {
834
942
  type: "string",
835
943
  enum: ["public", "private"],
836
944
  description: "Client brands are private and are omitted from the public agent manifest."
837
- }
945
+ },
946
+ messaging: { $ref: "#/$defs/brandMessaging" },
947
+ taglines: {
948
+ type: "array",
949
+ items: { type: "string", minLength: 1 },
950
+ minItems: 1,
951
+ description: "Phase 2 wave-1 (VI-541). Permanent, brand-level signature line(s) \u2014 who the brand is, not what it sells this quarter."
952
+ },
953
+ boilerplate: { $ref: "#/$defs/brandBoilerplate" },
954
+ colorUsage: { $ref: "#/$defs/brandColorUsage" },
955
+ accessibility: { $ref: "#/$defs/brandAccessibility" }
838
956
  }
839
957
  },
840
958
  spacing: {
@@ -1056,7 +1174,13 @@ var visor_theme_schema_default = {
1056
1174
  properties: {
1057
1175
  id: { type: "string", minLength: 1 },
1058
1176
  statement: { type: "string", minLength: 1, description: "The pillar's claim in one line." },
1059
- governs: { $ref: "#/$defs/brandGoverns" }
1177
+ governs: { $ref: "#/$defs/brandGoverns" },
1178
+ proof: {
1179
+ type: "array",
1180
+ items: { type: "string", minLength: 1 },
1181
+ minItems: 1,
1182
+ description: "Phase 2 wave-1 (VI-541). Reasons-to-believe \u2014 checkable evidence backing this pillar's claim (the message-house foundation)."
1183
+ }
1060
1184
  }
1061
1185
  },
1062
1186
  brandVoice: {
@@ -1099,6 +1223,66 @@ var visor_theme_schema_default = {
1099
1223
  use: { type: "string", minLength: 1 },
1100
1224
  avoid: { type: "string", minLength: 1 }
1101
1225
  }
1226
+ },
1227
+ brandMessaging: {
1228
+ type: "object",
1229
+ description: "Phase 2 wave-1 (VI-541). Message-house roof \u2014 the single umbrella message above the pillars.",
1230
+ additionalProperties: false,
1231
+ required: ["roof"],
1232
+ properties: {
1233
+ roof: { type: "string", minLength: 1, description: "One overarching statement the pillars support." }
1234
+ }
1235
+ },
1236
+ brandBoilerplate: {
1237
+ type: "object",
1238
+ description: 'Phase 2 wave-1 (VI-541). Reusable "about us" copy \u2014 short and long forms.',
1239
+ additionalProperties: false,
1240
+ required: ["short", "long"],
1241
+ properties: {
1242
+ short: { type: "string", minLength: 1 },
1243
+ long: { type: "string", minLength: 1 }
1244
+ }
1245
+ },
1246
+ brandColorPairing: {
1247
+ type: "object",
1248
+ description: "A color-pairing rule expressed as brand intent (not a computed value).",
1249
+ additionalProperties: false,
1250
+ required: ["use", "with", "rule"],
1251
+ properties: {
1252
+ use: { type: "string", minLength: 1, description: "The token or role being used (e.g. --primary)." },
1253
+ with: { type: "string", minLength: 1, description: "What it pairs against (token, role, or surface)." },
1254
+ rule: { type: "string", minLength: 1, description: "The intent \u2014 when and how the pairing is allowed." }
1255
+ }
1256
+ },
1257
+ brandColorUsage: {
1258
+ type: "object",
1259
+ description: "Phase 2 wave-1 (VI-541). Color-usage intent \u2014 the brand's allowed pairings.",
1260
+ additionalProperties: false,
1261
+ required: ["pairings"],
1262
+ properties: {
1263
+ pairings: { type: "array", items: { $ref: "#/$defs/brandColorPairing" }, minItems: 1 }
1264
+ }
1265
+ },
1266
+ brandContrastTarget: {
1267
+ type: "object",
1268
+ description: "A contrast target expressed as brand intent (a WCAG 2.1 AA threshold).",
1269
+ additionalProperties: false,
1270
+ required: ["context", "ratio"],
1271
+ properties: {
1272
+ context: { type: "string", minLength: 1, description: "The text/UI context the target applies to." },
1273
+ ratio: { type: "string", minLength: 1, description: 'The minimum contrast ratio (e.g. "4.5:1").' }
1274
+ }
1275
+ },
1276
+ brandAccessibility: {
1277
+ type: "object",
1278
+ description: "Phase 2 wave-1 (VI-541). Accessibility intent \u2014 the standard and its contrast targets (authored as intent; live computation is the render surface's concern).",
1279
+ additionalProperties: false,
1280
+ required: ["standard", "contrast", "intent"],
1281
+ properties: {
1282
+ standard: { type: "string", minLength: 1, description: 'The conformance standard. Visor targets "WCAG 2.1 AA".' },
1283
+ contrast: { type: "array", items: { $ref: "#/$defs/brandContrastTarget" }, minItems: 1 },
1284
+ intent: { type: "string", minLength: 1, description: "How the brand applies the standard (intent, not computed results)." }
1285
+ }
1102
1286
  }
1103
1287
  }
1104
1288
  };
@@ -291,6 +291,12 @@ interface BrandPillar {
291
291
  statement: string;
292
292
  /** What the pillar governs (coherence-checked against the live system). */
293
293
  governs: BrandGoverns;
294
+ /**
295
+ * Reasons-to-believe — the message-house foundation (VI-541, Phase 2 wave-1).
296
+ * Concrete, checkable evidence backing this pillar's claim. Optional so a
297
+ * record can carry pillars without proof points.
298
+ */
299
+ proof?: string[];
294
300
  }
295
301
  /** A fixed voice trait with a worked example. */
296
302
  interface BrandVoiceTrait {
@@ -319,15 +325,56 @@ interface BrandLexiconEntry {
319
325
  use: string;
320
326
  avoid: string;
321
327
  }
328
+ /** Message-house roof — the single umbrella message above the pillars. */
329
+ interface BrandMessaging {
330
+ /** One overarching statement the pillars support (message-house roof). */
331
+ roof: string;
332
+ }
333
+ /** Reusable "about us" copy — short and long forms. */
334
+ interface BrandBoilerplate {
335
+ short: string;
336
+ long: string;
337
+ }
338
+ /** A color-pairing rule expressed as brand intent (not a computed value). */
339
+ interface BrandColorPairing {
340
+ /** The token or role being used (e.g. `--primary`). */
341
+ use: string;
342
+ /** What it pairs against (token, role, or surface). */
343
+ with: string;
344
+ /** The intent — when and how the pairing is allowed. */
345
+ rule: string;
346
+ }
347
+ /** Color-usage intent — the brand's allowed pairings. */
348
+ interface BrandColorUsage {
349
+ pairings: BrandColorPairing[];
350
+ }
351
+ /** A contrast target expressed as brand intent (a WCAG 2.1 AA threshold). */
352
+ interface BrandContrastTarget {
353
+ /** The text/UI context the target applies to. */
354
+ context: string;
355
+ /** The minimum contrast ratio (e.g. "4.5:1"). */
356
+ ratio: string;
357
+ }
358
+ /** Accessibility intent — the standard and its contrast targets. */
359
+ interface BrandAccessibility {
360
+ /** The conformance standard. Visor targets "WCAG 2.1 AA". */
361
+ standard: string;
362
+ /** WCAG 2.1 AA contrast targets, authored as intent (live computation is the render surface's concern). */
363
+ contrast: BrandContrastTarget[];
364
+ /** How the brand applies the standard (intent, not computed results). */
365
+ intent: string;
366
+ }
322
367
  /** Visibility of a brand strategy. Client brands are `private`. */
323
368
  type BrandVisibility = "public" | "private";
324
369
  /**
325
370
  * The `brand-strategy` block — strategy + verbal identity as data.
326
371
  *
327
372
  * `tone` is keyed by UI state (`error`, `success`, …); the keys are validated
328
- * against the recognized UI states (coherence check D2). All ten fields are
329
- * required in v1 — F1 authors the full record, and the downstream Workbench
330
- * surfaces render each section.
373
+ * against the recognized UI states (coherence check D2). The ten Phase 1 fields
374
+ * are required in v1 — F1 authors the full record, and the downstream Workbench
375
+ * surfaces render each section. The Phase 2 wave-1 fields (`messaging`,
376
+ * `taglines`, `boilerplate`, `colorUsage`, `accessibility`; VI-541) are optional
377
+ * so a record — e.g. a private client brand — can omit them.
331
378
  */
332
379
  interface BrandStrategy {
333
380
  positioning: BrandPositioning;
@@ -343,6 +390,16 @@ interface BrandStrategy {
343
390
  /** Aaker core/extended — the immutable subset, as section names. */
344
391
  core: string[];
345
392
  visibility: BrandVisibility;
393
+ /** Message-house roof — the umbrella message above the pillars. */
394
+ messaging?: BrandMessaging;
395
+ /** Permanent, brand-level signature line(s) — who the brand is, not what it sells this quarter. */
396
+ taglines?: string[];
397
+ /** Reusable "about us" copy — short and long forms. */
398
+ boilerplate?: BrandBoilerplate;
399
+ /** Color-usage intent — the brand's allowed pairings. */
400
+ colorUsage?: BrandColorUsage;
401
+ /** Accessibility intent — WCAG 2.1 AA standard + contrast targets. */
402
+ accessibility?: BrandAccessibility;
346
403
  }
347
404
  /**
348
405
  * The agent-facing projection of a brand strategy, embedded in
@@ -779,4 +836,4 @@ interface ThemeData {
779
836
  output: ThemeOutput;
780
837
  }
781
838
 
782
- export { type BrandPositioning as A, type BrandSlot as B, type ColorRole as C, type BrandStrategyIssueSeverity as D, type BrandToneEntry as E, type FontResolveOptions as F, type GoogleFontEntry as G, type BrandVariant as H, type BrandVisibility as I, type BrandVoice as J, type BrandVoiceTrait as K, type ColorFormat as L, DEFAULT_BRAND_STRATEGY_SURFACES as M, DEFAULT_BRAND_STRATEGY_TONE_STATES as N, type OKLCH as O, type ParsedColor as P, type FontSource as Q, type ResolvedThemeConfig as R, type SerializedBrandStrategy as S, type ThemeFontResult as T, GOVERNS_WILDCARD as U, type VisorTypography as V, type RGBA as W, type SemanticTokenValue as X, type FontResolution as a, type FontDisplayStrategy as b, type VisorBrand as c, type BrandSource as d, type BrandResolution as e, type ThemeBrandResult as f, type BrandStrategy as g, type BrandStrategyContext as h, type BrandStrategyIssue as i, type BrandStrategyValidationResult as j, type GeneratedPrimitives as k, type ThemeOutput as l, type ThemeData as m, type VisorThemeConfig as n, type FullShadeScale as o, type SelectiveShadeScale as p, type RGB as q, type SemanticTokens as r, type ShadeStep as s, BRAND_VARIANTS as t, BRAND_VISIBILITIES as u, type BrandArchetype as v, type BrandGoverns as w, type BrandLexiconEntry as x, type BrandPersonalityTrait as y, type BrandPillar as z };
839
+ export { GOVERNS_WILDCARD as $, type BrandContrastTarget as A, type BrandSlot as B, type ColorRole as C, type BrandGoverns as D, type BrandLexiconEntry as E, type FontResolveOptions as F, type GoogleFontEntry as G, type BrandMessaging as H, type BrandPersonalityTrait as I, type BrandPillar as J, type BrandPositioning as K, type BrandStrategyIssueSeverity as L, type BrandToneEntry as M, type BrandVariant as N, type OKLCH as O, type ParsedColor as P, type BrandVisibility as Q, type ResolvedThemeConfig as R, type SerializedBrandStrategy as S, type ThemeFontResult as T, type BrandVoice as U, type VisorTypography as V, type BrandVoiceTrait as W, type ColorFormat as X, DEFAULT_BRAND_STRATEGY_SURFACES as Y, DEFAULT_BRAND_STRATEGY_TONE_STATES as Z, type FontSource as _, type FontResolution as a, type RGBA as a0, type SemanticTokenValue as a1, type FontDisplayStrategy as b, type VisorBrand as c, type BrandSource as d, type BrandResolution as e, type ThemeBrandResult as f, type BrandStrategy as g, type BrandStrategyContext as h, type BrandStrategyIssue as i, type BrandStrategyValidationResult as j, type GeneratedPrimitives as k, type ThemeOutput as l, type ThemeData as m, type VisorThemeConfig as n, type FullShadeScale as o, type SelectiveShadeScale as p, type RGB as q, type SemanticTokens as r, type ShadeStep as s, BRAND_VARIANTS as t, BRAND_VISIBILITIES as u, type BrandAccessibility as v, type BrandArchetype as w, type BrandBoilerplate as x, type BrandColorPairing as y, type BrandColorUsage as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loworbitstudio/visor-theme-engine",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Theme engine for the Visor design system — shade generation, token mapping, font resolution, and import/export for .visor.yaml themes.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -343,7 +343,17 @@
343
343
  "type": "string",
344
344
  "enum": ["public", "private"],
345
345
  "description": "Client brands are private and are omitted from the public agent manifest."
346
- }
346
+ },
347
+ "messaging": { "$ref": "#/$defs/brandMessaging" },
348
+ "taglines": {
349
+ "type": "array",
350
+ "items": { "type": "string", "minLength": 1 },
351
+ "minItems": 1,
352
+ "description": "Phase 2 wave-1 (VI-541). Permanent, brand-level signature line(s) — who the brand is, not what it sells this quarter."
353
+ },
354
+ "boilerplate": { "$ref": "#/$defs/brandBoilerplate" },
355
+ "colorUsage": { "$ref": "#/$defs/brandColorUsage" },
356
+ "accessibility": { "$ref": "#/$defs/brandAccessibility" }
347
357
  }
348
358
  },
349
359
  "spacing": {
@@ -565,7 +575,13 @@
565
575
  "properties": {
566
576
  "id": { "type": "string", "minLength": 1 },
567
577
  "statement": { "type": "string", "minLength": 1, "description": "The pillar's claim in one line." },
568
- "governs": { "$ref": "#/$defs/brandGoverns" }
578
+ "governs": { "$ref": "#/$defs/brandGoverns" },
579
+ "proof": {
580
+ "type": "array",
581
+ "items": { "type": "string", "minLength": 1 },
582
+ "minItems": 1,
583
+ "description": "Phase 2 wave-1 (VI-541). Reasons-to-believe — checkable evidence backing this pillar's claim (the message-house foundation)."
584
+ }
569
585
  }
570
586
  },
571
587
  "brandVoice": {
@@ -608,6 +624,66 @@
608
624
  "use": { "type": "string", "minLength": 1 },
609
625
  "avoid": { "type": "string", "minLength": 1 }
610
626
  }
627
+ },
628
+ "brandMessaging": {
629
+ "type": "object",
630
+ "description": "Phase 2 wave-1 (VI-541). Message-house roof — the single umbrella message above the pillars.",
631
+ "additionalProperties": false,
632
+ "required": ["roof"],
633
+ "properties": {
634
+ "roof": { "type": "string", "minLength": 1, "description": "One overarching statement the pillars support." }
635
+ }
636
+ },
637
+ "brandBoilerplate": {
638
+ "type": "object",
639
+ "description": "Phase 2 wave-1 (VI-541). Reusable \"about us\" copy — short and long forms.",
640
+ "additionalProperties": false,
641
+ "required": ["short", "long"],
642
+ "properties": {
643
+ "short": { "type": "string", "minLength": 1 },
644
+ "long": { "type": "string", "minLength": 1 }
645
+ }
646
+ },
647
+ "brandColorPairing": {
648
+ "type": "object",
649
+ "description": "A color-pairing rule expressed as brand intent (not a computed value).",
650
+ "additionalProperties": false,
651
+ "required": ["use", "with", "rule"],
652
+ "properties": {
653
+ "use": { "type": "string", "minLength": 1, "description": "The token or role being used (e.g. --primary)." },
654
+ "with": { "type": "string", "minLength": 1, "description": "What it pairs against (token, role, or surface)." },
655
+ "rule": { "type": "string", "minLength": 1, "description": "The intent — when and how the pairing is allowed." }
656
+ }
657
+ },
658
+ "brandColorUsage": {
659
+ "type": "object",
660
+ "description": "Phase 2 wave-1 (VI-541). Color-usage intent — the brand's allowed pairings.",
661
+ "additionalProperties": false,
662
+ "required": ["pairings"],
663
+ "properties": {
664
+ "pairings": { "type": "array", "items": { "$ref": "#/$defs/brandColorPairing" }, "minItems": 1 }
665
+ }
666
+ },
667
+ "brandContrastTarget": {
668
+ "type": "object",
669
+ "description": "A contrast target expressed as brand intent (a WCAG 2.1 AA threshold).",
670
+ "additionalProperties": false,
671
+ "required": ["context", "ratio"],
672
+ "properties": {
673
+ "context": { "type": "string", "minLength": 1, "description": "The text/UI context the target applies to." },
674
+ "ratio": { "type": "string", "minLength": 1, "description": "The minimum contrast ratio (e.g. \"4.5:1\")." }
675
+ }
676
+ },
677
+ "brandAccessibility": {
678
+ "type": "object",
679
+ "description": "Phase 2 wave-1 (VI-541). Accessibility intent — the standard and its contrast targets (authored as intent; live computation is the render surface's concern).",
680
+ "additionalProperties": false,
681
+ "required": ["standard", "contrast", "intent"],
682
+ "properties": {
683
+ "standard": { "type": "string", "minLength": 1, "description": "The conformance standard. Visor targets \"WCAG 2.1 AA\"." },
684
+ "contrast": { "type": "array", "items": { "$ref": "#/$defs/brandContrastTarget" }, "minItems": 1 },
685
+ "intent": { "type": "string", "minLength": 1, "description": "How the brand applies the standard (intent, not computed results)." }
686
+ }
611
687
  }
612
688
  }
613
689
  }