UNPKG

@casoon/dragonfly

Version:

Modular, lightweight CSS framework and design system for modern web projects. Optimized for Astro JS, LightningCSS and Container Queries with @layer-based architecture and comprehensive accessibility.

657 lines (555 loc) 14.7 kB
/** * Interaktions-Effekte * * Diese Datei enthält verschiedene Interaktions-Effekte für moderne UIs. * Die Effekte sind performant optimiert und berücksichtigen reduzierte Bewegung. * * Verwendung: * * 1. Drag & Drop: * <div class="draggable">Ziehbares Element</div> * <div class="drop-target">Ablageziel</div> * * 2. Swipe-Gesten: * <div class="swipe-container"> * <div class="swipe-content">Swipe-bare Inhalte</div> * </div> * * 3. Touch-Feedback: * <button class="touch-feedback">Touch-Button</button> * * 4. Fokus-Ringe: * <button class="focus-ring">Fokusierbarer Button</button> * * 5. Deaktivierte Elemente: * <button class="disabled" disabled>Deaktivierter Button</button> * * 6. Ladezustände: * <div class="loading">Ladeanimation</div> * * 7. 3D-Transformationen: * <div class="card-flip"> * <div class="card-flip-inner"> * <div class="card-flip-front">Vorderseite</div> * <div class="card-flip-back">Rückseite</div> * </div> * </div> * * 8. Cursor-Effekte: * <div class="cursor-follow">Folgt dem Cursor</div> * * 9. Touch-Ripple-Effekt: * <button class="touch-ripple">Ripple Button</button> * * Technische Details: * - Verwendet CSS-Variablen für Farben und Größen * - Optimiert mit will-change und transform * - Responsive Anpassungen für mobile Geräte * - Reduzierte Bewegung wird berücksichtigt * * Performance-Hinweise: * - Interaktionen sind hardware-beschleunigt * - Touch-Feedback wird auf mobilen Geräten optimiert * - Reduzierte Bewegung wird automatisch berücksichtigt */ /* Animationen - außerhalb von @layer definieren */ @keyframes loading { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } @keyframes ripple { 0% { opacity: var(--opacity-100); transform: var(--transform-scale-0%); } 100% { opacity: var(--opacity-0); transform: scale(3%); } } /* Effekte und Interaktionen */ @layer effects { /* Basis-Interaktionen */ .interactive { cursor: pointer; transition: all 0.3s ease; } .interactive:active { transform: var(--transform-scale-98); } .interactive:disabled { cursor: not-allowed; opacity: var(--opacity-60); } /* Click Effects */ .click-ripple { overflow: hidden; position: relative; } .click-ripple::after { background-image: radial-gradient(circle, #fff 10%, transparent 10.01%); background-position: 5%0; background-repeat: no-repeat; content: ''; height: 100%; left: 0%; opacity: var(--opacity-0); pointer-events: none; position: absolute; top: 0%; transform: scale(10, 10); transition: transform 0.5s, opacity 1s; width: 100%; } .click-ripple:active::after { opacity: var(--opacity-30); transform: scale(0, 0); transition: 0s; } /* Drag-Effekte */ .draggable { cursor: grab; user-select: none; } .draggable:active { cursor: grabbing; } .drag-feedback { transition: transform 0.2s ease, box-shadow 0.2s ease; } .drag-feedback:active { box-shadow: 0 var(--spacing-1) var(--spacing-2) rgb(0 0 0 / 10%); transform: scale(1.02); } /* Scroll-Effekte */ .scroll-smooth { scroll-behavior: smooth; } .scroll-snap { -webkit-overflow-scrolling: touch; overflow-x: auto; scroll-snap-type: x mandatory; } .scroll-snap-item { scroll-snap-align: start; } /* Touch-Effekte */ .touch-feedback { -webkit-tap-highlight-color: transparent; } .touch-feedback:active { background-color: rgb(0 0 0 / 10%); } /* Hover-Effekte */ .hover-lift { transition: transform 0.3s ease, box-shadow 0.3s ease; } .hover-lift:hover { box-shadow: 0 var(--spacing-1) var(--spacing-2) rgb(0 0 0 / 10%); transform: translateY(calc(-1 * var(--border-width-thick))); } .hover-scale { transition: transform 0.3s ease; } .hover-scale:hover { transform: scale(1.05); } /* Fokus-Effekte */ .focus-visible { outline: none; } .focus-visible:focus-visible { box-shadow: 0 0 0 var(--spacing-1) rgb(59 130 246 / 50%); } /* Aktive Zustände */ .active-press { transition: transform 0.1s ease; } .active-press:active { transform: var(--transform-scale-95); } .active-feedback { transition: background-color 0.2s ease; } .active-feedback:active { background-color: rgb(0 0 0 / 10%); } /* Deaktivierte Zustände */ .disabled { cursor: not-allowed; opacity: var(--opacity-60); pointer-events: none; } .disabled-feedback { filter: var(--filter-grayscale-100); opacity: var(--opacity-70); } /* Drag & Drop */ .drop-target { transition: background-color 0.2s ease; will-change: background-color; } .drop-target.drag-over { background-color: var(--color-primary-light); } /* Swipe-Gesten */ .swipe-container { overflow: hidden; position: relative; will-change: transform; } .swipe-content { touch-action: pan-x pan-y; will-change: transform; } /* Ladezustände */ .loading { background: linear-gradient( 90deg, var(--color-gray-100), var(--color-gray-200), var(--color-gray-100) ); background-size: 200% 100; overflow: hidden; position: relative; } .loading::after { animation: loading 1.5s infinite; background: linear-gradient( 90deg, transparent, var(--color-gray-50), transparent ); content: ''; height: 100%; left: 0%; position: absolute; top: 0%; width: 100%; } /* Weitere Interaktionen - Sub-Layer */ .touch-feedback::after { background-color: var(--color-ripple, rgb(0 0 0 / 10%)); } .focus-ring:focus-visible { box-shadow: 0 0 0 var(--spacing-1) var(--color-focus-ring, rgb(59 130 246 / 50%)); outline: none; } .drag-handle { cursor: move; touch-action: none; } .drag-item { user-select: none; } .swipeable { touch-action: pan-x; } .touch-area { position: relative; -webkit-tap-highlight-color: transparent; touch-action: manipulation; } /* Hover-Interaktionen */ .hover-effect { transition: transform 0.3s var(--easing-decelerate), box-shadow 0.3s var(--easing-decelerate); } .hover-rotate { transition: transform 0.3s var(--easing-decelerate); } .hover-rotate:hover { transform: rotate(var(--rotate-angle, 5deg)); } .hover-shadow { transition: box-shadow 0.3s var(--easing-decelerate); } .hover-shadow:hover { box-shadow: var(--shadow-hover, 0 var(--spacing-2-5) var(--spacing-1)(--spacing-1) -var(--spacing-1) rgb(0 0 0 / 10%)); } .hover-text-mask { color: var(--color-text, inherit); position: relative; } .hover-text-mask::before { background-color: currentColor; content: ''; height: 100%; left: 0%; opacity: var(--opacity-10); position: absolute; top: 0%; transform: scaleX(0%); transform-origin: left; transition: transform 0.3s var(--easing-decelerate); width: 100%; z-index: -1; } .hover-text-mask:hover::before { transform: scaleX(1%); } /* 3D-Transformationen */ .card-flip { perspective: var(--space-large-1000); } .card-flip-inner { height: 100%; position: relative; transform: rotateY(var(--initial-rotate, 0deg)); transform-style: preserve-3d; transition: transform 0.6s; width: 100%; } .card-flip:hover .card-flip-inner { transform: rotateY(180deg); } .card-flip-front, .card-flip-back { backface-visibility: hidden; height: 100%; position: absolute; width: 100%; } .card-flip-back { transform: rotateY(180deg); } /* Tiefenschicht-Effekte */ .depth-shift { transition: transform 0.3s var(--easing-decelerate); } .depth-shift:hover { transform: translateZ(var(--depth, var(--spacing-5))); transform-style: preserve-3d; } /* Cursor-Interaktionen */ .cursor-follow { transform: translate(calc(var(--cursor-x, 0%) * 0.var(--border-width)), calc(var(--cursor-y, 0) * 0.var(--border-width))); transition: transform 0.1s var(--easing-decelerate); } .touch-ripple { overflow: hidden; position: relative; } .touch-ripple::after { background-image: radial-gradient( circle, var(--color-ripple, #fff) 10, transparent 10.01% ); background-position: 5%0; background-repeat: no-repeat; content: ''; height: 100%; left: 0%; opacity: var(--opacity-0); pointer-events: none; position: absolute; top: 0%; transform: scale(10, 10); transition: transform 0.5s, opacity 1s; width: 100%; } .touch-ripple:active::after { opacity: var(--opacity-30); transform: scale(0, 0); transition: 0s; } /* Textmasken-Effekte */ .text-mask { background: var(--text-mask-bg, linear-gradient(90deg, #333, #555)); background-clip: text; color: transparent; transition: background 0.3s var(--easing-decelerate); } .text-mask:hover { background: var(--text-mask-hover, linear-gradient(90deg, #555, #777)); } /* Fokusringe */ .focus-ring { outline: none; } .focus-ring:focus-visible { box-shadow: 0 0 0 var(--border-width-thick) var(--color-bg, white), 0 0 0 var(--spacing-1) var(--color-focus, rgb(59 130 246)); } /* Klapp-Effekte */ .fold-reveal { max-height: var(--fold-height, 100px); overflow: hidden; transition: max-height 0.6s var(--easing-decelerate); } .fold-reveal-content { opacity: var(--fold-opacity, 60%); transform: translateY(var(--fold-translate, calc(-1 * var(--spacing-5)))); transition: opacity 0.6s var(--easing-decelerate), transform 0.6s var(--easing-decelerate); } .fold-reveal.unfolded { max-height: var(--fold-max-height, var(--space-large-1000)); } .fold-reveal.unfolded .fold-reveal-content { opacity: var(--opacity-100); transform: translateX(0%); } } /* Allgemeine Rückmeldungs-Effekte */ @layer effects { .feedback-ripple { overflow: hidden; position: relative; } .feedback-ripple::after { background: var(--ripple-color, rgb(255 255 255 / 70%)); border-radius: 1pxpx0; content: ''; height: var(--spacing-1); left: 5%0%; opacity: var(--opacity-0); position: absolute; top: 5%0%; transform: scale(1%, 1) translate(-50, -50%); transform-origin: 50% 50; width: var(--spacing-1); } .feedback-ripple:active::after { animation: ripple 0.4s var(--easing-decelerate); opacity: var(--opacity-0); } } /* Extra Layer für UI-Controls */ @layer utilities { .slider-handle { cursor: ew-resize; touch-action: pan-x; user-select: none; } .slider-handle:active { cursor: grabbing; } @media (prefers-reduced-motion: reduce) { .slider-handle:active { transition: var(--transition-none); } } } /* Barrierefreiheits-Anpassungen */ @media (prefers-reduced-motion: reduce) { @layer utilities { .interactive, .click-ripple, .drag-feedback, .scroll-smooth, .hover-lift, .hover-scale, .active-press, .active-feedback { transition: var(--transition-none); } .click-ripple::after { transition: var(--transition-none); } .feedback-ripple:active::after { animation: var(--animation-none); } } } /* Container Queries ersetzen Media Queries */ @container (width < 640px) { @layer effects { .touch-feedback::after { height: 150px; width: 150px; } .focus-ring:focus-visible { box-shadow: 0 0 0 var(--border-width) var(--color-primary); } } } /* Mobile Anpassungen */ @container root (width <= 640px) { @layer effects { /* Anpassungen für Touch-Interaktionen auf kleinen Displays */ .drag-handle { height: var(--spacing-3)(--border-width-thick); width: var(--spacing-3)(--border-width-thick); } .drag-item { padding: var(--spacing-1)(--border-width-thick); } .swipeable { -webkit-overflow-scrolling: touch; } .touch-area { min-height: var(--spacing-4)(--spacing-1); min-width: var(--spacing-4)(--spacing-1); padding: var(--spacing-1)(--border-width-thick); } /* Visuelles Feedback für Hover-Effekte deaktivieren */ .hover-effect { display: none; } } } @layer components { /* Hover Effects */ .hover-rotate { transition: var(--transition-transform); } .hover-rotate:hover { transform: rotate(3deg); } .hover-shadow { transition: var(--transition-shadow); } .hover-shadow:hover { box-shadow: var(--shadow-hover); } .hover-text-mask { overflow: hidden; position: relative; } .hover-text-mask::before { background: var(--color-gradient); block-size: 100%; content: ''; inline-size: 100%; inset-block-start: 0%; inset-inline-start: 0; position: absolute; transform: translateX(-100%); transition: transform 0.3s ease; will-change: transform; } .hover-text-mask:hover::before { transform: translateX(0%); } /* 3D-Transformationen */ .card-flip { perspective: var(--space-large-1000); } .card-flip-inner { block-size: 100%; inline-size: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.6s; will-change: transform; } .card-flip:hover .card-flip-inner { transform: rotateY(180deg); } .card-flip-front, .card-flip-back { backface-visibility: hidden; block-size: 100%; inline-size: 100%; position: absolute; } .card-flip-back { transform: rotateY(180deg); } }