This document maps StellaOps UI components to their interaction types and motion token usage. Components must use tokens from the catalog (tokens/motion.scss, motion-tokens.ts) rather than bespoke values.
| Token | CSS Variable | Duration | Use Case |
|---|
| durationXs | –motion-duration-xs | 80ms | Focus rings, instant feedback |
| durationSm | –motion-duration-sm | 140ms | Hover states, button presses |
| durationMd | –motion-duration-md | 200ms | Modal open/close, panel slide |
| durationLg | –motion-duration-lg | 260ms | Page transitions, accordions |
| durationXl | –motion-duration-xl | 320ms | Complex sequences, onboarding |
| easeStandard | –motion-ease-standard | cubic-bezier(0.2,0,0,1) | Default for all transitions |
| easeEntrance | –motion-ease-entrance | cubic-bezier(0.18,0.89,0.32,1) | Elements appearing |
| easeExit | –motion-ease-exit | cubic-bezier(0.36,0,-0.56,-0.56) | Elements leaving |
| easeBounce | –motion-ease-bounce | cubic-bezier(0.34,1.56,0.64,1) | Playful feedback only |
| Component | Interaction Type | Tokens Used | Reduced-Motion Behavior |
|---|
| Button (primary) | Hover lift, press scale | durationSm, easeStandard, scaleSm | No transform, instant state change |
| Button (secondary) | Hover border, press | durationXs, easeStandard | Instant state change |
| Modal/Dialog | Fade + slide up | durationMd, easeEntrance/Exit, translateMd | Instant show/hide |
| Dropdown/Menu | Fade + scale | durationSm, easeStandard, scaleSm | Instant show/hide |
| Toast/Snackbar | Slide in, auto-dismiss | durationMd, easeEntrance, translateLg | Instant show/hide |
| Accordion | Height expand | durationMd, easeStandard | Instant toggle |
| Tabs | Indicator slide | durationSm, easeStandard | Instant indicator |
| Progress bar | Width fill | durationMd, easeStandard | No animation, instant fill |
| Spinner/Loader | Rotate animation | N/A (CSS keyframe) | Static icon or hidden |
| Skeleton | Shimmer pulse | durationXl, easeStandard | Static skeleton, no shimmer |
| Badge (status) | Scale pop on change | durationXs, easeBounce, scaleSm | No scale, instant |
| Focus ring | Fade in | durationXs, easeStandard | Always visible, no fade |
| Tooltip | Fade + offset | durationSm, easeEntrance, translateSm | Instant show/hide |
| Banner (info) | Slide down | durationMd, easeEntrance, translateMd | Instant show/hide |
| Error state | Shake animation | durationSm, easeStandard | No shake, border only |
| Metric | Budget | Measurement |
|---|
| Interaction response | <= 100ms | Time to visual feedback |
| Animation frame (avg) | <= 16ms | Chrome DevTools Performance |
| Animation frame (p95) | <= 50ms | Chrome DevTools Performance |
| Layout shift | <= 0.05 CLS | Lighthouse CI |
| LCP placeholder | Show within 400ms | Lighthouse CI |
- Lint rule: ESLint/Stylelint blocks non-catalog easing values in
.scss and .ts files - Storybook check: All component stories must demonstrate reduced-motion variant
- Playwright check: Snapshot tests run with
--disable-animations and reduced-motion emulation - CI gate: Perf budget failures block merge
Per advisory §15: Component-to-interaction mapping for Time-to-First-Signal.
Location: src/app/features/runs/components/first-signal-card/
| Interaction | Trigger | Duration | Easing | Token |
|---|
| Skeleton shimmer | Initial load | 2s loop | linear | Custom keyframe |
| Skeleton → Content | Signal loaded | 200ms | easeEntrance | durationMd |
| Badge appear | Kind change | 140ms | easeEntrance | durationSm |
| Progress update | Phase change | 140ms | easeStandard | durationSm |
| Button hover | Mouse enter | 80ms | easeStandard | durationXs |
| Button press | Click | 80ms | easeExit | durationXs |
| Error shake | Load error | 260ms | easeStandard | durationLg |
Telemetry Events:
| Event | Trigger | Payload |
|---|
ttfs_start | Component mount | { runId, surface, t } |
ttfs_signal_rendered | Signal displayed | { runId, surface, cacheHit, source, kind, ttfsMs } |
ui.click.action | Action button click | { runId, actionType, target } |
ui.retry | Retry button click | { runId, errorCode } |
Accessibility:
| Requirement | Implementation |
|---|
| Focus visible | :focus-visible outline on buttons |
| Screen reader | aria-live="polite" on content area |
| Loading state | aria-busy="true" on card |
| Status | role="status" on badge |
| Keyboard | Tab navigation, Enter/Space activation |
| Reduced motion | Disable shimmer, use instant transitions |
Location: src/app/features/evidence/evidence-panel.component.ts
| Interaction | Trigger | Duration | Easing | Token |
|---|
| Tab switch | Tab click | 200ms | easeStandard | durationMd |
| Panel expand | Accordion open | 260ms | easeEntrance | durationLg |
| Panel collapse | Accordion close | 200ms | easeExit | durationMd |
| Copy success | Copy button | 140ms | easeEntrance | durationSm |
| Copy tooltip | After copy | 2s visible | — | — |
| Export progress | Export start | Indeterminate | linear | Spinner |
Telemetry Events:
| Event | Trigger | Payload |
|---|
finding_open | Panel mount | { findingId, entryPoint, t } |
proof_rendered | Proof visible | { findingId, proofKind, source, t } |
ui.copy.verify_command | Copy verify cmd | `{ type: ‘cosign’ |
evidence.export | Export complete | { findingId, fileSize } |
Location: src/app/features/evidence/components/proof-spine/
| Interaction | Trigger | Duration | Easing | Token |
|---|
| Hash truncation expand | Hover/focus | 140ms | easeStandard | durationSm |
| Copy single | Copy button | 80ms | easeExit | durationXs |
| Copy all | Copy all button | 140ms | easeEntrance | durationSm |
| Status badge | Load | 200ms | easeEntrance | durationMd |
| Event | Category | Sampling | Always Sample |
|---|
ttfs_start | ttfs | Per config | No |
ttfs_signal_rendered | ttfs | Per config | No |
ttfs_timeout | ttfs | — | Yes |
ttfs_error | ttfs | — | Yes |
| Event | Category | Sampling | Always Sample |
|---|
finding_open | tte | Per config | No |
proof_rendered | tte | Per config | No |
| Event | Category | Sampling | Always Sample |
|---|
ux.bounce | ui | — | Yes |
ux.non_bounce | ui | Per config | No |
ux.open_to_action | ui | Per config | No |
ui.copy.* | ui | Per config | No |
ui.click.* | ui | Per config | No |
evidence.export | ui | Per config | No |
| Environment | TTFS Rate | TTE Rate | UI Rate |
|---|
| Development | 100% | 100% | 100% |
| Staging | 100% | 100% | 100% |
| Production | ≥25% | 50% | 25% |
FirstSignalCard:
idle → loading → loaded
↘ unavailable
↘ error → (retry) → loading
EvidencePanel:
idle → loading → loaded
↘ partial (some proofs failed)
↘ error
| Deliverable | Path |
|---|
| FirstSignalCard | src/Web/StellaOps.Web/src/app/features/runs/components/first-signal-card/ |
| FirstSignalStore | src/Web/StellaOps.Web/src/app/features/runs/stores/first-signal.store.ts |
| EvidencePanel | src/Web/StellaOps.Web/src/app/features/evidence/evidence-panel.component.ts |
| ProofSpine | src/Web/StellaOps.Web/src/app/features/evidence/components/proof-spine/ |
| TTFS Telemetry | src/Web/StellaOps.Web/src/app/core/telemetry/ttfs-telemetry.service.ts |
| Sampling Config | src/Web/StellaOps.Web/src/app/core/telemetry/telemetry-config.ts |
| i18n Keys | src/Web/StellaOps.Web/src/assets/i18n/en.json |
| Test Fixtures | tests/fixtures/ttfs/ttfs-fixtures.ts |
| E2E Tests | src/Web/StellaOps.Web/tests/e2e/micro-interactions.spec.ts |
- Token catalog:
src/Web/StellaOps.Web/src/styles/tokens/_motion.scss - TypeScript tokens:
src/Web/StellaOps.Web/src/app/styles/motion-tokens.ts - Storybook stories:
src/Web/StellaOps.Web/src/stories/motion-tokens.stories.ts - TTFS Architecture:
docs/modules/telemetry/ttfs-architecture.md - Advisory:
docs/product/advisories/14-Dec-2025 - UX and Time-to-Evidence Technical Reference.md
- Sprint 0338: TTFS Foundation
- Sprint 0339: First Signal API
- Sprint 0340: FirstSignalCard UI
- Sprint 0341: TTFS Enhancements