ui-svelte 0.2.5 → 0.2.6

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/dist/css/base.css CHANGED
@@ -4,13 +4,6 @@
4
4
  @apply bg-background text-on-background;
5
5
  }
6
6
 
7
- body:has(#popover) {
8
- overflow: hidden !important;
9
- }
10
-
11
- body:has(#popover).had-scroll {
12
- padding-right: var(--scrollbar-size, 6px) !important;
13
- }
14
7
 
15
8
  ::-webkit-scrollbar {
16
9
  width: var(--scrollbar-size, 6px);
@@ -40,7 +33,6 @@
40
33
  }
41
34
  }
42
35
 
43
- /* Base headings - responsive typography without margins */
44
36
  h1 {
45
37
  @apply font-extrabold tracking-tight;
46
38
  @apply text-[1.875rem] md:text-4xl lg:text-5xl;
@@ -77,7 +69,6 @@
77
69
  @apply leading-4 md:leading-5 lg:leading-6;
78
70
  }
79
71
 
80
- /* Text formatting */
81
72
  strong {
82
73
  @apply font-semibold;
83
74
  }
@@ -86,7 +77,6 @@
86
77
  @apply italic;
87
78
  }
88
79
 
89
- /* Utility classes */
90
80
  .snippet {
91
81
  @apply relative rounded bg-muted text-on-muted font-mono font-semibold;
92
82
  @apply px-[0.3rem] py-[0.2rem];
@@ -123,7 +113,6 @@
123
113
  @apply text-lg md:text-xl lg:text-2xl;
124
114
  }
125
115
 
126
- /* Blockquote - keeping minimal spacing */
127
116
  blockquote {
128
117
  @apply border-l-2 border-muted italic;
129
118
  @apply pl-4 md:pl-5 lg:pl-6;
@@ -136,11 +125,9 @@
136
125
  }
137
126
 
138
127
  .prose {
139
- /* Base font size and line height - responsive */
140
128
  @apply text-sm md:text-base lg:text-lg;
141
129
  @apply leading-6 md:leading-7 lg:leading-8;
142
130
 
143
- /* Headings */
144
131
  h1 {
145
132
  @apply scroll-m-20 font-extrabold tracking-tight;
146
133
  @apply text-[1.875rem] md:text-4xl lg:text-5xl;
@@ -188,27 +175,23 @@
188
175
  @apply mt-3 md:mt-3 lg:mt-4 mb-1.5;
189
176
  }
190
177
 
191
- /* Reset margins after headings */
192
178
  h2 + *,
193
179
  h3 + *,
194
180
  h4 + * {
195
181
  @apply mt-0;
196
182
  }
197
183
 
198
- /* Paragraphs */
199
184
  p {
200
185
  @apply text-on-muted;
201
186
  @apply mt-4 md:mt-5 lg:mt-6 mb-4 md:mb-5 lg:mb-6;
202
187
  }
203
188
 
204
- /* Lead paragraph */
205
189
  [class~='lead'] {
206
190
  @apply text-base md:text-lg lg:text-xl;
207
191
  @apply leading-7 md:leading-8 lg:leading-8;
208
192
  @apply mt-4 md:mt-6 lg:mt-6 mb-4 md:mb-6 lg:mb-6;
209
193
  }
210
194
 
211
- /* Lists */
212
195
  ul,
213
196
  ol {
214
197
  @apply mt-4 md:mt-5 lg:mt-6 mb-4 md:mb-5 lg:mb-6;
@@ -264,13 +247,11 @@
264
247
  @apply pl-5.5 md:pl-6.5 lg:pl-6.5;
265
248
  }
266
249
 
267
- /* Links */
268
250
  a {
269
251
  @apply font-medium text-primary underline underline-offset-4;
270
252
  @apply hover:text-primary/80 transition-colors;
271
253
  }
272
254
 
273
- /* Inline code */
274
255
  code {
275
256
  @apply relative rounded bg-muted text-on-muted font-mono font-semibold;
276
257
  @apply px-[0.3rem] py-[0.2rem];
@@ -285,7 +266,6 @@
285
266
  @apply text-base md:text-[1.125rem] lg:text-[1.3125rem];
286
267
  }
287
268
 
288
- /* Code blocks */
289
269
  pre {
290
270
  @apply overflow-x-auto rounded-lg bg-muted text-on-muted;
291
271
  @apply mt-5 md:mt-6 lg:mt-8 mb-5 md:mb-6 lg:mb-8;
@@ -299,14 +279,12 @@
299
279
  }
300
280
  }
