neba 1.2.0 → 1.3.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.
Files changed (91) hide show
  1. package/README.md +7 -5
  2. package/dist/components/alert/Alert.d.ts +7 -1
  3. package/dist/components/alert/Alert.js +1 -1
  4. package/dist/components/animate-appear/AnimateAppear.d.ts +55 -0
  5. package/dist/components/animate-appear/AnimateAppear.js +1 -0
  6. package/dist/components/animate-appear/index.d.ts +2 -0
  7. package/dist/components/animate-appear/index.js +1 -0
  8. package/dist/components/animate-blink/AnimateBlink.d.ts +31 -0
  9. package/dist/components/animate-blink/AnimateBlink.js +1 -0
  10. package/dist/components/animate-blink/index.d.ts +2 -0
  11. package/dist/components/animate-blink/index.js +1 -0
  12. package/dist/components/animate-fade/AnimateFade.d.ts +31 -0
  13. package/dist/components/animate-fade/AnimateFade.js +1 -0
  14. package/dist/components/animate-fade/index.d.ts +2 -0
  15. package/dist/components/animate-fade/index.js +1 -0
  16. package/dist/components/animate-grow/AnimateGrow.d.ts +41 -0
  17. package/dist/components/animate-grow/AnimateGrow.js +1 -0
  18. package/dist/components/animate-grow/index.d.ts +2 -0
  19. package/dist/components/animate-grow/index.js +1 -0
  20. package/dist/components/animate-headline/AnimateHeadline.d.ts +55 -0
  21. package/dist/components/animate-headline/AnimateHeadline.js +1 -0
  22. package/dist/components/animate-headline/index.d.ts +2 -0
  23. package/dist/components/animate-headline/index.js +1 -0
  24. package/dist/components/animate-lighting/AnimateLighting.d.ts +62 -0
  25. package/dist/components/animate-lighting/AnimateLighting.js +1 -0
  26. package/dist/components/animate-lighting/index.d.ts +2 -0
  27. package/dist/components/animate-lighting/index.js +1 -0
  28. package/dist/components/animate-marquee/AnimateMarquee.d.ts +65 -0
  29. package/dist/components/animate-marquee/AnimateMarquee.js +1 -0
  30. package/dist/components/animate-marquee/index.d.ts +2 -0
  31. package/dist/components/animate-marquee/index.js +1 -0
  32. package/dist/components/animate-rotate/AnimateRotate.d.ts +48 -0
  33. package/dist/components/animate-rotate/AnimateRotate.js +1 -0
  34. package/dist/components/animate-rotate/index.d.ts +2 -0
  35. package/dist/components/animate-rotate/index.js +1 -0
  36. package/dist/components/animate-slide/AnimateSlide.d.ts +46 -0
  37. package/dist/components/animate-slide/AnimateSlide.js +1 -0
  38. package/dist/components/animate-slide/index.d.ts +2 -0
  39. package/dist/components/animate-slide/index.js +1 -0
  40. package/dist/components/animate-typing/AnimateTyping.d.ts +58 -0
  41. package/dist/components/animate-typing/AnimateTyping.js +1 -0
  42. package/dist/components/animate-typing/index.d.ts +2 -0
  43. package/dist/components/animate-typing/index.js +1 -0
  44. package/dist/components/animate-zoom/AnimateZoom.d.ts +37 -0
  45. package/dist/components/animate-zoom/AnimateZoom.js +1 -0
  46. package/dist/components/animate-zoom/index.d.ts +2 -0
  47. package/dist/components/animate-zoom/index.js +1 -0
  48. package/dist/components/aspect-ratio/AspectRatio.d.ts +63 -0
  49. package/dist/components/aspect-ratio/AspectRatio.js +1 -0
  50. package/dist/components/aspect-ratio/index.d.ts +2 -0
  51. package/dist/components/aspect-ratio/index.js +1 -0
  52. package/dist/components/avatar/Avatar.d.ts +7 -1
  53. package/dist/components/avatar/Avatar.js +1 -1
  54. package/dist/components/blockquote/Blockquote.d.ts +7 -1
  55. package/dist/components/blockquote/Blockquote.js +1 -1
  56. package/dist/components/box/Box.d.ts +7 -1
  57. package/dist/components/box/Box.js +1 -1
  58. package/dist/components/chip/Chip.d.ts +7 -1
  59. package/dist/components/chip/Chip.js +1 -1
  60. package/dist/components/color-picker/ColorPicker.d.ts +102 -0
  61. package/dist/components/color-picker/ColorPicker.js +1 -0
  62. package/dist/components/color-picker/index.d.ts +2 -0
  63. package/dist/components/color-picker/index.js +1 -0
  64. package/dist/components/drawer/Drawer.d.ts +145 -0
  65. package/dist/components/drawer/Drawer.js +1 -0
  66. package/dist/components/drawer/index.d.ts +2 -0
  67. package/dist/components/drawer/index.js +1 -0
  68. package/dist/components/icon/Icon.d.ts +8 -1
  69. package/dist/components/icon/Icon.js +1 -1
  70. package/dist/components/popover/Popover.d.ts +109 -0
  71. package/dist/components/popover/Popover.js +1 -0
  72. package/dist/components/popover/index.d.ts +2 -0
  73. package/dist/components/popover/index.js +1 -0
  74. package/dist/components/skeleton/Skeleton.d.ts +76 -0
  75. package/dist/components/skeleton/Skeleton.js +1 -0
  76. package/dist/components/skeleton/index.d.ts +2 -0
  77. package/dist/components/skeleton/index.js +1 -0
  78. package/dist/components/typography/Typography.d.ts +7 -1
  79. package/dist/components/typography/Typography.js +1 -1
  80. package/dist/index.d.ts +16 -0
  81. package/dist/index.js +1 -1
  82. package/dist/internal/animate.d.ts +175 -0
  83. package/dist/internal/animate.js +1 -0
  84. package/dist/internal/color.d.ts +108 -0
  85. package/dist/internal/color.js +1 -0
  86. package/dist/internal/i18n.d.ts +25 -0
  87. package/dist/internal/i18n.js +1 -1
  88. package/dist/styles.css +1 -1
  89. package/dist/tailwind.css +379 -0
  90. package/dist/types.d.ts +112 -0
  91. package/package.json +8 -8
