@carissa-io/theme-default 0.3.20 → 0.3.22

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/DESIGN.md CHANGED
@@ -486,6 +486,33 @@ From [components-suggested-prompts](components/components-suggested-prompts): `S
486
486
  ## Message actions
487
487
  From [components-message-actions](components/components-message-actions): `MessageActions`. Quiet ghost icon buttons (transparent, a `--c-surface` fill on hover, `--c-surface-active` while pressed), 0.25rem apart, 16px icons in the `--c-text` colour (14.23:1 / 13.88:1 or more on the bubbles). Keyboard focus is the Button's `--c-focus` ring on the item that holds the roving focus. A chosen thumb takes `--c-info` (6.15:1 / 7.05:1 on `--c-surface`, 6.70:1 / 7.79:1 on the background) and a copied action `--c-success` (4.60:1 / 10.29:1 on `--c-surface`, 5.02:1 / 11.36:1 on the background; 3.98:1 on `--c-surface-active` in light, which is above the 3:1 an icon needs). Neither state is colour alone: the thumb carries `aria-pressed` and the copied action swaps the copy icon for a check and takes the `Copied` name, which is also announced. The component has no variable for a fill on the chosen thumb, so the visible cue of the chosen thumb is its colour; a filled icon or a background would be a component release.
488
488
 
489
+ ## Motion
490
+ The motion components read five shared tokens (`$motion-duration-short` 0.25s, `$motion-duration-medium` 0.5s, `$motion-duration-long` 0.9s, `$motion-easing` `cubic-bezier(0.16, 1, 0.3, 1)` and `$motion-stagger` 0.15s), so the character of the motion moves from one place. The easing decelerates: a fast start and a soft landing. They are not interface transitions: those stay on `$transition-duration` (0.2s, `ease`).
491
+
492
+ ## Animate
493
+ From [components-animate](components/components-animate): `Animate`. The entrance lasts the medium token, lands on the shared easing and travels 0.75rem (`rise`) or starts at 96% (`scale`); the `pulse` is a 6% swell. Only opacity and transform move: the block sets no colour, so the text inside keeps the contrast of the palette (17.93:1 / 18.16:1 for `--c-text` on the background) once the entrance has landed, in both modes. The default stays no motion: the page picks one orchestrated moment. Under `prefers-reduced-motion` the element shows its final state at once.
494
+
495
+ ## Sequence
496
+ From [components-sequence](components/components-sequence): `Sequence`. The items come in 0.15s apart (`$motion-stagger`), stacked with 1rem between them; the motion itself is the Animate one, so the block is configured after it. No colour of its own. Under `prefers-reduced-motion` every item is shown at once.
497
+
498
+ ## Typewriter
499
+ From [components-typewriter](components/components-typewriter): `Typewriter`. The cursor is a 0.08em bar in `--c-text`, the colour of the text it follows (17.93:1 / 18.16:1 on the background), blinking once per four short tokens (1s; a blink of the short token alone would flicker). It stays solid while characters are typed. The typing speed is a prop. Under `prefers-reduced-motion` the final text shows at once and the cursor is not drawn.
500
+
501
+ ## Scramble
502
+ From [components-scramble](components/components-scramble): `Scramble`. Characters still decoding sit at 0.7 opacity and the settled text returns to full opacity over the short token on the shared easing. The text keeps `--c-text`; the contrast figures are those of the palette once it has settled. Under `prefers-reduced-motion` the final text shows at once.
503
+
504
+ ## Counter
505
+ From [components-counter](components/components-counter): `Counter`. A count lasts the long token (0.9s) on the decelerating `out` easing, read by the component through custom properties. Equal-width digits stay on so the figure does not jitter. No colour of its own: it inherits the text around it. The final value is the accessible text from the first render.
506
+
507
+ ## Highlight sweep
508
+ From [components-highlight-sweep](components/components-highlight-sweep): `HighlightSweep`. The marker is `--c-surface-active` behind the text (`--c-text` on it: 14.23:1 in light, 13.88:1 in dark; both AA, tested), 0.2em wider on each side, sweeping over the medium token on the shared easing. The gray marks without a hue; if the highlight carries meaning, say it in text or use `as="mark"`. Under `prefers-reduced-motion` the marker shows at its end state.
509
+
510
+ ## Ticker
511
+ From [components-ticker](components/components-ticker): `Ticker`. A constant-speed loop of 40s (a marquee is not an entrance, so it does not read the motion tokens) with 2rem between items. The pause control is a 2.75rem round button (the touch target of the theme): `--c-text` glyph on `--c-background`, a 1px `--c-muted` border (5.74:1 / 7.85:1 on the background) and a 2px `--c-focus` outline offset by 2px on keyboard focus. The text of the control comes from the app (`pause_label`, `play_label`). Under `prefers-reduced-motion` the ticker is a static list with no control.
512
+
513
+ ## Stagger list
514
+ From [components-stagger-list](components/components-stagger-list): `StaggerList`. Rows come in 0.15s apart (`$motion-stagger`) with 0.5rem between them; the motion of each row is the Animate one, so the block is configured after it. No colour of its own. Under `prefers-reduced-motion` every row is shown at once.
515
+
489
516
  ## Keyboard focus (all controls)
490
517
  One rule keeps the controls coherent: **an outlined control thickens its own border from 1px to 2px, black** (text fields,
491
518
  select, combobox, date picker, textarea, upload, empty checkbox, off switch, radio, secondary and ghost buttons); **a filled
package/README.md CHANGED
@@ -123,6 +123,11 @@ change.
123
123
  | `$focus-color` | `$black` | focus color |
124
124
  | `$focus-width` | `1px` | focus ring width |
125
125
  | `$transition-duration` | `0.2s` | color and border transitions |
126
+ | `$motion-duration-short` | `0.25s` | quick entrances and small moves (`scramble` settle, four of them make the `typewriter` cursor blink) |
127
+ | `$motion-duration-medium` | `0.5s` | the default entrance (`animate`) and the `highlight-sweep` |
128
+ | `$motion-duration-long` | `0.9s` | counts (`counter`), reveals and other slow moves |
129
+ | `$motion-easing` | `cubic-bezier(0.16, 1, 0.3, 1)` | the one decelerating easing of the motion components |
130
+ | `$motion-stagger` | `0.15s` | gap between the items of a `sequence` or a `stagger-list` |
126
131
  | `$label-font-size` | `0.6875rem` | field label size |
127
132
  | `$label-font-weight` | `700` | field label weight |
128
133
  | `$label-letter-spacing` | `0.1em` | field label tracking |
@@ -169,7 +174,7 @@ it: check the names against the theme's `scss/_tokens.scss` before you switch.
169
174
 
170
175
  ## What it configures
171
176
  `components-input`, `-button`, `-checkbox`, `-switch`, `-radio-group`, `-slider`, `-popover`, `-listbox`, `-select`,
172
- `-combobox`, `-upload`, `-calendar`, `-date-picker`, `-search`, `-typography`, `-card`, `-alert`, `-spinner`, `-modal`, `-toast`, `-divider`, `-badge`, `-tag`, `-link`, `-avatar`, `-skeleton`, `-progress`, `-tooltip`, `-prose`, `-tabs`, `-accordion`, `-breadcrumb`, `-pagination`, `-stepper`, `-sidebar`, `-navbar`, `-drawer`, `-menu`, `-table`, `-list`, `-description-list`, `-stat`, `-empty-state`, `-timeline`, `-tree`, `-code`, `-field`, `-number-input`, `-otp-input`, `-phone`, `-tag-input`, `-time-picker`, `-date-range`, `-rating`, `-color-picker`, `-async-autocomplete`, `-section`, `-faq`, `-carousel`, `-visually-hidden`, `-live-region`, `-theme-switch`, `-icon`, `-ai-label`, `-thinking-indicator`, `-streaming-text`, `-message`, `-message-list`, `-prompt-input`, `-suggested-prompts` and `-message-actions`. `components-email`, `-password`, `-url` and `-textarea`
177
+ `-combobox`, `-upload`, `-calendar`, `-date-picker`, `-search`, `-typography`, `-card`, `-alert`, `-spinner`, `-modal`, `-toast`, `-divider`, `-badge`, `-tag`, `-link`, `-avatar`, `-skeleton`, `-progress`, `-tooltip`, `-prose`, `-tabs`, `-accordion`, `-breadcrumb`, `-pagination`, `-stepper`, `-sidebar`, `-navbar`, `-drawer`, `-menu`, `-table`, `-list`, `-description-list`, `-stat`, `-empty-state`, `-timeline`, `-tree`, `-code`, `-field`, `-number-input`, `-otp-input`, `-phone`, `-tag-input`, `-time-picker`, `-date-range`, `-rating`, `-color-picker`, `-async-autocomplete`, `-section`, `-faq`, `-carousel`, `-visually-hidden`, `-live-region`, `-theme-switch`, `-icon`, `-ai-label`, `-thinking-indicator`, `-streaming-text`, `-message`, `-message-list`, `-prompt-input`, `-suggested-prompts`, `-message-actions`, `-animate`, `-sequence`, `-typewriter`, `-scramble`, `-counter`, `-highlight-sweep`, `-ticker` and `-stagger-list`. `components-email`, `-password`, `-url` and `-textarea`
173
178
  are styled through the input.
174
179
 
175
180
  The checkbox's mixed state (`indeterminate`, from the next `components-checkbox` release, class `c-checkbox--indeterminate`) is the checked box with a 10px dash in `--c-on-primary`; on an older checkbox the rule is simply unused.
package/dist/index.css CHANGED
@@ -5894,3 +5894,395 @@ button.c-theme-switch:focus-visible {
5894
5894
  .c-message-actions__action--copied {
5895
5895
  color: var(--c-success);
5896
5896
  }
5897
+
5898
+ @keyframes c-animate-fade {
5899
+ from {
5900
+ opacity: 0;
5901
+ }
5902
+ to {
5903
+ opacity: 1;
5904
+ }
5905
+ }
5906
+ @keyframes c-animate-rise {
5907
+ from {
5908
+ opacity: 0;
5909
+ transform: translateY(0.75rem);
5910
+ }
5911
+ to {
5912
+ opacity: 1;
5913
+ transform: none;
5914
+ }
5915
+ }
5916
+ @keyframes c-animate-scale {
5917
+ from {
5918
+ opacity: 0;
5919
+ transform: scale(0.96);
5920
+ }
5921
+ to {
5922
+ opacity: 1;
5923
+ transform: none;
5924
+ }
5925
+ }
5926
+ @keyframes c-animate-pulse {
5927
+ 0%, 100% {
5928
+ transform: none;
5929
+ }
5930
+ 50% {
5931
+ transform: scale(1.06);
5932
+ }
5933
+ }
5934
+ .c-animate--fade[data-state=active],
5935
+ .c-animate--rise[data-state=active],
5936
+ .c-animate--scale[data-state=active],
5937
+ .c-animate--pulse[data-state=active] {
5938
+ animation-duration: var(--c-animate-duration, 0.5s);
5939
+ animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
5940
+ animation-delay: var(--c-animate-delay, 0s);
5941
+ animation-fill-mode: both;
5942
+ }
5943
+
5944
+ .c-animate--fade[data-state=active] {
5945
+ animation-name: c-animate-fade;
5946
+ }
5947
+
5948
+ .c-animate--rise[data-state=active] {
5949
+ animation-name: c-animate-rise;
5950
+ }
5951
+
5952
+ .c-animate--scale[data-state=active] {
5953
+ animation-name: c-animate-scale;
5954
+ }
5955
+
5956
+ .c-animate--pulse[data-state=active] {
5957
+ animation-name: c-animate-pulse;
5958
+ }
5959
+
5960
+ .c-animate--entrance[data-state=idle] {
5961
+ opacity: 0;
5962
+ }
5963
+
5964
+ @media (prefers-reduced-motion: reduce) {
5965
+ .c-animate,
5966
+ .c-animate[data-state] {
5967
+ opacity: 1;
5968
+ transform: none;
5969
+ animation: none;
5970
+ }
5971
+ }
5972
+ .c-sequence {
5973
+ display: flex;
5974
+ flex-direction: column;
5975
+ gap: 1rem;
5976
+ }
5977
+ .c-sequence[data-running=false] * {
5978
+ animation-play-state: paused;
5979
+ }
5980
+
5981
+ .c-sequence__item {
5982
+ --c-animate-delay: calc(var(--c-sequence-index, 0) * var(--c-sequence-stagger, 0.15s));
5983
+ }
5984
+
5985
+ @keyframes c-typewriter-blink {
5986
+ 0%, 49% {
5987
+ opacity: 1;
5988
+ }
5989
+ 50%, 100% {
5990
+ opacity: 0;
5991
+ }
5992
+ }
5993
+ .c-typewriter__sr {
5994
+ position: absolute;
5995
+ width: 1px;
5996
+ height: 1px;
5997
+ margin: -1px;
5998
+ padding: 0;
5999
+ overflow: hidden;
6000
+ clip-path: inset(50%);
6001
+ white-space: nowrap;
6002
+ border: 0;
6003
+ }
6004
+
6005
+ .c-typewriter__text {
6006
+ white-space: pre-wrap;
6007
+ }
6008
+
6009
+ .c-typewriter__cursor {
6010
+ display: inline-block;
6011
+ width: 0.08em;
6012
+ height: 1em;
6013
+ margin-inline-start: 0.1em;
6014
+ vertical-align: text-bottom;
6015
+ background: var(--c-text);
6016
+ animation: c-typewriter-blink 1s steps(1, end) infinite;
6017
+ }
6018
+ .c-typewriter__cursor--active {
6019
+ animation: none;
6020
+ }
6021
+
6022
+ @media (prefers-reduced-motion: reduce) {
6023
+ .c-typewriter__cursor {
6024
+ animation: none;
6025
+ }
6026
+ }
6027
+ .c-scramble__sr {
6028
+ position: absolute;
6029
+ width: 1px;
6030
+ height: 1px;
6031
+ margin: -1px;
6032
+ padding: 0;
6033
+ overflow: hidden;
6034
+ clip-path: inset(50%);
6035
+ white-space: nowrap;
6036
+ border: 0;
6037
+ }
6038
+
6039
+ .c-scramble__text {
6040
+ white-space: pre-wrap;
6041
+ font-variant-numeric: tabular-nums;
6042
+ transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1);
6043
+ }
6044
+
6045
+ .c-scramble--scrambling .c-scramble__text {
6046
+ opacity: 0.7;
6047
+ }
6048
+
6049
+ @media (prefers-reduced-motion: reduce) {
6050
+ .c-scramble__text {
6051
+ opacity: 1;
6052
+ transition: none;
6053
+ }
6054
+ }
6055
+ .c-counter {
6056
+ --c-counter-duration: 0.9s;
6057
+ --c-counter-easing: out;
6058
+ position: relative;
6059
+ font-variant-numeric: tabular-nums;
6060
+ }
6061
+
6062
+ .c-counter__text {
6063
+ position: absolute;
6064
+ width: 1px;
6065
+ height: 1px;
6066
+ padding: 0;
6067
+ margin: -1px;
6068
+ overflow: hidden;
6069
+ clip-path: inset(50%);
6070
+ white-space: nowrap;
6071
+ border: 0;
6072
+ }
6073
+
6074
+ @keyframes c-highlight-sweep {
6075
+ from {
6076
+ background-size: 0% 100%;
6077
+ }
6078
+ to {
6079
+ background-size: 100% 100%;
6080
+ }
6081
+ }
6082
+ .c-highlight-sweep {
6083
+ padding-inline: 0.2em;
6084
+ background-image: linear-gradient(var(--c-surface-active), var(--c-surface-active));
6085
+ background-repeat: no-repeat;
6086
+ background-position: left bottom;
6087
+ background-size: 0% 100%;
6088
+ -webkit-box-decoration-break: clone;
6089
+ box-decoration-break: clone;
6090
+ }
6091
+
6092
+ .c-highlight-sweep--rtl {
6093
+ background-position: right bottom;
6094
+ }
6095
+
6096
+ .c-highlight-sweep[data-state=active] {
6097
+ background-size: 100% 100%;
6098
+ animation-name: c-highlight-sweep;
6099
+ animation-duration: var(--c-highlight-sweep-duration, 0.5s);
6100
+ animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
6101
+ animation-delay: var(--c-highlight-sweep-delay, 0s);
6102
+ animation-fill-mode: backwards;
6103
+ }
6104
+
6105
+ @media (prefers-reduced-motion: reduce) {
6106
+ .c-highlight-sweep,
6107
+ .c-highlight-sweep[data-state] {
6108
+ background-size: 100% 100%;
6109
+ animation: none;
6110
+ }
6111
+ }
6112
+ @keyframes c-ticker-x {
6113
+ from {
6114
+ transform: translateX(0);
6115
+ }
6116
+ to {
6117
+ transform: translateX(-50%);
6118
+ }
6119
+ }
6120
+ @keyframes c-ticker-y {
6121
+ from {
6122
+ transform: translateY(0);
6123
+ }
6124
+ to {
6125
+ transform: translateY(-50%);
6126
+ }
6127
+ }
6128
+ .c-ticker {
6129
+ position: relative;
6130
+ display: flex;
6131
+ align-items: center;
6132
+ gap: 2rem;
6133
+ }
6134
+
6135
+ .c-ticker__viewport {
6136
+ flex: 1;
6137
+ min-width: 0;
6138
+ overflow: hidden;
6139
+ }
6140
+
6141
+ .c-ticker__track {
6142
+ display: flex;
6143
+ width: max-content;
6144
+ animation-duration: var(--c-ticker-duration, 40s);
6145
+ animation-timing-function: linear;
6146
+ animation-iteration-count: infinite;
6147
+ }
6148
+
6149
+ .c-ticker__list {
6150
+ display: flex;
6151
+ flex: none;
6152
+ gap: var(--c-ticker-gap, 2rem);
6153
+ min-width: 100vw;
6154
+ padding: 0 var(--c-ticker-gap, 2rem) 0 0;
6155
+ margin: 0;
6156
+ list-style: none;
6157
+ }
6158
+
6159
+ .c-ticker__item {
6160
+ flex: none;
6161
+ }
6162
+
6163
+ .c-ticker--horizontal .c-ticker__track {
6164
+ animation-name: c-ticker-x;
6165
+ }
6166
+
6167
+ .c-ticker--right .c-ticker__track,
6168
+ .c-ticker--down .c-ticker__track {
6169
+ animation-direction: reverse;
6170
+ }
6171
+
6172
+ .c-ticker--vertical .c-ticker__viewport {
6173
+ height: 12rem;
6174
+ }
6175
+ .c-ticker--vertical .c-ticker__track {
6176
+ flex-direction: column;
6177
+ width: auto;
6178
+ animation-name: c-ticker-y;
6179
+ }
6180
+ .c-ticker--vertical .c-ticker__list {
6181
+ flex-direction: column;
6182
+ min-width: 0;
6183
+ min-height: 12rem;
6184
+ padding: 0 0 var(--c-ticker-gap, 2rem) 0;
6185
+ }
6186
+
6187
+ .c-ticker[data-state=paused] .c-ticker__track,
6188
+ .c-ticker[data-state=idle] .c-ticker__track,
6189
+ .c-ticker__viewport:focus-within .c-ticker__track,
6190
+ .c-ticker--pause-on-hover .c-ticker__viewport:hover .c-ticker__track {
6191
+ animation-play-state: paused;
6192
+ }
6193
+
6194
+ .c-ticker[data-state=static] .c-ticker__track {
6195
+ width: auto;
6196
+ animation: none;
6197
+ }
6198
+ .c-ticker[data-state=static] .c-ticker__list {
6199
+ flex: 0 1 auto;
6200
+ flex-wrap: wrap;
6201
+ min-width: 0;
6202
+ min-height: 0;
6203
+ padding: 0;
6204
+ }
6205
+ .c-ticker[data-state=static] .c-ticker__viewport {
6206
+ height: auto;
6207
+ mask-image: none;
6208
+ }
6209
+
6210
+ .c-ticker__toggle {
6211
+ position: relative;
6212
+ box-sizing: border-box;
6213
+ flex: none;
6214
+ width: 2.75rem;
6215
+ height: 2.75rem;
6216
+ padding: 0;
6217
+ color: var(--c-text);
6218
+ cursor: pointer;
6219
+ background: var(--c-background);
6220
+ border: 1px solid var(--c-muted);
6221
+ border-radius: 50%;
6222
+ }
6223
+ .c-ticker__toggle::before {
6224
+ position: absolute;
6225
+ top: 50%;
6226
+ left: 50%;
6227
+ content: "";
6228
+ }
6229
+ .c-ticker__toggle:focus-visible {
6230
+ outline: 2px solid var(--c-focus);
6231
+ outline-offset: 2px;
6232
+ }
6233
+
6234
+ .c-ticker__toggle--playing::before {
6235
+ width: 0.5rem;
6236
+ height: 0.75rem;
6237
+ border-right: 0.1875rem solid currentcolor;
6238
+ border-left: 0.1875rem solid currentcolor;
6239
+ transform: translate(-50%, -50%);
6240
+ }
6241
+
6242
+ .c-ticker__toggle--paused::before {
6243
+ border-top: 0.375rem solid transparent;
6244
+ border-bottom: 0.375rem solid transparent;
6245
+ border-left: 0.625rem solid currentcolor;
6246
+ transform: translate(-35%, -50%);
6247
+ }
6248
+
6249
+ @media (prefers-reduced-motion: reduce) {
6250
+ .c-ticker__track {
6251
+ width: auto;
6252
+ animation: none;
6253
+ }
6254
+ .c-ticker__list--copy,
6255
+ .c-ticker__toggle {
6256
+ display: none;
6257
+ }
6258
+ .c-ticker__list {
6259
+ flex: 0 1 auto;
6260
+ flex-wrap: wrap;
6261
+ min-width: 0;
6262
+ min-height: 0;
6263
+ padding: 0;
6264
+ }
6265
+ }
6266
+ .c-stagger-list {
6267
+ display: flex;
6268
+ flex-direction: column;
6269
+ gap: 0.5rem;
6270
+ padding: 0;
6271
+ margin: 0;
6272
+ list-style: none;
6273
+ }
6274
+
6275
+ .c-stagger-list__item {
6276
+ --c-animate-delay: calc(
6277
+ var(--c-stagger-list-delay, 0s) + var(--c-stagger-list-index, 0) *
6278
+ var(--c-stagger-list-stagger, 0.15s)
6279
+ );
6280
+ }
6281
+
6282
+ @media (prefers-reduced-motion: reduce) {
6283
+ .c-stagger-list__item {
6284
+ opacity: 1;
6285
+ transform: none;
6286
+ animation: none;
6287
+ }
6288
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carissa-io/theme-default",
3
- "version": "0.3.20",
3
+ "version": "0.3.22",
4
4
  "description": "Carissa UI: the default theme (Swiss minimalism) as SCSS variable overrides of the Carissa components, with a compiled CSS build",
5
5
  "type": "module",
6
6
  "style": "dist/index.css",
@@ -108,7 +108,15 @@
108
108
  "@carissa-io/components-message-list": "^0.1.0",
109
109
  "@carissa-io/components-prompt-input": "^0.1.2",
110
110
  "@carissa-io/components-suggested-prompts": "^0.1.0",
111
- "@carissa-io/components-message-actions": "^0.1.0"
111
+ "@carissa-io/components-message-actions": "^0.1.0",
112
+ "@carissa-io/components-animate": "^0.1.0",
113
+ "@carissa-io/components-sequence": "^0.1.0",
114
+ "@carissa-io/components-typewriter": "^0.1.0",
115
+ "@carissa-io/components-scramble": "^0.1.0",
116
+ "@carissa-io/components-counter": "^0.1.0",
117
+ "@carissa-io/components-highlight-sweep": "^0.1.0",
118
+ "@carissa-io/components-ticker": "^0.1.0",
119
+ "@carissa-io/components-stagger-list": "^0.1.0"
112
120
  },
113
121
  "peerDependenciesMeta": {
114
122
  "@carissa-io/components-input": {
@@ -335,12 +343,37 @@
335
343
  },
336
344
  "@carissa-io/components-message-actions": {
337
345
  "optional": true
346
+ },
347
+ "@carissa-io/components-animate": {
348
+ "optional": true
349
+ },
350
+ "@carissa-io/components-sequence": {
351
+ "optional": true
352
+ },
353
+ "@carissa-io/components-typewriter": {
354
+ "optional": true
355
+ },
356
+ "@carissa-io/components-scramble": {
357
+ "optional": true
358
+ },
359
+ "@carissa-io/components-counter": {
360
+ "optional": true
361
+ },
362
+ "@carissa-io/components-highlight-sweep": {
363
+ "optional": true
364
+ },
365
+ "@carissa-io/components-ticker": {
366
+ "optional": true
367
+ },
368
+ "@carissa-io/components-stagger-list": {
369
+ "optional": true
338
370
  }
339
371
  },
340
372
  "devDependencies": {
341
373
  "@carissa-io/components-accordion": "^0.1.0",
342
374
  "@carissa-io/components-ai-label": "^0.1.0",
343
375
  "@carissa-io/components-alert": "^0.1.0",
376
+ "@carissa-io/components-animate": "^0.1.0",
344
377
  "@carissa-io/components-async-autocomplete": "^0.1.0",
345
378
  "@carissa-io/components-avatar": "^0.1.0",
346
379
  "@carissa-io/components-badge": "^0.1.0",
@@ -353,6 +386,7 @@
353
386
  "@carissa-io/components-code": "^0.1.0",
354
387
  "@carissa-io/components-color-picker": "^0.1.0",
355
388
  "@carissa-io/components-combobox": "^0.1.1",
389
+ "@carissa-io/components-counter": "^0.1.0",
356
390
  "@carissa-io/components-date-picker": "^0.1.1",
357
391
  "@carissa-io/components-date-range": "^0.1.0",
358
392
  "@carissa-io/components-description-list": "^0.1.0",
@@ -361,6 +395,7 @@
361
395
  "@carissa-io/components-empty-state": "^0.1.0",
362
396
  "@carissa-io/components-faq": "^0.1.0",
363
397
  "@carissa-io/components-field": "^0.1.0",
398
+ "@carissa-io/components-highlight-sweep": "^0.1.0",
364
399
  "@carissa-io/components-icon": "^0.1.0",
365
400
  "@carissa-io/components-input": "^0.2.1",
366
401
  "@carissa-io/components-link": "^0.1.0",
@@ -383,13 +418,16 @@
383
418
  "@carissa-io/components-prose": "^0.1.0",
384
419
  "@carissa-io/components-radio-group": "^0.1.1",
385
420
  "@carissa-io/components-rating": "^0.1.0",
421
+ "@carissa-io/components-scramble": "^0.1.0",
386
422
  "@carissa-io/components-search": "^0.1.1",
387
423
  "@carissa-io/components-section": "^0.1.0",
388
424
  "@carissa-io/components-select": "^0.1.1",
425
+ "@carissa-io/components-sequence": "^0.1.0",
389
426
  "@carissa-io/components-sidebar": "^0.1.0",
390
427
  "@carissa-io/components-skeleton": "^0.1.0",
391
428
  "@carissa-io/components-slider": "^0.1.1",
392
429
  "@carissa-io/components-spinner": "^0.1.0",
430
+ "@carissa-io/components-stagger-list": "^0.1.0",
393
431
  "@carissa-io/components-stat": "^0.1.0",
394
432
  "@carissa-io/components-stepper": "^0.1.0",
395
433
  "@carissa-io/components-streaming-text": "^0.1.0",
@@ -402,11 +440,13 @@
402
440
  "@carissa-io/components-textarea": "^0.1.1",
403
441
  "@carissa-io/components-theme-switch": "^0.1.0",
404
442
  "@carissa-io/components-thinking-indicator": "^0.1.0",
443
+ "@carissa-io/components-ticker": "^0.1.0",
405
444
  "@carissa-io/components-time-picker": "^0.1.0",
406
445
  "@carissa-io/components-timeline": "^0.1.0",
407
446
  "@carissa-io/components-toast": "^0.1.0",
408
447
  "@carissa-io/components-tooltip": "^0.1.0",
409
448
  "@carissa-io/components-tree": "^0.1.0",
449
+ "@carissa-io/components-typewriter": "^0.1.0",
410
450
  "@carissa-io/components-typography": "^0.1.0",
411
451
  "@carissa-io/components-upload": "^0.1.1",
412
452
  "@carissa-io/components-visually-hidden": "^0.1.0",
package/scss/_tokens.scss CHANGED
@@ -30,6 +30,14 @@ $min-height: 2.75rem; // 44px touch target
30
30
  $focus-color: $black;
31
31
  $focus-width: 1px;
32
32
  $transition-duration: 0.2s;
33
+ // Motion tokens shared by every motion component (entrances, counts, sequences), so the character of the motion moves from one
34
+ // place. They are not interface transitions: those stay on $transition-duration above. One decelerating easing: fast start,
35
+ // soft landing.
36
+ $motion-duration-short: 0.25s;
37
+ $motion-duration-medium: 0.5s;
38
+ $motion-duration-long: 0.9s;
39
+ $motion-easing: cubic-bezier(0.16, 1, 0.3, 1);
40
+ $motion-stagger: 0.15s; // the gap between the items of a sequence
33
41
  $label-font-size: 0.6875rem;
34
42
  $label-font-weight: 700;
35
43
  $label-letter-spacing: 0.1em;
package/scss/index.scss CHANGED
@@ -1861,3 +1861,76 @@
1861
1861
  $c-message-actions-pressed-color: $var-info,
1862
1862
  $c-message-actions-copied-color: $var-success
1863
1863
  );
1864
+
1865
+ // Animate: the entrance lasts the medium motion token and lands on the shared decelerating easing; the rise travels 0.75rem and
1866
+ // the pulse is a 6% swell. It has no colour of its own (opacity and transform only), so both modes follow. The default of the
1867
+ // component stays no motion: the page chooses its one orchestrated moment. Under prefers-reduced-motion every element shows its
1868
+ // final state at once.
1869
+ @use '@carissa-io/components-animate/scss' as animate with (
1870
+ $c-animate-duration: $motion-duration-medium,
1871
+ $c-animate-easing: $motion-easing,
1872
+ $c-animate-distance: 0.75rem,
1873
+ $c-animate-scale-from: 0.96,
1874
+ $c-animate-pulse-scale: 1.06
1875
+ );
1876
+
1877
+ // Sequence: the items come in $motion-stagger apart (the short token is too slow for a list), stacked with 1rem between them.
1878
+ // The motion itself (duration, distance, easing) is the Animate one above, so this block comes after it. No colours.
1879
+ @use '@carissa-io/components-sequence/scss' as sequence with (
1880
+ $c-sequence-stagger: $motion-stagger,
1881
+ $c-sequence-direction: column,
1882
+ $c-sequence-gap: 1rem
1883
+ );
1884
+
1885
+ // Typewriter: the cursor is a thin bar in the text colour (the text it follows), blinking once per four short tokens (a blink of the
1886
+ // short token alone would flicker). The typing speed is a prop. No other colour; under prefers-reduced-motion the cursor is not drawn.
1887
+ @use '@carissa-io/components-typewriter/scss' as typewriter with (
1888
+ $c-typewriter-cursor-width: 0.08em,
1889
+ $c-typewriter-cursor-color: $var-text,
1890
+ $c-typewriter-cursor-blink: $motion-duration-short * 4
1891
+ );
1892
+
1893
+ // Scramble: the characters that have not settled yet are dimmed to 0.7 and the settled text returns to full opacity over the short
1894
+ // token on the shared easing. The text keeps --c-text, so the contrast of the settled text is the palette's.
1895
+ @use '@carissa-io/components-scramble/scss' as scramble with (
1896
+ $c-scramble-scrambled-opacity: 0.7,
1897
+ $c-scramble-transition: $motion-duration-short,
1898
+ $c-scramble-easing: $motion-easing
1899
+ );
1900
+
1901
+ // Counter: a count lasts the long token on the shared decelerating easing (the component reads both through custom properties).
1902
+ // Equal-width digits stay on so the number does not jitter. No colour: the figure inherits the text around it.
1903
+ @use '@carissa-io/components-counter/scss' as counter with (
1904
+ $c-counter-duration: $motion-duration-long,
1905
+ $c-counter-easing: out
1906
+ );
1907
+
1908
+ // Highlight sweep: the marker is the --c-surface-active gray behind the text (the text keeps --c-text on it, see the contrast
1909
+ // test), and it sweeps over the medium token on the shared easing. Under prefers-reduced-motion it shows at its end state.
1910
+ @use '@carissa-io/components-highlight-sweep/scss' as highlight-sweep with (
1911
+ $c-highlight-sweep-color: $var-surface-active,
1912
+ $c-highlight-sweep-duration: $motion-duration-medium,
1913
+ $c-highlight-sweep-easing: $motion-easing,
1914
+ $c-highlight-sweep-padding-inline: 0.2em
1915
+ );
1916
+
1917
+ // Ticker: a long constant-speed loop (it keeps its own 40s, a marquee is not an entrance), 2rem between items. The pause control
1918
+ // is a 2.75rem round button like the theme's other controls: --c-text glyph, 1px --c-muted border, and the --c-focus outline.
1919
+ @use '@carissa-io/components-ticker/scss' as ticker with (
1920
+ $c-ticker-duration: 40s,
1921
+ $c-ticker-easing: linear,
1922
+ $c-ticker-gap: 2rem,
1923
+ $c-ticker-toggle-size: $min-height,
1924
+ $c-ticker-toggle-color: $var-text,
1925
+ $c-ticker-toggle-background: $var-background,
1926
+ $c-ticker-toggle-border-color: $var-muted,
1927
+ $c-ticker-toggle-icon-size: 0.5rem,
1928
+ $c-ticker-toggle-focus-outline: 2px solid $var-focus
1929
+ );
1930
+
1931
+ // Stagger list: the rows come in $motion-stagger apart, stacked with 0.5rem between them. The motion of each row (duration,
1932
+ // distance, easing) is the Animate one above, so this block comes after it. No colours.
1933
+ @use '@carissa-io/components-stagger-list/scss' as stagger-list with (
1934
+ $c-stagger-list-stagger: $motion-stagger,
1935
+ $c-stagger-list-gap: 0.5rem
1936
+ );