@spaethtech/svelte-ui 0.6.1-dev.33.c371f46 → 0.7.0

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.
@@ -162,7 +162,9 @@ examples):
162
162
  `onShow(ctx)`/`onHide(ctx)` control focus — set `ctx.autoFocus` to a selector/element/`false`,
163
163
  `ctx.restoreFocus` likewise) · `ConfirmDialog` (title + message + Confirm/Cancel, built on Dialog)
164
164
  · `Popup` `Menu` · `tooltip` (a Svelte `use:` action) · `anchored` (positioning engine)
165
- - **Navigation:** `TabStrip` `SideBarMenu`
165
+ - **Navigation:** `TabStrip` (tabs; `size`/`variant`, `gap`/`pad` bordered spacer cells, `height` for a
166
+ fixed-height bar with bottom-aligned tabs, `activeSurface` to flow the active tab into the content
167
+ below) · `SideBarMenu`
166
168
  - **Feedback:** `Alert` `Banner` `Badge` `Toaster` + `toast`
167
169
  - **Layout:** `Card` `CardHeader` `CardBody` `CardFooter` · **`Grid`** (auto-placed equal-cell grid;
168
170
  `columns` + `gap`)
@@ -49,6 +49,12 @@
49
49
  * row bottom-aligns to the base line and the extra space breathes ABOVE it. `size` still sets the
50
50
  * tab height; `height` sets the whole bar. Unset → the strip hugs the tab row. */
51
51
  height?: number;
52
+ /** CSS colour for the ACTIVE tab's fill AND its bottom border — so the base line disappears under
53
+ * it and the tab reads as one surface with the content below. Default `"transparent"` (reveals
54
+ * whatever surface the strip sits on — today's behaviour). Set a token/colour to force a specific
55
+ * surface, e.g. `activeSurface="var(--ui-color-background)"` when the strip sits in a container
56
+ * whose background differs from the content below. Any CSS colour value. */
57
+ activeSurface?: string;
52
58
  onselect?: (index: number) => void;
53
59
  right?: Snippet;
54
60
  /** Optional aria-label for the strip when it renders as a navigation landmark
@@ -67,6 +73,7 @@
67
73
  gap = 0,
68
74
  pad = 0,
69
75
  height,
76
+ activeSurface = "transparent",
70
77
  onselect,
71
78
  right,
72
79
  "aria-label": ariaLabel,
@@ -230,13 +237,18 @@
230
237
  ].join(" "),
231
238
  );
232
239
 
233
- // Selected: transparent fill, so the tab shows whatever surface the strip sits on (page background,
234
- // a Card/panel, an inverse surface) and reads as one continuous surface with the content below it —
235
- // the folder-tab effect on ANY background, not just the page. Label + icon take `var(--ui-accent)`
236
- // (the variant colour, or plain text for neutral/ghost icons inherit via currentColor), the
237
- // clearest signal of the strip's variant. Bottom border transparent — the base line breaks under it.
238
- const selectedStyleClass =
239
- "[background-color:transparent] [color:var(--ui-accent)] [border-bottom-color:transparent]";
240
+ // Selected: label + icon take `var(--ui-accent)` (the variant colour, or plain text for
241
+ // neutral/ghost icons inherit via currentColor), the clearest signal of the strip's variant. The
242
+ // FILL + bottom-border colour are driven by `activeSurface` via inline `style` (below) a dynamic
243
+ // colour can't be a Tailwind class (the scanner never sees a runtime-built arbitrary value). Default
244
+ // `transparent`: the tab shows whatever surface the strip sits on and the base line breaks under it
245
+ // (the folder-tab effect on ANY background) — identical to before.
246
+ const selectedStyleClass = "[color:var(--ui-accent)]";
247
+ // Applied only to the active tab. `background-color` = the fill; `border-bottom-color` = the same, so
248
+ // the base-line segment under the active tab vanishes into that surface.
249
+ const selectedStyle = $derived(
250
+ `background-color:${activeSurface}; border-bottom-color:${activeSurface}`,
251
+ );
240
252
 
241
253
  // `ghost` is the "transparent, hover-tint only" grey variant (per `types/variants.ts`): unlike
242
254
  // `neutral`, its inactive tabs have NO resting fill — they sit on the bare surface and only tint on
@@ -276,6 +288,7 @@
276
288
  : isActive
277
289
  ? selectedStyleClass
278
290
  : `${unselectedStyleClass} ${lineBottom}`}
291
+ {@const tabStyle = isActive && !t.disabled ? selectedStyle : undefined}
279
292
  {@const tabTabIndex = t.disabled ? -1 : semanticMode === "tablist" ? (isRoving ? 0 : -1) : 0}
280
293
  {@const tabAriaCurrent =
281
294
  semanticMode === "navigation" && isActive ? ("page" as const) : undefined}
@@ -305,6 +318,7 @@
305
318
  focusedIndex = i;
306
319
  }}
307
320
  class={`${tabBase} ${stateClass}`}
321
+ style={tabStyle}
308
322
  >
309
323
  {#if t.icon}{@render t.icon()}{/if}
310
324
  <span class={`truncate ${t.icon ? labelClass : "inline-block"}`}>{t.label}</span>
@@ -328,6 +342,7 @@
328
342
  focusedIndex = i;
329
343
  }}
330
344
  class={`${tabBase} ${stateClass}`}
345
+ style={tabStyle}
331
346
  disabled={t.disabled}
332
347
  >
333
348
  {#if t.icon}{@render t.icon()}{/if}
@@ -42,6 +42,12 @@ interface Props extends Omit<HTMLAttributes<HTMLUListElement>, "onselect"> {
42
42
  * row bottom-aligns to the base line and the extra space breathes ABOVE it. `size` still sets the
43
43
  * tab height; `height` sets the whole bar. Unset → the strip hugs the tab row. */