301
281
 
302
- /* Blockquotes */
303
282
  blockquote {
304
283
  @apply border-l-4 border-muted italic text-on-muted;
305
284
  @apply mt-6 md:mt-8 lg:mt-10 mb-6 md:mb-8 lg:mb-10;
306
285
  @apply pl-5 md:pl-5 lg:pl-6;
307
286
  }
308
287
 
309
- /* Horizontal rules */
310
288
  hr {
311
289
  @apply border-muted;
312
290
  @apply my-10 md:my-12 lg:my-12;
@@ -316,7 +294,6 @@
316
294
  @apply mt-0;
317
295
  }
318
296
 
319
- /* Images and media */
320
297
  img,
321
298
  video,
322
299
  picture {
@@ -328,7 +305,6 @@
328
305
  @apply mt-0 mb-0;
329
306
  }
330
307
 
331
- /* Figures */
332
308
  figure {
333
309
  @apply mt-6 md:mt-8 lg:mt-8 mb-6 md:mb-8 lg:mb-8;
334
310
  }
@@ -343,7 +319,6 @@
343
319
  @apply mt-2 md:mt-3 lg:mt-3;
344
320
  }
345
321
 
346
- /* Tables */
347
322
  table {
348
323
  @apply w-full text-left;
349
324
  @apply text-[0.75rem] md:text-[0.875rem] lg:text-base;
@@ -379,7 +354,6 @@
379
354
  @apply pr-0;
380
355
  }
381
356
 
382
- /* Keyboard input */
383
357
  kbd {
384
358
  @apply font-mono font-semibold;
385
359
  @apply rounded bg-muted text-on-muted;
@@ -388,7 +362,6 @@
388
362
  @apply py-0.5 md:py-0.75 lg:py-1;
389
363
  }
390
364
 
391
- /* Text formatting */
392
365
  strong {
393
366
  @apply font-semibold;
394
367
  }
@@ -397,7 +370,6 @@
397
370
  @apply italic;
398
371
  }
399
372
 
400
- /* Reset first and last child margins */
401
373
  > :first-child {
402
374
  @apply mt-0;
403
375
  }
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { codeToHtml } from 'shiki';
3
3
  import { useClipboard } from '../hooks/use-clipboard.svelte.js';
4
- import { IconButton } from '../index.js';
4
+ import { Chip, IconButton } from '../index.js';
5
5
  import { Checkmark24RegularIcon, Copy24RegularIcon } from '../icons/index.js';
6
6
  import { theme } from '../stores/theme.svelte.js';
7
7
  import { cn } from '../utils/class-names.js';
@@ -57,16 +57,16 @@
57
57
  onmouseleave={() => (hover = false)}
58
58
  >
