@abstraks-dev/ui-library 1.2.0 → 1.2.1

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.
@@ -16,39 +16,32 @@ afterAll(() => {
16
16
  describe('Footer', () => {
17
17
  it('renders with default props', () => {
18
18
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
19
- navigation: /*#__PURE__*/_react.default.createElement("li", null, "NavItem")
19
+ navigationOne: /*#__PURE__*/_react.default.createElement("li", null, "NavItem")
20
20
  }));
21
21
  expect(_react2.screen.getByRole('contentinfo')).toBeInTheDocument();
22
22
  expect(_react2.screen.getByText('NavItem')).toBeInTheDocument();
23
23
  });
24
- it('renders mobile navigation when width < 768', () => {
25
- // Mock useWindowSize to return mobile width
26
- jest.spyOn(require('../utils/useWindowSize'), 'useWindowSize').mockReturnValue({
27
- width: 500
28
- });
24
+ it('renders navigation sections', () => {
29
25
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
30
- navigation: /*#__PURE__*/_react.default.createElement("li", null, "MobileNav")
26
+ navigationOne: /*#__PURE__*/_react.default.createElement("li", null, "Section1"),
27
+ navigationTwo: /*#__PURE__*/_react.default.createElement("li", null, "Section2"),
28
+ navigationThree: /*#__PURE__*/_react.default.createElement("li", null, "Section3")
31
29
  }));
32
- expect(_react2.screen.getByLabelText('Mobile footer navigation')).toBeInTheDocument();
33
- expect(_react2.screen.getByText('MobileNav')).toBeInTheDocument();
30
+ expect(_react2.screen.getByText('Section1')).toBeInTheDocument();
31
+ expect(_react2.screen.getByText('Section2')).toBeInTheDocument();
32
+ expect(_react2.screen.getByText('Section3')).toBeInTheDocument();
34
33
  });
35
- it('renders desktop navigation when width >= 768', () => {
36
- jest.spyOn(require('../utils/useWindowSize'), 'useWindowSize').mockReturnValue({
37
- width: 1024
38
- });
34
+ it('renders copyright when not authenticated', () => {
39
35
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
40
- navigation: /*#__PURE__*/_react.default.createElement("li", null, "DesktopNav")
36
+ hasAuth: false
41
37
  }));
42
- expect(_react2.screen.getByLabelText('Desktop footer navigation')).toBeInTheDocument();
43
- expect(_react2.screen.getByText('DesktopNav')).toBeInTheDocument();
38
+ expect(_react2.screen.getByText( 2025 Abstraks. All rights reserved./)).toBeInTheDocument();
44
39
  });
45
- it('renders auth menu when hasAuth is true (mobile)', () => {
46
- jest.spyOn(require('../utils/useWindowSize'), 'useWindowSize').mockReturnValue({
47
- width: 500
48
- });
40
+ it('renders auth menu when hasAuth is true', () => {
41
+ const authMenuContent = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("li", null, /*#__PURE__*/_react.default.createElement("span", null, "Home")), /*#__PURE__*/_react.default.createElement("li", null, /*#__PURE__*/_react.default.createElement("span", null, "Search")), /*#__PURE__*/_react.default.createElement("li", null, /*#__PURE__*/_react.default.createElement("span", null, "Add")), /*#__PURE__*/_react.default.createElement("li", null, /*#__PURE__*/_react.default.createElement("span", null, "Profile")));
49
42
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
50
43
  hasAuth: true,
51
- navigation: null
44
+ authMenu: authMenuContent
52
45
  }));
53
46
  expect(_react2.screen.getByLabelText('User navigation')).toBeInTheDocument();
54
47
  expect(_react2.screen.getByText('Home')).toBeInTheDocument();
@@ -56,11 +49,53 @@ describe('Footer', () => {
56
49
  expect(_react2.screen.getByText('Add')).toBeInTheDocument();
57
50
  expect(_react2.screen.getByText('Profile')).toBeInTheDocument();
58
51
  });
