bitwrench 2.0.17 → 2.0.19

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 (72) hide show
  1. package/README.md +169 -75
  2. package/dist/bitwrench-bccl.cjs.js +228 -55
  3. package/dist/bitwrench-bccl.cjs.min.js +3 -3
  4. package/dist/bitwrench-bccl.esm.js +228 -55
  5. package/dist/bitwrench-bccl.esm.min.js +3 -3
  6. package/dist/bitwrench-bccl.umd.js +228 -55
  7. package/dist/bitwrench-bccl.umd.min.js +3 -3
  8. package/dist/bitwrench-code-edit.cjs.js +7 -9
  9. package/dist/bitwrench-code-edit.cjs.min.js +5 -7
  10. package/dist/bitwrench-code-edit.es5.js +6 -8
  11. package/dist/bitwrench-code-edit.es5.min.js +5 -7
  12. package/dist/bitwrench-code-edit.esm.js +7 -9
  13. package/dist/bitwrench-code-edit.esm.min.js +5 -7
  14. package/dist/bitwrench-code-edit.umd.js +7 -9
  15. package/dist/bitwrench-code-edit.umd.min.js +5 -7
  16. package/dist/bitwrench-debug.js +268 -0
  17. package/dist/bitwrench-debug.min.js +3 -0
  18. package/dist/bitwrench-lean.cjs.js +1190 -2348
  19. package/dist/bitwrench-lean.cjs.min.js +20 -20
  20. package/dist/bitwrench-lean.es5.js +1285 -2551
  21. package/dist/bitwrench-lean.es5.min.js +18 -18
  22. package/dist/bitwrench-lean.esm.js +1190 -2348
  23. package/dist/bitwrench-lean.esm.min.js +20 -20
  24. package/dist/bitwrench-lean.umd.js +1190 -2348
  25. package/dist/bitwrench-lean.umd.min.js +20 -20
  26. package/dist/bitwrench-util-css.cjs.js +236 -0
  27. package/dist/bitwrench-util-css.cjs.min.js +22 -0
  28. package/dist/bitwrench-util-css.es5.js +414 -0
  29. package/dist/bitwrench-util-css.es5.min.js +21 -0
  30. package/dist/bitwrench-util-css.esm.js +230 -0
  31. package/dist/bitwrench-util-css.esm.min.js +21 -0
  32. package/dist/bitwrench-util-css.umd.js +242 -0
  33. package/dist/bitwrench-util-css.umd.min.js +21 -0
  34. package/dist/bitwrench.cjs.js +1404 -2388
  35. package/dist/bitwrench.cjs.min.js +21 -21
  36. package/dist/bitwrench.css +503 -132
  37. package/dist/bitwrench.es5.js +1588 -2659
  38. package/dist/bitwrench.es5.min.js +19 -19
  39. package/dist/bitwrench.esm.js +1405 -2389
  40. package/dist/bitwrench.esm.min.js +21 -21
  41. package/dist/bitwrench.min.css +1 -1
  42. package/dist/bitwrench.umd.js +1404 -2388
  43. package/dist/bitwrench.umd.min.js +21 -21
  44. package/dist/builds.json +214 -104
  45. package/dist/bwserve.cjs.js +514 -68
  46. package/dist/bwserve.esm.js +513 -69
  47. package/dist/sri.json +46 -36
  48. package/package.json +6 -3
  49. package/readme.html +183 -85
  50. package/src/bitwrench-bccl-entry.js +3 -4
  51. package/src/bitwrench-bccl.js +224 -50
  52. package/src/bitwrench-code-edit.js +6 -8
  53. package/src/bitwrench-color-utils.js +31 -9
  54. package/src/bitwrench-debug.js +245 -0
  55. package/src/bitwrench-esm-entry.js +11 -0
  56. package/src/bitwrench-styles.js +474 -240
  57. package/src/bitwrench-util-css.js +229 -0
  58. package/src/bitwrench.js +689 -2042
  59. package/src/bwserve/attach.js +57 -0
  60. package/src/bwserve/bwclient.js +141 -0
  61. package/src/bwserve/bwshell.js +102 -0
  62. package/src/bwserve/client.js +151 -1
  63. package/src/bwserve/index.js +127 -28
  64. package/src/cli/attach.js +587 -0
  65. package/src/cli/convert.js +2 -5
  66. package/src/cli/index.js +7 -0
  67. package/src/cli/inject.js +1 -1
  68. package/src/cli/serve.js +185 -5
  69. package/src/generate-css.js +11 -4
  70. package/src/vendor/html2canvas.min.js +20 -0
  71. package/src/version.js +3 -3
  72. package/src/bwserve/shell.js +0 -106
@@ -46,10 +46,12 @@ export var SPACING_SCALE = {
46
46
  7: '3rem' // 48px
47
47
  };
48
48
 
49
+ let _S=SPACING_SCALE;
50
+
49
51
  export var SPACING_PRESETS = {
50
- compact: { btn: SPACING_SCALE[1] + ' ' + SPACING_SCALE[3], card: SPACING_SCALE[3] + ' ' + SPACING_SCALE[4], alert: SPACING_SCALE[2] + ' ' + SPACING_SCALE[4], cell: SPACING_SCALE[2] + ' ' + SPACING_SCALE[3], input: SPACING_SCALE[1] + ' ' + SPACING_SCALE[3] },
51
- normal: { btn: SPACING_SCALE[2] + ' ' + SPACING_SCALE[4], card: SPACING_SCALE[5] + ' ' + SPACING_SCALE[5], alert: SPACING_SCALE[3] + ' ' + SPACING_SCALE[5], cell: SPACING_SCALE[3] + ' ' + SPACING_SCALE[4], input: SPACING_SCALE[2] + ' ' + SPACING_SCALE[3] },
52
- spacious: { btn: SPACING_SCALE[3] + ' ' + SPACING_SCALE[5], card: SPACING_SCALE[6] + ' ' + SPACING_SCALE[6], alert: SPACING_SCALE[4] + ' ' + SPACING_SCALE[5], cell: SPACING_SCALE[4] + ' ' + SPACING_SCALE[5], input: SPACING_SCALE[3] + ' ' + SPACING_SCALE[4] }
52
+ compact: { btn: _S[1] + ' ' + _S[3], card: _S[3] + ' ' + _S[4], alert: _S[2] + ' ' + _S[4], cell: _S[2] + ' ' + _S[3], input: _S[1] + ' ' + _S[3] },
53
+ normal: { btn: _S[2] + ' ' + _S[4], card: _S[5] + ' ' + _S[5], alert: _S[3] + ' ' + _S[5], cell: _S[3] + ' ' + _S[4], input: _S[2] + ' ' + _S[3] },
54
+ spacious: { btn: _S[3] + ' ' + _S[5], card: _S[6] + ' ' + _S[6], alert: _S[4] + ' ' + _S[5], cell: _S[4] + ' ' + _S[5], input: _S[3] + ' ' + _S[4] }
53
55
  };
54
56
 