package/dist/tailwind.css CHANGED
@@ -962,6 +962,48 @@
962
962
  animation-delay: calc(var(--n-i, 0) * 160ms);
963
963
  }
964
964
 
965
+ /*
966
+ * The highlight travelling across a placeholder.
967
+ *
968
+ * `inset-inline-start` rather than a transform, the same trade `neba-sweep`
969
+ * makes and for the same reason: it runs the other way under RTL without being
970
+ * told, and the layout pass it costs is confined to a box with nothing in it.
971
+ *
972
+ * A pseudo-element rather than a child, so a Skeleton is one element per shape
973
+ * and a stack of ten lines is ten nodes rather than twenty. The gradient is
974
+ * symmetric, so it needs no direction of its own.
975
+ */
976
+ @keyframes neba-skeleton-sweep {
977
+ from {
978
+ inset-inline-start: -60%;
979
+ }
980
+ to {
981
+ inset-inline-start: 100%;
982
+ }
983
+ }
984
+
985
+ .neba-skeleton::after {
986
+ content: '';
987
+ position: absolute;
988
+ inset-block: 0;
989
+ inline-size: 60%;
990
+ background-image: linear-gradient(90deg, transparent, var(--n-panel-press), transparent);
991
+ animation: neba-skeleton-sweep 1.5s var(--neba-ease) infinite;
992
+ }
993
+
994
+ /* The reduced-motion stand-in: the highlight stops travelling and the whole
995
+ placeholder breathes instead. Colour, which is the axis every other state in
996
+ the library already uses. */
997
+ @keyframes neba-skeleton-pulse {
998
+ 0%,
999
+ 100% {
1000
+ background-color: transparent;
1001
+ }
1002
+ 50% {
1003
+ background-color: var(--n-panel-press);
1004
+ }
1005
+ }
1006
+
965
1007
  /* The reduced-motion stand-in for the sweep: the segment stops travelling and
966
1008
  the whole groove pulses instead. Colour, which is the axis every other state
967
1009
  in the library already uses. */
@@ -975,6 +1017,308 @@
975
1017
  }
976
1018
  }
977
1019
 
