brand-shell 0.11.0 → 0.12.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.
@@ -15,6 +15,17 @@
15
15
  }
16
16
  },
17
17
  "$defs": {
18
+ "CustomSocialLink": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "required": ["platform", "href", "label"],
22
+ "properties": {
23
+ "platform": { "type": "string", "minLength": 1 },
24
+ "href": { "type": "string", "minLength": 1 },
25
+ "label": { "type": "string", "minLength": 1 },
26
+ "iconSvg": { "type": "string", "minLength": 1 }
27
+ }
28
+ },
18
29
  "Target": {
19
30
  "type": "string",
20
31
  "enum": ["_blank", "_self", "_parent", "_top"]
@@ -63,12 +74,16 @@
63
74
  "primaryAction": { "$ref": "#/$defs/BrandAction" },
64
75
  "secondaryAction": { "$ref": "#/$defs/BrandAction" },
65
76
  "linkedin": { "type": "string", "minLength": 1 },
66
- "gmail": { "type": "string", "minLength": 1 },
77
+ "email": { "type": "string", "minLength": 1 },
67
78
  "github": { "type": "string", "minLength": 1 },
68
79
  "twitter": { "type": "string", "minLength": 1 },
69
80
  "discord": { "type": "string", "minLength": 1 },
70
81
  "website": { "type": "string", "minLength": 1 },
71
- "tagline": { "type": "string", "minLength": 1 }
82
+ "tagline": { "type": "string", "minLength": 1 },
83
+ "customSocialLinks": {
84
+ "type": "array",
85
+ "items": { "$ref": "#/$defs/CustomSocialLink" }
86
+ }
72
87
  }
73
88
  },
74
89
  "BrandTheme": {
@@ -82,7 +97,11 @@
82
97
  "linkColor": { "type": "string", "minLength": 1 },
83
98
  "socialIconSize": { "type": "string", "minLength": 1 },
84
99
  "buttonTextColor": { "type": "string", "minLength": 1 },
85
- "ctaLayout": { "type": "string", "enum": ["inline", "stacked"] }
100
+ "ctaLayout": { "type": "string", "enum": ["inline", "stacked"] },
101
+ "borderRadius": { "type": "string", "minLength": 1 },
102
+ "headerHeight": { "type": "string", "minLength": 1 },
103
+ "footerPadding": { "type": "string", "minLength": 1 },
104
+ "secondaryButtonBg": { "type": "string", "minLength": 1 }
86
105
  }
87
106
  }
88
107
  }
package/dist/index.d.mts CHANGED
@@ -1,16 +1,27 @@
1
- import { i as BrandTheme, n as BrandDetails, r as BrandNavLink, t as BrandAction } from "./types-PQziYg7Z.mjs";
1
+ import { a as CustomSocialLink, i as BrandTheme, n as BrandDetails, r as BrandNavLink, t as BrandAction } from "./types-B_CLRZMO.mjs";
2
2
  import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+ import { ReactElement, ReactNode } from "react";
3
4
 
4
5
  //#region src/Header.d.ts
6
+ interface LinkRenderProps {
7
+ href: string;
8
+ className: string;
9
+ "aria-label": string;
10
+ target: string;
11
+ rel?: string;
12
+ children: ReactNode;
13
+ }
5
14
  interface HeaderProps {
6
15
  details: BrandDetails;
7
16
  theme?: BrandTheme | null;
8
17
  className?: string;
18
+ renderLink?: (props: LinkRenderProps) => ReactElement;
9
19
  }
10
20
  declare function Header({
11
21
  details,
12
22
  theme,
13
- className
23
+ className,
24
+ renderLink
14
25
  }: HeaderProps): react_jsx_runtime0.JSX.Element;
15
26
  //#endregion
16
27
  //#region src/Footer.d.ts
@@ -18,19 +29,22 @@ interface FooterProps {
18
29
  details: BrandDetails;
19
30
  theme?: BrandTheme | null;
20
31
  className?: string;
32
+ renderLink?: (props: LinkRenderProps) => ReactElement;
21
33
  }
22
34
  declare function Footer({
23
35
  details,
24
36
  theme,
25
- className
37
+ className,
38
+ renderLink
26
39
  }: FooterProps): react_jsx_runtime0.JSX.Element;
27
40
  //#endregion
28
41
  //#region src/core/social.d.ts
29
42
  type SocialPlatform = "website" | "linkedin" | "email" | "github" | "twitter" | "discord";
30
43
  interface SocialLink {
31
- platform: SocialPlatform;
44
+ platform: SocialPlatform | string;
32
45
  href: string;
33
46
  label: string;
47
+ iconSvg?: string;
34
48
  }
35
49
  declare function detailsToSocialLinks(details: BrandDetails): SocialLink[];
36
50
  //#endregion
@@ -61,8 +75,9 @@ interface NormalizedBrandDetails extends Omit<BrandDetails, "navLinks" | "primar
61
75
  declare function normalizeNavLinks(navLinks?: BrandNavLink[]): ShellNavLink[];
62
76
  declare function normalizeBrandDetails(details: BrandDetails): NormalizedBrandDetails;
63
77
  declare function normalizeCtaLinks(primaryAction?: BrandAction, secondaryAction?: BrandAction): ShellActionLink[];
78
+ declare function buildShellViewModelFromNormalized(normalized: NormalizedBrandDetails): ShellViewModel;
64
79
  declare function buildShellViewModel(details: BrandDetails): ShellViewModel;
65
- declare function normalizeGmailHref(gmail?: string): string | undefined;
80
+ declare function normalizeEmailHref(email?: string): string | undefined;
66
81
  //#endregion
67
82
  //#region src/core/dev.d.ts
68
83
  declare function shouldValidateInDev(): boolean;
@@ -89,5 +104,5 @@ declare function assertValidBrandTheme(theme: unknown, context?: string): assert
89
104
  declare function normalizeBrandTheme(theme?: BrandTheme | null): BrandTheme | null;
90
105
  declare function formatValidationErrors(context: string, errors: string[]): string;
91
106
  //#endregion
