@notionhive/footers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/bin/footers.js +16 -0
  2. package/category.config.json +7 -0
  3. package/package.json +24 -0
  4. package/registry/footer-01.json +6 -0
  5. package/registry/footer-02.json +9 -0
  6. package/registry/footer-03.json +9 -0
  7. package/registry/footer-04.json +9 -0
  8. package/registry/footer-05.json +9 -0
  9. package/registry/footer-06.json +7 -0
  10. package/registry/footer-07.json +9 -0
  11. package/registry/footer-08.json +9 -0
  12. package/registry/footer-09.json +9 -0
  13. package/registry/footer-10.json +9 -0
  14. package/registry/footer-11.json +7 -0
  15. package/registry/footer-12.json +9 -0
  16. package/registry/footer-13.json +9 -0
  17. package/registry/footer-14.json +9 -0
  18. package/registry/footer-15.json +9 -0
  19. package/registry/footer-16.json +9 -0
  20. package/registry/footer-17.json +9 -0
  21. package/registry/footer-18.json +9 -0
  22. package/registry/footer-19.json +9 -0
  23. package/registry/footer-20.json +9 -0
  24. package/registry/footer-21.json +9 -0
  25. package/registry/footer-22.json +9 -0
  26. package/registry/footer-23.json +9 -0
  27. package/registry/footer-24.json +9 -0
  28. package/registry/footer-25.json +9 -0
  29. package/registry/footer-26.json +9 -0
  30. package/registry/footer-27.json +9 -0
  31. package/registry/footer-28.json +9 -0
  32. package/registry/footer-29.json +6 -0
  33. package/registry/footer-30.json +6 -0
  34. package/registry/index.json +263 -0
  35. package/templates/components/atoms/SafeImage/SafeImage.jsx +101 -0
  36. package/templates/components/atoms/SafeImage/index.js +1 -0
  37. package/templates/components/organisms/Footer01/Footer01.jsx +142 -0
  38. package/templates/components/organisms/Footer01/Footer01.propTypes.js +110 -0
  39. package/templates/components/organisms/Footer01/index.js +1 -0
  40. package/templates/components/organisms/Footer02/Footer02.jsx +136 -0
  41. package/templates/components/organisms/Footer02/Footer02.propTypes.js +112 -0
  42. package/templates/components/organisms/Footer02/index.js +1 -0
  43. package/templates/components/organisms/Footer03/Footer03.jsx +162 -0
  44. package/templates/components/organisms/Footer03/Footer03.propTypes.js +73 -0
  45. package/templates/components/organisms/Footer03/index.js +1 -0
  46. package/templates/components/organisms/Footer04/Footer04.jsx +128 -0
  47. package/templates/components/organisms/Footer04/Footer04.propTypes.js +93 -0
  48. package/templates/components/organisms/Footer04/index.js +1 -0
  49. package/templates/components/organisms/Footer05/Footer05.jsx +101 -0
  50. package/templates/components/organisms/Footer05/Footer05.propTypes.js +42 -0
  51. package/templates/components/organisms/Footer05/index.js +1 -0
  52. package/templates/components/organisms/Footer06/Footer06.jsx +170 -0
  53. package/templates/components/organisms/Footer06/Footer06.propTypes.js +53 -0
  54. package/templates/components/organisms/Footer06/index.js +1 -0
  55. package/templates/components/organisms/Footer07/Footer07.jsx +253 -0
  56. package/templates/components/organisms/Footer07/Footer07.propTypes.js +83 -0
  57. package/templates/components/organisms/Footer07/index.js +1 -0
  58. package/templates/components/organisms/Footer08/Footer08.jsx +183 -0
  59. package/templates/components/organisms/Footer08/Footer08.propTypes.js +92 -0
  60. package/templates/components/organisms/Footer08/index.js +1 -0
  61. package/templates/components/organisms/Footer09/Footer09.jsx +199 -0
  62. package/templates/components/organisms/Footer09/Footer09.propTypes.js +76 -0
  63. package/templates/components/organisms/Footer09/index.js +1 -0
  64. package/templates/components/organisms/Footer10/Footer10.jsx +172 -0
  65. package/templates/components/organisms/Footer10/Footer10.propTypes.js +38 -0
  66. package/templates/components/organisms/Footer10/index.js +1 -0
  67. package/templates/components/organisms/Footer11/Footer11.jsx +107 -0
  68. package/templates/components/organisms/Footer11/Footer11.propTypes.js +64 -0
  69. package/templates/components/organisms/Footer11/index.js +1 -0
  70. package/templates/components/organisms/Footer12/Footer12.jsx +122 -0
  71. package/templates/components/organisms/Footer12/Footer12.propTypes.js +94 -0
  72. package/templates/components/organisms/Footer12/index.js +1 -0
  73. package/templates/components/organisms/Footer13/Footer13.jsx +156 -0
  74. package/templates/components/organisms/Footer13/Footer13.propTypes.js +80 -0
  75. package/templates/components/organisms/Footer13/index.js +1 -0
  76. package/templates/components/organisms/Footer14/Footer14.jsx +157 -0
  77. package/templates/components/organisms/Footer14/Footer14.propTypes.js +60 -0
  78. package/templates/components/organisms/Footer14/index.js +1 -0
  79. package/templates/components/organisms/Footer15/Footer15.jsx +184 -0
  80. package/templates/components/organisms/Footer15/Footer15.propTypes.js +58 -0
  81. package/templates/components/organisms/Footer15/index.js +1 -0
  82. package/templates/components/organisms/Footer16/Footer16.jsx +87 -0
  83. package/templates/components/organisms/Footer16/Footer16.propTypes.js +73 -0
  84. package/templates/components/organisms/Footer16/index.js +1 -0
  85. package/templates/components/organisms/Footer17/Footer17.jsx +176 -0
  86. package/templates/components/organisms/Footer17/Footer17.propTypes.js +117 -0
  87. package/templates/components/organisms/Footer17/index.js +1 -0
  88. package/templates/components/organisms/Footer18/Footer18.jsx +236 -0
  89. package/templates/components/organisms/Footer18/Footer18.propTypes.js +92 -0
  90. package/templates/components/organisms/Footer18/index.js +1 -0
  91. package/templates/components/organisms/Footer19/Footer19.jsx +287 -0
  92. package/templates/components/organisms/Footer19/Footer19.propTypes.js +116 -0
  93. package/templates/components/organisms/Footer19/index.js +1 -0
  94. package/templates/components/organisms/Footer20/Footer20.jsx +201 -0
  95. package/templates/components/organisms/Footer20/Footer20.propTypes.js +70 -0
  96. package/templates/components/organisms/Footer20/index.js +1 -0
  97. package/templates/components/organisms/Footer21/Footer21.jsx +134 -0
  98. package/templates/components/organisms/Footer21/Footer21.propTypes.js +122 -0
  99. package/templates/components/organisms/Footer21/index.js +1 -0
  100. package/templates/components/organisms/Footer22/Footer22.jsx +136 -0
  101. package/templates/components/organisms/Footer22/Footer22.propTypes.js +58 -0
  102. package/templates/components/organisms/Footer22/index.js +1 -0
  103. package/templates/components/organisms/Footer23/Footer23.jsx +177 -0
  104. package/templates/components/organisms/Footer23/Footer23.propTypes.js +97 -0
  105. package/templates/components/organisms/Footer23/index.js +1 -0
  106. package/templates/components/organisms/Footer24/Footer24.jsx +192 -0
  107. package/templates/components/organisms/Footer24/Footer24.propTypes.js +132 -0
  108. package/templates/components/organisms/Footer24/index.js +1 -0
  109. package/templates/components/organisms/Footer25/Footer25.jsx +135 -0
  110. package/templates/components/organisms/Footer25/Footer25.propTypes.js +124 -0
  111. package/templates/components/organisms/Footer25/index.js +1 -0
  112. package/templates/components/organisms/Footer26/Footer26.jsx +167 -0
  113. package/templates/components/organisms/Footer26/Footer26.propTypes.js +54 -0
  114. package/templates/components/organisms/Footer26/index.js +1 -0
  115. package/templates/components/organisms/Footer27/Footer27.jsx +171 -0
  116. package/templates/components/organisms/Footer27/Footer27.propTypes.js +105 -0
  117. package/templates/components/organisms/Footer27/index.js +1 -0
  118. package/templates/components/organisms/Footer28/Footer28.jsx +98 -0
  119. package/templates/components/organisms/Footer28/Footer28.propTypes.js +96 -0
  120. package/templates/components/organisms/Footer28/index.js +1 -0
  121. package/templates/components/organisms/Footer29/Footer29.jsx +118 -0
  122. package/templates/components/organisms/Footer29/Footer29.propTypes.js +68 -0
  123. package/templates/components/organisms/Footer29/index.js +1 -0
  124. package/templates/components/organisms/Footer30/Footer30.jsx +92 -0
  125. package/templates/components/organisms/Footer30/Footer30.propTypes.js +51 -0
  126. package/templates/components/organisms/Footer30/index.js +1 -0
  127. package/templates/public/footer/footer02/payment-01.png +0 -0
  128. package/templates/public/footer/footer02/payment-02.png +0 -0
  129. package/templates/public/footer/footer02/payment-03.png +0 -0
  130. package/templates/public/footer/footer02/payment-04.png +0 -0
  131. package/templates/public/footer/footer02/payment-05.png +0 -0
  132. package/templates/public/footer/footer02/payment-06.png +0 -0
  133. package/templates/public/footer/footer02/payment-07.png +0 -0
  134. package/templates/public/footer/footer02/payment-08.png +0 -0
  135. package/templates/public/footer/footer02/payment-09.svg +5 -0
  136. package/templates/public/footer/footer02/payment-10.svg +11 -0
  137. package/templates/public/footer/footer02/payment-11.svg +22 -0
  138. package/templates/public/footer/footer02/payment-12.svg +25 -0
  139. package/templates/public/footer/footer02/payment-13.svg +10 -0
  140. package/templates/public/footer/footer02/payment-14.svg +36 -0
  141. package/templates/public/footer/footer02/payment-15.svg +18 -0
  142. package/templates/public/footer/footer02/payment-sprite-01.png +0 -0
  143. package/templates/public/footer/footer02/payment-sprite-02.png +0 -0
  144. package/templates/public/footer/footer02/payment-sprite-03.png +0 -0
  145. package/templates/public/footer/footer02/payment-sprite-04.png +0 -0
  146. package/templates/public/footer/footer02/payment-sprite-05.png +0 -0
  147. package/templates/public/footer/footer02/payment-sprite-06.png +0 -0
  148. package/templates/public/footer/footer03/logo-mask.png +0 -0
  149. package/templates/public/footer/footer03/logo.png +0 -0
  150. package/templates/public/footer/footer04/logo.svg +45 -0
  151. package/templates/public/footer/footer05/brand-watermark.png +0 -0
  152. package/templates/public/footer/footer07/logo.png +0 -0
  153. package/templates/public/footer/footer07/watch-hero.png +0 -0
  154. package/templates/public/footer/footer07/watch-overlay.png +0 -0
  155. package/templates/public/footer/footer08/logo.png +0 -0
  156. package/templates/public/footer/footer08/logo.svg +3 -0
  157. package/templates/public/footer/footer09/logo.svg +13 -0
  158. package/templates/public/footer/footer10/hero-image.png +0 -0
  159. package/templates/public/footer/footer10/hero-mask-download.svg +3 -0
  160. package/templates/public/footer/footer10/hero-mask.svg +3 -0
  161. package/templates/public/footer/footer12/logo-icon.svg +3 -0
  162. package/templates/public/footer/footer12/refugee-logo.png +0 -0
  163. package/templates/public/footer/footer13/logo.png +0 -0
  164. package/templates/public/footer/footer14/logo.png +0 -0
  165. package/templates/public/footer/footer15/logo.svg +19 -0
  166. package/templates/public/footer/footer16/logo.svg +6 -0
  167. package/templates/public/footer/footer17/logo-left.svg +11 -0
  168. package/templates/public/footer/footer17/logo-right.svg +36 -0
  169. package/templates/public/footer/footer18/logo.png +108 -0
  170. package/templates/public/footer/footer18/logo.svg +108 -0
  171. package/templates/public/footer/footer19/logo.png +0 -0
  172. package/templates/public/footer/footer19/payment-bkash.png +0 -0
  173. package/templates/public/footer/footer19/payment-mastercard.svg +12 -0
  174. package/templates/public/footer/footer19/payment-nagad.png +0 -0
  175. package/templates/public/footer/footer19/payment-nexus.png +0 -0
  176. package/templates/public/footer/footer19/payment-rocket.png +0 -0
  177. package/templates/public/footer/footer19/payment-upay.png +0 -0
  178. package/templates/public/footer/footer19/payment-visa.svg +6 -0
  179. package/templates/public/footer/footer20/bg.png +0 -0
  180. package/templates/public/footer/footer20/logo.png +0 -0
  181. package/templates/public/footer/footer21/logo.png +0 -0
  182. package/templates/public/footer/footer22/logo-icon.svg +10 -0
  183. package/templates/public/footer/footer22/logo-name.svg +5 -0
  184. package/templates/public/footer/footer22/logo-tagline.svg +5 -0
  185. package/templates/public/footer/footer23/logo-icon.svg +10 -0
  186. package/templates/public/footer/footer23/logo-name.svg +20 -0
  187. package/templates/public/footer/footer23/logo-tagline.svg +17 -0
  188. package/templates/public/footer/footer24/logo.svg +46 -0
  189. package/templates/public/footer/footer24/map.png +0 -0
  190. package/templates/public/footer/footer25/logo-icon.svg +15 -0
  191. package/templates/public/footer/footer25/logo-text.svg +42 -0
  192. package/templates/public/footer/footer25/map-pin.svg +7 -0
  193. package/templates/public/footer/footer25/map.png +0 -0
  194. package/templates/public/footer/footer26/logo-jpl.svg +5 -0
  195. package/templates/public/footer/footer26/logo.png +0 -0
  196. package/templates/public/footer/footer27/logo.png +0 -0
  197. package/templates/public/footer/footer28/logo.png +0 -0
