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

View File

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

View File

@ -1,6 +1,32 @@
@tailwind base; @import 'tailwindcss';
@tailwind components; @import '@immich/ui/theme/default.css';
@tailwind utilities;
@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 { @layer base {
:root { :root {
@ -21,107 +47,97 @@
--immich-dark-success: 56 142 60; --immich-dark-success: 56 142 60;
--immich-dark-warning: 245 124 0; --immich-dark-warning: 245 124 0;
} }
}
@font-face { *,
font-family: 'Overpass'; ::after,
src: url('$lib/assets/fonts/overpass/Overpass.ttf') format('truetype-variations'); ::before,
font-weight: 1 999; ::backdrop,
font-style: normal; ::file-selector-button {
ascent-override: 106.25%; border-color: rgb(var(--immich-ui-default-border));
size-adjust: 106.25%; }
}
@font-face { button:not(:disabled),
font-family: 'Overpass Mono'; [role='button']:not(:disabled) {
src: url('$lib/assets/fonts/overpass/OverpassMono.ttf') format('truetype-variations'); cursor: pointer;
font-weight: 1 999; }
font-style: monospace;
ascent-override: 106.25%;
size-adjust: 106.25%;
}
: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);
}
: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: #f1f1f1;
border-radius: 16px;
}
/* Handle */
html::-webkit-scrollbar-thumb {
background: rgba(85, 86, 87, 0.408);
border-radius: 16px;
}
/* Handle on hover */
html::-webkit-scrollbar-thumb:hover {
background: #4250afad;
border-radius: 16px;
}
body {
margin: 0;
color: #3a3a3a;
}
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));
} }
@layer utilities { @layer utilities {
.immich-form-input { @font-face {
@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; font-family: 'Overpass';
src: url('$lib/assets/fonts/overpass/Overpass.ttf') format('truetype-variations');
font-weight: 1 999;
font-style: normal;
ascent-override: 106.25%;
size-adjust: 106.25%;
} }
.immich-form-label { @font-face {
@apply font-medium text-gray-500 dark:text-gray-300; font-family: 'Overpass Mono';
src: url('$lib/assets/fonts/overpass/OverpassMono.ttf') format('truetype-variations');
font-weight: 1 999;
font-style: monospace;
ascent-override: 106.25%;
size-adjust: 106.25%;
} }
/* width */ :root {
.immich-scrollbar { font-family: 'Overpass', sans-serif;
scrollbar-width: thin; /* 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);
} }
/* Hidden scrollbar */ :root.dark {
/* width */ color-scheme: dark;
.scrollbar-hidden {
scrollbar-width: none;
} }
.scrollbar-stable { :root:not(.dark) {
scrollbar-gutter: stable both-edges; color-scheme: light;
}
html {
height: 100%;
width: 100%;
}
html::-webkit-scrollbar {
width: 8px;
}
/* Track */
html::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 16px;
}
/* Handle */
html::-webkit-scrollbar-thumb {
background: rgba(85, 86, 87, 0.408);
border-radius: 16px;
}
/* Handle on hover */
html::-webkit-scrollbar-thumb:hover {
background: #4250afad;
border-radius: 16px;
}
body {
margin: 0;
color: #3a3a3a;
}
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));
} }
} }

View File

@ -102,7 +102,7 @@
</head> </head>
<noscript <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. To use Immich, you must enable JavaScript or use a JavaScript compatible browser.
</noscript> </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" 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} aria-expanded={!isCollapsed}
> >
<Icon <Icon path={mdiChevronRight} size="24" class="inline-block -mt-2.5 transition-all duration-250 {iconRotation}" />
path={mdiChevronRight}
size="24"
class="inline-block -mt-2.5 transition-all duration-[250ms] {iconRotation}"
/>
<span class="font-bold text-3xl text-black dark:text-white">{group.name}</span> <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> <span class="ms-1.5">({$t('albums_count', { values: { count: albums.length } })})</span>
</button> </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}> <AssetGrid enableRouting={true} {album} {assetStore} {assetInteraction}>
<section class="pt-8 md:pt-24 px-2 md:px-0"> <section class="pt-8 md:pt-24 px-2 md:px-0">
<!-- ALBUM TITLE --> <!-- ALBUM TITLE -->

