Interaction Patterns Documentation

40 Days of Existential Threats - Interactive Experience Collection


Design System Overview

Core Philosophy

The interaction design for “40 Days of Existential Threats” must create a cohesive, immersive journey that feels like a single unified experience rather than 40 separate applications. All interactions should feel intentional, purposeful, and contribute to the dark, luminous aesthetic with bioluminescent accents.

Timing Philosophy

  • Fast (100-150ms): Micro-interactions, state changes that need to feel instant
  • Standard (200-300ms): Most UI transitions, hover states, reveals
  • Dramatic (400-600ms): Entrance animations, surprise reveals, page transitions
  • Ambient (800-1500ms): Background animations, subtle glow pulses

Easing Library

:root {
  /* Standard easings */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in: cubic-bezier(0.7, 0, 0.84, 0);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  
  /* Dramatic easings */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-dramatic: cubic-bezier(0.87, 0, 0.13, 1);
  --ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
  
  /* Subtle easings */
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-gentle: cubic-bezier(0.25, 0.1, 0.25, 1);
}

Color Tokens for Interactions

:root {
  /* Bioluminescent accent colors */
  --glow-cyan: rgba(0, 255, 255, 0.6);
  --glow-magenta: rgba(255, 0, 255, 0.6);
  --glow-amber: rgba(255, 191, 0, 0.6);
  --glow-danger: rgba(255, 50, 50, 0.6);
  
  /* Surface colors */
  --surface-dark: #0a0a0f;
  --surface-elevated: #12121a;
  --surface-card: #1a1a24;
  
  /* Text colors */
  --text-primary: rgba(255, 255, 255, 0.95);
  --text-secondary: rgba(255, 255, 255, 0.7);
  --text-muted: rgba(255, 255, 255, 0.5);
}

1. Button States & Behaviors

Primary Button

Visual Specification

.btn-primary {
  /* Default State */
  background: linear-gradient(135deg, #00d4ff 0%, #0099cc 100%);
  color: #0a0a0f;
  padding: 14px 28px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 16px;
  border: none;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  
  /* Glow effect */
  box-shadow: 
    0 0 0 0 transparent,
    0 4px 20px rgba(0, 212, 255, 0.3);
  
  /* Transition setup */
  transition: 
    transform 150ms var(--ease-out),
    box-shadow 200ms var(--ease-smooth),
    background 200ms var(--ease-smooth);
}

State Definitions

Default State

  • Background: Gradient from cyan (#00d4ff) to darker cyan (#0099cc)
  • Text: Dark (#0a0a0f) for contrast
  • Shadow: Subtle glow (0 4px 20px rgba(0, 212, 255, 0.3))
  • Scale: 1.0

Hover State

.btn-primary:hover {
  transform: scale(1.03);
  box-shadow: 
    0 0 30px rgba(0, 212, 255, 0.5),
    0 8px 30px rgba(0, 212, 255, 0.4);
}
  • Timing: 200ms
  • Easing: var(—ease-out)
  • Enhanced glow effect
  • Subtle scale increase (1.03)

Active/Pressed State

.btn-primary:active {
  transform: scale(0.97);
  box-shadow: 
    0 0 15px rgba(0, 212, 255, 0.4),
    inset 0 2px 8px rgba(0, 0, 0, 0.3);
}
  • Timing: 100ms
  • Easing: var(—ease-in)
  • Scale decrease (0.97)
  • Inset shadow for pressed feel

Focus State

.btn-primary:focus-visible {
  outline: none;
  box-shadow: 
    0 0 0 3px rgba(0, 212, 255, 0.3),
    0 0 30px rgba(0, 212, 255, 0.5);
}
  • Visible focus ring for accessibility
  • 3px outline with 30% opacity
  • Maintains hover glow

Disabled State

.btn-primary:disabled {
  background: linear-gradient(135deg, #3a3a4a 0%, #2a2a3a 100%);
  color: rgba(255, 255, 255, 0.3);
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}
  • Muted colors
  • No hover effects
  • Not-allowed cursor

Loading State

.btn-primary.loading {
  color: transparent;
  pointer-events: none;
}
 
.btn-primary.loading::after {
  content: '';
  position: absolute;
  width: 20px;
  height: 20px;
  border: 2px solid transparent;
  border-top-color: #0a0a0f;
  border-radius: 50%;
  animation: btn-spin 0.8s linear infinite;
}
 
@keyframes btn-spin {
  to { transform: rotate(360deg); }
}
  • Text hidden, spinner visible
  • Spinner: 20px, 2px border
  • Animation: 0.8s linear infinite

Secondary Button

.btn-secondary {
  background: transparent;
  color: #00d4ff;
  padding: 14px 28px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 16px;
  border: 2px solid rgba(0, 212, 255, 0.5);
  cursor: pointer;
  
  transition: 
    background 200ms var(--ease-smooth),
    border-color 200ms var(--ease-smooth),
    box-shadow 200ms var(--ease-smooth),
    transform 150ms var(--ease-out);
}
 
.btn-secondary:hover {
  background: rgba(0, 212, 255, 0.1);
  border-color: rgba(0, 212, 255, 0.8);
  box-shadow: 0 0 20px rgba(0, 212, 255, 0.3);
  transform: scale(1.02);
}
 
.btn-secondary:active {
  transform: scale(0.98);
  background: rgba(0, 212, 255, 0.15);
}

Ghost Button

.btn-ghost {
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  padding: 12px 20px;
  border-radius: 6px;
  font-weight: 500;
  font-size: 14px;
  border: none;
  cursor: pointer;
  
  transition: 
    background 150ms var(--ease-smooth),
    color 150ms var(--ease-smooth),
    transform 100ms var(--ease-out);
}
 
.btn-ghost:hover {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.95);
}
 
.btn-ghost:active {
  transform: scale(0.98);
  background: rgba(255, 255, 255, 0.12);
}

Danger Button

.btn-danger {
  background: linear-gradient(135deg, #ff4444 0%, #cc0000 100%);
  color: #ffffff;
  padding: 14px 28px;
  border-radius: 8px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  
  box-shadow: 0 4px 20px rgba(255, 68, 68, 0.3);
  
  transition: 
    transform 150ms var(--ease-out),
    box-shadow 200ms var(--ease-smooth);
}
 
.btn-danger:hover {
  transform: scale(1.03);
  box-shadow: 
    0 0 30px rgba(255, 68, 68, 0.5),
    0 8px 30px rgba(255, 68, 68, 0.4);
}
 
.btn-danger:active {
  transform: scale(0.97);
}

Icon Button

.btn-icon {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
  border: none;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  
  transition: 
    background 200ms var(--ease-smooth),
    color 200ms var(--ease-smooth),
    transform 150ms var(--ease-spring),
    box-shadow 200ms var(--ease-smooth);
}
 
.btn-icon:hover {
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.95);
  transform: scale(1.1);
}
 
.btn-icon:active {
  transform: scale(0.95);
}
 
.btn-icon:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px rgba(0, 212, 255, 0.5);
}

Button Group Patterns

.btn-group {
  display: flex;
  gap: 12px;
}
 
.btn-group .btn {
  /* Stagger animation for group */
  animation: btn-enter 300ms var(--ease-out) backwards;
}
 
.btn-group .btn:nth-child(1) { animation-delay: 0ms; }
.btn-group .btn:nth-child(2) { animation-delay: 50ms; }
.btn-group .btn:nth-child(3) { animation-delay: 100ms; }
 
@keyframes btn-enter {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

2. Card Layouts & Interactions

Static Card (Information Display)

.card-static {
  background: var(--surface-card);
  border-radius: 12px;
  padding: 24px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  
  /* Subtle ambient glow */
  box-shadow: 
    0 4px 20px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.03);
}

Hover Card (Elevated on Interaction)

.card-hover {
  background: var(--surface-card);
  border-radius: 12px;
  padding: 24px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  cursor: default;
  
  box-shadow: 
    0 4px 20px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.03);
  
  transition: 
    transform 300ms var(--ease-out),
    box-shadow 300ms var(--ease-smooth),
    border-color 300ms var(--ease-smooth);
}
 
.card-hover:hover {
  transform: translateY(-4px);
  box-shadow: 
    0 12px 40px rgba(0, 0, 0, 0.4),
    0 0 30px rgba(0, 212, 255, 0.1);
  border-color: rgba(0, 212, 255, 0.2);
}

Clickable Card

.card-clickable {
  background: var(--surface-card);
  border-radius: 12px;
  padding: 24px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  
  box-shadow: 
    0 4px 20px rgba(0, 0, 0, 0.3);
  
  transition: 
    transform 200ms var(--ease-out),
    box-shadow 300ms var(--ease-smooth),
    border-color 200ms var(--ease-smooth);
}
 
.card-clickable:hover {
  transform: translateY(-2px) scale(1.01);
  box-shadow: 
    0 8px 30px rgba(0, 0, 0, 0.4),
    0 0 40px rgba(0, 212, 255, 0.15);
  border-color: rgba(0, 212, 255, 0.3);
}
 
.card-clickable:active {
  transform: translateY(0) scale(0.99);
}
 
/* Ripple effect on click */
.card-clickable::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  border-radius: 50%;
  background: rgba(0, 212, 255, 0.3);
  transform: translate(-50%, -50%);
  transition: width 400ms var(--ease-out), height 400ms var(--ease-out), opacity 400ms;
  opacity: 0;
}
 
