chore: upgrade to tailwind v4 (#18353)

This commit is contained in:
Daniel Dietzler 2025-05-18 15:51:33 +02:00 committed by GitHub
parent c411c1472a
commit 56156b97e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 930 additions and 733 deletions

1302
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@
"dependencies": {
"@formatjs/icu-messageformat-parser": "^2.9.8",
"@immich/sdk": "file:../open-api/typescript-sdk",
"@immich/ui": "^0.21.1",
"@immich/ui": "^0.22.0",
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
"@mdi/js": "^7.4.47",
"@photo-sphere-viewer/core": "^5.11.5",
@ -52,6 +52,7 @@
"svelte-i18n": "^4.0.1",
"svelte-maplibre": "^1.0.0",
"svelte-persisted-store": "^0.12.0",
"tabbable": "^6.2.0",
"thumbhash": "^0.1.1"
},
"devDependencies": {
@ -63,6 +64,8 @@
"@sveltejs/enhanced-img": "^0.5.0",
"@sveltejs/kit": "^2.15.2",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/postcss": "^4.1.7",
"@tailwindcss/vite": "^4.1.7",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/svelte": "^5.2.6",
"@testing-library/user-event": "^14.5.2",
@ -89,7 +92,7 @@
"rollup-plugin-visualizer": "^5.14.0",
"svelte": "^5.25.3",
"svelte-check": "^4.1.5",
"tailwindcss": "^3.4.17",
"tailwindcss": "^4.1.7",
"tslib": "^2.6.2",
"typescript": "^5.7.3",
"typescript-eslint": "^8.28.0",

View File

@ -1,6 +1,5 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@tailwindcss/postcss': {},
},
};

View File

@ -1,6 +1,32 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
@import '@immich/ui/theme/default.css';
@config '../tailwind.config.js';
@utility immich-form-input {
@apply rounded-xl bg-slate-200 px-3 py-3 text-sm focus:border-immich-primary disabled:cursor-not-allowed disabled:bg-gray-400 disabled:text-gray-100 dark:bg-gray-600 dark:text-immich-dark-fg dark:disabled:bg-gray-800 dark:disabled:text-gray-200;
}
@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;
}
@custom-variant dark (&:where(.dark, .dark *));
@layer base {
:root {
@ -21,8 +47,22 @@
--immich-dark-success: 56 142 60;
--immich-dark-warning: 245 124 0;
}
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: rgb(var(--immich-ui-default-border));
}
button:not(:disabled),
[role='button']:not(:disabled) {
cursor: pointer;
}
}
@layer utilities {
@font-face {
font-family: 'Overpass';
src: url('$lib/assets/fonts/overpass/Overpass.ttf') format('truetype-variations');
@ -44,8 +84,8 @@
:root {
font-family: 'Overpass', sans-serif;
/* Used by layouts to ensure proper spacing between navbar and content */
--navbar-height: calc(theme(spacing.18) + 4px);
--navbar-height-md: calc(theme(spacing.18) + 4px - 14px);
--navbar-height: calc(4.5rem + 4px);
--navbar-height-md: calc(4.5rem + 4px - 14px);
}
:root.dark {
@ -100,28 +140,4 @@ input:focus-visible {
.icon-white-drop-shadow {
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.8));
}
@layer utilities {
.immich-form-input {
@apply rounded-xl bg-slate-200 px-3 py-3 text-sm focus:border-immich-primary disabled:cursor-not-allowed disabled:bg-gray-400 disabled:text-gray-100 dark:bg-gray-600 dark:text-immich-dark-fg dark:disabled:bg-gray-800 dark:disabled:text-gray-200;
}
.immich-form-label {
@apply font-medium text-gray-500 dark:text-gray-300;
}
/* width */
.immich-scrollbar {
scrollbar-width: thin;
}
/* Hidden scrollbar */
/* width */
.scrollbar-hidden {
scrollbar-width: none;
}
.scrollbar-stable {
scrollbar-gutter: stable both-edges;
}
}

View File

@ -102,7 +102,7 @@
</head>
<noscript
class="absolute z-[1000] flex h-screen w-screen place-content-center place-items-center bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg"
class="absolute z-1000 flex h-screen w-screen place-content-center place-items-center bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg"
>
To use Immich, you must enable JavaScript or use a JavaScript compatible browser.
</noscript>

View File

@ -51,11 +51,7 @@
class="w-full text-start mt-2 pt-2 pe-2 pb-2 rounded-md transition-colors cursor-pointer dark:text-immich-dark-fg hover:text-immich-primary dark:hover:text-immich-dark-primary hover:bg-subtle dark:hover:bg-immich-dark-gray"
aria-expanded={!isCollapsed}
>
<Icon
path={mdiChevronRight}
size="24"
class="inline-block -mt-2.5 transition-all duration-[250ms] {iconRotation}"
/>
<Icon path={mdiChevronRight} size="24" class="inline-block -mt-2.5 transition-all duration-250 {iconRotation}" />
<span class="font-bold text-3xl text-black dark:text-white">{group.name}</span>
<span class="ms-1.5">({$t('albums_count', { values: { count: albums.length } })})</span>
</button>

View File

@ -58,7 +58,7 @@
}}
/>
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-[var(--navbar-height-md)] pt-[var(--navbar-height)]">
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
<AssetGrid enableRouting={true} {album} {assetStore} {assetInteraction}>
<section class="pt-8 md:pt-24 px-2 md:px-0">
<!-- ALBUM TITLE -->

