The half of Reflekt that most products never design. Sound defines the emotional register before a single word is spoken. Motion defines whether the product feels alive or mechanical. Both must be as considered as every screen we built in Phase 1.
This brief is precise enough for a sound designer to execute all three sounds in 3 days without a single follow-up question. Every parameter is defined. Every constraint is explicit.
| Sound | Duration | Hz Range | Character | Volume | Test With |
|---|---|---|---|---|---|
| Wake | 600–800ms | 440–660 Hz | Warm sine, slow attack | -18 dBFS | Seniors asleep / occupied |
| Confirm | 200–280ms | 520–600 Hz | Clean, fast decay | -14 dBFS | Does it feel satisfying, not celebratory? |
| Alert | 2× 400ms + gap | 330–440 Hz | Rounded, two-pulse | -10 dBFS | Seniors 70+ — does it cause anxiety? |
Every animation in Reflekt follows these four principles. No random transitions. No decorative motion. Each movement has a reason and a feeling. All CSS is copy-paste ready for your dev team.
/* ── REFLEKT ENTRANCE ── */ .reflekt-enter { opacity: 0; transform: translateY(16px); transition: opacity 480ms cubic-bezier(0.2, 0, 0, 1), transform 480ms cubic-bezier(0.2, 0, 0, 1); } .reflekt-enter.visible { opacity: 1; transform: translateY(0); } /* Staggered children — add to each child */ .reflekt-enter:nth-child(1) { transition-delay: 0ms } .reflekt-enter:nth-child(2) { transition-delay: 60ms } .reflekt-enter:nth-child(3) { transition-delay: 120ms } .reflekt-enter:nth-child(4) { transition-delay: 180ms } /* JS: add 'visible' class on mount or scroll into view */ // element.classList.add('visible')
/* ── REFLEKT STATE TRANSITION ── */ .state-card { transition: background-color 400ms cubic-bezier(0.4, 0, 0.2, 1), border-color 400ms cubic-bezier(0.4, 0, 0.2, 1), color 400ms cubic-bezier(0.4, 0, 0.2, 1); } /* Content inside crossfades */ .state-content { transition: opacity 200ms ease-out; } .state-content.changing { opacity: 0; } .state-content.changed { transition: opacity 300ms ease-in; opacity: 1; } /* Alert transitions: 20% faster for urgency */ .state-card[data-state="alert"] .state-content { transition-duration: 160ms, 240ms; }
/* ── REFLEKT TOUCH FEEDBACK ── */ .reflekt-btn { transition: transform 120ms cubic-bezier(0.2, 0, 0, 1), box-shadow 120ms cubic-bezier(0.2, 0, 0, 1), opacity 120ms ease; -webkit-tap-highlight-color: transparent; user-select: none; } .reflekt-btn:active { transform: scale(0.96); box-shadow: 0 1px 4px rgba(0,0,0,0.2); } /* Release: spring back */ .reflekt-btn:not(:active) { transition: transform 180ms cubic-bezier(0.34, 1.2, 0.64, 1), box-shadow 180ms ease-out; } /* Status dot — ambient breath */ .status-dot { animation: dotBreath 3s ease-in-out infinite; } @keyframes dotBreath { 0%, 100% { box-shadow: 0 0 0 0 rgba(74,138,90,0.4) } 50% { box-shadow: 0 0 0 6px rgba(74,138,90,0) } }
/* ── REFLEKT AMBIENT MOTION (LUMA SCREEN) ── */ /* Time display — slow breath, 6s cycle */ .luma-time { animation: timeBreath 6s ease-in-out infinite; } @keyframes timeBreath { 0%, 100% { opacity: 0.88 } 50% { opacity: 0.72 } } /* Greeting — slower fade, 8s cycle */ .luma-greeting { animation: greetFade 8s ease-in-out infinite; } @keyframes greetFade { 0%, 100% { opacity: 0.65 } 50% { opacity: 0.48 } } /* Background teal glow — 4s pulse */ .luma-glow { animation: ambientPulse 4s ease-in-out infinite; } @keyframes ambientPulse { 0%, 100% { opacity: 0.5; transform: scale(1) } 50% { opacity: 1; transform: scale(1.1) } } /* HELP state: gentle urgency pulse */ .luma-help-glow { animation: helpGlow 2s ease-in-out infinite; } @keyframes helpGlow { 0%, 100% { box-shadow: 0 20px 60px rgba(0,0,0,0.4) } 50% { box-shadow: 0 20px 60px rgba(138,48,48,0.2) } } /* Accessibility: respect prefers-reduced-motion */ @media (prefers-reduced-motion: reduce) { .luma-time, .luma-greeting, .luma-glow, .luma-help-glow { animation: none; } }