@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
CSS
/**
* 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);
}
}