rafters 0.0.77 → 0.0.78

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 (2) hide show
  1. package/dist/index.js +315 -368
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22589,42 +22589,48 @@ var DEFAULT_SHADOW_DEFINITIONS = {
22589
22589
  };
22590
22590
  var DEFAULT_DURATION_DEFINITIONS = {
22591
22591
  instant: {
22592
- ms: 0,
22592
+ range: [0, 0],
22593
+ default: 0,
22593
22594
  band: "",
22594
22595
  meaning: "No perceptible transition. Cursor changes, text selection, badge counts. Below all perception -- there is nothing to track, so nothing is communicated.",
22595
22596
  contexts: ["disabled-motion", "prefers-reduced-motion", "cursor", "badge-count"],
22596
22597
  motionIntent: "transition"
22597
22598
  },
22598
22599
  micro: {
22599
- ms: 100,
22600
+ range: [50, 120],
22601
+ default: 100,
22600
22602
  band: "at the instantaneous threshold (Nielsen 0.1s)",
22601
22603
  meaning: "Immediate but visible. Focus rings and press feedback. At the instantaneous threshold -- acknowledgment that input landed, not communication of a change.",
22602
22604
  contexts: ["focus", "press", "micro-feedback"],
22603
22605
  motionIntent: "transition"
22604
22606
  },
22605
22607
  fast: {
22606
- ms: 150,
22608
+ range: [120, 200],
22609
+ default: 150,
22607
22610
  band: "below the communicative window",
22608
22611
  meaning: "Hover states. The cursor is already there, so the response must match its speed. Below the communicative window -- acknowledgment, not communication.",
22609
22612
  contexts: ["hover", "micro-feedback"],
22610
22613
  motionIntent: "transition"
22611
22614
  },
22612
22615
  moderate: {
22613
- ms: 250,
22616
+ range: [200, 300],
22617
+ default: 200,
22614
22618
  band: "communicative (~200-300ms)",
22615
22619
  meaning: "Dropdowns, tab switches, small reveals. The communicative window: fast enough to feel responsive, slow enough for the eye to track a trajectory and build a spatial model.",
22616
22620
  contexts: ["dropdowns", "tab-switches", "small-reveals"],
22617
22621
  motionIntent: "transition"
22618
22622
  },
22619
22623
  normal: {
22620
- ms: 350,
22624
+ range: [300, 400],
22625
+ default: 300,
22621
22626
  band: "communicative, larger movement",
22622
22627
  meaning: "The workhorse -- modal entrances, toggles, standard state transitions. The communicative window for larger movement.",
22623
22628
  contexts: ["modals", "toggles", "state-changes"],
22624
22629
  motionIntent: "enter"
22625
22630
  },
22626
22631
  slow: {
22627
- ms: 500,
22632
+ range: [400, 500],
22633
+ default: 400,
22628
22634
  band: "at the sluggish boundary",
22629
22635
  meaning: "Sheets, page transitions, large spatial movement where the user needs orientation. At the sluggish boundary -- the ceiling for anything but full-screen spatial transitions.",
22630
22636
  contexts: ["sheets", "page-transitions", "large-spatial-movement"],
@@ -22633,10 +22639,10 @@ var DEFAULT_DURATION_DEFINITIONS = {
22633
22639
  };
22634
22640
  var DEFAULT_EASING_DEFINITIONS = {
22635
22641
  standard: {
22636
- curve: [0.25, 0.1, 0.25, 1],
22637
- meaning: "Precision -- arrives exactly where it should, engineered rather than thrown. Decelerates into its final position. General-purpose state transitions and hover.",
22642
+ curve: [0.4, 0, 0.2, 1],
22643
+ meaning: "Precision -- a responsive start that decelerates into place, engineered rather than thrown. The general-purpose workhorse for state transitions and hover: the fast start matches a cursor already on target, where a symmetric ease would drag.",
22638
22644
  contexts: ["state-changes", "hover", "general-purpose"],
22639
- css: "cubic-bezier(0.25, 0.1, 0.25, 1)"
22645
+ css: "cubic-bezier(0.4, 0, 0.2, 1)"
22640
22646
  },
22641
22647
  enter: {
22642
22648
  curve: [0, 0, 0.2, 1],
@@ -22669,6 +22675,252 @@ var DEFAULT_EASING_DEFINITIONS = {
22669
22675
  css: "cubic-bezier(0.2, 0.8, 0.2, 1)"
22670
22676
  }
22671
22677
  };
22678
+ var DEFAULT_KEYFRAME_DEFINITIONS = {
22679
+ "fade-in": {
22680
+ css: () => "from { opacity: 0; } to { opacity: 1; }",
22681
+ meaning: "Fade from transparent to opaque",
22682
+ contexts: ["enter", "appear", "show"]
22683
+ },
22684
+ "fade-out": {
22685
+ css: () => "from { opacity: 1; } to { opacity: 0; }",
22686
+ meaning: "Fade from opaque to transparent",
22687
+ contexts: ["exit", "disappear", "hide"]
22688
+ },
22689
+ "slide-in-from-top": {
22690
+ css: () => "from { transform: translateY(-100%); } to { transform: translateY(0); }",
22691
+ meaning: "Slide in from above",
22692
+ contexts: ["dropdown", "notification", "toast"]
22693
+ },
22694
+ "slide-in-from-bottom": {
22695
+ css: () => "from { transform: translateY(100%); } to { transform: translateY(0); }",
22696
+ meaning: "Slide in from below",
22697
+ contexts: ["sheet", "drawer", "mobile-menu"]
22698
+ },
22699
+ "slide-in-from-left": {
22700
+ css: () => "from { transform: translateX(-100%); } to { transform: translateX(0); }",
22701
+ meaning: "Slide in from left",
22702
+ contexts: ["sidebar", "panel", "drawer"]
22703
+ },
22704
+ "slide-in-from-right": {
22705
+ css: () => "from { transform: translateX(100%); } to { transform: translateX(0); }",
22706
+ meaning: "Slide in from right",
22707
+ contexts: ["sidebar", "panel", "drawer"]
22708
+ },
22709
+ "slide-out-to-top": {
22710
+ css: () => "from { transform: translateY(0); } to { transform: translateY(-100%); }",
22711
+ meaning: "Slide out upward",
22712
+ contexts: ["dropdown-exit", "notification-dismiss"]
22713
+ },
22714
+ "slide-out-to-bottom": {
22715
+ css: () => "from { transform: translateY(0); } to { transform: translateY(100%); }",
22716
+ meaning: "Slide out downward",
22717
+ contexts: ["sheet-exit", "drawer-close"]
22718
+ },
22719
+ "slide-out-to-left": {
22720
+ css: () => "from { transform: translateX(0); } to { transform: translateX(-100%); }",
22721
+ meaning: "Slide out to left",
22722
+ contexts: ["sidebar-close", "panel-exit"]
22723
+ },
22724
+ "slide-out-to-right": {
22725
+ css: () => "from { transform: translateX(0); } to { transform: translateX(100%); }",
22726
+ meaning: "Slide out to right",
22727
+ contexts: ["sidebar-close", "panel-exit"]
22728
+ },
22729
+ "scale-in": {
22730
+ css: (ctx) => `from { transform: scale(${ctx.scaleStart}); opacity: 0; } to { transform: scale(1); opacity: 1; }`,
22731
+ meaning: "Scale up while fading in",
22732
+ contexts: ["modal", "popover", "dialog"]
22733
+ },
22734
+ "scale-out": {
22735
+ css: (ctx) => `from { transform: scale(1); opacity: 1; } to { transform: scale(${ctx.scaleStart}); opacity: 0; }`,
22736
+ meaning: "Scale down while fading out",
22737
+ contexts: ["modal-exit", "popover-close"]
22738
+ },
22739
+ spin: {
22740
+ css: () => "from { transform: rotate(0deg); } to { transform: rotate(360deg); }",
22741
+ meaning: "Continuous rotation",
22742
+ contexts: ["loading", "spinner", "refresh"]
22743
+ },
22744
+ ping: {
22745
+ css: (ctx) => `75%, 100% { transform: scale(${ctx.pingScale}); opacity: 0; }`,
22746
+ meaning: "Expanding pulse that fades out",
22747
+ contexts: ["notification-badge", "attention", "pulse"]
22748
+ },
22749
+ pulse: {
22750
+ css: (ctx) => `0%, 100% { opacity: 1; } 50% { opacity: ${ctx.pulseOpacity}; }`,
22751
+ meaning: "Gentle opacity pulse",
22752
+ contexts: ["skeleton", "loading-placeholder"]
22753
+ },
22754
+ bounce: {
22755
+ // Inline beziers retained deliberately -- see the provenance note above.
22756
+ css: (ctx) => `0%, 100% { transform: translateY(-${ctx.bouncePercent}%); animation-timing-function: cubic-bezier(0.8, 0, 1, 1); } 50% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }`,
22757
+ meaning: "Bouncing motion",
22758
+ contexts: ["attention", "scroll-indicator"]
22759
+ },
22760
+ "caret-blink": {
22761
+ css: () => "0%, 70%, 100% { opacity: 1; } 20%, 50% { opacity: 0; }",
22762
+ meaning: "Text cursor blinking",
22763
+ contexts: ["input-caret", "text-cursor"]
22764
+ }
22765
+ };
22766
+ var DEFAULT_ANIMATION_DEFINITIONS = {
22767
+ "fade-in": {
22768
+ keyframe: "fade-in",
22769
+ duration: { tier: "fast" },
22770
+ curve: "enter",
22771
+ meaning: "Fade in animation",
22772
+ contexts: ["enter", "appear"]
22773
+ },
22774
+ "fade-out": {
22775
+ keyframe: "fade-out",
22776
+ duration: { tier: "fast" },
22777
+ curve: "exit",
22778
+ meaning: "Fade out animation",
22779
+ contexts: ["exit", "disappear"]
22780
+ },
22781
+ "slide-in-from-top": {
22782
+ keyframe: "slide-in-from-top",
22783
+ duration: { tier: "normal" },
22784
+ curve: "enter",
22785
+ meaning: "Slide in from top",
22786
+ contexts: ["dropdown", "notification"]
22787
+ },
22788
+ "slide-in-from-bottom": {
22789
+ keyframe: "slide-in-from-bottom",
22790
+ duration: { tier: "normal" },
22791
+ curve: "enter",
22792
+ meaning: "Slide in from bottom",
22793
+ contexts: ["sheet", "drawer"]
22794
+ },
22795
+ "slide-in-from-left": {
22796
+ keyframe: "slide-in-from-left",
22797
+ duration: { tier: "normal" },
22798
+ curve: "enter",
22799
+ meaning: "Slide in from left",
22800
+ contexts: ["sidebar", "panel"]
22801
+ },
22802
+ "slide-in-from-right": {
22803
+ keyframe: "slide-in-from-right",
22804
+ duration: { tier: "normal" },
22805
+ curve: "enter",
22806
+ meaning: "Slide in from right",
22807
+ contexts: ["sidebar", "panel"]
22808
+ },
22809
+ "slide-out-to-top": {
22810
+ keyframe: "slide-out-to-top",
22811
+ duration: { tier: "fast" },
22812
+ curve: "exit",
22813
+ meaning: "Slide out to top",
22814
+ contexts: ["dropdown-exit"]
22815
+ },
22816
+ "slide-out-to-bottom": {
22817
+ keyframe: "slide-out-to-bottom",
22818
+ duration: { tier: "fast" },
22819
+ curve: "exit",
22820
+ meaning: "Slide out to bottom",
22821
+ contexts: ["sheet-exit"]
22822
+ },
22823
+ "slide-out-to-left": {
22824
+ keyframe: "slide-out-to-left",
22825
+ duration: { tier: "fast" },
22826
+ curve: "exit",
22827
+ meaning: "Slide out to left",
22828
+ contexts: ["sidebar-close"]
22829
+ },
22830
+ "slide-out-to-right": {
22831
+ keyframe: "slide-out-to-right",
22832
+ duration: { tier: "fast" },
22833
+ curve: "exit",
22834
+ meaning: "Slide out to right",
22835
+ contexts: ["sidebar-close"]
22836
+ },
22837
+ "scale-in": {
22838
+ keyframe: "scale-in",
22839
+ duration: { tier: "normal" },
22840
+ curve: "spring-snappy",
22841
+ meaning: "Scale in with spring",
22842
+ contexts: ["modal", "popover"]
22843
+ },
22844
+ "scale-out": {
22845
+ keyframe: "scale-out",
22846
+ duration: { tier: "fast" },
22847
+ curve: "exit",
22848
+ meaning: "Scale out",
22849
+ contexts: ["modal-exit"]
22850
+ },
22851
+ spin: {
22852
+ keyframe: "spin",
22853
+ duration: { loopPeriod: "1s" },
22854
+ curve: "linear",
22855
+ iterations: "infinite",
22856
+ meaning: "Continuous spin",
22857
+ contexts: ["loading", "spinner"]
22858
+ },
22859
+ ping: {
22860
+ keyframe: "ping",
22861
+ duration: { loopPeriod: "1s" },
22862
+ curve: "enter",
22863
+ iterations: "infinite",
22864
+ meaning: "Pinging pulse",
22865
+ contexts: ["notification"]
22866
+ },
22867
+ pulse: {
22868
+ keyframe: "pulse",
22869
+ duration: { loopPeriod: "2s" },
22870
+ curve: "standard",
22871
+ iterations: "infinite",
22872
+ meaning: "Gentle pulse",
22873
+ contexts: ["skeleton", "loading"]
22874
+ },
22875
+ bounce: {
22876
+ keyframe: "bounce",
22877
+ duration: { loopPeriod: "1s" },
22878
+ curve: "standard",
22879
+ iterations: "infinite",
22880
+ meaning: "Bouncing",
22881
+ contexts: ["attention"]
22882
+ },
22883
+ "caret-blink": {
22884
+ keyframe: "caret-blink",
22885
+ duration: { loopPeriod: "1.25s" },
22886
+ curve: "enter",
22887
+ iterations: "infinite",
22888
+ meaning: "Caret blinking",
22889
+ contexts: ["input"]
22890
+ }
22891
+ };
22892
+ var DEFAULT_MOTION_COMPOSITE_PRESETS = {
22893
+ "motion-fade-in": {
22894
+ durationTier: "fast",
22895
+ curve: "enter",
22896
+ meaning: "Fade in animation preset",
22897
+ contexts: ["fade-in", "appear"]
22898
+ },
22899
+ "motion-fade-out": {
22900
+ durationTier: "fast",
22901
+ curve: "exit",
22902
+ meaning: "Fade out animation preset",
22903
+ contexts: ["fade-out", "disappear"]
22904
+ },
22905
+ "motion-slide-in": {
22906
+ durationTier: "normal",
22907
+ curve: "enter",
22908
+ meaning: "Slide in animation preset",
22909
+ contexts: ["slide-in", "panel-enter", "modal-enter"]
22910
+ },
22911
+ "motion-slide-out": {
22912
+ durationTier: "fast",
22913
+ curve: "exit",
22914
+ meaning: "Slide out animation preset",
22915
+ contexts: ["slide-out", "panel-exit", "modal-exit"]
22916
+ },
22917
+ "motion-scale-in": {
22918
+ durationTier: "normal",
22919
+ curve: "spring-snappy",
22920
+ meaning: "Scale in with spring animation",
22921
+ contexts: ["pop-in", "button-press", "emphasis"]
22922
+ }
22923
+ };
22672
22924
  var DEFAULT_MOTION_SEMANTIC_MAPPINGS = {
22673
22925
  hover: {
22674
22926
  properties: ["color", "background-color", "border-color"],
@@ -22752,11 +23004,11 @@ var DEFAULT_MOTION_SEMANTIC_MAPPINGS = {
22752
23004
  },
22753
23005
  "sheet-in": {
22754
23006
  properties: ["transform"],
22755
- durationTier: "slow",
23007
+ durationTier: "normal",
22756
23008
  curve: "spring-smooth",
22757
23009
  reducedMotion: { properties: ["opacity"], ms: 250 },
22758
23010
  category: "enter",
22759
- sizeReasoning: "A sheet is the largest spatial movement -- slow tier with the physical settle of a smooth spring, because the user must track it into place. Reduced motion becomes a cross-fade.",
23011
+ sizeReasoning: "A sheet is a large spatial movement -- normal tier with the physical settle of a smooth spring, because the user must track it into place. Reduced motion becomes a cross-fade.",
22760
23012
  meaning: "Sheet/drawer entrance.",
22761
23013
  contexts: ["sheet", "drawer", "side-panel"]
22762
23014
  },
@@ -22792,11 +23044,11 @@ var DEFAULT_MOTION_SEMANTIC_MAPPINGS = {
22792
23044
  },
22793
23045
  page: {
22794
23046
  properties: ["opacity", "transform"],
22795
- durationTier: "slow",
23047
+ durationTier: "normal",
22796
23048
  curve: "spring-smooth",
22797
23049
  reducedMotion: { properties: ["opacity"], ms: 200 },
22798
23050
  category: "enter",
22799
- sizeReasoning: "A whole-view transition -- slow tier with the physical settle of a smooth spring, because the user reorients across the largest possible distance.",
23051
+ sizeReasoning: "A whole-view transition -- normal tier with the physical settle of a smooth spring, because the user reorients across a large distance.",
22800
23052
  meaning: "Page/route transition.",
22801
23053
  contexts: ["page-transition", "route-change", "view-switch"]
22802
23054
  }
@@ -25423,14 +25675,7 @@ function tryParseUnit(cssValue, registry2 = DEFAULT_UNITS) {
25423
25675
  }
25424
25676
 
25425
25677
  // ../design-tokens/src/generators/motion.ts
25426
- var LEGACY_EASING_REMAP = {
25427
- linear: "linear",
25428
- "ease-out": "enter",
25429
- "ease-in": "exit",
25430
- "ease-in-out": "standard",
25431
- spring: "spring-snappy"
25432
- };
25433
- function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs, semanticMappings) {
25678
+ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs, semanticMappings, keyframeDefs, animationDefs, compositePresets) {
25434
25679
  const tokens = [];
25435
25680
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25436
25681
  const { baseTransitionDuration, progressionRatio } = config2;
@@ -25445,22 +25690,26 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs, sema
25445
25690
  semanticMeaning: "Legacy base transition duration. The perceptual duration scale (motion-duration-*) no longer derives from this; retained only as a reference value and delay-progression base.",
25446
25691
  usageContext: ["calculation-reference", "delay-base"],
25447
25692
  progressionSystem: progressionRatio,
25448
- description: `Base duration (${baseTransitionDuration}ms). Delay tokens use ${progressionRatio} progression (ratio ${ratioVal}); duration tiers are perceptually derived literals.`,
25693
+ description: `Base duration (${baseTransitionDuration}ms). Delay tokens use ${progressionRatio} progression (ratio ${ratioVal}); duration tiers are perceptual RANGES a designer sets within and do not derive from this base.`,
25449
25694
  generatedAt: timestamp,
25450
25695
  containerQueryAware: false,
25451
25696
  reducedMotionAware: true,
25452
25697
  userOverride: null,
25453
25698
  usagePatterns: {
25454
25699
  do: ["Reference as the delay-progression base"],
25455
- never: ["Assume the perceptual duration tiers derive from this"]
25700
+ never: [
25701
+ "Assume the perceptual duration tiers derive from this -- they are ranges a designer sets within, bounded by perception, not computed from this base"
25702
+ ]
25456
25703
  }
25457
25704
  });
25458
25705
  for (const scale2 of MOTION_DURATION_SCALE) {
25459
25706
  const def = durationDefs[scale2];
25460
25707
  if (!def) continue;
25461
25708
  const scaleIndex = MOTION_DURATION_SCALE.indexOf(scale2);
25462
- const durationMs = def.ms;
25709
+ const durationMs = def.default;
25710
+ const [rangeMin, rangeMax] = def.range;
25463
25711
  const bandNote = def.band ? ` Band: ${def.band}.` : "";
25712
+ const rangeNote = rangeMin === rangeMax ? " Fixed." : ` Range: ${rangeMin}-${rangeMax}ms.`;
25464
25713
  tokens.push({
25465
25714
  name: `motion-duration-${scale2}`,
25466
25715
  value: `${durationMs}ms`,
@@ -25471,9 +25720,9 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs, sema
25471
25720
  scalePosition: scaleIndex,
25472
25721
  motionIntent: def.motionIntent,
25473
25722
  motionDuration: durationMs,
25474
- mathRelationship: def.band ? `${durationMs}ms (perceptual: ${def.band})` : `${durationMs}ms`,
25723
+ mathRelationship: def.band ? `${durationMs}ms within ${rangeMin}-${rangeMax}ms (perceptual: ${def.band})` : `${durationMs}ms (fixed)`,
25475
25724
  dependsOn: [],
25476
- description: `Duration ${scale2}: ${durationMs}ms.${bandNote} ${def.meaning}`,
25725
+ description: `Duration ${scale2}: ${durationMs}ms.${rangeNote}${bandNote} ${def.meaning}`,
25477
25726
  generatedAt: timestamp,
25478
25727
  containerQueryAware: false,
25479
25728
  reducedMotionAware: true,
@@ -25542,384 +25791,79 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs, sema
25542
25791
  const pingScale = Math.round(ratioValue2 ** 3 * 10) / 10;
25543
25792
  const pulseOpacity = Math.round(1 / ratioValue2 ** 4 * 100) / 100;
25544
25793
  const bouncePercent = Math.round(100 / ratioValue2 ** 6);
25545
- const keyframes = [
25546
- {
25547
- name: "fade-in",
25548
- css: "from { opacity: 0; } to { opacity: 1; }",
25549
- meaning: "Fade from transparent to opaque",
25550
- contexts: ["enter", "appear", "show"]
25551
- },
25552
- {
25553
- name: "fade-out",
25554
- css: "from { opacity: 1; } to { opacity: 0; }",
25555
- meaning: "Fade from opaque to transparent",
25556
- contexts: ["exit", "disappear", "hide"]
25557
- },
25558
- {
25559
- name: "slide-in-from-top",
25560
- css: "from { transform: translateY(-100%); } to { transform: translateY(0); }",
25561
- meaning: "Slide in from above",
25562
- contexts: ["dropdown", "notification", "toast"]
25563
- },
25564
- {
25565
- name: "slide-in-from-bottom",
25566
- css: "from { transform: translateY(100%); } to { transform: translateY(0); }",
25567
- meaning: "Slide in from below",
25568
- contexts: ["sheet", "drawer", "mobile-menu"]
25569
- },
25570
- {
25571
- name: "slide-in-from-left",
25572
- css: "from { transform: translateX(-100%); } to { transform: translateX(0); }",
25573
- meaning: "Slide in from left",
25574
- contexts: ["sidebar", "panel", "drawer"]
25575
- },
25576
- {
25577
- name: "slide-in-from-right",
25578
- css: "from { transform: translateX(100%); } to { transform: translateX(0); }",
25579
- meaning: "Slide in from right",
25580
- contexts: ["sidebar", "panel", "drawer"]
25581
- },
25582
- {
25583
- name: "slide-out-to-top",
25584
- css: "from { transform: translateY(0); } to { transform: translateY(-100%); }",
25585
- meaning: "Slide out upward",
25586
- contexts: ["dropdown-exit", "notification-dismiss"]
25587
- },
25588
- {
25589
- name: "slide-out-to-bottom",
25590
- css: "from { transform: translateY(0); } to { transform: translateY(100%); }",
25591
- meaning: "Slide out downward",
25592
- contexts: ["sheet-exit", "drawer-close"]
25593
- },
25594
- {
25595
- name: "slide-out-to-left",
25596
- css: "from { transform: translateX(0); } to { transform: translateX(-100%); }",
25597
- meaning: "Slide out to left",
25598
- contexts: ["sidebar-close", "panel-exit"]
25599
- },
25600
- {
25601
- name: "slide-out-to-right",
25602
- css: "from { transform: translateX(0); } to { transform: translateX(100%); }",
25603
- meaning: "Slide out to right",
25604
- contexts: ["sidebar-close", "panel-exit"]
25605
- },
25606
- {
25607
- name: "scale-in",
25608
- css: `from { transform: scale(${scaleStart}); opacity: 0; } to { transform: scale(1); opacity: 1; }`,
25609
- meaning: "Scale up while fading in",
25610
- contexts: ["modal", "popover", "dialog"]
25611
- },
25612
- {
25613
- name: "scale-out",
25614
- css: `from { transform: scale(1); opacity: 1; } to { transform: scale(${scaleStart}); opacity: 0; }`,
25615
- meaning: "Scale down while fading out",
25616
- contexts: ["modal-exit", "popover-close"]
25617
- },
25618
- {
25619
- name: "spin",
25620
- css: "from { transform: rotate(0deg); } to { transform: rotate(360deg); }",
25621
- meaning: "Continuous rotation",
25622
- contexts: ["loading", "spinner", "refresh"]
25623
- },
25624
- {
25625
- name: "ping",
25626
- css: `75%, 100% { transform: scale(${pingScale}); opacity: 0; }`,
25627
- meaning: "Expanding pulse that fades out",
25628
- contexts: ["notification-badge", "attention", "pulse"]
25629
- },
25630
- {
25631
- name: "pulse",
25632
- css: `0%, 100% { opacity: 1; } 50% { opacity: ${pulseOpacity}; }`,
25633
- meaning: "Gentle opacity pulse",
25634
- contexts: ["skeleton", "loading-placeholder"]
25635
- },
25636
- {
25637
- name: "bounce",
25638
- css: `0%, 100% { transform: translateY(-${bouncePercent}%); animation-timing-function: cubic-bezier(0.8, 0, 1, 1); } 50% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }`,
25639
- meaning: "Bouncing motion",
25640
- contexts: ["attention", "scroll-indicator"]
25641
- },
25642
- {
25643
- name: "accordion-down",
25644
- css: "from { height: 0; } to { height: var(--radix-accordion-content-height); }",
25645
- meaning: "Expand accordion content",
25646
- contexts: ["accordion", "collapsible", "expand"]
25647
- },
25648
- {
25649
- name: "accordion-up",
25650
- css: "from { height: var(--radix-accordion-content-height); } to { height: 0; }",
25651
- meaning: "Collapse accordion content",
25652
- contexts: ["accordion", "collapsible", "collapse"]
25653
- },
25654
- {
25655
- name: "caret-blink",
25656
- css: "0%, 70%, 100% { opacity: 1; } 20%, 50% { opacity: 0; }",
25657
- meaning: "Text cursor blinking",
25658
- contexts: ["input-caret", "text-cursor"]
25659
- }
25660
- ];
25661
- for (const kf of keyframes) {
25794
+ const keyframeContext = {
25795
+ scaleStart,
25796
+ pingScale,
25797
+ pulseOpacity,
25798
+ bouncePercent
25799
+ };
25800
+ for (const [name, kf] of Object.entries(keyframeDefs)) {
25662
25801
  tokens.push({
25663
- name: `motion-keyframe-${kf.name}`,
25664
- value: kf.css,
25802
+ name: `motion-keyframe-${name}`,
25803
+ value: kf.css(keyframeContext),
25665
25804
  category: "motion",
25666
25805
  namespace: "motion",
25667
25806
  semanticMeaning: kf.meaning,
25668
25807
  usageContext: kf.contexts,
25669
- keyframeName: kf.name,
25670
- description: `Keyframe ${kf.name}: ${kf.meaning}`,
25808
+ keyframeName: name,
25809
+ description: `Keyframe ${name}: ${kf.meaning}`,
25671
25810
  generatedAt: timestamp,
25672
25811
  containerQueryAware: false,
25673
25812
  reducedMotionAware: true,
25674
25813
  userOverride: null
25675
25814
  });
25676
25815
  }
25677
- const animations = [
25678
- {
25679
- name: "fade-in",
25680
- keyframe: "fade-in",
25681
- duration: "fast",
25682
- easing: "ease-out",
25683
- meaning: "Fade in animation",
25684
- contexts: ["enter", "appear"]
25685
- },
25686
- {
25687
- name: "fade-out",
25688
- keyframe: "fade-out",
25689
- duration: "fast",
25690
- easing: "ease-in",
25691
- meaning: "Fade out animation",
25692
- contexts: ["exit", "disappear"]
25693
- },
25694
- {
25695
- name: "slide-in-from-top",
25696
- keyframe: "slide-in-from-top",
25697
- duration: "normal",
25698
- easing: "ease-out",
25699
- meaning: "Slide in from top",
25700
- contexts: ["dropdown", "notification"]
25701
- },
25702
- {
25703
- name: "slide-in-from-bottom",
25704
- keyframe: "slide-in-from-bottom",
25705
- duration: "normal",
25706
- easing: "ease-out",
25707
- meaning: "Slide in from bottom",
25708
- contexts: ["sheet", "drawer"]
25709
- },
25710
- {
25711
- name: "slide-in-from-left",
25712
- keyframe: "slide-in-from-left",
25713
- duration: "normal",
25714
- easing: "ease-out",
25715
- meaning: "Slide in from left",
25716
- contexts: ["sidebar", "panel"]
25717
- },
25718
- {
25719
- name: "slide-in-from-right",
25720
- keyframe: "slide-in-from-right",
25721
- duration: "normal",
25722
- easing: "ease-out",
25723
- meaning: "Slide in from right",
25724
- contexts: ["sidebar", "panel"]
25725
- },
25726
- {
25727
- name: "slide-out-to-top",
25728
- keyframe: "slide-out-to-top",
25729
- duration: "fast",
25730
- easing: "ease-in",
25731
- meaning: "Slide out to top",
25732
- contexts: ["dropdown-exit"]
25733
- },
25734
- {
25735
- name: "slide-out-to-bottom",
25736
- keyframe: "slide-out-to-bottom",
25737
- duration: "fast",
25738
- easing: "ease-in",
25739
- meaning: "Slide out to bottom",
25740
- contexts: ["sheet-exit"]
25741
- },
25742
- {
25743
- name: "slide-out-to-left",
25744
- keyframe: "slide-out-to-left",
25745
- duration: "fast",
25746
- easing: "ease-in",
25747
- meaning: "Slide out to left",
25748
- contexts: ["sidebar-close"]
25749
- },
25750
- {
25751
- name: "slide-out-to-right",
25752
- keyframe: "slide-out-to-right",
25753
- duration: "fast",
25754
- easing: "ease-in",
25755
- meaning: "Slide out to right",
25756
- contexts: ["sidebar-close"]
25757
- },
25758
- {
25759
- name: "scale-in",
25760
- keyframe: "scale-in",
25761
- duration: "normal",
25762
- easing: "spring",
25763
- meaning: "Scale in with spring",
25764
- contexts: ["modal", "popover"]
25765
- },
25766
- {
25767
- name: "scale-out",
25768
- keyframe: "scale-out",
25769
- duration: "fast",
25770
- easing: "ease-in",
25771
- meaning: "Scale out",
25772
- contexts: ["modal-exit"]
25773
- },
25774
- {
25775
- name: "spin",
25776
- keyframe: "spin",
25777
- duration: "1s",
25778
- easing: "linear",
25779
- iterations: "infinite",
25780
- meaning: "Continuous spin",
25781
- contexts: ["loading", "spinner"]
25782
- },
25783
- {
25784
- name: "ping",
25785
- keyframe: "ping",
25786
- duration: "1s",
25787
- easing: "ease-out",
25788
- iterations: "infinite",
25789
- meaning: "Pinging pulse",
25790
- contexts: ["notification"]
25791
- },
25792
- {
25793
- name: "pulse",
25794
- keyframe: "pulse",
25795
- duration: "2s",
25796
- easing: "ease-in-out",
25797
- iterations: "infinite",
25798
- meaning: "Gentle pulse",
25799
- contexts: ["skeleton", "loading"]
25800
- },
25801
- {
25802
- name: "bounce",
25803
- keyframe: "bounce",
25804
- duration: "1s",
25805
- easing: "ease-in-out",
25806
- iterations: "infinite",
25807
- meaning: "Bouncing",
25808
- contexts: ["attention"]
25809
- },
25810
- {
25811
- name: "accordion-down",
25812
- keyframe: "accordion-down",
25813
- duration: "normal",
25814
- easing: "ease-out",
25815
- meaning: "Accordion expand",
25816
- contexts: ["accordion", "collapsible"]
25817
- },
25818
- {
25819
- name: "accordion-up",
25820
- keyframe: "accordion-up",
25821
- duration: "normal",
25822
- easing: "ease-out",
25823
- meaning: "Accordion collapse",
25824
- contexts: ["accordion", "collapsible"]
25825
- },
25826
- {
25827
- name: "caret-blink",
25828
- keyframe: "caret-blink",
25829
- duration: "1.25s",
25830
- easing: "ease-out",
25831
- iterations: "infinite",
25832
- meaning: "Caret blinking",
25833
- contexts: ["input"]
25834
- }
25835
- ];
25836
- for (const anim of animations) {
25816
+ for (const [name, anim] of Object.entries(animationDefs)) {
25837
25817
  let durationValue;
25838
25818
  let durationRef;
25839
- if (anim.duration.endsWith("s") || anim.duration.endsWith("ms")) {
25840
- durationValue = anim.duration;
25841
- durationRef = anim.duration;
25819
+ let durationDependency;
25820
+ if ("loopPeriod" in anim.duration) {
25821
+ durationValue = anim.duration.loopPeriod;
25822
+ durationRef = anim.duration.loopPeriod;
25823
+ durationDependency = [];
25842
25824
  } else {
25843
- const durationDef = durationDefs[anim.duration];
25825
+ const durationDef = durationDefs[anim.duration.tier];
25844
25826
  if (!durationDef) continue;
25845
- durationValue = `${durationDef.ms}ms`;
25846
- durationRef = `var(--motion-duration-${anim.duration})`;
25827
+ durationValue = `${durationDef.default}ms`;
25828
+ durationRef = `var(--motion-duration-${anim.duration.tier})`;
25829
+ durationDependency = [`motion-duration-${anim.duration.tier}`];
25847
25830
  }
25848
- const easingKey = LEGACY_EASING_REMAP[anim.easing] ?? anim.easing;
25849
- const easingDef = easingDefs[easingKey];
25831
+ const easingDef = easingDefs[anim.curve];
25850
25832
  if (!easingDef) continue;
25851
- const easingRef = `var(--motion-easing-${easingKey})`;
25833
+ const easingRef = `var(--motion-easing-${anim.curve})`;
25852
25834
  const iterations = anim.iterations || "";
25853
25835
  const animValue = iterations ? `${anim.keyframe} ${durationRef} ${easingRef} ${iterations}` : `${anim.keyframe} ${durationRef} ${easingRef}`;
25854
25836
  tokens.push({
25855
- name: `motion-animation-${anim.name}`,
25837
+ name: `motion-animation-${name}`,
25856
25838
  value: animValue,
25857
25839
  category: "motion",
25858
25840
  namespace: "motion",
25859
25841
  semanticMeaning: anim.meaning,
25860
25842
  usageContext: anim.contexts,
25861
- animationName: anim.name,
25843
+ animationName: name,
25862
25844
  keyframeName: anim.keyframe,
25863
25845
  animationDuration: durationValue,
25864
25846
  animationEasing: easingDef.css,
25865
25847
  animationIterations: anim.iterations || "1",
25866
25848
  dependsOn: [
25867
25849
  `motion-keyframe-${anim.keyframe}`,
25868
- ...anim.duration.endsWith("s") || anim.duration.endsWith("ms") ? [] : [`motion-duration-${anim.duration}`],
25869
- `motion-easing-${easingKey}`
25850
+ ...durationDependency,
25851
+ `motion-easing-${anim.curve}`
25870
25852
  ],
25871
- description: `Animation ${anim.name}: ${anim.meaning}`,
25853
+ description: `Animation ${name}: ${anim.meaning}`,
25872
25854
  generatedAt: timestamp,
25873
25855
  containerQueryAware: false,
25874
25856
  reducedMotionAware: true,
25875
25857
  userOverride: null
25876
25858
  });
25877
25859
  }
25878
- const composites2 = [
25879
- {
25880
- name: "motion-fade-in",
25881
- duration: "fast",
25882
- easing: "ease-out",
25883
- meaning: "Fade in animation preset",
25884
- contexts: ["fade-in", "appear"]
25885
- },
25886
- {
25887
- name: "motion-fade-out",
25888
- duration: "fast",
25889
- easing: "ease-in",
25890
- meaning: "Fade out animation preset",
25891
- contexts: ["fade-out", "disappear"]
25892
- },
25893
- {
25894
- name: "motion-slide-in",
25895
- duration: "normal",
25896
- easing: "ease-out",
25897
- meaning: "Slide in animation preset",
25898
- contexts: ["slide-in", "panel-enter", "modal-enter"]
25899
- },
25900
- {
25901
- name: "motion-slide-out",
25902
- duration: "fast",
25903
- easing: "ease-in",
25904
- meaning: "Slide out animation preset",
25905
- contexts: ["slide-out", "panel-exit", "modal-exit"]
25906
- },
25907
- {
25908
- name: "motion-scale-in",
25909
- duration: "normal",
25910
- easing: "spring",
25911
- meaning: "Scale in with spring animation",
25912
- contexts: ["pop-in", "button-press", "emphasis"]
25913
- }
25914
- ];
25915
- for (const comp of composites2) {
25916
- const durationDef = durationDefs[comp.duration];
25917
- const easingKey = LEGACY_EASING_REMAP[comp.easing] ?? comp.easing;
25918
- const easingDef = easingDefs[easingKey];
25860
+ for (const [name, comp] of Object.entries(compositePresets)) {
25861
+ const durationDef = durationDefs[comp.durationTier];
25862
+ const easingDef = easingDefs[comp.curve];
25919
25863
  if (!durationDef || !easingDef) continue;
25920
- const durationMs = durationDef.ms;
25864
+ const durationMs = durationDef.default;
25921
25865
  tokens.push({
25922
- name: comp.name,
25866
+ name,
25923
25867
  value: `${durationMs}ms ${easingDef.css}`,
25924
25868
  category: "motion",
25925
25869
  namespace: "motion",
@@ -25927,9 +25871,9 @@ function generateMotionTokens(config2, durationDefs, easingDefs, delayDefs, sema
25927
25871
  usageContext: comp.contexts,
25928
25872
  motionDuration: durationMs,
25929
25873
  easingCurve: easingDef.curve,
25930
- easingName: easingKey,
25931
- dependsOn: [`motion-duration-${comp.duration}`, `motion-easing-${easingKey}`],
25932
- description: `${comp.meaning}. Combines ${comp.duration} duration with ${easingKey} easing.`,
25874
+ easingName: comp.curve,
25875
+ dependsOn: [`motion-duration-${comp.durationTier}`, `motion-easing-${comp.curve}`],
25876
+ description: `${comp.meaning}. Combines ${comp.durationTier} duration with ${comp.curve} easing.`,
25933
25877
  generatedAt: timestamp,
25934
25878
  containerQueryAware: false,
25935
25879
  reducedMotionAware: true,
@@ -26885,7 +26829,10 @@ function createGeneratorDefs(colorPaletteBases) {
26885
26829
  DEFAULT_DURATION_DEFINITIONS,
26886
26830
  DEFAULT_EASING_DEFINITIONS,
26887
26831
  DEFAULT_DELAY_DEFINITIONS,
26888
- DEFAULT_MOTION_SEMANTIC_MAPPINGS
26832
+ DEFAULT_MOTION_SEMANTIC_MAPPINGS,
26833
+ DEFAULT_KEYFRAME_DEFINITIONS,
26834
+ DEFAULT_ANIMATION_DEFINITIONS,
26835
+ DEFAULT_MOTION_COMPOSITE_PRESETS
26889
26836
  )
26890
26837
  },
26891
26838
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.77",
3
+ "version": "0.0.78",
4
4
  "description": "Design Intelligence CLI. Scaffold tokens, import existing shadcn/Tailwind v4 sources, add components, and serve an MCP server so AI agents read decisions instead of guessing.",
5
5
  "homepage": "https://rafters.studio",
6
6
  "license": "MIT",