92
- export { type BrandAction, type BrandDetails, type BrandNavLink, BrandShellValidationError, type BrandTheme, type BrandValidationResult, Footer, Header, type LinkTarget, type NormalizedBrandDetails, type ShellActionLink, type ShellNavLink, type ShellViewModel, type SocialLink, type SocialPlatform, type ThemeVariables, assertValidBrandDetails, assertValidBrandTheme, buildShellViewModel, detailsToSocialLinks, formatValidationErrors, normalizeBrandDetails, normalizeBrandTheme, normalizeCtaLinks, normalizeGmailHref, normalizeNavLinks, shouldValidateInDev, themeToCssVariables, validateBrandDetails, validateBrandTheme };
107
+ export { type BrandAction, type BrandDetails, type BrandNavLink, BrandShellValidationError, type BrandTheme, type BrandValidationResult, type CustomSocialLink, Footer, type FooterProps, Header, type HeaderProps, type LinkRenderProps, type LinkTarget, type NormalizedBrandDetails, type ShellActionLink, type ShellNavLink, type ShellViewModel, type SocialLink, type SocialPlatform, type ThemeVariables, assertValidBrandDetails, assertValidBrandTheme, buildShellViewModel, buildShellViewModelFromNormalized, detailsToSocialLinks, formatValidationErrors, normalizeBrandDetails, normalizeBrandTheme, normalizeCtaLinks, normalizeEmailHref, normalizeNavLinks, shouldValidateInDev, themeToCssVariables, validateBrandDetails, validateBrandTheme };
93
108
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/Header.tsx","../src/Footer.tsx","../src/core/social.ts","../src/core/shell.ts","../src/core/dev.ts","../src/core/theme.ts","../src/core/validation.ts"],"mappings":";;;;UAqBiB,WAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,SAAA;AAAA;AAAA,iBAGc,MAAA,CAAA;EAAS,OAAA;EAAS,KAAA;EAAO;AAAA,GAAa,WAAA,GAAW,kBAAA,CAAA,GAAA,CAAA,OAAA;;;UCNhD,WAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,SAAA;AAAA;AAAA,iBAGc,MAAA,CAAA;EAAS,OAAA;EAAS,KAAA;EAAO;AAAA,GAAa,WAAA,GAAW,kBAAA,CAAA,GAAA,CAAA,OAAA;;;KCzBrD,cAAA;AAAA,UAEK,UAAA;EACf,QAAA,EAAU,cAAA;EACV,IAAA;EACA,KAAA;AAAA;AAAA,iBAGc,oBAAA,CAAqB,OAAA,EAAS,YAAA,GAAe,UAAA;;;KCNjD,UAAA,GAAa,WAAA,CAAY,YAAA;AAAA,UAEpB,YAAA,SAAqB,YAAA;EACpC,SAAA;EACA,GAAA;EACA,MAAA,EAAQ,UAAA;AAAA;AAAA,UAGO,eAAA,SAAwB,WAAA;EACvC,SAAA;EACA,GAAA;EACA,MAAA,EAAQ,UAAA;EACR,OAAA,EAAS,WAAA,CAAY,WAAA;AAAA;AAAA,UAGN,cAAA;EACf,QAAA,EAAU,YAAA;EACV,QAAA,EAAU,eAAA;EACV,WAAA,EAAa,UAAA;AAAA;AAAA,KAGH,oBAAA,GAAuB,IAAA,CAAK,eAAA,eAA8B,IAAA,CAAK,WAAA;AAAA,UAE1D,sBAAA,SAA+B,IAAA,CAAK,YAAA;EACnD,QAAA,EAAU,YAAA;EACV,aAAA,GAAgB,oBAAA;EAChB,eAAA,GAAkB,oBAAA;AAAA;AAAA,iBAGJ,iBAAA,CAAkB,QAAA,GAAU,YAAA,KAAsB,YAAA;AAAA,iBAgBlD,qBAAA,CAAsB,OAAA,EAAS,YAAA,GAAe,sBAAA;AAAA,iBAmB9C,iBAAA,CACd,aAAA,GAAgB,WAAA,EAChB,eAAA,GAAkB,WAAA,GACjB,eAAA;AAAA,iBAkBa,mBAAA,CAAoB,OAAA,EAAS,YAAA,GAAe,cAAA;AAAA,iBAU5C,kBAAA,CAAmB,KAAA;;;iBC1FnB,mBAAA,CAAA;;;KCHJ,cAAA,GAAiB,MAAA;AAAA,iBAEb,mBAAA,CAAoB,KAAA,GAAQ,UAAA,UAAoB,cAAA;;;UCY/C,qBAAA;EACf,KAAA;EACA,MAAA;EACA,UAAA,EAAY,CAAA;AAAA;AAAA,cAGD,yBAAA,SAAkC,KAAA;EAAA,SACpC,OAAA;EAAA,SACA,MAAA;cAEG,OAAA,UAAiB,MAAA;AAAA;AAAA,iBAQf,oBAAA,CAAqB,OAAA,YAAmB,qBAAA,CAAsB,sBAAA;AAAA,iBAmD9D,kBAAA,CAAmB,KAAA,YAAiB,qBAAA,CAAsB,UAAA;AAAA,iBAuC1D,uBAAA,CAAwB,OAAA,WAAkB,OAAA,oBAAmC,OAAA,IAAW,YAAA;AAAA,iBAOxF,qBAAA,CAAsB,KAAA,WAAgB,OAAA,oBAAiC,KAAA,IAAS,UAAA;AAAA,iBAOhF,mBAAA,CAAoB,KAAA,GAAQ,UAAA,UAAoB,UAAA;AAAA,iBAwBhD,sBAAA,CAAuB,OAAA,UAAiB,MAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/Header.tsx","../src/Footer.tsx","../src/core/social.ts","../src/core/shell.ts","../src/core/dev.ts","../src/core/theme.ts","../src/core/validation.ts"],"mappings":";;;;;UAsBiB,eAAA;EACf,IAAA;EACA,SAAA;EACA,YAAA;EACA,MAAA;EACA,GAAA;EACA,QAAA,EAAU,SAAA;AAAA;AAAA,UAGK,WAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,SAAA;EACA,UAAA,IAAc,KAAA,EAAO,eAAA,KAAoB,YAAA;AAAA;AAAA,iBAG3B,MAAA,CAAA;EAAS,OAAA;EAAS,KAAA;EAAO,SAAA;EAAW;AAAA,GAAc,WAAA,GAAW,kBAAA,CAAA,GAAA,CAAA,OAAA;;;UCd5D,WAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,SAAA;EACA,UAAA,IAAc,KAAA,EAAO,eAAA,KAAoB,YAAA;AAAA;AAAA,iBAG3B,MAAA,CAAA;EAAS,OAAA;EAAS,KAAA;EAAO,SAAA;EAAW;AAAA,GAAc,WAAA,GAAW,kBAAA,CAAA,GAAA,CAAA,OAAA;;;KC7BjE,cAAA;AAAA,UAEK,UAAA;EACf,QAAA,EAAU,cAAA;EACV,IAAA;EACA,KAAA;EACA,OAAA;AAAA;AAAA,iBAGc,oBAAA,CAAqB,OAAA,EAAS,YAAA,GAAe,UAAA;;;KCPjD,UAAA,GAAa,WAAA,CAAY,YAAA;AAAA,UAEpB,YAAA,SAAqB,YAAA;EACpC,SAAA;EACA,GAAA;EACA,MAAA,EAAQ,UAAA;AAAA;AAAA,UAGO,eAAA,SAAwB,WAAA;EACvC,SAAA;EACA,GAAA;EACA,MAAA,EAAQ,UAAA;EACR,OAAA,EAAS,WAAA,CAAY,WAAA;AAAA;AAAA,UAGN,cAAA;EACf,QAAA,EAAU,YAAA;EACV,QAAA,EAAU,eAAA;EACV,WAAA,EAAa,UAAA;AAAA;AAAA,KAGH,oBAAA,GAAuB,IAAA,CAAK,eAAA,eAA8B,IAAA,CAAK,WAAA;AAAA,UAE1D,sBAAA,SAA+B,IAAA,CAAK,YAAA;EACnD,QAAA,EAAU,YAAA;EACV,aAAA,GAAgB,oBAAA;EAChB,eAAA,GAAkB,oBAAA;AAAA;AAAA,iBAGJ,iBAAA,CAAkB,QAAA,GAAU,YAAA,KAAsB,YAAA;AAAA,iBAgBlD,qBAAA,CAAsB,OAAA,EAAS,YAAA,GAAe,sBAAA;AAAA,iBAoB9C,iBAAA,CACd,aAAA,GAAgB,WAAA,EAChB,eAAA,GAAkB,WAAA,GACjB,eAAA;AAAA,iBAkBa,iCAAA,CAAkC,UAAA,EAAY,sBAAA,GAAyB,cAAA;AAAA,iBAQvE,mBAAA,CAAoB,OAAA,EAAS,YAAA,GAAe,cAAA;AAAA,iBAI5C,kBAAA,CAAmB,KAAA;;;iBC7FnB,mBAAA,CAAA;;;KCHJ,cAAA,GAAiB,MAAA;AAAA,iBAEb,mBAAA,CAAoB,KAAA,GAAQ,UAAA,UAAoB,cAAA;;;UCgB/C,qBAAA;EACf,KAAA;EACA,MAAA;EACA,UAAA,EAAY,CAAA;AAAA;AAAA,cAGD,yBAAA,SAAkC,KAAA;EAAA,SACpC,OAAA;EAAA,SACA,MAAA;cAEG,OAAA,UAAiB,MAAA;AAAA;AAAA,iBAQf,oBAAA,CAAqB,OAAA,YAAmB,qBAAA,CAAsB,sBAAA;AAAA,iBA6D9D,kBAAA,CAAmB,KAAA,YAAiB,qBAAA,CAAsB,UAAA;AAAA,iBAuC1D,uBAAA,CAAwB,OAAA,WAAkB,OAAA,oBAAmC,OAAA,IAAW,YAAA;AAAA,iBAOxF,qBAAA,CAAsB,KAAA,WAAgB,OAAA,oBAAiC,KAAA,IAAS,UAAA;AAAA,iBAOhF,mBAAA,CAAoB,KAAA,GAAQ,UAAA,UAAoB,UAAA;AAAA,iBAwBhD,sBAAA,CAAuB,OAAA,UAAiB,MAAA"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as normalizeBrandTheme, c as themeToCssVariables, d as normalizeBrandDetails, f as normalizeCtaLinks, h as detailsToSocialLinks, i as formatValidationErrors, l as shouldValidateInDev, m as normalizeNavLinks, n as assertValidBrandDetails, o as validateBrandDetails, p as normalizeGmailHref, r as assertValidBrandTheme, s as validateBrandTheme, t as BrandShellValidationError, u as buildShellViewModel } from "./validation-xdqzwr3p.mjs";
1
+ import { a as normalizeBrandTheme, c as themeToCssVariables, d as buildShellViewModelFromNormalized, f as normalizeBrandDetails, g as detailsToSocialLinks, h as normalizeNavLinks, i as formatValidationErrors, l as shouldValidateInDev, m as normalizeEmailHref, n as assertValidBrandDetails, o as validateBrandDetails, p as normalizeCtaLinks, r as assertValidBrandTheme, s as validateBrandTheme, t as BrandShellValidationError, u as buildShellViewModel } from "./validation-CtH2UkVv.mjs";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
 