1020
+ /* ---------------------------------------------------------------------------
1021
+ * Animation
1022
+ *
1023
+ * The `Animate*` components, and the `transition` prop that shares their
1024
+ * effects. Everything below is real CSS for the reason the loading indicators
1025
+ * above are: a keyframe is not a Tailwind variant, so there is nothing to
1026
+ * express one with.
1027
+ *
1028
+ * The whole set is built on one idea. An effect is a single `@keyframes` that
1029
+ * runs from a state written entirely in `--n-anim-*` custom properties to the
1030
+ * element's natural one — so `src/internal/animate.ts` never generates CSS, it
1031
+ * only fills slots, and running an effect backwards is `animation-direction:
1032
+ * reverse` rather than a second keyframe. That is what makes `mode="out"` cost
1033
+ * nothing on all six.
1034
+ *
1035
+ * `fill-mode` is `both` throughout, which is what lets an animation that has
1036
+ * not been triggered yet simply sit paused on its own first frame. Without it,
1037
+ * an element waiting to be scrolled into view would be fully drawn and then
1038
+ * blink out the moment it arrived.
1039
+ *
1040
+ * This is also where the library's rule against transforms is at its narrowest.
1041
+ * A *control* never moves — that rule is about a button resampling its label
1042
+ * under the pointer, and it still holds without exception. An `Animate*` wraps
1043
+ * content the caller asked to have moved, and it moves it with the independent
1044
+ * `translate`, `scale` and `rotate` properties rather than the `transform`
1045
+ * shorthand, so a caller's own transform on the same element survives.
1046
+ * ------------------------------------------------------------------------- */
1047
+
1048
+ .neba-anim {
1049
+ animation-duration: var(--n-anim-duration, 320ms);
1050
+ animation-delay: var(--n-anim-delay, 0ms);
1051
+ animation-timing-function: var(--n-anim-ease, var(--neba-ease));
1052
+ animation-iteration-count: var(--n-anim-repeat, 1);
1053
+ animation-direction: var(--n-anim-direction, normal);
1054
+ animation-fill-mode: both;
1055
+ animation-play-state: var(--n-anim-state, running);
1056
+ }
1057
+
1058
+ @keyframes neba-anim-fade {
1059
+ from {
1060
+ opacity: var(--n-anim-opacity, 0);
1061
+ }
1062
+ to {
1063
+ opacity: 1;
1064
+ }
1065
+ }
1066
+
1067
+ .neba-anim-fade {
1068
+ animation-name: neba-anim-fade;
1069
+ }
1070
+
1071
+ /* Grow and Zoom share this one: they are the same arithmetic at two strengths,
1072
+ separated by their defaults and by their origin rather than by their maths. */
1073
+ @keyframes neba-anim-scale {
1074
+ from {
1075
+ opacity: var(--n-anim-opacity, 0);
1076
+ scale: var(--n-anim-scale, 0.8);
1077
+ }
1078
+ to {
1079
+ opacity: 1;
1080
+ scale: 1;
1081
+ }
1082
+ }
1083
+
1084
+ .neba-anim-scale {
1085
+ animation-name: neba-anim-scale;
1086
+ }
1087
+
1088
+ @keyframes neba-anim-slide {
1089
+ from {
1090
+ opacity: var(--n-anim-opacity, 0);
1091
+ translate: var(--n-anim-x, 0px) var(--n-anim-y, 0px);
1092
+ }
1093
+ to {
1094
+ opacity: 1;
1095
+ translate: 0 0;
1096
+ }
1097
+ }
1098
+
1099
+ .neba-anim-slide {
1100
+ animation-name: neba-anim-slide;
1101
+ }
1102
+
1103
+ /* Two angles rather than one, so the same keyframe covers both the quarter turn
1104
+ something arrives on and the endless one a badge spins through. */
1105
+ @keyframes neba-anim-rotate {
1106
+ from {
1107
+ opacity: var(--n-anim-opacity, 0);
1108
+ rotate: var(--n-anim-angle, -180deg);
1109
+ }
1110
+ to {
1111
+ opacity: 1;
1112
+ rotate: var(--n-anim-angle-to, 0deg);
1113
+ }
1114
+ }
1115
+
1116
+ .neba-anim-rotate {
1117
+ animation-name: neba-anim-rotate;
1118
+ }
1119
+
1120
+ /* Symmetric on purpose: a blink that ended anywhere but where it started would
1121
+ leave the element half drawn whenever the count ran out. */
1122
+ @keyframes neba-anim-blink {
1123
+ 0%,
1124
+ 100% {
1125
+ opacity: 1;
1126
+ }
1127
+ 50% {
1128
+ opacity: var(--n-anim-opacity, 0);
1129
+ }
1130
+ }
1131
+
1132
+ .neba-anim-blink {
1133
+ animation-name: neba-anim-blink;
1134
+ }
1135
+
1136
+ /*
1137
+ * Lighting: a light travelling around the outside of something.
1138
+ *
1139
+ * The arc is a conic gradient and what moves is its `from` angle, which is only
1140
+ * animatable because the angle is a registered custom property with a type. The
1141
+ * alternative — rotating the pseudo-element itself — swings its corners out
1142
+ * past the element on every quarter turn.
1143
+ *
1144
+ * `z-index: -1` inside an isolated stacking context puts the glow behind the
1145
+ * content but still inside the component, so it reads as light escaping from
1146
+ * under a sheet rather than as a border drawn around one.
1147
+ */
1148
+ @property --neba-glow-angle {
1149
+ syntax: '<angle>';
1150
+ initial-value: 0deg;
1151
+ inherits: false;
1152
+ }
1153
+
1154
+ @keyframes neba-anim-lighting {
1155
+ to {
1156
+ --neba-glow-angle: 360deg;
1157
+ }
1158
+ }
1159
+
1160
+ .neba-anim-lighting {
1161
+ position: relative;
1162
+ isolation: isolate;
1163
+ }
1164
+
1165
+ .neba-anim-lighting::before {
1166
+ content: '';
1167
+ position: absolute;
1168
+ z-index: -1;
1169
+ inset: calc(-1 * var(--n-anim-glow-width, 3px));
1170
+ border-radius: inherit;
1171
+ background: conic-gradient(
1172
+ from var(--neba-glow-angle),
1173
+ transparent 0deg,
1174
+ var(--n-anim-glow, var(--n-accent)) var(--n-anim-glow-arc, 50deg),
1175
+ transparent calc(var(--n-anim-glow-arc, 50deg) * 2),
1176
+ transparent 360deg
1177
+ );
1178
+ filter: blur(var(--n-anim-glow-blur, 4px));
1179
+ animation-name: neba-anim-lighting;
1180
+ animation-duration: var(--n-anim-duration, 3s);
1181
+ animation-delay: var(--n-anim-delay, 0ms);
1182
+ animation-timing-function: linear;
1183
+ animation-iteration-count: var(--n-anim-repeat, infinite);
1184
+ animation-direction: var(--n-anim-direction, normal);
1185
+ animation-fill-mode: both;
1186
+ animation-play-state: var(--n-anim-state, running);
1187
+ }
1188
+
1189
+ /*
1190
+ * Marquee: two identical copies, both moving by exactly one copy plus the gap.
1191
+ *
1192
+ * Which is the whole trick — when the first copy has travelled its own width,
1193
+ * the second is standing precisely where the first began, so there is no seam
1194
+ * and no frame where the strip is empty. A percentage `translate` resolves
1195
+ * against the element's own box, so neither copy has to be measured for this
1196
+ * to hold; measuring only ever decides how *fast* it goes.
1197
+ */
1198
+ @keyframes neba-anim-marquee-x {
1199
+ from {
1200
+ translate: 0 0;
1201
+ }
1202
+ to {
1203
+ translate: calc(-100% - var(--n-anim-gap, 0px)) 0;
1204
+ }
1205
+ }
1206
+
1207
+ @keyframes neba-anim-marquee-y {
1208
+ from {
1209
+ translate: 0 0;
1210
+ }
1211
+ to {
1212
+ translate: 0 calc(-100% - var(--n-anim-gap, 0px));
1213
+ }
1214
+ }
1215
+
1216
+ .neba-marquee {
1217
+ display: flex;
1218
+ overflow: hidden;
1219
+ gap: var(--n-anim-gap, 0px);
1220
+ }
1221
+
1222
+ .neba-marquee-vertical {
1223
+ flex-direction: column;
1224
+ }
1225
+
1226
+ .neba-marquee-track {
1227
+ display: flex;
1228
+ flex: 0 0 auto;
1229
+ align-items: center;
1230
+ gap: var(--n-anim-gap, 0px);
1231
+ animation-name: neba-anim-marquee-x;
1232
+ animation-duration: var(--n-anim-duration, 12s);
1233
+ animation-delay: var(--n-anim-delay, 0ms);
1234
+ /* Linear unless a caller insists otherwise. An eased marquee slows at both
1235
+ ends of a loop that has no ends, which reads as the page stuttering. */
1236
+ animation-timing-function: var(--n-anim-ease, linear);
1237
+ animation-iteration-count: var(--n-anim-repeat, infinite);
1238
+ animation-direction: var(--n-anim-direction, normal);
1239
+ animation-fill-mode: both;
1240
+ animation-play-state: var(--n-anim-state, running);
1241
+ }
1242
+
1243
+ .neba-marquee-vertical .neba-marquee-track {
1244
+ flex-direction: column;
1245
+ animation-name: neba-anim-marquee-y;
1246
+ }
1247
+
1248
+ .neba-marquee[data-pause-on-hover]:hover .neba-marquee-track {
1249
+ animation-play-state: paused;
1250
+ }
1251
+
1252
+ /*
1253
+ * Headline: one line replacing another from below.
1254
+ *
1255
+ * Every item is in the same grid cell, so the box is as tall as the tallest of
1256
+ * them and never resizes as the reel turns. The ones not on screen keep their
1257
+ * space with `visibility` rather than `display`, which is what makes that true.
1258
+ */
1259
+ @keyframes neba-anim-headline-in {
1260
+ from {
1261
+ translate: 0 var(--n-anim-rise, 100%);
1262
+ opacity: 0;
1263
+ }
1264
+ to {
1265
+ translate: 0 0;
1266
+ opacity: 1;
1267
+ }
1268
+ }
1269
+
1270
+ @keyframes neba-anim-headline-out {
1271
+ from {
1272
+ translate: 0 0;
1273
+ opacity: 1;
1274
+ }
1275
+ to {
1276
+ translate: 0 calc(-1 * var(--n-anim-rise, 100%));
1277
+ opacity: 0;
1278
+ }
1279
+ }
1280
+
1281
+ .neba-headline {
1282
+ display: grid;
1283
+ overflow: hidden;
1284
+ }
1285
+
1286
+ .neba-headline-item {
1287
+ grid-area: 1 / 1;
1288
+ visibility: hidden;
1289
+ animation-duration: var(--n-anim-duration, 480ms);
1290
+ animation-timing-function: var(--n-anim-ease, var(--neba-ease));
1291
+ animation-fill-mode: both;
1292
+ }
1293
+
1294
+ .neba-headline-item[data-state='active'] {
1295
+ visibility: visible;
1296
+ animation-name: neba-anim-headline-in;
1297
+ }
1298
+
1299
+ .neba-headline-item[data-state='leaving'] {
1300
+ visibility: visible;
1301
+ animation-name: neba-anim-headline-out;
1302
+ }
1303
+
1304
+ /* The block after a typewriter. `step-end` rather than a fade, because a caret
1305
+ that eases is a caret that looks like it is being rendered slowly. */
1306
+ @keyframes neba-anim-caret {
1307
+ 0%,
1308
+ 45% {
1309
+ opacity: 1;
1310
+ }
1311
+ 55%,
1312
+ 100% {
1313
+ opacity: 0;
1314
+ }
1315
+ }
1316
+
1317
+ .neba-typing-caret {
1318
+ display: inline-block;
1319
+ animation: neba-anim-caret 1s step-end infinite;
1320
+ }
1321
+
978
1322
  /* Respect a reduced-motion preference. Both layers track the pointer, so they
979
1323
  are motion in the sense that matters here — drop them entirely. */
