@ambientcss/components 2.0.0 → 2.1.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/src/styles.css CHANGED
@@ -2,6 +2,22 @@
2
2
  --ambx-grid: 4px;
3
3
  --ambx-grid-half: calc(var(--ambx-grid) / 2);
4
4
  --ambx-grid-quarter: calc(var(--ambx-grid) / 4);
5
+ /* Border-radius scale: deliberately identical in px to
6
+ @ambientcss/css's frozen .amb-rounded/-md/-lg/-xl/-full utilities
7
+ (4/8/12/16/9999) — cross-layer coherence, not coincidence. Radius
8
+ stays fixed across size variants; only footprint scales with size. */
9
+ --ambx-radius-sm: calc(var(--ambx-grid) * 1);
10
+ --ambx-radius-md: calc(var(--ambx-grid) * 2);
11
+ --ambx-radius-lg: calc(var(--ambx-grid) * 3);
12
+ --ambx-radius-xl: calc(var(--ambx-grid) * 4);
13
+ --ambx-radius-full: 9999px;
14
+ /* Spacing/clearance scale for composing controls into a device face
15
+ (hardware-panel convention: a single modular pitch, tighter within a
16
+ functional cluster, looser between clusters/at the panel edge). Each
17
+ tier ~1.6-1.7x the last so they stay visually distinct at a glance. */
18
+ --ambx-gap-tight: calc(var(--ambx-grid) * 3);
19
+ --ambx-gap-normal: calc(var(--ambx-grid) * 5);
20
+ --ambx-gap-loose: calc(var(--ambx-grid) * 8);
5
21
  --ambx-type-1-size: calc(var(--ambx-grid) * 8);
6
22
  --ambx-type-1-line: calc(var(--ambx-grid) * 10);
7
23
  --ambx-type-2-size: calc(var(--ambx-grid) * 6);
@@ -17,6 +33,31 @@
17
33
  --ambx-fader-label-clearance: calc(var(--ambx-grid) * 4);
18
34
  }
19
35
 
36
+ /* Every control sizes its moving part as a percentage of a padded box —
37
+ a button's cap is 100% of a well inset by the clearance ring, a knob's
38
+ face 100% of the knob. Under the default content-box those paddings add
39
+ to the percentage instead of fitting inside it, and the cap renders
40
+ larger than the well it sits in. Scoped to the package's own elements
41
+ rather than shipped as a global reset: apps that already reset globally
42
+ (apps/demo does) see no change, and apps that do not are no longer
43
+ silently broken. */
44
+ .amb-button,
45
+ .amb-knob,
46
+ .amb-switch,
47
+ .amb-fader,
48
+ .amb-slider,
49
+ .ambx-panel,
50
+ .ambx-stack,
51
+ .amb-button *,
52
+ .amb-knob *,
53
+ .amb-switch *,
54
+ .amb-fader *,
55
+ .amb-slider *,
56
+ .ambx-panel *,
57
+ .ambx-stack * {
58
+ box-sizing: border-box;
59
+ }
60
+
20
61
  .amb-heading-1 {
21
62
  font-size: var(--ambx-type-1-size);
22
63
  line-height: var(--ambx-type-1-line);
@@ -48,16 +89,18 @@
48
89
  padding: var(--ambx-grid-half); /* the clearance gap ring */
49
90
  --amb-thickness: 0.27; /* seat depth of the well */
50
91
  background-color: var(--amb-lume);
51
- border-radius: calc(var(--ambx-grid) * 3 + var(--ambx-grid-half));
52
- min-width: calc(var(--ambx-grid) * 16);
92
+ /* Well radius = cap radius + its own clearance padding (concentric
93
+ shapes nest by adding the gap, not by picking a second number). */
94
+ border-radius: calc(var(--ambx-radius-lg) + var(--ambx-grid-half));
95
+ min-width: var(--ambx-button-size);
53
96
  }
54
97
 
