brand-shell 0.14.0 → 0.16.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.
@@ -83,7 +83,9 @@
83
83
  "customSocialLinks": {
84
84
  "type": "array",
85
85
  "items": { "$ref": "#/$defs/CustomSocialLink" }
86
- }
86
+ },
87
+ "logoSrc": { "type": "string", "minLength": 1 },
88
+ "logoAlt": { "type": "string", "minLength": 1 }
87
89
  }
88
90
  },
89
91
  "BrandTheme": {
@@ -101,7 +103,8 @@
101
103
  "borderRadius": { "type": "string", "minLength": 1 },
102
104
  "headerHeight": { "type": "string", "minLength": 1 },
103
105
  "footerPadding": { "type": "string", "minLength": 1 },
104
- "secondaryButtonBg": { "type": "string", "minLength": 1 }
106
+ "secondaryButtonBg": { "type": "string", "minLength": 1 },
107
+ "logoHeight": { "type": "string", "minLength": 1 }
105
108
  }
106
109
  }
107
110
  }
package/dist/default.css CHANGED
@@ -109,6 +109,18 @@
109
109
  color: var(--_primary);
110
110
  }
111
111
 
112
+ .brand-shell-header__logo {
113
+ height: var(--brand-logo-height, 2rem);
114
+ width: auto;
115
+ display: block;
116
+ object-fit: contain;
117
+ }
118
+
119
+ /* Hide decorative dot when logo is present */
120
+ .brand-shell-header__name:has(.brand-shell-header__logo)::before {
121
+ display: none;
122
+ }
123
+
112
124
  .brand-shell-header__name:focus-visible {
113
125
  outline: 2px solid var(--_primary);
114
126
  outline-offset: 3px;
@@ -434,9 +446,19 @@
434
446
  .brand-shell-footer__name {
435
447
  font-size: 1rem;
436
448
  font-weight: 600;
449
+ color: var(--_text);
450
+ text-decoration: none;
437
451
  margin: 0 0 var(--brand-space-sm) 0;
438
452
  }
439
453
 
454
+ .brand-shell-footer__logo {
455
+ height: calc(var(--brand-logo-height, 2rem) * 0.75);
456
+ width: auto;
457
+ display: block;
458
+ object-fit: contain;
459
+ margin-bottom: var(--brand-space-sm);
460
+ }
461
+
440
462
  .brand-shell-footer__nav {
441
463
  display: flex;
442
464
  align-items: center;
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as BrandAction, D as CustomSocialLink, E as BrandTheme, S as detailsToSocialLinks, T as BrandNavLink, _ as normalizeCtaLinks, a as formatValidationErrors, b as SocialLink, c as validateBrandTheme, d as ShellActionLink, f as ShellNavLink, g as normalizeBrandDetails, h as buildShellViewModelFromNormalized, i as assertValidBrandTheme, l as LinkTarget, m as buildShellViewModel, n as BrandValidationResult, o as normalizeBrandTheme, p as ShellViewModel, r as assertValidBrandDetails, s as validateBrandDetails, t as BrandShellValidationError, u as NormalizedBrandDetails, v as normalizeEmailHref, w as BrandDetails, x as SocialPlatform, y as normalizeNavLinks } from "./validation-Cic4hPME.mjs";
1
+ import { C as BrandAction, D as CustomSocialLink, E as BrandTheme, S as detailsToSocialLinks, T as BrandNavLink, _ as normalizeCtaLinks, a as formatValidationErrors, b as SocialLink, c as validateBrandTheme, d as ShellActionLink, f as ShellNavLink, g as normalizeBrandDetails, h as buildShellViewModelFromNormalized, i as assertValidBrandTheme, l as LinkTarget, m as buildShellViewModel, n as BrandValidationResult, o as normalizeBrandTheme, p as ShellViewModel, r as assertValidBrandDetails, s as validateBrandDetails, t as BrandShellValidationError, u as NormalizedBrandDetails, v as normalizeEmailHref, w as BrandDetails, x as SocialPlatform, y as normalizeNavLinks } from "./validation-D1q4yKZZ.mjs";
2
2
  import * as react_jsx_runtime0 from "react/jsx-runtime";
3
3
  import { ReactElement, ReactNode } from "react";
4
4
 
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
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";
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-BhhgC3n1.mjs";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
 
4
4
  //#region src/react/theme.ts
@@ -118,15 +118,20 @@ function Header({ details, theme, className, renderLink }) {
118
118
  rel,
119
119
  children
120
120
  });
121
+ const logoContent = normalizedDetails.logoSrc ? /* @__PURE__ */ jsx("img", {
122
+ src: normalizedDetails.logoSrc,
123
+ alt: normalizedDetails.logoAlt ?? normalizedDetails.name,
124
+ className: "brand-shell-header__logo"
125
+ }) : normalizedDetails.name;
121
126
  const brandIdentity = normalizedDetails.homeHref ? /* @__PURE__ */ jsx(LinkEl, {
122
127
  href: normalizedDetails.homeHref,
123
128
  className: "brand-shell-header__name",
124
129
  "aria-label": `${normalizedDetails.name} home`,
125
130
  target: "_self",
126
- children: normalizedDetails.name
131
+ children: logoContent
127
132
  }) : /* @__PURE__ */ jsx("span", {
128
133
  className: "brand-shell-header__name",
129
- children: normalizedDetails.name
134
+ children: logoContent
130
135
  });
