@dorsk/tsumikit 0.40.1 → 0.41.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.
package/README.md CHANGED
@@ -167,7 +167,8 @@ Dropzone, CodeBlock, Callout, EmptyState, ConfirmModal, Pagination, Toaster,
167
167
  ThemePicker (popover grid of 2×2 palette swatches: bg · surface · text · accent per theme), FontScalePicker (popover with a stepped slider across the five text sizes), SectionHeader, KeyValue, LoadMore,
168
168
  GitRef (branch chip + PR link tinted by state + `+N −N` diff; `collapse`
169
169
  auto/never/glyph degrades to icons inside a narrow `.cq` container),
170
- CapBar (consumption track with a draggable, keyboard-steppable cap handle; `oninput` live, `onchange` on commit),
170
+ CapBar (consumption track with a draggable, keyboard-steppable cap handle that
171
+ shows a `N%` bubble while it is moved; `oninput` live, `onchange` on commit),
171
172
  Drawer (side-panel `<dialog>`: `side`, `width` clamped to the viewport, full-screen
172
173
  under 48rem; `nav` page column that turns into a horizontal strip on narrow
173
174
  screens, or `navMobile`; sticky `footer`; Escape / scrim / close button all close),
@@ -63,6 +63,7 @@
63
63
 
64
64
  let trackEl = $state<HTMLDivElement | null>(null);
65
65
  let dragging = $state(false);
66
+ let keying = $state(false);
66
67
  let committed = cap;
67
68
 
68
69
  function setCap(next: number) {
@@ -98,6 +99,7 @@
98
99
  const next = capKeyStep(e.key, e.shiftKey, snapCap(cap, step, min, max), step, min, max);
99
100
  if (next === null) return;
100
101
  e.preventDefault();
102
+ keying = true;
101
103
  setCap(next);
102
104
  }
103
105
  function keyUp(e: KeyboardEvent) {
@@ -111,6 +113,7 @@
111
113
  class="cap-bar size-{size} tone-{tone} {klass}"
112
114
  class:readonly
113
115
  class:dragging
116
+ class:keying
114
117
  style="--label-w: {labelWidth}; --readout-w: {readoutWidth}; --pct: {pct}%; --cap: {capPct}%; {styleProp}"
115
118
  >
116
119
  {#if label}
@@ -141,7 +144,9 @@
141
144
  aria-disabled={readonly ? 'true' : undefined}
142
145
  onkeydown={keyDown}
143
146
  onkeyup={keyUp}
147
+ onblur={() => (keying = false)}
144
148
  ></div>
149
+ <div class="bubble" aria-hidden="true">{cap}%</div>
145
150
  </div>
146
151
  <div class="readout" title={readoutTitle}>
147
152
  {#if readout === undefined}{readoutText}{:else if typeof readout === 'string'}{readout}{:else}{@render readout()}{/if}
@@ -225,6 +230,34 @@
225
230
  outline: 2px solid var(--accent);
226
231
  outline-offset: 2px;
227
232
  }
233
+ .bubble {
234
+ position: absolute;
235
+ bottom: calc(100% + 8px);
236
+ left: var(--cap);
237
+ transform: translateX(-50%);
238
+ padding: 1px var(--sp-1);
239
+ border: 1px solid var(--border-strong);
240
+ border-radius: var(--r-sm);
241
+ background: var(--bg-elevated-2);
242
+ box-shadow: var(--shadow-sm);
243
+ color: var(--text);
244
+ font-size: var(--fs-xs);
245
+ line-height: 1.4;
246
+ font-variant-numeric: tabular-nums;
247
+ white-space: nowrap;
248
+ pointer-events: none;
249
+ opacity: 0;
250
+ visibility: hidden;
251
+ transition:
252
+ opacity 0.12s var(--ease),
253
+ visibility 0.12s;
254
+ z-index: 3;
255
+ }
256
+ .dragging .bubble,
257
+ .keying .bubble {
258
+ opacity: 1;
259
+ visibility: visible;
260
+ }
228
261
  .readonly .track,
229
262
  .readonly .handle {
230
263
  cursor: default;
@@ -251,7 +284,8 @@
251
284
  grid-column: 1 / 3;
252
285
  }
253
286
  @media (prefers-reduced-motion: reduce) {
254
- .fill {
287
+ .fill,
288
+ .bubble {
255
289
  transition: none;
256
290
  }
257
291
  }
@@ -174,10 +174,17 @@
174
174
  /* The trigger owns its look (a ghost icon-button) from tokens — it no longer
175
175
  borrows global .btn classes, so a consumer can restyle it via `triggerClass`
176
176
  (+ `bare`) from their own scoped CSS instead of fighting globals. */
177
- .pop-trigger {
177
+ /* Default trigger chrome. Zero specificity (`:where`) so any consumer class
178
+ (triggerClass) overrides it; `:not(.bare)` keeps it off bare triggers such
179
+ as the Timestamp <time>, which stay plain inline text. */
180
+ :where(.pop-trigger:not(.bare)) {
178
181
  display: inline-flex;
179
182
  align-items: center;
180
183
  justify-content: center;
184
+ min-height: var(--box-md);
185
+ min-width: var(--box-md);
186
+ padding: var(--sp-2);
187
+ border: 1px solid transparent;
181
188
  border-radius: var(--r-md);
182
189
  background: transparent;
183
190
  color: var(--text);
@@ -185,15 +192,7 @@
185
192
  background 0.12s var(--ease),
186
193
  border-color 0.12s var(--ease);
187
194
  }
188
- /* Box sizing stays off `bare` triggers so an inline trigger (e.g. a
189
- Timestamp <time>) keeps its text's line box. */
190
- .pop-trigger:not(.bare) {
191
- min-height: var(--box-md);
192
- min-width: var(--box-md);
193
- padding: var(--sp-2);
194
- border: 1px solid transparent;
195
- }
196
- .pop-trigger:hover:not(:disabled) {
195
+ :where(.pop-trigger:not(.bare):hover:not(:disabled)) {
197
196
  background: var(--bg-elevated-2);
198
197
  }
199
198
  /* Supplying canonical chrome props opts into the same dimensions, variants
@@ -328,19 +327,15 @@
328
327
  /* `bare`: strip the chrome down to a plain button the consumer styles. */
329
328
  :where(.pop-trigger.bare) {
330
329
  display: inline;
331
- height: auto;
332
- min-height: 0;
333
- min-width: 0;
334
- width: auto;
335
330
  padding: 0;
336
331
  border: 0;
332
+ border-radius: 0;
337
333
  background: none;
334
+ color: inherit;
338
335
  font: inherit;
339
336
  line-height: inherit;
340
337
  vertical-align: baseline;
341
- }
342
- :where(.pop-trigger.bare:hover:not(:disabled)) {
343
- background: none;
338
+ cursor: pointer;
344
339
  }
345
340
  .pop-panel {
346
341
  position: fixed;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.40.1",
3
+ "version": "0.41.0",
4
4
  "description": "Minimal, dependency-free Svelte 5 + pure-CSS UI kit. Token-driven atoms, molecules & layouts with theming out of the box.",
5
5
  "type": "module",
6
6
  "license": "MIT",