UNPKG

modalitor

Version:

A modern, accessible modal system with focus management, URL hash support, and rich animations

410 lines (364 loc) 9.18 kB
/** * @name Modalitor * @author HamidReza Yazdani * @license MIT * @version 1.0.3 * @description A modern, accessible modal system with focus management, URL hash support, and rich animations */ /* Base modal styles */ .modalitor { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 0.5rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); display: none; max-height: calc(100vh - 2rem); overflow: hidden; width: 100%; margin: 1rem; flex-direction: column; overflow-y: auto; } .modalitor.active { display: flex; } /* Modal sizes */ .modalitor-sm { max-width: 400px; } .modalitor-md { max-width: 600px; } .modalitor-lg { max-width: 800px; } .modalitor-xl { max-width: 1000px; } .modalitor-full { width: 100%; height: 100%; max-height: 100vh; margin: 0; border-radius: 0; } /* Modal sections */ .modalitor-header { padding: 1rem; border-bottom: 1px solid rgba(0, 0, 0, 0.1); display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; position: sticky; top: 0; z-index: 1; background: #fff; } .modalitor-title { margin: 0; font-size: 1.25rem; font-weight: 600; line-height: 1.5; } .modalitor-content { position: relative; flex: 1 1 auto; padding: 1rem; overflow-y: auto; } .modalitor-footer { padding: 0.75rem 1rem; border-top: 1px solid rgba(0, 0, 0, 0.1); display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 0.5rem; flex-shrink: 0; position: sticky; bottom: 0; z-index: 1; background: #fff; } /* Close button */ .modalitor-close { position: relative; padding: 0.5rem; background: none; border: none; width: 2.5rem; height: 2.5rem; display: flex; align-items: center; justify-content: center; border-radius: 0.375rem; cursor: pointer; color: #666; transition: background-color 0.2s, color 0.2s; } .modalitor-close:hover { background-color: rgba(0, 0, 0, 0.05); color: #000; } .modalitor-close::before { content: "×"; font-size: 1.5rem; line-height: 1; } /* Overlay */ .modalitor-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); display: none; } .modalitor-overlay.active { display: block; } /* Base Animation Setup */ .modalitor[class*="animation-"] { opacity: 0; display: flex !important; } .modalitor[class*="animation-"].active { opacity: 1; } /* Animation Keyframes */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } @keyframes unfoldIn { 0% { transform: translate(-50%, -50%) scaleY(.005) scaleX(0); } 50% { transform: translate(-50%, -50%) scaleY(.005) scaleX(1); } 100% { transform: translate(-50%, -50%) scaleY(1) scaleX(1); } } @keyframes unfoldOut { 0% { transform: translate(-50%, -50%) scaleY(1) scaleX(1); } 50% { transform: translate(-50%, -50%) scaleY(.005) scaleX(1); } 100% { transform: translate(-50%, -50%) scaleY(.005) scaleX(0); } } @keyframes zoomIn { from { transform: translate(-50%, -50%) scale3d(0.3, 0.3, 0.3); opacity: 0; } to { transform: translate(-50%, -50%) scale3d(1, 1, 1); opacity: 1; } } @keyframes zoomOut { from { transform: translate(-50%, -50%) scale3d(1, 1, 1); opacity: 1; } to { transform: translate(-50%, -50%) scale3d(0.3, 0.3, 0.3); opacity: 0; } } @keyframes slideDown { from { transform: translate(-50%, -100%); } to { transform: translate(-50%, -50%); } } @keyframes slideUp { from { transform: translate(-50%, 0%); } to { transform: translate(-50%, -50%); } } @keyframes slideLeft { from { transform: translate(-100%, -50%); } to { transform: translate(-50%, -50%); } } @keyframes slideRight { from { transform: translate(0%, -50%); } to { transform: translate(-50%, -50%); } } @keyframes flipIn { from { transform: translate(-50%, -50%) perspective(400px) rotate3d(1, 0, 0, 60deg); opacity: 0; } 70% { transform: translate(-50%, -50%) perspective(400px) rotate3d(1, 0, 0, -15deg); opacity: 1; } to { transform: translate(-50%, -50%) perspective(400px); opacity: 1; } } @keyframes flipOut { from { transform: translate(-50%, -50%) perspective(400px); opacity: 1; } 30% { transform: translate(-50%, -50%) perspective(400px) rotate3d(1, 0, 0, -15deg); opacity: 1; } to { transform: translate(-50%, -50%) perspective(400px) rotate3d(1, 0, 0, 45deg); opacity: 0; } } @keyframes rotateIn { from { transform: translate(-50%, -50%) rotate3d(0, 0, 1, -180deg) scale3d(0.3, 0.3, 0.3); opacity: 0; } to { transform: translate(-50%, -50%) rotate3d(0, 0, 1, 0) scale3d(1, 1, 1); opacity: 1; } } @keyframes rotateOut { from { transform: translate(-50%, -50%) rotate3d(0, 0, 1, 0) scale3d(1, 1, 1); opacity: 1; } to { transform: translate(-50%, -50%) rotate3d(0, 0, 1, 180deg) scale3d(0.3, 0.3, 0.3); opacity: 0; } } @keyframes doorIn { from { transform: translate(-50%, -50%) scale3d(0, 1, 1); opacity: 0; } to { transform: translate(-50%, -50%) scale3d(1, 1, 1); opacity: 1; } } @keyframes doorOut { from { transform: translate(-50%, -50%) scale3d(1, 1, 1); opacity: 1; } 60% { transform: translate(-50%, -50%) scale3d(0.01, 1, 1); opacity: 1; } to { transform: translate(-50%, -50%) scale3d(0, 1, 0.1); opacity: 0; } } @keyframes roadRunnerIn { 0% { transform: translate(-1500px, -50%) skewX(30deg) scaleX(1.3); } 70% { transform: translate(-30px, -50%) skewX(0deg) scaleX(.9); } 100% { transform: translate(-50%, -50%) skewX(0deg) scaleX(1); } } @keyframes roadRunnerOut { 0% { transform: translate(-50%, -50%) skewX(0deg) scaleX(1); } 30% { transform: translate(-30px, -50%) skewX(-5deg) scaleX(.9); } 100% { transform: translate(1500px, -50%) skewX(30deg) scaleX(1.3); } } /* Animation Classes */ .modalitor.animation-fade { &.active { animation: fadeIn 0.3s forwards; } &.closing { animation: fadeOut 0.3s forwards; } } .modalitor.animation-unfold { &.active { animation: unfoldIn 0.6s cubic-bezier(0.165, 0.840, 0.440, 1.000) forwards; } &.closing { animation: unfoldOut 0.6s cubic-bezier(0.165, 0.840, 0.440, 1.000) forwards; } } .modalitor.animation-zoom { &.active { animation: zoomIn 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } &.closing { animation: zoomOut 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } } .modalitor.animation-slide-down { &.active { animation: slideDown 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } &.closing { animation: slideUp 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } } .modalitor.animation-slide-up { &.active { animation: slideUp 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } &.closing { animation: slideDown 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } } .modalitor.animation-slide-left { &.active { animation: slideLeft 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } &.closing { animation: slideRight 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } } .modalitor.animation-slide-right { &.active { animation: slideRight 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } &.closing { animation: slideLeft 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } } .modalitor.animation-flip { perspective: 1300px; &.active { animation: flipIn 0.6s ease-in forwards; backface-visibility: visible !important; } &.closing { animation: flipOut 0.6s ease-out forwards; backface-visibility: visible !important; } } .modalitor.animation-rotate { &.active { animation: rotateIn 0.6s ease forwards; transform-origin: center; } &.closing { animation: rotateOut 0.6s ease forwards; transform-origin: center; } } .modalitor.animation-door { &.active { animation: doorIn 0.5s cubic-bezier(0.4, 0, 0, 1.5) forwards; } &.closing { animation: doorOut 0.5s ease forwards; } } .modalitor.animation-roadrunner { &.active { animation: roadRunnerIn 0.8s cubic-bezier(0.36, 0, 0.66, -0.56) forwards; } &.closing { animation: roadRunnerOut 0.5s ease forwards; } } /* Responsive adjustments */ @media (max-width: 640px) { .modalitor { margin: 1rem; max-height: calc(100vh - 2rem); } .modalitor-sm, .modalitor-md, .modalitor-lg, .modalitor-xl { max-width: calc(100vw - 2rem); } } /* Mobile animations override */ @media (max-width: 1024px) { .modalitor[class*="animation-"] { &.active { animation: slideUp 0.4s ease forwards; } &.closing { animation: slideDown 0.4s ease forwards; } } }