55
57
  export var RADIUS_PRESETS = {
@@ -161,20 +163,14 @@ export var DEFAULT_PALETTE_CONFIG = {
161
163
  * Built-in theme presets — named color combinations
162
164
  * Each preset provides primary, secondary, and tertiary seed colors.
163
165
  */
164
- export var THEME_PRESETS = {
165
- teal: { primary: '#006666', secondary: '#6c757d', tertiary: '#006666' },
166
- ocean: { primary: '#0077b6', secondary: '#90e0ef', tertiary: '#00b4d8' },
167
- sunset: { primary: '#e76f51', secondary: '#264653', tertiary: '#e9c46a' },
168
- forest: { primary: '#2d6a4f', secondary: '#95d5b2', tertiary: '#52b788' },
169
- slate: { primary: '#343a40', secondary: '#adb5bd', tertiary: '#6c757d' },
170
- rose: { primary: '#e11d48', secondary: '#fda4af', tertiary: '#fb7185' },
171
- indigo: { primary: '#4f46e5', secondary: '#a5b4fc', tertiary: '#818cf8' },
172
- amber: { primary: '#d97706', secondary: '#fbbf24', tertiary: '#f59e0b' },
173
- emerald: { primary: '#059669', secondary: '#6ee7b7', tertiary: '#34d399' },
174
- nord: { primary: '#5e81ac', secondary: '#88c0d0', tertiary: '#81a1c1' },
175
- coral: { primary: '#ef6461', secondary: '#4a7c7e', tertiary: '#e8a87c' },
176
- midnight: { primary: '#1e3a5f', secondary: '#7c8db5', tertiary: '#3d5a80' }
177
- };
166
+ export var THEME_PRESETS = Object.fromEntries([
167
+ ['teal','#006666','#6c757d','#006666'],['ocean','#0077b6','#90e0ef','#00b4d8'],
168
+ ['sunset','#e76f51','#264653','#e9c46a'],['forest','#2d6a4f','#95d5b2','#52b788'],
169
+ ['slate','#343a40','#adb5bd','#6c757d'],['rose','#e11d48','#fda4af','#fb7185'],
170
+ ['indigo','#4f46e5','#a5b4fc','#818cf8'],['amber','#d97706','#fbbf24','#f59e0b'],
171
+ ['emerald','#059669','#6ee7b7','#34d399'],['nord','#5e81ac','#88c0d0','#81a1c1'],
172
+ ['coral','#ef6461','#4a7c7e','#e8a87c'],['midnight','#1e3a5f','#7c8db5','#3d5a80']
173
+ ].map(function(e) { return [e[0], {primary:e[1],secondary:e[2],tertiary:e[3]}]; }));
178
174
 
179
175
  /**
180
176
  * Resolve layout config to spacing, radius, typeScale, elevation, and motion objects.
@@ -221,6 +217,7 @@ function scopeSelector(name, sel) {
221
217
  if (sel.includes(',')) return sel.split(',').map(function(s) { return '.' + name + ' ' + s.trim(); }).join(', ');
222
218
  return '.' + name + ' ' + sel;
223
219
  }
220
+ var _sx=scopeSelector;
224
221
 
225
222
  // =========================================================================
226
223
  // Themed CSS generators
@@ -229,12 +226,12 @@ function scopeSelector(name, sel) {
229
226
  function generateTypographyThemed(scope, palette, layout) {
230
227
  var mot = layout.motion;
231
228
  var rules = {};
232
- rules[scopeSelector(scope, 'a')] = {
229
+ rules[_sx(scope, 'a')] = {
233
230
  'color': palette.primary.base,
234
231
  'text-decoration': 'none',
235
232
  'transition': 'color ' + mot.fast + ' ' + mot.easing
236
233
  };
237
- rules[scopeSelector(scope, 'a:hover')] = {
234
+ rules[_sx(scope, 'a:hover')] = {
238
235
  'color': palette.primary.hover,
239
236
  'text-decoration': 'underline'
240
237
  };
@@ -247,11 +244,11 @@ function generateButtons(scope, palette, layout) {
247
244
  var rd = layout.radius;
248
245
 
249
246
  // Base button (only when scoped — unscoped uses defaultStyles)
250
- rules[scopeSelector(scope, '.bw_btn')] = {
247
+ rules[_sx(scope, '.bw_btn')] = {
251
248
  'padding': sp.btn,
252
249
  'border-radius': rd.btn
253
250
  };
254
- rules[scopeSelector(scope, '.bw_btn:focus-visible')] = {
251
+ rules[_sx(scope, '.bw_btn:focus-visible')] = {
255
252
  'outline': '2px solid currentColor',
256
253
  'outline-offset': '2px',
257
254
  'box-shadow': '0 0 0 3px ' + palette.primary.focus
@@ -260,12 +257,12 @@ function generateButtons(scope, palette, layout) {
260
257
  // Variant colors handled by palette class on component root
261
258
 
262
259
  // Size variants (structural, reuse layout radius)
263
- rules[scopeSelector(scope, '.bw_btn_lg')] = {
260
+ rules[_sx(scope, '.bw_btn_lg')] = {
264
261
  'padding': '0.625rem 1.5rem',
265
262
  'font-size': '1rem',
266
263
  'border-radius': rd.btn === '50rem' ? '50rem' : (parseInt(rd.btn) + 2) + 'px'
267
264
  };
268
- rules[scopeSelector(scope, '.bw_btn_sm')] = {
265
+ rules[_sx(scope, '.bw_btn_sm')] = {
269
266
  'padding': '0.25rem 0.75rem',
270
267
  'font-size': '0.8125rem',
271
268
  'border-radius': rd.btn === '50rem' ? '50rem' : (Math.max(parseInt(rd.btn) - 1, 0)) + 'px'
@@ -279,7 +276,7 @@ function generateAlerts(scope, palette, layout) {
279
276
  var sp = layout.spacing;
280
277
  var rd = layout.radius;
281
278
 
282
- rules[scopeSelector(scope, '.bw_alert')] = {
279
+ rules[_sx(scope, '.bw_alert')] = {
283
280
  'padding': sp.alert,
284
281
  'border-radius': rd.alert
285
282
  };
@@ -298,36 +295,36 @@ function generateCards(scope, palette, layout) {
298
295
 
299
296
  var elev = layout.elevation;
300
297
  var motion = layout.motion;
301
- rules[scopeSelector(scope, '.bw_card')] = {
298
+ rules[_sx(scope, '.bw_card')] = {
302
299
  'background-color': palette.surface || '#fff',
303
300
  'border': '1px solid ' + palette.light.border,
304
301
  'border-radius': rd.card,
305
302
  'box-shadow': elev.sm,
306
303
  'transition': 'box-shadow ' + motion.normal + ' ' + motion.easing + ', transform ' + motion.normal + ' ' + motion.easing
307
304
  };
308
- rules[scopeSelector(scope, '.bw_card:hover')] = {
305
+ rules[_sx(scope, '.bw_card:hover')] = {
309
306
  'box-shadow': elev.md
310
307
  };
311
- rules[scopeSelector(scope, '.bw_card_hoverable:hover')] = {
308
+ rules[_sx(scope, '.bw_card_hoverable:hover')] = {
312
309
  'box-shadow': elev.lg
313
310
  };
314
- rules[scopeSelector(scope, '.bw_card_body')] = {
311
+ rules[_sx(scope, '.bw_card_body')] = {
315
312
  'padding': sp.card
316
313
  };
317
- rules[scopeSelector(scope, '.bw_card_header')] = {
314
+ rules[_sx(scope, '.bw_card_header')] = {
318
315
  'padding': sp.card.split(' ').map(function(v) { return (parseFloat(v) * 0.7).toFixed(3).replace(/\.?0+$/, '') + 'rem'; }).join(' '),
319
- 'background-color': palette.light.light,
316
+ 'background-color': palette.surfaceAlt,
320
317
  'border-bottom': '1px solid ' + palette.light.border
321
318
  };
322
- rules[scopeSelector(scope, '.bw_card_footer')] = {
323
- 'background-color': palette.light.light,
319
+ rules[_sx(scope, '.bw_card_footer')] = {
320
+ 'background-color': palette.surfaceAlt,
324
321
  'border-top': '1px solid ' + palette.light.border,
325
322
  'color': palette.secondary.base
326
323
  };
327
- rules[scopeSelector(scope, '.bw_card_title')] = {
324
+ rules[_sx(scope, '.bw_card_title')] = {
328
325
  'color': palette.dark.base
329
326
  };
330
- rules[scopeSelector(scope, '.bw_card_subtitle')] = {
327
+ rules[_sx(scope, '.bw_card_subtitle')] = {
331
328
  'color': palette.secondary.base
332
329
  };
333
330
 
@@ -341,55 +338,55 @@ function generateForms(scope, palette, layout) {
341
338
  var sp = layout.spacing;
342
339
  var rd = layout.radius;
343
340
 
344
- rules[scopeSelector(scope, '.bw_form_control')] = {
341
+ rules[_sx(scope, '.bw_form_control')] = {
345
342
  'padding': sp.input,
346
343
  'border-radius': rd.input,
347
344
  'color': palette.dark.base,
348
345
  'background-color': palette.surface || '#fff',
349
346
  'border-color': palette.light.border
350
347
  };
351
- rules[scopeSelector(scope, '.bw_form_control:focus')] = {
348
+ rules[_sx(scope, '.bw_form_control:focus')] = {
352
349
  'border-color': palette.primary.border,
353
350
  'outline': '2px solid ' + palette.primary.base,
354
351
  'outline-offset': '-1px',
355
352
  'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
356
353
  };
357
- rules[scopeSelector(scope, '.bw_form_control::placeholder')] = {
354
+ rules[_sx(scope, '.bw_form_control::placeholder')] = {
358
355
  'color': palette.secondary.base
359
356
  };
360
- rules[scopeSelector(scope, '.bw_form_label')] = {
357
+ rules[_sx(scope, '.bw_form_label')] = {
361
358
  'color': palette.dark.base
362
359
  };
363
- rules[scopeSelector(scope, '.bw_form_text')] = {
360
+ rules[_sx(scope, '.bw_form_text')] = {
364
361
  'color': palette.secondary.base
365
362
  };
366
- rules[scopeSelector(scope, '.bw_form_check_input:checked')] = {
363
+ rules[_sx(scope, '.bw_form_check_input:checked')] = {
367
364
  'background-color': palette.primary.base,
368
365
  'border-color': palette.primary.base
369
366
  };
370
- rules[scopeSelector(scope, '.bw_form_check_input:focus')] = {
367
+ rules[_sx(scope, '.bw_form_check_input:focus')] = {
371
368
  'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
372
369
  };
373
370
  // Validation states
374
- rules[scopeSelector(scope, '.bw_form_control.bw_is_valid')] = { 'border-color': palette.success.base };
375
- rules[scopeSelector(scope, '.bw_form_control.bw_is_valid:focus')] = {
371
+ rules[_sx(scope, '.bw_form_control.bw_is_valid')] = { 'border-color': palette.success.base };
372
+ rules[_sx(scope, '.bw_form_control.bw_is_valid:focus')] = {
376
373
  'border-color': palette.success.base,
377
374
  'box-shadow': '0 0 0 0.2rem ' + palette.success.focus
378
375
  };
379
- rules[scopeSelector(scope, '.bw_form_control.bw_is_invalid')] = { 'border-color': palette.danger.base };
380
- rules[scopeSelector(scope, '.bw_form_control.bw_is_invalid:focus')] = {
376
+ rules[_sx(scope, '.bw_form_control.bw_is_invalid')] = { 'border-color': palette.danger.base };
377
+ rules[_sx(scope, '.bw_form_control.bw_is_invalid:focus')] = {
381
378
  'border-color': palette.danger.base,
382
379
  'box-shadow': '0 0 0 0.2rem ' + palette.danger.focus
383
380
  };
384
381
  // Form select
385
- rules[scopeSelector(scope, '.bw_form_select')] = {
382
+ rules[_sx(scope, '.bw_form_select')] = {
386
383
  'padding': sp.input,
387
384
  'border-radius': rd.input,
388
385
  'color': palette.dark.base,
389
386
  'background-color': palette.surface || '#fff',
390
387
  'border-color': palette.light.border
391
388
  };
392
- rules[scopeSelector(scope, '.bw_form_select:focus')] = {
389
+ rules[_sx(scope, '.bw_form_select:focus')] = {
393
390
  'border-color': palette.primary.border,
394
391
  'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
395
392
  };
@@ -397,43 +394,46 @@ function generateForms(scope, palette, layout) {
397
394
  return rules;
398
395
  }
399
396
 
400
- function generateNavigation(scope, palette) {
397
+ function generateNavigation(scope, palette, layout) {
401
398
  var rules = {};
402
- rules[scopeSelector(scope, '.bw_navbar')] = {
403
- 'background-color': palette.light.light,
399
+ rules[_sx(scope, '.bw_navbar')] = {
400
+ 'background-color': palette.surfaceAlt,
404
401
  'border-bottom-color': palette.light.border
405
402
  };
406
- rules[scopeSelector(scope, '.bw_navbar_brand')] = {
403
+ rules[_sx(scope, '.bw_navbar_brand')] = {
407
404
  'color': palette.dark.base
408
405
  };
409
- rules[scopeSelector(scope, '.bw_navbar_nav .bw_nav_link')] = {
410
- 'color': palette.secondary.base
406
+ rules[_sx(scope, '.bw_navbar_nav .bw_nav_link')] = {
407
+ 'color': palette.secondary.base,
408
+ 'border-radius': layout.radius.btn
411
409
  };
412
- rules[scopeSelector(scope, '.bw_navbar_nav .bw_nav_link:hover')] = {
413
- 'color': palette.dark.base
410
+ rules[_sx(scope, '.bw_navbar_nav .bw_nav_link:hover')] = {
411
+ 'color': palette.dark.base,
412
+ 'background-color': palette.surfaceAlt
414
413
  };
415
- rules[scopeSelector(scope, '.bw_navbar_nav .bw_nav_link.active')] = {
414
+ rules[_sx(scope, '.bw_navbar_nav .bw_nav_link.active')] = {
416
415
  'color': palette.primary.base,
417
- 'background-color': palette.primary.focus
416
+ 'background-color': palette.primary.focus,
417
+ 'font-weight': '600'
418
418
  };
419
- rules[scopeSelector(scope, '.bw_navbar_dark')] = {
419
+ rules[_sx(scope, '.bw_navbar_dark')] = {
420
420
  'background-color': palette.dark.base,
421
421
  'border-bottom-color': palette.dark.hover
422
422
  };
423
- rules[scopeSelector(scope, '.bw_navbar_dark .bw_navbar_brand')] = {
423
+ rules[_sx(scope, '.bw_navbar_dark .bw_navbar_brand')] = {
424
424
  'color': palette.light.base
425
425
  };
426
- rules[scopeSelector(scope, '.bw_navbar_dark .bw_nav_link')] = {
427
- 'color': 'rgba(255,255,255,.65)'
426
+ rules[_sx(scope, '.bw_navbar_dark .bw_nav_link')] = {
427
+ 'color': palette.light.border
428
428
  };
429
- rules[scopeSelector(scope, '.bw_navbar_dark .bw_nav_link:hover')] = {
430
- 'color': '#fff'
429
+ rules[_sx(scope, '.bw_navbar_dark .bw_nav_link:hover')] = {
430
+ 'color': palette.light.base
431
431
  };
432
- rules[scopeSelector(scope, '.bw_navbar_dark .bw_nav_link.active')] = {
433
- 'color': '#fff',
432
+ rules[_sx(scope, '.bw_navbar_dark .bw_nav_link.active')] = {
433
+ 'color': palette.light.base,
434
434
  'font-weight': '600'
435
435
  };
436
- rules[scopeSelector(scope, '.bw_nav_pills .bw_nav_link.active')] = {
436
+ rules[_sx(scope, '.bw_nav_pills .bw_nav_link.active')] = {
437
437
  'color': palette.primary.textOn,
438
438
  'background-color': palette.primary.base
439
439
  };
@@ -444,49 +444,58 @@ function generateTables(scope, palette, layout) {
444
444
  var rules = {};
445
445
  var sp = layout.spacing;
446
446
 
447
- rules[scopeSelector(scope, '.bw_table')] = {
447
+ rules[_sx(scope, '.bw_table')] = {
448
448
  'color': palette.dark.base,
449
449
  'border-color': palette.light.border
450
450
  };
451
- rules[scopeSelector(scope, '.bw_table > :not(caption) > * > *')] = {
451
+ rules[_sx(scope, '.bw_table > :not(caption) > * > *')] = {
452
452
  'padding': sp.cell,
453
453
  'border-bottom-color': palette.light.border
454
454
  };
455
- rules[scopeSelector(scope, '.bw_table > thead > tr > *')] = {
455
+ rules[_sx(scope, '.bw_table > thead > tr > *')] = {
456
456
  'color': palette.secondary.base,
457
457
  'border-bottom-color': palette.light.border,
458
- 'background-color': palette.light.light
458
+ 'background-color': palette.surfaceAlt
459
459
  };
460
- rules[scopeSelector(scope, '.bw_table_striped > tbody > tr:nth-of-type(odd) > *')] = {
461
- 'background-color': 'rgba(0, 0, 0, 0.05)'
460
+ rules[_sx(scope, '.bw_table_striped > tbody > tr:nth-of-type(odd) > *')] = {
461
+ 'background-color': palette.surfaceAlt
462
462
  };
463
- rules[scopeSelector(scope, '.bw_table_hover > tbody > tr:hover > *')] = {
463
+ rules[_sx(scope, '.bw_table_hover > tbody > tr:hover > *')] = {
464
464
  'background-color': palette.primary.focus
465
465
  };
466
- rules[scopeSelector(scope, '.bw_table_bordered')] = {
466
+ rules[_sx(scope, '.bw_table_selectable > tbody > tr')] = {
467
+ 'cursor': 'pointer'
468
+ };
469
+ rules[_sx(scope, '.bw_table > tbody > tr.bw_table_row_selected > *')] = {
470
+ 'background-color': palette.primary.light
471
+ };
472
+ rules[_sx(scope, '.bw_table_bordered')] = {
467
473
  'border-color': palette.light.border
468
474
  };
469
- rules[scopeSelector(scope, '.bw_table caption')] = {
475
+ rules[_sx(scope, '.bw_table caption')] = {
470
476
  'color': palette.secondary.base
471
477
  };
472
478
 
473
479
  return rules;
474
480
  }
475
481
 
476
- function generateTabs(scope, palette) {
477
- var rules = {};
478
- rules[scopeSelector(scope, '.bw_nav_tabs')] = {
482
+ function generateTabs(scope, palette, layout) {
483
+ var rules = {}, mo = layout.motion;
484
+ rules[_sx(scope, '.bw_nav_tabs')] = {
479
485
  'border-bottom-color': palette.light.border
480
486
  };
481
- rules[scopeSelector(scope, '.bw_nav_link')] = {
482
- 'color': palette.secondary.base
487
+ rules[_sx(scope, '.bw_nav_link')] = {
488
+ 'color': palette.secondary.base,
489
+ 'transition': 'color ' + mo.fast + ' ' + mo.easing + ', border-color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
483
490
  };
484
- rules[scopeSelector(scope, '.bw_nav_tabs .bw_nav_link:hover')] = {
491
+ rules[_sx(scope, '.bw_nav_tabs .bw_nav_link:hover')] = {
485
492
  'color': palette.dark.base,
493
+ 'background-color': palette.surfaceAlt,
486
494
  'border-bottom-color': palette.light.border
487
495
  };
488
- rules[scopeSelector(scope, '.bw_nav_tabs .bw_nav_link.active')] = {
496
+ rules[_sx(scope, '.bw_nav_tabs .bw_nav_link.active')] = {
489
497
  'color': palette.primary.base,
498
+ 'background-color': palette.primary.focus,
490
499
  'border-bottom': '2px solid ' + palette.primary.base
491
500
  };
492
501
  return rules;
@@ -495,23 +504,25 @@ function generateTabs(scope, palette) {
495
504
  function generateListGroups(scope, palette, layout) {
496
505
  var rules = {};
497
506
  var sp = layout.spacing;
507
+ var mo = layout.motion;
498
508
 
499
- rules[scopeSelector(scope, '.bw_list_group_item')] = {
509
+ rules[_sx(scope, '.bw_list_group_item')] = {
500
510
  'padding': sp.cell,
501
511
  'color': palette.dark.base,
502
512
  'background-color': palette.surface || '#fff',
503
- 'border-color': palette.light.border
513
+ 'border-color': palette.light.border,
514
+ 'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
504
515
  };
505
- rules[scopeSelector(scope, 'a.bw_list_group_item:hover')] = {
506
- 'background-color': palette.light.light,
516
+ rules[_sx(scope, 'a.bw_list_group_item:hover')] = {
517
+ 'background-color': palette.surfaceAlt,
507
518
  'color': palette.dark.hover
508
519
  };
509
- rules[scopeSelector(scope, '.bw_list_group_item.active')] = {
520
+ rules[_sx(scope, '.bw_list_group_item.active')] = {
510
521
  'color': palette.primary.textOn,
511
522
  'background-color': palette.primary.base,
512
523
  'border-color': palette.primary.base
513
524
  };
514
- rules[scopeSelector(scope, '.bw_list_group_item.disabled')] = {
525
+ rules[_sx(scope, '.bw_list_group_item.disabled')] = {
515
526
  'color': palette.secondary.base,
516
527
  'background-color': palette.surface || '#fff'
517
528
  };
@@ -519,28 +530,37 @@ function generateListGroups(scope, palette, layout) {
519
530
  return rules;
520
531
  }
521
532
 
522
- function generatePagination(scope, palette) {
523
- var rules = {};
524
- rules[scopeSelector(scope, '.bw_page_link')] = {
533
+ function generatePagination(scope, palette, layout) {
534
+ var rules = {}, mo = layout.motion, rd = layout.radius;
535
+ rules[_sx(scope, '.bw_page_item:first-child .bw_page_link')] = {
536
+ 'border-top-left-radius': rd.btn,
537
+ 'border-bottom-left-radius': rd.btn
538
+ };
539
+ rules[_sx(scope, '.bw_page_item:last-child .bw_page_link')] = {
540
+ 'border-top-right-radius': rd.btn,
541
+ 'border-bottom-right-radius': rd.btn
542
+ };
543
+ rules[_sx(scope, '.bw_page_link')] = {
525
544
  'color': palette.primary.base,
526
545
  'background-color': palette.surface || '#fff',
527
- 'border-color': palette.light.border
546
+ 'border-color': palette.light.border,
547
+ 'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
528
548
  };
529
- rules[scopeSelector(scope, '.bw_page_link:hover')] = {
549
+ rules[_sx(scope, '.bw_page_link:hover')] = {
530
550
  'color': palette.primary.hover,
531
- 'background-color': palette.light.light,
551
+ 'background-color': palette.surfaceAlt,
532
552
  'border-color': palette.light.border
533
553
  };
534
- rules[scopeSelector(scope, '.bw_page_link:focus')] = {
554
+ rules[_sx(scope, '.bw_page_link:focus')] = {
535
555
  'outline': '2px solid ' + palette.primary.base,
536
556
  'outline-offset': '-2px'
537
557
  };
538
- rules[scopeSelector(scope, '.bw_page_item.bw_active .bw_page_link')] = {
558
+ rules[_sx(scope, '.bw_page_item.bw_active .bw_page_link')] = {
539
559
  'color': palette.primary.textOn,
540
560
  'background-color': palette.primary.base,
541
561
  'border-color': palette.primary.base
542
562
  };
543
- rules[scopeSelector(scope, '.bw_page_item.bw_disabled .bw_page_link')] = {
563
+ rules[_sx(scope, '.bw_page_item.bw_disabled .bw_page_link')] = {
544
564
  'color': palette.secondary.base,
545
565
  'background-color': palette.surface || '#fff',
546
566
  'border-color': palette.light.border
@@ -550,12 +570,12 @@ function generatePagination(scope, palette) {
550
570
 
551
571
  function generateProgress(scope, palette) {
552
572
  var rules = {};
553
- rules[scopeSelector(scope, '.bw_progress')] = {
554
- 'background-color': palette.light.light,
573
+ rules[_sx(scope, '.bw_progress')] = {
574
+ 'background-color': palette.surfaceAlt,
555
575
  'box-shadow': 'inset 0 1px 2px rgba(0,0,0,.1)'
556
576
  };
557
- rules[scopeSelector(scope, '.bw_progress_bar')] = {
558
- 'color': '#fff',
577
+ rules[_sx(scope, '.bw_progress_bar')] = {
578
+ 'color': palette.primary.textOn,
559
579
  'background-color': palette.primary.base,
560
580
  'box-shadow': 'inset 0 -1px 0 rgba(0,0,0,.15)'
561
581
  };
@@ -574,7 +594,7 @@ function generateResetThemed(scope, palette) {
574
594
  'color': palette.dark.base,
575
595
  'background-color': bg
576
596
  };
577
- rules[scopeSelector(scope, 'body')] = baseReset;
597
+ rules[_sx(scope, 'body')] = baseReset;
578
598
  // Also apply to the scope element itself so themes work on any container, not just body
579
599
  if (scope) {
580
600
  rules['.' + scope] = baseReset;
@@ -582,18 +602,27 @@ function generateResetThemed(scope, palette) {
582
602
  return rules;
583
603
  }
584
604
 
585
- function generateBreadcrumbThemed(scope, palette) {
586
- var rules = {};
587
- rules[scopeSelector(scope, '.bw_breadcrumb_item + .bw_breadcrumb_item::before')] = {
588
- 'color': palette.secondary.base
605
+ function generateBreadcrumbThemed(scope, palette, layout) {
606
+ var rules = {}, mo = layout.motion;
607
+ rules[_sx(scope, '.bw_breadcrumb')] = {
608
+ 'background-color': palette.surfaceAlt,
609
+ 'padding': '0.625rem 1rem',
610
+ 'border-radius': layout.radius.btn
589
611
  };
590
- rules[scopeSelector(scope, '.bw_breadcrumb_item.active')] = {
612
+ rules[_sx(scope, '.bw_breadcrumb_item + .bw_breadcrumb_item::before')] = {
591
613
  'color': palette.secondary.base
592
614
  };
593
- rules[scopeSelector(scope, '.bw_breadcrumb_item a:hover')] = {
615
+ rules[_sx(scope, '.bw_breadcrumb_item a')] = {
616
+ 'color': palette.primary.base,
617
+ 'transition': 'color ' + mo.fast + ' ' + mo.easing
618
+ };
619
+ rules[_sx(scope, '.bw_breadcrumb_item a:hover')] = {
594
620
  'color': palette.primary.hover,
595
621
  'text-decoration': 'underline'
596
622
  };
623
+ rules[_sx(scope, '.bw_breadcrumb_item.active')] = {
624
+ 'color': palette.dark.base
625
+ };
597
626
  return rules;
598
627
  }
599
628
 
@@ -601,11 +630,11 @@ function generateBreadcrumbThemed(scope, palette) {
601
630
 
602
631
  function generateCloseButtonThemed(scope, palette) {
603
632
  var rules = {};
604
- rules[scopeSelector(scope, '.bw_close')] = {
633
+ rules[_sx(scope, '.bw_close')] = {
605
634
  'color': palette.dark.base,
606
635
  'opacity': '0.5'
607
636
  };
608
- rules[scopeSelector(scope, '.bw_close:focus')] = {
637
+ rules[_sx(scope, '.bw_close:focus')] = {
609
638
  'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
610
639
  };
611
640
  return rules;
@@ -613,82 +642,94 @@ function generateCloseButtonThemed(scope, palette) {
613
642
 
614
643
  function generateSectionsThemed(scope, palette) {
615
644
  var rules = {};
616
- rules[scopeSelector(scope, '.bw_section_subtitle')] = {
645
+ rules[_sx(scope, '.bw_section_subtitle')] = {
617
646
  'color': palette.secondary.base
618
647
  };
619
- rules[scopeSelector(scope, '.bw_feature_description')] = {
648
+ rules[_sx(scope, '.bw_feature_description')] = {
620
649
  'color': palette.secondary.base
621
650
  };
622
- rules[scopeSelector(scope, '.bw_cta_description')] = {
651
+ rules[_sx(scope, '.bw_cta_description')] = {
623
652
  'color': palette.secondary.base
624
653
  };
625
654
  return rules;
626
655
  }
627
656
 
628
- function generateAccordionThemed(scope, palette) {
657
+ function generateAccordionThemed(scope, palette, layout) {
629
658
  var rules = {};
630
- rules[scopeSelector(scope, '.bw_accordion_item')] = {
659
+ var rd = layout ? layout.radius : { card: '8px' };
660
+ rules[_sx(scope, '.bw_accordion_item')] = {
631
661
  'background-color': palette.surface || '#fff',
632
662
  'border-color': palette.light.border
633
663
  };
634
- rules[scopeSelector(scope, '.bw_accordion_button')] = {
664
+ rules[_sx(scope, '.bw_accordion_item:first-child')] = {
665
+ 'border-top-left-radius': rd.card,
666
+ 'border-top-right-radius': rd.card
667
+ };
668
+ rules[_sx(scope, '.bw_accordion_item:last-child')] = {
669
+ 'border-bottom-left-radius': rd.card,
670
+ 'border-bottom-right-radius': rd.card
671
+ };
672
+ rules[_sx(scope, '.bw_accordion_button')] = {
635
673
  'color': palette.dark.base
636
674
  };
637
- rules[scopeSelector(scope, '.bw_accordion_button:not(.bw_collapsed)')] = {
675
+ rules[_sx(scope, '.bw_accordion_button:not(.bw_collapsed)')] = {
638
676
  'color': palette.primary.darkText,
639
- 'background-color': palette.primary.light
677
+ 'background-color': palette.primary.light,
678
+ 'border-left': '3px solid ' + palette.primary.base
640
679
  };
641
- rules[scopeSelector(scope, '.bw_accordion_button:hover')] = {
642
- 'background-color': palette.light.light
680
+ rules[_sx(scope, '.bw_accordion_button:hover')] = {
681
+ 'background-color': palette.surfaceAlt
643
682
  };
644
- rules[scopeSelector(scope, '.bw_accordion_button:not(.bw_collapsed):hover')] = {
645
- 'background-color': palette.primary.hover
683
+ rules[_sx(scope, '.bw_accordion_button:not(.bw_collapsed):hover')] = {
684
+ 'background-color': palette.primary.base,
685
+ 'color': palette.primary.textOn
646
686
  };
647
- rules[scopeSelector(scope, '.bw_accordion_button:focus-visible')] = {
687
+ rules[_sx(scope, '.bw_accordion_button:focus-visible')] = {
648
688
  'box-shadow': '0 0 0 0.2rem ' + palette.primary.focus
649
689
  };
650
- rules[scopeSelector(scope, '.bw_accordion_body')] = {
651
- 'border-top': '1px solid ' + palette.light.border
690
+ rules[_sx(scope, '.bw_accordion_body')] = {
691
+ 'border-top': '1px solid ' + palette.light.border,
692
+ 'background-color': palette.surfaceAlt
652
693
  };
653
694
  return rules;
654
695
  }
655
696
 
656
697
  function generateCarouselThemed(scope, palette) {
657
698
  var rules = {};
658
- rules[scopeSelector(scope, '.bw_carousel')] = {
659
- 'background-color': palette.light.light
699
+ rules[_sx(scope, '.bw_carousel')] = {
700
+ 'background-color': palette.surfaceAlt
660
701
  };
661
- rules[scopeSelector(scope, '.bw_carousel_indicator.active')] = {
702
+ rules[_sx(scope, '.bw_carousel_indicator.active')] = {
662
703
  'background-color': palette.primary.base
663
704
  };
664
- rules[scopeSelector(scope, '.bw_carousel_control')] = {
665
- 'background-color': 'rgba(0,0,0,0.4)',
666
- 'color': '#fff'
705
+ rules[_sx(scope, '.bw_carousel_control')] = {
706
+ 'background-color': palette.dark.base,
707
+ 'color': palette.dark.textOn
667
708
  };
668
- rules[scopeSelector(scope, '.bw_carousel_control:hover')] = {
669
- 'background-color': 'rgba(0,0,0,0.6)'
709
+ rules[_sx(scope, '.bw_carousel_control:hover')] = {
710
+ 'background-color': palette.dark.hover
670
711
  };
671
- rules[scopeSelector(scope, '.bw_carousel_caption')] = {
672
- 'background': 'linear-gradient(transparent, rgba(0,0,0,0.6))',
673
- 'color': '#fff'
712
+ rules[_sx(scope, '.bw_carousel_caption')] = {
713
+ 'background': 'linear-gradient(transparent, ' + palette.dark.base + ')',
714
+ 'color': palette.dark.textOn
674
715
  };
675
716
  return rules;
676
717
  }
677
718
 
678
719
  function generateModalThemed(scope, palette, layout) {
679
720
  var rules = {};
680
- rules[scopeSelector(scope, '.bw_modal_content')] = {
721
+ rules[_sx(scope, '.bw_modal_content')] = {
681
722
  'background-color': palette.surface || '#fff',
682
723
  'border-color': palette.light.border,
683
724
  'box-shadow': layout.elevation.lg
684
725
  };
685
- rules[scopeSelector(scope, '.bw_modal_header')] = {
726
+ rules[_sx(scope, '.bw_modal_header')] = {
686
727
  'border-bottom-color': palette.light.border
687
728
  };
688
- rules[scopeSelector(scope, '.bw_modal_footer')] = {
729
+ rules[_sx(scope, '.bw_modal_footer')] = {
689
730
  'border-top-color': palette.light.border
690
731
  };
691
- rules[scopeSelector(scope, '.bw_modal_title')] = {
732
+ rules[_sx(scope, '.bw_modal_title')] = {
692
733
  'color': palette.dark.base
693
734
  };
694
735
  return rules;
@@ -696,13 +737,13 @@ function generateModalThemed(scope, palette, layout) {
696
737
 
697
738
  function generateToastThemed(scope, palette, layout) {
698
739
  var rules = {};
699
- rules[scopeSelector(scope, '.bw_toast')] = {
740
+ rules[_sx(scope, '.bw_toast')] = {
700
741
  'background-color': palette.surface || '#fff',
701
- 'border-color': 'rgba(0,0,0,0.1)',
742
+ 'border-color': palette.light.border,
702
743
  'box-shadow': layout.elevation.lg
703
744
  };
704
- rules[scopeSelector(scope, '.bw_toast_header')] = {
705
- 'border-bottom-color': 'rgba(0,0,0,0.05)'
745
+ rules[_sx(scope, '.bw_toast_header')] = {
746
+ 'border-bottom-color': palette.light.border
706
747
  };
707
748
  // Variant toast borders handled by palette class
708
749
  return rules;
@@ -710,22 +751,23 @@ function generateToastThemed(scope, palette, layout) {
710
751
 
711
752
  function generateDropdownThemed(scope, palette, layout) {
712
753
  var rules = {};
713
- rules[scopeSelector(scope, '.bw_dropdown_menu')] = {
754
+ rules[_sx(scope, '.bw_dropdown_menu')] = {
714
755
  'background-color': palette.surface || '#fff',
715
756
  'border-color': palette.light.border,
716
757
  'box-shadow': layout.elevation.md
717
758
  };
718
- rules[scopeSelector(scope, '.bw_dropdown_item')] = {
719
- 'color': palette.dark.base
759
+ rules[_sx(scope, '.bw_dropdown_item')] = {
760
+ 'color': palette.dark.base,
761
+ 'transition': 'background-color ' + layout.motion.fast + ' ' + layout.motion.easing
720
762
  };
721
- rules[scopeSelector(scope, '.bw_dropdown_item:hover')] = {
763
+ rules[_sx(scope, '.bw_dropdown_item:hover')] = {
722
764
  'color': palette.dark.hover,
723
- 'background-color': palette.light.light
765
+ 'background-color': palette.surfaceAlt
724
766
  };
725
- rules[scopeSelector(scope, '.bw_dropdown_item.disabled')] = {
767
+ rules[_sx(scope, '.bw_dropdown_item.disabled')] = {
726
768
  'color': palette.secondary.base
727
769
  };
728
- rules[scopeSelector(scope, '.bw_dropdown_divider')] = {
770
+ rules[_sx(scope, '.bw_dropdown_divider')] = {
729
771
  'border-top-color': palette.light.border
730
772
  };
731
773
  return rules;
@@ -733,15 +775,15 @@ function generateDropdownThemed(scope, palette, layout) {
733
775
 
734
776
  function generateSwitchThemed(scope, palette) {
735
777
  var rules = {};
736
- rules[scopeSelector(scope, '.bw_form_switch .bw_switch_input')] = {
778
+ rules[_sx(scope, '.bw_form_switch .bw_switch_input')] = {
737
779
  'background-color': palette.secondary.base,
738
780
  'border-color': palette.secondary.base
739
781
  };
740
- rules[scopeSelector(scope, '.bw_form_switch .bw_switch_input:checked')] = {
782
+ rules[_sx(scope, '.bw_form_switch .bw_switch_input:checked')] = {
741
783
  'background-color': palette.primary.base,
742
784
  'border-color': palette.primary.base
743
785
  };
744
- rules[scopeSelector(scope, '.bw_form_switch .bw_switch_input:focus')] = {
786
+ rules[_sx(scope, '.bw_form_switch .bw_switch_input:focus')] = {
745
787
  'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
746
788
  };
747
789
  return rules;
@@ -749,88 +791,102 @@ function generateSwitchThemed(scope, palette) {
749
791
 
750
792
  function generateSkeletonThemed(scope, palette) {
751
793
  var rules = {};
752
- rules[scopeSelector(scope, '.bw_skeleton')] = {
753
- 'background': 'linear-gradient(90deg, ' + palette.light.border + ' 25%, ' + palette.light.light + ' 37%, ' + palette.light.border + ' 63%)'
794
+ rules[_sx(scope, '.bw_skeleton')] = {
795
+ 'background': 'linear-gradient(90deg, ' + palette.light.border + ' 25%, ' + palette.surfaceAlt + ' 37%, ' + palette.light.border + ' 63%)'
754
796
  };
755
797
  return rules;
756
798
  }
757
799
 
758
800
  // generateAvatarThemed: removed — palette class on root handles variants
759
801
 
760
- function generateStatCardThemed(scope, palette) {
761
- var rules = {};
802
+ function generateStatCardThemed(scope, palette, layout) {
803
+ var rules = {}, mo = layout.motion, el = layout.elevation, rd = layout.radius;
804
+ rules[_sx(scope, '.bw_stat_card')] = {
805
+ 'background-color': palette.surface || '#fff',
806
+ 'color': palette.dark.base,
807
+ 'border': '1px solid ' + palette.light.border,
808
+ 'border-radius': rd.card,
809
+ 'box-shadow': el.sm,
810
+ 'transition': 'box-shadow ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
811
+ };
812
+ rules[_sx(scope, '.bw_stat_card:hover')] = { 'box-shadow': el.md };
762
813
  // Variant border colors handled by palette class
763
- rules[scopeSelector(scope, '.bw_stat_change_up')] = { 'color': palette.success.base };
764
- rules[scopeSelector(scope, '.bw_stat_change_down')] = { 'color': palette.danger.base };
814
+ rules[_sx(scope, '.bw_stat_change_up')] = { 'color': palette.success.base };
815
+ rules[_sx(scope, '.bw_stat_change_down')] = { 'color': palette.danger.base };
765
816
  return rules;
766
817
  }
767
818
 
768
819
  function generateTimelineThemed(scope, palette) {
769
820
  var rules = {};
770
- rules[scopeSelector(scope, '.bw_timeline::before')] = { 'background-color': palette.light.border };
821
+ rules[_sx(scope, '.bw_timeline::before')] = { 'background-color': palette.light.border };
771
822
  // Variant marker colors handled by palette class
772
- rules[scopeSelector(scope, '.bw_timeline_date')] = { 'color': palette.secondary.base };
823
+ rules[_sx(scope, '.bw_timeline_date')] = { 'color': palette.secondary.base };
773
824
  return rules;
774
825
  }
775
826
 
776
827
  function generateStepperThemed(scope, palette) {
777
828
  var rules = {};
778
- rules[scopeSelector(scope, '.bw_step_indicator')] = {
779
- 'background-color': palette.light.light,
829
+ rules[_sx(scope, '.bw_step_indicator')] = {
830
+ 'background-color': palette.surfaceAlt,
780
831
  'border': '2px solid ' + palette.light.border,
781
832
  'color': palette.secondary.base
782
833
  };
783
- rules[scopeSelector(scope, '.bw_step + .bw_step::before')] = { 'background-color': palette.light.border };
784
- rules[scopeSelector(scope, '.bw_step_active .bw_step_indicator')] = {
834
+ rules[_sx(scope, '.bw_step + .bw_step::before')] = { 'background-color': palette.light.border };
835
+ rules[_sx(scope, '.bw_step_active .bw_step_indicator')] = {
785
836
  'background-color': palette.primary.base,
786
837
  'color': palette.primary.textOn
787
838
  };
788
- rules[scopeSelector(scope, '.bw_step_active .bw_step_label')] = {
839
+ rules[_sx(scope, '.bw_step_active .bw_step_label')] = {
789
840
  'color': palette.dark.base,
790
841
  'font-weight': '600'
791
842
  };
792
- rules[scopeSelector(scope, '.bw_step_completed .bw_step_indicator')] = {
843
+ rules[_sx(scope, '.bw_step_completed .bw_step_indicator')] = {
793
844
  'background-color': palette.primary.base,
794
845
  'color': palette.primary.textOn
795
846
  };
796
- rules[scopeSelector(scope, '.bw_step_completed .bw_step_label')] = { 'color': palette.primary.base };
797
- rules[scopeSelector(scope, '.bw_step_completed + .bw_step::before')] = { 'background-color': palette.primary.base };
847
+ rules[_sx(scope, '.bw_step_completed .bw_step_label')] = { 'color': palette.primary.base };
848
+ rules[_sx(scope, '.bw_step_completed + .bw_step::before')] = { 'background-color': palette.primary.base };
798
849
  return rules;
799
850
  }
800
851
 
801
852
  function generateChipInputThemed(scope, palette) {
802
853
  var rules = {};
803
- rules[scopeSelector(scope, '.bw_chip_input')] = { 'border-color': palette.light.border };
804
- rules[scopeSelector(scope, '.bw_chip_input:focus-within')] = {
854
+ rules[_sx(scope, '.bw_chip_input')] = {
855
+ 'border-color': palette.light.border,
856
+ 'background-color': palette.surface || '#fff',
857
+ 'color': palette.dark.base
858
+ };
859
+ rules[_sx(scope, '.bw_chip_input:focus-within')] = {
805
860
  'border-color': palette.primary.base,
806
861
  'box-shadow': '0 0 0 0.2rem ' + palette.primary.focus
807
862
  };
808
- rules[scopeSelector(scope, '.bw_chip')] = {
809
- 'background-color': palette.light.light,
863
+ rules[_sx(scope, '.bw_chip')] = {
864
+ 'background-color': palette.surfaceAlt,
810
865
  'color': palette.dark.base
811
866
  };
812
- rules[scopeSelector(scope, '.bw_chip_remove:hover')] = {
867
+ rules[_sx(scope, '.bw_chip_remove:hover')] = {
813
868
  'color': palette.danger.base,
814
869
  'background-color': palette.danger.light
815
870
  };
816
871
  return rules;
817
872
  }
818
873
 
819
- function generateFileUploadThemed(scope, palette) {
820
- var rules = {};
821
- rules[scopeSelector(scope, '.bw_file_upload')] = {
874
+ function generateFileUploadThemed(scope, palette, layout) {
875
+ var rules = {}, mo = layout.motion;
876
+ rules[_sx(scope, '.bw_file_upload')] = {
822
877
  'border-color': palette.light.border,
823
- 'background-color': palette.light.light
878
+ 'background-color': palette.surfaceAlt,
879
+ 'transition': 'border-color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
824
880
  };
825
- rules[scopeSelector(scope, '.bw_file_upload:hover')] = {
881
+ rules[_sx(scope, '.bw_file_upload:hover')] = {
826
882
  'border-color': palette.primary.base,
827
883
  'background-color': palette.primary.light
828
884
  };
829
- rules[scopeSelector(scope, '.bw_file_upload:focus')] = {
885
+ rules[_sx(scope, '.bw_file_upload:focus')] = {
830
886
  'outline': '2px solid ' + palette.primary.base,
831
887
  'outline-offset': '2px'
832
888
  };
833
- rules[scopeSelector(scope, '.bw_file_upload.bw_file_upload_active')] = {
889
+ rules[_sx(scope, '.bw_file_upload.bw_file_upload_active')] = {
834
890
  'border-color': palette.primary.base,
835
891
  'background-color': palette.primary.light,
836
892
  'border-style': 'solid'
@@ -840,35 +896,73 @@ function generateFileUploadThemed(scope, palette) {
840
896
 
841
897
  function generateRangeThemed(scope, palette) {
842
898
  var rules = {};
843
- rules[scopeSelector(scope, '.bw_range')] = { 'background-color': palette.light.border };
844
- rules[scopeSelector(scope, '.bw_range::-webkit-slider-thumb')] = {
899
+ rules[_sx(scope, '.bw_range')] = { 'background-color': palette.light.border };
900
+ rules[_sx(scope, '.bw_range::-webkit-slider-thumb')] = {
845
901
  'background-color': palette.primary.base,
846
- 'border-color': '#fff',
902
+ 'border-color': palette.surface || '#fff',
847
903
  'box-shadow': '0 1px 3px rgba(0,0,0,0.2)',
848
904
  'transition': 'background-color 0.15s ease-out, transform 0.15s ease-out'
849
905
  };
850
- rules[scopeSelector(scope, '.bw_range::-moz-range-thumb')] = {
906
+ rules[_sx(scope, '.bw_range::-moz-range-thumb')] = {
851
907
  'background-color': palette.primary.base,
852
- 'border-color': '#fff',
908
+ 'border-color': palette.surface || '#fff',
853
909
  'box-shadow': '0 1px 3px rgba(0,0,0,0.2)'
854
910
  };
855
911
  return rules;
856
912
  }
857
913
 
858
- function generateSearchThemed(scope, palette) {
859
- var rules = {};
860
- rules[scopeSelector(scope, '.bw_search_clear:hover')] = { 'color': palette.dark.base };
914
+ function generateTooltipThemed(scope, palette, layout) {
915
+ var rules = {}, sp = layout.spacing, rd = layout.radius, el = layout.elevation, mo = layout.motion;
916
+ rules[_sx(scope, '.bw_tooltip')] = {
917
+ 'background-color': palette.dark.base, 'color': palette.dark.textOn,
918
+ 'padding': sp.input, 'border-radius': rd.badge, 'box-shadow': el.md,
919
+ 'transition': 'opacity ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
920
+ };
921
+ return rules;
922
+ }
923
+
924
+ function generatePopoverThemed(scope, palette, layout) {
925
+ var rules = {}, sp = layout.spacing, rd = layout.radius, el = layout.elevation, mo = layout.motion;
926
+ rules[_sx(scope, '.bw_popover')] = {
927
+ 'background-color': palette.surface || '#fff', 'color': palette.dark.base,
928
+ 'border': '1px solid ' + palette.light.border, 'border-radius': rd.card, 'box-shadow': el.lg,
929
+ 'transition': 'opacity ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
930
+ };
931
+ rules[_sx(scope, '.bw_popover_header')] = {
932
+ 'background-color': palette.surfaceAlt, 'border-bottom': '1px solid ' + palette.light.border,
933
+ 'padding': sp.input
934
+ };
935
+ rules[_sx(scope, '.bw_popover_body')] = { 'padding': sp.card };
861
936
  return rules;
862
937
  }
863
938
 
864
- function generateCodeDemoThemed(scope, palette) {
939
+ function generateSearchThemed(scope, palette, layout) {
940
+ var rules = {}, mo = layout.motion;
941
+ rules[_sx(scope, '.bw_search_input')] = {
942
+ 'background-color': palette.surface || '#fff',
943
+ 'color': palette.dark.base
944
+ };
945
+ rules[_sx(scope, '.bw_search_clear')] = {
946
+ 'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
947
+ };
948
+ rules[_sx(scope, '.bw_search_clear:hover')] = { 'color': palette.dark.base };
949
+ return rules;
950
+ }
951
+
952
+ function generateCodeDemoThemed(scope, palette, layout) {
865
953
  var rules = {};
866
- rules[scopeSelector(scope, '.bw_code_copy_btn_copied')] = {
954
+ var rd = layout ? layout.radius : { card: '0.375rem' };
955
+ rules[_sx(scope, '.bw_code_demo')] = {
956
+ 'background-color': palette.surface || '#fff',
957
+ 'color': palette.dark.base,
958
+ 'border-radius': rd.card
959
+ };
960
+ rules[_sx(scope, '.bw_code_copy_btn_copied')] = {
867
961
  'background': palette.success.base,
868
962
  'color': palette.success.textOn,
869
963
  'border-color': palette.success.base
870
964
  };
871
- rules[scopeSelector(scope, '.bw_copy_btn:hover')] = {
965
+ rules[_sx(scope, '.bw_copy_btn:hover')] = {
872
966
  'background': 'rgba(255,255,255,0.2)',
873
967
  'color': '#fff'
874
968
  };
@@ -878,7 +972,7 @@ function generateCodeDemoThemed(scope, palette) {
878
972
  function generateNavPillsThemed(scope, palette, layout) {
879
973
  var rules = {};
880
974
  var rd = layout.radius;
881
- rules[scopeSelector(scope, '.bw_nav_pills .bw_nav_link')] = { 'border-radius': rd.btn };
975
+ rules[_sx(scope, '.bw_nav_pills .bw_nav_link')] = { 'border-radius': rd.btn };
882
976
  return rules;
883
977
  }
884
978
 
@@ -904,21 +998,21 @@ function generatePaletteClasses(scope, palette) {
904
998
  var s = palette[k];
905
999
 
906
1000
  // --- Root palette class: sets default bg/color/border ---
907
- rules[scopeSelector(scope, '.bw_' + k)] = {
1001
+ rules[_sx(scope, '.bw_' + k)] = {
908
1002
  'background-color': s.base,
909
1003
  'color': s.textOn,
910
1004
  'border-color': s.base
911
1005
  };
912
1006
 
913
1007
  // --- Pseudo-states (shared across all components) ---
914
- rules[scopeSelector(scope, '.bw_' + k + ':hover')] = {
1008
+ rules[_sx(scope, '.bw_' + k + ':hover')] = {
915
1009
  'background-color': s.hover,
916
1010
  'border-color': s.active
917
1011
  };
918
- rules[scopeSelector(scope, '.bw_' + k + ':active')] = {
1012
+ rules[_sx(scope, '.bw_' + k + ':active')] = {
919
1013
  'background-color': s.active
920
1014
  };
921
- rules[scopeSelector(scope, '.bw_' + k + ':focus-visible')] = {
1015
+ rules[_sx(scope, '.bw_' + k + ':focus-visible')] = {
922
1016
  'box-shadow': '0 0 0 3px ' + s.focus,
923
1017
  'outline': 'none'
924
1018
  };
@@ -926,70 +1020,99 @@ function generatePaletteClasses(scope, palette) {
926
1020
  // --- Component-specific overrides ---
927
1021
 
928
1022
  // Alerts: light bg, dark text, subtle border
929
- rules[scopeSelector(scope, '.bw_alert.bw_' + k)] = {
1023
+ rules[_sx(scope, '.bw_alert.bw_' + k)] = {
930
1024
  'background-color': s.light,
931
1025
  'color': s.darkText,
932
1026
  'border-color': s.border
933
1027
  };
934
1028
 
935
1029
  // Toast: inherit bg, left border accent
936
- rules[scopeSelector(scope, '.bw_toast.bw_' + k)] = {
1030
+ rules[_sx(scope, '.bw_toast.bw_' + k)] = {
937
1031
  'background-color': 'inherit',
938
1032
  'color': 'inherit',
939
1033
  'border-left': '4px solid ' + s.base
940
1034
  };
941
1035
 
942
1036
  // Stat card: inherit bg, left border accent
943
- rules[scopeSelector(scope, '.bw_stat_card.bw_' + k)] = {
1037
+ rules[_sx(scope, '.bw_stat_card.bw_' + k)] = {
944
1038
  'background-color': 'inherit',
945
1039
  'color': 'inherit',
946
1040
  'border-left-color': s.base
947
1041
  };
948
1042
 
949
1043
  // Card accent: left border accent, inherit bg
950
- rules[scopeSelector(scope, '.bw_card.bw_' + k)] = {
1044
+ rules[_sx(scope, '.bw_card.bw_' + k)] = {
951
1045
  'background-color': 'inherit',
952
1046
  'color': 'inherit',
953
1047
  'border-left': '4px solid ' + s.base
954
1048
  };
955
1049
 
956
1050
  // Timeline marker: colored dot
957
- rules[scopeSelector(scope, '.bw_timeline_marker.bw_' + k)] = {
1051
+ rules[_sx(scope, '.bw_timeline_marker.bw_' + k)] = {
958
1052
  'box-shadow': '0 0 0 2px ' + s.base
959
1053
  };
960
1054
 
961
- // Spinner: text color only, transparent bg
962
- rules[scopeSelector(scope, '.bw_spinner_border.bw_' + k + ',\n' + scopeSelector(scope, '.bw_spinner_grow.bw_' + k))] = {
1055
+ // Spinner: set color, re-apply border pattern so the root palette class
1056
+ // border-color doesn't fill in the transparent gap that makes it spin.
1057
+ // Also neutralize hover/active which would override border-right-color.
1058
+ rules[_sx(scope, '.bw_spinner_border.bw_' + k)] = {
963
1059
  'background-color': 'transparent',
964
1060
  'color': s.base,
965
- 'border-color': 'currentColor'
1061
+ 'border-color': s.base,
1062
+ 'border-right-color': 'transparent'
1063
+ };
1064
+ rules[_sx(scope, '.bw_spinner_border.bw_' + k + ':hover')] = {
1065
+ 'background-color': 'transparent',
1066
+ 'border-color': s.base,
1067
+ 'border-right-color': 'transparent'
1068
+ };
1069
+ rules[_sx(scope, '.bw_spinner_grow.bw_' + k)] = {
1070
+ 'background-color': s.base,
1071
+ 'color': s.base
966
1072
  };
967
1073
 
968
1074
  // Outline button: transparent bg, colored border+text, solid on hover
969
- rules[scopeSelector(scope, '.bw_btn_outline.bw_' + k)] = {
1075
+ rules[_sx(scope, '.bw_btn_outline.bw_' + k)] = {
970
1076
  'background-color': 'transparent',
971
1077
  'color': s.base,
972
1078
  'border-color': s.base
973
1079
  };
974
- rules[scopeSelector(scope, '.bw_btn_outline.bw_' + k + ':hover')] = {
1080
+ rules[_sx(scope, '.bw_btn_outline.bw_' + k + ':hover')] = {
975
1081
  'background-color': s.base,
976
1082
  'color': s.textOn
977
1083
  };
978
1084
 
979
1085
  // Hero: gradient background
980
- rules[scopeSelector(scope, '.bw_hero.bw_' + k)] = {
1086
+ rules[_sx(scope, '.bw_hero.bw_' + k)] = {
981
1087
  'background': 'linear-gradient(135deg, ' + s.base + ' 0%, ' + s.hover + ' 100%)',
982
1088
  'color': s.textOn
983
1089
  };
984
1090
 
985
- // Progress bar: white text on colored bg (default is fine, just ensure text)
986
- rules[scopeSelector(scope, '.bw_progress_bar.bw_' + k)] = {
987
- 'color': '#fff'
1091
+ // Progress bar: contrasting text on colored bg
1092
+ rules[_sx(scope, '.bw_progress_bar.bw_' + k)] = {
1093
+ 'color': s.textOn
1094
+ };
1095
+
1096
+ // Background utility: .bw_bg_primary, .bw_bg_secondary, etc.
1097
+ rules[_sx(scope, '.bw_bg_' + k)] = {
1098
+ 'background-color': s.base,
1099
+ 'color': s.textOn
1100
+ };
1101
+
1102
+ // Text color utility: .bw_text_primary, .bw_text_secondary, etc.
1103
+ rules[_sx(scope, '.bw_text_' + k)] = {
1104
+ 'color': s.base
988
1105
  };
989
1106
  });
990
1107
 
991
- // Text muted
992
- rules[scopeSelector(scope, '.bw_text_muted')] = { 'color': palette.secondary.base };
1108
+ // Text muted — always a neutral gray, never a brand color
1109
+ rules[_sx(scope, '.bw_text_muted')] = { 'color': '#6c757d' };
1110
+
1111
+ // Common bg/text utilities that aren't per-variant
1112
+ rules[_sx(scope, '.bw_bg_dark')] = { 'background-color': '#212529', 'color': '#f8f9fa' };
1113
+ rules[_sx(scope, '.bw_bg_light')] = { 'background-color': '#f8f9fa', 'color': '#212529' };
1114
+ rules[_sx(scope, '.bw_text_light')] = { 'color': '#f8f9fa' };
1115
+ rules[_sx(scope, '.bw_text_dark')] = { 'color': '#212529' };
993
1116
 
994
1117
  return rules;
995
1118
  }
@@ -1011,30 +1134,32 @@ export function generateThemedCSS(scopeName, palette, layout) {
1011
1134
  generateAlerts(scopeName, palette, layout),
1012
1135
  generateCards(scopeName, palette, layout),
1013
1136
  generateForms(scopeName, palette, layout),
1014
- generateNavigation(scopeName, palette),
1137
+ generateNavigation(scopeName, palette, layout),
1015
1138
  generateTables(scopeName, palette, layout),
1016
- generateTabs(scopeName, palette),
1139
+ generateTabs(scopeName, palette, layout),
1017
1140
  generateListGroups(scopeName, palette, layout),
1018
- generatePagination(scopeName, palette),
1141
+ generatePagination(scopeName, palette, layout),
1019
1142
  generateProgress(scopeName, palette),
1020
- generateBreadcrumbThemed(scopeName, palette),
1143
+ generateBreadcrumbThemed(scopeName, palette, layout),
1021
1144
  generateCloseButtonThemed(scopeName, palette),
1022
1145
  generateSectionsThemed(scopeName, palette),
1023
- generateAccordionThemed(scopeName, palette),
1146
+ generateAccordionThemed(scopeName, palette, layout),
1024
1147
  generateCarouselThemed(scopeName, palette),
1025
1148
  generateModalThemed(scopeName, palette, layout),
1026
1149
  generateToastThemed(scopeName, palette, layout),
1027
1150
  generateDropdownThemed(scopeName, palette, layout),
1028
1151
  generateSwitchThemed(scopeName, palette),
1029
1152
  generateSkeletonThemed(scopeName, palette),
1030
- generateStatCardThemed(scopeName, palette),
1153
+ generateStatCardThemed(scopeName, palette, layout),
1031
1154
  generateTimelineThemed(scopeName, palette),
1032
1155
  generateStepperThemed(scopeName, palette),
1033
1156
  generateChipInputThemed(scopeName, palette),
1034
- generateFileUploadThemed(scopeName, palette),
1157
+ generateFileUploadThemed(scopeName, palette, layout),
1035
1158
  generateRangeThemed(scopeName, palette),
1036
- generateSearchThemed(scopeName, palette),
1037
- generateCodeDemoThemed(scopeName, palette),
1159
+ generateSearchThemed(scopeName, palette, layout),
1160
+ generateTooltipThemed(scopeName, palette, layout),
1161
+ generatePopoverThemed(scopeName, palette, layout),
1162
+ generateCodeDemoThemed(scopeName, palette, layout),
1038
1163
  generateNavPillsThemed(scopeName, palette, layout),
1039
1164
  generatePaletteClasses(scopeName, palette)
1040
1165
  );
@@ -1259,6 +1384,8 @@ var structuralRules = {
1259
1384
  },
1260
1385
  '.bw_table caption': { 'font-size': '0.875rem', 'caption-side': 'bottom' },
1261
1386
  '.bw_table_bordered > :not(caption) > * > *': { 'border-width': '1px', 'border-style': 'solid' },
1387
+ '.bw_table_selectable > tbody > tr': { 'cursor': 'pointer' },
1388
+ '.bw_table > tbody > tr.bw_table_row_selected > *': { 'background-color': 'rgba(0, 102, 102, 0.1)' },
1262
1389
  '.bw_table_responsive': { 'overflow-x': 'auto', '-webkit-overflow-scrolling': 'touch' }
1263
1390
  },
1264
1391
 
@@ -1312,6 +1439,7 @@ var structuralRules = {
1312
1439
  '.bw_nav_tabs .bw_nav_item': { 'margin-bottom': '-2px' },
1313
1440
  '.bw_nav_link': {
1314
1441
  'display': 'block', 'font-size': '0.875rem', 'font-weight': '500',
1442
+ 'padding': '0.625rem 1rem',
1315
1443
  'text-decoration': 'none', 'cursor': 'pointer',
1316
1444
  'border': 'none', 'background': 'transparent', 'font-family': 'inherit'
1317
1445
  },
@@ -1346,10 +1474,11 @@ var structuralRules = {
1346
1474
  '.bw_page_item': { 'display': 'list-item', 'list-style': 'none' },
1347
1475
  '.bw_page_link': {
1348
1476
  'position': 'relative', 'display': 'block', 'padding': '0.375rem 0.75rem',
1349
- 'margin-left': '-1px', 'line-height': '1.25', 'text-decoration': 'none'
1477
+ 'margin-left': '-1px', 'line-height': '1.25', 'text-decoration': 'none',
1478
+ 'border': '1px solid transparent', 'cursor': 'pointer',
1479
+ 'font-family': 'inherit', 'font-size': 'inherit', 'background': 'none'
1350
1480
  },
1351
- '.bw_page_item:first-child .bw_page_link': { 'margin-left': '0', 'border-top-left-radius': '0.375rem', 'border-bottom-left-radius': '0.375rem' },
1352
- '.bw_page_item:last-child .bw_page_link': { 'border-top-right-radius': '0.375rem', 'border-bottom-right-radius': '0.375rem' },
1481
+ '.bw_page_item:first-child .bw_page_link': { 'margin-left': '0' },
1353
1482
  '.bw_page_link:focus-visible': { 'z-index': '3', 'outline': '2px solid currentColor', 'outline-offset': '-2px' }
1354
1483
  },
1355
1484
 
@@ -1506,6 +1635,7 @@ var structuralRules = {
1506
1635
  '.bw_accordion_header': { 'margin': '0' },
1507
1636
  '.bw_accordion_button': {
1508
1637
  'position': 'relative', 'display': 'flex', 'align-items': 'center', 'width': '100%',
1638
+ 'padding': '0.875rem 1.25rem',
1509
1639
  'font-size': '1rem', 'font-weight': '500', 'text-align': 'left',
1510
1640
  'background-color': 'transparent', 'border': '0', 'overflow-anchor': 'none', 'cursor': 'pointer',
1511
1641
  'font-family': 'inherit'
@@ -1517,10 +1647,9 @@ var structuralRules = {
1517
1647
  'background-repeat': 'no-repeat', 'background-size': '1.25rem'
1518
1648
  },
1519
1649
  '.bw_accordion_button:not(.bw_collapsed)::after': { 'transform': 'rotate(-180deg)' },
1520
- '.bw_accordion_collapse': { 'max-height': '0', 'overflow': 'hidden' },
1521
- '.bw_accordion_collapse.bw_collapse_show': { 'max-height': 'none' },
1522
- '.bw_accordion_item:first-child': { 'border-top-left-radius': '8px', 'border-top-right-radius': '8px' },
1523
- '.bw_accordion_item:last-child': { 'border-bottom-left-radius': '8px', 'border-bottom-right-radius': '8px' }
1650
+ '.bw_accordion_body': { 'padding': '1rem 1.25rem' },
1651
+ '.bw_accordion_collapse': { 'max-height': '0', 'overflow': 'hidden', 'transition': 'max-height 0.3s ease' },
1652
+ '.bw_accordion_collapse.bw_collapse_show': { 'max-height': 'none' }
1524
1653
  },
1525
1654
 
1526
1655
  // ---- Carousel ----
@@ -1574,10 +1703,10 @@ var structuralRules = {
1574
1703
  'position': 'relative', 'display': 'flex', 'flex-direction': 'column', 'pointer-events': 'auto',
1575
1704
  'background-clip': 'padding-box', 'border': '1px solid transparent', 'outline': '0'
1576
1705
  },
1577
- '.bw_modal_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between' },
1706
+ '.bw_modal_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'padding': '1rem 1.25rem', 'border-bottom': '1px solid transparent' },
1578
1707
  '.bw_modal_title': { 'margin': '0', 'font-size': '1.25rem', 'font-weight': '600', 'line-height': '1.3' },
1579
- '.bw_modal_body': { 'position': 'relative', 'flex': '1 1 auto' },
1580
- '.bw_modal_footer': { 'display': 'flex', 'flex-wrap': 'wrap', 'align-items': 'center', 'justify-content': 'flex-end', 'gap': '0.5rem' }
1708
+ '.bw_modal_body': { 'position': 'relative', 'flex': '1 1 auto', 'padding': '1rem 1.25rem' },
1709
+ '.bw_modal_footer': { 'display': 'flex', 'flex-wrap': 'wrap', 'align-items': 'center', 'justify-content': 'flex-end', 'gap': '0.5rem', 'padding': '0.75rem 1.25rem', 'border-top': '1px solid transparent' }
1581
1710
  },
1582
1711
 
1583
1712
  // ---- Toast ----
@@ -1598,8 +1727,8 @@ var structuralRules = {
1598
1727
  },
1599
1728
  '.bw_toast.bw_toast_show': { 'opacity': '1', 'transform': 'translateY(0)' },
1600
1729
  '.bw_toast.bw_toast_hiding': { 'opacity': '0' },
1601
- '.bw_toast_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'font-size': '0.875rem' },
1602
- '.bw_toast_body': { 'font-size': '0.9375rem' }
1730
+ '.bw_toast_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'padding': '0.5rem 0.75rem', 'font-size': '0.875rem', 'border-bottom': '1px solid transparent' },
1731
+ '.bw_toast_body': { 'padding': '0.5rem 0.75rem', 'font-size': '0.9375rem' }
1603
1732
  },
1604
1733
 
1605
1734
  // ---- Dropdown ----
@@ -1613,15 +1742,15 @@ var structuralRules = {
1613
1742
  '.bw_dropdown_menu': {
1614
1743
  'position': 'absolute', 'top': '100%', 'left': '0', 'z-index': '1000', 'display': 'block',
1615
1744
  'min-width': '10rem', 'padding': '0.5rem 0', 'margin': '0.125rem 0 0',
1616
- 'background-clip': 'padding-box',
1745
+ 'background-clip': 'padding-box', 'border': '1px solid transparent',
1617
1746
  'opacity': '0', 'visibility': 'hidden', 'pointer-events': 'none'
1618
1747
  },
1619
1748
  '.bw_dropdown_menu.bw_dropdown_show': { 'opacity': '1', 'visibility': 'visible', 'pointer-events': 'auto' },
1620
1749
  '.bw_dropdown_menu_end': { 'left': 'auto', 'right': '0' },
1621
1750
  '.bw_dropdown_item': {
1622
- 'display': 'block', 'width': '100%', 'clear': 'both',
1751
+ 'display': 'block', 'width': '100%', 'padding': '0.4rem 1rem', 'clear': 'both',
1623
1752
  'font-weight': '400', 'text-align': 'inherit', 'text-decoration': 'none', 'white-space': 'nowrap',
1624
- 'background-color': 'transparent', 'border': '0', 'font-size': '0.9375rem'
1753
+ 'background-color': 'transparent', 'border': '0', 'font-size': '0.9375rem', 'cursor': 'pointer'
1625
1754
  },
1626
1755
  '.bw_dropdown_item:focus-visible': { 'outline': '2px solid currentColor', 'outline-offset': '-2px' },
1627
1756
  '.bw_dropdown_divider': { 'height': '0', 'margin': '0.5rem 0', 'overflow': 'hidden', 'opacity': '1' }
@@ -1666,7 +1795,13 @@ var structuralRules = {
1666
1795
 
1667
1796
  // ---- Stat card ----
1668
1797
  statCard: {
1669
- '.bw_stat_card': { 'border-left': '4px solid transparent' },
1798
+ '.bw_stat_card': {
1799
+ 'padding': '1.25rem',
1800
+ 'border-left': '4px solid transparent',
1801
+ 'border-radius': '0.375rem',
1802
+ 'background-color': 'inherit',
1803
+ 'transition': 'transform 0.15s ease'
1804
+ },
1670
1805
  '.bw_stat_card:hover': { 'transform': 'translateY(-1px)' },
1671
1806
  '.bw_stat_icon': { 'font-size': '1.5rem', 'margin-bottom': '0.5rem' },
1672
1807
  '.bw_stat_value': { 'font-size': '2rem', 'font-weight': '700', 'line-height': '1.2' },
@@ -1937,6 +2072,33 @@ function generateUtilityRules() {
1937
2072
  rules['.bw_text_left'] = { 'text-align': 'left' };
1938
2073
  rules['.bw_text_right'] = { 'text-align': 'right' };
1939
2074
  rules['.bw_text_center'] = { 'text-align': 'center' };
2075
+ rules['.bw_text_justify'] = { 'text-align': 'justify' };
2076
+
2077
+ // Font weight
2078
+ rules['.bw_fw_bold'] = { 'font-weight': '700' };
2079
+ rules['.bw_fw_semibold'] = { 'font-weight': '600' };
2080
+ rules['.bw_fw_normal'] = { 'font-weight': '400' };
2081
+ rules['.bw_fw_light'] = { 'font-weight': '300' };
2082
+
2083
+ // Font style
2084
+ rules['.bw_fst_italic'] = { 'font-style': 'italic' };
2085
+ rules['.bw_fst_normal'] = { 'font-style': 'normal' };
2086
+
2087
+ // Text decoration
2088
+ rules['.bw_text_underline'] = { 'text-decoration': 'underline' };
2089
+ rules['.bw_text_line_through'] = { 'text-decoration': 'line-through' };
2090
+ rules['.bw_text_decoration_none'] = { 'text-decoration': 'none' };
2091
+
2092
+ // Text transform
2093
+ rules['.bw_text_uppercase'] = { 'text-transform': 'uppercase' };
2094
+ rules['.bw_text_lowercase'] = { 'text-transform': 'lowercase' };
2095
+ rules['.bw_text_capitalize'] = { 'text-transform': 'capitalize' };
2096
+
2097
+ // Font size
2098
+ rules['.bw_fs_sm'] = { 'font-size': '0.875rem' };
2099
+ rules['.bw_fs_base'] = { 'font-size': '1rem' };
2100
+ rules['.bw_fs_lg'] = { 'font-size': '1.25rem' };
2101
+ rules['.bw_fs_xl'] = { 'font-size': '1.5rem' };
1940
2102
 
1941
2103
  // Flexbox
1942
2104
  var jc = { start: 'flex-start', end: 'flex-end', center: 'center', between: 'space-between', around: 'space-around' };
@@ -2029,6 +2191,20 @@ function generateUtilityRules() {
2029
2191
  rules['.list-inline-item'] = { 'display': 'inline-block' };
2030
2192
  rules['.list-inline-item:not(:last-child)'] = { 'margin-right': '.5rem' };
2031
2193
 
2194
+ // Typography — bw_ prefixed utilities via loops
2195
+ var _imp = function(p, v) { var o = {}; o[p] = v + ' !important'; return o; };
2196
+ [['fs',{'xs':'0.75rem','sm':'0.875rem','base':'1rem','lg':'1.125rem','xl':'1.25rem','2xl':'1.5rem'},'font-size'],
2197
+ ['fw',{light:'300',normal:'400',medium:'500',semibold:'600',bold:'700'},'font-weight'],
2198
+ ['lh',{tight:'1.25',normal:'1.5',relaxed:'1.75'},'line-height']
2199
+ ].forEach(function(d) { for (var dk in d[1]) rules['.bw_'+d[0]+'_'+dk] = _imp(d[2], d[1][dk]); });
2200
+
2201
+ // Flex utilities
2202
+ rules['.bw_flex'] = { 'display': 'flex' };
2203
+ rules['.bw_flex_column'] = { 'flex-direction': 'column' };
2204
+ rules['.bw_flex_wrap'] = { 'flex-wrap': 'wrap' };
2205
+ rules['.bw_flex_center'] = { 'display': 'flex', 'align-items': 'center', 'justify-content': 'center' };
2206
+ for (var gk in spacingValues) rules['.bw_gap_' + gk] = { 'gap': spacingValues[gk] + ' !important' };
2207
+
2032
2208
  // Visibility
2033
2209
  rules['.bw_visible, .visible'] = { 'visibility': 'visible !important' };
2034
2210
  rules['.bw_invisible, .invisible'] = { 'visibility': 'hidden !important' };
@@ -2089,6 +2265,26 @@ export function getStructuralStyles() {
2089
2265
  return getStructuralCSS();
2090
2266
  }
2091
2267
 
2268
+ /**
2269
+ * Get CSS reset rules only (box-sizing, html/body font, reduced-motion).
2270
+ * Separate from themed/structural rules for independent injection.
2271
+ * @returns {Object} CSS rules object for the reset layer
2272
+ */
2273
+ export function getResetStyles() {
2274
+ var rules = {};
2275
+ Object.assign(rules, structuralRules.base);
2276
+ // Include reduced-motion preference
2277
+ rules['@media (prefers-reduced-motion: reduce)'] = {
2278
+ '*, *::before, *::after': {
2279
+ 'animation-duration': '0.01ms !important',
2280
+ 'animation-iteration-count': '1 !important',
2281
+ 'transition-duration': '0.01ms !important',
2282
+ 'scroll-behavior': 'auto !important'
2283
+ }
2284
+ };
2285
+ return rules;
2286
+ }
2287
+
2092
2288
  // =========================================================================
2093
2289
  // defaultStyles — backward-compatible categorized view
2094
2290
  // =========================================================================
@@ -2126,7 +2322,7 @@ export function getAllStyles() {
2126
2322
  }
2127
2323
 
2128
2324
  // =========================================================================
2129
- // Theme configuration object (deprecated — use generateTheme())
2325
+ // Theme configuration object (deprecated — use makeStyles())
2130
2326
  // =========================================================================
2131
2327
 
2132
2328
  export let theme = {
@@ -2231,6 +2427,44 @@ export function generateAlternateCSS(name, altPalette, layout) {
2231
2427
  return altRules;
2232
2428
  }
2233
2429
 
2430
+ /**
2431
+ * Prefix every selector in a rules object with a scope selector.
2432
+ * Handles @media/@keyframes blocks and comma-separated selectors.
2433
+ * @param {Object} rules - CSS rules object
2434
+ * @param {string} prefix - Scope prefix (e.g. '#my-dashboard', '.bw_theme_alt')
2435
+ * @param {boolean} [compound=false] - If true, use compound selector (no space)
2436
+ * for the first segment: `#scope.bw_theme_alt .sel` vs `#scope .sel`
2437
+ * @returns {Object} New rules object with scoped selectors
2438
+ */
2439
+ export function scopeRulesUnder(rules, prefix, compound) {
2440
+ var scoped = {};
2441
+ for (var sel in rules) {
2442
+ if (!rules.hasOwnProperty(sel)) continue;
2443
+ if (sel.charAt(0) === '@') {
2444
+ // @media / @keyframes — recurse into the block
2445
+ var innerBlock = rules[sel];
2446
+ var scopedInner = {};
2447
+ for (var innerSel in innerBlock) {
2448
+ if (!innerBlock.hasOwnProperty(innerSel)) continue;
2449
+ scopedInner[_prefixSelector(innerSel, prefix)] = innerBlock[innerSel];
2450
+ }
2451
+ scoped[sel] = scopedInner;
2452
+ } else {
2453
+ scoped[_prefixSelector(sel, prefix)] = rules[sel];
2454
+ }
2455
+ }
2456
+ return scoped;
2457
+ }
2458
+
2459
+ function _prefixSelector(sel, prefix) {
2460
+ var parts = sel.split(',');
2461
+ var result = [];
2462
+ for (var i = 0; i < parts.length; i++) {
2463
+ result.push(prefix + ' ' + parts[i].trim());
2464
+ }
2465
+ return result.join(', ');
2466
+ }
2467
+
2234
2468
  export function deepMerge(target, source) {
2235
2469
  for (const key of Object.keys(source)) {
2236
2470
  if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])