59
59
  {#if open}
60
- {#if !hover && !hideLang}
60
+ {#if (!hover && !hideLang && !clipboard.copied) || (disableCopy && !hideLang)}
61
61
  <div class="code-info">
62
- <div class="code-lang">{lang}</div>
62
+ <Chip variant="muted">{lang}</Chip>
63
63
  </div>
64
64
  {/if}
65
- {#if hover && !disableCopy}
65
+ {#if (hover && !disableCopy) || clipboard.copied || (!disableCopy && hideLang)}
66
66
  <div class="code-info">
67
67
  <IconButton
68
68
  onclick={handleCopy}
69
- variant="ghost"
69
+ variant="muted"
70
70
  size="sm"
71
71
  icon={clipboard.copied ? Checkmark24RegularIcon : Copy24RegularIcon}
72
72
  />
@@ -10,38 +10,41 @@
10
10
  .badge-text {
11
11
  @apply text-xs leading-none;
12
12
  }
13
- .is-top-left .badge-text {
13
+
14
+ /* Position styles */
15
+ .is-top-left .badge-content {
14
16
  @apply top-0 left-0 translate-x-[-50%] translate-y-[-50%];
15
17
  }
16
- .is-top-right .badge-text {
18
+ .is-top-right .badge-content {
17
19
  @apply top-0 right-0 translate-x-[50%] translate-y-[-50%];
18
20
  }
19
- .is-bottom-left .badge-text {
21
+ .is-bottom-left .badge-content {
20
22
  @apply bottom-0 left-0 translate-x-[-50%] translate-y-[50%];
21
23
  }
22
- .is-bottom-right .badge-text {
24
+ .is-bottom-right .badge-content {
23
25
  @apply bottom-0 right-0 translate-x-[50%] translate-y-[50%];
24
26
  }
25
27
 
26
- .is-primary .badge-text {
28
+ /* Variant styles */
29
+ .is-primary .badge-content {
27
30
  @apply bg-primary text-on-primary;
28
31
  }
29
- .is-secondary .badge-text {
32
+ .is-secondary .badge-content {
30
33
  @apply bg-secondary text-on-secondary;
31
34
  }
32
- .is-muted .badge-text {
35
+ .is-muted .badge-content {
33
36
  @apply bg-muted text-on-muted;
34
37
  }
35
- .is-success .badge-text {
38
+ .is-success .badge-content {
36
39
  @apply bg-success text-on-success;
37
40
  }
38
- .is-warning .badge-text {
41
+ .is-warning .badge-content {
39
42
  @apply bg-warning text-on-warning;
40
43
  }
41
- .is-danger .badge-text {
44
+ .is-danger .badge-content {
42
45
  @apply bg-danger text-on-danger;
43
46
  }
44
- .is-info .badge-text {
47
+ .is-info .badge-content {
45
48
  @apply bg-info text-on-info;
46
49
  }
47
50
  }
@@ -9,13 +9,17 @@
9
9
  icon: IconData;
10
10
  href?: string;
11
11
  onclick?: (item: BottomNavItem) => void;
12
+ badge?: string | number;
13
+ isFab?: boolean;
14
+ isActive?: boolean;
12
15
  };
13
16
 
14
17
  type Props = {
15
18
  items: BottomNavItem[];
16
- variant?: 'primary' | 'secondary' | 'muted';
19
+ variant?: 'primary' | 'secondary' | 'ghost' | 'line';
17
20
  size?: 'sm' | 'md' | 'lg';
18
- showLabels?: boolean;
21
+ isSolid?: boolean;
22
+ isBlock?: boolean;
19
23
  class?: string;
20
24
  };
21
25
 
@@ -24,13 +28,15 @@
24
28
  class: className,
25
29
  variant = 'primary',
26
30
  size = 'md',
27
- showLabels = true
31
+ isSolid = false,
32
+ isBlock = false
28
33
  }: Props = $props();
29
34
 
30
35
  const variantClasses = {
31
- primary: 'bottomnav-primary',
32
- secondary: 'bottomnav-secondary',
33
- muted: 'bottomnav-muted'
36
+ primary: 'is-primary',
37
+ secondary: 'is-secondary',
38
+ ghost: 'is-ghost',
39
+ line: 'is-line'
34
40
  };
35
41
 
36
42
  const sizeClasses = {
@@ -39,9 +45,15 @@
39
45
  lg: 'is-lg'
40
46
  };
41
47
 
42
- function isItemActive(href?: string): boolean {
43
- if (!href) return false;
44
- return page.url.pathname === href || page.url.pathname.startsWith(href + '/');
48
+ // Determine if any item has a label
49
+ const hasLabels = $derived(items.some((item) => item.label));
50
+
51
+ function isItemActive(item: BottomNavItem): boolean {
52
+ // If item has explicit isActive, use that
53
+ if (item.isActive !== undefined) return item.isActive;
54
+ // Otherwise check against current URL
55
+ if (!item.href) return false;
56
+ return page.url.pathname === item.href || page.url.pathname.startsWith(item.href + '/');
45
57
  }
46
58
 
47
59
  function handleItemClick(item: BottomNavItem) {
@@ -56,22 +68,37 @@
56
68
  'bottomnav',
57
69
  variantClasses[variant],
58
70
  sizeClasses[size],
59
- !showLabels && 'is-icon-only',
71
+ isSolid && 'is-solid',
72
+ !hasLabels && 'is-icon-only',
73
+ !isBlock && 'is-fixed',
60
74
  className
61
75
  )}
62
76
  >
63
77
  {#each items as item}
78
+ {@const active = isItemActive(item)}
79
+ {@const itemClass = cn('bottomnav-item', active && 'is-active', item.isFab && 'is-fab')}
80
+
64
81
  {#if item.href}
65
- <a href={item.href} class={cn('bottomnav-item', isItemActive(item.href) && 'is-active')}>
66
- <Icon icon={item.icon} class="bottomnav-icon" />
67
- {#if showLabels && item.label}
82
+ <a href={item.href} class={itemClass}>
83
+ <span class="bottomnav-icon-wrapper">
84
+ <Icon icon={item.icon} class="bottomnav-icon" />
85
+ {#if item.badge !== undefined}
86
+ <span class="bottomnav-badge">{item.badge}</span>
87
+ {/if}
88
+ </span>
89
+ {#if item.label}
68
90
  <span class="bottomnav-label">{item.label}</span>
69
91
  {/if}
70
92
  </a>
71
93
  {:else}
72
- <button type="button" class="bottomnav-item" onclick={() => handleItemClick(item)}>
73
- <Icon icon={item.icon} class="bottomnav-icon" />
74
- {#if showLabels && item.label}
94
+ <button type="button" class={itemClass} onclick={() => handleItemClick(item)}>
95
+ <span class="bottomnav-icon-wrapper">
96
+ <Icon icon={item.icon} class="bottomnav-icon" />
97
+ {#if item.badge !== undefined}
98
+ <span class="bottomnav-badge">{item.badge}</span>
99
+ {/if}
100
+ </span>
101
+ {#if item.label}
75
102
  <span class="bottomnav-label">{item.label}</span>
76
103
  {/if}
77
104
  </button>
@@ -26,10 +26,18 @@
26
26
  type Props = {
27
27
  items: MenuItem[];
28
28
  size?: 'sm' | 'md' | 'lg';
29
+ isSolid?: boolean;
30
+ variant?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger';
29
31
  class?: string;
30
32
  };
31
33
 
32
- const { items = [], class: className, size = 'md' }: Props = $props();
34
+ const {
35
+ items = [],
36
+ class: className,
37
+ size = 'md',
38
+ isSolid = false,
39
+ variant = 'muted'
40
+ }: Props = $props();
33
41
 
34
42
  let openSubmenuIndex = $state<number | null>(null);
35
43
  let triggerElements = $state<Record<number, HTMLElement>>({});
@@ -48,6 +56,18 @@
48
56
  lg: 'is-lg'
49
57
  };
50
58
 
59
+ const solidClass = $derived(isSolid ? 'is-filled' : 'is-default');
60
+
61
+ const variantClasses = {
62
+ primary: 'is-primary',
63
+ secondary: 'is-secondary',
64
+ muted: 'is-muted',
65
+ success: 'is-success',
66
+ info: 'is-info',
67
+ warning: 'is-warning',
68
+ danger: 'is-danger'
69
+ };
70
+
51
71
  const style = $derived(
52
72
  `top: ${position.top}px; left: ${position.left}px; ${position.width !== 'auto' ? `width: ${position.width}px;` : ''} transform-origin: ${position.isBottomHalf ? 'bottom' : 'top'} center;`
53
73
  );
@@ -165,11 +185,11 @@
165
185
  });
166
186
  </script>
167
187
 
168
- <nav class={cn('navmenu', sizeClasses[size], className)}>
188
+ <nav class={cn('navmenu', sizeClasses[size], solidClass, variantClasses[variant], className)}>
169
189
  {#each items as item, index}
170
190
  {#if item.href && !item.subitems && !item.megamenu}
171
191
  <a href={item.href} class={cn('navmenu-item', isItemActive(item.href) && 'is-active')}>
172
- <span class="navmenu-label">{item.label}</span>
192
+ <span class="navmenu-label" data-text={item.label}>{item.label}</span>
173
193
  </a>
174
194
  {:else}
175
195
  <button
@@ -182,7 +202,7 @@
182
202
  bind:this={triggerElements[index]}
183
203
  onclick={() => handleItemClick(item, index)}
184
204
  >
185
- <span class="navmenu-label">{item.label}</span>
205
+ <span class="navmenu-label" data-text={item.label}>{item.label}</span>
186
206
  {#if item.subitems || item.megamenu}
187
207
  <Icon
188
208
  icon={ChevronDown24RegularIcon}
@@ -36,6 +36,8 @@
36
36
  isWide?: boolean;
37
37
  isCompact?: boolean;
38
38
  isCollapsible?: boolean;
39
+ isSolid?: boolean;
40
+ variant?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger';
39
41
  };
40
42
 
41
43
  const {
@@ -44,7 +46,9 @@
44
46
  size = 'md',
45
47
  isWide,
46
48
  isCompact,
47
- isCollapsible
49
+ isCollapsible,
50
+ isSolid = false,
51
+ variant = 'muted'
48
52
  }: Props = $props();
49
53
 
50
54
  let openSubmenus = $state<Record<number, boolean>>({});
@@ -74,13 +78,25 @@
74
78
  lg: 'is-lg'
75
79
  };
76
80
 
81
+ const solidClass = $derived(isSolid ? 'is-solid' : '');
82
+
83
+ const variantClasses = {
84
+ primary: 'is-primary',
85
+ secondary: 'is-secondary',
86
+ muted: 'is-muted',
87
+ success: 'is-success',
88
+ info: 'is-info',
89
+ warning: 'is-warning',
90
+ danger: 'is-danger'
91
+ };
92
+
77
93
  function toggleSubmenu(index: number) {
78
94
  openSubmenus[index] = !openSubmenus[index];
79
95
  }
80
96
 
81
97
  function isItemActive(href?: string): boolean {
82
98
  if (!href) return false;
83
- return page.url.pathname.includes(href);
99
+ return page.url.pathname === href;
84
100
  }
85
101
 
86
102
  function toggleExpand() {
@@ -112,6 +128,8 @@
112
128
  class={cn(
113
129
  'sidenav',
114
130
  sizeClasses[size],
131
+ solidClass,
132
+ isSolid && variantClasses[variant],
115
133
  isCompact && 'is-compact',
116
134
  isCollapsible && 'is-collapsible',
117
135
  !isExpanded && 'is-collapsed',
@@ -26,6 +26,8 @@ type Props = {
26
26
  isWide?: boolean;
27
27
  isCompact?: boolean;
28
28
  isCollapsible?: boolean;
29
+ isSolid?: boolean;
30
+ variant?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger';
29
31
  };
30
32
  declare const SideNav: import("svelte").Component<Props, {}, "">;
31
33
  type SideNav = ReturnType<typeof SideNav>;
@@ -1,19 +1,26 @@
1
1
  @layer components {
2
2
  .bottomnav {
3
- @apply fixed bottom-0 left-0 right-0 z-50;
4
3
  @apply flex items-center justify-around;
5
4
  @apply bg-background text-on-background;
6
5
  @apply border-t border-muted;
7
6
  @apply shadow-lg;
8
- padding-bottom: env(safe-area-inset-bottom);
7
+
8
+ &.is-fixed {
9
+ @apply fixed bottom-0 left-0 right-0 z-50;
10
+ padding-bottom: env(safe-area-inset-bottom);
11
+ }
9
12
 
10
13
  .bottomnav-item {
11
14
  @apply flex flex-col items-center justify-center gap-1;
12
- @apply flex-1 py-2 px-1;
15
+ @apply flex-1 py-3 px-2;
13
16
  @apply cursor-pointer select-none outline-none;
14
17
  @apply transition-all duration-200;
15
18
  @apply relative;
16
19
 
20
+ .bottomnav-icon-wrapper {
21
+ @apply relative inline-flex;
22
+ }
23
+
17
24
  .bottomnav-icon {
18
25
  @apply h-6 w-6 shrink-0;
19
26
  @apply transition-all duration-200;
@@ -24,22 +31,55 @@
24
31
  @apply transition-all duration-200;
25
32
  }
26
33
 
34
+ .bottomnav-badge {
35
+ @apply absolute -top-1.5 -right-2;
36
+ @apply min-w-4 h-4 px-1;
37
+ @apply flex items-center justify-center;
38
+ @apply text-[10px] font-bold;
39
+ @apply bg-danger text-on-danger;
40
+ @apply rounded-full;
41
+ }
42
+
27
43
  &:hover {
28
44
  @apply bg-muted/50;
29
45
  }
30
46
 
31
- &.is-active {
32
- @apply text-primary;
47
+ /* FAB Style */
48
+ &.is-fab {
49
+ @apply flex-none;
50
+
51
+ &:hover {
52
+ @apply bg-transparent;
53
+ }
54
+
55
+ .bottomnav-icon-wrapper {
56
+ @apply w-12 h-12 -mt-6;
57
+ @apply flex items-center justify-center;
58
+ @apply bg-primary text-on-primary;
59
+ @apply rounded-full shadow-lg;
60
+ @apply transition-all duration-200;
61
+ }
33
62
 
34
63
  .bottomnav-icon {
35
- @apply text-primary;
64
+ @apply h-6 w-6 text-on-primary;
65
+ }
66
+
67
+ &:hover .bottomnav-icon-wrapper {
68
+ @apply shadow-xl shadow-primary/30;
69
+ transform: translateY(-2px);
70
+ }
71
+
72
+ &:active .bottomnav-icon-wrapper {
73
+ transform: translateY(0);
74
+ @apply shadow-md;
36
75
  }
37
76
  }
38
77
  }
39
78
 
79
+ /* Size Variants */
40
80
  &.is-sm {
41
81
  .bottomnav-item {
42
- @apply gap-0.5 py-1.5;
82
+ @apply gap-0.5 py-2 px-1;
43
83
 
44
84
  .bottomnav-icon {
45
85
  @apply h-5 w-5;
@@ -48,12 +88,16 @@
48
88
  .bottomnav-label {
49
89
  @apply text-[10px];
50
90
  }
91
+
92
+ &.is-fab .bottomnav-icon-wrapper {
93
+ @apply w-10 h-10 -mt-5;
94
+ }
51
95
  }
52
96
  }
53
97
 
54
98
  &.is-md {
55
99
  .bottomnav-item {
56
- @apply gap-1 py-2;
100
+ @apply gap-1 py-3 px-2;
57
101
 
58
102
  .bottomnav-icon {
59
103
  @apply h-6 w-6;
@@ -67,7 +111,7 @@
67
111
 
68
112
  &.is-lg {
69
113
  .bottomnav-item {
70
- @apply gap-1.5 py-3;
114
+ @apply gap-1.5 py-4 px-3;
71
115
 
72
116
  .bottomnav-icon {
73
117
  @apply h-7 w-7;
@@ -76,6 +120,14 @@
76
120
  .bottomnav-label {
77
121
  @apply text-sm;
78
122
  }
123
+
124
+ &.is-fab .bottomnav-icon-wrapper {
125
+ @apply w-14 h-14 -mt-7;
126
+
127
+ .bottomnav-icon {
128
+ @apply h-7 w-7;
129
+ }
130
+ }
79
131
  }
80
132
  }
81
133
 
@@ -89,7 +141,8 @@
89
141
  }
90
142
  }
91
143
 
92
- &.bottomnav-primary {
144
+ /* Variant: Primary */
145
+ &.is-primary {
93
146
  .bottomnav-item.is-active {
94
147
  @apply text-primary;
95
148
 
@@ -101,9 +154,14 @@
101
154
  @apply text-primary;
102
155
  }
103
156
  }
157
+
158
+ .bottomnav-item.is-fab .bottomnav-icon-wrapper {
159
+ @apply bg-primary;
160
+ }
104
161
  }
105
162
 
106
- &.bottomnav-secondary {
163
+ /* Variant: Secondary */
164
+ &.is-secondary {
107
165
  .bottomnav-item.is-active {
108
166
  @apply text-secondary;
109
167
 
@@ -115,20 +173,86 @@
115
173
  @apply text-secondary;
116
174
  }
117
175
  }
176
+
177
+ .bottomnav-item.is-fab .bottomnav-icon-wrapper {
178
+ @apply bg-secondary;
179
+ }
118
180
  }
119
181
 
120
- &.bottomnav-muted {
182
+ /* Variant: Ghost - just color change, no indicator */
183
+ &.is-ghost {
121
184
  .bottomnav-item.is-active {
122
- @apply bg-muted text-on-muted;
185
+ @apply text-on-surface;
123
186
 
124
187
  .bottomnav-icon {
125
- @apply text-on-muted;
188
+ @apply text-on-surface;
126
189
  }
127
190
 
128
191
  .bottomnav-label {
129
- @apply text-on-muted;
192
+ @apply text-on-surface font-semibold;
130
193
  }
131
194
  }
132
195
  }
196
+
197
+ /* Variant: Line - underline indicator */
198
+ &.is-line {
199
+ .bottomnav-item {
200
+ &::after {
201
+ content: '';
202
+ @apply absolute bottom-0 left-1/2 -translate-x-1/2;
203
+ @apply w-0 h-0.5 rounded-full;
204
+ @apply bg-primary;
205
+ @apply transition-all duration-200;
206
+ }
207
+
208
+ &.is-active {
209
+ @apply text-primary;
210
+
211
+ .bottomnav-icon {
212
+ @apply text-primary;
213
+ }
214
+
215
+ .bottomnav-label {
216
+ @apply text-primary;
217
+ }
218
+
219
+ &::after {
220
+ @apply w-8;
221
+ }
222
+ }
223
+ }
224
+ }
225
+
226
+ /* Solid modifier - adds background pill on active */
227
+ &.is-solid {
228
+ .bottomnav-item {
229
+ @apply relative isolate;
230
+
231
+ &::before {
232
+ content: '';
233
+ @apply absolute inset-x-2 top-1 bottom-1;
234
+ @apply rounded-xl;
235
+ @apply bg-transparent;
236
+ @apply transition-all duration-200;
237
+ z-index: -1;
238
+ }
239
+ }
240
+
241
+ &.is-primary .bottomnav-item.is-active:not(.is-fab)::before {
242
+ @apply bg-primary/10;
243
+ }
244
+
245
+ &.is-secondary .bottomnav-item.is-active:not(.is-fab)::before {
246
+ @apply bg-secondary/10;
247
+ }
248
+
249
+ &.is-ghost .bottomnav-item.is-active:not(.is-fab)::before {
250
+ @apply bg-muted;
251
+ }
252
+
253
+ &.is-line .bottomnav-item.is-active:not(.is-fab)::before {
254
+ @apply bg-primary/10;
255
+ }
256
+ }
133
257
  }
134
258
  }
@@ -14,6 +14,21 @@
14
14
  .navmenu-label {
15
15
  @apply text-base whitespace-nowrap;
16
16
  @apply font-light hover:font-semibold;
17
+ /* Prevent layout shift on font-weight change */
18
+ display: inline-flex;
19
+ flex-direction: column;
20
+ align-items: center;
21
+ justify-content: center;
22
+
23
+ &::after {
24
+ content: attr(data-text);
25
+ @apply font-semibold;
26
+ height: 0;
27
+ visibility: hidden;
28
+ overflow: hidden;
29
+ user-select: none;
30
+ pointer-events: none;
31
+ }
17
32
  }
18
33
 
19
34
  .navmenu-arrow {
@@ -225,4 +240,84 @@
225
240
  @apply px-3 py-2 text-xs font-semibold uppercase tracking-wide;
226
241
  }
227
242
  }
243
+
244
+ /* Filled state style - hover/active with background colors */
245
+ .navmenu.is-filled .navmenu-item {
246
+ .navmenu-label {
247
+ @apply font-normal hover:font-normal;
248
+ }
249
+
250
+ &:hover {
251
+ @apply bg-muted/50;
252
+ }
253
+
254
+ &.is-active {
255
+ @apply bg-muted;
256
+
257
+ .navmenu-label {
258
+ @apply font-normal;
259
+ }
260
+ }
261
+ }
262
+
263
+ /* Variant colors for filled style */
264
+ .navmenu.is-filled.is-primary {
265
+ .navmenu-item:hover {
266
+ @apply bg-primary/20;
267
+ }
268
+
269
+ .navmenu-item.is-active {
270
+ @apply bg-primary text-on-primary;
271
+ }
272
+ }
273
+
274
+ .navmenu.is-filled.is-secondary {
275
+ .navmenu-item:hover {
276
+ @apply bg-secondary/20;
277
+ }
278
+
279
+ .navmenu-item.is-active {
280
+ @apply bg-secondary text-on-secondary;
281
+ }
282
+ }
283
+
284
+ .navmenu.is-filled.is-success {
285
+ .navmenu-item:hover {
286
+ @apply bg-success/20;
287
+ }
288
+
289
+ .navmenu-item.is-active {
290
+ @apply bg-success text-on-success;
291
+ }
292
+ }
293
+
294
+ .navmenu.is-filled.is-info {
295
+ .navmenu-item:hover {
296
+ @apply bg-info/20;
297
+ }
298
+
299
+ .navmenu-item.is-active {
300
+ @apply bg-info text-on-info;
301
+ }
302
+ }
303
+
304
+ .navmenu.is-filled.is-warning {
305
+ .navmenu-item:hover {
306
+ @apply bg-warning/20;
307
+ }
308
+
309
+ .navmenu-item.is-active {
310
+ @apply bg-warning text-on-warning;
311
+ }
312
+ }
313
+
314
+ .navmenu.is-filled.is-danger {
315
+ .navmenu-item:hover {
316
+ @apply bg-danger/20;
317
+ }
318
+
319
+ .navmenu-item.is-active {
320
+ @apply bg-danger text-on-danger;
321
+ }
322
+ }
228
323
  }
@@ -300,4 +300,84 @@
300
300
  }
301
301
  }
302
302
  }
303
+
304
+ /* Solid state style - hover/active with background colors */
305
+ .sidenav.is-solid .sidenav-item {
306
+ .sidenav-content .sidenav-label {
307
+ @apply font-normal hover:font-normal;
308
+ }
309
+
310
+ &:hover {
311
+ @apply bg-muted/50;
312
+ }
313
+
314
+ &.is-active {
315
+ @apply bg-muted;
316
+
317
+ .sidenav-content .sidenav-label {
318
+ @apply font-normal;
319
+ }
320
+ }
321
+ }
322
+
323
+ /* Variant colors for solid style */
324
+ .sidenav.is-solid.is-primary {
325
+ .sidenav-item:hover {
326
+ @apply bg-primary/20;
327
+ }
328
+
329
+ .sidenav-item.is-active {
330
+ @apply bg-primary text-on-primary;
331
+ }
332
+ }
333
+
334
+ .sidenav.is-solid.is-secondary {
335
+ .sidenav-item:hover {
336
+ @apply bg-secondary/20;
337
+ }
338
+
339
+ .sidenav-item.is-active {
340
+ @apply bg-secondary text-on-secondary;
341
+ }
342
+ }
343
+
344
+ .sidenav.is-solid.is-success {
345
+ .sidenav-item:hover {
346
+ @apply bg-success/20;
347
+ }
348
+
349
+ .sidenav-item.is-active {
350
+ @apply bg-success text-on-success;
351
+ }
352
+ }
353
+
354
+ .sidenav.is-solid.is-info {
355
+ .sidenav-item:hover {
356
+ @apply bg-info/20;
357
+ }
358
+
359
+ .sidenav-item.is-active {
360
+ @apply bg-info text-on-info;
361
+ }
362
+ }
363
+
364
+ .sidenav.is-solid.is-warning {
365
+ .sidenav-item:hover {
366
+ @apply bg-warning/20;
367
+ }
368
+
369
+ .sidenav-item.is-active {
370
+ @apply bg-warning text-on-warning;
371
+ }
372
+ }
373
+
374
+ .sidenav.is-solid.is-danger {
375
+ .sidenav-item:hover {
376
+ @apply bg-danger/20;
377
+ }
378
+
379
+ .sidenav-item.is-active {
380
+ @apply bg-danger text-on-danger;
381
+ }
382
+ }
303
383
  }
@@ -2,19 +2,19 @@
2
2
  .tabs {
3
3
  @apply flex relative;
4
4
  &.is-top {
5
- @apply flex-col;
5
+ @apply flex-col gap-2;
6
6
  }
7
7
  &.is-bottom {
8
- @apply flex-col-reverse;
8
+ @apply flex-col-reverse gap-2;
9
9
  }
10
10
  &.is-start {
11
- @apply flex-row h-fit gap-1;
11
+ @apply flex-row h-fit gap-2;
12
12
  .tabs-list {
13
13
  @apply flex-col;
14
14
  }
15
15
  }
16
16
  &.is-end {
17
- @apply flex-row-reverse h-fit gap-1;
17
+ @apply flex-row-reverse h-fit gap-2;
18
18
  .tabs-list {
19
19
  @apply flex-col;
20
20
  }
@@ -1,17 +1,17 @@
1
1
  export const popover = (node) => {
2
2
  const targetEl = document.body;
3
- const hadScroll = document.documentElement.scrollHeight > window.innerHeight;
4
3
  node.id = 'popover';
5
4
  targetEl?.appendChild(node);
6
- if (hadScroll) {
7
- targetEl.classList.add('had-scroll');
5
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
6
+ if (scrollbarWidth > 0) {
7
+ document.body.style.paddingRight = `${scrollbarWidth}px`;
8
8
  }
9
+ document.body.style.overflow = 'hidden';
9
10
  return {
10
11
  destroy() {
11
- if (hadScroll) {
12
- targetEl.classList.remove('had-scroll');
13
- }
14
12
  node.remove();
13
+ document.body.style.overflow = '';
14
+ document.body.style.paddingRight = '';
15
15
  }
16
16
  };
17
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-svelte",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "author": {
5
5
  "name": "SappsDev",
6
6
  "email": "info@sappsdev.com"