131
136
  return /* @__PURE__ */ jsx("header", {
132
137
  className: combinedClassName,
@@ -234,10 +239,24 @@ function Footer({ details, theme, className, renderLink }) {
234
239
  children: [
235
240
  /* @__PURE__ */ jsxs("div", {
236
241
  className: "brand-shell-footer__brand",
237
- children: [/* @__PURE__ */ jsx("p", {
238
- className: "brand-shell-footer__name",
239
- children: normalizedDetails.name
240
- }), normalizedDetails.tagline && /* @__PURE__ */ jsx("p", {
242
+ children: [(() => {
243
+ const logoContent = normalizedDetails.logoSrc ? /* @__PURE__ */ jsx("img", {
244
+ src: normalizedDetails.logoSrc,
245
+ alt: normalizedDetails.logoAlt ?? normalizedDetails.name,
246
+ className: "brand-shell-footer__logo"
247
+ }) : null;
248
+ if (normalizedDetails.homeHref) return /* @__PURE__ */ jsx(LinkEl, {
249
+ href: normalizedDetails.homeHref,
250
+ className: "brand-shell-footer__name",
251
+ "aria-label": `${normalizedDetails.name} home`,
252
+ target: "_self",
253
+ children: logoContent ?? normalizedDetails.name
254
+ });
255
+ return logoContent ? logoContent : /* @__PURE__ */ jsx("p", {
256
+ className: "brand-shell-footer__name",
257
+ children: normalizedDetails.name
258
+ });
259
+ })(), normalizedDetails.tagline && /* @__PURE__ */ jsx("p", {
241
260
  className: "brand-shell-footer__tagline",
242
261
  children: normalizedDetails.tagline
243
262
  })]
@@ -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 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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"}
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 logoContent = normalizedDetails.logoSrc ? (\n <img\n src={normalizedDetails.logoSrc}\n alt={normalizedDetails.logoAlt ?? normalizedDetails.name}\n className=\"brand-shell-header__logo\"\n />\n ) : (\n normalizedDetails.name\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 {logoContent}\n </LinkEl>\n ) : (\n <span className=\"brand-shell-header__name\">{logoContent}</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 {(() => {\n const logoContent = normalizedDetails.logoSrc ? (\n <img\n src={normalizedDetails.logoSrc}\n alt={normalizedDetails.logoAlt ?? normalizedDetails.name}\n className=\"brand-shell-footer__logo\"\n />\n ) : null;\n\n if (normalizedDetails.homeHref) {\n return (\n <LinkEl\n href={normalizedDetails.homeHref}\n className=\"brand-shell-footer__name\"\n aria-label={`${normalizedDetails.name} home`}\n target=\"_self\"\n >\n {logoContent ?? normalizedDetails.name}\n </LinkEl>\n );\n }\n\n return logoContent\n ? logoContent\n : <p className=\"brand-shell-footer__name\">{normalizedDetails.name}</p>;\n })()}\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 { E as BrandTheme, w as BrandDetails } from "./validation-Cic4hPME.mjs";
1
+ import { E as BrandTheme, w as BrandDetails } from "./validation-D1q4yKZZ.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-CtH2UkVv.mjs";
1
+ import { l as shouldValidateInDev, n as assertValidBrandDetails, r as assertValidBrandTheme } from "./validation-BhhgC3n1.mjs";
2
2
  import { applyBrandShellProps, registerBrandShellElements } from "./web.mjs";
3
3
 
4
4
  //#region src/svelte/index.ts
@@ -60,6 +60,7 @@ function normalizeSafeHref(href) {
60
60
  const schemeMatch = trimmed.match(ABSOLUTE_SCHEME_PATTERN);
61
61
  if (!schemeMatch) return trimmed;
62
62
  const protocol = `${schemeMatch[1]?.toLowerCase()}:`;
63
+ if (protocol === "data:") return trimmed.startsWith("data:image/") ? trimmed : void 0;
63
64
  if (!ALLOWED_PROTOCOLS.has(protocol)) return;
64
65
  return trimmed;
65
66
  }
@@ -103,7 +104,9 @@ function normalizeBrandDetails(details) {
103
104
  navLinks: normalizeNavLinks(details.navLinks),
104
105
  primaryAction: normalizedPrimaryAction,
105
106
  secondaryAction: normalizedSecondaryAction,
106
- customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks)
107
+ customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks),
108
+ logoSrc: normalizeSafeHref(details.logoSrc),
109
+ logoAlt: details.logoAlt
107
110
  };
108
111
  }
109
112
  function normalizeCtaLinks(primaryAction, secondaryAction) {
@@ -205,6 +208,7 @@ function themeToCssVariables(theme) {
205
208
  if (theme.headerHeight != null) style[`--${THEME_VAR_PREFIX}-header-height`] = theme.headerHeight;
206
209
  if (theme.footerPadding != null) style[`--${THEME_VAR_PREFIX}-footer-padding`] = theme.footerPadding;
207
210
  if (theme.secondaryButtonBg != null) style[`--${THEME_VAR_PREFIX}-button-secondary`] = theme.secondaryButtonBg;
211
+ if (theme.logoHeight != null) style[`--${THEME_VAR_PREFIX}-logo-height`] = theme.logoHeight;
208
212
  return style;
209
213
  }
210
214
  function getAccessibleTextColor(backgroundColor) {
@@ -292,7 +296,8 @@ const THEME_KEYS = new Set([
292
296
  "borderRadius",
293
297
  "headerHeight",
294
298
  "footerPadding",
295
- "secondaryButtonBg"
299
+ "secondaryButtonBg",
300
+ "logoHeight"
296
301
  ]);
297
302
  var BrandShellValidationError = class extends Error {
298
303
  constructor(context, errors) {
@@ -328,6 +333,9 @@ function validateBrandDetails(details) {
328
333
  validateOptionalString(details.discord, "details.discord", errors);
329
334
  validateSafeHref(details.discord, "details.discord", errors);
330
335
  validateOptionalString(details.tagline, "details.tagline", errors);
336
+ validateOptionalString(details.logoSrc, "details.logoSrc", errors);
337
+ validateSafeHref(details.logoSrc, "details.logoSrc", errors);
338
+ validateOptionalString(details.logoAlt, "details.logoAlt", errors);
331
339
  if (details.navLinks != null) if (!Array.isArray(details.navLinks)) errors.push("details.navLinks must be an array.");
332
340
  else details.navLinks.forEach((link, index) => validateNavLink(link, `details.navLinks[${index}]`, errors));
333
341
  if (details.primaryAction != null) validateAction(details.primaryAction, "details.primaryAction", errors);
@@ -478,4 +486,4 @@ function isRecord(value) {
478
486
 
479
487
  //#endregion
480
488
  export { normalizeBrandTheme as a, themeToCssVariables as c, buildShellViewModelFromNormalized as d, normalizeBrandDetails as f, detailsToSocialLinks as g, normalizeNavLinks as h, formatValidationErrors as i, shouldValidateInDev as l, normalizeEmailHref as m, assertValidBrandDetails as n, validateBrandDetails as o, normalizeCtaLinks as p, assertValidBrandTheme as r, validateBrandTheme as s, BrandShellValidationError as t, buildShellViewModel as u };
481
- //# sourceMappingURL=validation-CtH2UkVv.mjs.map
489
+ //# sourceMappingURL=validation-BhhgC3n1.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-BhhgC3n1.mjs","names":[],"sources":["../src/core/social.ts","../src/core/links.ts","../src/core/shell.ts","../src/core/dev.ts","../src/core/theme.ts","../src/core/validation.ts"],"sourcesContent":["import type { BrandDetails } from \"./types\";\n\nexport type SocialPlatform = \"website\" | \"linkedin\" | \"email\" | \"github\" | \"twitter\" | \"discord\";\n\nexport interface SocialLink {\n platform: SocialPlatform | string;\n href: string;\n label: string;\n iconSvg?: string;\n}\n\nexport function detailsToSocialLinks(details: BrandDetails): SocialLink[] {\n const links: SocialLink[] = [];\n if (details.website) links.push({ platform: \"website\", href: details.website, label: \"Website\" });\n if (details.linkedin) links.push({ platform: \"linkedin\", href: details.linkedin, label: \"LinkedIn\" });\n if (details.email) links.push({ platform: \"email\", href: details.email, label: \"Email\" });\n if (details.github) links.push({ platform: \"github\", href: details.github, label: \"GitHub\" });\n if (details.twitter) links.push({ platform: \"twitter\", href: details.twitter, label: \"Twitter\" });\n if (details.discord) links.push({ platform: \"discord\", href: details.discord, label: \"Discord\" });\n if (details.customSocialLinks) {\n for (const custom of details.customSocialLinks) {\n links.push({ platform: custom.platform, href: custom.href, label: custom.label, iconSvg: custom.iconSvg });\n }\n }\n return links;\n}\n","const ABSOLUTE_SCHEME_PATTERN = /^([a-zA-Z][a-zA-Z\\d+.-]*):/;\nconst UNSAFE_CHAR_PATTERN = /[\\u0000-\\u001f\\u007f]/;\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\"]);\nconst REQUIRED_BLANK_REL_TOKENS = [\"noopener\", \"noreferrer\"];\n\nexport function normalizeSafeHref(href: unknown): string | undefined {\n if (typeof href !== \"string\") return undefined;\n const trimmed = href.trim();\n if (trimmed.length === 0) return undefined;\n if (UNSAFE_CHAR_PATTERN.test(trimmed)) return undefined;\n if (trimmed.startsWith(\"//\")) return undefined;\n\n const schemeMatch = trimmed.match(ABSOLUTE_SCHEME_PATTERN);\n if (!schemeMatch) {\n return trimmed;\n }\n\n const protocol = `${schemeMatch[1]?.toLowerCase()}:`;\n if (protocol === \"data:\") {\n return trimmed.startsWith(\"data:image/\") ? trimmed : undefined;\n }\n if (!ALLOWED_PROTOCOLS.has(protocol)) {\n return undefined;\n }\n\n return trimmed;\n}\n\nexport function isSafeHref(href: unknown): boolean {\n return normalizeSafeHref(href) != null;\n}\n\nexport function normalizeRel(target: \"_blank\" | \"_self\" | \"_parent\" | \"_top\", rel?: string): string | undefined {\n const normalizedRel = typeof rel === \"string\" ? rel.trim() : \"\";\n if (target !== \"_blank\") {\n return normalizedRel.length > 0 ? normalizedRel : undefined;\n }\n\n const tokens = normalizedRel.length > 0 ? normalizedRel.split(/\\s+/).filter(Boolean) : [];\n const tokenSet = new Set(tokens.map((token) => token.toLowerCase()));\n\n for (const requiredToken of REQUIRED_BLANK_REL_TOKENS) {\n if (!tokenSet.has(requiredToken)) {\n tokens.push(requiredToken);\n }\n }\n\n return tokens.join(\" \");\n}\n","import { detailsToSocialLinks, type SocialLink } from \"./social\";\nimport type { BrandAction, BrandDetails, BrandNavLink, CustomSocialLink } from \"./types\";\nimport { normalizeRel, normalizeSafeHref } from \"./links\";\n\nexport type LinkTarget = NonNullable<BrandNavLink[\"target\"]>;\n\nexport interface ShellNavLink extends BrandNavLink {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n}\n\nexport interface ShellActionLink extends BrandAction {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n variant: NonNullable<BrandAction[\"variant\"]>;\n}\n\nexport interface ShellViewModel {\n navLinks: ShellNavLink[];\n ctaLinks: ShellActionLink[];\n socialLinks: SocialLink[];\n}\n\nexport type NormalizedActionLink = Omit<ShellActionLink, \"variant\"> & Pick<BrandAction, \"variant\">;\n\nexport interface NormalizedBrandDetails extends Omit<BrandDetails, \"navLinks\" | \"primaryAction\" | \"secondaryAction\"> {\n navLinks: ShellNavLink[];\n primaryAction?: NormalizedActionLink;\n secondaryAction?: NormalizedActionLink;\n}\n\nexport function normalizeNavLinks(navLinks: BrandNavLink[] = []): ShellNavLink[] {\n return navLinks.flatMap((link) => {\n const href = normalizeSafeHref(link.href);\n if (!href) return [];\n\n const { target, rel } = normalizeLinkTargetRel(link.target, link.rel);\n return {\n ...link,\n href,\n ariaLabel: link.ariaLabel ?? link.label,\n target,\n rel,\n };\n });\n}\n\nexport function normalizeBrandDetails(details: BrandDetails): NormalizedBrandDetails {\n const normalizedPrimaryAction = normalizeAction(details.primaryAction);\n const normalizedSecondaryAction = normalizeAction(details.secondaryAction);\n\n return {\n ...details,\n homeHref: normalizeSafeHref(details.homeHref),\n email: normalizeEmailHref(details.email),\n website: normalizeSafeHref(details.website),\n linkedin: normalizeSafeHref(details.linkedin),\n github: normalizeSafeHref(details.github),\n twitter: normalizeSafeHref(details.twitter),\n discord: normalizeSafeHref(details.discord),\n navLinks: normalizeNavLinks(details.navLinks),\n primaryAction: normalizedPrimaryAction,\n secondaryAction: normalizedSecondaryAction,\n customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks),\n logoSrc: normalizeSafeHref(details.logoSrc),\n logoAlt: details.logoAlt,\n };\n}\n\nexport function normalizeCtaLinks(\n primaryAction?: BrandAction,\n secondaryAction?: BrandAction,\n): ShellActionLink[] {\n const normalizedPrimaryAction = normalizeAction(primaryAction);\n const normalizedSecondaryAction = normalizeAction(secondaryAction);\n const actions = [normalizedSecondaryAction, normalizedPrimaryAction].filter(\n (action): action is NonNullable<typeof action> => Boolean(action),\n );\n\n return actions.map((action, index) => {\n const variant =\n action.variant ?? (action === normalizedPrimaryAction || index === actions.length - 1 ? \"primary\" : \"secondary\");\n\n return {\n ...action,\n variant,\n };\n });\n}\n\nexport function buildShellViewModelFromNormalized(normalized: NormalizedBrandDetails): ShellViewModel {\n return {\n navLinks: normalized.navLinks,\n ctaLinks: normalizeCtaLinks(normalized.primaryAction, normalized.secondaryAction),\n socialLinks: detailsToSocialLinks(normalized),\n };\n}\n\nexport function buildShellViewModel(details: BrandDetails): ShellViewModel {\n return buildShellViewModelFromNormalized(normalizeBrandDetails(details));\n}\n\nexport function normalizeEmailHref(email?: string): string | undefined {\n if (typeof email !== \"string\") return undefined;\n const trimmed = email.trim();\n if (trimmed.length === 0) return undefined;\n if (trimmed.toLowerCase().startsWith(\"mailto:\")) {\n const address = trimmed.slice(7).trim();\n if (address.length === 0) return undefined;\n return normalizeSafeHref(`mailto:${address}`);\n }\n return normalizeSafeHref(`mailto:${trimmed}`);\n}\n\nfunction normalizeCustomSocialLinks(links?: CustomSocialLink[]): CustomSocialLink[] | undefined {\n if (!links || links.length === 0) return undefined;\n const normalized: CustomSocialLink[] = [];\n for (const link of links) {\n const href = normalizeSafeHref(link.href);\n if (!href) continue;\n normalized.push({ ...link, href });\n }\n return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction normalizeAction(action?: BrandAction): NormalizedActionLink | undefined {\n if (!action) return undefined;\n\n const href = normalizeSafeHref(action.href);\n if (!href) return undefined;\n\n const { target, rel } = normalizeLinkTargetRel(action.target, action.rel);\n return {\n ...action,\n href,\n target,\n rel,\n ariaLabel: action.ariaLabel ?? action.label,\n };\n}\n\nexport function normalizeLinkTargetRel(target?: BrandNavLink[\"target\"], rel?: string) {\n const normalizedTarget: LinkTarget = target ?? \"_self\";\n const normalizedRel = normalizeRel(normalizedTarget, rel);\n return {\n target: normalizedTarget,\n rel: normalizedRel,\n };\n}\n","type ImportMetaEnv = {\n DEV?: boolean;\n MODE?: string;\n};\n\ntype ImportMetaLike = {\n env?: ImportMetaEnv;\n};\n\nexport function shouldValidateInDev(): boolean {\n const nodeEnv = typeof process !== \"undefined\" ? process.env?.NODE_ENV : undefined;\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n const { env } = import.meta as unknown as ImportMetaLike;\n if (typeof env?.DEV === \"boolean\") {\n return env.DEV;\n }\n\n return false;\n}\n","import type { BrandTheme } from \"./types\";\n\nconst THEME_VAR_PREFIX = \"brand\";\nconst DARK_TEXT = \"#0f172a\";\nconst LIGHT_TEXT = \"#f8fafc\";\n\nexport type ThemeVariables = Record<string, string>;\n\nexport function themeToCssVariables(theme?: BrandTheme | null): ThemeVariables {\n if (!theme) return {};\n const style: ThemeVariables = {};\n if (theme.primaryColor != null) style[`--${THEME_VAR_PREFIX}-primary`] = theme.primaryColor;\n if (theme.backgroundColor != null) style[`--${THEME_VAR_PREFIX}-bg`] = theme.backgroundColor;\n if (theme.textColor != null) style[`--${THEME_VAR_PREFIX}-text`] = theme.textColor;\n if (theme.fontFamily != null) style[`--${THEME_VAR_PREFIX}-font`] = theme.fontFamily;\n if (theme.linkColor != null) style[`--${THEME_VAR_PREFIX}-link`] = theme.linkColor;\n if (theme.socialIconSize != null) style[`--${THEME_VAR_PREFIX}-social-size`] = theme.socialIconSize;\n if (theme.buttonTextColor != null) {\n style[`--${THEME_VAR_PREFIX}-button-text`] = theme.buttonTextColor;\n } else if (theme.primaryColor != null) {\n const buttonTextColor = getAccessibleTextColor(theme.primaryColor);\n if (buttonTextColor) style[`--${THEME_VAR_PREFIX}-button-text`] = buttonTextColor;\n }\n if (theme.borderRadius != null) style[`--${THEME_VAR_PREFIX}-radius`] = theme.borderRadius;\n if (theme.headerHeight != null) style[`--${THEME_VAR_PREFIX}-header-height`] = theme.headerHeight;\n if (theme.footerPadding != null) style[`--${THEME_VAR_PREFIX}-footer-padding`] = theme.footerPadding;\n if (theme.secondaryButtonBg != null) style[`--${THEME_VAR_PREFIX}-button-secondary`] = theme.secondaryButtonBg;\n if (theme.logoHeight != null) style[`--${THEME_VAR_PREFIX}-logo-height`] = theme.logoHeight;\n return style;\n}\n\nfunction getAccessibleTextColor(backgroundColor: string): string | undefined {\n const rgb = parseColorToRgb(backgroundColor);\n if (!rgb) return undefined;\n\n const contrastWithDark = contrastRatio(rgb, { r: 15, g: 23, b: 42 });\n const contrastWithLight = contrastRatio(rgb, { r: 248, g: 250, b: 252 });\n\n return contrastWithDark > contrastWithLight ? DARK_TEXT : LIGHT_TEXT;\n}\n\n/**\n * Parses a hex (#rgb / #rrggbb) or rgb()/rgba() color string to RGB components.\n * hsl(), oklch(), and other formats are not supported and return undefined.\n */\nfunction parseColorToRgb(color: string): { r: number; g: number; b: number } | undefined {\n const trimmed = color.trim();\n\n const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed);\n if (hexMatch) {\n const value = hexMatch[1];\n if (value.length === 3) {\n return {\n r: Number.parseInt(value[0] + value[0], 16),\n g: Number.parseInt(value[1] + value[1], 16),\n b: Number.parseInt(value[2] + value[2], 16),\n };\n }\n return {\n r: Number.parseInt(value.slice(0, 2), 16),\n g: Number.parseInt(value.slice(2, 4), 16),\n b: Number.parseInt(value.slice(4, 6), 16),\n };\n }\n\n const rgbMatch = /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/.exec(trimmed);\n if (rgbMatch) {\n return { r: +rgbMatch[1], g: +rgbMatch[2], b: +rgbMatch[3] };\n }\n\n return undefined;\n}\n\nfunction contrastRatio(\n first: { r: number; g: number; b: number },\n second: { r: number; g: number; b: number },\n): number {\n const firstLuminance = relativeLuminance(first);\n const secondLuminance = relativeLuminance(second);\n const lighter = Math.max(firstLuminance, secondLuminance);\n const darker = Math.min(firstLuminance, secondLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction relativeLuminance(color: { r: number; g: number; b: number }): number {\n const transform = (channel: number) => {\n const normalized = channel / 255;\n if (normalized <= 0.03928) return normalized / 12.92;\n return ((normalized + 0.055) / 1.055) ** 2.4;\n };\n\n const r = transform(color.r);\n const g = transform(color.g);\n const b = transform(color.b);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n","import { normalizeBrandDetails, normalizeEmailHref, type NormalizedBrandDetails } from \"./shell\";\nimport { normalizeSafeHref } from \"./links\";\nimport type { BrandAction, BrandDetails, BrandNavLink, BrandTheme, CustomSocialLink } from \"./types\";\n\nconst LINK_TARGETS = new Set<NonNullable<BrandNavLink[\"target\"]>>([\"_blank\", \"_self\", \"_parent\", \"_top\"]);\nconst CTA_VARIANTS = new Set<NonNullable<BrandAction[\"variant\"]>>([\"primary\", \"secondary\", \"ghost\"]);\nconst CTA_LAYOUTS = new Set<NonNullable<BrandTheme[\"ctaLayout\"]>>([\"inline\", \"stacked\"]);\nconst THEME_KEYS = new Set<keyof BrandTheme>([\n \"primaryColor\",\n \"backgroundColor\",\n \"textColor\",\n \"fontFamily\",\n \"linkColor\",\n \"socialIconSize\",\n \"buttonTextColor\",\n \"ctaLayout\",\n \"borderRadius\",\n \"headerHeight\",\n \"footerPadding\",\n \"secondaryButtonBg\",\n \"logoHeight\",\n]);\n\ntype ValidationErrorPath = string;\n\nexport interface BrandValidationResult<T> {\n valid: boolean;\n errors: string[];\n normalized: T | null;\n}\n\nexport class BrandShellValidationError extends Error {\n readonly context: string;\n readonly errors: string[];\n\n constructor(context: string, errors: string[]) {\n super(formatValidationErrors(context, errors));\n this.name = \"BrandShellValidationError\";\n this.context = context;\n this.errors = errors;\n }\n}\n\nexport function validateBrandDetails(details: unknown): BrandValidationResult<NormalizedBrandDetails> {\n const errors: string[] = [];\n\n if (!isRecord(details)) {\n errors.push(\"details must be an object.\");\n return { valid: false, errors, normalized: null };\n }\n\n validateRequiredString(details.name, \"details.name\", errors);\n validateOptionalString(details.homeHref, \"details.homeHref\", errors);\n validateSafeHref(details.homeHref, \"details.homeHref\", errors);\n validateOptionalString(details.website, \"details.website\", errors);\n validateSafeHref(details.website, \"details.website\", errors);\n validateOptionalString(details.linkedin, \"details.linkedin\", errors);\n validateSafeHref(details.linkedin, \"details.linkedin\", errors);\n validateOptionalString(details.email, \"details.email\", errors);\n validateEmail(details.email, \"details.email\", errors);\n validateOptionalString(details.github, \"details.github\", errors);\n validateSafeHref(details.github, \"details.github\", errors);\n validateOptionalString(details.twitter, \"details.twitter\", errors);\n validateSafeHref(details.twitter, \"details.twitter\", errors);\n validateOptionalString(details.discord, \"details.discord\", errors);\n validateSafeHref(details.discord, \"details.discord\", errors);\n validateOptionalString(details.tagline, \"details.tagline\", errors);\n validateOptionalString(details.logoSrc, \"details.logoSrc\", errors);\n validateSafeHref(details.logoSrc, \"details.logoSrc\", errors);\n validateOptionalString(details.logoAlt, \"details.logoAlt\", errors);\n\n if (details.navLinks != null) {\n if (!Array.isArray(details.navLinks)) {\n errors.push(\"details.navLinks must be an array.\");\n } else {\n details.navLinks.forEach((link, index) => validateNavLink(link, `details.navLinks[${index}]`, errors));\n }\n }\n\n if (details.primaryAction != null) {\n validateAction(details.primaryAction, \"details.primaryAction\", errors);\n }\n if (details.secondaryAction != null) {\n validateAction(details.secondaryAction, \"details.secondaryAction\", errors);\n }\n\n if (details.customSocialLinks != null) {\n if (!Array.isArray(details.customSocialLinks)) {\n errors.push(\"details.customSocialLinks must be an array.\");\n } else {\n details.customSocialLinks.forEach((link, index) =>\n validateCustomSocialLink(link, `details.customSocialLinks[${index}]`, errors),\n );\n }\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandDetails(details as unknown as BrandDetails),\n };\n}\n\nexport function validateBrandTheme(theme: unknown): BrandValidationResult<BrandTheme | null> {\n if (theme == null) {\n return {\n valid: true,\n errors: [],\n normalized: null,\n };\n }\n\n const errors: string[] = [];\n\n if (!isRecord(theme)) {\n errors.push(\"theme must be an object when provided.\");\n return { valid: false, errors, normalized: null };\n }\n\n for (const key of Object.keys(theme)) {\n if (!THEME_KEYS.has(key as keyof BrandTheme)) {\n errors.push(`theme.${key} is not a supported theme key.`);\n continue;\n }\n if (key === \"ctaLayout\") {\n validateCtaLayout(theme[key], \"theme.ctaLayout\", errors);\n continue;\n }\n validateOptionalString(theme[key], `theme.${key}`, errors);\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandTheme(theme as BrandTheme),\n };\n}\n\nexport function assertValidBrandDetails(details: unknown, context = \"BrandDetails\"): asserts details is BrandDetails {\n const result = validateBrandDetails(details);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function assertValidBrandTheme(theme: unknown, context = \"BrandTheme\"): asserts theme is BrandTheme | null | undefined {\n const result = validateBrandTheme(theme);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function normalizeBrandTheme(theme?: BrandTheme | null): BrandTheme | null {\n if (!theme) return null;\n\n const normalized: BrandTheme = {};\n for (const key of THEME_KEYS) {\n const value = theme[key];\n if (key === \"ctaLayout\") {\n if (typeof value === \"string\" && CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n normalized[key] = value as BrandTheme[\"ctaLayout\"];\n }\n continue;\n }\n\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length > 0) {\n normalized[key] = trimmed;\n }\n }\n }\n\n return Object.keys(normalized).length > 0 ? normalized : null;\n}\n\nexport function formatValidationErrors(context: string, errors: string[]): string {\n return `${context} validation failed:\\n${errors.map((error) => `- ${error}`).join(\"\\n\")}`;\n}\n\nfunction validateNavLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n validateOptionalString(link.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(link.rel, `${path}.rel`, errors);\n validateTarget(link.target, `${path}.target`, errors);\n}\n\nfunction validateAction(action: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(action)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(action.label, `${path}.label`, errors);\n validateRequiredString(action.href, `${path}.href`, errors);\n validateSafeHref(action.href, `${path}.href`, errors);\n validateOptionalString(action.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(action.rel, `${path}.rel`, errors);\n validateTarget(action.target, `${path}.target`, errors);\n\n if (action.variant != null) {\n if (typeof action.variant !== \"string\" || !CTA_VARIANTS.has(action.variant as NonNullable<BrandAction[\"variant\"]>)) {\n errors.push(`${path}.variant must be one of: primary, secondary, ghost.`);\n }\n }\n}\n\nfunction validateCustomSocialLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n if (typeof link.platform !== \"string\" || link.platform.trim().length === 0) {\n errors.push(`${path}.platform must be a non-empty string.`);\n }\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n if (link.iconSvg != null) {\n validateOptionalString(link.iconSvg, `${path}.iconSvg`, errors);\n }\n}\n\nfunction validateTarget(target: unknown, path: ValidationErrorPath, errors: string[]) {\n if (target == null) return;\n if (typeof target !== \"string\" || !LINK_TARGETS.has(target as NonNullable<BrandNavLink[\"target\"]>)) {\n errors.push(`${path} must be one of: _blank, _self, _parent, _top.`);\n }\n}\n\nfunction validateCtaLayout(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || !CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n errors.push(`${path} must be one of: inline, stacked.`);\n }\n}\n\nfunction validateRequiredString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string.`);\n }\n}\n\nfunction validateOptionalString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string when provided.`);\n }\n}\n\nfunction validateSafeHref(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeSafeHref(value)) {\n errors.push(`${path} must use a safe URL/path (http, https, mailto, tel, or relative path).`);\n }\n}\n\nfunction validateEmail(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeEmailHref(value)) {\n errors.push(`${path} must be a valid email or mailto URL.`);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":"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"}
@@ -69,6 +69,10 @@ interface BrandDetails {
69
69
  tagline?: string;
70
70
  /** Additional social links for custom platforms (Bluesky, Mastodon, YouTube, etc.) */
71
71
  customSocialLinks?: CustomSocialLink[];
72
+ /** URL to logo image shown in header/footer. Replaces the text name visually. */
73
+ logoSrc?: string;
74
+ /** Alt text for the logo. Defaults to `name` if omitted. */
75
+ logoAlt?: string;
72
76
  }
73
77
  /**
74
78
  * Optional theme to adapt branding without custom CSS.
@@ -99,6 +103,8 @@ interface BrandTheme {
99
103
  footerPadding?: string;
100
104
  /** Secondary button background color → --brand-button-secondary */
101
105
  secondaryButtonBg?: string;
106
+ /** Logo image height (e.g. "2rem", "32px") → --brand-logo-height */
107
+ logoHeight?: string;
102
108
  }
103
109
  //#endregion
104
110
  //#region src/core/social.d.ts
@@ -161,4 +167,4 @@ declare function normalizeBrandTheme(theme?: BrandTheme | null): BrandTheme | nu
161
167
  declare function formatValidationErrors(context: string, errors: string[]): string;
162
168
  //#endregion
163
169
  export { BrandAction as C, CustomSocialLink as D, BrandTheme as E, detailsToSocialLinks as S, BrandNavLink as T, normalizeCtaLinks as _, formatValidationErrors as a, SocialLink as b, validateBrandTheme as c, ShellActionLink as d, ShellNavLink as f, normalizeBrandDetails as g, buildShellViewModelFromNormalized as h, assertValidBrandTheme as i, LinkTarget as l, buildShellViewModel as m, BrandValidationResult as n, normalizeBrandTheme as o, ShellViewModel as p, assertValidBrandDetails as r, validateBrandDetails as s, BrandShellValidationError as t, NormalizedBrandDetails as u, normalizeEmailHref as v, BrandDetails as w, SocialPlatform as x, normalizeNavLinks as y };
164
- //# sourceMappingURL=validation-Cic4hPME.d.mts.map
170
+ //# sourceMappingURL=validation-D1q4yKZZ.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-D1q4yKZZ.d.mts","names":[],"sources":["../src/core/types.ts","../src/core/social.ts","../src/core/shell.ts","../src/core/validation.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;EAYA;EAVA,OAAA;EAYO;EAVP,OAAA;EAiBe;EAff,OAAA;;EAEA,OAAA;EAeA;EAbA,iBAAA,GAAoB,gBAAA;EAiBpB;EAfA,OAAA;EAmBA;EAjBA,OAAA;AAAA;;;;;UAOe,UAAA;EA0Bf;EAxBA,YAAA;EAwBU;EAtBV,eAAA;;EAEA,SAAA;ECvFU;EDyFV,UAAA;;EAEA,SAAA;EC3FwB;ED6FxB,cAAA;EC3FyB;ED6FzB,eAAA;EC5FwB;ED8FxB,SAAA;EC9FU;EDgGV,YAAA;EC9FA;EDgGA,YAAA;EC/FO;EDiGP,aAAA;EC9Fc;EDgGd,iBAAA;;EAEA,UAAA;AAAA;;;KC3GU,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,iBAsB9C,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;;;UC/ElB,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,iBAgE9D,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/vue.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { E as BrandTheme, c as validateBrandTheme, s as validateBrandDetails, w as BrandDetails } from "./validation-Cic4hPME.mjs";
1
+ import { E as BrandTheme, c as validateBrandTheme, s as validateBrandDetails, w as BrandDetails } from "./validation-D1q4yKZZ.mjs";
2
2
  import { LinkFactoryOptions } from "./web.mjs";
3
3
  import * as vue from "vue";
4
4
  import { PropType } from "vue";
package/dist/vue.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { l as shouldValidateInDev, n as assertValidBrandDetails, o as validateBrandDetails, r as assertValidBrandTheme, s as validateBrandTheme } from "./validation-CtH2UkVv.mjs";
1
+ import { l as shouldValidateInDev, n as assertValidBrandDetails, o as validateBrandDetails, r as assertValidBrandTheme, s as validateBrandTheme } from "./validation-BhhgC3n1.mjs";
2
2
  import { applyBrandShellProps, registerBrandShellElements } from "./web.mjs";
3
3
  import { defineComponent, h, onMounted, ref, watch } from "vue";
4
4
 
package/dist/web.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as BrandAction, E as BrandTheme, T as BrandNavLink, c as validateBrandTheme, s as validateBrandDetails, w as BrandDetails } from "./validation-Cic4hPME.mjs";
1
+ import { C as BrandAction, E as BrandTheme, T as BrandNavLink, c as validateBrandTheme, s as validateBrandDetails, w as BrandDetails } from "./validation-D1q4yKZZ.mjs";
2
2
 
3
3
  //#region src/web/index.d.ts
4
4
  declare const HTMLElementBase: typeof HTMLElement;
@@ -1 +1 @@
1
- {"version":3,"file":"web.d.mts","names":[],"sources":["../src/web/index.ts"],"mappings":";;;cAyBM,eAAA,SAAwB,WAAA;AAAA,UAGb,iCAAA;EACf,aAAA;EACA,aAAA;AAAA;AAAA,UAGe,kBAAA;EACf,IAAA;EACA,SAAA;EACA,SAAA;EACA,MAAA;EACA,GAAA;AAAA;AAAA,UAGe,sBAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,KAGrC,qBAAA,GAAwB,WAAA;EAClC,OAAA,GAAU,YAAA;EACV,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,KAAgB,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,UAGjC,8BAAA;EACf,OAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,uBASa,qBAAA,SAA8B,eAAA;EAAA,WAChC,kBAAA,CAAA;EAAA,QAIH,QAAA;EAAA,QACA,MAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EAAA,IAEJ,OAAA,CAAA,GAAW,YAAA;EAAA,IAIX,OAAA,CAAQ,KAAA,EAAO,YAAA;EAAA,IAKf,KAAA,CAAA,GAAS,UAAA;EAAA,IAIT,KAAA,CAAM,KAAA,EAAO,UAAA;EAAA,IAKb,UAAA,CAAA;EAAA,IAIA,UAAA,CAAW,KAAA;EAAA,IAKX,WAAA,CAAA,KAAiB,OAAA,EAAS,kBAAA,KAAuB,iBAAA;EAAA,IAIjD,WAAA,CAAY,KAAA,IAAS,OAAA,EAAS,kBAAA,KAAuB,iBAAA;EAKzD,iBAAA,CAAA;EAeA,wBAAA,CAAyB,IAAA,UAAc,SAAA,iBAA0B,QAAA;EAAA,mBAW9C,KAAA,CACjB,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;EAAA,QAEK,MAAA;EAAA,QA2BA,eAAA;AAAA;AAAA,cASG,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CACR,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;AAAA;AAAA,cAKQ,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CACR,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;AAAA;AAAA,iBAKW,0BAAA,CAA2B,OAAA,GAAS,iCAAA;;;;iBAsBpC,oBAAA,CACd,OAAA,EAAS,qBAAA,qBACT,KAAA,EAAO,sBAAA;AAAA,iBAaO,6BAAA,CAA8B,KAAA,EAAO,sBAAA,GAAyB,8BAAA;AAAA,QAwXtE,MAAA;EAAA,UACI,qBAAA;IACR,cAAA,EAAgB,kBAAA;IAChB,cAAA,EAAgB,kBAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"web.d.mts","names":[],"sources":["../src/web/index.ts"],"mappings":";;;cAyBM,eAAA,SAAwB,WAAA;AAAA,UAGb,iCAAA;EACf,aAAA;EACA,aAAA;AAAA;AAAA,UAGe,kBAAA;EACf,IAAA;EACA,SAAA;EACA,SAAA;EACA,MAAA;EACA,GAAA;AAAA;AAAA,UAGe,sBAAA;EACf,OAAA,EAAS,YAAA;EACT,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,KAGrC,qBAAA,GAAwB,WAAA;EAClC,OAAA,GAAU,YAAA;EACV,KAAA,GAAQ,UAAA;EACR,UAAA;EACA,WAAA,KAAgB,OAAA,EAAS,kBAAA,KAAuB,iBAAA;AAAA;AAAA,UAGjC,8BAAA;EACf,OAAA;EACA,KAAA;EACA,aAAA;AAAA;AAAA,uBASa,qBAAA,SAA8B,eAAA;EAAA,WAChC,kBAAA,CAAA;EAAA,QAIH,QAAA;EAAA,QACA,MAAA;EAAA,QACA,WAAA;EAAA,QACA,YAAA;EAAA,IAEJ,OAAA,CAAA,GAAW,YAAA;EAAA,IAIX,OAAA,CAAQ,KAAA,EAAO,YAAA;EAAA,IAKf,KAAA,CAAA,GAAS,UAAA;EAAA,IAIT,KAAA,CAAM,KAAA,EAAO,UAAA;EAAA,IAKb,UAAA,CAAA;EAAA,IAIA,UAAA,CAAW,KAAA;EAAA,IAKX,WAAA,CAAA,KAAiB,OAAA,EAAS,kBAAA,KAAuB,iBAAA;EAAA,IAIjD,WAAA,CAAY,KAAA,IAAS,OAAA,EAAS,kBAAA,KAAuB,iBAAA;EAKzD,iBAAA,CAAA;EAeA,wBAAA,CAAyB,IAAA,UAAc,SAAA,iBAA0B,QAAA;EAAA,mBAW9C,KAAA,CACjB,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;EAAA,QAEK,MAAA;EAAA,QA2BA,eAAA;AAAA;AAAA,cASG,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CACR,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;AAAA;AAAA,cAKQ,kBAAA,SAA2B,qBAAA;EAAA,UAC5B,KAAA,CACR,OAAA,EAAS,YAAA,EACT,KAAA,EAAO,UAAA,SACP,UAAA,iBACA,WAAA,IAAe,OAAA,EAAS,kBAAA,KAAuB,iBAAA,WAC9C,WAAA;AAAA;AAAA,iBAKW,0BAAA,CAA2B,OAAA,GAAS,iCAAA;;;;iBAsBpC,oBAAA,CACd,OAAA,EAAS,qBAAA,qBACT,KAAA,EAAO,sBAAA;AAAA,iBAaO,6BAAA,CAA8B,KAAA,EAAO,sBAAA,GAAyB,8BAAA;AAAA,QAiZtE,MAAA;EAAA,UACI,qBAAA;IACR,cAAA,EAAgB,kBAAA;IAChB,cAAA,EAAgB,kBAAA;EAAA;AAAA"}
package/dist/web.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as normalizeBrandTheme, c as themeToCssVariables, l as shouldValidateInDev, n as assertValidBrandDetails, o as validateBrandDetails, r as assertValidBrandTheme, s as validateBrandTheme, t as BrandShellValidationError, u as buildShellViewModel } from "./validation-CtH2UkVv.mjs";
1
+ import { a as normalizeBrandTheme, c as themeToCssVariables, l as shouldValidateInDev, n as assertValidBrandDetails, o as validateBrandDetails, r as assertValidBrandTheme, s as validateBrandTheme, t as BrandShellValidationError, u as buildShellViewModel } from "./validation-BhhgC3n1.mjs";
2
2
 
3
3
  //#region src/web/index.ts
4
4
  const SVG_NS = "http://www.w3.org/2000/svg";
@@ -170,6 +170,14 @@ function createHeader(details, theme, shellClass, linkFactory) {
170
170
  const inner = document.createElement("div");
171
171
  inner.className = "brand-shell-header__inner";
172
172
  const identity = details.homeHref ? createAnchor(details.homeHref, "brand-shell-header__name", details.name, `${details.name} home`, "_self", void 0, linkFactory) : createSpan("brand-shell-header__name", details.name);
173
+ if (details.logoSrc) {
174
+ const img = document.createElement("img");
175
+ img.src = details.logoSrc;
176
+ img.alt = details.logoAlt ?? details.name;
177
+ img.className = "brand-shell-header__logo";
178
+ identity.textContent = "";
179
+ identity.append(img);
180
+ }
173
181
  inner.append(identity);
174
182
  const actions = document.createElement("div");
175
183
  actions.className = "brand-shell-header__actions";
@@ -193,7 +201,19 @@ function createFooter(details, theme, shellClass, linkFactory) {
193
201
  top.className = "brand-shell-footer__top";
194
202
  const brand = document.createElement("div");
195
203
  brand.className = "brand-shell-footer__brand";
196
- brand.append(createParagraph("brand-shell-footer__name", details.name));
204
+ if (details.logoSrc) {
205
+ const img = document.createElement("img");
206
+ img.src = details.logoSrc;
207
+ img.alt = details.logoAlt ?? details.name;
208
+ img.className = "brand-shell-footer__logo";
209
+ if (details.homeHref) {
210
+ const identity = createAnchor(details.homeHref, "brand-shell-footer__name", details.name, `${details.name} home`, "_self", void 0, linkFactory);
211
+ identity.textContent = "";
212
+ identity.append(img);
213
+ brand.append(identity);
214
+ } else brand.append(img);
215
+ } else if (details.homeHref) brand.append(createAnchor(details.homeHref, "brand-shell-footer__name", details.name, `${details.name} home`, "_self", void 0, linkFactory));
216
+ else brand.append(createParagraph("brand-shell-footer__name", details.name));
197
217
  if (details.tagline) brand.append(createParagraph("brand-shell-footer__tagline", details.tagline));
198
218
  top.append(brand);
199
219
  const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);
package/dist/web.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"web.mjs","names":[],"sources":["../src/web/index.ts"],"sourcesContent":["import { buildShellViewModel, themeToCssVariables } from \"../core\";\nimport type {\n BrandDetails,\n BrandTheme,\n NormalizedBrandDetails,\n ShellActionLink,\n ShellNavLink,\n SocialLink,\n SocialPlatform,\n} from \"../core\";\nimport {\n BrandShellValidationError,\n assertValidBrandDetails,\n assertValidBrandTheme,\n normalizeBrandTheme,\n shouldValidateInDev,\n validateBrandDetails,\n validateBrandTheme,\n} from \"../core\";\n\nexport type { BrandAction, BrandDetails, BrandNavLink, BrandTheme } from \"../core\";\nexport { validateBrandDetails, validateBrandTheme } from \"../core\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\nconst HTMLElementBase: typeof HTMLElement =\n typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\n\nexport interface RegisterBrandShellElementsOptions {\n headerTagName?: string;\n footerTagName?: string;\n}\n\nexport interface LinkFactoryOptions {\n href: string;\n className: string;\n ariaLabel: string;\n target: string;\n rel?: string;\n}\n\nexport interface BrandShellElementProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement;\n}\n\nexport type BrandShellElementLike = HTMLElement & {\n details?: BrandDetails | null;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n linkFactory?: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;\n};\n\nexport interface SerializedBrandShellAttributes {\n details: string;\n theme?: string;\n \"shell-class\"?: string;\n}\n\ninterface ParsedAttributes {\n details: BrandDetails | null;\n theme: BrandTheme | null;\n shellClass: string | null;\n}\n\nabstract class BaseBrandShellElement extends HTMLElementBase {\n static get observedAttributes() {\n return [\"details\", \"theme\", \"shell-class\"];\n }\n\n private _details: BrandDetails | null = null;\n private _theme: BrandTheme | null = null;\n private _shellClass: string | null = null;\n private _linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null = null;\n\n get details(): BrandDetails | null {\n return this._details;\n }\n\n set details(value: BrandDetails | null) {\n this._details = value;\n this.render();\n }\n\n get theme(): BrandTheme | null {\n return this._theme;\n }\n\n set theme(value: BrandTheme | null) {\n this._theme = value;\n this.render();\n }\n\n get shellClass(): string | null {\n return this._shellClass;\n }\n\n set shellClass(value: string | null) {\n this._shellClass = value;\n this.render();\n }\n\n get linkFactory(): ((options: LinkFactoryOptions) => HTMLAnchorElement) | null {\n return this._linkFactory;\n }\n\n set linkFactory(value: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null) {\n this._linkFactory = value;\n this.render();\n }\n\n connectedCallback() {\n this.upgradeProperty(\"details\");\n this.upgradeProperty(\"theme\");\n this.upgradeProperty(\"shellClass\");\n this.upgradeProperty(\"linkFactory\");\n\n const attrs = parseAttributes(this);\n if (this._details == null) this._details = attrs.details;\n if (this._theme == null) this._theme = attrs.theme;\n if (this._shellClass == null) this._shellClass = attrs.shellClass;\n\n if (this.style.display !== \"block\") this.style.display = \"block\";\n this.render();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {\n if (name === \"details\") {\n this._details = parseJsonAttribute<BrandDetails>(newValue);\n } else if (name === \"theme\") {\n this._theme = parseJsonAttribute<BrandTheme>(newValue);\n } else if (name === \"shell-class\") {\n this._shellClass = normalizeClassName(newValue);\n }\n this.render();\n }\n\n protected abstract build(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null,\n ): HTMLElement;\n\n private render() {\n if (this._details == null) {\n this.replaceChildren();\n return;\n }\n\n const detailsResult = validateBrandDetails(this._details);\n if (!detailsResult.valid) {\n if (shouldValidateInDev()) {\n throw new BrandShellValidationError(\"brand-shell/web details\", detailsResult.errors);\n }\n this.replaceChildren();\n return;\n }\n\n const themeResult = validateBrandTheme(this._theme);\n if (!themeResult.valid && shouldValidateInDev()) {\n throw new BrandShellValidationError(\"brand-shell/web theme\", themeResult.errors);\n }\n\n const normalizedDetails = detailsResult.normalized as NormalizedBrandDetails;\n const normalizedTheme = themeResult.normalized ?? null;\n\n const element = this.build(normalizedDetails, normalizedTheme, this._shellClass, this._linkFactory);\n this.replaceChildren(element);\n }\n\n private upgradeProperty(propertyName: \"details\" | \"theme\" | \"shellClass\" | \"linkFactory\") {\n if (Object.prototype.hasOwnProperty.call(this, propertyName)) {\n const value = (this as unknown as Record<string, unknown>)[propertyName];\n delete (this as unknown as Record<string, unknown>)[propertyName];\n (this as unknown as Record<string, unknown>)[propertyName] = value;\n }\n }\n}\n\nexport class BrandHeaderElement extends BaseBrandShellElement {\n protected build(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null,\n ): HTMLElement {\n return createHeader(details, theme, shellClass, linkFactory ?? undefined);\n }\n}\n\nexport class BrandFooterElement extends BaseBrandShellElement {\n protected build(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null,\n ): HTMLElement {\n return createFooter(details, theme, shellClass, linkFactory ?? undefined);\n }\n}\n\nexport function registerBrandShellElements(options: RegisterBrandShellElementsOptions = {}) {\n if (typeof customElements === \"undefined\") {\n throw new Error(\"Custom elements are not available in this environment.\");\n }\n\n const headerTagName = options.headerTagName ?? \"brand-header\";\n const footerTagName = options.footerTagName ?? \"brand-footer\";\n\n if (!customElements.get(headerTagName)) {\n const HeaderConstructor =\n headerTagName === \"brand-header\" ? BrandHeaderElement : class extends BrandHeaderElement {};\n customElements.define(headerTagName, HeaderConstructor);\n }\n if (!customElements.get(footerTagName)) {\n const FooterConstructor =\n footerTagName === \"brand-footer\" ? BrandFooterElement : class extends BrandFooterElement {};\n customElements.define(footerTagName, FooterConstructor);\n }\n\n return { headerTagName, footerTagName };\n}\n\nexport function applyBrandShellProps(\n element: BrandShellElementLike | null | undefined,\n props: BrandShellElementProps,\n) {\n if (!element) return;\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, \"brand-shell/web details\");\n assertValidBrandTheme(props.theme, \"brand-shell/web theme\");\n }\n element.details = validateBrandDetails(props.details).normalized ?? props.details;\n element.theme = normalizeBrandTheme(props.theme ?? null);\n element.shellClass = props.shellClass ?? null;\n element.linkFactory = props.linkFactory ?? null;\n}\n\nexport function serializeBrandShellAttributes(props: BrandShellElementProps): SerializedBrandShellAttributes {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, \"brand-shell/web serialize details\");\n assertValidBrandTheme(props.theme, \"brand-shell/web serialize theme\");\n }\n\n const normalizedDetails = validateBrandDetails(props.details).normalized ?? props.details;\n const normalizedTheme = normalizeBrandTheme(props.theme ?? null);\n\n const attributes: SerializedBrandShellAttributes = {\n details: JSON.stringify(normalizedDetails),\n };\n\n if (normalizedTheme) {\n attributes.theme = JSON.stringify(normalizedTheme);\n }\n\n const shellClass = normalizeClassName(props.shellClass ?? null);\n if (shellClass) {\n attributes[\"shell-class\"] = shellClass;\n }\n\n return attributes;\n}\n\nfunction parseAttributes(element: Element): ParsedAttributes {\n return {\n details: parseJsonAttribute<BrandDetails>(element.getAttribute(\"details\")),\n theme: parseJsonAttribute<BrandTheme>(element.getAttribute(\"theme\")),\n shellClass: normalizeClassName(element.getAttribute(\"shell-class\")),\n };\n}\n\nfunction parseJsonAttribute<T>(value: string | null): T | null {\n if (!value) return null;\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n}\n\nfunction normalizeClassName(value: string | null): string | null {\n if (!value) return null;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : null;\n}\n\nfunction createHeader(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const header = document.createElement(\"header\");\n header.className = joinClassNames(\"brand-shell-header\", shellClass);\n header.setAttribute(\"role\", \"banner\");\n applyThemeVariables(header, theme);\n header.dataset.brandCtaLayout = resolveCtaLayout(theme);\n\n const inner = document.createElement(\"div\");\n inner.className = \"brand-shell-header__inner\";\n\n const identity = details.homeHref\n ? createAnchor(details.homeHref, \"brand-shell-header__name\", details.name, `${details.name} home`, \"_self\", undefined, linkFactory)\n : createSpan(\"brand-shell-header__name\", details.name);\n inner.append(identity);\n\n const actions = document.createElement(\"div\");\n actions.className = \"brand-shell-header__actions\";\n\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);\n\n if (navLinks.length > 0) {\n actions.append(createNav(navLinks, \"brand-shell-header\", \"Primary\", linkFactory));\n }\n\n if (ctaLinks.length > 0) {\n actions.append(createCtas(ctaLinks, \"brand-shell-header__ctas\", linkFactory));\n }\n\n if (socialLinks.length > 0) {\n actions.append(createSocialLinks(socialLinks, \"brand-shell-header__social\", \"brand-shell-header__social-link\"));\n }\n\n inner.append(actions);\n header.append(inner);\n return header;\n}\n\nfunction createFooter(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const footer = document.createElement(\"footer\");\n footer.className = joinClassNames(\"brand-shell-footer\", shellClass);\n footer.setAttribute(\"role\", \"contentinfo\");\n applyThemeVariables(footer, theme);\n footer.dataset.brandCtaLayout = resolveCtaLayout(theme);\n\n const inner = document.createElement(\"div\");\n inner.className = \"brand-shell-footer__inner\";\n\n const top = document.createElement(\"div\");\n top.className = \"brand-shell-footer__top\";\n\n const brand = document.createElement(\"div\");\n brand.className = \"brand-shell-footer__brand\";\n brand.append(createParagraph(\"brand-shell-footer__name\", details.name));\n if (details.tagline) {\n brand.append(createParagraph(\"brand-shell-footer__tagline\", details.tagline));\n }\n top.append(brand);\n\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);\n\n if (navLinks.length > 0) {\n top.append(createNav(navLinks, \"brand-shell-footer\", \"Footer\", linkFactory));\n }\n\n if (ctaLinks.length > 0) {\n top.append(createCtas(ctaLinks, \"brand-shell-footer__ctas\", linkFactory));\n }\n\n if (socialLinks.length > 0) {\n top.append(createSocialLinks(socialLinks, \"brand-shell-footer__social\", \"brand-shell-footer__social-link\"));\n }\n\n const copy = createParagraph(\"brand-shell-footer__copy\", `© ${new Date().getFullYear()} ${details.name}`);\n\n inner.append(top, copy);\n footer.append(inner);\n return footer;\n}\n\nfunction createNav(\n links: ShellNavLink[],\n blockClass: \"brand-shell-header\" | \"brand-shell-footer\",\n ariaLabel: string,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const nav = document.createElement(\"nav\");\n nav.className = `${blockClass}__nav`;\n nav.setAttribute(\"aria-label\", ariaLabel);\n\n const list = document.createElement(\"ul\");\n list.className = `${blockClass}__list`;\n\n for (const link of links) {\n const item = document.createElement(\"li\");\n const anchor = createAnchor(link.href, `${blockClass}__link`, link.label, link.ariaLabel, link.target, link.rel, linkFactory);\n item.append(anchor);\n list.append(item);\n }\n\n nav.append(list);\n return nav;\n}\n\nfunction createCtas(\n actions: ShellActionLink[],\n containerClass: string,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = containerClass;\n\n actions.forEach((action) => {\n const anchor = createAnchor(\n action.href,\n joinClassNames(\"brand-shell-button\", `brand-shell-button--${action.variant}`),\n action.label,\n action.ariaLabel,\n action.target,\n action.rel,\n linkFactory,\n );\n container.append(anchor);\n });\n\n return container;\n}\n\nfunction createSocialLinks(\n links: SocialLink[],\n containerClass: string,\n linkClass: string,\n): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = containerClass;\n container.setAttribute(\"aria-label\", \"Social links\");\n\n for (const link of links) {\n const anchor = createAnchor(link.href, linkClass, \"\");\n anchor.setAttribute(\"aria-label\", link.label);\n if (!link.href.startsWith(\"mailto:\")) {\n anchor.target = \"_blank\";\n anchor.rel = \"noopener noreferrer\";\n }\n\n const icon = createSocialIcon(link.platform, link.iconSvg);\n if (icon) {\n anchor.append(icon);\n } else {\n anchor.append(createSpan(\"\", link.label[0] ?? \"?\"));\n }\n\n container.append(anchor);\n }\n\n return container;\n}\n\nfunction createSocialIcon(platform: SocialPlatform | string, iconSvg?: string): Element | null {\n switch (platform) {\n case \"github\":\n return createFilledIcon(\n \"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 );\n case \"twitter\":\n return createFilledIcon(\"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 case \"linkedin\":\n return createFilledIcon(\n \"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 );\n case \"discord\":\n return createFilledIcon(\n \"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 );\n case \"email\":\n return createStrokedIcon([\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"], { rx: \"2\" });\n case \"website\":\n return createGlobeIcon();\n default:\n if (iconSvg) {\n const span = document.createElement(\"span\");\n span.setAttribute(\"aria-hidden\", \"true\");\n span.innerHTML = iconSvg;\n return span;\n }\n return null;\n }\n}\n\nfunction createFilledIcon(pathData: string): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"currentColor\");\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", pathData);\n svg.append(path);\n return svg;\n}\n\nfunction createStrokedIcon(pathData: string[], rectAttributes?: Record<string, string>): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n\n if (rectAttributes) {\n const rect = document.createElementNS(SVG_NS, \"rect\");\n rect.setAttribute(\"width\", \"20\");\n rect.setAttribute(\"height\", \"16\");\n rect.setAttribute(\"x\", \"2\");\n rect.setAttribute(\"y\", \"4\");\n for (const [attribute, value] of Object.entries(rectAttributes)) {\n rect.setAttribute(attribute, value);\n }\n svg.append(rect);\n }\n\n for (const d of pathData) {\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", d);\n svg.append(path);\n }\n\n return svg;\n}\n\nfunction createGlobeIcon(): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n\n const circle = document.createElementNS(SVG_NS, \"circle\");\n circle.setAttribute(\"cx\", \"12\");\n circle.setAttribute(\"cy\", \"12\");\n circle.setAttribute(\"r\", \"10\");\n svg.append(circle);\n\n for (const d of [\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\", \"M2 12h20\"]) {\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", d);\n svg.append(path);\n }\n\n return svg;\n}\n\nfunction createBaseSvg(): SVGSVGElement {\n const svg = document.createElementNS(SVG_NS, \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"width\", \"1em\");\n svg.setAttribute(\"height\", \"1em\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n return svg;\n}\n\nfunction applyThemeVariables(element: HTMLElement, theme: BrandTheme | null) {\n const style = themeToCssVariables(theme);\n for (const [name, value] of Object.entries(style)) {\n element.style.setProperty(name, value);\n }\n}\n\nfunction resolveCtaLayout(theme: BrandTheme | null): \"inline\" | \"stacked\" {\n return theme?.ctaLayout === \"stacked\" ? \"stacked\" : \"inline\";\n}\n\nfunction joinClassNames(...classNames: Array<string | null | undefined>) {\n return classNames.filter(Boolean).join(\" \");\n}\n\nfunction createAnchor(\n href: string,\n className: string,\n text: string,\n ariaLabel?: string,\n target?: string,\n rel?: string,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLAnchorElement {\n if (linkFactory) {\n const anchor = linkFactory({\n href,\n className,\n ariaLabel: ariaLabel ?? text,\n target: target ?? \"_self\",\n rel,\n });\n anchor.textContent = text;\n return anchor;\n }\n\n const anchor = document.createElement(\"a\");\n anchor.href = href;\n anchor.className = className;\n anchor.textContent = text;\n if (ariaLabel) anchor.setAttribute(\"aria-label\", ariaLabel);\n if (target) anchor.target = target;\n if (rel) anchor.rel = rel;\n return anchor;\n}\n\nfunction createParagraph(className: string, text: string): HTMLParagraphElement {\n const paragraph = document.createElement(\"p\");\n paragraph.className = className;\n paragraph.textContent = text;\n return paragraph;\n}\n\nfunction createSpan(className: string, text: string): HTMLSpanElement {\n const span = document.createElement(\"span\");\n span.className = className;\n span.textContent = text;\n return span;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"brand-header\": BrandHeaderElement;\n \"brand-footer\": BrandFooterElement;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"web.mjs","names":[],"sources":["../src/web/index.ts"],"sourcesContent":["import { buildShellViewModel, themeToCssVariables } from \"../core\";\nimport type {\n BrandDetails,\n BrandTheme,\n NormalizedBrandDetails,\n ShellActionLink,\n ShellNavLink,\n SocialLink,\n SocialPlatform,\n} from \"../core\";\nimport {\n BrandShellValidationError,\n assertValidBrandDetails,\n assertValidBrandTheme,\n normalizeBrandTheme,\n shouldValidateInDev,\n validateBrandDetails,\n validateBrandTheme,\n} from \"../core\";\n\nexport type { BrandAction, BrandDetails, BrandNavLink, BrandTheme } from \"../core\";\nexport { validateBrandDetails, validateBrandTheme } from \"../core\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\nconst HTMLElementBase: typeof HTMLElement =\n typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\n\nexport interface RegisterBrandShellElementsOptions {\n headerTagName?: string;\n footerTagName?: string;\n}\n\nexport interface LinkFactoryOptions {\n href: string;\n className: string;\n ariaLabel: string;\n target: string;\n rel?: string;\n}\n\nexport interface BrandShellElementProps {\n details: BrandDetails;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement;\n}\n\nexport type BrandShellElementLike = HTMLElement & {\n details?: BrandDetails | null;\n theme?: BrandTheme | null;\n shellClass?: string | null;\n linkFactory?: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null;\n};\n\nexport interface SerializedBrandShellAttributes {\n details: string;\n theme?: string;\n \"shell-class\"?: string;\n}\n\ninterface ParsedAttributes {\n details: BrandDetails | null;\n theme: BrandTheme | null;\n shellClass: string | null;\n}\n\nabstract class BaseBrandShellElement extends HTMLElementBase {\n static get observedAttributes() {\n return [\"details\", \"theme\", \"shell-class\"];\n }\n\n private _details: BrandDetails | null = null;\n private _theme: BrandTheme | null = null;\n private _shellClass: string | null = null;\n private _linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null = null;\n\n get details(): BrandDetails | null {\n return this._details;\n }\n\n set details(value: BrandDetails | null) {\n this._details = value;\n this.render();\n }\n\n get theme(): BrandTheme | null {\n return this._theme;\n }\n\n set theme(value: BrandTheme | null) {\n this._theme = value;\n this.render();\n }\n\n get shellClass(): string | null {\n return this._shellClass;\n }\n\n set shellClass(value: string | null) {\n this._shellClass = value;\n this.render();\n }\n\n get linkFactory(): ((options: LinkFactoryOptions) => HTMLAnchorElement) | null {\n return this._linkFactory;\n }\n\n set linkFactory(value: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null) {\n this._linkFactory = value;\n this.render();\n }\n\n connectedCallback() {\n this.upgradeProperty(\"details\");\n this.upgradeProperty(\"theme\");\n this.upgradeProperty(\"shellClass\");\n this.upgradeProperty(\"linkFactory\");\n\n const attrs = parseAttributes(this);\n if (this._details == null) this._details = attrs.details;\n if (this._theme == null) this._theme = attrs.theme;\n if (this._shellClass == null) this._shellClass = attrs.shellClass;\n\n if (this.style.display !== \"block\") this.style.display = \"block\";\n this.render();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {\n if (name === \"details\") {\n this._details = parseJsonAttribute<BrandDetails>(newValue);\n } else if (name === \"theme\") {\n this._theme = parseJsonAttribute<BrandTheme>(newValue);\n } else if (name === \"shell-class\") {\n this._shellClass = normalizeClassName(newValue);\n }\n this.render();\n }\n\n protected abstract build(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null,\n ): HTMLElement;\n\n private render() {\n if (this._details == null) {\n this.replaceChildren();\n return;\n }\n\n const detailsResult = validateBrandDetails(this._details);\n if (!detailsResult.valid) {\n if (shouldValidateInDev()) {\n throw new BrandShellValidationError(\"brand-shell/web details\", detailsResult.errors);\n }\n this.replaceChildren();\n return;\n }\n\n const themeResult = validateBrandTheme(this._theme);\n if (!themeResult.valid && shouldValidateInDev()) {\n throw new BrandShellValidationError(\"brand-shell/web theme\", themeResult.errors);\n }\n\n const normalizedDetails = detailsResult.normalized as NormalizedBrandDetails;\n const normalizedTheme = themeResult.normalized ?? null;\n\n const element = this.build(normalizedDetails, normalizedTheme, this._shellClass, this._linkFactory);\n this.replaceChildren(element);\n }\n\n private upgradeProperty(propertyName: \"details\" | \"theme\" | \"shellClass\" | \"linkFactory\") {\n if (Object.prototype.hasOwnProperty.call(this, propertyName)) {\n const value = (this as unknown as Record<string, unknown>)[propertyName];\n delete (this as unknown as Record<string, unknown>)[propertyName];\n (this as unknown as Record<string, unknown>)[propertyName] = value;\n }\n }\n}\n\nexport class BrandHeaderElement extends BaseBrandShellElement {\n protected build(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null,\n ): HTMLElement {\n return createHeader(details, theme, shellClass, linkFactory ?? undefined);\n }\n}\n\nexport class BrandFooterElement extends BaseBrandShellElement {\n protected build(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory: ((options: LinkFactoryOptions) => HTMLAnchorElement) | null,\n ): HTMLElement {\n return createFooter(details, theme, shellClass, linkFactory ?? undefined);\n }\n}\n\nexport function registerBrandShellElements(options: RegisterBrandShellElementsOptions = {}) {\n if (typeof customElements === \"undefined\") {\n throw new Error(\"Custom elements are not available in this environment.\");\n }\n\n const headerTagName = options.headerTagName ?? \"brand-header\";\n const footerTagName = options.footerTagName ?? \"brand-footer\";\n\n if (!customElements.get(headerTagName)) {\n const HeaderConstructor =\n headerTagName === \"brand-header\" ? BrandHeaderElement : class extends BrandHeaderElement {};\n customElements.define(headerTagName, HeaderConstructor);\n }\n if (!customElements.get(footerTagName)) {\n const FooterConstructor =\n footerTagName === \"brand-footer\" ? BrandFooterElement : class extends BrandFooterElement {};\n customElements.define(footerTagName, FooterConstructor);\n }\n\n return { headerTagName, footerTagName };\n}\n\nexport function applyBrandShellProps(\n element: BrandShellElementLike | null | undefined,\n props: BrandShellElementProps,\n) {\n if (!element) return;\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, \"brand-shell/web details\");\n assertValidBrandTheme(props.theme, \"brand-shell/web theme\");\n }\n element.details = validateBrandDetails(props.details).normalized ?? props.details;\n element.theme = normalizeBrandTheme(props.theme ?? null);\n element.shellClass = props.shellClass ?? null;\n element.linkFactory = props.linkFactory ?? null;\n}\n\nexport function serializeBrandShellAttributes(props: BrandShellElementProps): SerializedBrandShellAttributes {\n if (shouldValidateInDev()) {\n assertValidBrandDetails(props.details, \"brand-shell/web serialize details\");\n assertValidBrandTheme(props.theme, \"brand-shell/web serialize theme\");\n }\n\n const normalizedDetails = validateBrandDetails(props.details).normalized ?? props.details;\n const normalizedTheme = normalizeBrandTheme(props.theme ?? null);\n\n const attributes: SerializedBrandShellAttributes = {\n details: JSON.stringify(normalizedDetails),\n };\n\n if (normalizedTheme) {\n attributes.theme = JSON.stringify(normalizedTheme);\n }\n\n const shellClass = normalizeClassName(props.shellClass ?? null);\n if (shellClass) {\n attributes[\"shell-class\"] = shellClass;\n }\n\n return attributes;\n}\n\nfunction parseAttributes(element: Element): ParsedAttributes {\n return {\n details: parseJsonAttribute<BrandDetails>(element.getAttribute(\"details\")),\n theme: parseJsonAttribute<BrandTheme>(element.getAttribute(\"theme\")),\n shellClass: normalizeClassName(element.getAttribute(\"shell-class\")),\n };\n}\n\nfunction parseJsonAttribute<T>(value: string | null): T | null {\n if (!value) return null;\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n}\n\nfunction normalizeClassName(value: string | null): string | null {\n if (!value) return null;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : null;\n}\n\nfunction createHeader(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const header = document.createElement(\"header\");\n header.className = joinClassNames(\"brand-shell-header\", shellClass);\n header.setAttribute(\"role\", \"banner\");\n applyThemeVariables(header, theme);\n header.dataset.brandCtaLayout = resolveCtaLayout(theme);\n\n const inner = document.createElement(\"div\");\n inner.className = \"brand-shell-header__inner\";\n\n const identity = details.homeHref\n ? createAnchor(details.homeHref, \"brand-shell-header__name\", details.name, `${details.name} home`, \"_self\", undefined, linkFactory)\n : createSpan(\"brand-shell-header__name\", details.name);\n if (details.logoSrc) {\n const img = document.createElement(\"img\");\n img.src = details.logoSrc;\n img.alt = details.logoAlt ?? details.name;\n img.className = \"brand-shell-header__logo\";\n identity.textContent = \"\";\n identity.append(img);\n }\n inner.append(identity);\n\n const actions = document.createElement(\"div\");\n actions.className = \"brand-shell-header__actions\";\n\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);\n\n if (navLinks.length > 0) {\n actions.append(createNav(navLinks, \"brand-shell-header\", \"Primary\", linkFactory));\n }\n\n if (ctaLinks.length > 0) {\n actions.append(createCtas(ctaLinks, \"brand-shell-header__ctas\", linkFactory));\n }\n\n if (socialLinks.length > 0) {\n actions.append(createSocialLinks(socialLinks, \"brand-shell-header__social\", \"brand-shell-header__social-link\"));\n }\n\n inner.append(actions);\n header.append(inner);\n return header;\n}\n\nfunction createFooter(\n details: BrandDetails,\n theme: BrandTheme | null,\n shellClass: string | null,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const footer = document.createElement(\"footer\");\n footer.className = joinClassNames(\"brand-shell-footer\", shellClass);\n footer.setAttribute(\"role\", \"contentinfo\");\n applyThemeVariables(footer, theme);\n footer.dataset.brandCtaLayout = resolveCtaLayout(theme);\n\n const inner = document.createElement(\"div\");\n inner.className = \"brand-shell-footer__inner\";\n\n const top = document.createElement(\"div\");\n top.className = \"brand-shell-footer__top\";\n\n const brand = document.createElement(\"div\");\n brand.className = \"brand-shell-footer__brand\";\n if (details.logoSrc) {\n const img = document.createElement(\"img\");\n img.src = details.logoSrc;\n img.alt = details.logoAlt ?? details.name;\n img.className = \"brand-shell-footer__logo\";\n if (details.homeHref) {\n const identity = createAnchor(details.homeHref, \"brand-shell-footer__name\", details.name, `${details.name} home`, \"_self\", undefined, linkFactory);\n identity.textContent = \"\";\n identity.append(img);\n brand.append(identity);\n } else {\n brand.append(img);\n }\n } else if (details.homeHref) {\n brand.append(createAnchor(details.homeHref, \"brand-shell-footer__name\", details.name, `${details.name} home`, \"_self\", undefined, linkFactory));\n } else {\n brand.append(createParagraph(\"brand-shell-footer__name\", details.name));\n }\n if (details.tagline) {\n brand.append(createParagraph(\"brand-shell-footer__tagline\", details.tagline));\n }\n top.append(brand);\n\n const { navLinks, ctaLinks, socialLinks } = buildShellViewModel(details);\n\n if (navLinks.length > 0) {\n top.append(createNav(navLinks, \"brand-shell-footer\", \"Footer\", linkFactory));\n }\n\n if (ctaLinks.length > 0) {\n top.append(createCtas(ctaLinks, \"brand-shell-footer__ctas\", linkFactory));\n }\n\n if (socialLinks.length > 0) {\n top.append(createSocialLinks(socialLinks, \"brand-shell-footer__social\", \"brand-shell-footer__social-link\"));\n }\n\n const copy = createParagraph(\"brand-shell-footer__copy\", `© ${new Date().getFullYear()} ${details.name}`);\n\n inner.append(top, copy);\n footer.append(inner);\n return footer;\n}\n\nfunction createNav(\n links: ShellNavLink[],\n blockClass: \"brand-shell-header\" | \"brand-shell-footer\",\n ariaLabel: string,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const nav = document.createElement(\"nav\");\n nav.className = `${blockClass}__nav`;\n nav.setAttribute(\"aria-label\", ariaLabel);\n\n const list = document.createElement(\"ul\");\n list.className = `${blockClass}__list`;\n\n for (const link of links) {\n const item = document.createElement(\"li\");\n const anchor = createAnchor(link.href, `${blockClass}__link`, link.label, link.ariaLabel, link.target, link.rel, linkFactory);\n item.append(anchor);\n list.append(item);\n }\n\n nav.append(list);\n return nav;\n}\n\nfunction createCtas(\n actions: ShellActionLink[],\n containerClass: string,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = containerClass;\n\n actions.forEach((action) => {\n const anchor = createAnchor(\n action.href,\n joinClassNames(\"brand-shell-button\", `brand-shell-button--${action.variant}`),\n action.label,\n action.ariaLabel,\n action.target,\n action.rel,\n linkFactory,\n );\n container.append(anchor);\n });\n\n return container;\n}\n\nfunction createSocialLinks(\n links: SocialLink[],\n containerClass: string,\n linkClass: string,\n): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = containerClass;\n container.setAttribute(\"aria-label\", \"Social links\");\n\n for (const link of links) {\n const anchor = createAnchor(link.href, linkClass, \"\");\n anchor.setAttribute(\"aria-label\", link.label);\n if (!link.href.startsWith(\"mailto:\")) {\n anchor.target = \"_blank\";\n anchor.rel = \"noopener noreferrer\";\n }\n\n const icon = createSocialIcon(link.platform, link.iconSvg);\n if (icon) {\n anchor.append(icon);\n } else {\n anchor.append(createSpan(\"\", link.label[0] ?? \"?\"));\n }\n\n container.append(anchor);\n }\n\n return container;\n}\n\nfunction createSocialIcon(platform: SocialPlatform | string, iconSvg?: string): Element | null {\n switch (platform) {\n case \"github\":\n return createFilledIcon(\n \"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 );\n case \"twitter\":\n return createFilledIcon(\"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 case \"linkedin\":\n return createFilledIcon(\n \"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 );\n case \"discord\":\n return createFilledIcon(\n \"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 );\n case \"email\":\n return createStrokedIcon([\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"], { rx: \"2\" });\n case \"website\":\n return createGlobeIcon();\n default:\n if (iconSvg) {\n const span = document.createElement(\"span\");\n span.setAttribute(\"aria-hidden\", \"true\");\n span.innerHTML = iconSvg;\n return span;\n }\n return null;\n }\n}\n\nfunction createFilledIcon(pathData: string): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"currentColor\");\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", pathData);\n svg.append(path);\n return svg;\n}\n\nfunction createStrokedIcon(pathData: string[], rectAttributes?: Record<string, string>): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n\n if (rectAttributes) {\n const rect = document.createElementNS(SVG_NS, \"rect\");\n rect.setAttribute(\"width\", \"20\");\n rect.setAttribute(\"height\", \"16\");\n rect.setAttribute(\"x\", \"2\");\n rect.setAttribute(\"y\", \"4\");\n for (const [attribute, value] of Object.entries(rectAttributes)) {\n rect.setAttribute(attribute, value);\n }\n svg.append(rect);\n }\n\n for (const d of pathData) {\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", d);\n svg.append(path);\n }\n\n return svg;\n}\n\nfunction createGlobeIcon(): SVGSVGElement {\n const svg = createBaseSvg();\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"2\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n\n const circle = document.createElementNS(SVG_NS, \"circle\");\n circle.setAttribute(\"cx\", \"12\");\n circle.setAttribute(\"cy\", \"12\");\n circle.setAttribute(\"r\", \"10\");\n svg.append(circle);\n\n for (const d of [\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\", \"M2 12h20\"]) {\n const path = document.createElementNS(SVG_NS, \"path\");\n path.setAttribute(\"d\", d);\n svg.append(path);\n }\n\n return svg;\n}\n\nfunction createBaseSvg(): SVGSVGElement {\n const svg = document.createElementNS(SVG_NS, \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"width\", \"1em\");\n svg.setAttribute(\"height\", \"1em\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n return svg;\n}\n\nfunction applyThemeVariables(element: HTMLElement, theme: BrandTheme | null) {\n const style = themeToCssVariables(theme);\n for (const [name, value] of Object.entries(style)) {\n element.style.setProperty(name, value);\n }\n}\n\nfunction resolveCtaLayout(theme: BrandTheme | null): \"inline\" | \"stacked\" {\n return theme?.ctaLayout === \"stacked\" ? \"stacked\" : \"inline\";\n}\n\nfunction joinClassNames(...classNames: Array<string | null | undefined>) {\n return classNames.filter(Boolean).join(\" \");\n}\n\nfunction createAnchor(\n href: string,\n className: string,\n text: string,\n ariaLabel?: string,\n target?: string,\n rel?: string,\n linkFactory?: (options: LinkFactoryOptions) => HTMLAnchorElement,\n): HTMLAnchorElement {\n if (linkFactory) {\n const anchor = linkFactory({\n href,\n className,\n ariaLabel: ariaLabel ?? text,\n target: target ?? \"_self\",\n rel,\n });\n anchor.textContent = text;\n return anchor;\n }\n\n const anchor = document.createElement(\"a\");\n anchor.href = href;\n anchor.className = className;\n anchor.textContent = text;\n if (ariaLabel) anchor.setAttribute(\"aria-label\", ariaLabel);\n if (target) anchor.target = target;\n if (rel) anchor.rel = rel;\n return anchor;\n}\n\nfunction createParagraph(className: string, text: string): HTMLParagraphElement {\n const paragraph = document.createElement(\"p\");\n paragraph.className = className;\n paragraph.textContent = text;\n return paragraph;\n}\n\nfunction createSpan(className: string, text: string): HTMLSpanElement {\n const span = document.createElement(\"span\");\n span.className = className;\n span.textContent = text;\n return span;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"brand-header\": BrandHeaderElement;\n \"brand-footer\": BrandFooterElement;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brand-shell",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Reusable Header and Footer components with typed details and theme. Premium default UX, works in React, Vite, Next.js.",
5
5
  "homepage": "https://github.com/venwork-dev/brand-shell#readme",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"validation-Cic4hPME.d.mts","names":[],"sources":["../src/core/types.ts","../src/core/social.ts","../src/core/shell.ts","../src/core/validation.ts"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"validation-CtH2UkVv.mjs","names":[],"sources":["../src/core/social.ts","../src/core/links.ts","../src/core/shell.ts","../src/core/dev.ts","../src/core/theme.ts","../src/core/validation.ts"],"sourcesContent":["import type { BrandDetails } from \"./types\";\n\nexport type SocialPlatform = \"website\" | \"linkedin\" | \"email\" | \"github\" | \"twitter\" | \"discord\";\n\nexport interface SocialLink {\n platform: SocialPlatform | string;\n href: string;\n label: string;\n iconSvg?: string;\n}\n\nexport function detailsToSocialLinks(details: BrandDetails): SocialLink[] {\n const links: SocialLink[] = [];\n if (details.website) links.push({ platform: \"website\", href: details.website, label: \"Website\" });\n if (details.linkedin) links.push({ platform: \"linkedin\", href: details.linkedin, label: \"LinkedIn\" });\n if (details.email) links.push({ platform: \"email\", href: details.email, label: \"Email\" });\n if (details.github) links.push({ platform: \"github\", href: details.github, label: \"GitHub\" });\n if (details.twitter) links.push({ platform: \"twitter\", href: details.twitter, label: \"Twitter\" });\n if (details.discord) links.push({ platform: \"discord\", href: details.discord, label: \"Discord\" });\n if (details.customSocialLinks) {\n for (const custom of details.customSocialLinks) {\n links.push({ platform: custom.platform, href: custom.href, label: custom.label, iconSvg: custom.iconSvg });\n }\n }\n return links;\n}\n","const ABSOLUTE_SCHEME_PATTERN = /^([a-zA-Z][a-zA-Z\\d+.-]*):/;\nconst UNSAFE_CHAR_PATTERN = /[\\u0000-\\u001f\\u007f]/;\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\"]);\nconst REQUIRED_BLANK_REL_TOKENS = [\"noopener\", \"noreferrer\"];\n\nexport function normalizeSafeHref(href: unknown): string | undefined {\n if (typeof href !== \"string\") return undefined;\n const trimmed = href.trim();\n if (trimmed.length === 0) return undefined;\n if (UNSAFE_CHAR_PATTERN.test(trimmed)) return undefined;\n if (trimmed.startsWith(\"//\")) return undefined;\n\n const schemeMatch = trimmed.match(ABSOLUTE_SCHEME_PATTERN);\n if (!schemeMatch) {\n return trimmed;\n }\n\n const protocol = `${schemeMatch[1]?.toLowerCase()}:`;\n if (!ALLOWED_PROTOCOLS.has(protocol)) {\n return undefined;\n }\n\n return trimmed;\n}\n\nexport function isSafeHref(href: unknown): boolean {\n return normalizeSafeHref(href) != null;\n}\n\nexport function normalizeRel(target: \"_blank\" | \"_self\" | \"_parent\" | \"_top\", rel?: string): string | undefined {\n const normalizedRel = typeof rel === \"string\" ? rel.trim() : \"\";\n if (target !== \"_blank\") {\n return normalizedRel.length > 0 ? normalizedRel : undefined;\n }\n\n const tokens = normalizedRel.length > 0 ? normalizedRel.split(/\\s+/).filter(Boolean) : [];\n const tokenSet = new Set(tokens.map((token) => token.toLowerCase()));\n\n for (const requiredToken of REQUIRED_BLANK_REL_TOKENS) {\n if (!tokenSet.has(requiredToken)) {\n tokens.push(requiredToken);\n }\n }\n\n return tokens.join(\" \");\n}\n","import { detailsToSocialLinks, type SocialLink } from \"./social\";\nimport type { BrandAction, BrandDetails, BrandNavLink, CustomSocialLink } from \"./types\";\nimport { normalizeRel, normalizeSafeHref } from \"./links\";\n\nexport type LinkTarget = NonNullable<BrandNavLink[\"target\"]>;\n\nexport interface ShellNavLink extends BrandNavLink {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n}\n\nexport interface ShellActionLink extends BrandAction {\n ariaLabel: string;\n rel?: string;\n target: LinkTarget;\n variant: NonNullable<BrandAction[\"variant\"]>;\n}\n\nexport interface ShellViewModel {\n navLinks: ShellNavLink[];\n ctaLinks: ShellActionLink[];\n socialLinks: SocialLink[];\n}\n\nexport type NormalizedActionLink = Omit<ShellActionLink, \"variant\"> & Pick<BrandAction, \"variant\">;\n\nexport interface NormalizedBrandDetails extends Omit<BrandDetails, \"navLinks\" | \"primaryAction\" | \"secondaryAction\"> {\n navLinks: ShellNavLink[];\n primaryAction?: NormalizedActionLink;\n secondaryAction?: NormalizedActionLink;\n}\n\nexport function normalizeNavLinks(navLinks: BrandNavLink[] = []): ShellNavLink[] {\n return navLinks.flatMap((link) => {\n const href = normalizeSafeHref(link.href);\n if (!href) return [];\n\n const { target, rel } = normalizeLinkTargetRel(link.target, link.rel);\n return {\n ...link,\n href,\n ariaLabel: link.ariaLabel ?? link.label,\n target,\n rel,\n };\n });\n}\n\nexport function normalizeBrandDetails(details: BrandDetails): NormalizedBrandDetails {\n const normalizedPrimaryAction = normalizeAction(details.primaryAction);\n const normalizedSecondaryAction = normalizeAction(details.secondaryAction);\n\n return {\n ...details,\n homeHref: normalizeSafeHref(details.homeHref),\n email: normalizeEmailHref(details.email),\n website: normalizeSafeHref(details.website),\n linkedin: normalizeSafeHref(details.linkedin),\n github: normalizeSafeHref(details.github),\n twitter: normalizeSafeHref(details.twitter),\n discord: normalizeSafeHref(details.discord),\n navLinks: normalizeNavLinks(details.navLinks),\n primaryAction: normalizedPrimaryAction,\n secondaryAction: normalizedSecondaryAction,\n customSocialLinks: normalizeCustomSocialLinks(details.customSocialLinks),\n };\n}\n\nexport function normalizeCtaLinks(\n primaryAction?: BrandAction,\n secondaryAction?: BrandAction,\n): ShellActionLink[] {\n const normalizedPrimaryAction = normalizeAction(primaryAction);\n const normalizedSecondaryAction = normalizeAction(secondaryAction);\n const actions = [normalizedSecondaryAction, normalizedPrimaryAction].filter(\n (action): action is NonNullable<typeof action> => Boolean(action),\n );\n\n return actions.map((action, index) => {\n const variant =\n action.variant ?? (action === normalizedPrimaryAction || index === actions.length - 1 ? \"primary\" : \"secondary\");\n\n return {\n ...action,\n variant,\n };\n });\n}\n\nexport function buildShellViewModelFromNormalized(normalized: NormalizedBrandDetails): ShellViewModel {\n return {\n navLinks: normalized.navLinks,\n ctaLinks: normalizeCtaLinks(normalized.primaryAction, normalized.secondaryAction),\n socialLinks: detailsToSocialLinks(normalized),\n };\n}\n\nexport function buildShellViewModel(details: BrandDetails): ShellViewModel {\n return buildShellViewModelFromNormalized(normalizeBrandDetails(details));\n}\n\nexport function normalizeEmailHref(email?: string): string | undefined {\n if (typeof email !== \"string\") return undefined;\n const trimmed = email.trim();\n if (trimmed.length === 0) return undefined;\n if (trimmed.toLowerCase().startsWith(\"mailto:\")) {\n const address = trimmed.slice(7).trim();\n if (address.length === 0) return undefined;\n return normalizeSafeHref(`mailto:${address}`);\n }\n return normalizeSafeHref(`mailto:${trimmed}`);\n}\n\nfunction normalizeCustomSocialLinks(links?: CustomSocialLink[]): CustomSocialLink[] | undefined {\n if (!links || links.length === 0) return undefined;\n const normalized: CustomSocialLink[] = [];\n for (const link of links) {\n const href = normalizeSafeHref(link.href);\n if (!href) continue;\n normalized.push({ ...link, href });\n }\n return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction normalizeAction(action?: BrandAction): NormalizedActionLink | undefined {\n if (!action) return undefined;\n\n const href = normalizeSafeHref(action.href);\n if (!href) return undefined;\n\n const { target, rel } = normalizeLinkTargetRel(action.target, action.rel);\n return {\n ...action,\n href,\n target,\n rel,\n ariaLabel: action.ariaLabel ?? action.label,\n };\n}\n\nexport function normalizeLinkTargetRel(target?: BrandNavLink[\"target\"], rel?: string) {\n const normalizedTarget: LinkTarget = target ?? \"_self\";\n const normalizedRel = normalizeRel(normalizedTarget, rel);\n return {\n target: normalizedTarget,\n rel: normalizedRel,\n };\n}\n","type ImportMetaEnv = {\n DEV?: boolean;\n MODE?: string;\n};\n\ntype ImportMetaLike = {\n env?: ImportMetaEnv;\n};\n\nexport function shouldValidateInDev(): boolean {\n const nodeEnv = typeof process !== \"undefined\" ? process.env?.NODE_ENV : undefined;\n if (typeof nodeEnv === \"string\") {\n return nodeEnv !== \"production\";\n }\n\n const { env } = import.meta as unknown as ImportMetaLike;\n if (typeof env?.DEV === \"boolean\") {\n return env.DEV;\n }\n\n return false;\n}\n","import type { BrandTheme } from \"./types\";\n\nconst THEME_VAR_PREFIX = \"brand\";\nconst DARK_TEXT = \"#0f172a\";\nconst LIGHT_TEXT = \"#f8fafc\";\n\nexport type ThemeVariables = Record<string, string>;\n\nexport function themeToCssVariables(theme?: BrandTheme | null): ThemeVariables {\n if (!theme) return {};\n const style: ThemeVariables = {};\n if (theme.primaryColor != null) style[`--${THEME_VAR_PREFIX}-primary`] = theme.primaryColor;\n if (theme.backgroundColor != null) style[`--${THEME_VAR_PREFIX}-bg`] = theme.backgroundColor;\n if (theme.textColor != null) style[`--${THEME_VAR_PREFIX}-text`] = theme.textColor;\n if (theme.fontFamily != null) style[`--${THEME_VAR_PREFIX}-font`] = theme.fontFamily;\n if (theme.linkColor != null) style[`--${THEME_VAR_PREFIX}-link`] = theme.linkColor;\n if (theme.socialIconSize != null) style[`--${THEME_VAR_PREFIX}-social-size`] = theme.socialIconSize;\n if (theme.buttonTextColor != null) {\n style[`--${THEME_VAR_PREFIX}-button-text`] = theme.buttonTextColor;\n } else if (theme.primaryColor != null) {\n const buttonTextColor = getAccessibleTextColor(theme.primaryColor);\n if (buttonTextColor) style[`--${THEME_VAR_PREFIX}-button-text`] = buttonTextColor;\n }\n if (theme.borderRadius != null) style[`--${THEME_VAR_PREFIX}-radius`] = theme.borderRadius;\n if (theme.headerHeight != null) style[`--${THEME_VAR_PREFIX}-header-height`] = theme.headerHeight;\n if (theme.footerPadding != null) style[`--${THEME_VAR_PREFIX}-footer-padding`] = theme.footerPadding;\n if (theme.secondaryButtonBg != null) style[`--${THEME_VAR_PREFIX}-button-secondary`] = theme.secondaryButtonBg;\n return style;\n}\n\nfunction getAccessibleTextColor(backgroundColor: string): string | undefined {\n const rgb = parseColorToRgb(backgroundColor);\n if (!rgb) return undefined;\n\n const contrastWithDark = contrastRatio(rgb, { r: 15, g: 23, b: 42 });\n const contrastWithLight = contrastRatio(rgb, { r: 248, g: 250, b: 252 });\n\n return contrastWithDark > contrastWithLight ? DARK_TEXT : LIGHT_TEXT;\n}\n\n/**\n * Parses a hex (#rgb / #rrggbb) or rgb()/rgba() color string to RGB components.\n * hsl(), oklch(), and other formats are not supported and return undefined.\n */\nfunction parseColorToRgb(color: string): { r: number; g: number; b: number } | undefined {\n const trimmed = color.trim();\n\n const hexMatch = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(trimmed);\n if (hexMatch) {\n const value = hexMatch[1];\n if (value.length === 3) {\n return {\n r: Number.parseInt(value[0] + value[0], 16),\n g: Number.parseInt(value[1] + value[1], 16),\n b: Number.parseInt(value[2] + value[2], 16),\n };\n }\n return {\n r: Number.parseInt(value.slice(0, 2), 16),\n g: Number.parseInt(value.slice(2, 4), 16),\n b: Number.parseInt(value.slice(4, 6), 16),\n };\n }\n\n const rgbMatch = /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/.exec(trimmed);\n if (rgbMatch) {\n return { r: +rgbMatch[1], g: +rgbMatch[2], b: +rgbMatch[3] };\n }\n\n return undefined;\n}\n\nfunction contrastRatio(\n first: { r: number; g: number; b: number },\n second: { r: number; g: number; b: number },\n): number {\n const firstLuminance = relativeLuminance(first);\n const secondLuminance = relativeLuminance(second);\n const lighter = Math.max(firstLuminance, secondLuminance);\n const darker = Math.min(firstLuminance, secondLuminance);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction relativeLuminance(color: { r: number; g: number; b: number }): number {\n const transform = (channel: number) => {\n const normalized = channel / 255;\n if (normalized <= 0.03928) return normalized / 12.92;\n return ((normalized + 0.055) / 1.055) ** 2.4;\n };\n\n const r = transform(color.r);\n const g = transform(color.g);\n const b = transform(color.b);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n","import { normalizeBrandDetails, normalizeEmailHref, type NormalizedBrandDetails } from \"./shell\";\nimport { normalizeSafeHref } from \"./links\";\nimport type { BrandAction, BrandDetails, BrandNavLink, BrandTheme, CustomSocialLink } from \"./types\";\n\nconst LINK_TARGETS = new Set<NonNullable<BrandNavLink[\"target\"]>>([\"_blank\", \"_self\", \"_parent\", \"_top\"]);\nconst CTA_VARIANTS = new Set<NonNullable<BrandAction[\"variant\"]>>([\"primary\", \"secondary\", \"ghost\"]);\nconst CTA_LAYOUTS = new Set<NonNullable<BrandTheme[\"ctaLayout\"]>>([\"inline\", \"stacked\"]);\nconst THEME_KEYS = new Set<keyof BrandTheme>([\n \"primaryColor\",\n \"backgroundColor\",\n \"textColor\",\n \"fontFamily\",\n \"linkColor\",\n \"socialIconSize\",\n \"buttonTextColor\",\n \"ctaLayout\",\n \"borderRadius\",\n \"headerHeight\",\n \"footerPadding\",\n \"secondaryButtonBg\",\n]);\n\ntype ValidationErrorPath = string;\n\nexport interface BrandValidationResult<T> {\n valid: boolean;\n errors: string[];\n normalized: T | null;\n}\n\nexport class BrandShellValidationError extends Error {\n readonly context: string;\n readonly errors: string[];\n\n constructor(context: string, errors: string[]) {\n super(formatValidationErrors(context, errors));\n this.name = \"BrandShellValidationError\";\n this.context = context;\n this.errors = errors;\n }\n}\n\nexport function validateBrandDetails(details: unknown): BrandValidationResult<NormalizedBrandDetails> {\n const errors: string[] = [];\n\n if (!isRecord(details)) {\n errors.push(\"details must be an object.\");\n return { valid: false, errors, normalized: null };\n }\n\n validateRequiredString(details.name, \"details.name\", errors);\n validateOptionalString(details.homeHref, \"details.homeHref\", errors);\n validateSafeHref(details.homeHref, \"details.homeHref\", errors);\n validateOptionalString(details.website, \"details.website\", errors);\n validateSafeHref(details.website, \"details.website\", errors);\n validateOptionalString(details.linkedin, \"details.linkedin\", errors);\n validateSafeHref(details.linkedin, \"details.linkedin\", errors);\n validateOptionalString(details.email, \"details.email\", errors);\n validateEmail(details.email, \"details.email\", errors);\n validateOptionalString(details.github, \"details.github\", errors);\n validateSafeHref(details.github, \"details.github\", errors);\n validateOptionalString(details.twitter, \"details.twitter\", errors);\n validateSafeHref(details.twitter, \"details.twitter\", errors);\n validateOptionalString(details.discord, \"details.discord\", errors);\n validateSafeHref(details.discord, \"details.discord\", errors);\n validateOptionalString(details.tagline, \"details.tagline\", errors);\n\n if (details.navLinks != null) {\n if (!Array.isArray(details.navLinks)) {\n errors.push(\"details.navLinks must be an array.\");\n } else {\n details.navLinks.forEach((link, index) => validateNavLink(link, `details.navLinks[${index}]`, errors));\n }\n }\n\n if (details.primaryAction != null) {\n validateAction(details.primaryAction, \"details.primaryAction\", errors);\n }\n if (details.secondaryAction != null) {\n validateAction(details.secondaryAction, \"details.secondaryAction\", errors);\n }\n\n if (details.customSocialLinks != null) {\n if (!Array.isArray(details.customSocialLinks)) {\n errors.push(\"details.customSocialLinks must be an array.\");\n } else {\n details.customSocialLinks.forEach((link, index) =>\n validateCustomSocialLink(link, `details.customSocialLinks[${index}]`, errors),\n );\n }\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandDetails(details as unknown as BrandDetails),\n };\n}\n\nexport function validateBrandTheme(theme: unknown): BrandValidationResult<BrandTheme | null> {\n if (theme == null) {\n return {\n valid: true,\n errors: [],\n normalized: null,\n };\n }\n\n const errors: string[] = [];\n\n if (!isRecord(theme)) {\n errors.push(\"theme must be an object when provided.\");\n return { valid: false, errors, normalized: null };\n }\n\n for (const key of Object.keys(theme)) {\n if (!THEME_KEYS.has(key as keyof BrandTheme)) {\n errors.push(`theme.${key} is not a supported theme key.`);\n continue;\n }\n if (key === \"ctaLayout\") {\n validateCtaLayout(theme[key], \"theme.ctaLayout\", errors);\n continue;\n }\n validateOptionalString(theme[key], `theme.${key}`, errors);\n }\n\n if (errors.length > 0) {\n return { valid: false, errors, normalized: null };\n }\n\n return {\n valid: true,\n errors: [],\n normalized: normalizeBrandTheme(theme as BrandTheme),\n };\n}\n\nexport function assertValidBrandDetails(details: unknown, context = \"BrandDetails\"): asserts details is BrandDetails {\n const result = validateBrandDetails(details);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function assertValidBrandTheme(theme: unknown, context = \"BrandTheme\"): asserts theme is BrandTheme | null | undefined {\n const result = validateBrandTheme(theme);\n if (!result.valid) {\n throw new BrandShellValidationError(context, result.errors);\n }\n}\n\nexport function normalizeBrandTheme(theme?: BrandTheme | null): BrandTheme | null {\n if (!theme) return null;\n\n const normalized: BrandTheme = {};\n for (const key of THEME_KEYS) {\n const value = theme[key];\n if (key === \"ctaLayout\") {\n if (typeof value === \"string\" && CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n normalized[key] = value as BrandTheme[\"ctaLayout\"];\n }\n continue;\n }\n\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length > 0) {\n normalized[key] = trimmed;\n }\n }\n }\n\n return Object.keys(normalized).length > 0 ? normalized : null;\n}\n\nexport function formatValidationErrors(context: string, errors: string[]): string {\n return `${context} validation failed:\\n${errors.map((error) => `- ${error}`).join(\"\\n\")}`;\n}\n\nfunction validateNavLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n validateOptionalString(link.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(link.rel, `${path}.rel`, errors);\n validateTarget(link.target, `${path}.target`, errors);\n}\n\nfunction validateAction(action: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(action)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n validateRequiredString(action.label, `${path}.label`, errors);\n validateRequiredString(action.href, `${path}.href`, errors);\n validateSafeHref(action.href, `${path}.href`, errors);\n validateOptionalString(action.ariaLabel, `${path}.ariaLabel`, errors);\n validateOptionalString(action.rel, `${path}.rel`, errors);\n validateTarget(action.target, `${path}.target`, errors);\n\n if (action.variant != null) {\n if (typeof action.variant !== \"string\" || !CTA_VARIANTS.has(action.variant as NonNullable<BrandAction[\"variant\"]>)) {\n errors.push(`${path}.variant must be one of: primary, secondary, ghost.`);\n }\n }\n}\n\nfunction validateCustomSocialLink(link: unknown, path: ValidationErrorPath, errors: string[]) {\n if (!isRecord(link)) {\n errors.push(`${path} must be an object.`);\n return;\n }\n\n if (typeof link.platform !== \"string\" || link.platform.trim().length === 0) {\n errors.push(`${path}.platform must be a non-empty string.`);\n }\n validateRequiredString(link.label, `${path}.label`, errors);\n validateRequiredString(link.href, `${path}.href`, errors);\n validateSafeHref(link.href, `${path}.href`, errors);\n if (link.iconSvg != null) {\n validateOptionalString(link.iconSvg, `${path}.iconSvg`, errors);\n }\n}\n\nfunction validateTarget(target: unknown, path: ValidationErrorPath, errors: string[]) {\n if (target == null) return;\n if (typeof target !== \"string\" || !LINK_TARGETS.has(target as NonNullable<BrandNavLink[\"target\"]>)) {\n errors.push(`${path} must be one of: _blank, _self, _parent, _top.`);\n }\n}\n\nfunction validateCtaLayout(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || !CTA_LAYOUTS.has(value as NonNullable<BrandTheme[\"ctaLayout\"]>)) {\n errors.push(`${path} must be one of: inline, stacked.`);\n }\n}\n\nfunction validateRequiredString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string.`);\n }\n}\n\nfunction validateOptionalString(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push(`${path} must be a non-empty string when provided.`);\n }\n}\n\nfunction validateSafeHref(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeSafeHref(value)) {\n errors.push(`${path} must use a safe URL/path (http, https, mailto, tel, or relative path).`);\n }\n}\n\nfunction validateEmail(value: unknown, path: ValidationErrorPath, errors: string[]) {\n if (value == null) return;\n if (typeof value !== \"string\" || value.trim().length === 0) return;\n if (!normalizeEmailHref(value)) {\n errors.push(`${path} must be a valid email or mailto URL.`);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":"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"}