@certrev/cert-block 0.1.3 → 0.2.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 (41) hide show
  1. package/dist/components/CertBadge.d.ts +18 -3
  2. package/dist/components/CertBadge.d.ts.map +1 -1
  3. package/dist/components/CertBadge.js +8 -2
  4. package/dist/components/CertBadge.js.map +1 -1
  5. package/dist/components/CertReview.d.ts +7 -0
  6. package/dist/components/CertReview.d.ts.map +1 -1
  7. package/dist/components/CertReview.js +1 -1
  8. package/dist/components/CertReview.js.map +1 -1
  9. package/dist/components/format.d.ts +14 -2
  10. package/dist/components/format.d.ts.map +1 -1
  11. package/dist/components/format.js +18 -5
  12. package/dist/components/format.js.map +1 -1
  13. package/dist/components/render-cert-block.d.ts +89 -0
  14. package/dist/components/render-cert-block.d.ts.map +1 -0
  15. package/dist/components/render-cert-block.js +282 -0
  16. package/dist/components/render-cert-block.js.map +1 -0
  17. package/dist/components/render-def.d.ts +178 -0
  18. package/dist/components/render-def.d.ts.map +1 -0
  19. package/dist/components/render-def.js +204 -0
  20. package/dist/components/render-def.js.map +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +4 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/webcomponent/render-badge-html.d.ts +8 -0
  26. package/dist/webcomponent/render-badge-html.d.ts.map +1 -1
  27. package/dist/webcomponent/render-badge-html.js +31 -4
  28. package/dist/webcomponent/render-badge-html.js.map +1 -1
  29. package/package.json +77 -76
  30. package/src/__tests__/__snapshots__/render-cert-block.test.ts.snap +13 -0
  31. package/src/__tests__/components.test.tsx +16 -1
  32. package/src/__tests__/render-cert-block.test.ts +259 -0
  33. package/src/__tests__/render-def.test.tsx +286 -0
  34. package/src/__tests__/webcomponent.test.tsx +10 -0
  35. package/src/components/CertBadge.tsx +56 -4
  36. package/src/components/CertReview.tsx +8 -0
  37. package/src/components/format.ts +22 -5
  38. package/src/components/render-cert-block.ts +403 -0
  39. package/src/components/render-def.ts +286 -0
  40. package/src/index.ts +35 -0
  41. package/src/webcomponent/render-badge-html.ts +46 -4
@@ -11,19 +11,34 @@
11
11
  * an inline custom property (no color-only meaning — the credential text carries it), an
12
12
  * expert photo with meaningful alt text, and a verify link with a descriptive label.
13
13
  *
14
- * `badgeStyle: 'compact'` collapses to a single line (name + credentials + verify link);
15
- * `'full'` shows the photo, certified/updated dates, and the memo (per display flags).
14
+ * `badgeStyle: 'compact'` collapses the reviewer block to a single line (name + credentials);
15
+ * `'full'` shows the photo, certified/updated dates, and the memo (per display flags). BOTH
16
+ * styles carry the compensation cue + FTC scope line (the always-on compliance disclosures).
17
+ *
18
+ * THEMING (WS6): presentation defaults come from `payload.content.display`; an optional
19
+ * `renderDef` prop layers a brand's validated themeable tokens (emitted as `--certrev-*`
20
+ * CSS custom properties) and its subtractive field-visibility on top. No `renderDef` → the
21
+ * CertREV preset default (theme output byte-identical to un-themed).
16
22
  */
17
23
  import type { CertPayload } from '../contract/kernel.js';
