@ambientcss/components 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -641,7 +641,10 @@
641
641
  here and the frame's rotation carries them round to the value.
642
642
 
643
643
  The circle is the grounded referent's offset dot (knob() dot_frac 0.12,
644
- dot_offset 0.68). */
644
+ dot_offset 0.68). The ink is --amb-lume: the same emissive tone the
645
+ scene's lume and LEDs wear, so the pointer reads as backlit hardware
646
+ rather than as printed paint — bright in a bright scene, glowing when
647
+ the lights drop. */
645
648
  .amb-knob-indicator-circle {
646
649
  position: absolute;
647
650
  top: 16%;
@@ -650,7 +653,7 @@
650
653
  width: calc(var(--ambx-knob-size) * 0.125);
651
654
  height: calc(var(--ambx-knob-size) * 0.125);
652
655
  border-radius: 50%;
653
- background: var(--amb-highlight-color);
656
+ background: var(--amb-lume);
654
657
  }
655
658
 
656
659
  /* The rectangle is a short radial bar out near the rim, not a spoke from
@@ -667,7 +670,7 @@
667
670
  width: var(--ambx-knob-rect-width);
668
671
  height: 17%;
669
672
  border-radius: var(--ambx-grid-quarter);
670
- background: var(--amb-highlight-color);
673
+ background: var(--amb-lume);
671
674
  }
672
675
 
673
676
  .amb-knob:hover .amb-knob-indicator-circle,
@@ -682,8 +685,9 @@
682
685
  --ambx-travel-start/-sweep off the control), so a dot always lands where
683
686
  its value would, whatever `travel` is set to.
684
687
 
685
- Ink is --amb-label, not --amb-highlight-color: these are printed panel
686
- graphics like the control's legend, not part of the control. */
688
+ Ink is --amb-lume, matching the indicators: the scale glows with the
689
+ same emissive tone as the pointer, so ring and dot read as one lit
690
+ instrument rather than paint plus light. */
687
691
  .amb-knob-marker-ring {
688
692
  position: absolute;
689
693
  top: 50%;
@@ -701,7 +705,7 @@
701
705
  height: var(--ambx-knob-marker-size);
702
706
  margin: calc(var(--ambx-knob-marker-size) / -2);
703
707
  border-radius: 50%;
704
- background: var(--amb-label);
708
+ background: var(--amb-lume);
705
709
  transform: rotate(var(--amb-marker-angle))
706
710
  translateY(calc(-1 * var(--ambx-knob-marker-radius)));
707
711
  }
@@ -1270,7 +1274,9 @@
1270
1274
  /* The indicator: a short mark printed across the bar near one end. It is
1271
1275
  ink, not geometry — no edges, no shadow — and it is inside the bar rather
1272
1276
  than beside it because the bar already occupies the whole diameter it
1273
- would otherwise sit on. */
1277
+ would otherwise sit on. The ink is --amb-lume, matching the grounded
1278
+ knob's pointer: the mark reads as backlit rather than printed, glowing
1279
+ as the scene's lights drop. */
1274
1280
  .amb-console-indicator {
1275
1281
  position: absolute;
1276
1282
  left: 50%;
@@ -1279,7 +1285,7 @@
1279
1285
  height: 7.5%;
1280
1286
  transform: translateX(-50%);
1281
1287
  border-radius: calc(var(--ambx-size) * 0.012);
1282
- background-color: color-mix(in oklab, var(--amb-lit), black 82%);
1288
+ background-color: var(--amb-lume);
1283
1289
  }
1284
1290
 
1285
1291
  /* Panel graphics: the accent centre mark above the knob and the -/+ legends
@@ -1298,7 +1304,12 @@
1298
1304
  height: calc(var(--ambx-size) * 0.2);
1299
1305
  margin-bottom: calc(var(--ambx-size) * 0.07);
1300
1306
  border-radius: var(--ambx-radius-sm);
1301
- background-color: var(--ambx-accent);
1307
+ /* amb-surface's lit albedo with the albedo pinned to the accent (restated
1308
+ because this sheet loads independently of ambient.css and rule order is
1309
+ not guaranteed) — the mark keeps its orange identity but reads as a
1310
+ little body of material under the scene's light, not printed ink. */
1311
+ --amb-albedo: var(--ambx-accent);
1312
+ background-color: var(--amb-lit);
1302
1313
  transform: translateX(-50%);
1303
1314
  }
1304
1315
 
@@ -1349,15 +1360,35 @@
1349
1360
 