55
98
  .amb-button-cap {
56
99
  display: grid;
57
100
  place-items: center;
58
101
  width: 100%;
59
- padding: calc(var(--ambx-grid) * 1.5) calc(var(--ambx-grid) * 2);
60
- border-radius: calc(var(--ambx-grid) * 3);
102
+ padding: var(--ambx-button-cap-pad-y) var(--ambx-button-cap-pad-x);
103
+ border-radius: var(--ambx-radius-lg);
61
104
  text-transform: uppercase;
62
105
  font-weight: 600;
63
106
  color: var(--amb-label);
@@ -82,27 +125,30 @@
82
125
  3.6mm cap instead of the key's 4.5mm — thickness 0.8 — with the same
83
126
  0.7mm press travel). */
84
127
  .amb-button.amb-button-round {
85
- min-width: calc(var(--ambx-grid) * 12);
128
+ min-width: var(--ambx-button-size);
86
129
  aspect-ratio: 1;
87
130
  border-radius: 50%;
88
131
  }
89
132
 
90
133
  .amb-button-round .amb-button-cap {
91
134
  height: 100%;
92
- padding: var(--ambx-grid);
135
+ padding: var(--ambx-button-cap-inset);
93
136
  border-radius: 50%;
94
137
  }
95
138
 
96
139
  .amb-button.amb-button-square {
97
- min-width: calc(var(--ambx-grid) * 14);
140
+ min-width: var(--ambx-button-size);
98
141
  aspect-ratio: 1;
99
- border-radius: calc(var(--ambx-grid) * 2);
142
+ border-radius: var(--ambx-radius-md);
100
143
  }
101
144
 
102
145
  .amb-button-square .amb-button-cap {
103
146
  height: 100%;
104
- padding: var(--ambx-grid);
105
- border-radius: calc(var(--ambx-grid) * 1.5);
147
+ padding: var(--ambx-button-cap-inset);
148
+ /* Cap radius = well radius (var(--ambx-radius-md)) minus its own inset
149
+ clearance — the same concentric-nesting rule as the pill, solved the
150
+ other way because here the well, not the cap, sits on the scale. */
151
+ border-radius: calc(var(--ambx-radius-md) - var(--ambx-grid-half));
106
152
  --amb-thickness: 0.8;
107
153
  }
108
154
 
@@ -119,8 +165,8 @@
119
165
  fingers like the real thing. */
120
166
  .amb-knob {
121
167
  border-radius: 50%;
122
- height: calc(var(--ambx-grid) * 16);
123
- width: calc(var(--ambx-grid) * 16);
168
+ height: var(--ambx-knob-size);
169
+ width: var(--ambx-knob-size);
124
170
  }
125
171
 
126
172
  /* The body is the smooth circle at the tooth-root radius: it carries
@@ -257,8 +303,8 @@
257
303
  top: 16%;
258
304
  left: 50%;
259
305
  transform: translateX(-50%);
260
- width: calc(var(--ambx-grid) * 2);
261
- height: calc(var(--ambx-grid) * 2);
306
+ width: calc(var(--ambx-knob-size) * 0.125);
307
+ height: calc(var(--ambx-knob-size) * 0.125);
262
308
  border-radius: 50%;
263
309
  background: var(--amb-highlight-color);
264
310
  }
@@ -268,8 +314,8 @@
268
314
  .amb-knob-indicator-dot-center {
269
315
  top: 50%;
270
316
  transform: translate(-50%, -50%);
271
- width: calc(var(--ambx-grid) * 4.5);
272
- height: calc(var(--ambx-grid) * 4.5);
317
+ width: calc(var(--ambx-knob-size) * 0.28125);
318
+ height: calc(var(--ambx-knob-size) * 0.28125);
273
319
  }
274
320
 
275
321
  /* Radial indicator line (line variant): the referent's accent bar,
@@ -279,9 +325,9 @@
279
325
  top: 12%;
280
326
  left: 50%;
281
327
  transform: translateX(-50%);
282
- width: calc(var(--ambx-grid) * 0.75);
328
+ width: var(--ambx-knob-line-width);
283
329
  height: 34%;
284
- border-radius: calc(var(--ambx-grid) * 0.375);
330
+ border-radius: calc(var(--ambx-knob-line-width) / 2);
285
331
  background: var(--amb-highlight-color);
286
332
  }
287
333
 
@@ -295,9 +341,9 @@
295
341
  light, glowing in low light — overriding the groove's neutral floor. */
