@rhavenside/baseline-ui 1.0.15 → 1.0.17

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.
@@ -0,0 +1,273 @@
1
+ // ============================================================================
2
+ // Sidebar/Offcanvas Component (Technical Glass Design)
3
+ // ============================================================================
4
+
5
+ @use '../tokens/tokens' as *;
6
+
7
+ // Sidebar Toggle Button (Hamburger)
8
+ .bl-sidebar-toggle {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ padding: var(--spacing-sm);
13
+ background: var(--glass-bg-medium);
14
+ backdrop-filter: blur(var(--glass-blur-md));
15
+ -webkit-backdrop-filter: blur(var(--glass-blur-md));
16
+ border: 1px solid var(--glass-border-medium);
17
+ border-radius: var(--tech-border-radius-sm);
18
+ color: var(--color-text);
19
+ cursor: pointer;
20
+ transition: var(--transition-base);
21
+
22
+ &:hover {
23
+ background: var(--glass-bg-heavy);
24
+ border-color: var(--glass-border-heavy);
25
+ }
26
+
27
+ &:focus {
28
+ outline: 1px solid var(--color-border-focus);
29
+ outline-offset: 2px;
30
+ }
31
+
32
+ // Hide on desktop (≥768px)
33
+ @media (min-width: 768px) {
34
+ display: none;
35
+ }
36
+ }
37
+
38
+ // Sidebar Container
39
+ .bl-sidebar {
40
+ position: fixed;
41
+ top: 0;
42
+ left: 0;
43
+ z-index: var(--z-index-sidebar);
44
+ width: 280px;
45
+ height: 100vh;
46
+ display: flex;
47
+ flex-direction: column;
48
+ background: var(--glass-bg-medium);
49
+ backdrop-filter: blur(var(--glass-blur-lg));
50
+ -webkit-backdrop-filter: blur(var(--glass-blur-lg));
51
+ border-right: 1px solid var(--glass-border-medium);
52
+ box-shadow: var(--glass-shadow-lg);
53
+ transform: translateX(-100%);
54
+ transition: transform var(--transition-duration-slow) var(--transition-ease-out);
55
+ overflow-y: auto;
56
+ overflow-x: hidden;
57
+
58
+ // Desktop: Always visible, collapsible
59
+ @media (min-width: 768px) {
60
+ position: relative;
61
+ transform: translateX(0);
62
+ height: auto;
63
+ min-height: 100vh;
64
+ box-shadow: none;
65
+ border-right: 1px solid var(--glass-border-medium);
66
+ }
67
+
68
+ // Sidebar Open (Mobile)
69
+ &.bl-sidebar-open {
70
+ transform: translateX(0);
71
+ }
72
+
73
+ // Sidebar Collapsed (Desktop)
74
+ &.bl-sidebar-collapsed {
75
+ @media (min-width: 768px) {
76
+ width: 64px;
77
+
78
+ .bl-sidebar-header h3,
79
+ .bl-sidebar-nav .bl-nav-link span:not(.bl-icon) {
80
+ opacity: 0;
81
+ width: 0;
82
+ overflow: hidden;
83
+ }
84
+ }
85
+ }
86
+
87
+ // Sidebar Right Position
88
+ &.bl-sidebar-right {
89
+ left: auto;
90
+ right: 0;
91
+ border-right: none;
92
+ border-left: 1px solid var(--glass-border-medium);
93
+ transform: translateX(100%);
94
+
95
+ &.bl-sidebar-open {
96
+ transform: translateX(0);
97
+ }
98
+ }
99
+ }
100
+
101
+ // Sidebar Backdrop (Mobile only)
102
+ .bl-sidebar-backdrop {
103
+ position: fixed;
104
+ top: 0;
105
+ left: 0;
106
+ right: 0;
107
+ bottom: 0;
108
+ z-index: var(--z-index-sidebar-backdrop);
109
+ background: var(--color-bg-overlay);
110
+ backdrop-filter: blur(var(--glass-blur-sm));
111
+ -webkit-backdrop-filter: blur(var(--glass-blur-sm));
112
+ opacity: 0;
113
+ pointer-events: none;
114
+ transition: opacity var(--transition-duration-slow) var(--transition-ease-out);
115
+
116
+ // Show backdrop when sidebar is open (mobile)
117
+ .bl-sidebar.bl-sidebar-open ~ &,
118
+ body:has(.bl-sidebar.bl-sidebar-open) & {
119
+ @media (max-width: 767.98px) {
120
+ opacity: 1;
121
+ pointer-events: all;
122
+ }
123
+ }
124
+
125
+ // Hide on desktop
126
+ @media (min-width: 768px) {
127
+ display: none;
128
+ }
129
+ }
130
+
131
+ // Sidebar Header
132
+ .bl-sidebar-header {
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: space-between;
136
+ padding: var(--spacing-md);
137
+ border-bottom: 1px solid var(--glass-border-light);
138
+ background: var(--glass-bg-light);
139
+ backdrop-filter: blur(var(--glass-blur-sm));
140
+ -webkit-backdrop-filter: blur(var(--glass-blur-sm));
141
+
142
+ h3 {
143
+ margin: 0;
144
+ font-size: var(--font-size-lg);
145
+ font-weight: var(--font-weight-semibold);
146
+ color: var(--color-text);
147
+ transition: var(--transition-base);
148
+ }
149
+ }
150
+
151
+ // Sidebar Close Button
152
+ .bl-sidebar-close {
153
+ display: inline-flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ width: 32px;
157
+ height: 32px;
158
+ padding: 0;
159
+ background: transparent;
160
+ border: 1px solid transparent;
161
+ border-radius: var(--tech-border-radius-sm);
162
+ color: var(--color-text);
163
+ cursor: pointer;
164
+ font-size: 1.5rem;
165
+ line-height: 1;
166
+ transition: var(--transition-base);
167
+
168
+ &:hover {
169
+ background: var(--glass-bg-medium);
170
+ border-color: var(--glass-border-medium);
171
+ }
172
+
173
+ &:focus {
174
+ outline: 1px solid var(--color-border-focus);
175
+ outline-offset: 2px;
176
+ }
177
+
178
+ // Hide on desktop (collapsed sidebar shows it)
179
+ @media (min-width: 768px) {
180
+ display: none;
181
+
182
+ .bl-sidebar-collapsed & {
183
+ display: inline-flex;
184
+ }
185
+ }
186
+ }
187
+
188
+ // Sidebar Navigation
189
+ .bl-sidebar-nav {
190
+ flex: 1;
191
+ padding: var(--spacing-sm);
192
+ overflow-y: auto;
193
+ overflow-x: hidden;
194
+
195
+ .bl-nav {
196
+ background: transparent;
197
+ border: none;
198
+ padding: 0;
199
+ }
200
+
201
+ .bl-nav-link {
202
+ display: flex;
203
+ align-items: center;
204
+ gap: var(--spacing-sm);
205
+ padding: var(--spacing-sm) var(--spacing-md);
206
+ border-radius: var(--tech-border-radius-sm);
207
+ transition: var(--transition-base);
208
+
209
+ span:not(.bl-icon) {
210
+ transition: var(--transition-base);
211
+ }
212
+
213
+ .bl-icon {
214
+ flex-shrink: 0;
215
+ width: 20px;
216
+ height: 20px;
217
+ }
218
+ }
219
+ }
220
+
221
+ // Sidebar Footer (optional)
222
+ .bl-sidebar-footer {
223
+ padding: var(--spacing-md);
224
+ border-top: 1px solid var(--glass-border-light);
225
+ background: var(--glass-bg-light);
226
+ backdrop-filter: blur(var(--glass-blur-sm));
227
+ -webkit-backdrop-filter: blur(var(--glass-blur-sm));
228
+ }
229
+
230
+ // Body Scroll Lock (when sidebar is open on mobile)
231
+ body:has(.bl-sidebar.bl-sidebar-open) {
232
+ @media (max-width: 767.98px) {
233
+ overflow: hidden;
234
+ }
235
+ }
236
+
237
+ // Animation for sidebar slide
238
+ @keyframes bl-sidebar-slide-in {
239
+ from {
240
+ transform: translateX(-100%);
241
+ }
242
+ to {
243
+ transform: translateX(0);
244
+ }
245
+ }
246
+
247
+ @keyframes bl-sidebar-slide-out {
248
+ from {
249
+ transform: translateX(0);
250
+ }
251
+ to {
252
+ transform: translateX(-100%);
253
+ }
254
+ }
255
+
256
+ @keyframes bl-sidebar-slide-in-right {
257
+ from {
258
+ transform: translateX(100%);
259
+ }
260
+ to {
261
+ transform: translateX(0);
262
+ }
263
+ }
264
+
265
+ @keyframes bl-sidebar-slide-out-right {
266
+ from {
267
+ transform: translateX(0);
268
+ }
269
+ to {
270
+ transform: translateX(100%);
271
+ }
272
+ }
273
+
@@ -0,0 +1,241 @@
1
+ // ============================================================================
2
+ // Toast/Notification Component (Technical Glass Design)
3
+ // ============================================================================
4
+
5
+ @use '../tokens/tokens' as *;
6
+
7
+ // Toast Container (positioned containers)
8
+ .bl-toast-container {
9
+ position: fixed;
10
+ z-index: var(--z-index-toast);
11
+ pointer-events: none;
12
+ padding: var(--spacing-md);
13
+
14
+ // Position variants
15
+ &.bl-toast-top-left {
16
+ top: 0;
17
+ left: 0;
18
+ }
19
+
20
+ &.bl-toast-top-right {
21
+ top: 0;
22
+ right: 0;
23
+ }
24
+
25
+ &.bl-toast-top-center {
26
+ top: 0;
27
+ left: 50%;
28
+ transform: translateX(-50%);
29
+ }
30
+
31
+ &.bl-toast-bottom-left {
32
+ bottom: 0;
33
+ left: 0;
34
+ }
35
+
36
+ &.bl-toast-bottom-right {
37
+ bottom: 0;
38
+ right: 0;
39
+ }
40
+
41
+ &.bl-toast-bottom-center {
42
+ bottom: 0;
43
+ left: 50%;
44
+ transform: translateX(-50%);
45
+ }
46
+
47
+ // Stacking
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: var(--spacing-sm);
51
+ max-width: 400px;
52
+ width: 100%;
53
+
54
+ @media (max-width: 575.98px) {
55
+ max-width: 100%;
56
+ padding: var(--spacing-sm);
57
+ }
58
+ }
59
+
60
+ // Toast Item
61
+ .bl-toast {
62
+ display: flex;
63
+ align-items: flex-start;
64
+ gap: var(--spacing-sm);
65
+ padding: var(--spacing-md);
66
+ background: var(--glass-bg-medium);
67
+ backdrop-filter: blur(var(--glass-blur-lg));
68
+ -webkit-backdrop-filter: blur(var(--glass-blur-lg));
69
+ border: 1px solid var(--glass-border-medium);
70
+ border-radius: var(--tech-border-radius-md);
71
+ box-shadow: var(--glass-shadow-lg);
72
+ color: var(--color-text);
73
+ pointer-events: all;
74
+ min-width: 300px;
75
+ max-width: 100%;
76
+ animation: bl-toast-slide-in var(--transition-duration-base) var(--transition-ease-out);
77
+
78
+ @media (max-width: 575.98px) {
79
+ min-width: auto;
80
+ width: 100%;
81
+ }
82
+
83
+ // Toast closing animation
84
+ &.bl-toast-closing {
85
+ animation: bl-toast-slide-out var(--transition-duration-base) var(--transition-ease-out) forwards;
86
+ }
87
+ }
88
+
89
+ // Toast Content
90
+ .bl-toast-content {
91
+ flex: 1;
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: var(--spacing-xs);
95
+ }
96
+
97
+ .bl-toast-title {
98
+ font-size: var(--font-size-base);
99
+ font-weight: var(--font-weight-semibold);
100
+ color: var(--color-text);
101
+ margin: 0;
102
+ }
103
+
104
+ .bl-toast-message {
105
+ font-size: var(--font-size-sm);
106
+ color: var(--color-text-muted);
107
+ margin: 0;
108
+ line-height: var(--line-height-base);
109
+ }
110
+
111
+ // Toast Close Button
112
+ .bl-toast-close {
113
+ display: inline-flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ width: 24px;
117
+ height: 24px;
118
+ padding: 0;
119
+ background: transparent;
120
+ border: none;
121
+ border-radius: var(--tech-border-radius-sm);
122
+ color: var(--color-text-muted);
123
+ cursor: pointer;
124
+ font-size: 1.25rem;
125
+ line-height: 1;
126
+ transition: var(--transition-base);
127
+ flex-shrink: 0;
128
+
129
+ &:hover {
130
+ background: var(--glass-bg-light);
131
+ color: var(--color-text);
132
+ }
133
+
134
+ &:focus {
135
+ outline: 1px solid var(--color-border-focus);
136
+ outline-offset: 2px;
137
+ }
138
+ }
139
+
140
+ // Toast Variants
141
+ .bl-toast-success {
142
+ border-left: 4px solid var(--color-success);
143
+
144
+ .bl-toast-title {
145
+ color: var(--color-success);
146
+ }
147
+ }
148
+
149
+ .bl-toast-error {
150
+ border-left: 4px solid var(--color-error);
151
+
152
+ .bl-toast-title {
153
+ color: var(--color-error);
154
+ }
155
+ }
156
+
157
+ .bl-toast-warning {
158
+ border-left: 4px solid var(--color-warning);
159
+
160
+ .bl-toast-title {
161
+ color: var(--color-warning);
162
+ }
163
+ }
164
+
165
+ .bl-toast-info {
166
+ border-left: 4px solid var(--color-info);
167
+
168
+ .bl-toast-title {
169
+ color: var(--color-info);
170
+ }
171
+ }
172
+
173
+ // Toast Icon (optional)
174
+ .bl-toast-icon {
175
+ display: inline-flex;
176
+ align-items: center;
177
+ justify-content: center;
178
+ width: 24px;
179
+ height: 24px;
180
+ flex-shrink: 0;
181
+ font-size: 1.25rem;
182
+ }
183
+
184
+ // Animations
185
+ @keyframes bl-toast-slide-in {
186
+ from {
187
+ opacity: 0;
188
+ transform: translateY(-20px);
189
+ }
190
+ to {
191
+ opacity: 1;
192
+ transform: translateY(0);
193
+ }
194
+ }
195
+
196
+ @keyframes bl-toast-slide-out {
197
+ from {
198
+ opacity: 1;
199
+ transform: translateY(0);
200
+ }
201
+ to {
202
+ opacity: 0;
203
+ transform: translateY(-20px);
204
+ }
205
+ }
206
+
207
+ // Bottom position animations
208
+ .bl-toast-container.bl-toast-bottom-left,
209
+ .bl-toast-container.bl-toast-bottom-right,
210
+ .bl-toast-container.bl-toast-bottom-center {
211
+ .bl-toast {
212
+ animation: bl-toast-slide-in-bottom var(--transition-duration-base) var(--transition-ease-out);
213
+
214
+ &.bl-toast-closing {
215
+ animation: bl-toast-slide-out-bottom var(--transition-duration-base) var(--transition-ease-out) forwards;
216
+ }
217
+ }
218
+ }
219
+
220
+ @keyframes bl-toast-slide-in-bottom {
221
+ from {
222
+ opacity: 0;
223
+ transform: translateY(20px);
224
+ }
225
+ to {
226
+ opacity: 1;
227
+ transform: translateY(0);
228
+ }
229
+ }
230
+
231
+ @keyframes bl-toast-slide-out-bottom {
232
+ from {
233
+ opacity: 1;
234
+ transform: translateY(0);
235
+ }
236
+ to {
237
+ opacity: 0;
238
+ transform: translateY(20px);
239
+ }
240
+ }
241
+
@@ -8,6 +8,9 @@ import { initDropdown } from './components/dropdown.js';
8
8
  import { initTooltip } from './components/tooltip.js';
