@juxtui/ui 0.1.0 → 0.2.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/dist/style.css CHANGED
@@ -462,6 +462,211 @@
462
462
  }
463
463
  }
464
464
 
465
+ @layer juxt {
466
+ .j-accordion {
467
+ display: flex;
468
+ flex-direction: column;
469
+ font-family: var(--juxt-font-sans);
470
+ }
471
+ }
472
+
473
+ @layer juxt {
474
+ .j-accordion__item {
475
+ border-bottom: 1px solid var(--juxt-border-subtle);
476
+ }
477
+ .dark .j-accordion__item,
478
+ [data-theme='dark'] .j-accordion__item {
479
+ border-bottom-color: var(--juxt-border);
480
+ }
481
+ .j-accordion__heading {
482
+ margin: 0;
483
+ font: inherit;
484
+ }
485
+ .j-accordion__trigger {
486
+ display: flex;
487
+ align-items: center;
488
+ justify-content: space-between;
489
+ gap: var(--juxt-space-4);
490
+ width: 100%;
491
+ min-height: 3rem;
492
+ padding: var(--juxt-space-3) 0;
493
+ border: 0;
494
+ border-radius: var(--juxt-radius-xs);
495
+ background: transparent;
496
+ color: var(--juxt-fg);
497
+ font: inherit;
498
+ font-size: var(--juxt-text-md);
499
+ font-weight: var(--juxt-weight-medium);
500
+ letter-spacing: var(--juxt-tracking-tight);
501
+ text-align: left;
502
+ cursor: pointer;
503
+ outline: 2px solid transparent;
504
+ outline-offset: 0;
505
+ -webkit-tap-highlight-color: transparent;
506
+ transition:
507
+ color var(--juxt-duration-fast) var(--juxt-ease-standard),
508
+ outline-color var(--juxt-duration-fast) var(--juxt-ease-out),
509
+ outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);
510
+ }
511
+ .j-accordion__trigger:focus-visible {
512
+ outline-color: var(--juxt-ring);
513
+ outline-offset: 2px;
514
+ }
515
+ .j-accordion__trigger:disabled {
516
+ color: var(--juxt-fg-disabled);
517
+ cursor: not-allowed;
518
+ }
519
+ .j-accordion__chevron {
520
+ flex: none;
521
+ width: 1rem;
522
+ height: 1rem;
523
+ color: var(--juxt-fg-muted);
524
+ transition:
525
+ transform var(--juxt-duration-normal) var(--juxt-ease-out),
526
+ color var(--juxt-duration-fast) var(--juxt-ease-standard);
527
+ }
528
+ .j-accordion__trigger:hover:not(:disabled) .j-accordion__chevron {
529
+ color: var(--juxt-fg);
530
+ }
531
+ .j-accordion__item[data-state='open'] .j-accordion__chevron {
532
+ transform: rotate(180deg);
533
+ }
534
+
535
+ /* Height follows content via grid rows — no measuring, no magic numbers. */
536
+ .j-accordion__panel {
537
+ display: grid;
538
+ grid-template-rows: 0fr;
539
+ transition: grid-template-rows var(--juxt-duration-slow) var(--juxt-ease-out);
540
+ }
541
+ .j-accordion__item[data-state='open'] .j-accordion__panel {
542
+ grid-template-rows: 1fr;
543
+ }
544
+ .j-accordion__panel-inner {
545
+ min-height: 0;
546
+ overflow: hidden;
547
+ }
548
+ .j-accordion__content {
549
+ padding-bottom: var(--juxt-space-4);
550
+ color: var(--juxt-fg-secondary);
551
+ font-size: var(--juxt-text-md);
552
+ line-height: var(--juxt-leading-normal);
553
+ opacity: 0;
554
+ transition: opacity var(--juxt-duration-fast) var(--juxt-ease-standard);
555
+ }
556
+ .j-accordion__item[data-state='open'] .j-accordion__content {
557
+ opacity: 1;
558
+ transition-delay: 60ms;
559
+ }
560
+ @media (prefers-reduced-motion: reduce) {
561
+ .j-accordion__panel,
562
+ .j-accordion__chevron {
563
+ transition: none;
564
+ }
565
+ }
566
+ }
567
+
568
+ @layer juxt {
569
+ /*
570
+ * Alerts stay on the page's surface. Colour lives in the icon and a hairline
571
+ * of the border — enough to be noticed, never enough to shout.
572
+ */
573
+ .j-alert {
574
+ --j-alert-accent: var(--juxt-fg-muted);
575
+ --j-alert-border: var(--juxt-border);
576
+
577
+ position: relative;
578
+ display: flex;
579
+ align-items: flex-start;
580
+ gap: var(--juxt-space-3);
581
+ padding: var(--juxt-space-3) var(--juxt-space-4);
582
+ border: 1px solid var(--j-alert-border);
583
+ border-radius: var(--juxt-radius-md);
584
+ background: var(--juxt-surface);
585
+ color: var(--juxt-fg);
586
+ font-family: var(--juxt-font-sans);
587
+ }
588
+ .j-alert--info {
589
+ --j-alert-accent: var(--juxt-info-text);
590
+ }
591
+ .j-alert--success {
592
+ --j-alert-accent: var(--juxt-success-text);
593
+ }
594
+ .j-alert--warning {
595
+ --j-alert-accent: var(--juxt-warning-text);
596
+ --j-alert-border: var(--juxt-warning-border);
597
+ }
598
+ .j-alert--destructive {
599
+ --j-alert-accent: var(--juxt-danger-text);
600
+ --j-alert-border: var(--juxt-danger-border);
601
+ }
602
+ .j-alert__icon {
603
+ display: grid;
604
+ flex: none;
605
+ place-items: center;
606
+ height: 1.25rem;
607
+ color: var(--j-alert-accent);
608
+ }
609
+ .j-alert__icon > svg {
610
+ width: 1rem;
611
+ height: 1rem;
612
+ }
613
+ .j-alert__body {
614
+ display: flex;
615
+ flex: 1;
616
+ flex-direction: column;
617
+ gap: var(--juxt-space-0-5);
618
+ min-width: 0;
619
+ }
620
+ .j-alert__title {
621
+ margin: 0;
622
+ font-size: var(--juxt-text-md);
623
+ font-weight: var(--juxt-weight-medium);
624
+ line-height: 1.25rem;
625
+ letter-spacing: var(--juxt-tracking-tight);
626
+ }
627
+ .j-alert__description {
628
+ color: var(--juxt-fg-secondary);
629
+ font-size: var(--juxt-text-sm);
630
+ line-height: var(--juxt-leading-normal);
631
+ }
632
+ .j-alert__description > p {
633
+ margin: 0;
634
+ }
635
+ .j-alert__actions {
636
+ display: flex;
637
+ flex-wrap: wrap;
638
+ gap: var(--juxt-space-2);
639
+ margin-top: var(--juxt-space-2);
640
+ }
641
+ .j-alert:has(.j-alert__close) {
642
+ padding-right: var(--juxt-space-10);
643
+ }
644
+ .j-alert__close {
645
+ position: absolute;
646
+ top: var(--juxt-space-2-5);
647
+ right: var(--juxt-space-2-5);
648
+ display: grid;
649
+ place-items: center;
650
+ width: 1.5rem;
651
+ height: 1.5rem;
652
+ padding: 0;
653
+ border: 0;
654
+ border-radius: var(--juxt-radius-xs);
655
+ background: transparent;
656
+ color: var(--juxt-fg-muted);
657
+ cursor: pointer;
658
+ transition-property: background-color, color, outline-color, outline-offset;
659
+ }
660
+ .j-alert__close:hover {
661
+ background: var(--juxt-surface-hover);
662
+ color: var(--juxt-fg);
663
+ }
664
+ .j-alert__close > svg {
665
+ width: 0.875rem;
666
+ height: 0.875rem;
667
+ }
668
+ }
669
+
465
670
  @layer juxt {
466
671
  .j-avatar {
467
672
  --j-avatar-size: 2rem;
@@ -1750,6 +1955,280 @@ to {
1750
1955
  }
1751
1956
  }
1752
1957
 
1958
+ @layer juxt {
1959
+ .j-kbd-group {
1960
+ display: inline-flex;
1961
+ gap: 3px;
1962
+ vertical-align: middle;
1963
+ }
1964
+ .j-kbd-group--sm .j-kbd,
1965
+ .j-kbd--sm {
1966
+ min-width: 1.0625rem;
1967
+ height: 1.0625rem;
1968
+ font-size: 0.625rem;
1969
+ }
1970
+ }
1971
+
1972
+ @layer juxt {
1973
+ .j-popover {
1974
+ z-index: var(--juxt-z-popover);
1975
+ box-sizing: border-box;
1976
+ max-width: calc(100vw - 16px);
1977
+ max-height: var(--j-available-height, 32rem);
1978
+ padding: var(--juxt-space-4);
1979
+ overflow: auto;
1980
+ outline: none;
1981
+ font-size: var(--juxt-text-md);
1982
+ line-height: var(--juxt-leading-normal);
1983
+ }
1984
+ }
1985
+
1986
+ @layer juxt {
1987
+ .j-progress {
1988
+ --j-progress-height: 0.375rem;
1989
+ --j-progress-fill: var(--juxt-accent);
1990
+
1991
+ display: flex;
1992
+ flex-direction: column;
1993
+ gap: var(--juxt-space-2);
1994
+ width: 100%;
1995
+ font-family: var(--juxt-font-sans);
1996
+ }
1997
+ .j-progress--sm {
1998
+ --j-progress-height: 0.25rem;
1999
+ }
2000
+ .j-progress--neutral {
2001
+ --j-progress-fill: var(--juxt-fg);
2002
+ }
2003
+ .j-progress__header {
2004
+ display: flex;
2005
+ align-items: baseline;
2006
+ justify-content: space-between;
2007
+ gap: var(--juxt-space-3);
2008
+ font-size: var(--juxt-text-sm);
2009
+ }
2010
+ .j-progress__label {
2011
+ color: var(--juxt-fg);
2012
+ font-weight: var(--juxt-weight-medium);
2013
+ letter-spacing: var(--juxt-tracking-tight);
2014
+ }
2015
+ .j-progress__value {
2016
+ color: var(--juxt-fg-muted);
2017
+ font-variant-numeric: tabular-nums;
2018
+ }
2019
+ .j-progress__track {
2020
+ position: relative;
2021
+ height: var(--j-progress-height);
2022
+ overflow: hidden;
2023
+ border-radius: var(--juxt-radius-full);
2024
+ background: var(--juxt-surface-active);
2025
+ }
2026
+
2027
+ /* Width changes are transforms, so updates glide without reflow. */
2028
+ .j-progress__fill {
2029
+ position: absolute;
2030
+ inset: 0;
2031
+ border-radius: inherit;
2032
+ background: var(--j-progress-fill);
2033
+ transform: scaleX(0);
2034
+ transform-origin: left;
2035
+ transition: transform var(--juxt-duration-slow) var(--juxt-ease-out);
2036
+ }
2037
+ .j-progress.is-indeterminate .j-progress__fill {
2038
+ width: 40%;
2039
+ transform: none;
2040
+ animation: j-progress-indeterminate 1.4s var(--juxt-ease-standard) infinite;
2041
+ }
2042
+ @keyframes j-progress-indeterminate {
2043
+ from {
2044
+ transform: translateX(-100%);
2045
+ }
2046
+ to {
2047
+ transform: translateX(250%);
2048
+ }
2049
+ }
2050
+ @media (prefers-reduced-motion: reduce) {
2051
+ .j-progress.is-indeterminate .j-progress__fill {
2052
+ width: 100%;
2053
+ animation: j-progress-pulse 1.6s ease-in-out infinite;
2054
+ }
2055
+ @keyframes j-progress-pulse {
2056
+ 50% {
2057
+ opacity: 0.35;
2058
+ }
2059
+ }
2060
+ }
2061
+ }
2062
+
2063
+ @layer juxt {
2064
+ .j-radio {
2065
+ --j-radio-size: 1rem;
2066
+
2067
+ display: inline-flex;
2068
+ align-items: flex-start;
2069
+ gap: var(--juxt-space-2-5);
2070
+ font-family: var(--juxt-font-sans);
2071
+ }
2072
+ .j-radio__control {
2073
+ position: relative;
2074
+ display: inline-flex;
2075
+ flex: none;
2076
+ margin-top: calc((var(--juxt-text-md) * var(--juxt-leading-snug) - var(--j-radio-size)) / 2);
2077
+ }
2078
+ .j-radio__input {
2079
+ position: absolute;
2080
+ inset: -4px;
2081
+ z-index: 1;
2082
+ width: calc(100% + 8px);
2083
+ height: calc(100% + 8px);
2084
+ margin: 0;
2085
+ opacity: 0;
2086
+ cursor: pointer;
2087
+ }
2088
+ .j-radio__input:disabled {
2089
+ cursor: not-allowed;
2090
+ }
2091
+ .j-radio__circle {
2092
+ position: relative;
2093
+ display: block;
2094
+ width: var(--j-radio-size);
2095
+ height: var(--j-radio-size);
2096
+ border: 1px solid var(--juxt-border-strong);
2097
+ border-radius: var(--juxt-radius-full);
2098
+ background: var(--juxt-surface);
2099
+ box-shadow: var(--juxt-shadow-xs);
2100
+ outline: 2px solid transparent;
2101
+ outline-offset: 0;
2102
+ transition:
2103
+ background-color var(--juxt-duration-fast) var(--juxt-ease-standard),
2104
+ border-color var(--juxt-duration-fast) var(--juxt-ease-standard),
2105
+ transform var(--juxt-duration-fast) var(--juxt-ease-out),
2106
+ outline-color var(--juxt-duration-fast) var(--juxt-ease-out),
2107
+ outline-offset var(--juxt-duration-fast) var(--juxt-ease-out);
2108
+ }
2109
+
2110
+ /* The inner dot grows out of the centre once the fill lands. */
2111
+ .j-radio__circle::after {
2112
+ content: '';
2113
+ position: absolute;
2114
+ inset: 0;
2115
+ margin: auto;
2116
+ width: 0.375rem;
2117
+ height: 0.375rem;
2118
+ border-radius: var(--juxt-radius-full);
2119
+ background: var(--juxt-accent-fg);
2120
+ opacity: 0;
2121
+ transform: scale(0.3);
2122
+ transition:
2123
+ opacity var(--juxt-duration-instant) var(--juxt-ease-standard),
2124
+ transform var(--juxt-duration-normal) var(--juxt-ease-out);
2125
+ }
2126
+ .j-radio__input:hover:not(:disabled) + .j-radio__circle {
2127
+ border-color: var(--juxt-fg-muted);
2128
+ }
2129
+ .j-radio__input:active:not(:disabled) + .j-radio__circle {
2130
+ transform: scale(0.9);
2131
+ }
2132
+ .j-radio__input:focus-visible + .j-radio__circle {
2133
+ outline-color: var(--juxt-ring);
2134
+ outline-offset: 2px;
2135
+ }
2136
+ .j-radio[data-state='checked'] .j-radio__circle {
2137
+ border-color: var(--juxt-accent);
2138
+ background: var(--juxt-accent);
2139
+ }
2140
+ .j-radio[data-state='checked'] .j-radio__input:hover:not(:disabled) + .j-radio__circle {
2141
+ border-color: var(--juxt-accent-hover);
2142
+ background: var(--juxt-accent-hover);
2143
+ }
2144
+ .j-radio[data-state='checked'] .j-radio__circle::after {
2145
+ opacity: 1;
2146
+ transform: none;
2147
+ transition-delay: 30ms;
2148
+ }
2149
+ .j-radio-group.is-invalid .j-radio:not([data-state='checked']) .j-radio__circle {
2150
+ border-color: var(--juxt-danger-border);
2151
+ }
2152
+ @media (prefers-reduced-motion: reduce) {
2153
+ .j-radio__input:active:not(:disabled) + .j-radio__circle {
2154
+ transform: none;
2155
+ }
2156
+ }
2157
+ .j-radio__text {
2158
+ display: flex;
2159
+ flex-direction: column;
2160
+ gap: var(--juxt-space-0-5);
2161
+ min-width: 0;
2162
+ }
2163
+ .j-radio__label {
2164
+ color: var(--juxt-fg);
2165
+ font-size: var(--juxt-text-md);
2166
+ line-height: var(--juxt-leading-snug);
2167
+ letter-spacing: var(--juxt-tracking-tight);
2168
+ cursor: pointer;
2169
+ user-select: none;
2170
+ }
2171
+ .j-radio__description {
2172
+ color: var(--juxt-fg-muted);
2173
+ font-size: var(--juxt-text-sm);
2174
+ line-height: var(--juxt-leading-normal);
2175
+ }
2176
+ .j-radio.is-disabled .j-radio__circle {
2177
+ opacity: 0.5;
2178
+ box-shadow: none;
2179
+ }
2180
+ .j-radio.is-disabled .j-radio__label {
2181
+ color: var(--juxt-fg-muted);
2182
+ cursor: not-allowed;
2183
+ }
2184
+ }
2185
+
2186
+ @layer juxt {
2187
+ .j-radio-group {
2188
+ display: flex;
2189
+ flex-direction: column;
2190
+ gap: var(--juxt-space-1-5);
2191
+ min-width: 0;
2192
+ margin: 0;
2193
+ padding: 0;
2194
+ border: 0;
2195
+ font-family: var(--juxt-font-sans);
2196
+ }
2197
+ .j-radio-group__legend {
2198
+ display: inline-flex;
2199
+ gap: var(--juxt-space-0-5);
2200
+ margin-bottom: var(--juxt-space-1-5);
2201
+ padding: 0;
2202
+ color: var(--juxt-fg);
2203
+ font-size: var(--juxt-text-sm);
2204
+ font-weight: var(--juxt-weight-medium);
2205
+ line-height: var(--juxt-leading-snug);
2206
+ letter-spacing: var(--juxt-tracking-tight);
2207
+ }
2208
+ .j-radio-group__description,
2209
+ .j-radio-group__error {
2210
+ margin: calc(var(--juxt-space-1) * -1) 0 var(--juxt-space-1-5);
2211
+ color: var(--juxt-fg-muted);
2212
+ font-size: var(--juxt-text-xs);
2213
+ line-height: var(--juxt-leading-normal);
2214
+ }
2215
+ .j-radio-group__error {
2216
+ margin: var(--juxt-space-1) 0 0;
2217
+ color: var(--juxt-danger-text);
2218
+ }
2219
+ .j-radio-group__items {
2220
+ display: flex;
2221
+ }
2222
+ .j-radio-group__items--vertical {
2223
+ flex-direction: column;
2224
+ gap: var(--juxt-space-3);
2225
+ }
2226
+ .j-radio-group__items--horizontal {
2227
+ flex-wrap: wrap;
2228
+ gap: var(--juxt-space-3) var(--juxt-space-6);
2229
+ }
2230
+ }
2231
+
1753
2232
  @layer juxt {
1754
2233
  .j-select__trigger {
1755
2234
  width: 100%;
@@ -1861,6 +2340,284 @@ to {
1861
2340
  }
1862
2341
  }
1863
2342
 
2343
+ @layer juxt {
2344
+ .j-separator {
2345
+ flex: none;
2346
+ background: var(--juxt-border-subtle);
2347
+ }
2348
+ .dark .j-separator,
2349
+ [data-theme='dark'] .j-separator {
2350
+ background: var(--juxt-border);
2351
+ }
2352
+ .j-separator--horizontal {
2353
+ width: 100%;
2354
+ height: 1px;
2355
+ }
2356
+ .j-separator--vertical {
2357
+ align-self: stretch;
2358
+ width: 1px;
2359
+ min-height: 1rem;
2360
+ }
2361
+ .j-separator--labelled {
2362
+ display: flex;
2363
+ align-items: center;
2364
+ gap: var(--juxt-space-3);
2365
+ height: auto;
2366
+ background: none !important;
2367
+ color: var(--juxt-fg-muted);
2368
+ font-family: var(--juxt-font-sans);
2369
+ font-size: var(--juxt-text-xs);
2370
+ }
2371
+ .j-separator--labelled::before,
2372
+ .j-separator--labelled::after {
2373
+ content: '';
2374
+ flex: 1;
2375
+ height: 1px;
2376
+ background: var(--juxt-border-subtle);
2377
+ }
2378
+ .dark .j-separator--labelled::before,
2379
+ .dark .j-separator--labelled::after,
2380
+ [data-theme='dark'] .j-separator--labelled::before,
2381
+ [data-theme='dark'] .j-separator--labelled::after {
2382
+ background: var(--juxt-border);
2383
+ }
2384
+ .j-separator__label {
2385
+ flex: none;
2386
+ }
2387
+ }
2388
+
2389
+ @layer juxt {
2390
+ .j-sheet__overlay {
2391
+ position: fixed;
2392
+ inset: 0;
2393
+ z-index: var(--juxt-z-overlay);
2394
+ background: var(--juxt-overlay);
2395
+ }
2396
+ .j-sheet__positioner {
2397
+ position: fixed;
2398
+ inset: 0;
2399
+ z-index: var(--juxt-z-modal);
2400
+ display: flex;
2401
+ padding: var(--juxt-space-2);
2402
+ }
2403
+ .j-sheet__positioner--right {
2404
+ justify-content: flex-end;
2405
+ }
2406
+ .j-sheet__positioner--left {
2407
+ justify-content: flex-start;
2408
+ }
2409
+ .j-sheet__positioner--bottom {
2410
+ align-items: flex-end;
2411
+ padding-bottom: max(var(--juxt-space-2), env(safe-area-inset-bottom));
2412
+ }
2413
+
2414
+ /* A floating panel with a small inset from the edges — calmer than edge-to-edge. */
2415
+ .j-sheet {
2416
+ position: relative;
2417
+ display: flex;
2418
+ flex-direction: column;
2419
+ width: 100%;
2420
+ border: 1px solid var(--juxt-border);
2421
+ border-radius: var(--juxt-radius-lg);
2422
+ background: var(--juxt-surface-raised);
2423
+ box-shadow: var(--juxt-shadow-lg);
2424
+ color: var(--juxt-fg);
2425
+ font-family: var(--juxt-font-sans);
2426
+ outline: none;
2427
+ }
2428
+ .j-sheet--right,
2429
+ .j-sheet--left {
2430
+ height: 100%;
2431
+ }
2432
+ .j-sheet--right.j-sheet--sm,
2433
+ .j-sheet--left.j-sheet--sm {
2434
+ max-width: 20rem;
2435
+ }
2436
+ .j-sheet--right.j-sheet--md,
2437
+ .j-sheet--left.j-sheet--md {
2438
+ max-width: 25rem;
2439
+ }
2440
+ .j-sheet--right.j-sheet--lg,
2441
+ .j-sheet--left.j-sheet--lg {
2442
+ max-width: 35rem;
2443
+ }
2444
+ .j-sheet--bottom.j-sheet--sm {
2445
+ max-height: 20rem;
2446
+ }
2447
+ .j-sheet--bottom.j-sheet--md {
2448
+ max-height: 25rem;
2449
+ }
2450
+ .j-sheet--bottom.j-sheet--lg {
2451
+ max-height: min(35rem, calc(100dvh - var(--juxt-space-12)));
2452
+ }
2453
+ .j-sheet__header {
2454
+ display: flex;
2455
+ flex-direction: column;
2456
+ gap: var(--juxt-space-1-5);
2457
+ padding: var(--juxt-space-5) var(--juxt-space-12) var(--juxt-space-4) var(--juxt-space-5);
2458
+ border-bottom: 1px solid var(--juxt-border-subtle);
2459
+ }
2460
+ .dark .j-sheet__header,
2461
+ [data-theme='dark'] .j-sheet__header {
2462
+ border-bottom-color: var(--juxt-border);
2463
+ }
2464
+ .j-sheet__title {
2465
+ margin: 0;
2466
+ font-size: var(--juxt-text-lg);
2467
+ font-weight: var(--juxt-weight-semibold);
2468
+ line-height: var(--juxt-leading-snug);
2469
+ letter-spacing: var(--juxt-tracking-tight);
2470
+ }
2471
+ .j-sheet__description {
2472
+ margin: 0;
2473
+ color: var(--juxt-fg-secondary);
2474
+ font-size: var(--juxt-text-md);
2475
+ line-height: var(--juxt-leading-normal);
2476
+ }
2477
+ .j-sheet__body {
2478
+ flex: 1;
2479
+ min-height: 0;
2480
+ padding: var(--juxt-space-5);
2481
+ overflow-y: auto;
2482
+ overscroll-behavior: contain;
2483
+ color: var(--juxt-fg-secondary);
2484
+ font-size: var(--juxt-text-md);
2485
+ line-height: var(--juxt-leading-normal);
2486
+ }
2487
+ .j-sheet__footer {
2488
+ display: flex;
2489
+ justify-content: flex-end;
2490
+ gap: var(--juxt-space-2);
2491
+ padding: var(--juxt-space-4) var(--juxt-space-5);
2492
+ border-top: 1px solid var(--juxt-border-subtle);
2493
+ }
2494
+ .dark .j-sheet__footer,
2495
+ [data-theme='dark'] .j-sheet__footer {
2496
+ border-top-color: var(--juxt-border);
2497
+ }
2498
+ .j-sheet__close {
2499
+ position: absolute;
2500
+ top: var(--juxt-space-3);
2501
+ right: var(--juxt-space-3);
2502
+ display: grid;
2503
+ place-items: center;
2504
+ width: var(--juxt-control-sm);
2505
+ height: var(--juxt-control-sm);
2506
+ padding: 0;
2507
+ border: 0;
2508
+ border-radius: var(--juxt-radius-sm);
2509
+ background: transparent;
2510
+ color: var(--juxt-fg-muted);
2511
+ cursor: pointer;
2512
+ transition-property: background-color, color, outline-color, outline-offset;
2513
+ }
2514
+ .j-sheet__close:hover {
2515
+ background: var(--juxt-surface-hover);
2516
+ color: var(--juxt-fg);
2517
+ }
2518
+ .j-sheet__close > svg {
2519
+ width: 1rem;
2520
+ height: 1rem;
2521
+ }
2522
+ .j-sheet.is-nudging {
2523
+ animation: j-sheet-nudge 220ms var(--juxt-ease-out);
2524
+ }
2525
+ @keyframes j-sheet-nudge {
2526
+ 40% {
2527
+ transform: scale(1.006);
2528
+ }
2529
+ }
2530
+
2531
+ /* Motion: the backdrop fades; the sheet slides in from its edge. */
2532
+ .j-sheet-overlay-enter-active {
2533
+ transition: opacity var(--juxt-duration-normal) var(--juxt-ease-out);
2534
+ }
2535
+ .j-sheet-overlay-leave-active {
2536
+ transition: opacity var(--juxt-duration-fast) var(--juxt-ease-in);
2537
+ }
2538
+ .j-sheet-overlay-enter-from,
2539
+ .j-sheet-overlay-leave-to {
2540
+ opacity: 0;
2541
+ }
2542
+ .j-sheet-enter-active .j-sheet {
2543
+ transition: transform 280ms var(--juxt-ease-out);
2544
+ }
2545
+ .j-sheet-leave-active .j-sheet {
2546
+ transition: transform var(--juxt-duration-normal) var(--juxt-ease-in);
2547
+ }
2548
+ .j-sheet-enter-from .j-sheet--right,
2549
+ .j-sheet-leave-to .j-sheet--right {
2550
+ transform: translateX(calc(100% + var(--juxt-space-4)));
2551
+ }
2552
+ .j-sheet-enter-from .j-sheet--left,
2553
+ .j-sheet-leave-to .j-sheet--left {
2554
+ transform: translateX(calc(-100% - var(--juxt-space-4)));
2555
+ }
2556
+ .j-sheet-enter-from .j-sheet--bottom,
2557
+ .j-sheet-leave-to .j-sheet--bottom {
2558
+ transform: translateY(calc(100% + var(--juxt-space-4)));
2559
+ }
2560
+
2561
+ /* Reduced motion: the sheet fades in place instead of travelling. */
2562
+ @media (prefers-reduced-motion: reduce) {
2563
+ .j-sheet-enter-active .j-sheet,
2564
+ .j-sheet-leave-active .j-sheet {
2565
+ transition: opacity var(--juxt-duration-normal) var(--juxt-ease-standard);
2566
+ }
2567
+ .j-sheet-enter-from .j-sheet,
2568
+ .j-sheet-leave-to .j-sheet {
2569
+ opacity: 0;
2570
+ transform: none !important;
2571
+ }
2572
+ }
2573
+ }
2574
+
2575
+ @layer juxt {
2576
+ .j-skeleton {
2577
+ display: block;
2578
+ width: 100%;
2579
+ height: 1rem;
2580
+ border-radius: var(--juxt-radius-sm);
2581
+ background: var(--juxt-surface-active);
2582
+ animation: j-skeleton-pulse 1.8s var(--juxt-ease-standard) infinite;
2583
+ }
2584
+ .j-skeleton--circle {
2585
+ width: 2rem;
2586
+ height: 2rem;
2587
+ border-radius: var(--juxt-radius-full);
2588
+ }
2589
+ .j-skeleton--text {
2590
+ height: 0.75rem;
2591
+ border-radius: var(--juxt-radius-xs);
2592
+ }
2593
+ .j-skeleton-lines {
2594
+ display: flex;
2595
+ flex-direction: column;
2596
+ gap: var(--juxt-space-2);
2597
+ width: 100%;
2598
+ }
2599
+
2600
+ /* A slow breath, not a shimmer — loading shouldn't compete for attention. */
2601
+ @keyframes j-skeleton-pulse {
2602
+ 50% {
2603
+ opacity: 0.5;
2604
+ }
2605
+ }
2606
+ @media (prefers-reduced-motion: reduce) {
2607
+ .j-skeleton {
2608
+ animation: none;
2609
+ }
2610
+ }
2611
+ }
2612
+
2613
+ @layer juxt {
2614
+ .j-spinner-wrap {
2615
+ display: inline-flex;
2616
+ color: var(--juxt-fg-muted);
2617
+ vertical-align: middle;
2618
+ }
2619
+ }
2620
+
1864
2621
  @layer juxt {
1865
2622
  .j-switch {
1866
2623
  --j-switch-w: 2rem;