Category 01

Welcome Screen Animations

Hero title
Hello.
Welcome to Orbuculum.
Any large reveal moment
blurIn
Heavy blur dissolve with scale. The signature dramatic entrance — used for hero text, subtitles, and any large reveal moment. Vary duration for hierarchy: hero 2.8s, subtitle 2s.
2–2.8s cubic-bezier(0.16, 1, 0.3, 1)
Replaces: heroIn, contentIn
Keyframes
@keyframes blurIn {
  0% {
    opacity: 0;
    filter: blur(16px);
    transform: scale(0.96);
  }
  100% {
    opacity: 1;
    filter: blur(0px);
    transform: scale(1);
  }
}
Text block
Enjoy 3 free months of the Ultimate plan
1000
Accounts
10 000
Transactions
Integrations
Get Started
fadeIn
Soft blur + scale entrance with subtle overshoot at 70%. The universal element entrance — used for text blocks, stats, buttons, cards. Vary duration for hierarchy: 0.8–1s.
0.8–1s cubic-bezier(0.16, 1, 0.3, 1)
Replaces: fadeOnly, statsIn, buttonIn
Keyframes
@keyframes fadeIn {
  0% {
    opacity: 0;
    filter: blur(8px);
    transform: scale(0.95);
  }
  70% {
    transform: scale(1.02);
  }
  100% {
    opacity: 1;
    filter: blur(0px);
    transform: scale(1);
  }
}
Hello.
welcomeOut
Welcome screen exit animation. Scales up slightly while blurring out, creating a dreamy departure.
0.8s ease
Keyframes
@keyframes welcomeOut {
  0% {
    opacity: 1;
    transform: scale(1);
    filter: blur(0px);
  }
  100% {
    opacity: 0;
    transform: scale(1.03);
    filter: blur(10px);
  }
}
Category 02

Chat Animations

