@kubex/zinc 1.0.115 → 1.0.117

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.
Files changed (102) hide show
  1. package/dist/custom-elements.json +476 -287
  2. package/dist/vscode.html-custom-data.json +52 -52
  3. package/dist/web-types.json +116 -86
  4. package/dist/zn.d.ts +63 -11
  5. package/dist/zn.min.css +1 -1
  6. package/dist/zn.min.js +1096 -1087
  7. package/docs/_includes/default.njk +3 -4
  8. package/docs/assets/styles/docs.css +13 -13
  9. package/docs/pages/components/absolute-container.md +2 -2
  10. package/docs/pages/components/alert.md +5 -5
  11. package/docs/pages/components/audio-select.md +4 -4
  12. package/docs/pages/components/button-group.md +1 -31
  13. package/docs/pages/components/button.md +13 -12
  14. package/docs/pages/components/checkbox.md +4 -4
  15. package/docs/pages/components/chip.md +14 -14
  16. package/docs/pages/components/data-table.md +4 -4
  17. package/docs/pages/components/datepicker.md +5 -5
  18. package/docs/pages/components/dialog.md +2 -2
  19. package/docs/pages/components/file.md +81 -9
  20. package/docs/pages/components/header.md +4 -6
  21. package/docs/pages/components/hover-container.md +1 -1
  22. package/docs/pages/components/icon.md +14 -0
  23. package/docs/pages/components/inline-edit.md +4 -4
  24. package/docs/pages/components/item.md +2 -2
  25. package/docs/pages/components/menu.md +2 -2
  26. package/docs/pages/components/note.md +12 -12
  27. package/docs/pages/components/page.md +3 -3
  28. package/docs/pages/components/pagination.md +6 -6
  29. package/docs/pages/components/pane.md +3 -6
  30. package/docs/pages/components/panel.md +10 -12
  31. package/docs/pages/components/priority-list.md +3 -3
  32. package/docs/pages/components/radio-group.md +4 -4
  33. package/docs/pages/components/radio.md +4 -4
  34. package/docs/pages/components/scroll-container.md +10 -10
  35. package/docs/pages/components/select.md +5 -5
  36. package/docs/pages/components/split-button.md +4 -4
  37. package/docs/pages/components/tabs.md +3 -3
  38. package/docs/pages/components/tile.md +13 -13
  39. package/docs/pages/components/toggle.md +4 -4
  40. package/docs/pages/components/translations.md +5 -5
  41. package/docs/pages/components/vertical-stepper.md +3 -3
  42. package/docs/pages/components/well.md +19 -19
  43. package/docs/pages/getting-started/example-layout.md +16 -16
  44. package/docs/pages/getting-started/example-page.md +1 -3
  45. package/package.json +2 -1
  46. package/scss/_root.scss +87 -60
  47. package/scss/shared/_button.scss +6 -1
  48. package/scss/shared/_form-elements.scss +1 -0
  49. package/scss/shared/_table.scss +1 -1
  50. package/scss/shared/layout.scss +5 -4
  51. package/scss/themes/_dark.scss +4 -4
  52. package/scss/themes/_light.scss +70 -287
  53. package/src/components/bulk-actions/bulk-actions.component.ts +0 -1
  54. package/src/components/button/button.component.ts +40 -10
  55. package/src/components/button/button.scss +67 -103
  56. package/src/components/button-menu/button-menu.component.ts +2 -6
  57. package/src/components/collapsible/collapsible.scss +1 -1
  58. package/src/components/cols/cols.scss +1 -1
  59. package/src/components/content-block/content-block.component.ts +1 -2
  60. package/src/components/data-table/data-table.component.ts +4 -8
  61. package/src/components/data-table/data-table.test.ts +7 -0
  62. package/src/components/data-table-filter/data-table-filter.component.ts +1 -1
  63. package/src/components/data-table-sort/data-table-sort.component.ts +2 -2
  64. package/src/components/dialog/dialog.component.ts +0 -1
  65. package/src/components/editor/modules/dialog/dialog.component.ts +0 -1
  66. package/src/components/editor/modules/dialog/dialog.scss +1 -1
  67. package/src/components/editor/modules/toolbar/toolbar.component.ts +0 -1
  68. package/src/components/expanding-action/expanding-action.component.ts +0 -3
  69. package/src/components/expanding-action/expanding-action.scss +0 -5
  70. package/src/components/file/file.component.ts +158 -17
  71. package/src/components/file/file.scss +122 -29
  72. package/src/components/header/header.component.ts +2 -8
  73. package/src/components/header/header.scss +4 -25
  74. package/src/components/icon/icon.component.ts +80 -1
  75. package/src/components/icon/icon.scss +8 -0
  76. package/src/components/icon/icon.test.ts +24 -0
  77. package/src/components/icon-picker/icon-picker.component.ts +0 -1
  78. package/src/components/inline-edit/inline-edit.component.ts +3 -4
  79. package/src/components/input/input.scss +21 -6
  80. package/src/components/input-group/input-group.scss +2 -1
  81. package/src/components/markdown-editor/markdown-editor.component.ts +1 -5
  82. package/src/components/menu/menu.scss +1 -1
  83. package/src/components/menu-item/menu-item.scss +4 -4
  84. package/src/components/navbar/navbar.component.ts +20 -0
  85. package/src/components/navbar/navbar.scss +31 -17
  86. package/src/components/navbar/navbar.test.ts +72 -0
  87. package/src/components/note/note.component.ts +0 -1
  88. package/src/components/page/page.component.ts +43 -12
  89. package/src/components/page/page.scss +21 -17
  90. package/src/components/page/page.test.ts +18 -1
  91. package/src/components/page-nav/page-nav.scss +6 -6
  92. package/src/components/panel/panel.component.ts +3 -5
  93. package/src/components/panel/panel.scss +3 -3
  94. package/src/components/panel/panel.test.ts +14 -0
  95. package/src/components/query-builder/query-builder.component.ts +0 -1
  96. package/src/components/slideout/slideout.component.ts +0 -1
  97. package/src/components/sp/sp.scss +2 -2
  98. package/src/components/tab/tab.component.ts +2 -0
  99. package/src/components/tabs/tabs.component.ts +1 -1
  100. package/src/components/tile/tile.scss +1 -1
  101. package/src/components/translation-group/translation-group.component.ts +0 -3
  102. package/src/components/translations/translations.component.ts +0 -3