44
44
  height?: number;
45
+ /** CSS colour for the ACTIVE tab's fill AND its bottom border — so the base line disappears under
46
+ * it and the tab reads as one surface with the content below. Default `"transparent"` (reveals
47
+ * whatever surface the strip sits on — today's behaviour). Set a token/colour to force a specific
48
+ * surface, e.g. `activeSurface="var(--ui-color-background)"` when the strip sits in a container
49
+ * whose background differs from the content below. Any CSS colour value. */
50
+ activeSurface?: string;
45
51
  onselect?: (index: number) => void;
46
52
  right?: Snippet;
47
53
  /** Optional aria-label for the strip when it renders as a navigation landmark
@@ -197,6 +197,22 @@ Thin wrappers over `Input`, each preconfigured with an MDI icon (rendered into I
197
197
  - **Location**: `src/lib/components/NumberInput.svelte`
198
198
  - **Features**: number formatting and validation
199
199
 
200
+ ## Navigation
201
+
202
+ ### TabStrip
203
+
204
+ A horizontal tab bar (renders as a `tablist` of buttons, or a `navigation` of links when any tab has
205
+ `href`). Tabs split into left / right clusters; a continuous base line runs under the strip and breaks
206
+ under the active tab. Fully demoed at `/tabstrip`.
207
+
208
+ - **Location**: `src/lib/components/TabStrip/TabStrip.svelte`
209
+ - **Axes**: `size`, `variant` (both `Responsive`)
210
+ - **Props**: `tabs` (`TabDefinition[]`), `activeTabIndex` (bindable), `mode` (`'expanded'` |
211
+ `'collapsed'`, responsive), `borderless`, `gap` (rem — bordered cells between tabs), `pad` (rem —
212
+ leading/trailing side cells), **`height`** (rem — total bar height; tabs bottom-align, space above),
213
+ **`activeSurface`** (CSS colour for the active tab's fill + bottom border; default `"transparent"`),
214
+ `right` (Snippet), `aria-label`.
215
+
200
216
  ## Data Components
201
217
 
202
218
  These pair with the headless **`@spaethtech/svelte-ui/data`** layer (query-language parser/AST, `createGrid`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaethtech/svelte-ui",
3
- "version": "0.6.1-dev.33.c371f46",
3
+ "version": "0.7.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/spaethtech/svelte-ui.git"