.card-clickable:active::after {
  width: 300px;
  height: 300px;
  opacity: 1;
  transition: 0ms;
}

Card with Content Reveal

.card-reveal {
  background: var(--surface-card);
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.06);
}
 
.card-reveal .card-header {
  padding: 20px 24px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
 
.card-reveal .card-content {
  max-height: 0;
  overflow: hidden;
  padding: 0 24px;
  
  transition: 
    max-height 400ms var(--ease-out),
    padding 400ms var(--ease-out),
    opacity 300ms var(--ease-smooth);
  opacity: 0;
}
 
.card-reveal.expanded .card-content {
  max-height: 500px;
  padding: 0 24px 24px;
  opacity: 1;
}
 
.card-reveal .expand-icon {
  transition: transform 300ms var(--ease-spring);
}
 
.card-reveal.expanded .expand-icon {
  transform: rotate(180deg);
}

Card Grid with Stagger Animation

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
}
 
.card-grid .card {
  animation: card-enter 500ms var(--ease-out) backwards;
}
 
/* Stagger delays for up to 12 cards */
.card-grid .card:nth-child(1) { animation-delay: 0ms; }
.card-grid .card:nth-child(2) { animation-delay: 50ms; }
.card-grid .card:nth-child(3) { animation-delay: 100ms; }
.card-grid .card:nth-child(4) { animation-delay: 150ms; }
.card-grid .card:nth-child(5) { animation-delay: 200ms; }
.card-grid .card:nth-child(6) { animation-delay: 250ms; }
.card-grid .card:nth-child(7) { animation-delay: 300ms; }
.card-grid .card:nth-child(8) { animation-delay: 350ms; }
.card-grid .card:nth-child(9) { animation-delay: 400ms; }
.card-grid .card:nth-child(10) { animation-delay: 450ms; }
.card-grid .card:nth-child(11) { animation-delay: 500ms; }
.card-grid .card:nth-child(12) { animation-delay: 550ms; }
 