4
4
  //#region src/react/theme.ts
@@ -73,8 +73,8 @@ function MailIcon({ className, width = "1em", height = "1em", ...props }) {
73
73
  }), /* @__PURE__ */ jsx("path", { d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" })]
74
74
  });
75
75
  }
76
- function MessageCircleIcon({ className, width = "1em", height = "1em", ...props }) {
77
- return /* @__PURE__ */ jsx("svg", {
76
+ function GlobeIcon({ className, width = "1em", height = "1em", ...props }) {
77
+ return /* @__PURE__ */ jsxs("svg", {
78
78
  viewBox: "0 0 24 24",
79
79
  fill: "none",
80
80
  stroke: "currentColor",
@@ -85,13 +85,21 @@ function MessageCircleIcon({ className, width = "1em", height = "1em", ...props
85
85
  width,
86
86
  height,
87
87
  ...props,
88
- children: /* @__PURE__ */ jsx("path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z" })
88
+ children: [
89
+ /* @__PURE__ */ jsx("circle", {
90
+ cx: "12",
91
+ cy: "12",
92
+ r: "10"
93
+ }),
94
+ /* @__PURE__ */ jsx("path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }),
95
+ /* @__PURE__ */ jsx("path", { d: "M2 12h20" })
96
+ ]
89
97
  });
90
98
  }
91
99
 
92
100
  //#endregion
93
101
  //#region src/Header.tsx
94
- function Header({ details, theme, className }) {
102
+ function Header({ details, theme, className, renderLink }) {
95
103
  if (shouldValidateInDev()) {
96
104
  assertValidBrandDetails(details, "brand-shell/Header details");
97
105
  assertValidBrandTheme(theme, "brand-shell/Header theme");
@@ -100,12 +108,21 @@ function Header({ details, theme, className }) {
100
108
  const normalizedTheme = normalizeBrandTheme(theme);
101
109
  const ctaLayout = normalizedTheme?.ctaLayout === "stacked" ? "stacked" : "inline";
102
110
  const style = themeToStyle(normalizedTheme);
103
- const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(normalizedDetails);
111
+ const { navLinks, ctaLinks, socialLinks } = buildShellViewModelFromNormalized(normalizedDetails);
104
112
  const combinedClassName = ["brand-shell-header", className].filter(Boolean).join(" ");
105
- const brandIdentity = normalizedDetails.homeHref ? /* @__PURE__ */ jsx("a", {
113
+ const LinkEl = renderLink ? renderLink : ({ href, className: cls, "aria-label": ariaLabel, target, rel, children }) => /* @__PURE__ */ jsx("a", {
114
+ href,
115
+ className: cls,
116
+ "aria-label": ariaLabel,
117
+ target,
118
+ rel,
119
+ children
120
+ });
121
+ const brandIdentity = normalizedDetails.homeHref ? /* @__PURE__ */ jsx(LinkEl, {
106
122
  href: normalizedDetails.homeHref,
107
123
  className: "brand-shell-header__name",
108
124
  "aria-label": `${normalizedDetails.name} home`,
125
+ target: "_self",
109
126
  children: normalizedDetails.name
110
127
  }) : /* @__PURE__ */ jsx("span", {
111
128
  className: "brand-shell-header__name",
@@ -127,7 +144,7 @@ function Header({ details, theme, className }) {
127
144
  children: /* @__PURE__ */ jsx("ul", {
128
145
  className: "brand-shell-header__list",
129
146
  children: navLinks.map((link) => {
130
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", {
147
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(LinkEl, {
131
148
  href: link.href,
132
149
  className: "brand-shell-header__link",
133
150
  "aria-label": link.ariaLabel,
@@ -140,7 +157,7 @@ function Header({ details, theme, className }) {
140
157
  }),
141
158
  ctaLinks.length > 0 && /* @__PURE__ */ jsx("div", {
142
159
  className: "brand-shell-header__ctas",
143
- children: ctaLinks.map((action) => /* @__PURE__ */ jsx("a", {
160
+ children: ctaLinks.map((action) => /* @__PURE__ */ jsx(LinkEl, {
144
161
  href: action.href,
145
162
  className: ["brand-shell-button", `brand-shell-button--${action.variant}`].join(" "),
146
163
  "aria-label": action.ariaLabel,
@@ -154,13 +171,19 @@ function Header({ details, theme, className }) {
154
171
  "aria-label": "Social links",
155
172
  children: socialLinks.map((link) => {
156
173
  const Icon = SOCIAL_ICON_COMPONENTS$1[link.platform];
174
+ const isMailto = link.href.startsWith("mailto:");
157
175
  return /* @__PURE__ */ jsx("a", {
158
176
  href: link.href,
159
177
  className: "brand-shell-header__social-link",
160
178
  "aria-label": link.label,
161
- target: "_blank",
162
- rel: "noopener noreferrer",
163
- children: Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx("span", { children: link.label[0] })
179
+ ...!isMailto && {
180
+ target: "_blank",
181
+ rel: "noopener noreferrer"
182
+ },
183
+ children: Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) : link.iconSvg ? /* @__PURE__ */ jsx("span", {
184
+ dangerouslySetInnerHTML: { __html: link.iconSvg },
185
+ "aria-hidden": "true"
186
+ }) : /* @__PURE__ */ jsx("span", { children: link.label[0] })
164
187
  }, link.href + link.platform);
165
188
  })
166
189
  })
@@ -170,7 +193,7 @@ function Header({ details, theme, className }) {
170
193
  });
171
194
  }
172
195
  const SOCIAL_ICON_COMPONENTS$1 = {
173
- website: MessageCircleIcon,
196
+ website: GlobeIcon,
174
197
  linkedin: LinkedinIcon,
175
198
  email: MailIcon,
176
199
  github: GithubIcon,
@@ -180,7 +203,7 @@ const SOCIAL_ICON_COMPONENTS$1 = {
180
203
 
181
204
  //#endregion
182
205
  //#region src/Footer.tsx
183
- function Footer({ details, theme, className }) {
206
+ function Footer({ details, theme, className, renderLink }) {
184
207
  if (shouldValidateInDev()) {
185
208
  assertValidBrandDetails(details, "brand-shell/Footer details");
186
209
  assertValidBrandTheme(theme, "brand-shell/Footer theme");
@@ -189,9 +212,18 @@ function Footer({ details, theme, className }) {
189
212
  const normalizedTheme = normalizeBrandTheme(theme);
190
213
  const ctaLayout = normalizedTheme?.ctaLayout === "stacked" ? "stacked" : "inline";
191
214
  const style = themeToStyle(normalizedTheme);
192
- const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(normalizedDetails);
215
+ const { navLinks, ctaLinks, socialLinks } = buildShellViewModelFromNormalized(normalizedDetails);
216
+ const combinedClassName = ["brand-shell-footer", className].filter(Boolean).join(" ");
217
+ const LinkEl = renderLink ? renderLink : ({ href, className: cls, "aria-label": ariaLabel, target, rel, children }) => /* @__PURE__ */ jsx("a", {
218
+ href,
219
+ className: cls,
220
+ "aria-label": ariaLabel,
221
+ target,
222
+ rel,
223
+ children
224
+ });
193
225
  return /* @__PURE__ */ jsx("footer", {
194
- className: ["brand-shell-footer", className].filter(Boolean).join(" "),
226
+ className: combinedClassName,
195
227
  "data-brand-cta-layout": ctaLayout,
196
228
  style,
197
229
  role: "contentinfo",
@@ -216,7 +248,7 @@ function Footer({ details, theme, className }) {
216
248
  children: /* @__PURE__ */ jsx("ul", {
217
249
  className: "brand-shell-footer__list",
218
250
  children: navLinks.map((link) => {
219
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", {
251
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(LinkEl, {
220
252
  href: link.href,
221
253
  className: "brand-shell-footer__link",
222
254
  "aria-label": link.ariaLabel,
@@ -229,7 +261,7 @@ function Footer({ details, theme, className }) {
229
261
  }),
230
262
  ctaLinks.length > 0 && /* @__PURE__ */ jsx("div", {
231
263
  className: "brand-shell-footer__ctas",
232
- children: ctaLinks.map((action) => /* @__PURE__ */ jsx("a", {
264
+ children: ctaLinks.map((action) => /* @__PURE__ */ jsx(LinkEl, {
233
265
  href: action.href,
234
266
  className: ["brand-shell-button", `brand-shell-button--${action.variant}`].join(" "),
235
267
  "aria-label": action.ariaLabel,
@@ -243,13 +275,19 @@ function Footer({ details, theme, className }) {
243
275
  "aria-label": "Social links",
244
276
  children: socialLinks.map((link) => {
245
277
  const Icon = SOCIAL_ICON_COMPONENTS[link.platform];
278
+ const isMailto = link.href.startsWith("mailto:");
246
279
  return /* @__PURE__ */ jsx("a", {
247
280
  href: link.href,
248
281
  className: "brand-shell-footer__social-link",
249
282
  "aria-label": link.label,
250
- target: "_blank",
251
- rel: "noopener noreferrer",
252
- children: Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx("span", { children: link.label[0] })
283
+ ...!isMailto && {
284
+ target: "_blank",
285
+ rel: "noopener noreferrer"
286
+ },
287
+ children: Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) : link.iconSvg ? /* @__PURE__ */ jsx("span", {
288
+ dangerouslySetInnerHTML: { __html: link.iconSvg },
289
+ "aria-hidden": "true"
290
+ }) : /* @__PURE__ */ jsx("span", { children: link.label[0] })
253
291
  }, link.href + link.platform);
254
292
  })
255
293
  })
@@ -267,7 +305,7 @@ function Footer({ details, theme, className }) {
267
305
  });
268
306
  }
269
307
  const SOCIAL_ICON_COMPONENTS = {
270
- website: MessageCircleIcon,
308
+ website: GlobeIcon,
271
309
  linkedin: LinkedinIcon,
272
310
  email: MailIcon,
273
311
  github: GithubIcon,
@@ -276,5 +314,5 @@ const SOCIAL_ICON_COMPONENTS = {
276
314
  };
277
315
 
278
316
  //#endregion
279
- export { BrandShellValidationError, Footer, Header, assertValidBrandDetails, assertValidBrandTheme, buildShellViewModel, detailsToSocialLinks, formatValidationErrors, normalizeBrandDetails, normalizeBrandTheme, normalizeCtaLinks, normalizeGmailHref, normalizeNavLinks, shouldValidateInDev, themeToCssVariables, validateBrandDetails, validateBrandTheme };
317
+ export { BrandShellValidationError, Footer, Header, assertValidBrandDetails, assertValidBrandTheme, buildShellViewModel, buildShellViewModelFromNormalized, detailsToSocialLinks, formatValidationErrors, normalizeBrandDetails, normalizeBrandTheme, normalizeCtaLinks, normalizeEmailHref, normalizeNavLinks, shouldValidateInDev, themeToCssVariables, validateBrandDetails, validateBrandTheme };
280
318
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["SOCIAL_ICON_COMPONENTS"],"sources":["../src/react/theme.ts","../src/icons.tsx","../src/Header.tsx","../src/Footer.tsx"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport type { BrandTheme } from \"../core/types\";\nimport { themeToCssVariables } from \"../core/theme\";\n\nexport function themeToStyle(theme?: BrandTheme | null): CSSProperties {\n return themeToCssVariables(theme) as CSSProperties;\n}\n","/**\n * Social Media Icons\n *\n * Brand icons from lucide-react are deprecated and will be removed in v1.0.\n * This file provides reusable icon components using Simple Icons SVG paths.\n * See: https://github.com/lucide-icons/lucide/issues/670\n */\n\nimport type { SVGProps } from \"react\";\n\nexport interface IconProps extends SVGProps<SVGSVGElement> {\n className?: string;\n \"aria-label\"?: string;\n}\n\nexport function GithubIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\" />\n </svg>\n );\n}\n\nexport function TwitterIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n </svg>\n );\n}\n\nexport function LinkedinIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\" />\n </svg>\n );\n}\n\nexport function DiscordIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z\" />\n </svg>\n );\n}\n\nexport function MailIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n width={width}\n height={height}\n {...props}\n >\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\" />\n </svg>\n );\n}\n\nexport function MessageCircleIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n width={width}\n height={height}\n {...props}\n >\n <path d=\"M7.9 20A9 9 0 1 0 4 16.1L2 22Z\" />\n </svg>\n );\n}\n","import type { BrandDetails, BrandTheme } from \"./types\";\nimport {\n assertValidBrandDetails,\n assertValidBrandTheme,\n buildShellViewModel,\n normalizeBrandDetails,\n normalizeBrandTheme,\n shouldValidateInDev,\n} from \"./core\";\nimport type { SocialPlatform } from \"./core\";\nimport type { IconProps } from \"./icons\";\nimport { themeToStyle } from \"./react/theme\";\nimport {\n DiscordIcon,\n GithubIcon,\n LinkedinIcon,\n MailIcon,\n MessageCircleIcon,\n TwitterIcon,\n} from \"./icons\";\n\nexport interface HeaderProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n className?: string;\n}\n\nexport function Header({ details, theme, className }: HeaderProps) {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(details, \"brand-shell/Header details\");\n assertValidBrandTheme(theme, \"brand-shell/Header theme\");\n }\n\n const normalizedDetails = normalizeBrandDetails(details);\n const normalizedTheme = normalizeBrandTheme(theme);\n const ctaLayout = normalizedTheme?.ctaLayout === \"stacked\" ? \"stacked\" : \"inline\";\n const style = themeToStyle(normalizedTheme);\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(normalizedDetails);\n const combinedClassName = [\"brand-shell-header\", className].filter(Boolean).join(\" \");\n const brandIdentity = normalizedDetails.homeHref ? (\n <a\n href={normalizedDetails.homeHref}\n className=\"brand-shell-header__name\"\n aria-label={`${normalizedDetails.name} home`}\n >\n {normalizedDetails.name}\n </a>\n ) : (\n <span className=\"brand-shell-header__name\">{normalizedDetails.name}</span>\n );\n\n return (\n <header className={combinedClassName} data-brand-cta-layout={ctaLayout} style={style} role=\"banner\">\n <div className=\"brand-shell-header__inner\">\n {brandIdentity}\n <div className=\"brand-shell-header__actions\">\n {navLinks.length > 0 && (\n <nav className=\"brand-shell-header__nav\" aria-label=\"Primary\">\n <ul className=\"brand-shell-header__list\">\n {navLinks.map((link) => {\n return (\n <li key={link.href + link.label}>\n <a\n href={link.href}\n className=\"brand-shell-header__link\"\n aria-label={link.ariaLabel}\n target={link.target}\n rel={link.rel}\n >\n {link.label}\n </a>\n </li>\n );\n })}\n </ul>\n </nav>\n )}\n {ctaLinks.length > 0 && (\n <div className=\"brand-shell-header__ctas\">\n {ctaLinks.map((action) => (\n <a\n key={action.href + action.label}\n href={action.href}\n className={[\"brand-shell-button\", `brand-shell-button--${action.variant}`].join(\" \")}\n aria-label={action.ariaLabel}\n target={action.target}\n rel={action.rel}\n >\n {action.label}\n </a>\n ))}\n </div>\n )}\n {socialLinks.length > 0 && (\n <div className=\"brand-shell-header__social\" aria-label=\"Social links\">\n {socialLinks.map((link) => {\n const Icon = SOCIAL_ICON_COMPONENTS[link.platform];\n return (\n <a\n key={link.href + link.platform}\n href={link.href}\n className=\"brand-shell-header__social-link\"\n aria-label={link.label}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {Icon ? <Icon aria-hidden=\"true\" /> : <span>{link.label[0]}</span>}\n </a>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </header>\n );\n}\n\nconst SOCIAL_ICON_COMPONENTS: Record<SocialPlatform, (props: IconProps) => JSX.Element> = {\n website: MessageCircleIcon,\n linkedin: LinkedinIcon,\n email: MailIcon,\n github: GithubIcon,\n twitter: TwitterIcon,\n discord: DiscordIcon,\n};\n","import type { BrandDetails, BrandTheme } from \"./types\";\nimport {\n assertValidBrandDetails,\n assertValidBrandTheme,\n buildShellViewModel,\n normalizeBrandDetails,\n normalizeBrandTheme,\n shouldValidateInDev,\n} from \"./core\";\nimport type { SocialPlatform } from \"./core\";\nimport type { IconProps } from \"./icons\";\nimport { themeToStyle } from \"./react/theme\";\nimport {\n DiscordIcon,\n GithubIcon,\n LinkedinIcon,\n MailIcon,\n MessageCircleIcon,\n TwitterIcon,\n} from \"./icons\";\n\nexport interface FooterProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n className?: string;\n}\n\nexport function Footer({ details, theme, className }: FooterProps) {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(details, \"brand-shell/Footer details\");\n assertValidBrandTheme(theme, \"brand-shell/Footer theme\");\n }\n\n const normalizedDetails = normalizeBrandDetails(details);\n const normalizedTheme = normalizeBrandTheme(theme);\n const ctaLayout = normalizedTheme?.ctaLayout === \"stacked\" ? \"stacked\" : \"inline\";\n const style = themeToStyle(normalizedTheme);\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(normalizedDetails);\n const combinedClassName = [\"brand-shell-footer\", className].filter(Boolean).join(\" \");\n\n return (\n <footer className={combinedClassName} data-brand-cta-layout={ctaLayout} style={style} role=\"contentinfo\">\n <div className=\"brand-shell-footer__inner\">\n <div className=\"brand-shell-footer__top\">\n <div className=\"brand-shell-footer__brand\">\n <p className=\"brand-shell-footer__name\">{normalizedDetails.name}</p>\n {normalizedDetails.tagline && <p className=\"brand-shell-footer__tagline\">{normalizedDetails.tagline}</p>}\n </div>\n {navLinks.length > 0 && (\n <nav className=\"brand-shell-footer__nav\" aria-label=\"Footer\">\n <ul className=\"brand-shell-footer__list\">\n {navLinks.map((link) => {\n return (\n <li key={link.href + link.label}>\n <a\n href={link.href}\n className=\"brand-shell-footer__link\"\n aria-label={link.ariaLabel}\n target={link.target}\n rel={link.rel}\n >\n {link.label}\n </a>\n </li>\n );\n })}\n </ul>\n </nav>\n )}\n {ctaLinks.length > 0 && (\n <div className=\"brand-shell-footer__ctas\">\n {ctaLinks.map((action) => (\n <a\n key={action.href + action.label}\n href={action.href}\n className={[\"brand-shell-button\", `brand-shell-button--${action.variant}`].join(\" \")}\n aria-label={action.ariaLabel}\n target={action.target}\n rel={action.rel}\n >\n {action.label}\n </a>\n ))}\n </div>\n )}\n {socialLinks.length > 0 && (\n <div className=\"brand-shell-footer__social\" aria-label=\"Social links\">\n {socialLinks.map((link) => {\n const Icon = SOCIAL_ICON_COMPONENTS[link.platform];\n return (\n <a\n key={link.href + link.platform}\n href={link.href}\n className=\"brand-shell-footer__social-link\"\n aria-label={link.label}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {Icon ? <Icon aria-hidden=\"true\" /> : <span>{link.label[0]}</span>}\n </a>\n );\n })}\n </div>\n )}\n </div>\n <p className=\"brand-shell-footer__copy\">&copy; {new Date().getFullYear()} {normalizedDetails.name}</p>\n </div>\n </footer>\n );\n}\n\nconst SOCIAL_ICON_COMPONENTS: Record<SocialPlatform, (props: IconProps) => JSX.Element> = {\n website: MessageCircleIcon,\n linkedin: LinkedinIcon,\n email: MailIcon,\n github: GithubIcon,\n twitter: TwitterIcon,\n discord: DiscordIcon,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["SOCIAL_ICON_COMPONENTS"],"sources":["../src/react/theme.ts","../src/icons.tsx","../src/Header.tsx","../src/Footer.tsx"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport type { BrandTheme } from \"../core/types\";\nimport { themeToCssVariables } from \"../core/theme\";\n\nexport function themeToStyle(theme?: BrandTheme | null): CSSProperties {\n return themeToCssVariables(theme) as CSSProperties;\n}\n","/**\n * Social Media Icons\n *\n * Brand icons from lucide-react are deprecated and will be removed in v1.0.\n * This file provides reusable icon components using Simple Icons SVG paths.\n * See: https://github.com/lucide-icons/lucide/issues/670\n */\n\nimport type { SVGProps } from \"react\";\n\nexport interface IconProps extends SVGProps<SVGSVGElement> {\n className?: string;\n \"aria-label\"?: string;\n}\n\nexport function GithubIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\" />\n </svg>\n );\n}\n\nexport function TwitterIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n </svg>\n );\n}\n\nexport function LinkedinIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\" />\n </svg>\n );\n}\n\nexport function DiscordIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className} width={width} height={height} {...props}>\n <path d=\"M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z\" />\n </svg>\n );\n}\n\nexport function MailIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n width={width}\n height={height}\n {...props}\n >\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\" />\n </svg>\n );\n}\n\nexport function GlobeIcon({ className, width = \"1em\", height = \"1em\", ...props }: IconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n width={width}\n height={height}\n {...props}\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\" />\n <path d=\"M2 12h20\" />\n </svg>\n );\n}\n","import type { ReactElement, ReactNode } from \"react\";\nimport type { BrandDetails, BrandTheme } from \"./types\";\nimport {\n assertValidBrandDetails,\n assertValidBrandTheme,\n buildShellViewModelFromNormalized,\n normalizeBrandDetails,\n normalizeBrandTheme,\n shouldValidateInDev,\n} from \"./core\";\nimport type { SocialPlatform } from \"./core\";\nimport type { IconProps } from \"./icons\";\nimport { themeToStyle } from \"./react/theme\";\nimport {\n DiscordIcon,\n GithubIcon,\n GlobeIcon,\n LinkedinIcon,\n MailIcon,\n TwitterIcon,\n} from \"./icons\";\n\nexport interface LinkRenderProps {\n href: string;\n className: string;\n \"aria-label\": string;\n target: string;\n rel?: string;\n children: ReactNode;\n}\n\nexport interface HeaderProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n className?: string;\n renderLink?: (props: LinkRenderProps) => ReactElement;\n}\n\nexport function Header({ details, theme, className, renderLink }: HeaderProps) {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(details, \"brand-shell/Header details\");\n assertValidBrandTheme(theme, \"brand-shell/Header theme\");\n }\n\n const normalizedDetails = normalizeBrandDetails(details);\n const normalizedTheme = normalizeBrandTheme(theme);\n const ctaLayout = normalizedTheme?.ctaLayout === \"stacked\" ? \"stacked\" : \"inline\";\n const style = themeToStyle(normalizedTheme);\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModelFromNormalized(normalizedDetails);\n const combinedClassName = [\"brand-shell-header\", className].filter(Boolean).join(\" \");\n\n const LinkEl = renderLink\n ? renderLink\n : ({ href, className: cls, \"aria-label\": ariaLabel, target, rel, children }: LinkRenderProps) => (\n <a href={href} className={cls} aria-label={ariaLabel} target={target} rel={rel}>{children}</a>\n );\n\n const brandIdentity = normalizedDetails.homeHref ? (\n <LinkEl\n href={normalizedDetails.homeHref}\n className=\"brand-shell-header__name\"\n aria-label={`${normalizedDetails.name} home`}\n target=\"_self\"\n >\n {normalizedDetails.name}\n </LinkEl>\n ) : (\n <span className=\"brand-shell-header__name\">{normalizedDetails.name}</span>\n );\n\n return (\n <header className={combinedClassName} data-brand-cta-layout={ctaLayout} style={style} role=\"banner\">\n <div className=\"brand-shell-header__inner\">\n {brandIdentity}\n <div className=\"brand-shell-header__actions\">\n {navLinks.length > 0 && (\n <nav className=\"brand-shell-header__nav\" aria-label=\"Primary\">\n <ul className=\"brand-shell-header__list\">\n {navLinks.map((link) => {\n return (\n <li key={link.href + link.label}>\n <LinkEl\n href={link.href}\n className=\"brand-shell-header__link\"\n aria-label={link.ariaLabel}\n target={link.target}\n rel={link.rel}\n >\n {link.label}\n </LinkEl>\n </li>\n );\n })}\n </ul>\n </nav>\n )}\n {ctaLinks.length > 0 && (\n <div className=\"brand-shell-header__ctas\">\n {ctaLinks.map((action) => (\n <LinkEl\n key={action.href + action.label}\n href={action.href}\n className={[\"brand-shell-button\", `brand-shell-button--${action.variant}`].join(\" \")}\n aria-label={action.ariaLabel}\n target={action.target}\n rel={action.rel}\n >\n {action.label}\n </LinkEl>\n ))}\n </div>\n )}\n {socialLinks.length > 0 && (\n <div className=\"brand-shell-header__social\" aria-label=\"Social links\">\n {socialLinks.map((link) => {\n const Icon = SOCIAL_ICON_COMPONENTS[link.platform as SocialPlatform];\n const isMailto = link.href.startsWith(\"mailto:\");\n return (\n <a\n key={link.href + link.platform}\n href={link.href}\n className=\"brand-shell-header__social-link\"\n aria-label={link.label}\n {...(!isMailto && { target: \"_blank\", rel: \"noopener noreferrer\" })}\n >\n {Icon ? (\n <Icon aria-hidden=\"true\" />\n ) : link.iconSvg ? (\n <span dangerouslySetInnerHTML={{ __html: link.iconSvg }} aria-hidden=\"true\" />\n ) : (\n <span>{link.label[0]}</span>\n )}\n </a>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </header>\n );\n}\n\nconst SOCIAL_ICON_COMPONENTS: Record<SocialPlatform, (props: IconProps) => JSX.Element> = {\n website: GlobeIcon,\n linkedin: LinkedinIcon,\n email: MailIcon,\n github: GithubIcon,\n twitter: TwitterIcon,\n discord: DiscordIcon,\n};\n","import type { ReactElement } from \"react\";\nimport type { BrandDetails, BrandTheme } from \"./types\";\nimport {\n assertValidBrandDetails,\n assertValidBrandTheme,\n buildShellViewModelFromNormalized,\n normalizeBrandDetails,\n normalizeBrandTheme,\n shouldValidateInDev,\n} from \"./core\";\nimport type { SocialPlatform } from \"./core\";\nimport type { IconProps } from \"./icons\";\nimport { themeToStyle } from \"./react/theme\";\nimport {\n DiscordIcon,\n GithubIcon,\n GlobeIcon,\n LinkedinIcon,\n MailIcon,\n TwitterIcon,\n} from \"./icons\";\n\nimport type { LinkRenderProps } from \"./Header\";\n\nexport interface FooterProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n className?: string;\n renderLink?: (props: LinkRenderProps) => ReactElement;\n}\n\nexport function Footer({ details, theme, className, renderLink }: FooterProps) {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(details, \"brand-shell/Footer details\");\n assertValidBrandTheme(theme, \"brand-shell/Footer theme\");\n }\n\n const normalizedDetails = normalizeBrandDetails(details);\n const normalizedTheme = normalizeBrandTheme(theme);\n const ctaLayout = normalizedTheme?.ctaLayout === \"stacked\" ? \"stacked\" : \"inline\";\n const style = themeToStyle(normalizedTheme);\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModelFromNormalized(normalizedDetails);\n const combinedClassName = [\"brand-shell-footer\", className].filter(Boolean).join(\" \");\n\n const LinkEl = renderLink\n ? renderLink\n : ({ href, className: cls, \"aria-label\": ariaLabel, target, rel, children }: LinkRenderProps) => (\n <a href={href} className={cls} aria-label={ariaLabel} target={target} rel={rel}>{children}</a>\n );\n\n return (\n <footer className={combinedClassName} data-brand-cta-layout={ctaLayout} style={style} role=\"contentinfo\">\n <div className=\"brand-shell-footer__inner\">\n <div className=\"brand-shell-footer__top\">\n <div className=\"brand-shell-footer__brand\">\n <p className=\"brand-shell-footer__name\">{normalizedDetails.name}</p>\n {normalizedDetails.tagline && <p className=\"brand-shell-footer__tagline\">{normalizedDetails.tagline}</p>}\n </div>\n {navLinks.length > 0 && (\n <nav className=\"brand-shell-footer__nav\" aria-label=\"Footer\">\n <ul className=\"brand-shell-footer__list\">\n {navLinks.map((link) => {\n return (\n <li key={link.href + link.label}>\n <LinkEl\n href={link.href}\n className=\"brand-shell-footer__link\"\n aria-label={link.ariaLabel}\n target={link.target}\n rel={link.rel}\n >\n {link.label}\n </LinkEl>\n </li>\n );\n })}\n </ul>\n </nav>\n )}\n {ctaLinks.length > 0 && (\n <div className=\"brand-shell-footer__ctas\">\n {ctaLinks.map((action) => (\n <LinkEl\n key={action.href + action.label}\n href={action.href}\n className={[\"brand-shell-button\", `brand-shell-button--${action.variant}`].join(\" \")}\n aria-label={action.ariaLabel}\n target={action.target}\n rel={action.rel}\n >\n {action.label}\n </LinkEl>\n ))}\n </div>\n )}\n {socialLinks.length > 0 && (\n <div className=\"brand-shell-footer__social\" aria-label=\"Social links\">\n {socialLinks.map((link) => {\n const Icon = SOCIAL_ICON_COMPONENTS[link.platform as SocialPlatform];\n const isMailto = link.href.startsWith(\"mailto:\");\n return (\n <a\n key={link.href + link.platform}\n href={link.href}\n className=\"brand-shell-footer__social-link\"\n aria-label={link.label}\n {...(!isMailto && { target: \"_blank\", rel: \"noopener noreferrer\" })}\n >\n {Icon ? (\n <Icon aria-hidden=\"true\" />\n ) : link.iconSvg ? (\n <span dangerouslySetInnerHTML={{ __html: link.iconSvg }} aria-hidden=\"true\" />\n ) : (\n <span>{link.label[0]}</span>\n )}\n </a>\n );\n })}\n </div>\n )}\n </div>\n <p className=\"brand-shell-footer__copy\">&copy; {new Date().getFullYear()} {normalizedDetails.name}</p>\n </div>\n </footer>\n );\n}\n\nconst SOCIAL_ICON_COMPONENTS: Record<SocialPlatform, (props: IconProps) => JSX.Element> = {\n website: GlobeIcon,\n linkedin: LinkedinIcon,\n email: MailIcon,\n github: GithubIcon,\n twitter: TwitterIcon,\n discord: DiscordIcon,\n};\n"],"mappings":"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"}
package/dist/svelte.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as BrandTheme, n as BrandDetails } from "./types-PQziYg7Z.mjs";
1
+ import { i as BrandTheme, n as BrandDetails } from "./types-B_CLRZMO.mjs";
2
2
  import { BrandShellElementProps } from "./web.mjs";
3
3
  import { Action } from "svelte/action";
4
4
 
package/dist/svelte.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { l as shouldValidateInDev, n as assertValidBrandDetails, r as assertValidBrandTheme } from "./validation-xdqzwr3p.mjs";
1
+ import { l as shouldValidateInDev, n as assertValidBrandDetails, r as assertValidBrandTheme } from "./validation-CtH2UkVv.mjs";
2
2
  import { applyBrandShellProps, registerBrandShellElements } from "./web.mjs";
3
3
 
4
4
  //#region src/svelte/index.ts
@@ -1,4 +1,18 @@
1
1
  //#region src/core/types.d.ts
2
+ /**
3
+ * A custom social link for platforms not built into brand-shell.
4
+ * Built-in platforms retain priority; custom links are appended in insertion order.
5
+ */
6
+ interface CustomSocialLink {
7
+ /** Arbitrary identifier used as a key (e.g. "bluesky", "mastodon") */
8
+ platform: string;
9
+ /** Link URL (must be a safe href) */
10
+ href: string;
11
+ /** Accessible label (used as aria-label) */
12
+ label: string;
13
+ /** Optional inline SVG string (24×24 viewBox). aria-hidden="true" is added automatically. */
14
+ iconSvg?: string;
15
+ }
2
16
  /**
3
17
  * Schema for header/footer content. Caller passes these; package does not store them.
4
18
  */
@@ -42,7 +56,7 @@ interface BrandDetails {
42
56
  /** LinkedIn profile URL */
43
57
  linkedin?: string;
44
58
  /** Email address (e.g. mailto: or plain) */
45
- gmail?: string;
59
+ email?: string;
46
60
  /** GitHub profile URL */
47
61
  github?: string;
48
62
  /** Twitter/X profile URL */
@@ -53,6 +67,8 @@ interface BrandDetails {
53
67
  website?: string;
54
68
  /** Optional tagline (e.g. in footer) */
55
69
  tagline?: string;
70
+ /** Additional social links for custom platforms (Bluesky, Mastodon, YouTube, etc.) */
71
+ customSocialLinks?: CustomSocialLink[];
56
72
  }
57
73
  /**
58
74
  * Optional theme to adapt branding without custom CSS.
@@ -75,7 +91,15 @@ interface BrandTheme {
75
91
  buttonTextColor?: string;
76
92
  /** Mobile CTA arrangement: side-by-side (`inline`) or one-per-row (`stacked`) */
77
93
  ctaLayout?: "inline" | "stacked";
94
+ /** Border radius for buttons and containers (e.g. 0.5rem, 4px) → --brand-radius */
95
+ borderRadius?: string;
96
+ /** Header height override (e.g. 4rem, 64px) → --brand-header-height */
97
+ headerHeight?: string;
98
+ /** Footer padding override (e.g. 3rem 1.5rem) → --brand-footer-padding */
99
+ footerPadding?: string;
100
+ /** Secondary button background color → --brand-button-secondary */
101
+ secondaryButtonBg?: string;
78
102
  }
79
103
  //#endregion
80
- export { BrandTheme as i, BrandDetails as n, BrandNavLink as r, BrandAction as t };
81
- //# sourceMappingURL=types-PQziYg7Z.d.mts.map
104
+ export { CustomSocialLink as a, BrandTheme as i, BrandDetails as n, BrandNavLink as r, BrandAction as t };
105
+ //# sourceMappingURL=types-B_CLRZMO.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-B_CLRZMO.d.mts","names":[],"sources":["../src/core/types.ts"],"mappings":";;AAIA;;;UAAiB,gBAAA;EAEf;EAAA,QAAA;EAIA;EAFA,IAAA;EAIO;EAFP,KAAA;EAQe;EANf,OAAA;AAAA;;;;UAMe,YAAA;EAQf;EANA,KAAA;EAQG;EANH,IAAA;EASe;EAPf,SAAA;;EAEA,MAAA;EAOA;EALA,GAAA;AAAA;AAAA,UAGe,WAAA;EAUf;EARA,KAAA;EAUO;EARP,IAAA;EAWe;EATf,SAAA;;EAEA,MAAA;EAegB;EAbhB,GAAA;EA+BoB;EA7BpB,OAAA;AAAA;AAAA,UAGe,YAAA;EAIf;EAFA,IAAA;EAIW;EAFX,QAAA;EAIgB;EAFhB,QAAA,GAAW,YAAA;EAIO;EAFlB,aAAA,GAAgB,WAAA;EAMhB;EAJA,eAAA,GAAkB,WAAA;EAQlB;EANA,QAAA;EAUA;EARA,KAAA;EAYA;EAVA,MAAA;EAUoC;EARpC,OAAA;EAee;EAbf,OAAA;;EAEA,OAAA;EAaA;EAXA,OAAA;EAeA;EAbA,iBAAA,GAAoB,gBAAA;AAAA;;;;;UAOL,UAAA;EAsBf;EApBA,YAAA;EAsBiB;EApBjB,eAAA;;EAEA,SAAA;;EAEA,UAAA;;EAEA,SAAA;;EAEA,cAAA;;EAEA,eAAA;;EAEA,SAAA;;EAEA,YAAA;;EAEA,YAAA;;EAEA,aAAA;;EAEA,iBAAA;AAAA"}