980
1324
  @media (prefers-reduced-motion: reduce) {
@@ -1014,4 +1358,39 @@
1014
1358
  .neba-typing-dot {
1015
1359
  animation-duration: 2.6s;
1016
1360
  }
1361
+
1362
+ /* The highlight stops travelling and the placeholder pulses in place. Kept
1363
+ running for the same reason: a skeleton that holds still is indistinguishable
1364
+ from an empty box that finished loading with nothing in it. */
1365
+ .neba-skeleton::after {
1366
+ inset-inline-start: 0;
1367
+ inline-size: 100%;
1368
+ background-image: none;
1369
+ animation: neba-skeleton-pulse 2.4s var(--neba-ease) infinite;
1370
+ }
1371
+
1372
+ /*
1373
+ * The Animate components go the other way from the indicators above, and the
1374
+ * difference is what each of them is saying. A spinner that stops is lying
1375
+ * about whether anything is happening; an entrance that does not play has
1376
+ * still delivered its content, which is the only thing it was ever carrying.
1377
+ *
1378
+ * `animation: none` rather than a zero duration, because the shorthand also
1379
+ * clears `fill-mode` — and it is the fill that was holding an untriggered
1380
+ * element on its own first frame at `opacity: 0`. Left in place, respecting a
1381
+ * reader's preference would blank the page.
1382
+ */
1383
+ .neba-anim,
1384
+ .neba-marquee-track,
1385
+ .neba-headline-item,
1386
+ .neba-typing-caret {
1387
+ animation: none;
1388
+ }
1389
+
1390
+ /* The arc stops travelling and becomes an even glow. The same trade the
1391
+ skeleton makes: the decoration survives, the motion does not. */
1392
+ .neba-anim-lighting::before {
1393
+ background: var(--n-anim-glow, var(--n-accent));
1394
+ animation: none;
1395
+ }
1017
1396
  }
