@abstraks-dev/ui-library 1.2.0 → 1.2.2

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.
package/README.md CHANGED
@@ -57,6 +57,12 @@ npm run build-css:watch
57
57
  npm run build-css:compressed
58
58
  ```
59
59
 
60
+ ## Production Storybook
61
+
62
+ You can view the latest production build of the UI Library Storybook here:
63
+
64
+ [UI-Library Site](https://main.ddpmt4w9k9d7q.amplifyapp.com/?path=/docs/abstraks-ui-library--docs)
65
+
60
66
  ## Deployment
61
67
 
62
68
  This library supports both manual and automated deployment to npm using GitHub Actions.
@@ -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();
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ var _exportNames = {
17
17
  DragAndDrop: true,
18
18
  Error: true,
19
19
  FileInput: true,
20
+ Footer: true,
20
21
  Form: true,
21
22
  Header: true,
22
23
  Heading: true,
@@ -228,6 +229,12 @@ Object.defineProperty(exports, "Filter", {
228
229
  return _Filter.Filter;
229
230
  }
230
231
  });
232
+ Object.defineProperty(exports, "Footer", {
233
+ enumerable: true,
234
+ get: function () {
235
+ return _Footer.Footer;
236
+ }
237
+ });
231
238
  Object.defineProperty(exports, "Form", {
232
239
  enumerable: true,
233
240
  get: function () {
@@ -427,6 +434,7 @@ var _Crud = _interopRequireDefault(require("./components/Crud"));
427
434
  var _DragAndDrop = _interopRequireDefault(require("./components/DragAndDrop"));
428
435
  var _Error = _interopRequireDefault(require("./components/Error"));
429
436
  var _FileInput = _interopRequireDefault(require("./components/FileInput"));
437
+ var _Footer = require("./components/Footer");
430
438
  var _Form = _interopRequireDefault(require("./components/Form"));
431
439
  var _Header = _interopRequireDefault(require("./components/Header"));
432
440
  var _Heading = _interopRequireDefault(require("./components/Heading"));
@@ -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"}