1350
1361
  /* The track is a pure-CSS part: it reads --ambx-percent off the control root
1351
1362
  and mixes its own colour from it, so it lights and dims with the travel
1352
- without the mechanism knowing this element exists. */
1363
+ without the mechanism knowing this element exists.
1364
+
1365
+ BOTH ends of that mix are now lit reflectance rather than paint, so the
1366
+ whole base responds to the lamp:
1367
+ - OFF: a dark grey body — the scene's lit albedo held at 30% in linear
1368
+ light. It sits well below the panel as a recess should but still
1369
+ tracks the lamp, because the base colour is --amb-lit: dim the key
1370
+ and the floor dims with it. (It briefly sat at plain --amb-lit, which
1371
+ read flush with the panel.)
1372
+ - ON: the accent worn as reflectance, through the SAME law the thumb's
1373
+ face goes through. ambient.css publishes its diffuse exposure factor
1374
+ as --amb-exposure (= key*0.6396 + fill*0.5496, applied in linear
1375
+ light), so mixing in `srgb-linear` by that amount reproduces
1376
+ amb-surface's shading exactly — track ON and thumb sit at the same
1377
+ brightness, instead of the mean-of-intensities approximation that
1378
+ left the floor visibly darker than the body it carries. */
1353
1379
  .amb-console-track {
1354
1380
  position: absolute;
1355
1381
  inset: 0;
1356
1382
  border-radius: var(--ambx-radius-full);
1383
+ --_lit-accent: color-mix(
1384
+ in srgb-linear,
1385
+ var(--ambx-accent) calc(var(--amb-exposure) * 100%),
1386
+ black
1387
+ );
1357
1388
  background-color: color-mix(
1358
1389
  in oklab,
1359
- color-mix(in oklab, var(--amb-lit), black 55%),
1360
- var(--ambx-accent) calc(var(--ambx-percent) * 100%)
1390
+ color-mix(in srgb-linear, var(--amb-lit) 30%, black),
1391
+ var(--_lit-accent) calc(var(--ambx-percent) * 100%)
1361
1392
  );
1362
1393
  --amb-thickness: 0.4;
1363
1394
  transition: background-color 160ms ease;
@@ -1366,20 +1397,29 @@
1366
1397
  /* Flat-topped, but a body: `.ambient` with a thickness and NO edge
1367
1398
  treatment, which is the combination that casts a drop shadow while
1368
1399
  leaving the top face unbroken — the chamfer and fillet layers of the
1369
- composite are gated on their own properties, and neither is set here. */
1400
+ composite are gated on their own properties, and neither is set here.
1401
+ The face stays the accent ORANGE, but as reflectance not paint:
1402
+ --amb-albedo feeds amb-surface's exposure law (restated here because
1403
+ this sheet loads independently of ambient.css, so rule order is not
1404
+ guaranteed), so the thumb still re-shades when the lamp moves or the
1405
+ intensities change — an orange body under the scene's light, not a
1406
+ sticker of orange. */
1370
1407
  .amb-console-thumb {
1371
1408
  position: absolute;
1372
1409
  inset: 0;
1373
1410
  border-radius: 50%;
1374
- background-color: color-mix(in oklab, var(--ambx-accent), white 12%);
1375
- /* The white ring is a BORDER, not a spread shadow. Two reasons, and the
1411
+ --amb-albedo: var(--ambx-accent);
1412
+ background-color: var(--amb-lit);
1413
+ /* The ring is a BORDER, not a spread shadow. Two reasons, and the
1376
1414
  second is the load-bearing one: with the package's border-box reset the
1377
1415
  ring grows inward, so the thumb keeps the outer diameter measured off
1378
1416
  the reference — and `box-shadow` is the property `.ambient` composites
1379
1417
  the whole lighting model into, so setting it here would silently wipe
1380
- the drop shadow the thickness above is there to cast. */
1381
- border: calc(var(--ambx-latch-thumb-h) * 0.09) solid
1382
- hsl(var(--amb-light-hue) var(--amb-light-saturation) 99%);
1418
+ the drop shadow the thickness above is there to cast. The ring's ink
1419
+ is --amb-lume: lit albedo vanished against the thumb's orange face, so
1420
+ the ring reads as an emissive halo instead — present in bright light,
1421
+ glowing as the scene dims. */
1422
+ border: calc(var(--ambx-latch-thumb-h) * 0.09) solid var(--amb-lume);
1383
1423
  }
1384
1424
 