View File

@ -56,7 +56,7 @@
<Icon <Icon
path={mdiChevronRight} path={mdiChevronRight}
size="20" 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="font-bold text-2xl">{albumGroup.name}</span>
<span class="ms-1.5"> <span class="ms-1.5">

View File

@ -108,7 +108,7 @@
</script> </script>
<div <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"> <div class="text-white">
{#if showCloseButton} {#if showCloseButton}

View File

@ -216,7 +216,7 @@
slow: ??ms slow: ??ms
--> -->
<div <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:width="inherit"
style:height="inherit" style:height="inherit"
onmouseenter={onMouseEnter} onmouseenter={onMouseEnter}
@ -266,7 +266,7 @@
{#if !usingMobileDevice && !disabled} {#if !usingMobileDevice && !disabled}
<div <div
class={[ 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 }, { 'rounded-xl': selected },
]} ]}
></div> ></div>
@ -279,7 +279,7 @@
<!-- Outline on focus --> <!-- Outline on focus -->
<div <div
class={[ 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 }, { 'rounded-xl': selected },
]} ]}
></div> ></div>

View File

@ -108,7 +108,7 @@
{#if showMenu} {#if showMenu}
<div <div
transition:fly={{ y: -30, duration: 250 }} 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, position,
)}" )}"
> >
@ -117,7 +117,7 @@
{@const buttonStyle = renderedOption.disabled ? '' : 'transition-all hover:bg-gray-300 dark:hover:bg-gray-800'} {@const buttonStyle = renderedOption.disabled ? '' : 'transition-all hover:bg-gray-300 dark:hover:bg-gray-800'}
<button <button
type="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} disabled={renderedOption.disabled}
onclick={() => !renderedOption.disabled && handleSelectOption(option)} onclick={() => !renderedOption.disabled && handleSelectOption(option)}
> >

View File