52
+ it('applies box shadow based on auth state and screen size', () => {
53
+ // Test desktop without auth (should have box shadow)
54
+ jest.spyOn(require('../utils/useWindowSize'), 'useWindowSize').mockReturnValue({
55
+ width: 1024
56
+ });
57
+ const {
58
+ rerender
59
+ } = (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
60
+ hasAuth: false
61
+ }));
62
+ expect(_react2.screen.getByRole('contentinfo')).toHaveStyle('box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1)');
63
+
64
+ // Test mobile with auth (should have box shadow)
65
+ jest.spyOn(require('../utils/useWindowSize'), 'useWindowSize').mockReturnValue({
66
+ width: 500
67
+ });
68
+ rerender(/*#__PURE__*/_react.default.createElement(_Footer.default, {
69
+ hasAuth: true
70
+ }));
71
+ expect(_react2.screen.getByRole('contentinfo')).toHaveStyle('box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1)');
72
+ });
59
73
  it('applies custom aria-label', () => {
60
74
  (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
61
- navigation: /*#__PURE__*/_react.default.createElement("li", null, "Nav"),
75
+ navigationOne: /*#__PURE__*/_react.default.createElement("li", null, "Nav"),
62
76
  "aria-label": "Custom Footer"
63
77
  }));
64
78
  expect(_react2.screen.getByRole('contentinfo')).toHaveAttribute('aria-label', 'Custom Footer');
65
79
  });
80
+ it('renders with custom id', () => {
81
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
82
+ id: "custom-footer",
83
+ navigationOne: /*#__PURE__*/_react.default.createElement("li", null, "Nav")
84
+ }));
85
+ expect(_react2.screen.getByRole('contentinfo')).toHaveAttribute('id', 'custom-footer');
86
+ });
87
+ it('renders without copyright when hasAuth is true', () => {
88
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
89
+ hasAuth: true,
90
+ authMenu: /*#__PURE__*/_react.default.createElement("li", null, "Menu")
91
+ }));
92
+ expect(_react2.screen.queryByText(/© 2025 Abstraks. All rights reserved./)).not.toBeInTheDocument();
93
+ });
94
+ it('renders with custom className', () => {
95
+ (0, _react2.render)(/*#__PURE__*/_react.default.createElement(_Footer.default, {
96
+ className: "custom-footer",
97
+ navigationOne: /*#__PURE__*/_react.default.createElement("li", null, "Nav")
98
+ }));
99
+ expect(_react2.screen.getByRole('contentinfo')).toHaveClass('footer custom-footer');
100
+ });
66
101
  });
@@ -8,7 +8,6 @@ var _react = _interopRequireWildcard(require("react"));
8
8
  var _propTypes = require("prop-types");
9
9
  var _useWindowSize = require("../utils/useWindowSize");
10
10
  var _ssrSafeId = require("../utils/ssrSafeId");