1385
1425
  @media (prefers-reduced-motion: reduce) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ambientcss/components",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "React skeuomorphic components powered by Ambient CSS",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,17 +44,23 @@
44
44
  "react-dom": "^18.3.0 || ^19.0.0"
45
45
  },
46
46
  "dependencies": {
47
- "@ambientcss/css": "3.0.0"
47
+ "@ambientcss/css": "3.0.1"
48
48
  },
49
49
  "devDependencies": {
50
+ "@testing-library/dom": "^10.4.1",
51
+ "@testing-library/react": "^16.3.2",
52
+ "@testing-library/user-event": "^14.6.5",
50
53
  "@types/react": "^19.0.8",
51
54
  "@types/react-dom": "^19.0.3",
55
+ "jsdom": "^30.0.1",
52
56
  "tsup": "^8.3.6",
53
- "typescript": "^5.8.2"
57
+ "typescript": "^5.8.2",
58
+ "vitest": "^4.1.11"
54
59
  },
55
60
  "scripts": {
56
61
  "build": "tsup && cp src/styles.css dist/styles.css",
57
62
  "clean": "rm -rf dist",
58
- "typecheck": "tsc --noEmit"
63
+ "typecheck": "tsc --noEmit",
64
+ "test": "vitest run"
59
65
  }
60
66
  }