@keyframes card-enter {
  from {
    opacity: 0;
    transform: translateY(30px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

Day Card (Specific to 40 Days)

.day-card {
  background: var(--surface-card);
  border-radius: 16px;
  padding: 24px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  
  transition: 
    transform 300ms var(--ease-out),
    box-shadow 400ms var(--ease-smooth),
    border-color 300ms var(--ease-smooth);
}
 
.day-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, #00d4ff, #ff00ff);
  opacity: 0;
  transition: opacity 300ms var(--ease-smooth);
}
 
.day-card:hover::before {
  opacity: 1;
}
 
.day-card:hover {
  transform: translateY(-6px);
  box-shadow: 
    0 16px 50px rgba(0, 0, 0, 0.5),
    0 0 50px rgba(0, 212, 255, 0.15);
  border-color: rgba(0, 212, 255, 0.25);
}
 
.day-card.locked {
  opacity: 0.5;
  cursor: not-allowed;
}
 
.day-card.locked:hover {
  transform: none;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
 
.day-card.completed::after {
  content: '✓';
  position: absolute;
  top: 16px;
  right: 16px;
  width: 28px;
  height: 28px;
  background: linear-gradient(135deg, #00d4ff, #0099cc);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: #0a0a0f;
  animation: check-pop 400ms var(--ease-spring);
}
 
@keyframes check-pop {
  0% { transform: scale(0); }
  70% { transform: scale(1.2); }
  100% { transform: scale(1); }
}

3. Slider/Range Behaviors

Custom Range Slider

.range-slider {
  position: relative;
  width: 100%;
  height: 40px;
  display: flex;
  align-items: center;
}
 
.range-slider input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  background: transparent;
  cursor: pointer;
  position: relative;
  z-index: 2;
}
 
/* Track */
.range-slider input[type="range"]::-webkit-slider-runnable-track {
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 3px;
  
  transition: background 200ms var(--ease-smooth);
}
 
.range-slider:hover input[type="range"]::-webkit-slider-runnable-track {
  background: rgba(255, 255, 255, 0.15);
}
 
/* Thumb */
.range-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  background: linear-gradient(135deg, #00d4ff, #0099cc);
  border-radius: 50%;
  margin-top: -7px;
  cursor: grab;
  
  box-shadow: 
    0 2px 8px rgba(0, 0, 0, 0.3),
    0 0 0 0 rgba(0, 212, 255, 0.4);
  
  transition: 
    transform 150ms var(--ease-spring),
    box-shadow 200ms var(--ease-smooth);
}
 
.range-slider input[type="range"]::-webkit-slider-thumb:hover {
  transform: scale(1.2);
  box-shadow: 
    0 4px 12px rgba(0, 0, 0, 0.4),
    0 0 20px rgba(0, 212, 255, 0.6);
}
 
.range-slider input[type="range"]::-webkit-slider-thumb:active {
  cursor: grabbing;
  transform: scale(1.1);
}
 
/* Firefox styles */
.range-slider input[type="range"]::-moz-range-track {
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 3px;
}
 
.range-slider input[type="range"]::-moz-range-thumb {
  width: 20px;
  height: 20px;
  background: linear-gradient(135deg, #00d4ff, #0099cc);
  border-radius: 50%;
  border: none;
  cursor: grab;
  
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  
  transition: transform 150ms var(--ease-spring);
}

Range with Fill Animation

.range-slider-fill {
  position: relative;
}
 
.range-slider-fill .track-fill {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 6px;
  background: linear-gradient(90deg, #00d4ff, #0099cc);
  border-radius: 3px;
  pointer-events: none;
  
  transition: width 50ms linear;
}
 
.range-slider-fill .track-glow {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 6px;
  background: linear-gradient(90deg, #00d4ff, #0099cc);
  border-radius: 3px;
  filter: blur(8px);
  opacity: 0.5;
  pointer-events: none;
  
  transition: width 50ms linear;
}

Range with Value Display

.range-with-value {
  display: flex;
  align-items: center;
  gap: 16px;
}
 
.range-with-value .value-display {
  min-width: 60px;
  padding: 8px 12px;
  background: rgba(0, 212, 255, 0.1);
  border: 1px solid rgba(0, 212, 255, 0.3);
  border-radius: 6px;
  color: #00d4ff;
  font-weight: 600;
  text-align: center;
  font-variant-numeric: tabular-nums;
  
  transition: 
    background 200ms var(--ease-smooth),
    transform 150ms var(--ease-spring);
}
 
.range-with-value input[type="range"]:active ~ .value-display,
.range-with-value:hover .value-display {
  background: rgba(0, 212, 255, 0.15);
  transform: scale(1.05);
}

Stepped Range Slider

.range-stepped {
  position: relative;
}
 
.range-stepped .step-marks {
  position: absolute;
  top: 24px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: space-between;
  padding: 0 10px;
}
 
.range-stepped .step-mark {
  width: 4px;
  height: 4px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  
  transition: 
    background 200ms var(--ease-smooth),
    transform 200ms var(--ease-spring);
}
 
.range-stepped .step-mark.active {
  background: #00d4ff;
  transform: scale(1.5);
}
 
.range-stepped .step-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 16px;
  padding: 0 6px;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
}

Touch-Optimized Range

@media (pointer: coarse) {
  .range-slider input[type="range"]::-webkit-slider-thumb {
    width: 28px;
    height: 28px;
    margin-top: -11px;
  }
  
  .range-slider input[type="range"] {
    height: 44px;
  }
  
  .range-slider input[type="range"]::-webkit-slider-runnable-track {
    height: 8px;
  }
}

4. Transition Patterns

Page/Screen Transitions

Fade Transition

.page-fade-enter {
  opacity: 0;
}
 
.page-fade-enter-active {
  opacity: 1;
  transition: opacity 400ms var(--ease-smooth);
}
 
.page-fade-exit {
  opacity: 1;
}
 
.page-fade-exit-active {
  opacity: 0;
  transition: opacity 300ms var(--ease-smooth);
}

Slide Transition

.page-slide-enter {
  opacity: 0;
  transform: translateX(30px);
}
 
.page-slide-enter-active {
  opacity: 1;
  transform: translateX(0);
  transition: 
    opacity 400ms var(--ease-out),
    transform 400ms var(--ease-out);
}
 
.page-slide-exit {
  opacity: 1;
  transform: translateX(0);
}
 
.page-slide-exit-active {
  opacity: 0;
  transform: translateX(-30px);
  transition: 
    opacity 300ms var(--ease-in),
    transform 300ms var(--ease-in);
}

Scale Transition (For Modals/Overlays)

.page-scale-enter {
  opacity: 0;
  transform: scale(0.95);
}
 
.page-scale-enter-active {
  opacity: 1;
  transform: scale(1);
  transition: 
    opacity 300ms var(--ease-out),
    transform 400ms var(--ease-spring);
}
 
.page-scale-exit {
  opacity: 1;
  transform: scale(1);
}
 
.page-scale-exit-active {
  opacity: 0;
  transform: scale(0.95);
  transition: 
    opacity 200ms var(--ease-in),
    transform 300ms var(--ease-in);
}

Content Replacement Animation

.content-replace {
  position: relative;
  overflow: hidden;
}
 
.content-replace .content-old {
  animation: content-out 300ms var(--ease-in) forwards;
}
 
.content-replace .content-new {
  animation: content-in 400ms var(--ease-out) 200ms backwards;
}
 
@keyframes content-out {
  to {
    opacity: 0;
    transform: translateY(-20px);
  }
}
 
@keyframes content-in {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Stagger Patterns for Lists

/* Standard stagger */
.stagger-list .item {
  animation: stagger-enter 400ms var(--ease-out) backwards;
}
 
.stagger-list .item:nth-child(1) { animation-delay: 0ms; }
.stagger-list .item:nth-child(2) { animation-delay: 50ms; }
.stagger-list .item:nth-child(3) { animation-delay: 100ms; }
.stagger-list .item:nth-child(4) { animation-delay: 150ms; }
.stagger-list .item:nth-child(5) { animation-delay: 200ms; }
 
@keyframes stagger-enter {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
 
/* Cascade stagger (for grid layouts) */
.cascade-grid .item {
  animation: cascade-enter 500ms var(--ease-out) backwards;
}
 
.cascade-grid .item:nth-child(1) { animation-delay: 0ms; }
.cascade-grid .item:nth-child(2) { animation-delay: 80ms; }
.cascade-grid .item:nth-child(3) { animation-delay: 160ms; }
.cascade-grid .item:nth-child(4) { animation-delay: 240ms; }
 
@keyframes cascade-enter {
  from {
    opacity: 0;
    transform: translateY(40px) scale(0.9);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

Exit Animations

/* Slide out left */
.exit-left {
  animation: exit-left 300ms var(--ease-in) forwards;
}
 
@keyframes exit-left {
  to {
    opacity: 0;
    transform: translateX(-100%);
  }
}
 
/* Slide out right */
.exit-right {
  animation: exit-right 300ms var(--ease-in) forwards;
}
 
@keyframes exit-right {
  to {
    opacity: 0;
    transform: translateX(100%);
  }
}
 
/* Scale out */
.exit-scale {
  animation: exit-scale 250ms var(--ease-in) forwards;
}
 
@keyframes exit-scale {
  to {
    opacity: 0;
    transform: scale(0.8);
  }
}
 
/* Fade out with blur */
.exit-blur {
  animation: exit-blur 400ms var(--ease-smooth) forwards;
}
 
@keyframes exit-blur {
  to {
    opacity: 0;
    filter: blur(10px);
  }
}

5. Progress Indicators

Linear Progress Bar (Determinate)

.progress-linear {
  width: 100%;
  height: 8px;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 4px;
  overflow: hidden;
  position: relative;
}
 
.progress-linear .progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #00d4ff, #0099cc);
  border-radius: 4px;
  width: 0%;
  
  transition: width 500ms var(--ease-out);
}
 
.progress-linear .progress-glow {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: linear-gradient(90deg, #00d4ff, #0099cc);
  border-radius: 4px;
  filter: blur(8px);
  opacity: 0.5;
  width: 0%;
  
  transition: width 500ms var(--ease-out);
}
 
/* Striped animation for active state */
.progress-linear.active .progress-fill {
  background: linear-gradient(
    90deg,
    #00d4ff 0%,
    #0099cc 50%,
    #00d4ff 100%
  );
  background-size: 200% 100%;
  animation: progress-stripes 1.5s linear infinite;
}
 
@keyframes progress-stripes {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

Linear Progress (Indeterminate)

.progress-linear-indeterminate {
  width: 100%;
  height: 4px;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}
 
.progress-linear-indeterminate::after {
  content: '';
  position: absolute;
  top: 0;
  left: -40%;
  width: 40%;
  height: 100%;
  background: linear-gradient(90deg, transparent, #00d4ff, transparent);
  border-radius: 2px;
  animation: indeterminate-slide 1.5s var(--ease-in-out) infinite;
}
 
@keyframes indeterminate-slide {
  0% { left: -40%; }
  50% { left: 100%; }
  100% { left: 100%; }
}

Circular Progress/Spinner

.progress-circular {
  width: 48px;
  height: 48px;
}
 
.progress-circular svg {
  transform: rotate(-90deg);
}
 
.progress-circular .track {
  fill: none;
  stroke: rgba(255, 255, 255, 0.08);
  stroke-width: 4;
}
 
.progress-circular .fill {
  fill: none;
  stroke: url(#progress-gradient);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-dasharray: 126;
  stroke-dashoffset: 126;
  
  transition: stroke-dashoffset 500ms var(--ease-out);
}
 
/* Indeterminate spinner */
.spinner {
  width: 40px;
  height: 40px;
  animation: spinner-rotate 1s linear infinite;
}
 
.spinner circle {
  fill: none;
  stroke: #00d4ff;
  stroke-width: 4;
  stroke-linecap: round;
  stroke-dasharray: 80;
  stroke-dashoffset: 60;
}
 
@keyframes spinner-rotate {
  to { transform: rotate(360deg); }
}

Step Indicators

.step-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
}
 
.step {
  display: flex;
  align-items: center;
  gap: 8px;
}
 
.step-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.15);
  border: 2px solid transparent;
  
  transition: 
    background 300ms var(--ease-smooth),
    border-color 300ms var(--ease-smooth),
    transform 300ms var(--ease-spring);
}
 
.step.active .step-dot {
  background: #00d4ff;
  box-shadow: 0 0 15px rgba(0, 212, 255, 0.5);
  transform: scale(1.2);
}
 
.step.completed .step-dot {
  background: #00d4ff;
}
 
.step-line {
  width: 40px;
  height: 2px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 1px;
  overflow: hidden;
  position: relative;
}
 
.step-line::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, #00d4ff, #0099cc);
  
  transition: width 400ms var(--ease-out);
}
 
.step.completed .step-line::after {
  width: 100%;
}

40-Day Journey Progress

.journey-progress {
  padding: 20px;
  background: var(--surface-card);
  border-radius: 16px;
}
 
.journey-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}
 
.journey-title {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.7);
}
 
.journey-count {
  font-size: 18px;
  font-weight: 700;
  color: #00d4ff;
}
 
.journey-bar {
  height: 12px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 6px;
  overflow: hidden;
  position: relative;
}
 
.journey-fill {
  height: 100%;
  background: linear-gradient(90deg, #00d4ff, #ff00ff, #ffbf00);
  background-size: 200% 100%;
  border-radius: 6px;
  width: 0%;
  
  transition: width 800ms var(--ease-out);
  animation: journey-shimmer 3s linear infinite;
}
 
@keyframes journey-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
 
.journey-days {
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
  padding: 0 4px;
}
 
.journey-day {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  
  transition: 
    background 300ms var(--ease-smooth),
    transform 300ms var(--ease-spring),
    box-shadow 300ms var(--ease-smooth);
}
 
.journey-day.completed {
  background: #00d4ff;
  box-shadow: 0 0 8px rgba(0, 212, 255, 0.5);
}
 
.journey-day.current {
  background: #ffbf00;
  transform: scale(1.5);
  box-shadow: 0 0 12px rgba(255, 191, 0, 0.6);
}

Micro-Progress (Individual Experience)

.micro-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: rgba(255, 255, 255, 0.05);
  z-index: 1000;
}
 
.micro-progress .fill {
  height: 100%;
  background: linear-gradient(90deg, #00d4ff, #0099cc);
  width: 0%;
  
  transition: width 100ms linear;
}
 
.micro-progress .glow {
  position: absolute;
  top: 0;
  right: 0;
  width: 100px;
  height: 100%;
  background: linear-gradient(90deg, transparent, #00d4ff);
  filter: blur(4px);
  opacity: 0.8;
  transform: translateX(-100%);
  
  animation: micro-glow 2s var(--ease-in-out) infinite;
}
 
@keyframes micro-glow {
  0%, 100% { transform: translateX(-100%); opacity: 0.8; }
  50% { transform: translateX(0%); opacity: 0.4; }
}

6. Modal & Overlay Behaviors

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.8);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 1000;
  
  opacity: 0;
  visibility: hidden;
  
  transition: 
    opacity 300ms var(--ease-smooth),
    visibility 300ms var(--ease-smooth);
}
 
.modal-overlay.active {
  opacity: 1;
  visibility: visible;
}
.modal-content {
  background: var(--surface-elevated);
  border-radius: 20px;
  padding: 32px;
  max-width: 500px;
  width: 100%;
  border: 1px solid rgba(255, 255, 255, 0.08);
  
  box-shadow: 
    0 25px 80px rgba(0, 0, 0, 0.6),
    0 0 60px rgba(0, 212, 255, 0.1);
  
  transform: scale(0.9) translateY(20px);
  opacity: 0;
  
  transition: 
    transform 400ms var(--ease-spring),
    opacity 300ms var(--ease-out);
}
 
.modal-overlay.active .modal-content {
  transform: scale(1) translateY(0);
  opacity: 1;
}
 
/* Exit animation */
.modal-overlay.closing {
  opacity: 0;
  visibility: hidden;
}
 
.modal-overlay.closing .modal-content {
  transform: scale(0.95) translateY(10px);
  opacity: 0;
}
.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 24px;
}
 
.modal-title {
  font-size: 24px;
  font-weight: 700;
  color: var(--text-primary);
}
 
.modal-close {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
  border: none;
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  
  transition: 
    background 200ms var(--ease-smooth),
    color 200ms var(--ease-smooth),
    transform 150ms var(--ease-spring);
}
 
.modal-close:hover {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.9);
  transform: rotate(90deg);
}

Backdrop Interactions

/* Click outside to close */
.modal-overlay {
  cursor: pointer;
}
 
.modal-content {
  cursor: default;
}
 
/* Prevent body scroll when modal is open */
body.modal-open {
  overflow: hidden;
  padding-right: var(--scrollbar-width, 0px);
}

Focus Trap Behavior

// Focus trap implementation pattern
function trapFocus(element) {
  const focusableElements = element.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const firstFocusable = focusableElements[0];
  const lastFocusable = focusableElements[focusableElements.length - 1];
  
  element.addEventListener('keydown', (e) => {
    if (e.key === 'Tab') {
      if (e.shiftKey && document.activeElement === firstFocusable) {
        e.preventDefault();
        lastFocusable.focus();
      } else if (!e.shiftKey && document.activeElement === lastFocusable) {
        e.preventDefault();
        firstFocusable.focus();
      }
    }
    
    if (e.key === 'Escape') {
      closeModal();
    }
  });
  
  // Focus first element on open
  firstFocusable?.focus();
}

Toast/Notification Overlay

.toast-container {
  position: fixed;
  bottom: 24px;
  right: 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  z-index: 1100;
}
 
.toast {
  background: var(--surface-elevated);
  border-radius: 12px;
  padding: 16px 20px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  min-width: 300px;
  max-width: 400px;
  
  display: flex;
  align-items: center;
  gap: 12px;
  
  box-shadow: 
    0 10px 40px rgba(0, 0, 0, 0.4),
    0 0 30px rgba(0, 212, 255, 0.1);
  
  animation: toast-enter 400ms var(--ease-spring);
}
 
.toast.exiting {
  animation: toast-exit 300ms var(--ease-in) forwards;
}
 
@keyframes toast-enter {
  from {
    opacity: 0;
    transform: translateX(100%) scale(0.9);
  }
  to {
    opacity: 1;
    transform: translateX(0) scale(1);
  }
}
 
@keyframes toast-exit {
  to {
    opacity: 0;
    transform: translateX(100%) scale(0.9);
  }
}
 
.toast-icon {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
 
.toast-icon.success {
  background: rgba(0, 255, 136, 0.2);
  color: #00ff88;
}
 
.toast-icon.error {
  background: rgba(255, 68, 68, 0.2);
  color: #ff4444;
}

7. Form Input Interactions

Text Input

.input-group {
  margin-bottom: 20px;
}
 
.input-label {
  display: block;
  margin-bottom: 8px;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.7);
  font-weight: 500;
  
  transition: color 200ms var(--ease-smooth);
}
 
.input-group:focus-within .input-label {
  color: #00d4ff;
}
 
.text-input {
  width: 100%;
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  color: var(--text-primary);
  font-size: 16px;
  
  transition: 
    background 200ms var(--ease-smooth),
    border-color 200ms var(--ease-smooth),
    box-shadow 200ms var(--ease-smooth);
}
 
.text-input::placeholder {
  color: rgba(255, 255, 255, 0.3);
}
 
.text-input:hover {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.15);
}
 
.text-input:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.05);
  border-color: #00d4ff;
  box-shadow: 
    0 0 0 3px rgba(0, 212, 255, 0.15),
    0 0 20px rgba(0, 212, 255, 0.1);
}

Input Validation States

/* Error state */
.text-input.error {
  border-color: #ff4444;
  background: rgba(255, 68, 68, 0.05);
}
 
.text-input.error:focus {
  box-shadow: 
    0 0 0 3px rgba(255, 68, 68, 0.15),
    0 0 20px rgba(255, 68, 68, 0.1);
}
 
/* Success state */
.text-input.success {
  border-color: #00ff88;
  background: rgba(0, 255, 136, 0.05);
}
 
.text-input.success:focus {
  box-shadow: 
    0 0 0 3px rgba(0, 255, 136, 0.15),
    0 0 20px rgba(0, 255, 136, 0.1);
}

Error Message Animation

.error-message {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 13px;
  color: #ff4444;
  
  animation: error-shake 400ms var(--ease-out);
}
 
@keyframes error-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(3px); }
}
 