296
342
  .amb-fader {
297
343
  position: relative;
298
- width: var(--ambx-grid);
344
+ width: var(--ambx-fader-track);
299
345
  height: calc(var(--ambx-grid) * 30);
300
- border-radius: var(--ambx-grid);
346
+ border-radius: var(--ambx-radius-full);
301
347
  background-color: var(--amb-lume);
302
348
  }
303
349
 
@@ -306,12 +352,12 @@
306
352
  with a single grip line across the top. */
307
353
  .amb-fader-thumb {
308
354
  position: absolute;
309
- width: calc(var(--ambx-grid) * 6);
310
- height: calc(var(--ambx-grid) * 9);
355
+ width: var(--ambx-fader-thumb-w);
356
+ height: var(--ambx-fader-thumb-h);
311
357
  left: 50%;
312
358
  top: 50%;
313
359
  transform: translate(-50%, -50%);
314
- border-radius: calc(var(--ambx-grid) * 2);
360
+ border-radius: var(--ambx-radius-md);
315
361
  display: grid;
316
362
  place-items: center;
317
363
  --amb-thickness: 1.5;
@@ -330,17 +376,17 @@
330
376
  gliding over it. */
331
377
  .amb-slider {
332
378
  position: relative;
333
- height: calc(var(--ambx-grid) * 1.5);
379
+ height: var(--ambx-slider-track);
334
380
  width: calc(var(--ambx-grid) * 30);
335
- border-radius: var(--ambx-grid);
381
+ border-radius: var(--ambx-radius-full);
336
382
  background-color: var(--amb-lume);
337
383
  --amb-thickness: 0.22;
338
384
  }
339
385
 
340
386
  .amb-slider-thumb {
341
387
  position: absolute;
342
- width: calc(var(--ambx-grid) * 6);
343
- height: calc(var(--ambx-grid) * 6);
388
+ width: var(--ambx-slider-thumb);
389
+ height: var(--ambx-slider-thumb);
344
390
  top: 50%;
345
391
  left: 50%;
346
392
  transform: translate(-50%, -50%);
@@ -426,13 +472,91 @@
426
472
  }
427
473
 
428
474
  .ambx-panel {
429
- border-radius: calc(var(--ambx-grid) * 4);
475
+ border-radius: var(--ambx-radius-xl);
430
476
  padding: calc(var(--ambx-grid) * 4);
431
477
  }
432
478
 
479
+ /* Opt-in "device face" mode: edge margin = the loose gap tier, so a
480
+ panel composing a .ambx-rack of controls reads its own border as one
481
+ more slot in the same lattice (Rams/Braun, Teenage Engineering
482
+ convention). Not the default padding above, which serves general
483
+ containers (cards, settings panels) that aren't device faces. */
484
+ .ambx-panel-device {
485
+ padding: var(--ambx-gap-loose);
486
+ }
487
+
488
+ /* Layout primitive for composing controls into a device face: pick a
489
+ gap tier by how tightly the controls belong together (tight = one
490
+ functional unit/repeated row, normal = a few related controls, loose
491
+ = separating distinct zones or the panel edge, see .ambx-panel-device
492
+ above) rather than picking an arbitrary gap per layout. */
493
+ .ambx-rack {
494
+ display: flex;
495
+ align-items: center;
496
+ gap: var(--ambx-gap-normal);
497
+ }
498
+
499
+ .ambx-rack-tight {
500
+ gap: var(--ambx-gap-tight);
501
+ }
502
+
503
+ .ambx-rack-loose {
504
+ gap: var(--ambx-gap-loose);
505
+ }
506
+
507
+ .ambx-rack-column {
508
+ flex-direction: column;
509
+ }
510
+
433
511
  .ambx-button {
434
- min-width: calc(var(--ambx-grid) * 18);
435
512
  line-height: var(--ambx-type-3-line);
513
+ --ambx-button-size: calc(var(--ambx-grid) * 18); /* 72px pill */
514
+ --ambx-button-cap-pad-x: calc(var(--ambx-grid) * 2); /* 8px */
515
+ --ambx-button-cap-pad-y: calc(var(--ambx-grid) * 1.5); /* 6px */
516
+ --ambx-button-cap-inset: var(--ambx-grid); /* 4px */
517
+ }
518
+
519
+ .ambx-button-sm {
520
+ --ambx-button-size: calc(var(--ambx-grid) * 16); /* 64px pill */
521
+ --ambx-button-cap-pad-x: calc(var(--ambx-grid) * 1.5); /* 6px */
522
+ --ambx-button-cap-pad-y: var(--ambx-grid); /* 4px */
523
+ --ambx-button-cap-inset: calc(var(--ambx-grid) * 0.75); /* 3px */
524
+ }
525
+
526
+ .ambx-button-md {
527
+ --ambx-button-size: calc(var(--ambx-grid) * 18); /* 72px pill */
528
+ --ambx-button-cap-pad-x: calc(var(--ambx-grid) * 2); /* 8px */
529
+ --ambx-button-cap-pad-y: calc(var(--ambx-grid) * 1.5); /* 6px */
530
+ --ambx-button-cap-inset: var(--ambx-grid); /* 4px */
531
+ }
532
+
533
+ .ambx-button-lg {
534
+ --ambx-button-size: calc(var(--ambx-grid) * 20); /* 80px pill */
535
+ --ambx-button-cap-pad-x: calc(var(--ambx-grid) * 2.5); /* 10px */
536
+ --ambx-button-cap-pad-y: calc(var(--ambx-grid) * 2); /* 8px */
537
+ --ambx-button-cap-inset: calc(var(--ambx-grid) * 1.25); /* 5px */
538
+ }
539
+
540
+ /* Round/square shapes have their own min-width table per size (not a
541
+ ratio of the pill's — the three shapes' true md values, 72/48/56,
542
+ share no common factor), overridden via compound selectors so they
543
+ win regardless of source order relative to .ambx-button-sm/-md/-lg. */
544
+ .amb-button-round.ambx-button-sm { --ambx-button-size: calc(var(--ambx-grid) * 10); } /* 40px */
545
+ .amb-button-round.ambx-button-md { --ambx-button-size: calc(var(--ambx-grid) * 12); } /* 48px */
546
+ .amb-button-round.ambx-button-lg { --ambx-button-size: calc(var(--ambx-grid) * 14); } /* 56px */
547
+
548
+ .amb-button-square.ambx-button-sm { --ambx-button-size: calc(var(--ambx-grid) * 12); } /* 48px */
549
+ .amb-button-square.ambx-button-md { --ambx-button-size: calc(var(--ambx-grid) * 14); } /* 56px */
550
+ .amb-button-square.ambx-button-lg { --ambx-button-size: calc(var(--ambx-grid) * 16); } /* 64px */
551
+
552
+ .ambx-button-sm .amb-button-cap {
553
+ font-size: var(--ambx-label-size);
554
+ line-height: var(--ambx-label-line);
555
+ }
556
+
557
+ .ambx-button-lg .amb-button-cap {
558
+ font-size: calc(var(--ambx-grid) * 3.5);
559
+ line-height: calc(var(--ambx-grid) * 5);
436
560
  }
437
561
 
438
562
  .ambx-switch {
@@ -488,18 +612,43 @@
488
612
  place-items: center;
489
613
  touch-action: none;
490
614
  user-select: none;
615
+ --ambx-knob-size: calc(var(--ambx-grid) * 16); /* 64px */
616
+ --ambx-knob-line-width: calc(var(--ambx-knob-size) * 0.046875);
491
617
  }
492
618
 
619
+ .ambx-knob-sm { --ambx-knob-size: calc(var(--ambx-grid) * 12); } /* 48px */
620
+ .ambx-knob-md { --ambx-knob-size: calc(var(--ambx-grid) * 16); } /* 64px */
621
+ .ambx-knob-lg { --ambx-knob-size: calc(var(--ambx-grid) * 20); } /* 80px */
622
+
493
623
  .ambx-knob-rotation {
494
624
  position: absolute;
495
625
  inset: 0;
496
626
  }
497
627
 
498
628
  .ambx-fader {
499
- width: calc(var(--ambx-grid) * 2);
500
- border-radius: 999px;
501
629
  touch-action: none;
502
630
  user-select: none;
631
+ --ambx-fader-track: calc(var(--ambx-grid) * 2); /* 8px */
632
+ --ambx-fader-thumb-w: calc(var(--ambx-grid) * 6); /* 24px */
633
+ --ambx-fader-thumb-h: calc(var(--ambx-grid) * 9); /* 36px */
634
+ }
635
+
636
+ .ambx-fader-sm {
637
+ --ambx-fader-track: calc(var(--ambx-grid) * 1.5); /* 6px */
638
+ --ambx-fader-thumb-w: calc(var(--ambx-grid) * 5); /* 20px */
639
+ --ambx-fader-thumb-h: calc(var(--ambx-grid) * 7.5); /* 30px */
640
+ }
641
+
642
+ .ambx-fader-md {
643
+ --ambx-fader-track: calc(var(--ambx-grid) * 2); /* 8px */
644
+ --ambx-fader-thumb-w: calc(var(--ambx-grid) * 6); /* 24px */
645
+ --ambx-fader-thumb-h: calc(var(--ambx-grid) * 9); /* 36px */
646
+ }
647
+
648
+ .ambx-fader-lg {
649
+ --ambx-fader-track: calc(var(--ambx-grid) * 3); /* 12px */
650
+ --ambx-fader-thumb-w: calc(var(--ambx-grid) * 8); /* 32px */
651
+ --ambx-fader-thumb-h: calc(var(--ambx-grid) * 12); /* 48px */
503
652
  }
504
653
 
505
654
  .ambx-fader-thumb {
@@ -507,10 +656,25 @@
507
656
  }
508
657
 
509
658
  .ambx-slider {
510
- height: calc(var(--ambx-grid) * 2);
511
- border-radius: 999px;
512
659
  touch-action: none;
513
660
  user-select: none;
661
+ --ambx-slider-track: calc(var(--ambx-grid) * 2); /* 8px */
662
+ --ambx-slider-thumb: calc(var(--ambx-grid) * 6); /* 24px */
663
+ }
664
+
665
+ .ambx-slider-sm {
666
+ --ambx-slider-track: calc(var(--ambx-grid) * 1.5); /* 6px */
667
+ --ambx-slider-thumb: calc(var(--ambx-grid) * 5); /* 20px */
668
+ }
669
+
670
+ .ambx-slider-md {
671
+ --ambx-slider-track: calc(var(--ambx-grid) * 2); /* 8px */
672
+ --ambx-slider-thumb: calc(var(--ambx-grid) * 6); /* 24px */
673
+ }
674
+
675
+ .ambx-slider-lg {
676
+ --ambx-slider-track: calc(var(--ambx-grid) * 3); /* 12px */
677
+ --ambx-slider-thumb: calc(var(--ambx-grid) * 8); /* 32px */
514
678
  }
515
679
 
516
680
  .ambx-slider-thumb {