@@ -106,6 +106,7 @@ export function AmbientBank({
106
106
  const ks = keyState(on, option.disabled || disabled);
107
107
  return (
108
108
  <button
109
+ key={option.value}
109
110
  {...keyProps(option, index)}
110
111
  {...stateData(ks)}
111
112
  className="ambx-key"
@@ -142,7 +142,9 @@ export function useBank(options: UseBankOptions) {
142
142
  const keyProps = (option: BankOption, index: number) => {
143
143
  const on = selected.includes(option.value);
144
144
  return {
145
- key: option.value,
145
+ /* No `key` here on purpose: the caller maps the options, so it owns
146
+ the list key. Spreading one into JSX makes React warn and drops
147
+ the key on release builds. */
146
148
  type: "button" as const,
147
149
  ref: (node: HTMLButtonElement | null) => {
148
150
  keyRefs.current[index] = node;
@@ -52,7 +52,7 @@ export function ConsoleBar({ className }: { className?: string | undefined }) {
52
52
  );
53
53
  }
54
54
 
55
- /** Panel graphics around the knob: the accent centre mark above it, and the
55
+ /** Panel graphics around the knob: the centre mark above it, and the
56
56
  * −/+ legends at the ends of the travel. Both sit outside the knob's own
57
57
  * box, which is what the `panel` frame is for. */
58
58
  export function ConsoleMarks({
@@ -66,7 +66,7 @@ export function ConsoleMarks({
66
66
  }) {
67
67
  return (
68
68
  <span className={cn("amb-console-marks", className)} aria-hidden>
69
- {mark ? <span className="amb-console-mark" /> : null}
69
+ {mark ? <span className="amb-console-mark amb-surface" /> : null}
70
70
  {legend ? (
71
71
  <>
72
72
  <span className="amb-console-legend amb-console-legend-min">−</span>
@@ -87,13 +87,15 @@ export function ToggleTrack({ className }: { className?: string | undefined }) {
87
87
  return <span className={cn("amb-console-track amb-groove", className)} />;
88
88
  }
89
89
 
90
- /** The travelling thumb: an accent disc inside a white ring.
90
+ /** The travelling thumb: an albedo disc inside a light ring.
91
91
  *
92
92
  * Flat on top and deliberately so — no chamfer, no fillet — but still a
93
93
  * knob-scale body, so it casts. That pairing is why the classes are spelt
94
94
  * out rather than reached through `.amb-fillet-2`: the edge treatments set
95
95
  * a thickness of their own, and here the thickness is wanted without the
96
- * cut that usually comes with it. */
96
+ * cut that usually comes with it. It wears `amb-surface`, so its colour IS
97
+ * the scene's albedo under the current light rather than a fixed paint —
98
+ * it re-shades when the lamp moves or the intensities change. */
97
99
  export function ToggleThumb({ className }: { className?: string | undefined }) {
98
- return <span className={cn("amb-console-thumb ambient amb-thickness-2", className)} />;
100
+ return <span className={cn("amb-console-thumb ambient amb-surface amb-thickness-2", className)} />;
99
101
  }
package/src/styles.css CHANGED
@@ -641,7 +641,10 @@
641
641
  here and the frame's rotation carries them round to the value.
642
642
 
643
643
  The circle is the grounded referent's offset dot (knob() dot_frac 0.12,
644
- dot_offset 0.68). */
644
+ dot_offset 0.68). The ink is --amb-lume: the same emissive tone the
645
+ scene's lume and LEDs wear, so the pointer reads as backlit hardware
646
+ rather than as printed paint — bright in a bright scene, glowing when
647
+ the lights drop. */
645
648
  .amb-knob-indicator-circle {
646
649
  position: absolute;
647
650
  top: 16%;
@@ -650,7 +653,7 @@
650
653
  width: calc(var(--ambx-knob-size) * 0.125);
651
654
  height: calc(var(--ambx-knob-size) * 0.125);
652
655
  border-radius: 50%;
653
- background: var(--amb-highlight-color);
656
+ background: var(--amb-lume);
654
657
  }
655
658
 
656
659
  /* The rectangle is a short radial bar out near the rim, not a spoke from
@@ -667,7 +670,7 @@
667
670
  width: var(--ambx-knob-rect-width);
668
671
  height: 17%;
669
672
  border-radius: var(--ambx-grid-quarter);
670
- background: var(--amb-highlight-color);
673
+ background: var(--amb-lume);
671
674
  }
672
675
 
673
676
  .amb-knob:hover .amb-knob-indicator-circle,
@@ -682,8 +685,9 @@
682
685
  --ambx-travel-start/-sweep off the control), so a dot always lands where
683
686
  its value would, whatever `travel` is set to.
684
687
 
685
- Ink is --amb-label, not --amb-highlight-color: these are printed panel
686
- graphics like the control's legend, not part of the control. */
688
+ Ink is --amb-lume, matching the indicators: the scale glows with the
689
+ same emissive tone as the pointer, so ring and dot read as one lit
690
+ instrument rather than paint plus light. */
687
691
  .amb-knob-marker-ring {
688
692
  position: absolute;
689
693
  top: 50%;
@@ -701,7 +705,7 @@
701
705
  height: var(--ambx-knob-marker-size);
702
706
  margin: calc(var(--ambx-knob-marker-size) / -2);
703
707
  border-radius: 50%;
704
- background: var(--amb-label);
708
+ background: var(--amb-lume);
705
709
  transform: rotate(var(--amb-marker-angle))
706
710
  translateY(calc(-1 * var(--ambx-knob-marker-radius)));
707
711
  }
@@ -1270,7 +1274,9 @@
1270
1274
  /* The indicator: a short mark printed across the bar near one end. It is
1271
1275
  ink, not geometry — no edges, no shadow — and it is inside the bar rather
1272
1276
  than beside it because the bar already occupies the whole diameter it
1273
- would otherwise sit on. */
1277
+ would otherwise sit on. The ink is --amb-lume, matching the grounded
1278
+ knob's pointer: the mark reads as backlit rather than printed, glowing
1279
+ as the scene's lights drop. */
1274
1280
  .amb-console-indicator {
1275
1281
  position: absolute;
1276
1282
  left: 50%;
@@ -1279,7 +1285,7 @@
1279
1285
  height: 7.5%;
1280
1286
  transform: translateX(-50%);
1281
1287
  border-radius: calc(var(--ambx-size) * 0.012);
1282
- background-color: color-mix(in oklab, var(--amb-lit), black 82%);
1288
+ background-color: var(--amb-lume);
1283
1289
  }
1284
1290
 
1285
1291
  /* Panel graphics: the accent centre mark above the knob and the -/+ legends
@@ -1298,7 +1304,12 @@
1298
1304
  height: calc(var(--ambx-size) * 0.2);
1299
1305
  margin-bottom: calc(var(--ambx-size) * 0.07);
1300
1306
  border-radius: var(--ambx-radius-sm);
1301
- background-color: var(--ambx-accent);
1307
+ /* amb-surface's lit albedo with the albedo pinned to the accent (restated
1308
+ because this sheet loads independently of ambient.css and rule order is
1309
+ not guaranteed) — the mark keeps its orange identity but reads as a
1310
+ little body of material under the scene's light, not printed ink. */
1311
+ --amb-albedo: var(--ambx-accent);
1312
+ background-color: var(--amb-lit);
1302
1313
  transform: translateX(-50%);
1303
1314
  }
1304
1315
 
@@ -1349,15 +1360,35 @@
1349
1360
 
1350
1361
  /* The track is a pure-CSS part: it reads --ambx-percent off the control root
1351
1362
  and mixes its own colour from it, so it lights and dims with the travel
1352
- without the mechanism knowing this element exists. */
1363
+ without the mechanism knowing this element exists.
1364
+
1365
+ BOTH ends of that mix are now lit reflectance rather than paint, so the
1366
+ whole base responds to the lamp:
1367
+ - OFF: a dark grey body — the scene's lit albedo held at 30% in linear
1368
+ light. It sits well below the panel as a recess should but still
1369
+ tracks the lamp, because the base colour is --amb-lit: dim the key
1370
+ and the floor dims with it. (It briefly sat at plain --amb-lit, which
1371
+ read flush with the panel.)
1372
+ - ON: the accent worn as reflectance, through the SAME law the thumb's
1373
+ face goes through. ambient.css publishes its diffuse exposure factor
1374
+ as --amb-exposure (= key*0.6396 + fill*0.5496, applied in linear
1375
+ light), so mixing in `srgb-linear` by that amount reproduces
1376
+ amb-surface's shading exactly — track ON and thumb sit at the same
1377
+ brightness, instead of the mean-of-intensities approximation that
1378
+ left the floor visibly darker than the body it carries. */
1353
1379
  .amb-console-track {
1354
1380
  position: absolute;
1355
1381
  inset: 0;
1356
1382
  border-radius: var(--ambx-radius-full);
1383
+ --_lit-accent: color-mix(
1384
+ in srgb-linear,
1385
+ var(--ambx-accent) calc(var(--amb-exposure) * 100%),
1386
+ black
1387
+ );
1357
1388
  background-color: color-mix(
1358
1389
  in oklab,
1359
- color-mix(in oklab, var(--amb-lit), black 55%),
1360
- var(--ambx-accent) calc(var(--ambx-percent) * 100%)
1390
+ color-mix(in srgb-linear, var(--amb-lit) 30%, black),
1391
+ var(--_lit-accent) calc(var(--ambx-percent) * 100%)
1361
1392
  );
1362
1393
  --amb-thickness: 0.4;
1363
1394
  transition: background-color 160ms ease;
@@ -1366,20 +1397,29 @@
1366
1397
  /* Flat-topped, but a body: `.ambient` with a thickness and NO edge
1367
1398
  treatment, which is the combination that casts a drop shadow while
1368
1399
  leaving the top face unbroken — the chamfer and fillet layers of the
1369
- composite are gated on their own properties, and neither is set here. */
1400
+ composite are gated on their own properties, and neither is set here.
1401
+ The face stays the accent ORANGE, but as reflectance not paint:
1402
+ --amb-albedo feeds amb-surface's exposure law (restated here because
1403
+ this sheet loads independently of ambient.css, so rule order is not
1404
+ guaranteed), so the thumb still re-shades when the lamp moves or the
1405
+ intensities change — an orange body under the scene's light, not a
1406
+ sticker of orange. */
1370
1407
  .amb-console-thumb {
1371
1408
  position: absolute;
1372
1409
  inset: 0;
1373
1410
  border-radius: 50%;
1374
- background-color: color-mix(in oklab, var(--ambx-accent), white 12%);
1375
- /* The white ring is a BORDER, not a spread shadow. Two reasons, and the
1411
+ --amb-albedo: var(--ambx-accent);
1412
+ background-color: var(--amb-lit);
1413
+ /* The ring is a BORDER, not a spread shadow. Two reasons, and the
1376
1414
  second is the load-bearing one: with the package's border-box reset the
1377
1415
  ring grows inward, so the thumb keeps the outer diameter measured off
1378
1416
  the reference — and `box-shadow` is the property `.ambient` composites
1379
1417
  the whole lighting model into, so setting it here would silently wipe
1380
- the drop shadow the thickness above is there to cast. */
1381
- border: calc(var(--ambx-latch-thumb-h) * 0.09) solid
1382
- hsl(var(--amb-light-hue) var(--amb-light-saturation) 99%);
1418
+ the drop shadow the thickness above is there to cast. The ring's ink
1419
+ is --amb-lume: lit albedo vanished against the thumb's orange face, so
1420
+ the ring reads as an emissive halo instead — present in bright light,
1421
+ glowing as the scene dims. */
1422
+ border: calc(var(--ambx-latch-thumb-h) * 0.09) solid var(--amb-lume);
1383
1423
  }
1384
1424
 
1385
1425
  @media (prefers-reduced-motion: reduce) {