@salemcobalt/ui 0.2.0-rc.33 → 0.2.0-rc.35
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/index.d.ts +1 -1
- package/dist/index.js +121 -46
- package/dist/index.js.map +1 -1
- package/dist/styles.css +81 -24
- package/package.json +3 -3
package/dist/styles.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* @salemcobalt/ui 0.2.0-rc.
|
|
1
|
+
/* @salemcobalt/ui 0.2.0-rc.35 — requires @salemcobalt/tokens/css. */
|
|
2
2
|
@layer sc {
|
|
3
3
|
/* ---- base.css ---- */
|
|
4
4
|
/* Shared: focus ring, reduced motion. Component files follow. */
|
|
@@ -853,11 +853,21 @@
|
|
|
853
853
|
|
|
854
854
|
/* ---- dialog.css ---- */
|
|
855
855
|
/* ---- Dialog ---------------------------------------------------------------- */
|
|
856
|
+
/* `--_overhang`: how far a docked surface (the scrim, a phone sheet, a full-screen dialog) runs past the bottom edge
|
|
857
|
+
of the layout viewport, out of sight. It covers what a collapsing or floating browser toolbar uncovers there. */
|
|
858
|
+
.sc-dialog__backdrop,
|
|
859
|
+
.sc-dialog__popup {
|
|
860
|
+
--_overhang: 160px;
|
|
861
|
+
}
|
|
856
862
|
.sc-dialog__backdrop {
|
|
857
863
|
position: fixed;
|
|
858
|
-
|
|
864
|
+
/* The scrim runs past the bottom edge: when a phone's browser toolbar collapses or floats, the strip it uncovers
|
|
865
|
+
is still dimmed instead of showing the page undimmed beneath it. */
|
|
866
|
+
inset: 0 0 calc(-1 * var(--_overhang));
|
|
859
867
|
z-index: var(--sc-z-backdrop);
|
|
860
868
|
background: var(--sc-color-surface-backdrop);
|
|
869
|
+
/* A drag on the scrim never pans the page behind it (iOS rubber-banding included); a tap still closes. */
|
|
870
|
+
touch-action: none;
|
|
861
871
|
transition: opacity var(--sc-duration-base) var(--sc-ease-enter);
|
|
862
872
|
}
|
|
863
873
|
/* A dialog opened from inside another (Base UI nests its portal after the parent's popup): its scrim covers the
|
|
@@ -874,10 +884,14 @@
|
|
|
874
884
|
transition-timing-function: var(--sc-ease-exit);
|
|
875
885
|
}
|
|
876
886
|
/* The popup never exceeds the viewport minus a gutter each side, whatever `size` says. 100dvw rather than
|
|
877
|
-
100% so the clamp holds inside a transformed or narrow portal container too.
|
|
887
|
+
100% so the clamp holds inside a transformed or narrow portal container too. Under `viewport-fit=cover` each
|
|
888
|
+
gutter is at least the safe-area inset on that axis (the larger of the pair, as the popup is centred), so a
|
|
889
|
+
centred dialog on a landscape phone stays clear of the notch, the rounded corners and the home indicator. */
|
|
878
890
|
.sc-dialog__popup {
|
|
879
891
|
--_gutter: var(--sc-spacing-md);
|
|
880
|
-
--
|
|
892
|
+
--_side: max(var(--_gutter), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
|
|
893
|
+
--_ends: max(var(--_gutter), env(safe-area-inset-top, 0px), env(safe-area-inset-bottom, 0px));
|
|
894
|
+
--_max: calc(100dvw - 2 * var(--_side));
|
|
881
895
|
position: fixed;
|
|
882
896
|
inset: 0;
|
|
883
897
|
margin: auto;
|
|
@@ -885,7 +899,7 @@
|
|
|
885
899
|
box-sizing: border-box;
|
|
886
900
|
inline-size: min(var(--_max), var(--sc-size-dialog-md));
|
|
887
901
|
max-inline-size: var(--_max);
|
|
888
|
-
max-block-size: min(100dvh - 2 * var(--
|
|
902
|
+
max-block-size: min(100dvh - 2 * var(--_ends), 85dvh);
|
|
889
903
|
block-size: fit-content;
|
|
890
904
|
overflow: hidden auto; /* vertical only: a 44px touch target on an edge button must not scroll it sideways */
|
|
891
905
|
padding: var(--sc-spacing-lg);
|
|
@@ -1001,22 +1015,45 @@
|
|
|
1001
1015
|
/* Below 640px the sheet docks to the bottom edge, full width, and slides up. The breakpoint is fixed
|
|
1002
1016
|
here because custom properties cannot drive a media query. */
|
|
1003
1017
|
@media (width < 640px) {
|
|
1018
|
+
/* The sheet's surface runs on past the bottom edge as a bottom border in its own colour (the overhang), so no
|
|
1019
|
+
strip of page shows under it when the browser toolbar collapses; the content and its safe-area padding end at
|
|
1020
|
+
the edge as before. `touch-action: none`: a drag on the sheet outside a scroller never pans the page behind
|
|
1021
|
+
it, while `Dialog.Body` and any other scroll container inside keep scrolling (touch-action resets at a
|
|
1022
|
+
scroll container). The drag (sheetSwipe.ts) moves it with `transform`, on top of the `translate` that
|
|
1023
|
+
opens and closes it.
|
|
1024
|
+
Motion: the sheet and its scrim share one duration and curve each way, so the scrim never goes before the
|
|
1025
|
+
sheet does. In: `slow` on `standard`, which leaves the bottom edge gently instead of jumping a quarter of its
|
|
1026
|
+
height in the first frame. Out: 75% of that on `exit`, accelerating, so it is in view until it leaves.
|
|
1027
|
+
After a swipe it decelerates instead (`data-swiped`), carrying on from the finger's speed. */
|
|
1004
1028
|
.sc-dialog__popup[data-variant='sheet'] {
|
|
1005
|
-
inset: auto 0
|
|
1029
|
+
inset: auto 0 calc(-1 * var(--_overhang)) 0;
|
|
1006
1030
|
margin: 0;
|
|
1007
1031
|
inline-size: 100%;
|
|
1008
1032
|
max-inline-size: none;
|
|
1009
|
-
max-block-size: calc(100dvh - env(safe-area-inset-top, 0px) - var(--sc-spacing-xl));
|
|
1033
|
+
max-block-size: calc(100dvh - env(safe-area-inset-top, 0px) - var(--sc-spacing-xl) + var(--_overhang));
|
|
1010
1034
|
padding-block-end: calc(var(--sc-spacing-lg) + env(safe-area-inset-bottom, 0px));
|
|
1011
1035
|
padding-inline: calc(var(--sc-spacing-lg) + env(safe-area-inset-left, 0px))
|
|
1012
1036
|
calc(var(--sc-spacing-lg) + env(safe-area-inset-right, 0px));
|
|
1013
|
-
border-block-end:
|
|
1037
|
+
border-block-end: var(--_overhang) solid var(--sc-color-surface-raised);
|
|
1014
1038
|
border-radius: var(--sc-radius-xl) var(--sc-radius-xl) 0 0;
|
|
1039
|
+
touch-action: none;
|
|
1015
1040
|
transition:
|
|
1016
|
-
|
|
1017
|
-
translate var(--sc-duration-base) var(--sc-ease-enter),
|
|
1041
|
+
translate var(--sc-duration-slow) var(--sc-ease-standard),
|
|
1018
1042
|
transform var(--sc-duration-slow) var(--sc-ease-standard);
|
|
1019
1043
|
}
|
|
1044
|
+
.sc-dialog__backdrop[data-variant='sheet'] {
|
|
1045
|
+
transition-duration: var(--sc-duration-slow);
|
|
1046
|
+
transition-timing-function: var(--sc-ease-standard);
|
|
1047
|
+
}
|
|
1048
|
+
.sc-dialog__popup[data-variant='sheet'][data-ending-style],
|
|
1049
|
+
.sc-dialog__backdrop[data-variant='sheet'][data-ending-style] {
|
|
1050
|
+
transition-duration: calc(var(--sc-duration-slow) * 0.75);
|
|
1051
|
+
transition-timing-function: var(--sc-ease-exit);
|
|
1052
|
+
}
|
|
1053
|
+
.sc-dialog__popup[data-variant='sheet'][data-ending-style][data-swiped] {
|
|
1054
|
+
transition-duration: var(--sc-duration-base);
|
|
1055
|
+
transition-timing-function: var(--sc-ease-enter);
|
|
1056
|
+
}
|
|
1020
1057
|
/* The handle: a 36 × 4 bar centred in a 64 × 24 target, where the bar alone used to sit. */
|
|
1021
1058
|
.sc-dialog__popup[data-variant='sheet'] > .sc-dialog__handle {
|
|
1022
1059
|
display: grid;
|
|
@@ -1043,12 +1080,6 @@
|
|
|
1043
1080
|
border-radius: var(--sc-radius-full);
|
|
1044
1081
|
background: currentColor;
|
|
1045
1082
|
}
|
|
1046
|
-
/* Swipe to dismiss (sheetSwipe.ts): the page never pans under the sheet, while `Dialog.Body` and any other
|
|
1047
|
-
scroll container inside keep scrolling, because touch-action resets at a scroll container. The drag
|
|
1048
|
-
moves the sheet with `transform`, which adds to the `translate` the exit transition animates. */
|
|
1049
|
-
.sc-dialog__popup[data-variant='sheet'][data-swipe-dismiss] {
|
|
1050
|
-
touch-action: none;
|
|
1051
|
-
}
|
|
1052
1083
|
.sc-dialog__popup[data-variant='sheet'][data-swipe-dismiss] > .sc-dialog__handle {
|
|
1053
1084
|
cursor: grab;
|
|
1054
1085
|
}
|
|
@@ -1057,10 +1088,11 @@
|
|
|
1057
1088
|
user-select: none;
|
|
1058
1089
|
transition: none;
|
|
1059
1090
|
}
|
|
1091
|
+
/* Just below the edge: its visible height, not counting the overhang already out of sight. */
|
|
1060
1092
|
.sc-dialog__popup[data-variant='sheet'][data-starting-style],
|
|
1061
1093
|
.sc-dialog__popup[data-variant='sheet'][data-ending-style] {
|
|
1062
1094
|
opacity: 1;
|
|
1063
|
-
translate: 0 100
|
|
1095
|
+
translate: 0 calc(100% - var(--_overhang));
|
|
1064
1096
|
scale: none;
|
|
1065
1097
|
}
|
|
1066
1098
|
/* Actions stack full width, confirming action on top, in thumb reach. */
|
|
@@ -1125,6 +1157,22 @@
|
|
|
1125
1157
|
overflow: hidden auto; /* vertical only: a 44px touch target on an edge button must not scroll it sideways */
|
|
1126
1158
|
overscroll-behavior: contain;
|
|
1127
1159
|
}
|
|
1160
|
+
/* A `Dialog.Close` on the full-screen surface sits at the top inline-end corner of the screen, so it clears the
|
|
1161
|
+
status bar and a landscape notch the way the padding does. */
|
|
1162
|
+
.sc-dialog__popup[data-variant='fullscreen'] .sc-dialog__close {
|
|
1163
|
+
inset-block-start: calc(var(--sc-spacing-sm) + env(safe-area-inset-top, 0px));
|
|
1164
|
+
inset-inline-end: calc(
|
|
1165
|
+
var(--sc-spacing-sm) + max(env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px))
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
/* Below 900px the full-screen surface runs past the bottom edge like the sheet (the overhang, a bottom border in
|
|
1169
|
+
its own colour), so a collapsing browser toolbar uncovers more of it, not the page. */
|
|
1170
|
+
@media (width < 900px) {
|
|
1171
|
+
.sc-dialog__popup[data-variant='fullscreen'] {
|
|
1172
|
+
inset-block-end: calc(-1 * var(--_overhang));
|
|
1173
|
+
border-block-end: var(--_overhang) solid var(--sc-color-surface-default);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1128
1176
|
/* From 900px the backdrop shows a margin around it, so it still reads as a layer over the app. */
|
|
1129
1177
|
@media (width >= 900px) {
|
|
1130
1178
|
.sc-dialog__popup[data-variant='fullscreen'] {
|
|
@@ -1161,7 +1209,11 @@
|
|
|
1161
1209
|
/* Reduced motion: a docked sheet appears and goes without sliding, and a released swipe closes or snaps
|
|
1162
1210
|
back in place without the spring. */
|
|
1163
1211
|
@media (width < 640px) and (prefers-reduced-motion: reduce) {
|
|
1164
|
-
.sc-dialog__popup[data-variant='sheet']
|
|
1212
|
+
.sc-dialog__popup[data-variant='sheet'],
|
|
1213
|
+
.sc-dialog__popup[data-variant='sheet'][data-ending-style],
|
|
1214
|
+
.sc-dialog__popup[data-variant='sheet'][data-ending-style][data-swiped],
|
|
1215
|
+
.sc-dialog__backdrop[data-variant='sheet'],
|
|
1216
|
+
.sc-dialog__backdrop[data-variant='sheet'][data-ending-style] {
|
|
1165
1217
|
transition-duration: 0ms;
|
|
1166
1218
|
}
|
|
1167
1219
|
.sc-dialog__popup[data-variant='sheet'][data-starting-style],
|
|
@@ -3028,18 +3080,20 @@
|
|
|
3028
3080
|
/* ---- selection-bar.css ---- */
|
|
3029
3081
|
/* ---- SelectionBar ---------------------------------------------------------- */
|
|
3030
3082
|
/* Floats centred at the bottom of the viewport, sized to its content. A raised surface like Menu and Popover, so
|
|
3031
|
-
washes inside use the raised tokens (docs/adr/0003).
|
|
3083
|
+
washes inside use the raised tokens (docs/adr/0003). Its sides keep the gutter or the larger side safe-area inset,
|
|
3084
|
+
whichever is wider (a landscape phone's notch and rounded corners under `viewport-fit=cover`). */
|
|
3032
3085
|
.sc-selection-bar {
|
|
3033
3086
|
--_gutter: var(--sc-spacing-md);
|
|
3087
|
+
--_side: max(var(--_gutter), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
|
|
3034
3088
|
--sc-color-surface-hover: var(--sc-color-surface-raised-hover);
|
|
3035
3089
|
--sc-color-surface-active: var(--sc-color-surface-raised-active);
|
|
3036
3090
|
position: fixed;
|
|
3037
|
-
inset-inline: var(--
|
|
3091
|
+
inset-inline: var(--_side);
|
|
3038
3092
|
inset-block-end: calc(var(--_gutter) + env(safe-area-inset-bottom, 0px));
|
|
3039
3093
|
z-index: var(--sc-z-sticky);
|
|
3040
3094
|
box-sizing: border-box;
|
|
3041
3095
|
inline-size: fit-content;
|
|
3042
|
-
max-inline-size: min(100vw - 2 * var(--
|
|
3096
|
+
max-inline-size: min(100vw - 2 * var(--_side), 48rem);
|
|
3043
3097
|
margin-inline: auto;
|
|
3044
3098
|
padding: var(--sc-spacing-2xs) var(--sc-spacing-xs) var(--sc-spacing-2xs) var(--sc-spacing-sm);
|
|
3045
3099
|
border: var(--sc-border-width-hairline) solid var(--sc-color-border-default);
|
|
@@ -3102,19 +3156,22 @@
|
|
|
3102
3156
|
/* Bottom-end, clear of a bar fixed along the bottom: an open SelectionBar, or any `position: fixed` bar of the app's
|
|
3103
3157
|
that sets `anchor-name: --sc-bottom-bar`. Without one, the gutter. Phones use the md gutter on both sides.
|
|
3104
3158
|
A sticky bar can't be an anchor (anchor() reads where it would be unstuck); while one shows, the app raises the
|
|
3105
|
-
toasts by setting `--sc-toast-offset-block-end` (on :root, for example) to the room it needs.
|
|
3159
|
+
toasts by setting `--sc-toast-offset-block-end` (on :root, for example) to the room it needs.
|
|
3160
|
+
Under `viewport-fit=cover` the side keeps clear of a landscape phone's notch or Dynamic Island and rounded corners:
|
|
3161
|
+
the gutter or the larger side inset, whichever is wider (the larger of the two, so inline-end is right either way). */
|
|
3106
3162
|
.sc-toast-viewport {
|
|
3107
3163
|
--_gutter: var(--sc-spacing-lg);
|
|
3164
|
+
--_side: max(var(--_gutter), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
|
|
3108
3165
|
--_bottom: calc(var(--_gutter) + var(--sc-toast-offset-block-end, 0px) + env(safe-area-inset-bottom, 0px));
|
|
3109
3166
|
position: fixed;
|
|
3110
3167
|
inset-block-end: var(--_bottom);
|
|
3111
|
-
inset-inline-end: var(--
|
|
3168
|
+
inset-inline-end: var(--_side);
|
|
3112
3169
|
z-index: var(--sc-z-toast);
|
|
3113
3170
|
display: flex;
|
|
3114
3171
|
flex-direction: column;
|
|
3115
3172
|
align-items: flex-end;
|
|
3116
3173
|
gap: var(--sc-spacing-xs);
|
|
3117
|
-
inline-size: min(360px, 100vw - 2 * var(--
|
|
3174
|
+
inline-size: min(360px, 100vw - 2 * var(--_side));
|
|
3118
3175
|
outline: none;
|
|
3119
3176
|
}
|
|
3120
3177
|
@supports (anchor-name: --a) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salemcobalt/ui",
|
|
3
|
-
"version": "0.2.0-rc.
|
|
3
|
+
"version": "0.2.0-rc.35",
|
|
4
4
|
"description": "React components for the Salem Cobalt design system: Button, IconButton, Icon set, BroomIcon, Badge, Card, Stack, Inline, Kbd, Dialog, FullScreenFlow, Tooltip, Menu, Popover, PriorityNav, Toolbar, Banner, Spinner, Progress, Tabs, Input, Textarea, Select, FileInput, Checkbox, Switch, Heading, Text, Table, Stat, Chip, Disclosure, EmptyState, Skeleton, Score, ReasonList, WordDiff, Quote; TextEditor (CodeMirror 6, inline word diffs) at @salemcobalt/ui/editor.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"diff": "^8.0.4",
|
|
47
47
|
"react": "^19.0.0",
|
|
48
48
|
"react-dom": "^19.0.0",
|
|
49
|
-
"@salemcobalt/tokens": "^0.2.0-rc.
|
|
49
|
+
"@salemcobalt/tokens": "^0.2.0-rc.35"
|
|
50
50
|
},
|
|
51
51
|
"peerDependenciesMeta": {
|
|
52
52
|
"@codemirror/commands": {
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"tsup": "^9.0.0",
|
|
110
110
|
"typescript": "^5.9.3",
|
|
111
111
|
"vitest": "^5.0.5",
|
|
112
|
-
"@salemcobalt/tokens": "^0.2.0-rc.
|
|
112
|
+
"@salemcobalt/tokens": "^0.2.0-rc.35"
|
|
113
113
|
},
|
|
114
114
|
"publishConfig": {
|
|
115
115
|
"access": "public"
|