View File

@ -56,7 +56,7 @@
<Icon
path={mdiChevronRight}
size="20"
class="inline-block -mt-2 transition-all duration-[250ms] {iconRotation}"
class="inline-block -mt-2 transition-all duration-250 {iconRotation}"
/>
<span class="font-bold text-2xl">{albumGroup.name}</span>
<span class="ms-1.5">

View File

@ -108,7 +108,7 @@
</script>
<div
class="flex h-16 place-items-center justify-between bg-gradient-to-b from-black/40 px-3 transition-transform duration-200"
class="flex h-16 place-items-center justify-between bg-linear-to-b from-black/40 px-3 transition-transform duration-200"
>
<div class="text-white">
{#if showCloseButton}

View File

@ -216,7 +216,7 @@
slow: ??ms
-->
<div
class={['group absolute top-[0px] bottom-[0px]', { 'cursor-not-allowed': disabled, 'cursor-pointer': !disabled }]}
class={['group absolute -top-[0px] -bottom-[0px]', { 'cursor-not-allowed': disabled, 'cursor-pointer': !disabled }]}
style:width="inherit"
style:height="inherit"
onmouseenter={onMouseEnter}
@ -266,7 +266,7 @@
{#if !usingMobileDevice && !disabled}
<div
class={[
'absolute h-full w-full bg-gradient-to-b from-black/25 via-[transparent_25%] opacity-0 transition-opacity group-hover:opacity-100',
'absolute h-full w-full bg-linear-to-b from-black/25 via-[transparent_25%] opacity-0 transition-opacity group-hover:opacity-100',
{ 'rounded-xl': selected },
]}
></div>
@ -279,7 +279,7 @@
<!-- Outline on focus -->
<div
class={[
'absolute size-full group-focus-visible:outline outline-4 -outline-offset-4 outline-immich-primary',
'absolute size-full group-focus-visible:outline-immich-primary focus:outline-4 -outline-offset-4 outline-immich-primary',
{ 'rounded-xl': selected },
]}
></div>

View File

@ -108,7 +108,7 @@
{#if showMenu}
<div
transition:fly={{ y: -30, duration: 250 }}
class="text-sm font-medium z-[1] absolute flex min-w-[250px] max-h-[70vh] overflow-y-auto immich-scrollbar flex-col rounded-2xl bg-gray-100 py-2 text-black shadow-lg dark:bg-gray-700 dark:text-white {className} {getAlignClass(
class="text-sm font-medium z-1 absolute flex min-w-[250px] max-h-[70vh] overflow-y-auto immich-scrollbar flex-col rounded-2xl bg-gray-100 py-2 text-black shadow-lg dark:bg-gray-700 dark:text-white {className} {getAlignClass(
position,
)}"
>
@ -117,7 +117,7 @@
{@const buttonStyle = renderedOption.disabled ? '' : 'transition-all hover:bg-gray-300 dark:hover:bg-gray-800'}
<button
type="button"
class="grid grid-cols-[36px,1fr] place-items-center p-2 disabled:opacity-40 {buttonStyle}"
class="grid grid-cols-[36px_1fr] place-items-center p-2 disabled:opacity-40 {buttonStyle}"
disabled={renderedOption.disabled}
onclick={() => !renderedOption.disabled && handleSelectOption(option)}
>

View File

@ -15,7 +15,7 @@
</script>
<fieldset
class="dark:bg-immich-dark-gray flex h-full rounded-2xl bg-gray-200 ring-gray-400 has-[:focus-visible]:ring dark:ring-gray-600"
class="dark:bg-immich-dark-gray flex h-full rounded-2xl bg-gray-200 ring-gray-400 has-focus-visible:ring dark:ring-gray-600"
>
<legend class="sr-only">{label}</legend>
{#each filters as filter, index (`${id}-${index}`)}

View File

@ -10,7 +10,7 @@
let { id, label, name, value, group = $bindable(undefined) }: Props = $props();
</script>
<div class="flex items-center space-x-2">
<div class="flex items-center gap-2">
<input type="radio" {name} {id} {value} class="focus-visible:ring" bind:group />
<label for={id}>{label}</label>
</div>

View File

@ -62,7 +62,7 @@
</a>
{#if showVerticalDots}
<div class="absolute top-2 end-2 z-[1]">
<div class="absolute top-2 end-2 z-1">
<ButtonContextMenu
buttonClass="icon-white-drop-shadow focus:opacity-100 {showVerticalDots ? 'opacity-100' : 'opacity-0'}"
color="opaque"

View File

@ -54,7 +54,7 @@
<AlbumCover {album} class="h-[200px] w-[200px] m-4 shadow-lg" />
</div>
<div class="flex-grow">
<div class="grow">
<div class="m-4 flex flex-col gap-2">
<label class="immich-form-label" for="name">{$t('name')}</label>
<input class="immich-form-input" id="name" type="text" bind:value={albumName} />

View File

@ -12,7 +12,11 @@
<section class="min-w-dvw flex min-h-dvh items-center justify-center relative">
<div class="absolute -z-10 w-full h-full flex place-items-center place-content-center">
<img src={immichLogo} class="max-w-screen-md mx-auto h-full mb-2 antialiased overflow-hidden" alt="Immich logo" />
<img
src={immichLogo}
class="max-w-(--breakpoint-md) mx-auto h-full mb-2 antialiased overflow-hidden"
alt="Immich logo"
/>
<div
class="w-full h-[99%] absolute start-0 top-0 backdrop-blur-[200px] bg-transparent dark:bg-immich-dark-bg/20"
></div>

View File

@ -36,7 +36,7 @@
}: Props = $props();
let scrollbarClass = $derived(scrollbar ? 'immich-scrollbar p-2' : 'scrollbar-hidden');
let hasTitleClass = $derived(title ? 'top-16 h-[calc(100%-theme(spacing.16))]' : 'top-0 h-full');
let hasTitleClass = $derived(title ? 'top-16 h-[calc(100%-(--spacing(16)))]' : 'top-0 h-full');
</script>
<header>
@ -48,10 +48,10 @@
</header>
<div
tabindex="-1"
class="relative z-0 grid grid-cols-[theme(spacing.0)_auto] overflow-hidden sidebar:grid-cols-[theme(spacing.64)_auto]
class="relative z-0 grid grid-cols-[--spacing(0)_auto] overflow-hidden sidebar:grid-cols-[--spacing(64)_auto]
{hideNavbar ? 'h-dvh' : 'h-[calc(100dvh-var(--navbar-height))]'}
{hideNavbar ? 'pt-[var(--navbar-height)]' : ''}
{hideNavbar ? 'max-md:pt-[var(--navbar-height-md)]' : ''}"
{hideNavbar ? 'pt-(--navbar-height)' : ''}
{hideNavbar ? 'max-md:pt-(--navbar-height-md)' : ''}"
>
{#if sidebar}
{@render sidebar()}

View File

@ -406,7 +406,7 @@
<!-- Viewer -->
<section class="overflow-hidden pt-32 md:pt-20" bind:clientHeight={viewerHeight}>
<div
class="ms-[-100%] box-border flex h-[calc(100vh_-_224px)] md:h-[calc(100vh_-_180px)] w-[300%] items-center justify-center gap-10 overflow-hidden"
class="ms-[-100%] box-border flex h-[calc(100vh-224px)] md:h-[calc(100vh-180px)] w-[300%] items-center justify-center gap-10 overflow-hidden"
>
<!-- PREVIOUS MEMORY -->
<div class="h-1/2 w-[20vw] rounded-2xl {current.previousMemory ? 'opacity-25 hover:opacity-70' : 'opacity-0'}">

View File

@ -47,7 +47,7 @@
{#if canScrollLeft || canScrollRight}
<div class="sticky start-0">
{#if canScrollLeft}
<div class="absolute start-4 top-[6rem]" transition:fade={{ duration: 200 }}>
<div class="absolute start-4 top-24" transition:fade={{ duration: 200 }}>
<button
type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
@ -60,7 +60,7 @@
</div>
{/if}
{#if canScrollRight}
<div class="absolute end-4 top-[6rem]" transition:fade={{ duration: 200 }}>
<div class="absolute end-4 top-24" transition:fade={{ duration: 200 }}>
<button
type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
@ -77,7 +77,7 @@
<div class="inline-block" use:resizeObserver={({ width }) => (innerWidth = width)}>
{#each memoryStore.memories as memory (memory.id)}
<a
class="memory-card relative me-2 md:me-4 last:me-0 inline-block aspect-[3/4] md:aspect-[4/3] max-md:h-[150px] xl:aspect-video h-[215px] rounded-xl"
class="memory-card relative me-2 md:me-4 last:me-0 inline-block aspect-3/4 md:aspect-4/3 max-md:h-[150px] xl:aspect-video h-[215px] rounded-xl"
href="{AppRoute.MEMORY}?{QueryParameter.ID}={memory.assets[0].id}"
>
<img
@ -87,7 +87,7 @@
draggable="false"
/>
<div
class="absolute start-0 top-0 h-full w-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent transition-all hover:bg-black/20"
class="absolute start-0 top-0 h-full w-full rounded-xl bg-linear-to-t from-black/40 via-transparent to-transparent transition-all hover:bg-black/20"
></div>
<p class="absolute bottom-2 start-4 text-lg text-white max-md:text-sm">
{$memoryLaneTitle(memory)}

View File

@ -28,11 +28,7 @@
class="w-fit mt-2 pt-2 pe-2 mb-2 dark:text-immich-dark-fg"
aria-expanded={!isCollapsed}
>
<Icon
path={mdiChevronRight}
size="24"
class="inline-block -mt-2.5 transition-all duration-[250ms] {iconRotation}"
/>
<Icon path={mdiChevronRight} size="24" class="inline-block -mt-2.5 transition-all duration-250 {iconRotation}" />
<span class="font-bold text-3xl text-black dark:text-white">{group.name}</span>
<span class="ms-1.5">({$t('places_count', { values: { count: places.length } })})</span>
</button>

View File

@ -112,7 +112,7 @@
{/each}
{/if}
{:else}
<div class="flex min-h-[calc(66vh_-_11rem)] w-full place-content-center items-center dark:text-white">
<div class="flex min-h-[calc(66vh-11rem)] w-full place-content-center items-center dark:text-white">
<div class="flex flex-col content-center items-center text-center">
<Icon path={mdiMapMarkerOff} size="3.5em" />
<p class="mt-5 text-3xl font-medium">{$t('no_places')}</p>

View File

@ -277,7 +277,7 @@
class:!rounded-b-none={isOpen && dropdownDirection === 'bottom'}
class:!rounded-t-none={isOpen && dropdownDirection === 'top'}
class:cursor-pointer={!isActive}
class="immich-form-input text-sm w-full !pe-12 transition-all"
class="immich-form-input text-sm w-full pe-12! transition-all"
id={inputId}
onfocus={activate}
oninput={onInput}
@ -341,7 +341,7 @@
role="listbox"
id={listboxId}
transition:fly={{ duration: 250 }}
class="fixed z-[1] text-start text-sm w-full overflow-y-auto bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-900"
class="fixed z-1 text-start text-sm w-full overflow-y-auto bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-900"
class:rounded-b-xl={dropdownDirection === 'bottom'}
class:rounded-t-xl={dropdownDirection === 'top'}
class:shadow={dropdownDirection === 'bottom'}

View File

@ -73,7 +73,7 @@
bind:this={menuElement}
class="{isVisible
? 'max-h-dvh'
: 'max-h-0'} flex flex-col transition-all duration-[250ms] ease-in-out outline-none overflow-auto"
: 'max-h-0'} flex flex-col transition-all duration-250 ease-in-out outline-none overflow-auto"
role="menu"
tabindex="-1"
>

View File

@ -66,7 +66,7 @@
let buttonClass = $derived(forceDark ? 'hover:text-immich-dark-gray' : undefined);
</script>
<div in:fly={{ y: 10, duration: 200 }} class="absolute top-0 w-full bg-transparent z-[1]">
<div in:fly={{ y: 10, duration: 200 }} class="absolute top-0 w-full bg-transparent z-1">
<nav
id="asset-selection-app-bar"
class={[

View File

@ -52,17 +52,17 @@
$effect(() => {
switch (width) {
case 'extra-wide': {
modalWidth = 'w-[56rem]';
modalWidth = 'w-4xl';
break;
}
case 'wide': {
modalWidth = 'w-[48rem]';
modalWidth = 'w-3xl';
break;
}
case 'narrow': {
modalWidth = 'w-[28rem]';
modalWidth = 'w-md';
break;
}

View File

@ -497,7 +497,7 @@
/>
{#if showAssetName && !isTimelineAsset(currentAsset)}
<div
class="absolute text-center p-1 text-xs font-mono font-semibold w-full bottom-0 bg-gradient-to-t bg-slate-50/75 overflow-clip text-ellipsis whitespace-pre-wrap"
class="absolute text-center p-1 text-xs font-mono font-semibold w-full bottom-0 bg-linear-to-t bg-slate-50/75 overflow-clip text-ellipsis whitespace-pre-wrap"
>
{currentAsset.originalFileName}
</div>

View File

@ -25,7 +25,7 @@
in:fade={{ duration: 100 }}
out:fade={{ duration: 100 }}
id="account-info-panel"
class="absolute z-[1] end-[25px] top-[75px] w-[min(360px,100vw-50px)] rounded-3xl bg-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray"
class="absolute z-1 end-[25px] top-[75px] w-[min(360px,100vw-50px)] rounded-3xl bg-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray"
use:focusTrap
>
<div

View File

@ -51,13 +51,10 @@
<svelte:window bind:innerWidth />
<nav
id="dashboard-navbar"
class="max-md:h-[var(--navbar-height-md)] h-[var(--navbar-height)] w-dvw text-sm overflow-hidden"
>
<nav id="dashboard-navbar" class="max-md:h-(--navbar-height-md) h-(--navbar-height) w-dvw text-sm overflow-hidden">
<SkipLink text={$t('skip_to_content')} />
<div
class="grid h-full grid-cols-[theme(spacing.32)_auto] items-center py-2 sidebar:grid-cols-[theme(spacing.64)_auto] {noBorder
class="grid h-full grid-cols-[--spacing(32)_auto] items-center py-2 sidebar:grid-cols-[--spacing(64)_auto] {noBorder
? ''
: 'border-b'}"
>

View File

@ -39,7 +39,7 @@
in:fade={{ duration: 100 }}
out:fade={{ duration: 100 }}
id="notification-panel"
class="absolute right-[25px] top-[70px] z-[1] w-[min(360px,100vw-50px)] rounded-3xl bg-gray-100 border border-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray text-light"
class="absolute right-[25px] top-[70px] z-1 w-[min(360px,100vw-50px)] rounded-3xl bg-gray-100 border border-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray text-light"
use:focusTrap
>
<Stack class="max-h-[500px]">

View File

@ -19,7 +19,7 @@
<div class="relative w-full">
<input
{...rest}
class="immich-form-input w-full !pe-12"
class="immich-form-input w-full pe-12!"
type={showPassword ? 'text' : 'password'}
{required}
value={password}

View File

@ -446,7 +446,7 @@
aria-valuemax={toScrollY(1)}
aria-valuemin={toScrollY(0)}
data-id="immich-scrubbable-scrollbar"
class="absolute end-0 z-[1] select-none hover:cursor-row-resize"
class="absolute end-0 z-1 select-none hover:cursor-row-resize"
style:padding-top={PADDING_TOP + 'px'}
style:padding-bottom={PADDING_BOTTOM + 'px'}
style:width
@ -483,7 +483,7 @@
out:fade={{ duration: 200 }}
>
<Icon path={mdiPlay} size="20" class="-rotate-90 relative top-[9px] -end-[2px]" />
<Icon path={mdiPlay} size="20" class="rotate-90 relative top-[1px] -end-[2px]" />
<Icon path={mdiPlay} size="20" class="rotate-90 relative top-px -end-[2px]" />
{#if (assetStore.scrolling && scrollHoverLabel) || isHover || isDragging}
<p
transition:fade={{ duration: 200 }}
@ -521,7 +521,7 @@
data-label={segments.at(0)?.dateFormatted}
>
{#if relativeTopOffset > 6}
<div class="absolute end-[0.75rem] h-[4px] w-[4px] rounded-full bg-gray-300"></div>
<div class="absolute end-3 h-[4px] w-[4px] rounded-full bg-gray-300"></div>
{/if}
</div>
<!-- Time Segment -->
@ -535,12 +535,12 @@
>
{#if !usingMobileDevice}
{#if segment.hasLabel}
<div class="absolute end-[1.25rem] top-[-16px] text-[12px] dark:text-immich-dark-fg font-immich-mono">
<div class="absolute end-5 top-[-16px] text-[12px] dark:text-immich-dark-fg font-immich-mono">
{segment.date.year}
</div>
{/if}
{#if segment.hasDot}
<div class="absolute end-[0.75rem] bottom-0 h-[4px] w-[4px] rounded-full bg-gray-300"></div>
<div class="absolute end-3 bottom-0 h-[4px] w-[4px] rounded-full bg-gray-300"></div>
{/if}
{/if}
</div>

View File

@ -34,7 +34,7 @@
{#if queryType === 'smart'}
<label for="context-input" class="immich-form-label">{$t('search_by_context')}</label>
<input
class="immich-form-input hover:cursor-text w-full !mt-1"
class="immich-form-input hover:cursor-text w-full mt-1!"
type="text"
id="context-input"
name="context"
@ -44,7 +44,7 @@
{:else if queryType === 'metadata'}
<label for="file-name-input" class="immich-form-label">{$t('search_by_filename')}</label>
<input
class="immich-form-input hover:cursor-text w-full !mt-1"
class="immich-form-input hover:cursor-text w-full mt-1!"
type="text"
id="file-name-input"
name="file-name"
@ -55,7 +55,7 @@
{:else if queryType === 'description'}
<label for="description-input" class="immich-form-label">{$t('search_by_description')}</label>
<input
class="immich-form-input hover:cursor-text w-full !mt-1"
class="immich-form-input hover:cursor-text w-full mt-1!"
type="text"
id="description-input"
name="description"

View File

@ -70,7 +70,7 @@
: 'text-immich-fg dark:text-immich-bg'}"
/>
<select
class="immich-form-input w-full appearance-none row-start-1 col-start-1 !pe-6"
class="immich-form-input w-full appearance-none row-start-1 col-start-1 pe-6!"
{disabled}
aria-describedby={desc ? `${name}-desc` : undefined}
{name}

View File

@ -31,7 +31,7 @@
<a
href={getLink(path)}
title={value}
class={`flex flex-grow place-items-center ps-2 py-1 text-sm rounded-lg hover:bg-slate-200 dark:hover:bg-slate-800 hover:font-semibold ${isTarget ? 'bg-slate-100 dark:bg-slate-700 font-semibold text-immich-primary dark:text-immich-dark-primary' : 'dark:text-gray-200'}`}
class={`flex grow place-items-center ps-2 py-1 text-sm rounded-lg hover:bg-slate-200 dark:hover:bg-slate-800 hover:font-semibold ${isTarget ? 'bg-slate-100 dark:bg-slate-700 font-semibold text-immich-primary dark:text-immich-dark-primary' : 'dark:text-gray-200'}`}
data-sveltekit-keepfocus
>
<button type="button" {onclick} class={Object.values(tree).length === 0 ? 'invisible' : ''}>

View File

@ -35,7 +35,7 @@
id="sidebar"
aria-label={ariaLabel}
tabindex="-1"
class="immich-scrollbar relative z-[1] w-0 sidebar:w-[16rem] overflow-y-auto overflow-x-hidden pt-8 transition-all duration-200 bg-light"
class="immich-scrollbar relative z-1 w-0 sidebar:w-[16rem] overflow-y-auto overflow-x-hidden pt-8 transition-all duration-200 bg-light"
class:shadow-2xl={isExpanded}
class:dark:border-e-immich-dark-gray={isExpanded}
class:border-r={isExpanded}

View File

@ -113,7 +113,7 @@
]}
/>
<div class="pt-4 rounded-3xl border dark:border-2 border-gray-300 dark:border-gray-700 max-w-[54rem] mx-auto mb-16">
<div class="pt-4 rounded-3xl border dark:border-2 border-gray-300 dark:border-gray-700 max-w-216 mx-auto mb-16">
<div class="flex flex-wrap gap-y-6 mb-4 px-6 w-full place-content-end justify-between">
<!-- MARK ALL BUTTONS -->
<div class="flex text-xs text-black">

View File

@ -91,7 +91,7 @@
</div>
<!-- <UserAvatar {user} size="md" /> -->
<div class="text-start flex-grow">
<div class="text-start grow">
<p class="text-immich-fg dark:text-immich-dark-fg">
{user.name}
</p>
@ -133,7 +133,7 @@
class="flex w-full place-items-center gap-4 p-4"
>
<UserAvatar {user} size="md" />
<div class="text-start flex-grow">
<div class="text-start grow">
<p class="text-immich-fg dark:text-immich-dark-fg">
{user.name}
</p>

View File

@ -168,7 +168,7 @@
<Modal icon={mdiTune} size="giant" title={$t('search_options')} {onClose}>
<ModalBody>
<form id={formId} autocomplete="off" {onsubmit} {onreset}>
<div class="space-y-10 pb-10" tabindex="-1">
<div class="flex flex-col gap-4 pb-10" tabindex="-1">
<!-- PEOPLE -->
<SearchPeopleSection bind:selectedPeople={filter.personIds} />

View File

@ -597,7 +597,7 @@
{/if}
{/if}
<main class="relative h-dvh overflow-hidden px-6 max-md:pt-[var(--navbar-height-md)] pt-[var(--navbar-height)]">
<main class="relative h-dvh overflow-hidden px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">
<AssetGrid
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
{album}
@ -699,7 +699,7 @@
</AssetGrid>
{#if showActivityStatus}
<div class="absolute z-[2] bottom-0 end-0 mb-6 me-6 justify-self-end">
<div class="absolute z-2 bottom-0 end-0 mb-6 me-6 justify-self-end">
<ActivityStatus
disabled={!album.isActivityEnabled}
isLiked={activityManager.isLiked}
@ -716,7 +716,7 @@
<div
transition:fly={{ duration: 150 }}
id="activity-panel"
class="z-[2] w-[360px] md:w-[460px] overflow-y-auto transition-all dark:border-l dark:border-s-immich-dark-gray"
class="z-2 w-[360px] md:w-[460px] overflow-y-auto transition-all dark:border-l dark:border-s-immich-dark-gray"
translate="yes"
>
<ActivityViewer

View File

@ -153,7 +153,7 @@
<Breadcrumbs {pathSegments} icon={mdiFolderHome} title={$t('folders')} getLink={getLinkForPath} />
<section class="mt-2 h-[calc(100%-theme(spacing.20))] overflow-auto immich-scrollbar">
<section class="mt-2 h-[calc(100%-(--spacing(20)))] overflow-auto immich-scrollbar">
<TreeItemThumbnails items={currentTreeItems} icon={mdiFolder} onClick={handleNavigateToFolder} />
<!-- Assets -->

View File

@ -387,7 +387,7 @@
{/snippet}
</PeopleInfiniteScroll>
{:else}
<div class="flex min-h-[calc(66vh_-_11rem)] w-full place-content-center items-center dark:text-white">
<div class="flex min-h-[calc(66vh-11rem)] w-full place-content-center items-center dark:text-white">
<div class="flex flex-col content-center items-center text-center">
<Icon path={mdiAccountOff} size="3.5em" />
<p class="mt-5 text-3xl font-medium max-w-lg line-clamp-2 overflow-hidden">

View File

@ -362,7 +362,7 @@
</script>
<main
class="relative z-0 h-dvh overflow-hidden tall:ms-4 md:pt-[var(--navbar-height-md)] pt-[var(--navbar-height)]"
class="relative z-0 h-dvh overflow-hidden tall:ms-4 md:pt-(--navbar-height-md) pt-(--navbar-height)"
use:scrollMemoryClearer={{
routeStartsWith: AppRoute.PEOPLE,
beforeClear: () => {

View File

@ -366,7 +366,7 @@
pageHeaderOffset={54}
/>
{:else if !isLoading}
<div class="flex min-h-[calc(66vh_-_11rem)] w-full place-content-center items-center dark:text-white">
<div class="flex min-h-[calc(66vh-11rem)] w-full place-content-center items-center dark:text-white">
<div class="flex flex-col content-center items-center text-center">
<Icon path={mdiImageOffOutline} size="3.5em" />
<p class="mt-5 text-3xl font-medium">{$t('no_results')}</p>

View File

@ -59,7 +59,7 @@
</svelte:head>
{#if passwordRequired}
<main
class="relative h-dvh overflow-hidden px-6 max-md:pt-[var(--navbar-height-md)] pt-[var(--navbar-height)] sm:px-12 md:px-24 lg:px-40"
class="relative h-dvh overflow-hidden px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height) sm:px-12 md:px-24 lg:px-40"
>
<div class="flex flex-col items-center justify-center mt-20">
<div class="text-2xl font-bold text-immich-primary dark:text-immich-dark-primary">{$t('password_required')}</div>

View File

@ -178,7 +178,7 @@
<Breadcrumbs {pathSegments} icon={mdiTagMultiple} title={$t('tags')} {getLink} />
<section class="mt-2 h-[calc(100%-theme(spacing.20))] overflow-auto immich-scrollbar">
<section class="mt-2 h-[calc(100%-(--spacing(20)))] overflow-auto immich-scrollbar">
{#if tag}
<AssetGrid enableRouting={true} {assetStore} {assetInteraction} removeAction={AssetAction.UNARCHIVE}>
{#snippet empty()}

View File

@ -16,7 +16,6 @@
import { copyToClipboard } from '$lib/utils';
import { isAssetViewerRoute } from '$lib/utils/navigation';
import { setTranslations } from '@immich/ui';
import '@immich/ui/theme/default.css';
import { onMount, type Snippet } from 'svelte';
import { t } from 'svelte-i18n';
import { run } from 'svelte/legacy';

View File

@ -1,8 +1,5 @@
import { tailwindConfig } from '@immich/ui/theme/default.js';
import plugin from 'tailwindcss/plugin';
const { colors, borderColor } = tailwindConfig();
/** @type {import('tailwindcss').Config} */
export default {
content: [
@ -10,7 +7,6 @@ export default {
'./node_modules/@immich/ui/dist/**/*.{svelte,js}',
'../../ui/src/**/*.{html,js,svelte,ts}',
],
darkMode: 'class',
theme: {
extend: {
colors: {
@ -31,10 +27,7 @@ export default {
'immich-dark-error': 'rgb(var(--immich-dark-error) / <alpha-value>)',
'immich-dark-success': 'rgb(var(--immich-dark-success) / <alpha-value>)',
'immich-dark-warning': 'rgb(var(--immich-dark-warning) / <alpha-value>)',
...colors,
},
borderColor,
fontFamily: {
'immich-mono': ['Overpass Mono', 'monospace'],
},

View File

@ -1,5 +1,6 @@
import { enhancedImages } from '@sveltejs/enhanced-img';
import { sveltekit } from '@sveltejs/kit/vite';
import tailwindcss from '@tailwindcss/vite';
import { svelteTesting } from '@testing-library/svelte/vite';
import path from 'node:path';
import { visualizer } from 'rollup-plugin-visualizer';
@ -36,6 +37,7 @@ export default defineConfig({
},
plugins: [
enhancedImages(),
tailwindcss(),
sveltekit(),
process.env.BUILD_STATS === 'true'
? visualizer({