11
- var _Animation = require("./Animation");
12
11
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
12
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
14
13
  /**
@@ -20,12 +19,15 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
20
19
  const Footer = exports.Footer = /*#__PURE__*/(0, _react.forwardRef)(({
21
20
  id,
22
21
  className = '',
23
- navigation = null,
22
+ navigationOne = null,
23
+ navigationTwo = null,
24
+ navigationThree = null,
24
25
  signInLink = null,
25
26
  signUpLink = null,
26
27
  avatarSrc = null,
27
28
  ellipsesList = [],
28
29
  hasAuth = false,
30
+ authMenu = null,
29
31
  'aria-label': ariaLabel = 'Footer',
30
32
  componentName = 'footer',
31
33
  additionalClassName = '',
@@ -35,54 +37,40 @@ const Footer = exports.Footer = /*#__PURE__*/(0, _react.forwardRef)(({
35
37
  const generateId = (0, _ssrSafeId.useStableId)('footer');
36
38
  const finalId = id || generateId();
37
39
  const finalClassName = className || additionalClassName;
40
+ const shouldApplyBoxShadow = !hasAuth && size.width > 768 || hasAuth && size.width < 768;
38
41
  return /*#__PURE__*/_react.default.createElement("footer", _extends({
39
42
  ref: ref,
40
43
  id: finalId,
41
44
  className: `${componentName} ${finalClassName}`,
42
45
  "data-testid": componentName,
43
46
  role: "contentinfo",
44
- "aria-label": ariaLabel
45
- }, restProps), /*#__PURE__*/_react.default.createElement(_Animation.AnimatedDiv, {
46
- fadingEntrances: "fadeIn",
47
- duration: "faster"
48
- }, size.width < 768 ?
49
- /*#__PURE__*/
50
- // mobile
51
- _react.default.createElement("div", {
47
+ "aria-label": ariaLabel,
48
+ style: shouldApplyBoxShadow ? {
49
+ boxShadow: '0 -2px 4px rgba(0, 0, 0, 0.1)'
50
+ } : undefined
51
+ }, restProps), /*#__PURE__*/_react.default.createElement("div", {
52
52
  className: "navigation",
53
53
  role: "navigation",
54
- "aria-label": "Mobile footer navigation"
55
- }, !hasAuth ? /*#__PURE__*/_react.default.createElement("ul", {
54
+ "aria-label": "footer navigation"
55
+ }, !hasAuth ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("ul", {
56
56
  className: "unordered-list",
57
57
  role: "list"
58
- }, navigation) : /*#__PURE__*/_react.default.createElement("ul", {
59
- className: "unordered-list auth-menu",
60
- role: "list",
61
- "aria-label": "User navigation"
62
58
  }, /*#__PURE__*/_react.default.createElement("li", {
63
- className: "list-item",
59
+ className: "list-item list-one",
64
60
  role: "listitem"
65
- }, "Home"), /*#__PURE__*/_react.default.createElement("li", {
66
- className: "list-item",
61
+ }, navigationOne), /*#__PURE__*/_react.default.createElement("li", {
62
+ className: "list-item list-two",
67
63
  role: "listitem"
68
- }, "Search"), /*#__PURE__*/_react.default.createElement("li", {
69
- className: "list-item",
64
+ }, navigationTwo), /*#__PURE__*/_react.default.createElement("li", {
65
+ className: "list-item list-three",
70
66
  role: "listitem"
71
- }, "Add"), /*#__PURE__*/_react.default.createElement("li", {
72
- className: "list-item",
73
- role: "listitem"
74
- }, "Profile"))) :
75
- /*#__PURE__*/
76
- // desktop
77
- _react.default.createElement("div", {
78
- className: "navigation",
79
- "data-testid": "navigation",
80
- role: "navigation",
81
- "aria-label": "Desktop footer navigation"
82
- }, /*#__PURE__*/_react.default.createElement("ul", {
83
- className: "unordered-list",
84
- role: "list"
85
- }, navigation))));
67
+ }, navigationThree)), /*#__PURE__*/_react.default.createElement("div", {
68
+ className: "bottom"
69
+ }, "\xA9 ", new Date().getFullYear(), " Abstraks. All rights reserved.")) : /*#__PURE__*/_react.default.createElement("ul", {
70
+ className: "unordered-list auth-menu",
71
+ role: "list",
72
+ "aria-label": "User navigation"
73
+ }, authMenu)));
86
74
  });
87
75
  Footer.displayName = 'Footer';