.error-icon {
  width: 16px;
  height: 16px;
  animation: error-icon-pop 300ms var(--ease-spring);
}
 
@keyframes error-icon-pop {
  0% { transform: scale(0); }
  70% { transform: scale(1.3); }
  100% { transform: scale(1); }
}

Success Confirmation

.success-checkmark {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: linear-gradient(135deg, #00ff88, #00cc6a);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 20px;
  
  animation: success-pop 500ms var(--ease-spring);
}
 
@keyframes success-pop {
  0% { transform: scale(0); }
  50% { transform: scale(1.2); }
  100% { transform: scale(1); }
}
 
.success-checkmark svg {
  width: 32px;
  height: 32px;
  stroke: #0a0a0f;
  stroke-width: 3;
  fill: none;
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
  
  animation: check-draw 400ms var(--ease-out) 200ms forwards;
}
 
@keyframes check-draw {
  to { stroke-dashoffset: 0; }
}
 
.success-message {
  text-align: center;
  animation: success-fade 400ms var(--ease-out) 300ms backwards;
}
 
@keyframes success-fade {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Textarea

.textarea {
  width: 100%;
  min-height: 120px;
  padding: 14px 16px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  color: var(--text-primary);
  font-size: 16px;
  resize: vertical;
  font-family: inherit;
  line-height: 1.6;
  
  transition: 
    background 200ms var(--ease-smooth),
    border-color 200ms var(--ease-smooth),
    box-shadow 200ms var(--ease-smooth);
}
 
.textarea:hover {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.15);
}
 
.textarea:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.05);
  border-color: #00d4ff;
  box-shadow: 
    0 0 0 3px rgba(0, 212, 255, 0.15),
    0 0 20px rgba(0, 212, 255, 0.1);
}