O
Hi! Let's start by creating your workspace.
messageIn
Bot message slides up from below with slight scale and blur. The primary chat entrance animation.
0.6s cubic-bezier(0.16, 1, 0.3, 1)
Keyframes
@keyframes messageIn {
  0% {
    opacity: 0;
    transform: translateY(16px) scale(0.98);
    filter: blur(3px);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0px);
  }
}
My Company
userMessageIn
User message slides in from the right with subtle scale. Mirrors the bot animation direction for natural conversation flow.
0.5s cubic-bezier(0.16, 1, 0.3, 1)
Keyframes
@keyframes userMessageIn {
  0% {
    opacity: 0;
    transform: translateX(12px) scale(0.98);
  }
  100% {
    opacity: 1;
    transform: translateX(0) scale(1);
  }
}
O
typingDot
Three dots bounce with staggered delays (0s, 0.2s, 0.4s). Creates a familiar "someone is typing" indicator.
1.4s infinite · stagger: 0.2s
Keyframes
@keyframes typingDot {
  0%, 60%, 100% {
    opacity: 0.3;
    transform: translateY(0);
  }
  30% {
    opacity: 1;
    transform: translateY(-4px);
  }
}
/* Stagger delays */
span:nth-child(2) { animation-delay: 0.2s; }
span:nth-child(3) { animation-delay: 0.4s; }
Welcome! Let's get you set up.
Sounds good!
Type your answer...
chatIn
Chat container fades in and slides up. Used when transitioning from the welcome screen to the chat interface.
0.6s ease
Keyframes
@keyframes chatIn {
  0% {
    opacity: 0;
    transform: translateY(20px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}
Category 03

UI Element Animations

My Company
tabSlideIn
Header tab slides in from the right with blur. Used when workspace tab appears after naming.
0.6s cubic-bezier(0.16, 1, 0.3, 1)
Keyframes
@keyframes tabSlideIn {
  0% {
    opacity: 0;
    transform: translateX(20px);
    filter: blur(4px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
    filter: blur(0);
  }
}
Accounts
Revenue
Costs
fadeSlideIn
Fade + slide up + slight scale. Used for categorization cards appearing in sequence during data analysis.
0.5s cubic-bezier(0.16, 1, 0.3, 1)
Keyframes
@keyframes fadeSlideIn {
  0% {
    opacity: 0;
    transform: translateY(12px) scale(0.97);
  }
  100% {
    opacity: 0.7;
    transform: translateY(0) scale(1);
  }
}
3 insights found
insightPop
Pop-in scale animation for insight icons. Scales from 0 through 1.5x overshoot back to 1x for a punchy entrance.
0.6s cubic-bezier(0.16, 1, 0.3, 1)
Keyframes
@keyframes insightPop {
  0%  { transform: scale(0);   opacity: 0; }
  50% { transform: scale(1.5); }
  100%{ transform: scale(1);   opacity: 1; }
}
32px
12px
spin
Standard loading spinner — border circle with accent top. Used for file uploads, app connections, data processing. 12px inline, 32px standalone.
0.8s linear infinite
CSS
/* Standard spinner */
.file-loader {
  width: 12px;
  height: 12px;
  border: 2px solid #2aa7ee;
  border-bottom-color: rgba(42, 167, 238, 0.2);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
progressBar
Width animates from 0% to 100%. Used during data analysis to show overall processing progress.
2s cubic-bezier(0.25, 0.1, 0.25, 1)
Keyframes
@keyframes progressBar {
  from { width: 0; }
  to   { width: 100%; }
}
Category 04

Micro-interactions

Action button hover
Buttons lift up 1px and fill with color on hover. Provides clear affordance without being heavy-handed.
transition: all 0.2s
CSS Transition
.action-btn {
  transition: all 0.2s;
}
.action-btn:hover {
  background: var(--blue-primary);
  color: white;
  transform: translateY(-1px);
}
Avatar hover
Avatars scale to 1.15x on hover, making selection feel tactile. Same effect is used to mark the selected state.
transition: transform 0.2s
CSS Transition
.avatar-circle {
  transition: all 0.2s;
}
.avatar-circle:hover {
  transform: scale(1.15);
}
UAH ×
EUR ×
PLN ×
GBP ×
CZK ×
Currency chip remove
Click to remove a chip. It fades to 0 opacity and scales down to 0.8 before being removed from the DOM.
0.25s ease
CSS Transition
.currency-chip.removing {
  opacity: 0;
  transform: scale(0.8);
  transition: all 0.25s ease;
}
Drop files here or upload from computer
Drop zone hover
Border color changes to blue and subtle background tint appears on hover, signaling the interactive drop area.
transition: all 0.2s
CSS Transition
.drop-zone {
  border: 1px dashed var(--text-dimmed);
  transition: all 0.2s;
}
.drop-zone:hover {
  border-color: var(--blue-primary);
  background: rgba(42,167,238,0.05);
}
Smooth scroll (easeInOutCubic)
Custom scroll implementation uses easeInOutCubic for buttery smooth programmatic scrolling between welcome and chat sections.
JS: requestAnimationFrame, 1200ms
JavaScript
function easeInOutCubic(t) {
  return t < 0.5
    ? 4 * t * t * t
    : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
// Used in animateScroll(target, 1200)
0
Transactions
0
Accounts
0
Categories
Stats counter
Numbers animate from 0 to target using easeOutCubic. Gives the data analysis step a satisfying "counting up" feel.
JS: easeOutCubic, ~2s
JavaScript
function animateStats(from, to, dur) {
  const start = performance.now();
  function tick(now) {
    const p = Math.min((now - start) / dur, 1);
    const eased = 1 - Math.pow(1 - p, 3);
    // easeOutCubic
    el.textContent = Math.round(
      from + (to - from) * eased
    );
    if (p < 1) requestAnimationFrame(tick);
  }
  requestAnimationFrame(tick);
}
Gradient border rotate (hover)
Active choice button has a static main-gradient border. On hover the gradient angle rotates a full 360° (94°→454°) so start and end frames match — no jump on mouseout. Both buttons also scale to 1.02x on hover.
3s linear infinite · scale 0.6s cubic-bezier(0.22, 1, 0.36, 1)
CSS
@property --grad-angle {
  syntax: '<angle>';
  initial-value: 94deg;
  inherits: false;
}

@keyframes spin-main-gradient {
  from { --grad-angle: 94deg; }
  to   { --grad-angle: 454deg; }
}

.choice-btn.is-active {
  border: 1px solid transparent;
  background:
    linear-gradient(var(--bg), var(--bg)) padding-box,
    linear-gradient(var(--grad-angle), #00f0ff 0%, #1da5f1 100%) border-box;
}

.choice-btn.is-active:hover {
  animation: spin-main-gradient 3s linear infinite;
}

.choice-btn:hover {
  transform: scale(1.02);
}
Unlocking padlock
Symbolises the Ultimate plan unlocking on the trial-info screen. Shackle lifts (translateY −4px), then tilts open on the vertical Y-axis (rotateY 45°) with real perspective — the visible width compresses through ~30% without looking like a flat 2D collapse. Body gives a soft pulse (scale 1.06) at the moment the shackle clears. SVG overflow: visible on the parent prevents the rotating shackle from being clipped by the viewBox.
1.4s cubic-bezier(0.22, 1, 0.36, 1) · 0.3s delay (prod) · looped here for preview
CSS
.welcome-lock {
  perspective: 600px;
  transform-style: preserve-3d;
  overflow: visible;
}

.welcome-lock .lock-shackle {
  transform-origin: 100% 50%;
  transform-box: fill-box;
}

@keyframes lockShackleOpen {
  0%   { transform: translateY(0) rotateY(0); }
  30%  { transform: translateY(-4px) rotateY(0); }
  100% { transform: translateY(-4px) rotateY(45deg); }
}

@keyframes lockBodyPulse {
  0%   { transform: scale(1); }
  30%  { transform: scale(1); }
  45%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}

.welcome-lock.animate-in .lock-shackle {
  animation: lockShackleOpen 1.4s cubic-bezier(0.22, 1, 0.36, 1) 0.3s forwards;
}

.welcome-lock.animate-in .lock-body {
  animation: lockBodyPulse 1.4s cubic-bezier(0.22, 1, 0.36, 1) 0.3s forwards;
}