// ========================================================================== // INTERACTION PATTERNS // ========================================================================== // Common interaction and state patterns // Theme-agnostic behavioral styles // ========================================================================== // HOVER EFFECTS .hover-lift { transition: transform var(--hover-transition-duration, 0.2s) var(--hover-transition-easing, ease); &:hover { transform: var(--hover-lift-transform, translateY(-4px)); } } .hover-scale { transition: transform var(--hover-transition-duration, 0.2s) var(--hover-transition-easing, ease); &:hover { transform: var(--hover-scale-transform, scale(1.05)); } } .hover-glow { transition: box-shadow var(--hover-transition-duration, 0.2s) var(--hover-transition-easing, ease); &:hover { box-shadow: var(--hover-glow-shadow, 0 0 20px rgba(0, 0, 0, 0.15)); } } .hover-fade { transition: opacity var(--hover-transition-duration, 0.2s) var(--hover-transition-easing, ease); &:hover { opacity: var(--hover-fade-opacity, 0.8); } } .hover-brighten { transition: filter var(--hover-transition-duration, 0.2s) var(--hover-transition-easing, ease); &:hover { filter: brightness(var(--hover-brighten-value, 1.1)); } } .hover-darken { transition: filter var(--hover-transition-duration, 0.2s) var(--hover-transition-easing, ease); &:hover { filter: brightness(var(--hover-darken-value, 0.9)); } } // FOCUS STATES .focus-ring { &:focus, &:focus-visible { outline: var(--focus-ring-width, 2px) solid var(--focus-ring-color, #007bff); outline-offset: var(--focus-ring-offset, 2px); } } .focus-ring-inset { &:focus, &:focus-visible { outline: var(--focus-ring-width, 2px) solid var(--focus-ring-color, #007bff); outline-offset: var(--focus-ring-inset-offset, -2px); } } .focus-glow { &:focus, &:focus-visible { box-shadow: var(--focus-glow-shadow, 0 0 0 3px rgba(0, 123, 255, 0.25)); outline: none; } } // ACTIVE STATES .active-press { transition: transform var(--active-transition-duration, 0.1s) var(--active-transition-easing, ease); &:active { transform: var(--active-press-transform, scale(0.98)); } } .active-sink { transition: transform var(--active-transition-duration, 0.1s) var(--active-transition-easing, ease); &:active { transform: var(--active-sink-transform, translateY(2px)); } } // LOADING STATES .loading { position: relative; pointer-events: none; &::after { content: ''; position: absolute; top: 50%; left: 50%; width: var(--loading-spinner-size, 20px); height: var(--loading-spinner-size, 20px); margin-top: calc(var(--loading-spinner-size, 20px) / -2); margin-left: calc(var(--loading-spinner-size, 20px) / -2); border: 2px solid var(--loading-spinner-color, rgba(0, 0, 0, 0.3)); border-top-color: var(--loading-spinner-active-color, #007bff); border-radius: 50%; animation: loading-spin var(--loading-spin-duration, 1s) linear infinite; z-index: 1; } &.loading-overlay::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: var(--loading-overlay-bg, rgba(255, 255, 255, 0.8)); z-index: 0; } } @keyframes loading-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .loading-pulse { animation: loading-pulse var(--loading-pulse-duration, 2s) infinite; } @keyframes loading-pulse { 0%, 100% { opacity: 1; } 50% { opacity: var(--loading-pulse-opacity, 0.5); } } .loading-skeleton { background: var(--skeleton-bg, #f0f0f0); background-image: linear-gradient( 90deg, var(--skeleton-bg, #f0f0f0), var(--skeleton-highlight, #e0e0e0), var(--skeleton-bg, #f0f0f0) ); background-size: 200px 100%; background-repeat: no-repeat; animation: loading-shimmer var(--skeleton-duration, 1.5s) infinite; border-radius: var(--skeleton-border-radius, 4px); } @keyframes loading-shimmer { 0% { background-position: -200px 0; } 100% { background-position: calc(200px + 100%) 0; } } // DISABLED STATES .disabled, [disabled] { opacity: var(--disabled-opacity, 0.6); pointer-events: none; cursor: not-allowed; } .disabled-loading { opacity: var(--disabled-loading-opacity, 0.8); pointer-events: none; cursor: wait; } // SUCCESS STATES .success-flash { animation: success-flash var(--success-flash-duration, 0.5s) ease-in-out; } @keyframes success-flash { 0%, 100% { background-color: transparent; } 50% { background-color: var(--success-flash-color, rgba(40, 167, 69, 0.2)); } } .success-checkmark { position: relative; &::after { content: '✓'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: var(--success-color, #28a745); font-size: var(--success-checkmark-size, 1.5rem); font-weight: bold; animation: success-checkmark-appear var(--success-checkmark-duration, 0.3s) ease-in-out; } } @keyframes success-checkmark-appear { 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); } 100% { opacity: 1; transform: translate(-50%, -50%) scale(1); } } // ERROR STATES .error-shake { animation: error-shake var(--error-shake-duration, 0.5s) ease-in-out; } @keyframes error-shake { 0%, 20%, 40%, 60%, 80%, 100% { transform: translateX(0); } 10%, 50%, 90% { transform: translateX(-5px); } 30%, 70% { transform: translateX(5px); } } .error-flash { animation: error-flash var(--error-flash-duration, 0.5s) ease-in-out; } @keyframes error-flash { 0%, 100% { background-color: transparent; } 50% { background-color: var(--error-flash-color, rgba(220, 53, 69, 0.2)); } } // WARNING STATES .warning-pulse { animation: warning-pulse var(--warning-pulse-duration, 2s) infinite; } @keyframes warning-pulse { 0%, 100% { opacity: 1; } 50% { opacity: var(--warning-pulse-opacity, 0.7); } } // DRAG AND DROP STATES .draggable { cursor: grab; user-select: none; transition: transform var(--draggable-transition-duration, 0.2s) var(--draggable-transition-easing, ease); &:active { cursor: grabbing; } &.dragging { cursor: grabbing; opacity: var(--dragging-opacity, 0.8); transform: var(--dragging-transform, rotate(5deg)); z-index: var(--z-dragging, 1000); } } .drop-zone { transition: all var(--drop-zone-transition-duration, 0.2s) var(--drop-zone-transition-easing, ease); border: 2px dashed transparent; border-radius: var(--drop-zone-border-radius, 8px); padding: var(--drop-zone-padding, 20px); &.drop-zone-active { border-color: var(--drop-zone-active-border, #007bff); background-color: var(--drop-zone-active-bg, rgba(0, 123, 255, 0.05)); } &.drop-zone-invalid { border-color: var(--drop-zone-invalid-border, #dc3545); background-color: var(--drop-zone-invalid-bg, rgba(220, 53, 69, 0.05)); } } // SELECTION STATES .selectable { cursor: pointer; transition: all var(--selectable-transition-duration, 0.2s) var(--selectable-transition-easing, ease); border: var(--selectable-border-width, 2px) solid transparent; border-radius: var(--selectable-border-radius, 4px); &:hover { border-color: var(--selectable-hover-border, rgba(0, 123, 255, 0.5)); } &.selected { border-color: var(--selectable-selected-border, #007bff); background-color: var(--selectable-selected-bg, rgba(0, 123, 255, 0.05)); } &.selecting { opacity: var(--selecting-opacity, 0.7); transform: var(--selecting-transform, scale(0.98)); } } // EXPANDABLE CONTENT .expandable { overflow: hidden; transition: height var(--expandable-transition-duration, 0.3s) var(--expandable-transition-easing, ease); &.expanding { transition: height var(--expanding-transition-duration, 0.3s) var(--expanding-transition-easing, ease-out); } &.collapsing { transition: height var(--collapsing-transition-duration, 0.3s) var(--collapsing-transition-easing, ease-in); } } .expandable-trigger { cursor: pointer; display: flex; align-items: center; gap: var(--expandable-trigger-gap, 8px); .expand-icon { transition: transform var(--expand-icon-transition-duration, 0.2s) var(--expand-icon-transition-easing, ease); } &.expanded .expand-icon { transform: var(--expand-icon-expanded-transform, rotate(180deg)); } } // SORTABLE LISTS .sortable { .sortable-item { cursor: move; user-select: none; transition: all var(--sortable-transition-duration, 0.2s) var(--sortable-transition-easing, ease); &.sortable-ghost { opacity: var(--sortable-ghost-opacity, 0.5); background: var(--sortable-ghost-bg, rgba(0, 123, 255, 0.1)); } &.sortable-chosen { opacity: var(--sortable-chosen-opacity, 0.8); transform: var(--sortable-chosen-transform, scale(1.02)); } } .sortable-handle { cursor: grab; display: flex; align-items: center; justify-content: center; padding: var(--sortable-handle-padding, 8px); opacity: var(--sortable-handle-opacity, 0.6); transition: opacity var(--sortable-handle-transition-duration, 0.2s); &:hover { opacity: 1; } &:active { cursor: grabbing; } } } // RIPPLE EFFECT .ripple-effect { position: relative; overflow: hidden; &::before { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; border-radius: 50%; background: var(--ripple-color, rgba(255, 255, 255, 0.3)); transform: translate(-50%, -50%); transition: width var(--ripple-duration, 0.6s), height var(--ripple-duration, 0.6s); pointer-events: none; } &.ripple-active::before { width: var(--ripple-size, 200px); height: var(--ripple-size, 200px); } } // FLOATING ELEMENTS .floating { animation: floating var(--floating-duration, 3s) ease-in-out infinite; } @keyframes floating { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(var(--floating-distance, -10px)); } } .bob { animation: bob var(--bob-duration, 2s) ease-in-out infinite; } @keyframes bob { 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(var(--bob-distance, -10px)); } 60% { transform: translateY(var(--bob-distance-small, -5px)); } } // ACCESSIBILITY ENHANCEMENTS @media (prefers-reduced-motion: reduce) { .hover-lift, .hover-scale, .hover-glow, .hover-fade, .hover-brighten, .hover-darken, .active-press, .active-sink, .expandable, .expandable-trigger .expand-icon, .sortable .sortable-item, .ripple-effect, .floating, .bob { transition: none; animation: none; } .loading::after { animation: none; } .loading-pulse, .loading-skeleton, .warning-pulse { animation: none; } } @media (prefers-contrast: high) { .focus-ring:focus, .focus-ring:focus-visible { outline-width: 3px; } .selectable { border-width: 3px; } .drop-zone { border-width: 3px; } }