package/dist/types.d.ts CHANGED
@@ -129,6 +129,118 @@ export type NebaVariant = 'solid' | 'outline' | 'text';
129
129
  * pressing removes one, so a flat control still answers a press without moving.
130
130
  */
131
131
  export type NebaElevation = 0 | 1 | 2 | 3;
132
+ /**
133
+ * The six effects the `Animate*` components are built out of, and the six
134
+ * values the `transition` prop takes.
135
+ *
136
+ * They are named after what a reader sees rather than after the CSS property
137
+ * underneath: `zoom` and `grow` are both a change of scale, and they are two
138
+ * words because they are two *gestures* — one arrives from the middle of where
139
+ * it will end up, the other unfolds from nothing.
140
+ *
141
+ * Everything past these six is a component rather than a value. Typing, a
142
+ * marquee and a headline reel need to know what their children *are*, and a
143
+ * prop that only sets class names cannot.
144
+ */
145
+ export type NebaAnimation = 'fade' | 'grow' | 'slide' | 'zoom' | 'rotate' | 'blink';
146
+ /**
147
+ * What makes an animation run.
148
+ *
149
+ * - `mount` — as soon as it is on the page. The default, and the only one that
150
+ * needs nothing from the caller.
151
+ * - `visible` — when it is scrolled into view. Once, unless `once` is off.
152
+ * - `hover` — while the pointer is on it, restarting from the beginning on each
153
+ * entry. Keyboard focus counts as a pointer here, or the effect would be
154
+ * unreachable without a mouse.
155
+ * - `manual` — never on its own. `play` is what runs it.
156
+ */
157
+ export type NebaAnimateTrigger = 'mount' | 'visible' | 'hover' | 'manual';
158
+ /** Whether an effect brings its child in or takes it away. */
159
+ export type NebaAnimateMode = 'in' | 'out';
160
+ /**
161
+ * How many times an animation runs. `'infinite'` rather than `Infinity`,
162
+ * because it is written into CSS as that word and a caller who types the
163
+ * number would be surprised by which one worked.
164
+ */
165
+ export type NebaAnimateRepeat = number | 'infinite';
166
+ /**
167
+ * The settings every `Animate*` component takes, and the reason they are one
168
+ * interface: a `delay` of 200 has to mean the same thing on a fade and on a
169
+ * marquee, exactly as a `size` of `md` means one height everywhere.
170
+ *
171
+ * Durations and delays are milliseconds — numbers, not CSS strings. A prop
172
+ * whose type is `string` invites `'0.4s'`, and then two components in the same
173
+ * screen are written in two units.
174
+ */
175
+ export interface NebaAnimateProps {
176
+ /** How long one run takes, in milliseconds. */
177
+ duration?: number;
178
+ /** How long before it starts, in milliseconds. @default 0 */
179
+ delay?: number;
180
+ /** The easing curve, as CSS writes it. @default the house curve */
181
+ easing?: string;
182
+ /** How many times it runs. @default 1 */
183
+ repeat?: NebaAnimateRepeat;
184
+ /** Runs every other pass backwards, so a repeat returns instead of jumping. */
185
+ alternate?: boolean;
186
+ /** Holds the animation where it is. @default false */
187
+ paused?: boolean;
188
+ /** What starts it. @default 'mount' */
189
+ trigger?: NebaAnimateTrigger;
190
+ /** Runs it, when `trigger` is `manual`. Each `false` → `true` starts it over. */
191
+ play?: boolean;
192
+ /**
193
+ * With `trigger="visible"`, whether it runs only the first time. Off, it runs
194
+ * again every time the element comes back into view.
195
+ * @default true
196
+ */
197
+ once?: boolean;
198
+ /**
199
+ * With `trigger="visible"`, how much of the element has to be on screen
200
+ * before it counts as visible, from `0` to `1`.
201
+ * @default 0.2
202
+ */
203
+ threshold?: number;
204
+ }
205
+ /** The options a `transition` prop takes when a bare effect name is not enough. */
206
+ export interface NebaTransitionOptions {
207
+ /** Which effect. */
208
+ type: NebaAnimation;
209
+ /** Milliseconds. */
210
+ duration?: number;
211
+ /** Milliseconds. @default 0 */
212
+ delay?: number;
213
+ /** CSS easing. @default the house curve */
214
+ easing?: string;
215
+ /** @default 1 */
216
+ repeat?: NebaAnimateRepeat;
217
+ /** Runs every other pass backwards. */
218
+ alternate?: boolean;
219
+ /** Which edge a `slide` comes from. @default 'bottom' */
220
+ from?: NebaSide;
221
+ /** How far a `slide` travels — a CSS length, or a number in pixels. */
222
+ distance?: number | string;
223
+ /** Where a `grow` or a `zoom` starts, as a multiple of its final size. */
224
+ scale?: number;
225
+ /** How far a `rotate` turns from, in degrees. */
226
+ angle?: number;
227
+ }
228
+ /**
229
+ * An entrance animation on a component that displays something.
230
+ *
231
+ * A bare effect name is the whole of what most callers want —
232
+ * `transition="fade"` — and the object form is there for the rest.
233
+ *
234
+ * It runs on mount and once. Anything else — replaying on scroll, on hover, or
235
+ * under your own control — is what the `Animate*` components are, and any
236
+ * component can be wrapped in one. This prop exists so the common case does not
237
+ * need an extra element in the tree.
238
+ *
239
+ * It is offered on components that *display* something and on none that are
240
+ * pressed. A control that moves under the pointer is the one thing the design
241
+ * language rules out, and a `transition` on a Button would be exactly that.
242
+ */
243
+ export type NebaTransition = NebaAnimation | NebaTransitionOptions;
132
244
  /** Style props shared by most components; spread into their own prop types. */