@ -15,7 +15,7 @@
</script> </script>
<fieldset <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> <legend class="sr-only">{label}</legend>
{#each filters as filter, index (`${id}-${index}`)} {#each filters as filter, index (`${id}-${index}`)}

View File

@ -10,7 +10,7 @@
let { id, label, name, value, group = $bindable(undefined) }: Props = $props(); let { id, label, name, value, group = $bindable(undefined) }: Props = $props();
</script> </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 /> <input type="radio" {name} {id} {value} class="focus-visible:ring" bind:group />
<label for={id}>{label}</label> <label for={id}>{label}</label>
</div> </div>

View File

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

View File

@ -54,7 +54,7 @@
<AlbumCover {album} class="h-[200px] w-[200px] m-4 shadow-lg" /> <AlbumCover {album} class="h-[200px] w-[200px] m-4 shadow-lg" />
</div> </div>
<div class="flex-grow"> <div class="grow">
<div class="m-4 flex flex-col gap-2"> <div class="m-4 flex flex-col gap-2">
<label class="immich-form-label" for="name">{$t('name')}</label> <label class="immich-form-label" for="name">{$t('name')}</label>
<input class="immich-form-input" id="name" type="text" bind:value={albumName} /> <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"> <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"> <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 <div
class="w-full h-[99%] absolute start-0 top-0 backdrop-blur-[200px] bg-transparent dark:bg-immich-dark-bg/20" class="w-full h-[99%] absolute start-0 top-0 backdrop-blur-[200px] bg-transparent dark:bg-immich-dark-bg/20"
></div> ></div>

View File

@ -36,7 +36,7 @@
}: Props = $props(); }: Props = $props();
let scrollbarClass = $derived(scrollbar ? 'immich-scrollbar p-2' : 'scrollbar-hidden'); 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> </script>
<header> <header>
@ -48,10 +48,10 @@
</header> </header>
<div <div
tabindex="-1" 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 ? 'h-dvh' : 'h-[calc(100dvh-var(--navbar-height))]'}
{hideNavbar ? 'pt-[var(--navbar-height)]' : ''} {hideNavbar ? 'pt-(--navbar-height)' : ''}
{hideNavbar ? 'max-md:pt-[var(--navbar-height-md)]' : ''}" {hideNavbar ? 'max-md:pt-(--navbar-height-md)' : ''}"
> >
{#if sidebar} {#if sidebar}
{@render sidebar()} {@render sidebar()}

View File

@ -406,7 +406,7 @@
<!-- Viewer --> <!-- Viewer -->
<section class="overflow-hidden pt-32 md:pt-20" bind:clientHeight={viewerHeight}> <section class="overflow-hidden pt-32 md:pt-20" bind:clientHeight={viewerHeight}>
<div <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 --> <!-- PREVIOUS MEMORY -->
<div class="h-1/2 w-[20vw] rounded-2xl {current.previousMemory ? 'opacity-25 hover:opacity-70' : 'opacity-0'}"> <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} {#if canScrollLeft || canScrollRight}
<div class="sticky start-0"> <div class="sticky start-0">
{#if canScrollLeft} {#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 <button
type="button" type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100" 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> </div>
{/if} {/if}
{#if canScrollRight} {#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 <button
type="button" type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100" 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)}> <div class="inline-block" use:resizeObserver={({ width }) => (innerWidth = width)}>
{#each memoryStore.memories as memory (memory.id)} {#each memoryStore.memories as memory (memory.id)}
<a <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}" href="{AppRoute.MEMORY}?{QueryParameter.ID}={memory.assets[0].id}"
> >
<img <img
@ -87,7 +87,7 @@
draggable="false" draggable="false"
/> />
<div <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> ></div>
<p class="absolute bottom-2 start-4 text-lg text-white max-md:text-sm"> <p class="absolute bottom-2 start-4 text-lg text-white max-md:text-sm">
{$memoryLaneTitle(memory)} {$memoryLaneTitle(memory)}

View File

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

View File

@ -112,7 +112,7 @@
{/each} {/each}
{/if} {/if}
{:else} {: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"> <div class="flex flex-col content-center items-center text-center">
<Icon path={mdiMapMarkerOff} size="3.5em" /> <Icon path={mdiMapMarkerOff} size="3.5em" />
<p class="mt-5 text-3xl font-medium">{$t('no_places')}</p> <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-b-none={isOpen && dropdownDirection === 'bottom'}
class:!rounded-t-none={isOpen && dropdownDirection === 'top'} class:!rounded-t-none={isOpen && dropdownDirection === 'top'}
class:cursor-pointer={!isActive} 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} id={inputId}
onfocus={activate} onfocus={activate}
oninput={onInput} oninput={onInput}
@ -341,7 +341,7 @@
role="listbox" role="listbox"
id={listboxId} id={listboxId}
transition:fly={{ duration: 250 }} 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-b-xl={dropdownDirection === 'bottom'}
class:rounded-t-xl={dropdownDirection === 'top'} class:rounded-t-xl={dropdownDirection === 'top'}
class:shadow={dropdownDirection === 'bottom'} class:shadow={dropdownDirection === 'bottom'}

View File

@ -73,7 +73,7 @@
bind:this={menuElement} bind:this={menuElement}
class="{isVisible class="{isVisible
? 'max-h-dvh' ? '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" role="menu"
tabindex="-1" tabindex="-1"
> >

View File

@ -66,7 +66,7 @@
let buttonClass = $derived(forceDark ? 'hover:text-immich-dark-gray' : undefined); let buttonClass = $derived(forceDark ? 'hover:text-immich-dark-gray' : undefined);
</script> </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 <nav
id="asset-selection-app-bar" id="asset-selection-app-bar"
class={[ class={[

View File

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

View File

@ -497,7 +497,7 @@
/> />
{#if showAssetName && !isTimelineAsset(currentAsset)} {#if showAssetName && !isTimelineAsset(currentAsset)}
<div <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} {currentAsset.originalFileName}
</div> </div>

View File

@ -25,7 +25,7 @@
in:fade={{ duration: 100 }} in:fade={{ duration: 100 }}
out:fade={{ duration: 100 }} out:fade={{ duration: 100 }}
id="account-info-panel" 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 use:focusTrap
> >
<div <div

View File

@ -51,13 +51,10 @@
<svelte:window bind:innerWidth /> <svelte:window bind:innerWidth />
<nav <nav id="dashboard-navbar" class="max-md:h-(--navbar-height-md) h-(--navbar-height) w-dvw text-sm overflow-hidden">
id="dashboard-navbar"
class="max-md:h-[var(--navbar-height-md)] h-[var(--navbar-height)] w-dvw text-sm overflow-hidden"
>
<SkipLink text={$t('skip_to_content')} /> <SkipLink text={$t('skip_to_content')} />
<div <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'}" : 'border-b'}"
> >

View File

@ -39,7 +39,7 @@
in:fade={{ duration: 100 }} in:fade={{ duration: 100 }}
out:fade={{ duration: 100 }} out:fade={{ duration: 100 }}
id="notification-panel" 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 use:focusTrap
> >
<Stack class="max-h-[500px]"> <Stack class="max-h-[500px]">

View File

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

View File

@ -446,7 +446,7 @@
aria-valuemax={toScrollY(1)} aria-valuemax={toScrollY(1)}
aria-valuemin={toScrollY(0)} aria-valuemin={toScrollY(0)}
data-id="immich-scrubbable-scrollbar" 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-top={PADDING_TOP + 'px'}
style:padding-bottom={PADDING_BOTTOM + 'px'} style:padding-bottom={PADDING_BOTTOM + 'px'}
style:width style:width
@ -483,7 +483,7 @@
out:fade={{ duration: 200 }} 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-[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} {#if (assetStore.scrolling && scrollHoverLabel) || isHover || isDragging}
<p <p
transition:fade={{ duration: 200 }} transition:fade={{ duration: 200 }}
@ -521,7 +521,7 @@
data-label={segments.at(0)?.dateFormatted} data-label={segments.at(0)?.dateFormatted}
> >
{#if relativeTopOffset > 6} {#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} {/if}
</div> </div>
<!-- Time Segment --> <!-- Time Segment -->
@ -535,12 +535,12 @@
> >
{#if !usingMobileDevice} {#if !usingMobileDevice}
{#if segment.hasLabel} {#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} {segment.date.year}
</div> </div>
{/if} {/if}
{#if segment.hasDot} {#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}
{/if} {/if}
</div> </div>

View File

@ -34,7 +34,7 @@
{#if queryType === 'smart'} {#if queryType === 'smart'}
<label for="context-input" class="immich-form-label">{$t('search_by_context')}</label> <label for="context-input" class="immich-form-label">{$t('search_by_context')}</label>
<input <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" type="text"
id="context-input" id="context-input"
name="context" name="context"
@ -44,7 +44,7 @@
{:else if queryType === 'metadata'} {:else if queryType === 'metadata'}
<label for="file-name-input" class="immich-form-label">{$t('search_by_filename')}</label> <label for="file-name-input" class="immich-form-label">{$t('search_by_filename')}</label>
<input <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" type="text"
id="file-name-input" id="file-name-input"
name="file-name" name="file-name"
@ -55,7 +55,7 @@
{:else if queryType === 'description'} {:else if queryType === 'description'}
<label for="description-input" class="immich-form-label">{$t('search_by_description')}</label> <label for="description-input" class="immich-form-label">{$t('search_by_description')}</label>
<input <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" type="text"
id="description-input" id="description-input"
name="description" name="description"

View File

@ -70,7 +70,7 @@
: 'text-immich-fg dark:text-immich-bg'}" : 'text-immich-fg dark:text-immich-bg'}"
/> />
<select <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} {disabled}
aria-describedby={desc ? `${name}-desc` : undefined} aria-describedby={desc ? `${name}-desc` : undefined}
{name} {name}

View File

@ -31,7 +31,7 @@
<a <a
href={getLink(path)} href={getLink(path)}
title={value} 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 data-sveltekit-keepfocus
> >
<button type="button" {onclick} class={Object.values(tree).length === 0 ? 'invisible' : ''}> <button type="button" {onclick} class={Object.values(tree).length === 0 ? 'invisible' : ''}>

View File

@ -35,7 +35,7 @@
id="sidebar" id="sidebar"
aria-label={ariaLabel} aria-label={ariaLabel}
tabindex="-1" 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:shadow-2xl={isExpanded}
class:dark:border-e-immich-dark-gray={isExpanded} class:dark:border-e-immich-dark-gray={isExpanded}
class:border-r={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"> <div class="flex flex-wrap gap-y-6 mb-4 px-6 w-full place-content-end justify-between">
<!-- MARK ALL BUTTONS --> <!-- MARK ALL BUTTONS -->
<div class="flex text-xs text-black"> <div class="flex text-xs text-black">

View File

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

View File

@ -168,7 +168,7 @@
<Modal icon={mdiTune} size="giant" title={$t('search_options')} {onClose}> <Modal icon={mdiTune} size="giant" title={$t('search_options')} {onClose}>
<ModalBody> <ModalBody>
<form id={formId} autocomplete="off" {onsubmit} {onreset}> <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 --> <!-- PEOPLE -->
<SearchPeopleSection bind:selectedPeople={filter.personIds} /> <SearchPeopleSection bind:selectedPeople={filter.personIds} />

View File

@ -597,7 +597,7 @@
{/if} {/if}
{/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 <AssetGrid
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true} enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
{album} {album}
@ -699,7 +699,7 @@
</AssetGrid> </AssetGrid>
{#if showActivityStatus} {#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 <ActivityStatus
disabled={!album.isActivityEnabled} disabled={!album.isActivityEnabled}
isLiked={activityManager.isLiked} isLiked={activityManager.isLiked}
@ -716,7 +716,7 @@
<div <div
transition:fly={{ duration: 150 }} transition:fly={{ duration: 150 }}
id="activity-panel" 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" translate="yes"
> >
<ActivityViewer <ActivityViewer

View File

@ -153,7 +153,7 @@
<Breadcrumbs {pathSegments} icon={mdiFolderHome} title={$t('folders')} getLink={getLinkForPath} /> <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} /> <TreeItemThumbnails items={currentTreeItems} icon={mdiFolder} onClick={handleNavigateToFolder} />
<!-- Assets --> <!-- Assets -->

View File

@ -387,7 +387,7 @@
{/snippet} {/snippet}
</PeopleInfiniteScroll> </PeopleInfiniteScroll>
{:else} {: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"> <div class="flex flex-col content-center items-center text-center">
<Icon path={mdiAccountOff} size="3.5em" /> <Icon path={mdiAccountOff} size="3.5em" />
<p class="mt-5 text-3xl font-medium max-w-lg line-clamp-2 overflow-hidden"> <p class="mt-5 text-3xl font-medium max-w-lg line-clamp-2 overflow-hidden">

View File

@ -362,7 +362,7 @@
</script> </script>
<main <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={{ use:scrollMemoryClearer={{
routeStartsWith: AppRoute.PEOPLE, routeStartsWith: AppRoute.PEOPLE,
beforeClear: () => { beforeClear: () => {

View File

@ -366,7 +366,7 @@
pageHeaderOffset={54} pageHeaderOffset={54}
/> />
{:else if !isLoading} {: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"> <div class="flex flex-col content-center items-center text-center">
<Icon path={mdiImageOffOutline} size="3.5em" /> <Icon path={mdiImageOffOutline} size="3.5em" />
<p class="mt-5 text-3xl font-medium">{$t('no_results')}</p> <p class="mt-5 text-3xl font-medium">{$t('no_results')}</p>

View File

@ -59,7 +59,7 @@
</svelte:head> </svelte:head>
{#if passwordRequired} {#if passwordRequired}
<main <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="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> <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} /> <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} {#if tag}
<AssetGrid enableRouting={true} {assetStore} {assetInteraction} removeAction={AssetAction.UNARCHIVE}> <AssetGrid enableRouting={true} {assetStore} {assetInteraction} removeAction={AssetAction.UNARCHIVE}>
{#snippet empty()} {#snippet empty()}

View File

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

View File

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

View File

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