Files
immich/web/src/app.css
T
midzelis aa9234ec5c feat(web): hero view transitions for memory viewer
Change-Id: I6221557a6b8561122baccbc651a48ae46a6a6964
2026-04-16 14:32:10 +00:00

698 lines
17 KiB
CSS

@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@source "../node_modules/@immich/ui";
/* @import '/usr/ui/dist/theme/default.css'; */
@utility immich-form-input {
@apply bg-gray-100 ring-1 ring-gray-200 transition outline-none focus-within:ring-1 disabled:cursor-not-allowed dark:bg-gray-800 dark:ring-neutral-900 flex w-full items-center rounded-lg disabled:bg-gray-300 disabled:text-dark dark:disabled:bg-gray-900 dark:disabled:text-gray-200 flex-1 py-2.5 text-base pl-4 pr-4;
}
@utility immich-form-label {
@apply font-medium text-gray-500 dark:text-gray-300;
}
@utility immich-scrollbar {
/* width */
scrollbar-width: thin;
}
@utility scrollbar-hidden {
/* Hidden scrollbar */
/* width */
scrollbar-width: none;
}
@utility scrollbar-stable {
scrollbar-gutter: stable both-edges;
}
@utility grid-auto-fit-* {
grid-template-columns: repeat(auto-fit, minmax(min(calc(var(--spacing) * --value(number)), 100%), 1fr));
}
@utility grid-auto-fill-* {
grid-template-columns: repeat(auto-fill, minmax(min(calc(var(--spacing) * --value(number)), 100%), 1fr));
}
@custom-variant dark (&:where(.dark, .dark *):not(.light));
@theme inline {
--color-immich-primary: rgb(var(--immich-primary));
--color-immich-bg: rgb(var(--immich-bg));
--color-immich-fg: rgb(var(--immich-fg));
--color-immich-gray: rgb(var(--immich-gray));
--color-immich-dark-primary: rgb(var(--immich-dark-primary));
--color-immich-dark-bg: rgb(var(--immich-dark-bg));
--color-immich-dark-fg: rgb(var(--immich-dark-fg));
--color-immich-dark-gray: rgb(var(--immich-dark-gray));
}
@theme {
--font-sans: 'GoogleSans', sans-serif;
--font-mono: 'GoogleSansCode', monospace;
--spacing-18: 4.5rem;
--breakpoint-tall: 800px;
--breakpoint-2xl: 1535px;
--breakpoint-xl: 1279px;
--breakpoint-lg: 1023px;
--breakpoint-md: 767px;
--breakpoint-sm: 639px;
--breakpoint-sidebar: 850px;
}
@layer base {
:root {
/* light */
--immich-primary: 66 80 175;
--immich-bg: 255 255 255;
--immich-fg: 0 0 0;
/* dark */
--immich-dark-primary: 172 203 250;
--immich-dark-bg: 10 10 10;
--immich-dark-fg: 229 231 235;
--immich-dark-gray: 33 33 33;
/* view transition variables */
--vt-duration-default: 250ms;
--vt-duration-hero: 280ms;
--vt-memory-easing: cubic-bezier(0.2, 0, 0, 1);
}
button:not(:disabled),
[role='button']:not(:disabled) {
cursor: pointer;
}
}
@layer utilities {
@font-face {
font-family: 'GoogleSans';
src: url('$lib/assets/fonts/GoogleSans/GoogleSans.ttf') format('truetype-variations');
font-weight: 410 900;
font-style: normal;
ascent-override: 106.25%;
size-adjust: 106.25%;
}
@font-face {
font-family: 'GoogleSansCode';
src: url('$lib/assets/fonts/GoogleSansCode/GoogleSansCode.ttf') format('truetype-variations');
font-weight: 1 900;
font-style: monospace;
}
:root {
font-family: var(--font-sans);
letter-spacing: 0.1px;
/* Used by layouts to ensure proper spacing between navbar and content */
--navbar-height: calc(4.5rem + 4px);
--navbar-height-md: calc(4.5rem + 4px - 14px);
}
:root.dark {
color-scheme: dark;
}
:root:not(.dark) {
color-scheme: light;
}
html {
height: 100%;
width: 100%;
}
html::-webkit-scrollbar {
width: 8px;
}
/* Track */
html::-webkit-scrollbar-track {
background-color: var(--immich-ui-light-100);
border-radius: 4px;
}
/* Handle */
html::-webkit-scrollbar-thumb {
background-color: color-mix(in oklab, var(--immich-ui-light-600) 40%, transparent);
border-radius: 4px;
}
/* Handle on hover */
html::-webkit-scrollbar-thumb:hover {
background-color: color-mix(in oklab, var(--immich-ui-primary-500) 70%, transparent);
}
body {
margin: 0;
color: var(--immich-ui-dark);
}
body.asset-viewer-open {
background-color: black;
}
input:focus-visible {
outline-offset: 0px !important;
outline: none !important;
}
.text-white-shadow {
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
.icon-white-drop-shadow {
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.8));
}
}
.maplibregl-popup {
.maplibregl-popup-tip {
@apply border-t-subtle! translate-y-[-1px];
}
.maplibregl-popup-content {
@apply bg-subtle rounded-lg;
}
}
@layer base {
::view-transition {
background: var(--color-black);
animation-duration: var(--vt-duration-default);
}
::view-transition-old(*),
::view-transition-new(*) {
mix-blend-mode: normal;
animation-duration: inherit;
}
::view-transition-old(*) {
animation-name: fadeOut;
animation-fill-mode: forwards;
}
::view-transition-new(*) {
animation-name: fadeIn;
animation-fill-mode: forwards;
}
::view-transition-old(root) {
animation: var(--vt-duration-default) 0s fadeOut forwards;
}
::view-transition-new(root) {
animation: var(--vt-duration-default) 0s fadeIn forwards;
}
::view-transition-image-pair(info) {
isolation: auto;
}
::view-transition-old(info) {
animation: var(--vt-duration-default) 0s panelSlideOutRight forwards;
}
::view-transition-new(info) {
animation: var(--vt-duration-default) 0s panelSlideInRight forwards;
}
::view-transition-group(detail-panel) {
z-index: 1;
}
::view-transition-old(detail-panel),
::view-transition-new(detail-panel) {
animation: none;
}
::view-transition-group(exclude-previousbutton),
::view-transition-group(exclude-nextbutton),
::view-transition-group(exclude) {
animation: none;
z-index: 5;
}
::view-transition-old(exclude-previousbutton),
::view-transition-old(exclude-nextbutton),
::view-transition-old(exclude) {
visibility: hidden;
}
::view-transition-new(exclude-previousbutton),
::view-transition-new(exclude-nextbutton),
::view-transition-new(exclude) {
animation: none;
z-index: 5;
}
::view-transition-group(hero) {
animation-duration: var(--vt-duration-hero);
animation-timing-function: var(--vt-memory-easing);
}
::view-transition-old(hero) {
animation: none;
display: none;
}
::view-transition-new(hero) {
animation: none;
align-content: center;
}
::view-transition-old(memory-overlay),
::view-transition-old(memory-controls),
::view-transition-new(memory-overlay),
::view-transition-new(memory-controls) {
width: 100%;
height: 100%;
object-fit: none;
object-position: left top;
}
html:active-view-transition-type(memory) {
&::view-transition-group(hero),
&::view-transition-group(hero-out) {
animation-duration: var(--vt-duration-memory);
animation-timing-function: var(--vt-memory-easing);
overflow: hidden;
z-index: 1;
}
&::view-transition-group(memory-overlay),
&::view-transition-group(memory-controls) {
animation: none;
z-index: 5;
}
&::view-transition-group(memory-overlay-prev),
&::view-transition-group(memory-overlay-next) {
animation: none;
z-index: 2;
opacity: 0.25;
}
&::view-transition-image-pair(memory-overlay),
&::view-transition-image-pair(memory-controls) {
isolation: auto;
}
&::view-transition-old(memory-overlay),
&::view-transition-old(memory-controls) {
animation: 120ms linear fadeOut forwards;
}
&::view-transition-new(memory-overlay),
&::view-transition-new(memory-controls) {
animation: 200ms linear calc(var(--vt-duration-memory) - 200ms) fadeIn forwards;
opacity: 0;
}
&::view-transition-old(memory-overlay-prev),
&::view-transition-old(memory-overlay-next) {
display: none;
}
&::view-transition-new(memory-overlay-prev),
&::view-transition-new(memory-overlay-next) {
animation: none;
width: 100%;
height: 100%;
object-fit: none;
object-position: left top;
}
&::view-transition-image-pair(hero) {
isolation: auto;
}
&::view-transition-old(hero) {
display: none;
}
&::view-transition-new(hero) {
animation: none;
object-fit: cover;
width: 100%;
height: 100%;
}
&::view-transition-image-pair(hero-out) {
isolation: auto;
}
&::view-transition-old(hero-out) {
display: none;
}
&::view-transition-new(hero-out) {
animation: var(--vt-duration-memory) var(--vt-memory-easing) dimDown forwards;
object-fit: cover;
width: 100%;
height: 100%;
}
&::view-transition-group(memory-departing) {
animation: none;
}
&::view-transition-old(memory-departing) {
animation: calc(var(--vt-duration-memory) * 0.4) linear fadeFromDim forwards;
}
&::view-transition-new(memory-departing) {
animation: none;
visibility: hidden;
}
}
html:active-view-transition-type(memory-enter) {
&::view-transition-group(hero) {
animation-duration: var(--vt-duration-hero);
animation-timing-function: var(--vt-memory-easing);
overflow: hidden;
}
&::view-transition-old(hero),
&::view-transition-new(hero) {
animation: none;
object-fit: cover;
width: 100%;
height: 100%;
}
&::view-transition-group(memory-overlay),
&::view-transition-group(memory-controls),
&::view-transition-group(memory-nav-buttons) {
animation: none;
z-index: 5;
}
&::view-transition-old(memory-overlay),
&::view-transition-old(memory-controls),
&::view-transition-old(memory-nav-buttons) {
animation: none;
visibility: hidden;
}
&::view-transition-new(memory-overlay),
&::view-transition-new(memory-controls),
&::view-transition-new(memory-nav-buttons) {
animation: 200ms linear var(--vt-duration-hero) fadeIn forwards;
opacity: 0;
}
}
::view-transition-old(memory-fade-out) {
animation: 500ms linear crossfadeOut forwards;
}
::view-transition-new(memory-fade-in) {
animation: 500ms linear crossfadeIn forwards;
}
html:active-view-transition-type(memory-nav-fast) {
&::view-transition-old(memory-fade-out) {
animation-duration: 250ms;
}
&::view-transition-new(memory-fade-in) {
animation-duration: 250ms;
}
&::view-transition-old(memory-overlay),
&::view-transition-old(memory-controls) {
animation-duration: 100ms;
}
&::view-transition-new(memory-overlay),
&::view-transition-new(memory-controls) {
animation: 100ms linear 150ms fadeIn forwards;
opacity: 0;
}
}
html:active-view-transition-type(memory-nav) {
&::view-transition-group(memory-overlay),
&::view-transition-group(memory-controls) {
animation: none;
z-index: 5;
}
&::view-transition-image-pair(memory-overlay),
&::view-transition-image-pair(memory-controls) {
isolation: auto;
}
&::view-transition-old(memory-overlay),
&::view-transition-old(memory-controls) {
animation: 150ms linear fadeOut forwards;
}
&::view-transition-new(memory-overlay),
&::view-transition-new(memory-controls) {
animation: 200ms linear 300ms fadeIn forwards;
opacity: 0;
}
&::view-transition-group(memory-overlay-prev),
&::view-transition-group(memory-overlay-next) {
animation: none;
opacity: 0.25;
}
&::view-transition-old(memory-overlay-prev),
&::view-transition-old(memory-overlay-next) {
display: none;
}
&::view-transition-new(memory-overlay-prev),
&::view-transition-new(memory-overlay-next) {
animation: none;
}
}
::view-transition-old(next),
::view-transition-old(next-old),
::view-transition-new(next),
::view-transition-new(next-new),
::view-transition-old(previous),
::view-transition-old(previous-old),
::view-transition-new(previous),
::view-transition-new(previous-new) {
animation-duration: var(--vt-duration-viewer-navigation);
animation-timing-function: var(--vt-viewer-slide-easing);
animation-fill-mode: forwards;
}
::view-transition-old(next),
::view-transition-old(next-old),
::view-transition-old(previous),
::view-transition-old(previous-old) {
opacity: var(--vt-viewer-old-opacity);
}
::view-transition-old(next),
::view-transition-old(next-old) {
animation-name: var(--vt-viewer-next-out);
}
::view-transition-new(next),
::view-transition-new(next-new) {
animation-name: var(--vt-viewer-next-in);
}
::view-transition-old(previous),
::view-transition-old(previous-old) {
animation-name: var(--vt-viewer-prev-out);
}
::view-transition-new(previous),
::view-transition-new(previous-new) {
animation-name: var(--vt-viewer-prev-in);
}
::view-transition-old(next-old),
::view-transition-new(next-new),
::view-transition-old(previous-old),
::view-transition-new(previous-new) {
overflow: hidden;
}
::view-transition-old(previous-old) {
z-index: -1;
}
@keyframes fadeFromDim {
from {
opacity: 0.25;
}
to {
opacity: 0;
}
}
@keyframes dimDown {
from {
opacity: 1;
}
to {
opacity: 0.25;
}
}
@keyframes flyInLeft {
from {
transform: translateX(calc(-1 * var(--vt-viewer-slide-distance)));
opacity: var(--vt-viewer-opacity-start);
filter: blur(var(--vt-viewer-blur-max));
}
to {
opacity: 1;
filter: blur(0);
}
}
@keyframes flyOutLeft {
from {
opacity: 1;
filter: blur(0);
}
to {
transform: translateX(calc(-1 * var(--vt-viewer-slide-distance)));
opacity: var(--vt-viewer-opacity-start);
filter: blur(var(--vt-viewer-blur-max));
}
}
@keyframes flyInRight {
from {
transform: translateX(var(--vt-viewer-slide-distance));
opacity: var(--vt-viewer-opacity-start);
filter: blur(var(--vt-viewer-blur-max));
}
to {
opacity: 1;
filter: blur(0);
}
}
@keyframes flyOutRight {
from {
opacity: 1;
filter: blur(0);
}
to {
transform: translateX(var(--vt-viewer-slide-distance));
opacity: var(--vt-viewer-opacity-start);
filter: blur(var(--vt-viewer-blur-max));
}
}
@keyframes panelSlideInRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes panelSlideOutRight {
from {
transform: translateX(0);
}
to {
transform: translateX(100%);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
50% {
opacity: 0.85;
}
to {
opacity: 1;
}
}
@keyframes fadeOut {
from {
opacity: 1;
}
50% {
opacity: 0.85;
}
to {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
::view-transition-group(hero) {
animation-name: none;
}
::view-transition-old(hero) {
animation: none;
display: none;
}
::view-transition-new(hero) {
animation: none;
}
html:active-view-transition-type(viewer) {
&::view-transition-old(hero) {
animation: none;
display: none;
}
&::view-transition-new(hero) {
animation: var(--vt-duration-default) 0s fadeIn forwards;
}
}
html:active-view-transition-type(timeline) {
&::view-transition-old(hero) {
animation: var(--vt-duration-default) 0s fadeOut forwards;
}
&::view-transition-new(hero) {
animation: var(--vt-duration-default) 0s fadeIn forwards;
}
}
::view-transition-group(letterbox-left),
::view-transition-group(letterbox-right),
::view-transition-group(letterbox-top),
::view-transition-group(letterbox-bottom) {
z-index: 100;
}
::view-transition-old(letterbox-left),
::view-transition-old(letterbox-right),
::view-transition-old(letterbox-top),
::view-transition-old(letterbox-bottom),
::view-transition-new(letterbox-left),
::view-transition-new(letterbox-right),
::view-transition-new(letterbox-top),
::view-transition-new(letterbox-bottom) {
background-color: transparent;
}
html:active-view-transition-type(viewer-nav) {
&::view-transition-group(previous),
&::view-transition-group(previous-old),
&::view-transition-group(next),
&::view-transition-group(next-old) {
width: 100% !important;
height: 100% !important;
transform: none !important;
}
&::view-transition-old(previous),
&::view-transition-old(previous-old),
&::view-transition-old(next),
&::view-transition-old(next-old) {
animation: var(--vt-duration-viewer-navigation) fadeOut forwards;
transform-origin: center;
height: 100%;
width: 100%;
object-fit: contain;
overflow: hidden;
}
&::view-transition-new(previous),
&::view-transition-new(previous-new),
&::view-transition-new(next),
&::view-transition-new(next-new) {
animation: var(--vt-duration-viewer-navigation) fadeIn forwards;
transform-origin: center;
height: 100%;
width: 100%;
object-fit: contain;
}
}
html:active-view-transition-type(memory-enter) {
&::view-transition-group(hero) {
animation-duration: 0s;
}
&::view-transition-old(hero) {
animation: var(--vt-duration-default) fadeOut forwards;
}
&::view-transition-new(hero) {
animation: var(--vt-duration-default) fadeIn forwards;
}
}
}
}