ketekny-ui-kit 1.0.103 → 1.0.105

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ketekny-ui-kit",
3
3
  "type": "module",
4
- "version": "1.0.103",
4
+ "version": "1.0.105",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -1,6 +1,19 @@
1
1
  <template>
2
2
  <main class="flex min-h-screen w-full max-w-none flex-col bg-slate-100">
3
- <section class="bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8">
3
+ <div
4
+ v-if="showHeaderExtraBeforeTitle"
5
+ class="sticky z-30 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur sm:px-6 lg:px-8"
6
+ :style="{ top: headerExtraTop }"
7
+ >
8
+ <div class="mx-auto flex w-full max-w-none items-center">
9
+ <slot name="header-extra" />
10
+ </div>
11
+ </div>
12
+
13
+ <section
14
+ v-if="hasTopSection"
15
+ class="bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8"
16
+ >
4
17
  <div class="mx-auto flex w-full max-w-none flex-col gap-5">
5
18
  <div
6
19
  v-if="pageTitle || pageDescription"
@@ -31,7 +44,11 @@
31
44
  </div>
32
45
  </section>
33
46
 
34
- <div class="sticky top-0 z-30 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur sm:px-6 lg:px-8">
47
+ <div
48
+ v-if="showHeaderExtraAfterTitle"
49
+ class="sticky z-30 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur sm:px-6 lg:px-8"
50
+ :style="{ top: headerExtraTop }"
51
+ >
35
52
  <div class="mx-auto flex w-full max-w-none items-center">
36
53
  <slot name="header-extra" />
37
54
  </div>
@@ -69,8 +86,30 @@ export default {
69
86
  type: String,
70
87
  default: '',
71
88
  },
89
+ headerExtraTop: {
90
+ type: String,
91
+ default: '0px',
92
+ },
93
+ headerExtraBeforeTitle: {
94
+ type: Boolean,
95
+ default: false,
96
+ },
72
97
  },