133
245
  export interface NebaStyleProps {
134
246
  /** @default 'solid' */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neba",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "A comprehensive React UI component library — buttons, fields, menus, dialogs, tables and much more — accessible and themeable, sharing one prop vocabulary and a translucent acrylic design language. ESM only, types included, dark mode built in.",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",
@@ -103,14 +103,14 @@
103
103
  "@types/node": "^26.1.2",
104
104
  "@types/react": "^19.2.18",
105
105
  "@types/react-dom": "^19.2.4",
106
- "@typescript-eslint/parser": "^8.65.0",
107
- "@vitejs/plugin-react-swc": "^4.3.3",
106
+ "@typescript-eslint/parser": "^8.66.0",
107
+ "@vitejs/plugin-react": "^6.0.5",
108
108
  "@vitest/browser": "^4.1.10",
109
109
  "@vitest/browser-playwright": "^4.1.10",
110
110
  "eslint": "^9.39.1",
111
111
  "eslint-config-prettier": "^10.1.8",
112
112
  "eslint-plugin-n": "^18.2.2",
113
- "globals": "^17.8.0",
113
+ "globals": "^17.9.0",
114
114
  "jiti": "^2.7.0",
115
115
  "playwright": "^1.62.1",
116
116
  "postcss": "^8.5.25",
@@ -120,9 +120,9 @@
120
120
  "react-dom": "^19.2.8",
121
121
  "tailwindcss": "^4.3.3",
122
122
  "terser-glob": "^1.2.1",
123
- "tsx": "^4.23.1",
124
- "typescript": "^5.9.3",
125
- "typescript-eslint": "^8.65.0",
123
+ "tsx": "^4.23.6",
124
+ "typescript": "^6.0.3",
125
+ "typescript-eslint": "^8.66.0",
126
126
  "vite": "^8.2.0",
127
127
  "vitepress": "^1.6.4",
128
128
  "vitepress-i18n": "^1.3.5",
@@ -131,7 +131,7 @@
131
131
  "vitest-browser-react": "^2.2.0"
132
132
  },
133
133
  "dependencies": {
134
- "@base-ui/react": "^1.6.0"
134
+ "@base-ui/react": "^1.7.0"
135
135
  },
136
136
  "peerDependencies": {
137
137
  "@types/react": "^18.0.0 || ^19.0.0",