9
9
  import { initTabs } from './components/tabs.js';
10
10
  import { initAlert, dismissAlert } from './components/alert.js';
11
+ import { initSidebar, toggleSidebar, openSidebar, closeSidebar } from './components/sidebar.js';
12
+ import { initAccordion, toggleAccordionItem } from './components/accordion.js';
13
+ import { showToast, showToastSuccess, showToastError, showToastWarning, showToastInfo } from './components/toast.js';
11
14
 
12
15
  // Export all functions for manual use
13
16
  export {
@@ -18,7 +21,18 @@ export {
18
21
  initTooltip,
19
22
  initTabs,
20
23
  initAlert,
21
- dismissAlert
24
+ dismissAlert,
25
+ initSidebar,
26
+ toggleSidebar,
27
+ openSidebar,
28
+ closeSidebar,
29
+ initAccordion,
30
+ toggleAccordionItem,
31
+ showToast,
32
+ showToastSuccess,
33
+ showToastError,
34
+ showToastWarning,
35
+ showToastInfo
22
36
  };
23
37
 
24
38
  // Auto-initialize on DOM ready
@@ -47,6 +61,16 @@ function autoInit() {
47
61
  document.querySelectorAll('.bl-alert-dismissible').forEach(alert => {
48
62
  initAlert(alert);
49
63
  });
64
+
65
+ // Initialize all sidebars
66
+ document.querySelectorAll('.bl-sidebar').forEach(sidebar => {
67
+ initSidebar(sidebar);
68
+ });
69
+
70
+ // Initialize all accordions
71
+ document.querySelectorAll('.bl-accordion').forEach(accordion => {
72
+ initAccordion(accordion);
73
+ });
50
74
  }
51
75
 
52
76
  // Initialize when DOM is ready
@@ -0,0 +1,43 @@
1
+ // ============================================================================
2
+ // Accordion Component JavaScript
3
+ // ============================================================================
4
+
5
+ export function initAccordion(accordionElement) {
6
+ if (!accordionElement) return;
7
+
8
+ const items = accordionElement.querySelectorAll('.bl-accordion-item');
9
+ const isSingleMode = accordionElement.classList.contains('bl-accordion-single');
10
+
11
+ items.forEach(item => {
12
+ const header = item.querySelector('.bl-accordion-header');
13
+ if (!header) return;
14
+
15
+ header.addEventListener('click', () => {
16
+ toggleAccordionItem(item, isSingleMode, accordionElement);
17
+ });
18
+ });
19
+ }
20
+
21
+ export function toggleAccordionItem(itemElement, closeOthers = false, accordionElement = null) {
22
+ if (!itemElement) return;
23
+
24
+ const isOpen = itemElement.classList.contains('bl-accordion-open');
25
+
26
+ if (closeOthers && accordionElement) {
27
+ // Close all other items
28
+ const allItems = accordionElement.querySelectorAll('.bl-accordion-item');
29
+ allItems.forEach(item => {
30
+ if (item !== itemElement && item.classList.contains('bl-accordion-open')) {
31
+ item.classList.remove('bl-accordion-open');
32
+ }
33
+ });
34
+ }
35
+
36
+ // Toggle current item
37
+ if (isOpen) {
38
+ itemElement.classList.remove('bl-accordion-open');
39
+ } else {
40
+ itemElement.classList.add('bl-accordion-open');
41
+ }
42
+ }
43
+
@@ -0,0 +1,101 @@
1
+ // ============================================================================
2
+ // Sidebar Component JavaScript
3
+ // ============================================================================
4
+
5
+ export function initSidebar(sidebarElement) {
6
+ if (!sidebarElement) return;
7
+
8
+ const sidebarId = sidebarElement.id;
9
+ if (!sidebarId) return;
10
+
11
+ // Create backdrop if it doesn't exist (mobile only)
12
+ let backdrop = document.querySelector('.bl-sidebar-backdrop');
13
+ if (!backdrop) {
14
+ backdrop = document.createElement('div');
15
+ backdrop.className = 'bl-sidebar-backdrop';
16
+ document.body.appendChild(backdrop);
17
+ }
18
+
19
+ // Close button
20
+ const closeButton = sidebarElement.querySelector('.bl-sidebar-close');
21
+ if (closeButton) {
22
+ closeButton.addEventListener('click', () => {
23
+ closeSidebar(sidebarId);
24
+ });
25
+ }
26
+
27
+ // Toggle buttons
28
+ const toggleButtons = document.querySelectorAll(`[data-sidebar-toggle="${sidebarId}"]`);
29
+ toggleButtons.forEach(btn => {
30
+ btn.addEventListener('click', () => {
31
+ toggleSidebar(sidebarId);
32
+ });
33
+ });
34
+
35
+ // Close on backdrop click (mobile only)
36
+ backdrop.addEventListener('click', () => {
37
+ if (isMobile() && sidebarElement.classList.contains('bl-sidebar-open')) {
38
+ closeSidebar(sidebarId);
39
+ }
40
+ });
41
+
42
+ // Close on ESC key (mobile only)
43
+ const escHandler = (e) => {
44
+ if (e.key === 'Escape' && isMobile() && sidebarElement.classList.contains('bl-sidebar-open')) {
45
+ closeSidebar(sidebarId);
46
+ }
47
+ };
48
+ document.addEventListener('keydown', escHandler);
49
+
50
+ // Store handler for cleanup if needed
51
+ sidebarElement._escHandler = escHandler;
52
+
53
+ // Handle window resize (responsive behavior)
54
+ const resizeHandler = () => {
55
+ if (!isMobile() && sidebarElement.classList.contains('bl-sidebar-open')) {
56
+ // On desktop, sidebar is always visible, remove open class
57
+ sidebarElement.classList.remove('bl-sidebar-open');
58
+ }
59
+ };
60
+ window.addEventListener('resize', resizeHandler);
61
+ sidebarElement._resizeHandler = resizeHandler;
62
+ }
63
+
64
+ export function toggleSidebar(sidebarId) {
65
+ const sidebar = document.getElementById(sidebarId);
66
+ if (!sidebar) return;
67
+
68
+ if (sidebar.classList.contains('bl-sidebar-open')) {
69
+ closeSidebar(sidebarId);
70
+ } else {
71
+ openSidebar(sidebarId);
72
+ }
73
+ }
74
+
75
+ export function openSidebar(sidebarId) {
76
+ const sidebar = document.getElementById(sidebarId);
77
+ if (!sidebar) return;
78
+
79
+ sidebar.classList.add('bl-sidebar-open');
80
+
81
+ // Lock body scroll on mobile
82
+ if (isMobile()) {
83
+ document.body.style.overflow = 'hidden';
84
+ }
85
+ }
86
+
87
+ export function closeSidebar(sidebarId) {
88
+ const sidebar = document.getElementById(sidebarId);
89
+ if (!sidebar) return;
90
+
91
+ sidebar.classList.remove('bl-sidebar-open');
92
+
93
+ // Unlock body scroll
94
+ document.body.style.overflow = '';
95
+ }
96
+
97
+ // Helper function to check if mobile
98
+ function isMobile() {
99
+ return window.innerWidth < 768;
100
+ }
101
+