24
+ import { type CertRenderDef } from './render-def.js';
18
25
  export interface CertBadgeProps {
19
26
  /** The cryptographically-verified payload (from `getVerifiedEnvelope`). */
20
27
  readonly payload: CertPayload;
21
- /** Optional accent override (else `display.accentColor`, else CertREV default). */
28
+ /** Optional accent override (else render-def, else `display.accentColor`, else CertREV default). */
22
29
  readonly accentColor?: string;
23
30
  /** Extra class names appended to the root element's class list. */
24
31
  readonly className?: string;
25
32
  /** Override the badge style independent of the signed display config. */
26
33
  readonly badgeStyle?: 'full' | 'compact';
34
+ /**
35
+ * The brand render-def (WS6). ABSENT → the badge renders the CertREV preset
36
+ * default (fail-safe; theme output byte-identical to un-themed). PRESENT → its
37
+ * validated themeable tokens are emitted as `--certrev-*` CSS custom properties
38
+ * and its subtractive hide-set removes brand-toggleable fields. Invalid tokens
39
+ * are dropped (defense-in-depth over the write-path gate).
40
+ */
41
+ readonly renderDef?: CertRenderDef;
27
42
  }
28
43
  export declare function CertBadge(props: CertBadgeProps): import("react").JSX.Element;
29
44
  //# sourceMappingURL=CertBadge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CertBadge.d.ts","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIrE,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC;AAuCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BA8F9C"}