8. Reveal & Surprise Patterns

Content Reveal Timing

/* Staggered text reveal */
.reveal-text .word {
  display: inline-block;
  opacity: 0;
  transform: translateY(20px);
  animation: word-reveal 500ms var(--ease-out) forwards;
}
 
.reveal-text .word:nth-child(1) { animation-delay: 0ms; }
.reveal-text .word:nth-child(2) { animation-delay: 80ms; }
.reveal-text .word:nth-child(3) { animation-delay: 160ms; }
.reveal-text .word:nth-child(4) { animation-delay: 240ms; }
.reveal-text .word:nth-child(5) { animation-delay: 320ms; }
 
@keyframes word-reveal {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
 
/* Character-by-character reveal */
.reveal-char .char {
  display: inline-block;
  opacity: 0;
  animation: char-reveal 400ms var(--ease-out) forwards;
}
 
@keyframes char-reveal {
  0% {
    opacity: 0;
    transform: translateY(10px) rotateX(-90deg);
  }
  100% {
    opacity: 1;
    transform: translateY(0) rotateX(0);
  }
}

Dramatic Entrance Animations

/* Scale and glow entrance */
.entrance-dramatic {
  opacity: 0;
  transform: scale(0.8);
  filter: blur(10px);
  
  animation: entrance-dramatic 800ms var(--ease-out) forwards;
}
 
@keyframes entrance-dramatic {
  0% {
    opacity: 0;
    transform: scale(0.8);
    filter: blur(10px);
  }
  50% {
    opacity: 1;
    transform: scale(1.02);
    filter: blur(0);
  }
  100% {
    opacity: 1;
    transform: scale(1);
    filter: blur(0);
  }
}
 
/* Slide from darkness */
.entrance-emerge {
  opacity: 0;
  transform: translateY(60px);
  
  animation: entrance-emerge 700ms var(--ease-out) forwards;
}
 
@keyframes entrance-emerge {
  0% {
    opacity: 0;
    transform: translateY(60px);
    box-shadow: 0 0 0 rgba(0, 212, 255, 0);
  }
  60% {
    opacity: 1;
    transform: translateY(-5px);
    box-shadow: 0 0 60px rgba(0, 212, 255, 0.3);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
    box-shadow: 0 0 30px rgba(0, 212, 255, 0.15);
  }
}
 
/* Ripple reveal */
.entrance-ripple {
  position: relative;
  overflow: hidden;
}
 
.entrance-ripple::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background: rgba(0, 212, 255, 0.2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  
  animation: ripple-expand 1000ms var(--ease-out) forwards;
}
 
@keyframes ripple-expand {
  to {
    width: 300%;
    height: 300%;
    opacity: 0;
  }
}

Particle/Confetti Effects

/* CSS-only confetti burst */
.confetti-container {
  position: absolute;
  pointer-events: none;
}
 
.confetti-piece {
  position: absolute;
  width: 8px;
  height: 8px;
  opacity: 0;
}
 
.confetti-piece.square {
  border-radius: 2px;
}
 
.confetti-piece.circle {
  border-radius: 50%;
}
 
.confetti-piece.animate {
  animation: confetti-fall 2000ms var(--ease-out) forwards;
}
 
@keyframes confetti-fall {
  0% {
    opacity: 1;
    transform: 
      translate(0, 0) 
      rotate(0deg) 
      scale(1);
  }
  100% {
    opacity: 0;
    transform: 
      translate(var(--tx), var(--ty)) 
      rotate(var(--rot)) 
      scale(0.5);
  }
}
 
/* Glow burst effect */
.glow-burst {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(0, 212, 255, 0.6), transparent);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
 
.glow-burst.animate {
  animation: glow-burst-expand 800ms var(--ease-out) forwards;
}
 
@keyframes glow-burst-expand {
  0% {
    width: 0;
    height: 0;
    opacity: 1;
  }
  100% {
    width: 400px;
    height: 400px;
    opacity: 0;
  }
}

Sound Cue Visual Indicators

/* Visual indicator for sound cue */
.sound-indicator {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  background: rgba(0, 212, 255, 0.1);
  border: 1px solid rgba(0, 212, 255, 0.3);
  border-radius: 20px;
  color: #00d4ff;
  font-size: 13px;
}
 
.sound-indicator .icon {
  animation: sound-pulse 1.5s var(--ease-in-out) infinite;
}
 
@keyframes sound-pulse {
  0%, 100% { 
    opacity: 0.6;
    transform: scale(1);
  }
  50% { 
    opacity: 1;
    transform: scale(1.1);
  }
}
 
/* Audio wave visualization */
.audio-waves {
  display: flex;
  align-items: center;
  gap: 3px;
  height: 20px;
}
 
.audio-wave {
  width: 3px;
  background: #00d4ff;
  border-radius: 2px;
  animation: wave-dance 1s var(--ease-in-out) infinite;
}
 
.audio-wave:nth-child(1) { height: 8px; animation-delay: 0ms; }
.audio-wave:nth-child(2) { height: 14px; animation-delay: 100ms; }
.audio-wave:nth-child(3) { height: 10px; animation-delay: 200ms; }
.audio-wave:nth-child(4) { height: 18px; animation-delay: 300ms; }
.audio-wave:nth-child(5) { height: 12px; animation-delay: 400ms; }
 
@keyframes wave-dance {
  0%, 100% { transform: scaleY(1); opacity: 0.6; }
  50% { transform: scaleY(1.3); opacity: 1; }
}

9. Micro-interactions

Toggle Switch

.toggle-switch {
  position: relative;
  width: 52px;
  height: 28px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  cursor: pointer;
  
  transition: background 300ms var(--ease-smooth);
}
 
.toggle-switch.active {
  background: linear-gradient(135deg, #00d4ff, #0099cc);
}
 
.toggle-switch .thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 22px;
  height: 22px;
  background: white;
  border-radius: 50%;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  
  transition: transform 300ms var(--ease-spring);
}
 
.toggle-switch.active .thumb {
  transform: translateX(24px);
}
 
/* Glow effect when active */
.toggle-switch.active {
  box-shadow: 0 0 20px rgba(0, 212, 255, 0.4);
}

Checkbox

.checkbox {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
}
 
.checkbox-input {
  display: none;
}
 
.checkbox-box {
  width: 22px;
  height: 22px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  
  transition: 
    background 200ms var(--ease-smooth),
    border-color 200ms var(--ease-smooth),
    box-shadow 200ms var(--ease-smooth);
}
 
.checkbox:hover .checkbox-box {
  border-color: rgba(255, 255, 255, 0.5);
}
 
.checkbox-input:checked + .checkbox-box {
  background: linear-gradient(135deg, #00d4ff, #0099cc);
  border-color: transparent;
  box-shadow: 0 0 15px rgba(0, 212, 255, 0.4);
}
 
.checkbox-box svg {
  width: 14px;
  height: 14px;
  stroke: #0a0a0f;
  stroke-width: 3;
  fill: none;
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  
  transition: stroke-dashoffset 200ms var(--ease-out);
}
 
.checkbox-input:checked + .checkbox-box svg {
  stroke-dashoffset: 0;
}
 
/* Check animation */
.checkbox-input:checked + .checkbox-box {
  animation: checkbox-pop 300ms var(--ease-spring);
}
 
@keyframes checkbox-pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}

Radio Button

.radio {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
}
 
.radio-input {
  display: none;
}
 
.radio-circle {
  width: 22px;
  height: 22px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  
  transition: border-color 200ms var(--ease-smooth);
}
 
.radio:hover .radio-circle {
  border-color: rgba(255, 255, 255, 0.5);
}
 
.radio-dot {
  width: 10px;
  height: 10px;
  background: #00d4ff;
  border-radius: 50%;
  transform: scale(0);
  box-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
  
  transition: transform 200ms var(--ease-spring);
}
 
.radio-input:checked + .radio-circle {
  border-color: #00d4ff;
}
 
.radio-input:checked + .radio-circle .radio-dot {
  transform: scale(1);
}

Tooltip

.tooltip-container {
  position: relative;
  display: inline-block;
}
 
.tooltip {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%) translateY(5px);
  padding: 10px 14px;
  background: var(--surface-elevated);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  font-size: 13px;
  color: var(--text-primary);
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: 100;
  
  box-shadow: 
    0 8px 30px rgba(0, 0, 0, 0.4),
    0 0 20px rgba(0, 212, 255, 0.1);
  
  transition: 
    opacity 200ms var(--ease-smooth),
    visibility 200ms var(--ease-smooth),
    transform 200ms var(--ease-out);
}
 