@@ -0,0 +1,156 @@
1
+ "use client";
2
+
3
+ import SafeImage from "../../atoms/SafeImage";
4
+ import {
5
+ footer13DefaultProps,
6
+ footer13PropTypes,
7
+ } from "./Footer13.propTypes";
8
+
9
+ function FacebookIcon({ className = "" }) {
10
+ return (
11
+ <svg viewBox="0 0 18 18" fill="currentColor" aria-hidden="true" className={className}>
12
+ <path d="M18 9a9 9 0 10-10.4 8.9v-6.3H5.5V9h2.1V7.1c0-2.1 1.2-3.2 3.1-3.2.9 0 1.8.2 1.8.2v2h-1c-1 0-1.3.6-1.3 1.3V9h2.3l-.4 2.6h-1.9v6.3A9 9 0 0018 9z" />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ function LinkedinIcon({ className = "" }) {
18
+ return (
19
+ <svg viewBox="0 0 18 18" fill="currentColor" aria-hidden="true" className={className}>
20
+ <path d="M4 2a2 2 0 100 4 2 2 0 000-4zM2 7h4v11H2V7zm6 0h3.8v1.5c.6-1 1.8-1.7 3.2-1.7 3.4 0 4 2.2 4 5.1V18h-4v-5.6c0-1.3 0-2.9-1.8-2.9-1.8 0-2.1 1.4-2.1 2.8V18H8V7z" />
21
+ </svg>
22
+ );
23
+ }
24
+
25
+ function TwitterIcon({ className = "" }) {
26
+ return (
27
+ <svg viewBox="0 0 18 18" fill="currentColor" aria-hidden="true" className={className}>
28
+ <path d="M14.2 2h2.5l-5.4 6.2L17.5 16h-4.9l-3.8-5-4.4 5H2.3l5.8-6.6L2 2h5l3.4 4.5L14.2 2zm-.9 12.6h1.4L5.1 3.3H3.6l9.7 11.3z" />
29
+ </svg>
30
+ );
31
+ }
32
+
33
+ function YoutubeIcon({ className = "" }) {
34
+ return (
35
+ <svg viewBox="0 0 18 18" fill="currentColor" aria-hidden="true" className={className}>
36
+ <path d="M17.6 4.8a2.3 2.3 0 00-1.6-1.6C14.4 3 9 3 9 3s-5.4 0-7 .2A2.3 2.3 0 00.4 4.8 24 24 0 000 9a24 24 0 00.4 4.2 2.3 2.3 0 001.6 1.6c1.6.2 7 .2 7 .2s5.4 0 7-.2a2.3 2.3 0 001.6-1.6A24 24 0 0018 9a24 24 0 00-.4-4.2zM7.2 11.7V6.3L12 9l-4.8 2.7z" />
37
+ </svg>
38
+ );
39
+ }
40
+
41
+ const SOCIAL_ICONS = {
42
+ facebook: FacebookIcon,
43
+ linkedin: LinkedinIcon,
44
+ twitter: TwitterIcon,
45
+ youtube: YoutubeIcon,
46
+ };
47
+
48
+ function LinkColumn({ column }) {
49
+ return (
50
+ <div className="flex min-w-0 flex-1 flex-col gap-8 px-4 py-8 lg:px-8 lg:py-12">
51
+ <p className="text-xl font-semibold leading-8 text-[#0a142f] lg:text-2xl">{column.title}</p>
52
+ <ul className="flex flex-col gap-4">
53
+ {column.links.map((link) => (
54
+ <li key={link.label}>
55
+ <a
56
+ href={link.href ?? "#"}
57
+ className="text-base font-medium leading-8 text-[rgba(10,20,47,0.8)] transition-colors duration-200 ease-out hover:text-[#0a142f] focus-visible:outline-2 focus-visible:outline-offset-2"
58
+ >
59
+ {link.label}
60
+ </a>
61
+ </li>
62
+ ))}
63
+ </ul>
64
+ </div>
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Footer13 — ASBE light footer with contact, link columns, and social row.
70
+ * Figma node 32:20962 at 1920px.
71
+ */
72
+ export function Footer13({
73
+ logoSrc = footer13DefaultProps.logoSrc,
74
+ logoAlt = footer13DefaultProps.logoAlt,
75
+ followLabel = footer13DefaultProps.followLabel,
76
+ socialLinks = footer13DefaultProps.socialLinks,
77
+ contact = footer13DefaultProps.contact,
78
+ linkColumns = footer13DefaultProps.linkColumns,
79
+ copyright = footer13DefaultProps.copyright,
80
+ className = "",
81
+ }) {
82
+ return (
83
+ <footer
84
+ className={["relative w-full overflow-hidden border-t border-[rgba(10,20,47,0.1)] bg-[#fafafa]", className]
85
+ .filter(Boolean)
86
+ .join(" ")}
87
+ data-footer="footer13"
88
+ >
89
+ <div className="mx-auto flex w-full max-w-[1920px] flex-col gap-5 px-4 pb-5 pt-12 sm:px-6 md:px-10 lg:px-20 xl:px-[120px] xl:pt-20">
90
+ <div className="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
91
+ <SafeImage
92
+ src={logoSrc}
93
+ alt={logoAlt}
94
+ width={195}
95
+ height={70}
96
+ className="h-12 w-auto max-w-[195px] sm:h-14 md:h-[70px]"
97
+ />
98
+ <div className="flex items-center gap-1">
99
+ <span className="text-sm tracking-[0.42px] text-[rgba(10,20,47,0.8)]">{followLabel}</span>
100
+ <div className="flex items-center">
101
+ {socialLinks.map((social) => {
102
+ const Icon = SOCIAL_ICONS[social.icon] ?? FacebookIcon;
103
+ return (
104
+ <a
105
+ key={social.label}
106
+ href={social.href ?? "#"}
107
+ aria-label={social.label}
108
+ className="rounded-full p-2.5 text-[#0a142f] transition-colors duration-200 ease-out hover:opacity-70 focus-visible:outline-2 focus-visible:outline-offset-2"
109
+ >
110
+ <Icon className="size-[18px]" />
111
+ </a>
112
+ );
113
+ })}
114
+ </div>
115
+ </div>
116
+ </div>
117
+
118
+ <div className="flex flex-col border-y border-[rgba(10,20,47,0.1)] lg:flex-row lg:items-stretch">
119
+ <div className="flex flex-col gap-10 px-4 py-10 lg:w-[541px] lg:shrink-0 lg:px-8 lg:py-12">
120
+ <p className="whitespace-pre-line text-lg font-medium leading-8 text-[rgba(10,20,47,0.8)]">
121
+ {contact.address}
122
+ </p>
123
+ <div className="flex flex-col gap-4">
124
+ <a
125
+ href={`tel:${contact.phone?.replace(/\s/g, "")}`}
126
+ className="text-2xl font-semibold leading-[42px] text-[#087cb9] transition-opacity duration-200 ease-out hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 sm:text-[32px]"
127
+ >
128
+ {contact.phone}
129
+ </a>
130
+ <a
131
+ href={`mailto:${contact.email}`}
132
+ className="text-lg font-medium leading-8 text-[rgba(10,20,47,0.8)] transition-colors duration-200 ease-out hover:text-[#0a142f] focus-visible:outline-2 focus-visible:outline-offset-2"
133
+ >
134
+ {contact.email}
135
+ </a>
136
+ </div>
137
+ </div>
138
+ {linkColumns.map((column, index) => (
139
+ <div key={column.title} className="flex min-w-0 flex-1 lg:flex-row">
140
+ {index > 0 ? (
141
+ <div className="hidden w-px shrink-0 bg-[rgba(10,20,47,0.1)] lg:block" aria-hidden="true" />
142
+ ) : null}
143
+ <LinkColumn column={column} />
144
+ </div>
145
+ ))}
146
+ </div>
147
+
148
+ <p className="py-4 text-center text-base leading-6 text-[rgba(10,20,47,0.8)]">{copyright}</p>
149
+ </div>
150
+ </footer>
151
+ );
152
+ }
153
+
154
+ Footer13.propTypes = footer13PropTypes;
155
+
156
+ export default Footer13;
@@ -0,0 +1,80 @@
1
+ import PropTypes from "prop-types";
2
+
3
+ const linkShape = PropTypes.shape({
4
+ label: PropTypes.string.isRequired,
5
+ href: PropTypes.string,
6
+ });
7
+
8
+ const columnShape = PropTypes.shape({
9
+ title: PropTypes.string.isRequired,
10
+ links: PropTypes.arrayOf(linkShape),
11
+ });
12
+
13
+ const socialShape = PropTypes.shape({
14
+ label: PropTypes.string.isRequired,
15
+ href: PropTypes.string,
16
+ icon: PropTypes.oneOf(["facebook", "linkedin", "twitter", "youtube"]),
17
+ });
18
+
19
+ const contactShape = PropTypes.shape({
20
+ address: PropTypes.string,
21
+ phone: PropTypes.string,
22
+ email: PropTypes.string,
23
+ });
24
+
25
+ export const footer13PropTypes = {
26
+ logoSrc: PropTypes.string,
27
+ logoAlt: PropTypes.string,
28
+ followLabel: PropTypes.string,
29
+ socialLinks: PropTypes.arrayOf(socialShape),
30
+ contact: PropTypes.shape(contactShape),
31
+ linkColumns: PropTypes.arrayOf(columnShape),
32
+ copyright: PropTypes.string,
33
+ className: PropTypes.string,
34
+ };
35
+
36
+ export const footer13DefaultProps = {
37
+ logoSrc: "/footer/footer13/logo.png",
38
+ logoAlt: "ASBE",
39
+ followLabel: "Follow us",
40
+ socialLinks: [
41
+ { label: "Facebook", href: "#", icon: "facebook" },
42
+ { label: "LinkedIn", href: "#", icon: "linkedin" },
43
+ { label: "Twitter", href: "#", icon: "twitter" },
44
+ { label: "YouTube", href: "#", icon: "youtube" },
45
+ ],
46
+ contact: {
47
+ address: "339 Meadowlands Drive East, Ottawa,\nOntario K2E 7B4, Canada",
48
+ phone: "+48 690 610 479",
49
+ email: "info@asbe.com",
50
+ },
51
+ linkColumns: [
52
+ {
53
+ title: "Quick Links",
54
+ links: [
55
+ { label: "About Us", href: "#" },
56
+ { label: "Services", href: "#" },
57
+ { label: "Products", href: "#" },
58
+ { label: "Contact Us", href: "#" },
59
+ ],
60
+ },
61
+ {
62
+ title: "Services",
63
+ links: [
64
+ { label: "Installation", href: "#" },
65
+ { label: "Maintenance", href: "#" },
66
+ { label: "Consultation", href: "#" },
67
+ ],
68
+ },
69
+ {
70
+ title: "Legal Regulations",
71
+ links: [
72
+ { label: "Privacy Policy", href: "#" },
73
+ { label: "Cookie Policy", href: "#" },
74
+ { label: "Terms of Service", href: "#" },
75
+ ],
76
+ },
77
+ ],
78
+ copyright: "Ⓒ 2025 ASBE. All rights reserved.",
79
+ className: "",
80
+ };
@@ -0,0 +1 @@
1
+ export { Footer13, default } from "./Footer13";
@@ -0,0 +1,157 @@
1
+ "use client";
2
+
3
+ import { SafeImage } from "../../atoms/SafeImage";
4
+ import {
5
+ footer14DefaultProps,
6
+ footer14PropTypes,
7
+ } from "./Footer14.propTypes";
8
+
9
+ function FacebookIcon({ className = "" }) {
10
+ return (
11
+ <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className={className}>
12
+ <path d="M20 10a10 10 0 10-11.6 9.9v-7H6.1V10h2.3V7.9c0-2.3 1.4-3.6 3.5-3.6 1 0 2 .2 2 .2v2.2h-1.1c-1.1 0-1.5.7-1.5 1.4V10h2.6l-.4 2.9h-2.2v7A10 10 0 0020 10z" />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ function TwitterIcon({ className = "" }) {
18
+ return (
19
+ <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className={className}>
20
+ <path d="M15.8 2h2.8l-6 6.9L19.4 18h-5.4l-4.2-5.6-4.9 5.6H2.6l6.4-7.3L2.2 2h5.6l3.8 5L15.8 2zm-1 14.1h1.5L5.7 3.7H4.1l10.7 12.4z" />
21
+ </svg>
22
+ );
23
+ }
24
+
25
+ function LinkedinIcon({ className = "" }) {
26
+ return (
27
+ <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className={className}>
28
+ <path d="M4.5 2.5a2.5 2.5 0 100 5 2.5 2.5 0 000-5zM2.5 8.5h4v11h-4v-11zm6 0h3.8v1.5c.7-1.1 2-1.9 3.5-1.9 3.8 0 4.5 2.5 4.5 5.7V19.5h-4.5v-6.2c0-1.5 0-3.3-2-3.3s-2.3 1.6-2.3 3.1v6.4H8.5V8.5z" />
29
+ </svg>
30
+ );
31
+ }
32
+
33
+ function YoutubeIcon({ className = "" }) {
34
+ return (
35
+ <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" className={className}>
36
+ <path d="M19.6 5.3a2.5 2.5 0 00-1.8-1.8C16 3.3 10 3.3 10 3.3s-6 0-7.8.2A2.5 2.5 0 001.4 5.3 27 27 0 000 10a27 27 0 00.4 4.7 2.5 2.5 0 001.8 1.8c1.8.2 7.8.2 7.8.2s6 0 7.8-.2a2.5 2.5 0 001.8-1.8A27 27 0 0020 10a27 27 0 00-.4-4.7zM8 12.9V7.1L13.2 10 8 12.9z" />
37
+ </svg>
38
+ );
39
+ }
40
+
41
+ const SOCIAL_ICONS = {
42
+ facebook: FacebookIcon,
43
+ twitter: TwitterIcon,
44
+ linkedin: LinkedinIcon,
45
+ youtube: YoutubeIcon,
46
+ };
47
+
48
+ /**
49
+ * Footer14 — NHCS dark school footer with contact and quick links.
50
+ * Figma node 32:20963 at 1366px (scaled to 1920).
51
+ */
52
+ export function Footer14({
53
+ logoSrc = footer14DefaultProps.logoSrc,
54
+ logoAlt = footer14DefaultProps.logoAlt,
55
+ description = footer14DefaultProps.description,
56
+ socialLinks = footer14DefaultProps.socialLinks,
57
+ addressTitle = footer14DefaultProps.addressTitle,
58
+ address = footer14DefaultProps.address,
59
+ callTitle = footer14DefaultProps.callTitle,
60
+ phoneLines = footer14DefaultProps.phoneLines,
61
+ quickLinksTitle = footer14DefaultProps.quickLinksTitle,
62
+ quickLinks = footer14DefaultProps.quickLinks,
63
+ secondaryLinks = footer14DefaultProps.secondaryLinks,
64
+ copyright = footer14DefaultProps.copyright,
65
+ className = "",
66
+ }) {
67
+ return (
68
+ <footer
69
+ className={["relative w-full overflow-hidden bg-[#001028]", className]
70
+ .filter(Boolean)
71
+ .join(" ")}
72
+ data-footer="footer14"
73
+ >
74
+ <div className="mx-auto w-full max-w-[1920px] px-4 py-12 sm:px-6 md:px-10 lg:px-16 xl:px-[112px] xl:py-[72px]">
75
+ <div className="grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-[minmax(0,283px)_minmax(0,255px)_minmax(0,168px)_minmax(0,168px)] lg:gap-10 xl:gap-[40px]">
76
+ <div className="flex flex-col gap-10">
77
+ <SafeImage
78
+ src={logoSrc}
79
+ alt={logoAlt}
80
+ width={85}
81
+ height={111}
82
+ className="h-[111px] w-auto max-w-[85px]"
83
+ />
84
+ <p className="max-w-[283px] text-lg leading-7 text-white/80">{description}</p>
85
+ <div className="flex flex-wrap gap-5">
86
+ {socialLinks.map((social) => {
87
+ const Icon = SOCIAL_ICONS[social.icon] ?? FacebookIcon;
88
+ return (
89
+ <a
90
+ key={social.label}
91
+ href={social.href ?? "#"}
92
+ aria-label={social.label}
93
+ className="flex size-9 items-center justify-center rounded-full border border-white/30 text-white transition-colors duration-200 ease-out hover:border-white hover:bg-white/10 focus-visible:outline-2 focus-visible:outline-offset-2"
94
+ >
95
+ <Icon className="size-5" />
96
+ </a>
97
+ );
98
+ })}
99
+ </div>
100
+ </div>
101
+
102
+ <div className="flex flex-col gap-10">
103
+ <div className="flex flex-col gap-2.5">
104
+ <p className="font-serif text-2xl leading-9 text-white">{addressTitle}</p>
105
+ <p className="max-w-[255px] text-lg leading-7 text-white/80">{address}</p>
106
+ </div>
107
+ <div className="flex flex-col gap-2.5">
108
+ <p className="font-serif text-2xl leading-9 text-white">{callTitle}</p>
109
+ <div className="text-lg leading-7 text-white/80">
110
+ {phoneLines.map((line) => (
111
+ <p key={line}>{line}</p>
112
+ ))}
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <div className="flex flex-col gap-2.5">
118
+ <p className="font-serif text-2xl leading-9 text-white">{quickLinksTitle}</p>
119
+ <ul className="flex flex-col gap-[15px]">
120
+ {quickLinks.map((link) => (
121
+ <li key={link.label}>
122
+ <a
123
+ href={link.href ?? "#"}
124
+ className="text-lg leading-7 text-white/80 transition-colors duration-200 ease-out hover:text-white focus-visible:outline-2 focus-visible:outline-offset-2"
125
+ >
126
+ {link.label}
127
+ </a>
128
+ </li>
129
+ ))}
130
+ </ul>
131
+ </div>
132
+
133
+ <ul className="flex flex-col gap-[15px] pt-0 lg:pt-[46px]">
134
+ {secondaryLinks.map((link) => (
135
+ <li key={link.label}>
136
+ <a
137
+ href={link.href ?? "#"}
138
+ className="text-lg leading-7 text-white/80 transition-colors duration-200 ease-out hover:text-white focus-visible:outline-2 focus-visible:outline-offset-2"
139
+ >
140
+ {link.label}
141
+ </a>
142
+ </li>
143
+ ))}
144
+ </ul>
145
+ </div>
146
+
147
+ <div className="mt-12 border-t border-white/10 pt-8 xl:mt-16">
148
+ <p className="text-center text-base leading-7 text-white/80">{copyright}</p>
149
+ </div>
150
+ </div>
151
+ </footer>
152
+ );
153
+ }
154
+
155
+ Footer14.propTypes = footer14PropTypes;
156
+
157
+ export default Footer14;
@@ -0,0 +1,60 @@
1
+ import PropTypes from "prop-types";
2
+
3
+ const linkShape = PropTypes.shape({
4
+ label: PropTypes.string.isRequired,
5
+ href: PropTypes.string,
6
+ });
7
+
8
+ const socialShape = PropTypes.shape({
9
+ label: PropTypes.string.isRequired,
10
+ href: PropTypes.string,
11
+ icon: PropTypes.oneOf(["facebook", "twitter", "linkedin", "youtube"]),
12
+ });
13
+
14
+ export const footer14PropTypes = {
15
+ logoSrc: PropTypes.string,
16
+ logoAlt: PropTypes.string,
17
+ description: PropTypes.string,
18
+ socialLinks: PropTypes.arrayOf(socialShape),
19
+ addressTitle: PropTypes.string,
20
+ address: PropTypes.string,
21
+ callTitle: PropTypes.string,
22
+ phoneLines: PropTypes.arrayOf(PropTypes.string),
23
+ quickLinksTitle: PropTypes.string,
24
+ quickLinks: PropTypes.arrayOf(linkShape),
25
+ secondaryLinks: PropTypes.arrayOf(linkShape),
26
+ copyright: PropTypes.string,
27
+ className: PropTypes.string,
28
+ };
29
+
30
+ export const footer14DefaultProps = {
31
+ logoSrc: "/footer/footer14/logo.png",
32
+ logoAlt: "NHCS",
33
+ description:
34
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque accumsan bibendum bibendum diam et.",
35
+ socialLinks: [
36
+ { label: "Facebook", href: "#", icon: "facebook" },
37
+ { label: "Twitter", href: "#", icon: "twitter" },
38
+ { label: "LinkedIn", href: "#", icon: "linkedin" },
39
+ { label: "YouTube", href: "#", icon: "youtube" },
40
+ ],
41
+ addressTitle: "Address –",
42
+ address: "33- A, South Kunipara, Middle Dhaka, 1208",
43
+ callTitle: "Call us –",
44
+ phoneLines: ["Cell: 01710-000089", "Office: 780-449-1421"],
45
+ quickLinksTitle: "Quick link –",
46
+ quickLinks: [
47
+ { label: "About NHCS", href: "#" },
48
+ { label: "Admissions", href: "#" },
49
+ { label: "Academics", href: "#" },
50
+ { label: "Life at NHCS", href: "#" },
51
+ { label: "Explore", href: "#" },
52
+ ],
53
+ secondaryLinks: [
54
+ { label: "Contact us", href: "#" },
55
+ { label: "Terms of Use", href: "#" },
56
+ { label: "Privacy Policy", href: "#" },
57
+ ],
58
+ copyright: "Copyright © 2022, New Horizon Canadian School. All rights reserved.",
59
+ className: "",
60
+ };
@@ -0,0 +1 @@
1
+ export { Footer14, default } from "./Footer14";
@@ -0,0 +1,184 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import SafeImage from "../../atoms/SafeImage";
5
+ import {
6
+ footer15DefaultProps,
7
+ footer15PropTypes,
8
+ } from "./Footer15.propTypes";
9
+
10
+ function ArrowUpRightIcon({ className = "" }) {
11
+ return (
12
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className}>
13
+ <path
14
+ d="M5 15L15 5M15 5H7M15 5v8"
15
+ stroke="currentColor"
16
+ strokeWidth="1.5"
17
+ strokeLinecap="round"
18
+ strokeLinejoin="round"
19
+ />
20
+ </svg>
21
+ );
22
+ }
23
+
24
+ function ChevronDownIcon({ className = "" }) {
25
+ return (
26
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className}>
27
+ <path d="M5 8l5 5 5-5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
28
+ </svg>
29
+ );
30
+ }
31
+
32
+ function LinkedinIcon({ className = "" }) {
33
+ return (
34
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className={className}>
35
+ <path d="M4.98 3.5C3.34 3.5 2 4.84 2 6.48s1.34 2.98 2.98 2.98 2.98-1.34 2.98-2.98S6.62 3.5 4.98 3.5zM2.4 9h5.16v14H2.4V9zm7.92 0h4.94v1.92c.7-1.2 2.4-2.1 4.94-2.1 5.28 0 6.26 3.48 6.26 8v6.18h-5.16v-5.48c0-1.3 0-3-1.84-3-2 0-2.3 1.56-2.3 3.18v5.3H10.32V9z" />
36
+ </svg>
37
+ );
38
+ }
39
+
40
+ function YoutubeIcon({ className = "" }) {
41
+ return (
42
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className={className}>
43
+ <path d="M23.5 6.2a3 3 0 00-2.1-2.1C19.6 3.5 12 3.5 12 3.5s-7.6 0-9.4.6A3 3 0 00.5 6.2 31.3 31.3 0 000 12a31.3 31.3 0 00.5 5.8 3 3 0 002.1 2.1c1.8.6 9.4.6 9.4.6s7.6 0 9.4-.6a3 3 0 002.1-2.1A31.3 31.3 0 0024 12a31.3 31.3 0 00-.5-5.8zM9.6 15.5v-7L15.8 12 9.6 15.5z" />
44
+ </svg>
45
+ );
46
+ }
47
+
48
+ function InstagramIcon({ className = "" }) {
49
+ return (
50
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className={className}>
51
+ <path d="M7 2h10a5 5 0 015 5v10a5 5 0 01-5 5H7a5 5 0 01-5-5V7a5 5 0 015-5zm5 5a5 5 0 100 10 5 5 0 000-10zm6.5-.9a1.1 1.1 0 100 2.2 1.1 1.1 0 000-2.2z" />
52
+ </svg>
53
+ );
54
+ }
55
+
56
+ const SOCIAL_ICONS = {
57
+ linkedin: LinkedinIcon,
58
+ youtube: YoutubeIcon,
59
+ instagram: InstagramIcon,
60
+ };
61
+
62
+ /**
63
+ * Footer15 — Shipora dark accordion-style nav footer with CTA.
64
+ * Figma node 32:21015 at 1920px.
65
+ */
66
+ export function Footer15({
67
+ tagline = footer15DefaultProps.tagline,
68
+ ctaLabel = footer15DefaultProps.ctaLabel,
69
+ ctaHref = footer15DefaultProps.ctaHref,
70
+ onCtaClick,
71
+ navItems = footer15DefaultProps.navItems,
72
+ socialLinks = footer15DefaultProps.socialLinks,
73
+ logoSrc = footer15DefaultProps.logoSrc,
74
+ logoAlt = footer15DefaultProps.logoAlt,
75
+ copyright = footer15DefaultProps.copyright,
76
+ legalLinks = footer15DefaultProps.legalLinks,
77
+ className = "",
78
+ }) {
79
+ const [expandedIndex, setExpandedIndex] = useState(null);
80
+
81
+ return (
82
+ <footer
83
+ className={["relative w-full overflow-hidden bg-[#13191f]", className]
84
+ .filter(Boolean)
85
+ .join(" ")}
86
+ data-footer="footer15"
87
+ >
88
+ <div className="mx-auto flex w-full max-w-[1920px] flex-col gap-10 px-4 py-16 sm:px-6 md:px-10 lg:gap-12 lg:px-20 lg:py-[140px] xl:px-[80px]">
89
+ <div className="flex flex-col gap-12 lg:flex-row lg:items-start lg:justify-between">
90
+ <div className="flex max-w-xl flex-col gap-10 lg:gap-[60px]">
91
+ <p className="font-mono text-xl font-semibold leading-relaxed text-white sm:text-2xl">
92
+ {tagline}
93
+ </p>
94
+ <a
95
+ href={ctaHref ?? "#"}
96
+ onClick={onCtaClick}
97
+ className="group flex h-16 w-full max-w-xs flex-col justify-between bg-[#ffd32d] px-4 pb-5 pt-2.5 text-[#13191f] transition-opacity duration-200 ease-out hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2"
98
+ >
99
+ <span className="flex justify-end">
100
+ <ArrowUpRightIcon className="size-5 transition-transform duration-300 ease-out group-hover:-translate-y-0.5 group-hover:translate-x-0.5 motion-reduce:transition-none" />
101
+ </span>
102
+ <span className="font-mono text-lg tracking-[-0.72px]">{ctaLabel}</span>
103
+ </a>
104
+ </div>
105
+
106
+ <nav className="w-full max-w-2xl" aria-label="Footer navigation">
107
+ {navItems.map((item, index) => (
108
+ <div
109
+ key={item.label}
110
+ className="border-b border-white/10 py-5 first:py-5 lg:py-8"
111
+ >
112
+ {item.expandable ? (
113
+ <button
114
+ type="button"
115
+ onClick={() =>
116
+ setExpandedIndex(expandedIndex === index ? null : index)
117
+ }
118
+ className="flex w-full items-center justify-between text-left font-mono text-lg tracking-[-0.72px] text-white transition-colors duration-200 ease-out hover:opacity-80 focus-visible:outline-2 focus-visible:outline-offset-2"
119
+ aria-expanded={expandedIndex === index}
120
+ >
121
+ {item.label}
122
+ <ChevronDownIcon
123
+ className={`size-5 transition-transform duration-300 ease-out motion-reduce:transition-none ${
124
+ expandedIndex === index ? "rotate-180" : ""
125
+ }`}
126
+ />
127
+ </button>
128
+ ) : (
129
+ <a
130
+ href={item.href ?? "#"}
131
+ className="block font-mono text-lg tracking-[-0.72px] text-white transition-colors duration-200 ease-out hover:opacity-80 focus-visible:outline-2 focus-visible:outline-offset-2"
132
+ >
133
+ {item.label}
134
+ </a>
135
+ )}
136
+ </div>
137
+ ))}
138
+ <div className="flex justify-end gap-3 pt-8">
139
+ {socialLinks.map((social) => {
140
+ const Icon = SOCIAL_ICONS[social.icon] ?? LinkedinIcon;
141
+ return (
142
+ <a
143
+ key={social.label}
144
+ href={social.href ?? "#"}
145
+ aria-label={social.label}
146
+ className="flex size-[34px] items-center justify-center rounded-full text-white transition-opacity duration-200 ease-out hover:opacity-70 focus-visible:outline-2 focus-visible:outline-offset-2"
147
+ >
148
+ <Icon className="size-[34px]" />
149
+ </a>
150
+ );
151
+ })}
152
+ </div>
153
+ </nav>
154
+ </div>
155
+
156
+ <div className="flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
157
+ <SafeImage
158
+ src={logoSrc}
159
+ alt={logoAlt}
160
+ width={518}
161
+ height={168}
162
+ className="h-16 w-auto max-w-[320px] sm:h-24 lg:h-[168px] lg:max-w-[518px]"
163
+ />
164
+ <div className="flex flex-col gap-4 text-sm leading-relaxed text-white/60 sm:flex-row sm:flex-wrap sm:items-center sm:gap-8">
165
+ <p>{copyright}</p>
166
+ {legalLinks.map((link) => (
167
+ <a
168
+ key={link.label}
169
+ href={link.href ?? "#"}
170
+ className="whitespace-nowrap transition-colors duration-200 ease-out hover:text-white focus-visible:outline-2 focus-visible:outline-offset-2"
171
+ >
172
+ {link.label}
173
+ </a>
174
+ ))}
175
+ </div>
176
+ </div>
177
+ </div>
178
+ </footer>
179
+ );
180
+ }
181
+
182
+ Footer15.propTypes = footer15PropTypes;
183
+
184
+ export default Footer15;