1
+ {"version":3,"file":"CertBadge.d.ts","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGrE,OAAO,EAGN,KAAK,aAAa,EAGlB,MAAM,iBAAiB,CAAA;AAExB,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;CAClC;AAuCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BA6H9C"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { safeHttpUrl } from './escape.js';
3
3
  import { credentialSuffix, expertNameWithCredentials, formatDate, resolveDisplay } from './format.js';
4
+ import { CERT_SCOPE_LINE, CERT_STRINGS_VERSION, resolveRenderTheme, themeCssVarDeclarations, } from './render-def.js';
4
5
  const ROOT_CLASS = 'certrev-badge';
5
6
  /** Inline-SVG verified checkmark — no external asset, accent-tinted, decorative. */
6
7
  function VerifiedMark({ accent }) {
@@ -15,7 +16,8 @@ function ExpertPhoto({ content, show }) {
15
16
  export function CertBadge(props) {
16
17
  const { payload } = props;
17
18
  const content = payload.content;
18
- const display = resolveDisplay(content.display, props.accentColor);
19
+ const display = resolveDisplay(content.display, props.accentColor, props.renderDef);
20
+ const theme = resolveRenderTheme(props.renderDef);
19
21
  const style = props.badgeStyle ?? display.badgeStyle;
20
22
  const verifyUrl = safeHttpUrl(content.verifyUrl);
21
23
  const profileUrl = safeHttpUrl(content.expert.profileUrl);
@@ -26,11 +28,15 @@ export function CertBadge(props) {
26
28
  const rootClass = `${ROOT_CLASS} ${ROOT_CLASS}--${style}${props.className ? ` ${props.className}` : ''}`;
27
29
  // Accent is exposed as a custom property so brand themes can read it, and applied
28
30
  // directly to the accent border so the component is self-styling without a stylesheet.
31
+ // A THEMED brand additionally emits the surface/radius/font vars; with no render-def
32
+ // `themeCssVarDeclarations` is empty, so the un-themed style is byte-identical.
29
33
  const rootStyle = {
30
34
  ['--certrev-accent']: accent,
31
35
  borderInlineStartColor: accent,
32
36
  };
37
+ for (const [name, value] of themeCssVarDeclarations(theme))
38
+ rootStyle[name] = value;
33
39
  const expertName = (_jsxs("span", { className: `${ROOT_CLASS}__expert-name`, children: [content.expert.displayName, suffix ? _jsxs("span", { className: `${ROOT_CLASS}__credentials`, children: [", ", suffix] }) : null] }));
34
- return (_jsxs("section", { className: rootClass, style: rootStyle, "data-certrev-cert-id": payload.certId, "aria-label": `Content reviewed by ${expertNameWithCredentials(content)}`, children: [_jsxs("div", { className: `${ROOT_CLASS}__header`, children: [_jsx(VerifiedMark, { accent: accent }), style === 'full' ? _jsx(ExpertPhoto, { content: content, show: display.showExpertPhoto }) : null, _jsxs("div", { className: `${ROOT_CLASS}__heading`, children: [_jsx("span", { className: `${ROOT_CLASS}__eyebrow`, children: "Expert reviewed" }), _jsxs("span", { className: `${ROOT_CLASS}__byline`, children: ["Reviewed by", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__expert-link`, href: profileUrl, rel: "noopener", children: expertName })) : (expertName)] })] })] }), style === 'full' ? (_jsxs(_Fragment, { children: [display.showMemo && content.memo ? _jsx("p", { className: `${ROOT_CLASS}__memo`, children: content.memo }) : null, display.showAuthor && content.author.name && content.author.name !== content.expert.displayName ? (_jsxs("p", { className: `${ROOT_CLASS}__author`, children: ["Written by ", content.author.name, content.author.title ? `, ${content.author.title}` : ''] })) : null, _jsxs("dl", { className: `${ROOT_CLASS}__dates`, children: [certifiedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Certified" }), _jsx("dd", { children: _jsx("time", { dateTime: content.certifiedAt, children: certifiedLabel }) })] })) : null, updatedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Last updated" }), _jsx("dd", { children: _jsx("time", { dateTime: content.contentModifiedAt ?? undefined, children: updatedLabel }) })] })) : null] })] })) : null, verifyUrl ? (_jsx("a", { className: `${ROOT_CLASS}__verify`, href: verifyUrl, rel: "noopener", "aria-label": "Verify this certification on CertREV", children: "Verify on CertREV" })) : null] }));
40
+ return (_jsxs("section", { className: rootClass, style: rootStyle, "data-certrev-cert-id": payload.certId, "data-certrev-strings-version": CERT_STRINGS_VERSION, "data-certrev-def-version": theme.version, "aria-label": `Content reviewed by ${expertNameWithCredentials(content)}`, children: [_jsxs("div", { className: `${ROOT_CLASS}__header`, children: [_jsx(VerifiedMark, { accent: accent }), style === 'full' ? _jsx(ExpertPhoto, { content: content, show: display.showExpertPhoto }) : null, _jsxs("div", { className: `${ROOT_CLASS}__heading`, children: [_jsx("span", { className: `${ROOT_CLASS}__eyebrow`, children: "Expert reviewed" }), _jsxs("span", { className: `${ROOT_CLASS}__byline`, children: ["Reviewed by", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__expert-link`, href: profileUrl, rel: "noopener", children: expertName })) : (expertName)] })] })] }), style === 'full' ? (_jsxs(_Fragment, { children: [display.showMemo && content.memo ? _jsx("p", { className: `${ROOT_CLASS}__memo`, children: content.memo }) : null, display.showAuthor && content.author.name && content.author.name !== content.expert.displayName ? (_jsxs("p", { className: `${ROOT_CLASS}__author`, children: ["Written by ", content.author.name, content.author.title ? `, ${content.author.title}` : ''] })) : null, _jsxs("dl", { className: `${ROOT_CLASS}__dates`, children: [certifiedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Certified" }), _jsx("dd", { children: _jsx("time", { dateTime: content.certifiedAt, children: certifiedLabel }) })] })) : null, updatedLabel ? (_jsxs("div", { className: `${ROOT_CLASS}__date`, children: [_jsx("dt", { children: "Last updated" }), _jsx("dd", { children: _jsx("time", { dateTime: content.contentModifiedAt ?? undefined, children: updatedLabel }) })] })) : null] })] })) : null, _jsxs("div", { className: `${ROOT_CLASS}__disclosure`, children: [_jsxs("span", { className: `${ROOT_CLASS}__compensated-cue`, children: ["Compensated", ' ', profileUrl ? (_jsx("a", { className: `${ROOT_CLASS}__cue-link`, href: profileUrl, rel: "noopener", children: "expert" })) : ('expert')] }), _jsx("p", { className: `${ROOT_CLASS}__ftc`, "data-ftc-disclosure": true, children: _jsx("span", { "data-ftc-line": true, children: CERT_SCOPE_LINE }) })] }), verifyUrl ? (_jsx("a", { className: `${ROOT_CLASS}__verify`, href: verifyUrl, rel: "noopener", "aria-label": "Verify this certification on CertREV", children: "Verify on CertREV" })) : null] }));
35
41
  }
36
42
  //# sourceMappingURL=CertBadge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CertBadge.js","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CertBadge.js","sourceRoot":"","sources":["../../src/components/CertBadge.tsx"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@
8
8
  * expert bio + backlink remain separately placeable for brands that want finer control.
9
9
  */
10
10
  import type { CertVerdict } from '../contract/kernel.js';
11
+ import type { CertRenderDef } from './render-def.js';
11
12
  export interface CertReviewProps {
12
13
  /** The verdict from `getVerifiedEnvelope` (or a direct kernel call). */
13
14
  readonly verdict: CertVerdict;
@@ -16,6 +17,12 @@ export interface CertReviewProps {
16
17
  readonly accentColor?: string;
17
18
  readonly className?: string;
18
19
  readonly badgeStyle?: 'full' | 'compact';
20
+ /**
21
+ * The brand render-def (WS6). Forwarded to `<CertBadge>` for themeable-token
22
+ * emission + subtractive visibility; absent → the CertREV preset default. The
23
+ * JSON-LD is presentation-blind, so the def never touches the structured data.
24
+ */
25
+ readonly renderDef?: CertRenderDef;
19
26
  /** Omit the JSON-LD (e.g. the page already emits it elsewhere). Default false. */
20
27
  readonly omitJsonLd?: boolean;
21
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CertReview.d.ts","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD,MAAM,WAAW,eAAe;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,uFAAuF;IACvF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,sCAehD"}
1
+ {"version":3,"file":"CertReview.d.ts","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,MAAM,WAAW,eAAe;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,uFAAuF;IACvF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,sCAgBhD"}
@@ -6,6 +6,6 @@ export function CertReview(props) {
6
6
  if (props.verdict.decision !== 'render')
7
7
  return null;
8
8
  const payload = props.verdict.payload;
9
- return (_jsxs(_Fragment, { children: [_jsx(CertBadge, { payload: payload, accentColor: props.accentColor, className: props.className, badgeStyle: props.badgeStyle }), props.omitJsonLd ? null : _jsx(CertJsonLd, { payload: payload, pageUrl: props.pageUrl })] }));
9
+ return (_jsxs(_Fragment, { children: [_jsx(CertBadge, { payload: payload, accentColor: props.accentColor, className: props.className, badgeStyle: props.badgeStyle, renderDef: props.renderDef }), props.omitJsonLd ? null : _jsx(CertJsonLd, { payload: payload, pageUrl: props.pageUrl })] }));
10
10
  }
11
11
  //# sourceMappingURL=CertReview.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CertReview.js","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":";AAWA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAc5C,MAAM,UAAU,UAAU,CAAC,KAAsB;IAChD,yEAAyE;IACzE,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAA;IACrC,OAAO,CACN,8BACC,KAAC,SAAS,IACT,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,KAAK,CAAC,WAAW,EAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,UAAU,EAAE,KAAK,CAAC,UAAU,GAC3B,EACD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,GAAI,IACjF,CACH,CAAA;AACF,CAAC"}
1
+ {"version":3,"file":"CertReview.js","sourceRoot":"","sources":["../../src/components/CertReview.tsx"],"names":[],"mappings":";AAWA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAqB5C,MAAM,UAAU,UAAU,CAAC,KAAsB;IAChD,yEAAyE;IACzE,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAA;IACrC,OAAO,CACN,8BACC,KAAC,SAAS,IACT,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,KAAK,CAAC,WAAW,EAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,UAAU,EAAE,KAAK,CAAC,UAAU,EAC5B,SAAS,EAAE,KAAK,CAAC,SAAS,GACzB,EACD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,GAAI,IACjF,CACH,CAAA;AACF,CAAC"}
@@ -3,6 +3,7 @@
3
3
  * no React — so the same formatting drives JSX and the hand-built Web Component string.
4
4
  */
5
5
  import type { CertContent, CertDisplayConfig } from '../contract/kernel.js';
6
+ import { type CertRenderDef } from './render-def.js';
6
7
  /** Default accent if the brand didn't pin one (CertREV teal). */
7
8
  export declare const DEFAULT_ACCENT = "#0f766e";
8
9
  export interface ResolvedDisplay {
@@ -12,8 +13,19 @@ export interface ResolvedDisplay {
12
13
  readonly showMemo: boolean;
13
14
  readonly badgeStyle: 'full' | 'compact';
14
15
  }
15
- /** Resolve the optional display config to concrete presentation flags + accent. */
16
- export declare function resolveDisplay(display: CertDisplayConfig | undefined, accentOverride?: string): ResolvedDisplay;
16
+ /**
17
+ * Resolve the optional display config + optional brand render-def to concrete
18
+ * presentation flags + accent.
19
+ *
20
+ * Accent PRECEDENCE mirrors the Shopify Liquid engine exactly: an explicit
21
+ * `accentOverride` (a block/prop override) wins, then the render-def's validated
22
+ * accent (a THEMED brand), then the signed display's accent, else the CertREV
23
+ * default. Visibility is SUBTRACTIVE: the render-def's hide-set can only turn a
24
+ * preset-placed field OFF (never on) — a hidden `reviewerPhoto` / `memo` wins over
25
+ * a display flag that shows it. When no render-def is supplied the theme resolves
26
+ * empty, so accent + visibility are byte-identical to the pre-theming behavior.
27
+ */
28
+ export declare function resolveDisplay(display: CertDisplayConfig | undefined, accentOverride?: string, renderDef?: CertRenderDef): ResolvedDisplay;
17
29
  export declare function formatDate(iso: string | null | undefined): string | null;
18
30
  /** "PhD, RD" — the expert's credential abbreviations, comma-joined. */
19
31
  export declare function credentialSuffix(content: CertContent): string;
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE3E,iEAAiE;AACjE,eAAO,MAAM,cAAc,YAAY,CAAA;AAEvC,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,eAAe,CAQ/G;AAUD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMxE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAE7D;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAGtE"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,EAAE,KAAK,aAAa,EAAqC,MAAM,iBAAiB,CAAA;AAEvF,iEAAiE;AACjE,eAAO,MAAM,cAAc,YAAY,CAAA;AAEvC,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,aAAa,GACvB,eAAe,CASjB;AAUD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMxE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAE7D;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAGtE"}
@@ -2,15 +2,28 @@
2
2
  * Presentation helpers shared by the React components + Web Component. Pure, no DOM,
3
3
  * no React — so the same formatting drives JSX and the hand-built Web Component string.
4
4
  */
5
+ import { isFieldHidden, resolveRenderTheme } from './render-def.js';
5
6
  /** Default accent if the brand didn't pin one (CertREV teal). */
6
7
  export const DEFAULT_ACCENT = '#0f766e';
7
- /** Resolve the optional display config to concrete presentation flags + accent. */
8
- export function resolveDisplay(display, accentOverride) {
8
+ /**
9
+ * Resolve the optional display config + optional brand render-def to concrete
10
+ * presentation flags + accent.
11
+ *
12
+ * Accent PRECEDENCE mirrors the Shopify Liquid engine exactly: an explicit
13
+ * `accentOverride` (a block/prop override) wins, then the render-def's validated
14
+ * accent (a THEMED brand), then the signed display's accent, else the CertREV
15
+ * default. Visibility is SUBTRACTIVE: the render-def's hide-set can only turn a
16
+ * preset-placed field OFF (never on) — a hidden `reviewerPhoto` / `memo` wins over
17
+ * a display flag that shows it. When no render-def is supplied the theme resolves
18
+ * empty, so accent + visibility are byte-identical to the pre-theming behavior.
19
+ */
20
+ export function resolveDisplay(display, accentOverride, renderDef) {
21
+ const theme = resolveRenderTheme(renderDef);
9
22
  return {
10
- accentColor: accentOverride ?? display?.accentColor ?? DEFAULT_ACCENT,
11
- showExpertPhoto: display?.showExpertPhoto ?? true,
23
+ accentColor: accentOverride ?? theme.accent ?? display?.accentColor ?? DEFAULT_ACCENT,
24
+ showExpertPhoto: (display?.showExpertPhoto ?? true) && !isFieldHidden(renderDef, 'reviewerPhoto'),
12
25
  showAuthor: display?.showAuthor ?? true,
13
- showMemo: display?.showMemo ?? true,
26
+ showMemo: (display?.showMemo ?? true) && !isFieldHidden(renderDef, 'memo'),
14
27
  badgeStyle: display?.badgeStyle ?? 'full',
15
28
  };
16
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAA;AAUvC,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,OAAsC,EAAE,cAAuB;IAC7F,OAAO;QACN,WAAW,EAAE,cAAc,IAAI,OAAO,EAAE,WAAW,IAAI,cAAc;QACrE,eAAe,EAAE,OAAO,EAAE,eAAe,IAAI,IAAI;QACjD,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI;QACvC,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI;QACnC,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,MAAM;KACzC,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;AAEnG,MAAM,UAAU,UAAU,CAAC,GAA8B;IACxD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAAE,OAAO,IAAI,CAAA;IACjC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,cAAc,EAAE,EAAE,CAAA;AAC7E,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACpD,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,yBAAyB,CAAC,OAAoB;IAC7D,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAA;AACxF,CAAC"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/components/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAsB,aAAa,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEvF,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAA;AAUvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC7B,OAAsC,EACtC,cAAuB,EACvB,SAAyB;IAEzB,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;IAC3C,OAAO;QACN,WAAW,EAAE,cAAc,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,EAAE,WAAW,IAAI,cAAc;QACrF,eAAe,EAAE,CAAC,OAAO,EAAE,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,eAAe,CAAC;QACjG,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI;QACvC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC;QAC1E,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,MAAM;KACzC,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;AAEnG,MAAM,UAAU,UAAU,CAAC,GAA8B;IACxD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAAE,OAAO,IAAI,CAAA;IACjC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,cAAc,EAAE,EAAE,CAAA;AAC7E,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACpD,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACxE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,yBAAyB,CAAC,OAAoB;IAC7D,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAA;AACxF,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * ─────────────────────────────────────────────────────────────────────────────
3
+ * renderCertBlock — the LOCKED CertREV cert design, as an SSR-safe HTML string
4
+ * ─────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * Paints the owner-approved cert render (decision 2026-07-17) in one of three
7
+ * placement modes — `banner`, `sidebar`, `floating` — from RESOLVED theme tokens +
8
+ * the cert facts. It returns a self-contained HTML string that renders with ZERO
9
+ * JavaScript (the CertREV modal + the reviewer-bio accordion are progressive
10
+ * enhancement, wired by the shared embed script via the `data-certrev-*` hooks).
11
+ *
12
+ * WHY A STRING (not a React component): the block is emitted into storefront HTML
13
+ * across every render surface (Shopify Liquid, WordPress PHP, the JS engine, and
14
+ * this headless twin). A pure string renderer — like `renderBadgeHtml` — runs on
15
+ * every runtime the main entry targets (edge / Workers / server) with no React, no
16
+ * DOM, no Node builtins.
17
+ *
18
+ * THEMING: the reference is variable-driven. This emits the guide's CSS-var theming
19
+ * hooks on the ROOT element so the block is self-contained on a storefront that does
20
+ * NOT define the guide's `:root` — the accent (`--ba`), its auto-contrast ink
21
+ * (`--ba-fg`), the corner radius (`--br`), the body font (`--bf`) and surface
22
+ * (`--surface`) come from the resolved theme; the navy trust tints + the fixed
23
+ * label/serif/wordmark fonts are LOCKED literals (guide "the lock is legal, not
24
+ * aesthetic"). A brand themes accent / font / corners / surface; the navy chrome ink
25
+ * stays fixed.
26
+ *
27
+ * COMPLIANCE (LOCKED, non-negotiable): the three material disclosures always render
28
+ * on a CLAIM face (banner + sidebar) — the compensation cue, the byte-exact FTC
29
+ * scope line, and the dated credential verification, which stays GROUPED with the
30
+ * credential (`<name>, <credential>` → "Credential verified by CertREV" → "on
31
+ * <date>"). The `floating` pill makes no claim (it only labels + links to the
32
+ * modal), so it carries no disclosures — matching the locked reference.
33
+ *
34
+ * SECURITY: this is storefront HTML built by hand (no JSX auto-escaping), so EVERY
35
+ * interpolated value is escaped — `escapeHtml` for content, `escapeAttribute` for
36
+ * attribute values, `safeHttpUrl` for hrefs. Theme tokens are re-validated
37
+ * (`resolveBlockTheme`) and only ever emitted as strict hex / length / enum-stack.
38
+ */
39
+ import { type ResolvedBlockTheme } from './render-def.js';
40
+ /** The three LOCKED placement faces. */
41
+ export type CertBlockMode = 'banner' | 'sidebar' | 'floating';
42
+ /**
43
+ * The cert facts + LOCKED display strings a face renders. Facts are escaped; the
44
+ * three locked strings (`compensationCue`, `scopeLine`, `credentialLine`) are
45
+ * byte-verbatim per the CertREV Display Guide and default to the mirrored constants
46
+ * when a caller omits them — so the compliance baseline is exact even if unsupplied.
47
+ */
48
+ export interface CertBlockFacts {
49
+ /** The content author (the "Written by" column). */
50
+ readonly authorName: string;
51
+ /** The author's subtitle (e.g. "Editorial"). Omitted when absent. */
52
+ readonly authorTitle?: string;
53
+ /** The reviewing expert's display name, WITHOUT the credential (e.g. "Jane Doe"). */
54
+ readonly reviewerName: string;
55
+ /** The reviewer's credential abbreviation (e.g. "MD"). Empty → name-only. */
56
+ readonly credential: string;
57
+ /** ISO instant CertREV verified the CREDENTIAL (the dated verification date). */
58
+ readonly credentialVerifiedAt: string;
59
+ /** ISO instant the article was certified (the "Certified <date>" footer). */
60
+ readonly certifiedAt: string;
61
+ /** The expert's memo (rendered as the serif blockquote). */
62
+ readonly memo: string;
63
+ /** The expert's bio (banner accordion / sidebar paragraph). Omitted when absent. */
64
+ readonly bio?: string;
65
+ /** The expert profile URL (the "Profile" link + modal `expert` hook). */
66
+ readonly profileUrl: string;
67
+ /** The certificate URL (the "Certificate" link + modal hook). */
68
+ readonly certificateUrl: string;
69
+ /** LOCKED — the compensation cue. Default: `COMPENSATED_EXPERT_CUE`. */
70
+ readonly compensationCue?: string;
71
+ /** LOCKED — the byte-exact FTC scope line. Default: `CERT_SCOPE_LINE`. */
72
+ readonly scopeLine?: string;
73
+ /** LOCKED — the verification attribution. Default: `CREDENTIAL_VERIFIED_ATTRIBUTION`. */
74
+ readonly credentialLine?: string;
75
+ }
76
+ /** The full input to `renderCertBlock`. */
77
+ export interface RenderCertBlockInput {
78
+ readonly mode: CertBlockMode;
79
+ /** RESOLVED theme tokens (the portal bakes these). Absent/invalid tokens fall back to navy. */
80
+ readonly theme?: Partial<ResolvedBlockTheme>;
81
+ readonly facts: CertBlockFacts;
82
+ }
83
+ /**
84
+ * Render the LOCKED CertREV cert design as an SSR-safe HTML string for one of the
85
+ * three placement modes. Returns a self-contained `<div class="certrev-cert …">`
86
+ * that needs no JS to render (the modal + bio accordion progressively enhance).
87
+ */
88
+ export declare function renderCertBlock(input: RenderCertBlockInput): string;
89
+ //# sourceMappingURL=render-cert-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-cert-block.d.ts","sourceRoot":"","sources":["../../src/components/render-cert-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,OAAO,EAMN,KAAK,kBAAkB,EAEvB,MAAM,iBAAiB,CAAA;AAExB,wCAAwC;AACxC,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oFAAoF;IACpF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC5C,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;CAC9B;AAmSD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAWnE"}