.tooltip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: var(--surface-elevated);
}
 
.tooltip-container:hover .tooltip {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}
 
/* Bottom tooltip variant */
.tooltip.bottom {
  bottom: auto;
  top: calc(100% + 10px);
  transform: translateX(-50%) translateY(-5px);
}
 
.tooltip.bottom::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: var(--surface-elevated);
}
 
.tooltip-container:hover .tooltip.bottom {
  transform: translateX(-50%) translateY(0);
}

10. Gesture Patterns (Mobile)

Swipe Behaviors

/* Swipeable container */
.swipe-container {
  overflow: hidden;
  touch-action: pan-y;
}
 
.swipe-item {
  transform: translateX(0);
  transition: transform 300ms var(--ease-out);
}
 
.swipe-item.swiped-left {
  transform: translateX(-80px);
}
 
.swipe-item.swiped-right {
  transform: translateX(80px);
}
 
/* Swipe actions revealed */
.swipe-actions {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  opacity: 0;
  transition: opacity 200ms var(--ease-smooth);
}
 
.swipe-actions.left {
  right: 0;
}
 
.swipe-actions.right {
  left: 0;
}
 
.swipe-item.swiped-left ~ .swipe-actions.left,
.swipe-item.swiped-right ~ .swipe-actions.right {
  opacity: 1;
}