@@ -1,15 +1,19 @@
1
1
  import {choose} from 'lit/directives/choose.js';
2
2
  import {classMap} from "lit/directives/class-map.js";
3
3
  import {type CSSResultGroup, html, render, unsafeCSS} from 'lit';
4
+ import {icons} from 'lucide';
4
5
  import {property} from 'lit/decorators.js';
5
6
  import {sha256} from '../../utilities/sha256';
6
7
  import {styleMap} from "lit/directives/style-map.js";
8
+ import {unsafeSVG} from 'lit/directives/unsafe-svg.js';
7
9
  import ZincElement from '../../internal/zinc-element';
10
+ import type {IconNode, SVGProps} from 'lucide';
8
11
 
9
12
  import styles from './icon.scss';
10
13
 
11
14
  type IconLibrary = "src" | "material" | "material-outlined" | "material-round" | "material-sharp" |
12
- "material-two-tone" | "material-symbols-outlined" | "gravatar" | "libravatar" | "avatar" | "brands" | "line";
15
+ "material-two-tone" | "material-symbols-outlined" | "gravatar" | "libravatar" | "avatar" | "brands" | "line" |
16
+ "lucide";
13
17
 
14
18
  export type IconColor =
15
19
  "default"
@@ -116,6 +120,9 @@ export default class ZnIcon extends ZincElement {
116
120
  return "avatar";
117
121
  case "line":
118
122
  return "line";
123
+ case "lucide":
124
+ case "lu":
125
+ return "lucide";
119
126
  case 'brand':
120
127
  case 'brands':
121
128
  return "brands";
@@ -251,6 +258,7 @@ export default class ZnIcon extends ZincElement {
251
258
  ["material-two-tone", () => html`<i part="icon" class="mi mi--two-tone">${this.src}</i>`],
252
259
  ["brands", () => html`<i part="icon" class="kb">${this.src}</i>`],
253
260
  ["line", () => html`<i part="icon" class="lni lni-${this.src}"></i>`],
261
+ ["lucide", () => this.renderLucideIcon()],
254
262
  ["material-symbols-outlined", () => html`<i part="icon" class="mi mi--symbol-outlined">${this.src}</i>`],
255
263
  ["gravatar", () => html`<img part="icon" alt="${this.alt}"
256
264
  src="https://www.gravatar.com/avatar/${this.src}?s=${this.size}${this.gravatarOptions}"/>`],
@@ -285,6 +293,77 @@ export default class ZnIcon extends ZincElement {
285
293
  return avatar.slice(0, 2).toUpperCase()
286
294
  }
287
295
 
296
+ private renderLucideIcon() {
297
+ const icon = this.getLucideIcon(this.src);
298
+
299
+ if (!icon) {
300
+ return html`Icon not found: ${this.src ?? ''}`;
301
+ }
302
+
303
+ return unsafeSVG(this.getLucideSvg(icon));
304
+ }
305
+
306
+ private getLucideIcon(src = ''): IconNode | undefined {
307
+ if (!src) {
308
+ return undefined;
309
+ }
310
+
311
+ const candidates = [src, this.toPascalCase(src)];
312
+ const iconSet = icons as Record<string, IconNode>;
313
+
314
+ for (const candidate of candidates) {
315
+ if (Object.prototype.hasOwnProperty.call(iconSet, candidate)) {
316
+ return iconSet[candidate];
317
+ }
318
+ }
319
+
320
+ return undefined;
321
+ }
322
+
323
+ private toPascalCase(input: string) {
324
+ return input
325
+ .split(/[-_\s]+/)
326
+ .filter(Boolean)
327
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
328
+ .join('');
329
+ }
330
+
331
+ private getLucideSvg(icon: IconNode) {
332
+ const children = icon
333
+ .map(([tag, attrs]) => `<${tag}${this.getSvgAttributes(attrs)} />`)
334
+ .join('');
335
+
336
+ return `
337
+ <svg
338
+ part="icon"
339
+ class="lucide"
340
+ xmlns="http://www.w3.org/2000/svg"
341
+ viewBox="0 0 24 24"
342
+ fill="none"
343
+ stroke="currentColor"
344
+ stroke-width="2"
345
+ stroke-linecap="round"
346
+ stroke-linejoin="round"
347
+ aria-hidden="true"
348
+ >${children}</svg>
349
+ `;
350
+ }
351
+
352
+ private getSvgAttributes(attrs: SVGProps) {
353
+ return Object.entries(attrs)
354
+ .filter(([, value]) => value !== undefined)
355
+ .map(([name, value]) => ` ${name}="${this.escapeSvgAttribute(String(value))}"`)
356
+ .join('');
357
+ }
358
+
359
+ private escapeSvgAttribute(value: string) {
360
+ return value
361
+ .replace(/&/g, '&amp;')
362
+ .replace(/"/g, '&quot;')
363
+ .replace(/</g, '&lt;')
364
+ .replace(/>/g, '&gt;');
365
+ }
366
+
288
367
 
289
368
  protected getColorForAvatar(avatarInitials: string) {
290
369
  const colors = [
@@ -219,6 +219,14 @@
219
219
  -webkit-font-smoothing: antialiased;
220
220
  }
221
221
 
222
+ .lucide {
223
+ display: block;
224
+ width: 100%;
225
+ height: 100%;
226
+ color: currentColor;
227
+ stroke: currentColor;
228
+ }
229
+
222
230
  img {
223
231
  max-width: 100%;
224
232
  vertical-align: middle;
@@ -30,4 +30,28 @@ describe('<zn-icon>', () => {
30
30
  'https://seccdn.libravatar.org/avatar/04e8703fcfb60849fb5c596abc8f3d7547c7e0099c3806a3ffec77a95dc02e25?s=48&d=identicon'
31
31
  );
32
32
  });
33
+
34
+ it('should render Lucide icons as inline SVGs', async () => {
35
+ const el = await fixture<ZnIcon>(html`
36
+ <zn-icon src="cloud-upload" library="lucide" size="48"></zn-icon>
37
+ `);
38
+
39
+ const svg = el.shadowRoot?.querySelector('svg.lucide');
40
+
41
+ expect(svg).to.exist;
42
+ expect(svg?.getAttribute('part')).to.equal('icon');
43
+ expect(svg?.querySelectorAll('path').length).to.be.greaterThan(0);
44
+ });
45
+
46
+ it('should support Lucide shorthand notation', async () => {
47
+ const el = await fixture<ZnIcon>(html`
48
+ <zn-icon src="circle-alert@lu" size="48"></zn-icon>
49
+ `);
50
+
51
+ const svg = el.shadowRoot?.querySelector('svg.lucide');
52
+
53
+ expect(el.library).to.equal('lucide');
54
+ expect(el.src).to.equal('circle-alert');
55
+ expect(svg).to.exist;
56
+ });
33
57
  });
@@ -261,7 +261,6 @@ export default class ZnIconPicker extends ZincElement implements ZincFormControl
261
261
  <span class="icon-picker__edit-text">Click to edit</span>
262
262
  <zn-button
263
263
  class="icon-picker__clear"
264
- size="x-small"
265
264
  color="transparent"
266
265
  icon="close"
267
266
  icon-size="16"
@@ -362,20 +362,19 @@ export default class ZnInlineEdit extends ZincElement implements ZincFormControl
362
362
  ${!this.isEditing ?
363
363
  html`
364
364
  ${hasEditText ? html`
365
- <zn-button @click="${this.disabled ? undefined : this.handleEditClick}" size="x-small"
365
+ <zn-button @click="${this.disabled ? undefined : this.handleEditClick}"
366
366
  color="secondary">
367
367
  ${this.editText}
368
368
  </zn-button>` : html`
369
369
  <zn-button @click="${this.disabled ? undefined : this.handleEditClick}"
370
370
  class="button--edit"
371
371
  icon="edit"
372
- size="x-small"
373
372
  icon-size="20"
374
373
  color="secondary"></zn-button>`}` :
375
374
  html`
376
- <zn-button type="submit" @click="${this.handleSubmitClick}" icon="check" size="x-small" icon-size="20"
375
+ <zn-button type="submit" @click="${this.handleSubmitClick}" icon="check" icon-size="20"
377
376
  color="secondary"></zn-button>
378
- <zn-button type="button" @click="${this.handleCancelClick}" icon="close" size="x-small" icon-size="20"
377
+ <zn-button type="button" @click="${this.handleCancelClick}" icon="close" icon-size="20"
379
378
  color="secondary"></zn-button>`}
380
379
  </div>
381
380
  </div>
@@ -91,6 +91,7 @@
91
91
  margin: 0;
92
92
  cursor: inherit;
93
93
  -webkit-appearance: none;
94
+ line-height: 24px;
94
95
  }
95
96
 
96
97
  /* Color input text should use monospace font */
@@ -176,7 +177,7 @@ input[type='text'].input__control[data-color-input] {
176
177
 
177
178
  .input--x-small .input__prefix ::slotted(*),
178
179
  .input__prefix-default {
179
- margin-inline-start: var(--zn-input-spacing-x-small);
180
+ margin-inline-start: var(--zn-spacing-x-small);
180
181
  }
181
182
 
182
183
  .input--x-small .input__suffix ::slotted(*),
@@ -244,12 +245,12 @@ input[type='text'].input__control[data-color-input] {
244
245
 
245
246
  .input--medium .input__prefix ::slotted(*),
246
247
  .input__prefix-default {
247
- margin-inline-start: var(--zn-input-spacing-medium);
248
+ margin-inline-start: var(--zn-spacing-small);
248
249
  }
249
250
 
250
251
  .input--medium .input__suffix ::slotted(*),
251
252
  .input__suffix-default {
252
- margin-inline-end: var(--zn-input-spacing-medium);
253
+ margin-inline-end: var(--zn-spacing-small);
253
254
  }
254
255
 
255
256
  .input--medium .input__suffix ::slotted(zn-button),
@@ -260,6 +261,20 @@ input[type='text'].input__control[data-color-input] {
260
261
  margin-inline-start: 0;
261
262
  }
262
263
 
264
+ .input__suffix ::slotted(zn-button),
265
+ .input__prefix ::slotted(zn-button) {
266
+ --zn-button-border-color: rgb(var(--zn-color-border));
267
+ --zn-button-border-width: 0;
268
+ }
269
+
270
+ .input__suffix ::slotted(zn-button) {
271
+ --zn-button-border-left-width: 1px;
272
+ }
273
+
274
+ .input__prefix ::slotted(zn-button) {
275
+ --zn-button-border-right-width: 1px;
276
+ }
277
+
263
278
  .input--large {
264
279
  border-radius: var(--zn-input-border-radius-large);
265
280
  font-size: var(--zn-input-font-size-large);
@@ -398,15 +413,15 @@ input[type='date']::-webkit-calendar-picker-indicator {
398
413
  }
399
414
 
400
415
  .input--small .input__color-swatch {
401
- margin-inline-start: var(--zn-input-spacing-small);
416
+ margin-inline-start: var(--zn-spacing-x2-small);
402
417
  }
403
418
 
404
419
  .input--medium .input__color-swatch {
405
- margin-inline-start: var(--zn-input-spacing-medium);
420
+ margin-inline-start: var(--zn-spacing-x-small);
406
421
  }
407
422
 
408
423
  .input--large .input__color-swatch {
409
- margin-inline-start: var(--zn-input-spacing-medium);
424
+ margin-inline-start: var(--zn-spacing-small);
410
425
  }
411
426
 
412
427
  .input__color-swatch-inner {
@@ -10,6 +10,7 @@
10
10
  flex-wrap: nowrap;
11
11
  align-items: center;
12
12
  width: 100%;
13
+ gap: var(--zn-spacing-small);
13
14
  }
14
15
 
15
16
  /* Gap variants */
@@ -89,7 +90,7 @@
89
90
  /* Button overrides - don't join to other inputs */
90
91
  ::slotted(zn-button[data-zn-input-group__input]) {
91
92
  flex: 0 0 auto;
92
- margin-inline-start: var(--zn-spacing-x-small);
93
+ margin-inline-start: 0;
93
94
  margin-inline-end: 0;
94
95
  }
95
96
 
@@ -3,7 +3,6 @@ import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
3
3
  import {defaultValue} from "../../internal/default-value";
4
4
  import {FormControlController} from "../../internal/form";
5
5
  import {HasSlotController} from "../../internal/slot";
6
- import {ifDefined} from "lit/directives/if-defined.js";
7
6
  import {marked} from "marked";
8
7
  import {property, query} from 'lit/decorators.js';
9
8
  import {watch} from "../../internal/watch";
@@ -275,11 +274,10 @@ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl
275
274
  ${hasHeader ? html`
276
275
  <zn-header class=${classMap({
277
276
  panel__header: true,
278
- 'panel__header--underline': this.underlineHeader,
277
+ 'panel__header--underline': !this.headerBorderless,
279
278
  })}
280
279
  icon=${this.icon}
281
280
  caption=${this.caption}
282
- description=${ifDefined(this.description)}
283
281
  transparent>
284
282
  ${hasActionSlot ? html`
285
283
  <slot name="actions" slot="actions" class="panel__header__actions"></slot>` : null}
@@ -306,7 +304,6 @@ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl
306
304
  <zn-button
307
305
  class="markdown-editor__expand-btn"
308
306
  type="button"
309
- size="medium"
310
307
  color="secondary"
311
308
  icon=${this.expanded ? 'close_fullscreen' : 'open_in_full'}
312
309
  icon-size="18"
@@ -379,7 +376,6 @@ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl
379
376
  })}
380
377
  data-mode=${mode}
381
378
  type="button"
382
- size="medium"
383
379
  color="default"
384
380
  ?outline=${!isActive}
385
381
  square
@@ -10,5 +10,5 @@
10
10
  overflow: auto;
11
11
  overscroll-behavior: none;
12
12
  box-shadow: var(--zn-shadow-medium);
13
- background-color: rgb(var(--zn-panel, 255, 255, 255));
13
+ background-color: rgb(var(--zn-color-navigation, 255, 255, 255));
14
14
  }
@@ -4,7 +4,7 @@
4
4
  --submenu-offset: -2px;
5
5
  --color: rgba(var(--zn-text), var(--zn-text-opacity));
6
6
  --hover-color: rgb(var(--zn-primary));
7
- --hover-bg: rgba(var(--zn-panel-highlight), var(--zn-panel-highlight-opacity));
7
+ --hover-bg: rgba(var(--zn-color-navigation-highlight), var(--zn-panel-highlight-opacity));
8
8
  display: block;
9
9
  }
10
10
 
@@ -154,9 +154,9 @@
154
154
  bottom: 0;
155
155
  left: 0;
156
156
  clip-path: polygon(
157
- var(--safe-triangle-cursor-x, 0) var(--safe-triangle-cursor-y, 0),
158
- var(--safe-triangle-submenu-start-x, 0) var(--safe-triangle-submenu-start-y, 0),
159
- var(--safe-triangle-submenu-end-x, 0) var(--safe-triangle-submenu-end-y, 0)
157
+ var(--safe-triangle-cursor-x, 0) var(--safe-triangle-cursor-y, 0),
158
+ var(--safe-triangle-submenu-start-x, 0) var(--safe-triangle-submenu-start-y, 0),
159
+ var(--safe-triangle-submenu-end-x, 0) var(--safe-triangle-submenu-end-y, 0)
160
160
  );
161
161
  }
162
162
 
@@ -253,6 +253,24 @@ export default class ZnNavbar extends ZincElement {
253
253
  return moreWidth;
254
254
  }
255
255
 
256
+ private _syncLastVisibleItem() {
257
+ const items = this._navItems?.querySelectorAll<HTMLElement>(':scope > li') || [];
258
+ let lastVisibleItem: HTMLElement | null = null;
259
+
260
+ for (const item of items) {
261
+ item.classList.remove('last-visible');
262
+
263
+ if (item.classList.contains('more') || item.classList.contains('hidden') || item.id === 'dropdown-item') {
264
+ continue;
265
+ }
266
+
267
+ lastVisibleItem = item;
268
+ }
269
+
270
+ lastVisibleItem?.classList.add('last-visible');
271
+ this._navItems?.classList.toggle('more-only', !lastVisibleItem && this._navItems.classList.contains('has-hidden'));
272
+ }
273
+
256
274
  private _getExpandableWidth(containerWidth: number): number {
257
275
  const expandableWidth = this._expandableMargin + (this._expandable?.getBoundingClientRect().width || this._expandable?.offsetWidth || 0);
258
276
  let fillWidth = 0;
@@ -298,6 +316,7 @@ export default class ZnNavbar extends ZincElement {
298
316
 
299
317
  if (!hasHidden && this._totalItemWidth <= availableWithoutMore) {
300
318
  this._navItems?.classList.toggle('has-hidden', false)
319
+ this._syncLastVisibleItem();
301
320
  return
302
321
  }
303
322
 
@@ -331,6 +350,7 @@ export default class ZnNavbar extends ZincElement {
331
350
  }
332
351
  }
333
352
  this._navItems?.classList.toggle('has-hidden', hasHidden && hideRemaining)
353
+ this._syncLastVisibleItem();
334
354
  }
335
355
 
336
356
  // Clones in the extended menu are static snapshots, so mirror the active
@@ -6,7 +6,7 @@
6
6
 
7
7
  ul.navbar {
8
8
  padding: 0;
9
- margin: 0 calc(var(--zn-base-gap) + var(--zn-gutter));
9
+ margin: 0 var(--zn-base-gap);
10
10
  }
11
11
  }
12
12
 
@@ -68,7 +68,7 @@
68
68
  }
69
69
 
70
70
  :host:not([stacked]) .navbar {
71
- margin: 0 0 0 var(--zn-spacing-medium);
71
+ margin: 0 0 0 var(--zn-base-gap);
72
72
  padding-left: 0;
73
73
 
74
74
  &--flush {
@@ -81,6 +81,7 @@ ul.navbar {
81
81
  padding-top: 0;
82
82
  font-size: var(--zn-font-size-medium);
83
83
  white-space: nowrap;
84
+ color: rgb(var(--zn-color-muted-text));
84
85
 
85
86
  a {
86
87
  text-decoration: none;
@@ -117,7 +118,7 @@ ul.navbar {
117
118
 
118
119
  li {
119
120
  border-bottom: 1px solid rgb(var(--zn-border-color));
120
- padding: var(--zn-spacing-medium);
121
+ padding: var(--zn-base-px);
121
122
 
122
123
  &.active:before, &.zn-tb-active:before {
123
124
  display: none;
@@ -133,12 +134,6 @@ ul.navbar {
133
134
  }
134
135
  }
135
136
 
136
- &--slim {
137
- li {
138
- padding: var(--zn-spacing-small);
139
- }
140
- }
141
-
142
137
  }
143
138
  }
144
139
 
@@ -147,8 +142,6 @@ ul.navbar {
147
142
  &--border {
148
143
  border-right: 1px solid rgb(var(--zn-border-color));
149
144
  }
150
-
151
- //margin-right: var(--zn-spacing-medium);
152
145
  }
153
146
  }
154
147
 
@@ -157,8 +150,6 @@ ul.navbar {
157
150
  &--border {
158
151
  border-left: 1px solid rgb(var(--zn-border-color));
159
152
  }
160
-
161
- // margin-left: var(--zn-spacing-medium);
162
153
  }
163
154
  }
164
155
 
@@ -253,12 +244,27 @@ ul.navbar.navbar--stacked.navbar--icon-bar {
253
244
 
254
245
  &:first-of-type {
255
246
  border-left-width: 1px;
247
+ border-top-left-radius: 6px;
248
+ }
249
+
250
+ &:last-of-type {
251
+ border-top-right-radius: 6px;
256
252
  }
257
253
 
258
254
  &.active, &.zn-tb-active {
255
+ color: rgb(var(--zn-color-text));
259
256
  border-bottom: 1px solid rgb(var(--navbar-active-colour));
260
257
  }
261
258
  }
259
+
260
+ &:not(.has-hidden) > li.last-visible {
261
+ border-top-right-radius: 6px;
262
+ }
263
+
264
+ &.more-only > li.more {
265
+ border-left-width: 1px;
266
+ border-top-left-radius: 6px;
267
+ }
262
268
  }
263
269
  }
264
270
 
@@ -299,13 +305,21 @@ ul.navbar.navbar--stacked.navbar--icon-bar {
299
305
 
300
306
  user-select: none;
301
307
  font-weight: 500;
302
- padding: 0 calc(var(--zn-base-gap, 8px));
303
- background: rgba(0, 0, 0, 0.05);
308
+ padding: 0 calc(var(--zn-base-px, 8px) * 2);
309
+ background: rgb(var(--zn-color-tab));
304
310
  transition: background-color 100ms ease-in-out;
305
311
  position: relative;
306
312
 
307
313
  &:hover {
308
- background: rgba(0, 0, 0, 0.03);
314
+ background: rgba(var(--zn-color-tab), 0.5);
315
+
316
+ &::after {
317
+ content: "";
318
+ position: absolute;
319
+ inset: 0;
320
+ background: rgb(var(--navbar-active-colour));
321
+ z-index: -1;
322
+ }
309
323
  }
310
324
 
311
325
  display: flex;
@@ -483,7 +497,7 @@ button[popovertarget] {
483
497
  display: flex;
484
498
  align-items: center;
485
499
  width: fit-content;
486
- height: 100%;
500
+ height: 40px;
487
501
  gap: var(--zn-spacing-2x-small);
488
502
  padding-right: calc(var(--zn-gutter, 0) + var(--zn-base-gap, 0));
489
503
  }
@@ -119,6 +119,78 @@ describe('<zn-navbar>', () => {
119
119
  expect(nav.classList.contains('has-hidden')).to.equal(true);
120
120
  });
121
121
 
122
+ it('rounds the last visible nav item when items are hidden behind more', async () => {
123
+ const el = await fixture<ZnNavbar>(html`
124
+ <zn-navbar>
125
+ <li>One</li>
126
+ <li>Two</li>
127
+ <li>Three</li>
128
+ </zn-navbar>
129
+ `);
130
+ await aTimeout(150);
131
+
132
+ const nav = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar')!;
133
+ const more = nav.querySelector<HTMLElement>(':scope > li.more')!;
134
+ const items = Array.from(nav.querySelectorAll<HTMLElement>(':scope > li:not(.more)'));
135
+
136
+ Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 250});
137
+ Object.defineProperty(more, 'offsetWidth', {configurable: true, get: () => 60});
138
+ Object.defineProperty(more, 'getBoundingClientRect', {
139
+ configurable: true,
140
+ value: () => ({width: 60})
141
+ });
142
+ items.forEach(item => {
143
+ Object.defineProperty(item, 'offsetWidth', {configurable: true, get: () => 100});
144
+ Object.defineProperty(item, 'getBoundingClientRect', {
145
+ configurable: true,
146
+ value: () => ({width: 100})
147
+ });
148
+ });
149
+
150
+ el.handleResize();
151
+
152
+ expect(nav.classList.contains('has-hidden')).to.equal(true);
153
+ expect(items[0].classList.contains('last-visible')).to.equal(true);
154
+ expect(items[1].classList.contains('hidden')).to.equal(true);
155
+ expect(getComputedStyle(items[0]).borderTopRightRadius).to.equal('6px');
156
+ });
157
+
158
+ it('keeps the left border when only the more button is visible', async () => {
159
+ const el = await fixture<ZnNavbar>(html`
160
+ <zn-navbar>
161
+ <li>One</li>
162
+ <li>Two</li>
163
+ </zn-navbar>
164
+ `);
165
+ await aTimeout(150);
166
+
167
+ const nav = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar')!;
168
+ const more = nav.querySelector<HTMLElement>(':scope > li.more')!;
169
+ const items = Array.from(nav.querySelectorAll<HTMLElement>(':scope > li:not(.more)'));
170
+
171
+ Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 100});
172
+ Object.defineProperty(more, 'offsetWidth', {configurable: true, get: () => 60});
173
+ Object.defineProperty(more, 'getBoundingClientRect', {
174
+ configurable: true,
175
+ value: () => ({width: 60})
176
+ });
177
+ items.forEach(item => {
178
+ Object.defineProperty(item, 'offsetWidth', {configurable: true, get: () => 150});
179
+ Object.defineProperty(item, 'getBoundingClientRect', {
180
+ configurable: true,
181
+ value: () => ({width: 150})
182
+ });
183
+ });
184
+
185
+ el.handleResize();
186
+
187
+ expect(nav.classList.contains('has-hidden')).to.equal(true);
188
+ expect(nav.classList.contains('more-only')).to.equal(true);
189
+ expect(items.every(item => item.classList.contains('hidden'))).to.equal(true);
190
+ expect(getComputedStyle(more).borderLeftWidth).to.equal('1px');
191
+ expect(getComputedStyle(more).borderTopLeftRadius).to.equal('6px');
192
+ });
193
+
122
194
  describe('hide-one', () => {
123
195
  it('hides when only one item is present', async () => {
124
196
  const el = await fixture<ZnNavbar>(html`
@@ -81,7 +81,6 @@ export default class ZnNote extends ZincElement {
81
81
  ${!showExpandableButton ? '' : html`
82
82
  <div class="note__toggle">
83
83
  <zn-button color="transparent"
84
- size="content"
85
84
  class="note__toggle__btn"
86
85
  @click="${this._toggleExpand}"
87
86
  aria-expanded="${String(this.expanded)}">