88
76
  Footer.propTypes = {
@@ -208,7 +208,7 @@ const Header = exports.Header = /*#__PURE__*/(0, _react.forwardRef)(({
208
208
  onKeyDown: handleKeyDown
209
209
  }, restProps), /*#__PURE__*/_react.default.createElement("div", {
210
210
  className: isMobile ? 'container-mobile' : 'container-large'
211
- }, logo, hasAuth && search, isMobile ? /*#__PURE__*/_react.default.createElement(_SideMenu.SideMenu, {
211
+ }, logo, hasAuth && !isMobile && search, isMobile ? /*#__PURE__*/_react.default.createElement(_SideMenu.SideMenu, {
212
212
  direction: "vertical",
213
213
  position: "top",
214
214
  isOpen: isMenuOpen,
@@ -84,6 +84,34 @@ const SideMenu = exports.SideMenu = /*#__PURE__*/(0, _react.forwardRef)(({
84
84
  const generateId = (0, _ssrSafeId.useStableId)('side-menu');
85
85
  const finalId = id || testId || generateId();
86
86
  const finalClassName = className || additionalClassName;
87
+
88
+ // Handle body scroll locking when menu is open
89
+ (0, _react.useEffect)(() => {
90
+ if (typeof document !== 'undefined') {
91
+ if (isOpen) {
92
+ // Store current scroll position
93
+ const scrollY = window.scrollY;
94
+ document.body.style.top = `-${scrollY}px`;
95
+ document.body.classList.add('side-menu-open');
96
+ } else {
97
+ document.body.classList.remove('side-menu-open');
98
+ // Restore scroll position
99
+ const scrollY = document.body.style.top;
100
+ document.body.style.top = '';
101
+ if (scrollY) {
102
+ window.scrollTo(0, parseInt(scrollY || '0', 10));
103
+ }
104
+ }
105
+ }
106
+
107
+ // Cleanup function
108
+ return () => {
109
+ if (typeof document !== 'undefined') {
110
+ document.body.classList.remove('side-menu-open');
111
+ document.body.style.top = '';
112
+ }
113
+ };
114
+ }, [isOpen]);
87
115
  const handleToggle = (0, _react.useCallback)(() => {
88
116
  if (isOpen) {
89
117
  onClose();
@@ -115,51 +115,143 @@
115
115
  align-items: center;
116
116
  flex-direction: row;
117
117
  padding: 15px;
118
+ /* Absolute positioning - reliably pins footer to bottom */
119
+ position: absolute;
120
+ bottom: 0;
121
+ left: 0;
122
+ right: 0;
123
+ background: #fff;
124
+ box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
125
+ /* Ensure footer stays above other content */
126
+ z-index: 200;
118
127
  }
119
- @media (min-width: 768px) {
120
- .footer .navigation {
121
- display: inline-flex;
122
- justify-content: flex-end;
123
- }
128
+ .footer .navigation {
129
+ display: flex;
130
+ flex-direction: column;
124
131
  }
125
132
  .footer .navigation .unordered-list {
126
133
  display: flex;
127
134
  flex-direction: column;
135
+ width: 100%;
128
136
  }
129
137
  @media (min-width: 768px) {
130
138
  .footer .navigation .unordered-list {
131
- display: inline-flex;
139
+ display: flex;
132
140
  flex-direction: row;
133
- align-items: center;
134
- justify-content: flex-end;
135
- padding: 0;
136
- margin: 0;
141
+ justify-content: space-between;
142
+ width: 50%;
137
143
  }
138
144
  }
139
145
  .footer .navigation .unordered-list.auth-menu {
140
- border-top: 1px solid #e9ecef;
141
146
  flex-direction: row;
142
147
  justify-content: space-between;
148
+ display: flex;
149
+ }
150
+ @media (min-width: 768px) {
151
+ .footer .navigation .unordered-list.auth-menu {
152
+ display: none;
153
+ }
143
154
  }
144
155
  .footer .navigation .unordered-list.auth-menu .list-item {
145
156
  display: inline-flex;
146
- /* width: 25%; */
157
+ align-items: center;
158
+ }
159
+ @media (min-width: 768px) {
160
+ .footer .navigation .unordered-list.auth-menu .list-item {
161
+ width: 33.3%;
162
+ }
163
+ }
164
+ .footer .navigation .bottom {
165
+ display: flex;
166
+ flex-direction: row;
167
+ margin-top: 10px;
168
+ font-size: 0.875rem;
169
+ color: #6c757d;
147
170
  }
148
- .footer .navigation .unordered-list .list-item {
171
+ .footer .navigation .list-item {
149
172
  display: flex;
150
173
  }
151
174
  @media (min-width: 768px) {
152
- .footer .navigation .unordered-list .list-item {
175
+ .footer .navigation .list-item {
153
176
  display: inline-flex;
154
- padding: 0 15px;
177
+ padding: 10px 0;
155
178
  }
156
179
  }
157
- .footer .navigation .anchor {
180
+ .footer .anchor {
158
181
  color: #6c757d;
159
182
  text-decoration: none;
160
183
  }
161
- .footer .navigation .anchor:hover {
184
+ .footer .anchor:hover {
162
185
  color: #333;
163
186
  }
164
187
 
188
+ /* Global styles to support absolute positioned footer */
189
+ /*
190
+ * Usage: Apply these classes to your layout:
191
+ * - Add 'sticky-footer-layout' class to your main container
192
+ * - Add 'main-content' class to your content wrapper
193
+ * - The footer will be absolutely positioned at the bottom
194
+ */
195
+ html.sticky-footer-layout,
196
+ body.sticky-footer-layout {
197
+ height: 100%;
198
+ margin: 0;
199
+ padding: 0;
200
+ /* Force proper height on mobile */
201
+ }
202
+ @media (max-width: 767px) {
203
+ html.sticky-footer-layout,
204
+ body.sticky-footer-layout {
205
+ height: 100%;
206
+ min-height: 100vh;
207
+ margin: 0;
208
+ padding: 0;
209
+ }
210
+ }
211
+
212
+ .sticky-footer-layout {
213
+ position: relative;
214
+ min-height: 100vh;
215
+ margin: 0;
216
+ padding: 0;
217
+ /* Add bottom padding to prevent content hiding behind footer */
218
+ padding-bottom: 80px; /* Adjust based on footer height */
219
+ /* Ensure layout works on all screen sizes */
220
+ /* Footer positioning within the layout */
221
+ }
222
+ @media (max-width: 767px) {
223
+ .sticky-footer-layout {
224
+ position: relative;
225
+ min-height: 100vh;
226
+ padding-bottom: 100px; /* Slightly more padding on mobile */
227
+ }
228
+ }
229
+ .sticky-footer-layout .main-content {
230
+ width: 100%;
231
+ padding-bottom: 20px; /* Additional content padding */
232
+ /* Mobile-specific main content */
233
+ }
234
+ @media (max-width: 767px) {
235
+ .sticky-footer-layout .main-content {
236
+ width: 100%;
237
+ padding-bottom: 20px;
238
+ }
239
+ }
240
+ .sticky-footer-layout .footer {
241
+ position: absolute;
242
+ bottom: 0;
243
+ left: 0;
244
+ right: 0;
245
+ /* Mobile-specific footer */
246
+ }
247
+ @media (max-width: 767px) {
248
+ .sticky-footer-layout .footer {
249
+ position: absolute;
250
+ bottom: 0;
251
+ left: 0;
252
+ right: 0;
253
+ width: 100%;
254
+ }
255
+ }
256
+
165
257
  /*# sourceMappingURL=footer.css.map */
@@ -1 +1 @@
1
- {"version":3,"sourceRoot":"","sources":["../../src/styles/_variables.scss","../../src/styles/footer.scss"],"names":[],"mappings":"AAgSA;EAJE;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;;;AC1RF;EACC;EACA;EACA;EACA;;AAGC;EADD;IAEE;IACA;;;AAGD;EACC;EACA;;AAEA;EAJD;IAKE;IACA;IACA;IACA;IACA;IACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC;AACA;;AAIF;EACC;;AAEA;EAHD;IAIE;IACA;;;AAKH;EACC,ODwCQ;ECvCR;;AAEA;EACC,OD1Bc","file":"footer.css"}
1
+ {"version":3,"sourceRoot":"","sources":["../../src/styles/_variables.scss","../../src/styles/footer.scss"],"names":[],"mappings":"AAgSA;EAJE;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;;;AC1RF;EACC;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA,SDyDgB;;ACvDhB;EACC;EACA;;AAEA;EACC;EACA;EACA;;AAEA;EALD;IAME;IACA;IACA;IACA;;;AAGD;EACC;EACA;EACA;;AAEA;EALD;IAME;;;AAGD;EACC;EACA;;AAEA;EAJD;IAKE;;;AAMJ;EACC;EACA;EACA;EACA;EACA,OD6BQ;;AC1BT;EACC;;AAEA;EAHD;IAIE;IACA;;;AAKH;EACC,ODeS;ECdT;;AAEA;EACC,ODnDe;;;ACwDlB;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA;AAAA;EAEC;EACA;EACA;AAEA;;AACA;EAPD;AAAA;IAQE;IACA;IACA;IACA;;;;AAIF;EACC;EACA;EACA;EACA;AAEA;EACA;AAEA;AAkBA;;AAjBA;EAVD;IAWE;IACA;IACA;;;AAGD;EACC;EACA;AAEA;;AACA;EALD;IAME;IACA;;;AAKF;EACC;EACA;EACA;EACA;AAEA;;AACA;EAPD;IAQE;IACA;IACA;IACA;IACA","file":"footer.css"}
@@ -6,53 +6,144 @@
6
6
  flex-direction: row;
7
7
  padding: 15px;
8
8
 
9
+ /* Absolute positioning - reliably pins footer to bottom */
10
+ position: absolute;
11
+ bottom: 0;
12
+ left: 0;
13
+ right: 0;
14
+ background: #fff;
15
+ box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
16
+
17
+ /* Ensure footer stays above other content */
18
+ z-index: $z-index-sticky;
19
+
9
20
  .navigation {
10
- @media (min-width: 768px) {
11
- display: inline-flex;
12
- justify-content: flex-end;
13
- }
21
+ display: flex;
22
+ flex-direction: column;
14
23
 
15
24
  .unordered-list {
16
25
  display: flex;
17
26
  flex-direction: column;
27
+ width: 100%;
18
28
 
19
29
  @media (min-width: 768px) {
20
- display: inline-flex;
30
+ display: flex;
21
31
  flex-direction: row;
22
- align-items: center;
23
- justify-content: flex-end;
24
- padding: 0;
25
- margin: 0;
32
+ justify-content: space-between;
33
+ width: 50%;
26
34
  }
27
35
 
28
36
  &.auth-menu {
29
- border-top: 1px solid $gray-200;
30
37
  flex-direction: row;
31
38
  justify-content: space-between;
32
-
33
- .list-item {
34
- display: inline-flex;
35
- /* width: 25%; */
36
- }
37
- }
38
-
39
- .list-item {
40
39
  display: flex;
41
40
 
42
41
  @media (min-width: 768px) {
42
+ display: none;
43
+ }
44
+
45
+ .list-item {
43
46
  display: inline-flex;
44
- padding: 0 15px;
47
+ align-items: center;
48
+
49
+ @media (min-width: 768px) {
50
+ width: 33.3%;
51
+ }
45
52
  }
46
53
  }
47
54
  }
48
55
 
49
- .anchor {
56
+ .bottom {
57
+ display: flex;
58
+ flex-direction: row;
59
+ margin-top: 10px;
60
+ font-size: 0.875rem;
50
61
  color: $gray-600;
51
- text-decoration: none;
62
+ }
63
+
64
+ .list-item {
65
+ display: flex;
52
66
 
53
- &:hover {
54
- color: $color-font-body;
67
+ @media (min-width: 768px) {
68
+ display: inline-flex;
69
+ padding: 10px 0;
55
70
  }
56
71
  }
57
72
  }
73
+
74
+ .anchor {
75
+ color: $gray-600;
76
+ text-decoration: none;
77
+
78
+ &:hover {
79
+ color: $color-font-body;
80
+ }
81
+ }
82
+ }
83
+
84
+ /* Global styles to support absolute positioned footer */
85
+ /*
86
+ * Usage: Apply these classes to your layout:
87
+ * - Add 'sticky-footer-layout' class to your main container
88
+ * - Add 'main-content' class to your content wrapper
89
+ * - The footer will be absolutely positioned at the bottom
90
+ */
91
+ html.sticky-footer-layout,
92
+ body.sticky-footer-layout {
93
+ height: 100%;
94
+ margin: 0;
95
+ padding: 0;
96
+
97
+ /* Force proper height on mobile */
98
+ @media (max-width: 767px) {
99
+ height: 100%;
100
+ min-height: 100vh;
101
+ margin: 0;
102
+ padding: 0;
103
+ }
104
+ }
105
+
106
+ .sticky-footer-layout {
107
+ position: relative;
108
+ min-height: 100vh;
109
+ margin: 0;
110
+ padding: 0;
111
+
112
+ /* Add bottom padding to prevent content hiding behind footer */
113
+ padding-bottom: 80px; /* Adjust based on footer height */
114
+
115
+ /* Ensure layout works on all screen sizes */
116
+ @media (max-width: 767px) {
117
+ position: relative;
118
+ min-height: 100vh;
119
+ padding-bottom: 100px; /* Slightly more padding on mobile */
120
+ }
121
+
122
+ .main-content {
123
+ width: 100%;
124
+ padding-bottom: 20px; /* Additional content padding */
125
+
126
+ /* Mobile-specific main content */
127
+ @media (max-width: 767px) {
128
+ width: 100%;
129
+ padding-bottom: 20px;
130
+ }
131
+ }
132
+
133
+ /* Footer positioning within the layout */
134
+ .footer {
135
+ position: absolute;
136
+ bottom: 0;
137
+ left: 0;
138
+ right: 0;
139
+
140
+ /* Mobile-specific footer */
141
+ @media (max-width: 767px) {
142
+ position: absolute;
143
+ bottom: 0;
144
+ left: 0;
145
+ right: 0;
146
+ width: 100%;
147
+ }
148
+ }
58
149
  }
@@ -139,8 +139,10 @@
139
139
  flex-direction: row;
140
140
  align-items: center;
141
141
  }
142
- .header .container-large .navigation {
143
- margin-left: auto;
142
+ @media (min-width: 768px) {
143
+ .header .container-large {
144
+ justify-content: space-between;
145
+ }
144
146
  }
145
147
  .header .container-mobile {
146
148
  width: 100%;
@@ -287,7 +289,6 @@
287
289
  @media (min-width: 768px) {
288
290
  .header .navigation .unordered-list .list-item {
289
291
  display: inline-flex;
290
- padding: 0 15px;
291
292
  }
292
293
  }
293
294
  .header .navigation .unordered-list .list-item [role=menuitem] {
@@ -1 +1 @@
1
- {"version":3,"sourceRoot":"","sources":["../../src/styles/_variables.scss","../../src/styles/header.scss"],"names":[],"mappings":"AAgSA;EAJE;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;;;AC1RF;EACQ;EACA;EACA,SDqES;ECpET;EACA;EACA;EACA;EACA;EACA,kBDcM;ECbN;EACA;;AAGP;EACC;EACA;;AAID;EApBD;IAqBE;IACA;;;AAGD;EACC;EACA;EACA;EACA;;AAEA;EACC;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;;AAEA;EACC,kBDxBU;;AC4BV;EACC;EACA;EACA;EACA,kBDhCS;;ACkCT;EACC;;AAIF;EACC,kBDxCS;;AC+Cb;AAAA;EAEC;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACC;;AAGD;AAAA;EACC;EACA;;AAKD;EADD;IAEE;IACA;;;AAGD;EACC;EACA;;AAEA;EAJD;IAKE;IACA;IACA;IACA;IACA;IACA;;;AAGD;EACC;;AAEA;EAHD;IAIE;IACA;;;AAIA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA,SDtEY;ECuEZ;EACA;EACA;EACA;EACA;EACA;EACA;EAGA,kBDhIO;ECiIP;EACA;EACA;EACA;EACA;;AAIA;EArBD;IAsBE;IACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA,ODzJQ;EC0JR;EACA;;AAEA;EAEC;EACA,ODpLK;;ACuLN;EACC;EACA;;AAUR;EACC;;AAEA;EAHD;IAIE;IACA;;;AAID;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAMJ;EACC,ODzIQ;EC0IR;EACA;;AAEA;EACC,OD5Mc;;AC+Mf;EACC,ODpOY;ECqOZ;EACA;;AAID;EACC;IAEC,OD1NS;IC2NT;;;AAOJ;EA1PD;IA2PE;;EAEA;IACC;IACA;;;AAIF;EAnQD;IAoQE;;EAEA;IACC;;;AAKF;EACC;AAAA;AAAA;IAGC;IACA;IACA;;;;AAKH;EACQ;EACA","file":"header.css"}
1
+ {"version":3,"sourceRoot":"","sources":["../../src/styles/_variables.scss","../../src/styles/header.scss"],"names":[],"mappings":"AAgSA;EAJE;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;;;AC1RF;EACC;EACA;EACA,SDqEgB;ECpEhB;EACA;EACA;EACA;EACA;EACA,kBDca;ECbb;EACA;;AAGA;EACC;EACA;;AAID;EApBD;IAqBE;IACA;;;AAGD;EACC;EACA;EACA;EACA;;AAEA;EAND;IAOE;;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;;AAEA;EACC,kBDxBU;;AC4BV;EACC;EACA;EACA;EACA,kBDhCS;;ACkCT;EACC;;AAIF;EACC,kBDxCS;;AC+Cb;AAAA;EAEC;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;EACC;;AAGD;AAAA;EACC;EACA;;AAKD;EADD;IAEE;IACA;;;AAGD;EACC;EACA;;AAEA;EAJD;IAKE;IACA;IACA;IACA;IACA;IACA;;;AAGD;EACC;;AAEA;EAHD;IAIE;IACA;;;AAIA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA,SDtEY;ECuEZ;EACA;EACA;EACA;EACA;EACA;EACA;EAGA,kBDhIO;ECiIP;EACA;EACA;EACA;EACA;;AAIA;EArBD;IAsBE;IACA;;;AAGD;EACC;EACA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA,ODzJQ;EC0JR;EACA;;AAEA;EAEC;EACA,ODpLK;;ACuLN;EACC;EACA;;AAUR;EACC;;AAEA;EAHD;IAIE;;;AAID;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAMJ;EACC,ODxIQ;ECyIR;EACA;;AAEA;EACC,OD3Mc;;AC8Mf;EACC,ODnOY;ECoOZ;EACA;;AAID;EACC;IAEC,ODzNS;IC0NT;;;AAOJ;EAzPD;IA0PE;;EAEA;IACC;IACA;;;AAIF;EAlQD;IAmQE;;EAEA;IACC;;;AAKF;EACC;AAAA;AAAA;IAGC;IACA;IACA;;;;AAKH;EACC;EACA","file":"header.css"}