Tap Feedback

/* Ripple tap effect */
.tap-ripple {
  position: relative;
  overflow: hidden;
}
 
.tap-ripple::after {
  content: '';
  position: absolute;
  top: var(--tap-y, 50%);
  left: var(--tap-x, 50%);
  width: 0;
  height: 0;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
 
.tap-ripple.tapped::after {
  animation: tap-ripple-effect 400ms var(--ease-out);
}
 
@keyframes tap-ripple-effect {
  to {
    width: 300px;
    height: 300px;
    opacity: 0;
  }
}
 
/* Scale tap feedback */
.tap-scale {
  transition: transform 100ms var(--ease-out);
}
 
.tap-scale:active {
  transform: scale(0.97);
}

Pull-to-Refresh

.ptr-container {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
 
.ptr-indicator {
  height: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: height 200ms var(--ease-smooth);
}
 
.ptr-indicator.pulling {
  height: 60px;
}
 
.ptr-indicator.refreshing {
  height: 60px;
}
 
.ptr-spinner {
  width: 24px;
  height: 24px;
  opacity: 0;
  transform: scale(0.5);
  transition: 
    opacity 200ms var(--ease-smooth),
    transform 200ms var(--ease-spring);
}
 
.ptr-indicator.pulling .ptr-spinner,
.ptr-indicator.refreshing .ptr-spinner {
  opacity: 1;
  transform: scale(1);
}
 
.ptr-indicator.refreshing .ptr-spinner {
  animation: ptr-spin 1s linear infinite;
}
 
@keyframes ptr-spin {
  to { transform: rotate(360deg); }
}
 
/* Arrow indicator */
.ptr-arrow {
  width: 20px;
  height: 20px;
  transition: transform 200ms var(--ease-spring);
}
 
.ptr-indicator.pulling .ptr-arrow {
  transform: rotate(180deg);
}

Touch-Optimized Targets

/* Minimum touch target size */
.touch-target {
  min-width: 44px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
}
 
/* Increased spacing for touch */
@media (pointer: coarse) {
  .button-group {
    gap: 16px;
  }
  
  .card-grid {
    gap: 24px;
  }
  
  .form-input {
    padding: 16px 18px;
    font-size: 16px; /* Prevents zoom on iOS */
  }
  
  .checkbox-box,
  .radio-circle {
    width: 26px;
    height: 26px;
  }
  
  .toggle-switch {
    width: 56px;
    height: 32px;
  }
  
  .toggle-switch .thumb {
    width: 26px;
    height: 26px;
  }
  
  .toggle-switch.active .thumb {
    transform: translateX(24px);
  }
}

Haptic Feedback Visual Indicators

/* Visual feedback for haptic events */
.haptic-pulse {
  animation: haptic-visual 100ms var(--ease-out);
}
 
@keyframes haptic-visual {
  0% { transform: scale(1); }
  50% { transform: scale(0.98); }
  100% { transform: scale(1); }
}
 
/* Success haptic visual */
.haptic-success {
  animation: haptic-success-visual 300ms var(--ease-spring);
}
 
@keyframes haptic-success-visual {
  0% { transform: scale(1); }
  30% { transform: scale(1.02); }
  60% { transform: scale(0.99); }
  100% { transform: scale(1); }
}

Implementation Notes

Performance Considerations

  1. Use transform and opacity for animations - they don’t trigger layout recalculations
  2. Apply will-change sparingly to elements that will animate:
    .will-animate {
      will-change: transform, opacity;
    }
  3. Remove will-change after animation completes to free up resources
  4. Use CSS animations over JavaScript animations when possible
  5. Implement prefers-reduced-motion support:
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
      }
    }

Accessibility Requirements

  1. All interactive elements must have visible focus states
  2. Color alone should not convey information - use icons, text, or patterns
  3. Animation should not be required to understand content
  4. Respect prefers-reduced-motion user preference
  5. Maintain keyboard navigation for all interactive elements

Browser Support

  • Modern browsers (Chrome 80+, Firefox 75+, Safari 13+, Edge 80+)
  • Use autoprefixer for vendor prefixes
  • Provide fallbacks for older browsers where necessary

Quick Reference: Timing Values

Animation TypeDurationEasing
Button hover200msease-out
Button active100msease-in
Card hover300msease-out
Modal open400msspring
Modal close300msease-in
Page transition400msease-out
List stagger50ms delay per itemease-out
Progress bar500msease-out
Toast enter400msspring
Toast exit300msease-in
Tooltip200msease-out
Checkbox check200msspring
Toggle switch300msspring
Content reveal400-600msease-out
Confetti burst2000msease-out
Glow pulse1500msease-in-out

Document Version: 1.0 Last Updated: 2024 For: 40 Days of Existential Threats Interactive Experience