73
98
  computed: {
99
+ hasTopSection() {
100
+ return Boolean(
101
+ this.pageTitle ||
102
+ this.pageDescription ||
103
+ this.externalUrl ||
104
+ this.$slots['header-actions']
105
+ )
106
+ },
107
+ showHeaderExtraBeforeTitle() {
108
+ return this.headerExtraBeforeTitle && Boolean(this.$slots['header-extra'])
109
+ },
110
+ showHeaderExtraAfterTitle() {
111
+ return !this.headerExtraBeforeTitle && Boolean(this.$slots['header-extra'])
112
+ },
74
113
  externalUrlLabel() {
75
114
  const target = String(this.externalUrl || '').trim()
76
115
  if (!target) return ''
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <main class="flex h-screen w-full max-w-none flex-col overflow-hidden bg-slate-100">
3
- <section class="shrink-0 bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8">
3
+ <section
4
+ v-if="hasTopSection"
5
+ class="shrink-0 bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8"
6
+ >
4
7
  <div class="mx-auto flex w-full max-w-none flex-col gap-5">
5
8
  <div
6
9
  v-if="pageTitle || pageDescription"
@@ -31,7 +34,10 @@
31
34
  </div>
32
35
  </section>
33
36
 
34
- <div class="shrink-0 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur">
37
+ <div
38
+ v-if="$slots['header-extra']"
39
+ class="shrink-0 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur"
40
+ >
35
41
  <div class="mx-auto flex w-full max-w-none items-center">
36
42
  <slot name="header-extra" />
37
43
  </div>
@@ -80,6 +86,14 @@ export default {
80
86
  },
81
87
  },
82
88
  computed: {
89
+ hasTopSection() {
90
+ return Boolean(
91
+ this.pageTitle ||
92
+ this.pageDescription ||
93
+ this.externalUrl ||
94
+ this.$slots['header-actions']
95
+ )
96
+ },
83
97
  externalUrlLabel() {
84
98
  const target = String(this.externalUrl || '').trim()
85
99
  if (!target) return ''
@@ -17,6 +17,7 @@ import demoAvatarUrl from '../assets/placeholder-user.jpg'
17
17
 
18
18
  const FAVORITES_STORAGE_KEY = 'ketekny-ui-sidebar-favorites'
19
19
  const MENU_TAB_STORAGE_KEY = 'ketekny-ui-sidebar-menu-tab'
20
+ const LOGO_BASE_URL = 'https://s3.ketekny.gr/public/web-apps/logos'
20
21
 
21
22
  export default {
22
23
  name: 'MultilevelSidebar',
@@ -71,6 +72,11 @@ export default {
71
72
  type: Boolean,
72
73
  default: false,
73
74
  },
75
+ theme: {
76
+ type: String,
77
+ default: 'dark',
78
+ validator: value => ['dark', 'light'].includes(value),
79
+ },
74
80
  },
75
81
  emits: ['select', 'logo-click', 'signin', 'signup', 'signout', 'edit-profile', 'item-click'],
76
82
  data() {
@@ -95,6 +101,17 @@ export default {
95
101
  width: this.collapsed ? this.collapsedWidth : this.width,
96
102
  }
97
103
  },
104
+ themeClass() {
105
+ return this.theme === 'light' ? 'sidebar-theme-light' : 'sidebar-theme-dark'
106
+ },
107
+ activeExpandedLogoSrc() {
108
+ if (this.theme === 'light') return `${LOGO_BASE_URL}/logo-light.png`
109
+ return `${LOGO_BASE_URL}/logo-dark.png`
110
+ },
111
+ activeCollapsedLogoSrc() {
112
+ if (this.theme === 'light') return `${LOGO_BASE_URL}/logo_icon.jpg`
113
+ return `${LOGO_BASE_URL}/logo-icon-dark.png`
114
+ },
98
115
  collapsedTitle() {
99
116
  if (!this.title) return ''
100
117
  const words = this.title.trim().split(/\s+/).filter(Boolean)
@@ -395,16 +412,17 @@ export default {
395
412
 
396
413
  <template>
397
414
  <aside
398
- class="group/sidebar fixed inset-y-0 left-0 z-40 flex h-full shrink-0 flex-col border-r border-sidebar-border bg-sidebar text-sidebar-foreground transition-[width,transform] duration-200 ease-out lg:static lg:translate-x-0"
415
+ class="group/sidebar fixed inset-y-0 left-0 z-40 flex h-full shrink-0 flex-col border-r border-[var(--sidebar-border)] bg-[var(--sidebar-bg)] text-[var(--sidebar-text)] transition-[width,transform] duration-200 ease-out lg:static lg:translate-x-0"
399
416
  :style="sidebarWidthStyle"
400
417
  :class="[
418
+ themeClass,
401
419
  mobileOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0',
402
420
  ]"
403
421
  >
404
422
  <button
405
423
  type="button"
406
424
  v-tooltip.right="collapsed ? 'Ανάπτυξη πλαϊνού μενού' : 'Σύμπτυξη πλαϊνού μενού'"
407
- class="absolute right-0 top-1/2 z-50 hidden h-14 w-6 -translate-y-1/2 translate-x-1/2 items-center justify-center rounded-r-md border border-sidebar-border bg-sidebar-accent text-sidebar-muted opacity-70 shadow-lg shadow-black/30 transition-[color,opacity] hover:opacity-100 hover:text-sidebar-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand lg:inline-flex"
425
+ class="absolute right-0 top-1/2 z-50 hidden h-14 w-6 -translate-y-1/2 translate-x-1/2 items-center justify-center rounded-r-md border border-[var(--sidebar-border)] bg-[var(--sidebar-surface)] text-[var(--sidebar-text-muted)] opacity-85 shadow-lg shadow-[var(--sidebar-shadow)] transition-[color,opacity,background-color] hover:bg-[var(--sidebar-hover)] hover:opacity-100 hover:text-[var(--sidebar-text)] focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand lg:inline-flex"
408
426
  :aria-label="collapsed ? 'Ανάπτυξη πλαϊνού μενού' : 'Σύμπτυξη πλαϊνού μενού'"
409
427
  @click="toggleCollapsed"
410
428
  >
@@ -415,14 +433,14 @@ export default {
415
433
  <div class="relative flex items-center px-4 py-4" :class="collapsed ? 'justify-center' : 'gap-2.5'">
416
434
  <img
417
435
  v-if="collapsed"
418
- src="https://s3.ketekny.gr/public/web-apps/logos/logo-icon-dark.png"
436
+ :src="activeCollapsedLogoSrc"
419
437
  alt="Ketekny logo"
420
438
  class="size-9 shrink-0 rounded-lg object-contain cursor-pointer"
421
439
  @click="handleLogoClick"
422
440
  />
423
441
  <img
424
442
  v-else
425
- src="https://support.ketekny.gr/logo-dark.png"
443
+ :src="activeExpandedLogoSrc"
426
444
  alt="Ketekny logo"
427
445
  class="min-w-0 flex-1 object-contain cursor-pointer"
428
446
  @click="handleLogoClick"
@@ -430,7 +448,7 @@ export default {
430
448
  </div>
431
449
 
432
450
  <div v-if="title && !collapsed" class="px-4 pb-2">
433
- <p class="text-xl font-bold text-center text-sidebar-foreground">
451
+ <p class="text-xl font-bold text-center text-[var(--sidebar-text)]">
434
452
  {{ title }}
435
453
  </p>
436
454
  </div>
@@ -438,7 +456,7 @@ export default {
438
456
  <div v-else-if="(title || appIcon) && collapsed" class="flex justify-center px-3 pb-2">
439
457
  <div
440
458
  v-tooltip.right="title"
441
- class="flex h-10 w-10 items-center justify-center rounded-md border border-sidebar-border/30 bg-sidebar-accent/30 text-[10px] font-bold tracking-[0.18em] text-sidebar-muted"
459
+ class="flex h-10 w-10 items-center justify-center rounded-md border border-[var(--sidebar-border)] bg-[var(--sidebar-surface-muted)] text-[10px] font-bold tracking-[0.18em] text-[var(--sidebar-text-muted)]"
442
460
  >
443
461
  <img
444
462
  v-if="appIcon"
@@ -452,19 +470,23 @@ export default {
452
470
  </div>
453
471
  </div>
454
472
 
473
+ <div v-if="$slots['sidebar-header']" :class="collapsed ? 'px-2 pb-2' : 'px-4 pb-3'">
474
+ <slot name="sidebar-header" />
475
+ </div>
476
+
455
477
  <nav
456
478
  class="flex-1 px-3 py-2"
457
479
  :class="collapsed ? 'overflow-visible' : 'overflow-y-auto'"
458
480
  aria-label="Main navigation"
459
481
  >
460
482
  <div v-if="showMenuTabs" class="mb-3 px-1">
461
- <div class="inline-flex w-full rounded-lg bg-sidebar-accent/35 p-1">
483
+ <div class="inline-flex w-full rounded-lg bg-[var(--sidebar-surface-muted)] p-1">
462
484
  <button
463
485
  type="button"
464
486
  class="flex-1 rounded-md px-3 py-1.5 text-sm font-medium transition-colors"
465
487
  :class="activeMenuTab === 'all'
466
- ? 'bg-sidebar text-sidebar-foreground shadow-sm'
467
- : 'text-sidebar-muted hover:text-sidebar-foreground'"
488
+ ? 'bg-[var(--sidebar-surface)] text-[var(--sidebar-text)] shadow-sm'
489
+ : 'text-[var(--sidebar-text-muted)] hover:text-[var(--sidebar-text)]'"
468
490
  @click="activeMenuTab = 'all'"
469
491
  >
470
492
  Όλα
@@ -473,8 +495,8 @@ export default {
473
495
  type="button"
474
496
  class="flex-1 rounded-md px-3 py-1.5 text-sm font-medium transition-colors"
475
497
  :class="activeMenuTab === 'favorites'
476
- ? 'bg-sidebar text-sidebar-foreground shadow-sm'
477
- : 'text-sidebar-muted hover:text-sidebar-foreground'"
498
+ ? 'bg-[var(--sidebar-surface)] text-[var(--sidebar-text)] shadow-sm'
499
+ : 'text-[var(--sidebar-text-muted)] hover:text-[var(--sidebar-text)]'"
478
500
  @click="activeMenuTab = 'favorites'"
479
501
  >
480
502
  Αγαπημένα
@@ -485,7 +507,7 @@ export default {
485
507
  <div v-if="!collapsed && (!showMenuTabs || activeMenuTab === 'all')" class="px-1 pb-3">
486
508
  <div class="relative">
487
509
  <Search
488
- class="pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-sidebar-muted"
510
+ class="pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-[var(--sidebar-text-muted)]"
489
511
  :stroke-width="2"
490
512
  />
491
513
  <input
@@ -493,12 +515,12 @@ export default {
493
515
  type="text"
494
516
  placeholder="Αναζήτηση μενού..."
495
517
  aria-label="Αναζήτηση μενού"
496
- class="w-full border rounded-md border-sidebar-border/35 bg-sidebar-accent/40 py-2 pl-8 pr-9 text-sidebar-foreground placeholder:text-sidebar-muted focus:border-white/15 focus:outline-none focus:ring-1 focus:ring-white/10"
518
+ class="w-full rounded-md border border-[var(--sidebar-border)] bg-[var(--sidebar-surface)] py-2 pl-8 pr-9 text-[var(--sidebar-text)] placeholder:text-[var(--sidebar-text-muted)] focus:border-[var(--sidebar-border-strong)] focus:outline-none focus:ring-1 focus:ring-[var(--sidebar-border-strong)]"
497
519
  />
498
520
  <button
499
521
  v-if="query"
500
522
  type="button"
501
- class="absolute right-2 top-1/2 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded-md text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-foreground"
523
+ class="absolute right-2 top-1/2 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded-md text-[var(--sidebar-text-muted)] transition-colors hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-text)]"
502
524
  aria-label="Καθαρισμός αναζήτησης"
503
525
  @click="clearQuery"
504
526
  >
@@ -517,6 +539,7 @@ export default {
517
539
  :open-ids="effectiveOpenIds"
518
540
  :collapsed="collapsed"
519
541
  :favorites="favoriteIds"
542
+ :theme="theme"
520
543
  @select="handleMenuSelect"
521
544
  @toggle="toggle"
522
545
  @toggle-favorite="toggleFavorite"
@@ -524,13 +547,13 @@ export default {
524
547
  </ul>
525
548
  <p
526
549
  v-if="!collapsed && displayedMenuItems.length === 0"
527
- class="px-3 py-6 text-center text-sidebar-muted"
550
+ class="px-3 py-6 text-center text-[var(--sidebar-text-muted)]"
528
551
  >
529
552
  Δεν βρέθηκαν αποτελέσματα.
530
553
  </p>
531
554
  </nav>
532
555
 
533
- <div class="mt-auto border-t border-sidebar-border px-3 pb-3 pt-4">
556
+ <div class="mt-auto border-t border-[var(--sidebar-border)] px-3 pb-3 pt-4">
534
557
  <div
535
558
  :class="collapsed
536
559
  ? 'flex flex-col items-center gap-1'
@@ -548,8 +571,8 @@ export default {
548
571
  activeId === link.id
549
572
  ? 'bg-brand text-brand-foreground font-medium'
550
573
  : link.id === 'sign-out'
551
- ? 'text-orange-300 hover:bg-orange-500/10 hover:text-orange-200'
552
- : 'text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-foreground',
574
+ ? 'text-[var(--sidebar-danger)] hover:bg-[var(--sidebar-danger-bg)] hover:text-[var(--sidebar-danger-hover)]'
575
+ : 'text-[var(--sidebar-text-soft)] hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-text)]',
553
576
  ]"
554
577
  :aria-label="link.label"
555
578
  @click="handleFooterSelect(link.id)"
@@ -561,8 +584,8 @@ export default {
561
584
  activeId === link.id
562
585
  ? 'text-brand-foreground'
563
586
  : link.id === 'sign-out'
564
- ? 'text-orange-400'
565
- : 'text-sidebar-muted'
587
+ ? 'text-[var(--sidebar-danger)]'
588
+ : 'text-[var(--sidebar-text-muted)]'
566
589
  "
567
590
  :stroke-width="2"
568
591
  />
@@ -575,7 +598,7 @@ export default {
575
598
  aria-label="Account options"
576
599
  >
577
600
  <template #trigger>
578
- <div class="flex w-full items-center gap-2 rounded-lg px-1 py-1 text-sidebar-foreground/90 transition-colors duration-150">
601
+ <div class="flex w-full items-center gap-2 rounded-lg px-1 py-1 text-[var(--sidebar-text-strong)] transition-colors duration-150">
579
602
  <div class="sidebar-footer-item flex min-h-[44px] flex-1 items-center gap-2.5 rounded-lg bg-transparent px-1.5 py-1.5 text-left font-medium">
580
603
  <img
581
604
  :src="demoAvatarUrl"
@@ -584,12 +607,12 @@ export default {
584
607
  />
585
608
  <span class="min-w-0 flex-1">
586
609
  <span class="block truncate text-sm font-semibold">{{ accountDisplayName || 'Το προφίλ μου' }}</span>
587
- <span class="block truncate text-xs font-normal text-sidebar-muted">{{ accountEmail }}</span>
610
+ <span class="block truncate text-xs font-normal text-[var(--sidebar-text-muted)]">{{ accountEmail }}</span>
588
611
  </span>
589
612
  </div>
590
613
 
591
614
  <span
592
- class="inline-flex size-9 items-center justify-center rounded-lg text-sidebar-muted"
615
+ class="inline-flex size-9 items-center justify-center rounded-lg text-[var(--sidebar-text-muted)]"
593
616
  aria-hidden="true"
594
617
  >
595
618
  <Ellipsis class="size-4" :stroke-width="2" />
@@ -602,23 +625,23 @@ export default {
602
625
 
603
626
  <div
604
627
  v-if="!collapsed"
605
- class="mt-3 border-t border-sidebar-border/70 px-2 pt-2 text-[11px] leading-snug text-sidebar-foreground/90"
628
+ class="mt-3 border-t border-[var(--sidebar-border)] px-2 pt-2 text-[11px] leading-snug text-[var(--sidebar-text-strong)]"
606
629
  >
607
- <div class="flex items-center gap-1.5 text-sidebar-foreground">
608
- <Building2 class="size-3 shrink-0 text-sidebar-muted" :stroke-width="2" />
630
+ <div class="flex items-center gap-1.5 text-[var(--sidebar-text)]">
631
+ <Building2 class="size-3 shrink-0 text-[var(--sidebar-text-muted)]" :stroke-width="2" />
609
632
  <p class="font-semibold uppercase tracking-[0.28em]">KETEKNY AE</p>
610
633
  </div>
611
634
  <div class="mt-1.5 pl-[18px]">
612
635
  <p class="truncate ">Βερανζέρου 13, 10677, Αθήνα
613
636
  <span>&nbsp;</span>
614
- <a href="tel:+302103648337" class="transition-colors hover:text-sidebar-foreground">
637
+ <a href="tel:+302103648337" class="transition-colors hover:text-[var(--sidebar-text)]">
615
638
  210 3648 337
616
639
  </a></p>
617
640
 
618
641
  <div class="flex flex-wrap items-center gap-x-3">
619
642
  <a
620
643
  :href="`mailto:${contactEmail}`"
621
- class="transition-colors hover:text-sidebar-foreground"
644
+ class="transition-colors hover:text-[var(--sidebar-text)]"
622
645
  >
623
646
  {{ contactEmail }}
624
647
  </a>
@@ -626,7 +649,7 @@ export default {
626
649
  :href="helpUrl || 'https://help.ketekny.gr'"
627
650
  target="_blank"
628
651
  rel="noreferrer"
629
- class="truncate transition-colors hover:text-sidebar-foreground"
652
+ class="truncate transition-colors hover:text-[var(--sidebar-text)]"
630
653
  >
631
654
  {{ helpUrl || 'https://help.ketekny.gr' }}
632
655
  </a>
@@ -636,3 +659,39 @@ export default {
636
659
  </div>
637
660
  </aside>
638
661
  </template>
662
+
663
+ <style scoped>
664
+ .sidebar-theme-dark {
665
+ --sidebar-bg: #0b0c0f;
666
+ --sidebar-surface: #161a22;
667
+ --sidebar-surface-muted: rgba(255, 255, 255, 0.06);
668
+ --sidebar-text: #eef2f7;
669
+ --sidebar-text-soft: rgba(238, 242, 247, 0.8);
670
+ --sidebar-text-strong: rgba(238, 242, 247, 0.9);
671
+ --sidebar-text-muted: #8a92a1;
672
+ --sidebar-border: rgba(255, 255, 255, 0.08);
673
+ --sidebar-border-strong: rgba(255, 255, 255, 0.16);
674
+ --sidebar-hover: rgba(255, 255, 255, 0.08);
675
+ --sidebar-shadow: rgba(0, 0, 0, 0.3);
676
+ --sidebar-danger: #f59e0b;
677
+ --sidebar-danger-hover: #fbbf24;
678
+ --sidebar-danger-bg: rgba(245, 158, 11, 0.12);
679
+ }
680
+
681
+ .sidebar-theme-light {
682
+ --sidebar-bg: #f8fafc;
683
+ --sidebar-surface: #ffffff;
684
+ --sidebar-surface-muted: #eef4fb;
685
+ --sidebar-text: #0f172a;
686
+ --sidebar-text-soft: rgba(15, 23, 42, 0.8);
687
+ --sidebar-text-strong: rgba(15, 23, 42, 0.9);
688
+ --sidebar-text-muted: #64748b;
689
+ --sidebar-border: rgba(148, 163, 184, 0.28);
690
+ --sidebar-border-strong: rgba(100, 116, 139, 0.4);
691
+ --sidebar-hover: #e2e8f0;
692
+ --sidebar-shadow: rgba(15, 23, 42, 0.12);
693
+ --sidebar-danger: #ea580c;
694
+ --sidebar-danger-hover: #c2410c;
695
+ --sidebar-danger-bg: #ffedd5;
696
+ }
697
+ </style>
@@ -16,6 +16,7 @@ export default {
16
16
  openIds: { type: Object, required: true },
17
17
  collapsed: { type: Boolean, default: false },
18
18
  favorites: { type: Object, default: null },
19
+ theme: { type: String, default: 'dark' },
19
20
  },
20
21
  emits: ['select', 'toggle', 'toggle-favorite'],
21
22
  computed: {
@@ -100,8 +101,8 @@ export default {
100
101
  isActiveLeaf || (isCollapsedTop && isActive)
101
102
  ? 'bg-brand text-brand-foreground font-medium'
102
103
  : isActiveTrail
103
- ? 'text-sidebar-foreground'
104
- : 'text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-foreground',
104
+ ? 'text-[var(--sidebar-text)]'
105
+ : 'text-[var(--sidebar-text-soft)] hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-text)]',
105
106
  ]"
106
107
  :aria-expanded="hasChildren && !isCollapsedTop ? isOpen : undefined"
107
108
  :aria-label="isCollapsedTop ? item.label : undefined"
@@ -120,7 +121,7 @@ export default {
120
121
  :class="[
121
122
  isActiveLeaf || (isCollapsedTop && isActive)
122
123
  ? 'text-brand-foreground'
123
- : 'text-sidebar-muted group-hover:text-sidebar-foreground',
124
+ : 'text-[var(--sidebar-text-muted)] group-hover:text-[var(--sidebar-text)]',
124
125
  ]"
125
126
  :stroke-width="2"
126
127
  />
@@ -131,7 +132,7 @@ export default {
131
132
  <span
132
133
  v-if="item.badge != null"
133
134
  class="ml-auto rounded-full px-1.5 py-0.5 text-xs font-medium tabular-nums"
134
- :class="isActiveLeaf ? 'bg-brand-foreground/20 text-brand-foreground' : 'bg-sidebar-accent text-sidebar-muted'"
135
+ :class="isActiveLeaf ? 'bg-brand-foreground/20 text-brand-foreground' : 'bg-[var(--sidebar-surface-muted)] text-[var(--sidebar-text-muted)]'"
135
136
  >
136
137
  {{ item.badge }}
137
138
  </span>
@@ -139,14 +140,14 @@ export default {
139
140
  <ExternalLink
140
141
  v-if="isExternalLink"
141
142
  class="size-3.5 shrink-0"
142
- :class="isActiveLeaf ? 'text-brand-foreground' : 'text-sidebar-muted'"
143
+ :class="isActiveLeaf ? 'text-brand-foreground' : 'text-[var(--sidebar-text-muted)]'"
143
144
  :stroke-width="2"
144
145
  />
145
146
 
146
147
  <button
147
148
  v-if="!hasChildren"
148
149
  type="button"
149
- class="inline-flex size-7 shrink-0 items-center justify-center rounded-md text-sidebar-muted opacity-0 transition-all duration-150 group-hover:opacity-100 hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:opacity-100 focus-visible:outline-none"
150
+ class="inline-flex size-7 shrink-0 items-center justify-center rounded-md text-[var(--sidebar-text-muted)] opacity-0 transition-all duration-150 group-hover:opacity-100 hover:bg-[var(--sidebar-hover)] hover:text-[var(--sidebar-text)] focus-visible:opacity-100 focus-visible:outline-none"
150
151
  :class="isFavorite ? 'opacity-100 text-amber-400 hover:text-amber-300' : ''"
151
152
  :aria-label="isFavorite ? 'Remove from favorites' : 'Add to favorites'"
152
153
  @click="toggleFavorite"
@@ -160,7 +161,7 @@ export default {
160
161
 
161
162
  <ChevronRight
162
163
  v-if="hasChildren"
163
- class="size-4 shrink-0 text-sidebar-muted transition-transform duration-200"
164
+ class="size-4 shrink-0 text-[var(--sidebar-text-muted)] transition-transform duration-200"
164
165
  :class="isOpen ? 'rotate-90' : ''"
165
166
  :stroke-width="2"
166
167
  />
@@ -169,7 +170,7 @@ export default {
169
170
  <span
170
171
  v-if="isCollapsedTop && hasChildren"
171
172
  class="absolute right-1.5 top-1.5 size-1.5 rounded-full"
172
- :class="isActive ? 'bg-brand-foreground/80' : 'bg-sidebar-muted/60'"
173
+ :class="isActive ? 'bg-brand-foreground/80' : 'bg-[var(--sidebar-text-muted)]/60'"
173
174
  aria-hidden="true"
174
175
  />
175
176
  </button>
@@ -182,7 +183,7 @@ export default {
182
183
  >
183
184
  <ul
184
185
  v-if="hasChildren && isOpen"
185
- class="overflow-hidden border-l border-sidebar-border/25 ml-4 pl-2 transition-[height] duration-200 ease-out"
186
+ class="overflow-hidden border-l border-[var(--sidebar-border)] ml-4 pl-2 transition-[height] duration-200 ease-out"
186
187
  >
187
188
  <SidebarMenuItem
188
189
  v-for="child in item.children"
@@ -193,6 +194,7 @@ export default {
193
194
  :open-ids="openIds"
194
195
  :collapsed="collapsed"
195
196
  :favorites="favorites"
197
+ :theme="theme"
196
198
  @select="$emit('select', $event)"
197
199
  @toggle="$emit('toggle', $event)"
198
200
  @toggle-favorite="$emit('toggle-favorite', $event)"
@@ -205,14 +207,14 @@ export default {
205
207
  class="invisible absolute left-full top-0 z-50 pl-2 opacity-0 transition-opacity duration-150 group-hover/fly:visible group-hover/fly:opacity-100"
206
208
  >
207
209
  <div
208
- class="min-w-56 rounded-lg border border-sidebar-border bg-sidebar p-2 shadow-xl shadow-black/40"
210
+ class="min-w-56 rounded-lg border border-[var(--sidebar-border)] bg-[var(--sidebar-surface)] p-2 shadow-xl shadow-[var(--sidebar-shadow)]"
209
211
  role="menu"
210
212
  :aria-label="item.label"
211
213
  >
212
214
  <button
213
215
  type="button"
214
216
  class="mb-1 flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left font-semibold"
215
- :class="isActive ? 'text-brand' : 'text-sidebar-foreground hover:bg-sidebar-accent'"
217
+ :class="isActive ? 'text-brand' : 'text-[var(--sidebar-text)] hover:bg-[var(--sidebar-hover)]'"
216
218
  @click="!hasChildren && $emit('select', item)"
217
219
  >
218
220
  <component
@@ -224,7 +226,7 @@ export default {
224
226
  <span class="truncate">{{ item.label }}</span>
225
227
  <ExternalLink
226
228
  v-if="isExternalLink"
227
- class="ml-auto size-3.5 shrink-0 text-sidebar-muted"
229
+ class="ml-auto size-3.5 shrink-0 text-[var(--sidebar-text-muted)]"
228
230
  :stroke-width="2"
229
231
  />
230
232
  </button>
@@ -239,6 +241,7 @@ export default {
239
241
  :open-ids="openIds"
240
242
  :collapsed="false"
241
243
  :favorites="favorites"
244
+ :theme="theme"
242
245
  @select="$emit('select', $event)"
243
246
  @toggle="$emit('toggle', $event)"
244
247
  @toggle-favorite="$emit('toggle-favorite', $event)"
@@ -3,6 +3,7 @@
3
3
  <div v-if="mobileMenuOpen" class="fixed inset-0 z-30 bg-slate-950/50 backdrop-blur-[1px] lg:hidden" @click="closeMenu" />
4
4
 
5
5
  <MultilevelSidebar
6
+ :theme="theme"
6
7
  :title="title"
7
8
  :app-icon="appIcon"
8
9
  :width="width"
@@ -19,16 +20,29 @@
19
20
  @edit-profile="handleEditProfile"
20
21
  @item-click="handleItemClick"
21
22
  @select="handleSelect"
22
- />
23
+ >
24
+ <template v-if="$slots['sidebar-header']" #sidebar-header>
25
+ <slot name="sidebar-header" />
26
+ </template>
27
+ </MultilevelSidebar>
23
28
 
24
- <main class="flex flex-col flex-1 overflow-y-auto">
29
+ <main class="flex min-h-0 flex-1 flex-col overflow-y-auto">
30
+ <div
31
+ v-if="$slots.header"
32
+ class="sticky top-0 z-20 shrink-0"
33
+ >
34
+ <slot name="header" />
35
+ </div>
25
36
  <slot />
26
37
  </main>
27
38
 
28
39
  <button
29
- v-if="showBurger && !mobileMenuOpen"
40
+ v-if="!mobileMenuOpen"
30
41
  type="button"
31
- class="fixed z-50 inline-flex items-center justify-center text-white transition-colors rounded-full shadow-lg bottom-5 left-5 size-14 bg-sidebar shadow-black/25 hover:bg-sidebar-accent lg:hidden"
42
+ class="fixed bottom-5 left-5 z-50 inline-flex size-14 items-center justify-center rounded-full transition-colors lg:hidden"
43
+ :class="theme === 'light'
44
+ ? 'border border-slate-200 bg-white text-slate-700 shadow-lg shadow-slate-900/10 hover:bg-slate-100'
45
+ : 'bg-sidebar text-white shadow-lg shadow-black/25 hover:bg-sidebar-accent'"
32
46
  aria-label="Open menu"
33
47
  @click="toggleMenu">
34
48
  <Menu class="size-6" :stroke-width="2.25" />
@@ -79,9 +93,10 @@ export default {
79
93
  type: String,
80
94
  default: "https://help.ketekny.gr",
81
95
  },
82
- showBurger: {
83
- type: Boolean,
84
- default: true,
96
+ theme: {
97
+ type: String,
98
+ default: "dark",
99
+ validator: value => ["dark", "light"].includes(value),
85
100
  },
86
101
  },
87
102
  emits: ["select-menu", "logo-click", "signin", "signup", "signout", "edit-profile", "item-click"],