Upgrade to Tailwind 4
This commit is contained in:
parent
d17dc81720
commit
7ab319142f
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -9,6 +9,7 @@
|
||||
"camelcase",
|
||||
"cloudflarestorage",
|
||||
"cmdk",
|
||||
"Consolas",
|
||||
"CredentialsSignin",
|
||||
"datetime",
|
||||
"Eterna",
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
"@next/bundle-analyzer": "15.1.6",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/postcss": "^4.0.5",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
@ -57,7 +58,6 @@
|
||||
"@types/react": "19.0.8",
|
||||
"@types/react-dom": "19.0.3",
|
||||
"@types/sanitize-html": "^2.13.0",
|
||||
"autoprefixer": "10.4.20",
|
||||
"clsx": "^2.1.1",
|
||||
"cross-fetch": "^4.1.0",
|
||||
"eslint": "9.20.0",
|
||||
@ -65,7 +65,7 @@
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"postcss": "8.5.1",
|
||||
"tailwindcss": "3.4.17",
|
||||
"tailwindcss": "4.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "5.7.3"
|
||||
}
|
||||
|
||||
837
pnpm-lock.yaml
generated
837
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ export default function AdminAddAllUploads({
|
||||
<div className="w-full space-y-4 py-1">
|
||||
<div className="flex">
|
||||
<div className={clsx(
|
||||
'flex-grow',
|
||||
'grow',
|
||||
tagErrorMessage ? 'text-error' : 'text-main',
|
||||
)}>
|
||||
{showTags
|
||||
|
||||
@ -156,11 +156,11 @@ export default function AdminBatchEditPanelClient({
|
||||
color="gray"
|
||||
className={clsx(
|
||||
'min-h-[3.5rem]',
|
||||
'backdrop-blur-lg !border-transparent',
|
||||
'!text-gray-900 dark:!text-gray-100',
|
||||
'!bg-gray-100/90 dark:!bg-gray-900/70',
|
||||
'backdrop-blur-lg border-transparent!',
|
||||
'text-gray-900! dark:text-gray-100!',
|
||||
'bg-gray-100/90! dark:bg-gray-900/70!',
|
||||
// Override default <Note /> content spacing
|
||||
'[&>*>*:first-child]:gap-1.5 [&>*>*:first-child]:sm:gap-2.5',
|
||||
'[&>*>*:first-child]:gap-1.5 sm:[&>*>*:first-child]:gap-2.5',
|
||||
)}
|
||||
padding={isInTagMode ? 'tight-cta-right-left' : 'tight-cta-right'}
|
||||
cta={<div className="flex items-center gap-1.5 sm:gap-2.5">
|
||||
|
||||
@ -67,7 +67,7 @@ export default function AdminNavClient({
|
||||
)}>
|
||||
<div className={clsx(
|
||||
'flex gap-0.5 md:gap-1.5 -mx-1',
|
||||
'flex-grow overflow-x-auto',
|
||||
'grow overflow-x-auto',
|
||||
)}>
|
||||
{items.map(({ label, href, count }) =>
|
||||
<LinkWithStatus
|
||||
@ -101,7 +101,7 @@ export default function AdminNavClient({
|
||||
</LinkWithLoader>
|
||||
</div>
|
||||
{shouldShowBanner &&
|
||||
<Note icon={<FaRegClock className="flex-shrink-0" />}>
|
||||
<Note icon={<FaRegClock className="shrink-0" />}>
|
||||
Photo updates detected—they may take several minutes to show up
|
||||
for visitors
|
||||
</Note>}
|
||||
|
||||
@ -42,7 +42,7 @@ export default function AdminPhotosClient({
|
||||
return (
|
||||
<SiteGrid
|
||||
contentMain={
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex">
|
||||
<div className="grow min-w-0">
|
||||
<PhotoUpload
|
||||
|
||||
@ -79,7 +79,7 @@ export default function AdminPhotosTable({
|
||||
</span>
|
||||
{photo.priorityOrder !== null &&
|
||||
<span className={clsx(
|
||||
'text-xs leading-none px-1.5 py-1 rounded-sm',
|
||||
'text-xs leading-none px-1.5 py-1 rounded-xs',
|
||||
'dark:text-gray-300',
|
||||
'bg-gray-100 dark:bg-gray-800',
|
||||
)}>
|
||||
|
||||
@ -35,6 +35,6 @@ export default function AdminTagBadge({
|
||||
return (
|
||||
hideBadge
|
||||
? renderBadgeContent()
|
||||
: <Badge className="!py-[3px]">{renderBadgeContent()}</Badge>
|
||||
: <Badge className="py-[3px]!">{renderBadgeContent()}</Badge>
|
||||
);
|
||||
}
|
||||
@ -14,11 +14,11 @@ export default function DeleteButton({
|
||||
icon={<BiTrash size={16} />}
|
||||
spinnerColor="text"
|
||||
className={clsx(
|
||||
'!text-red-500 dark:!text-red-600',
|
||||
'active:!bg-red-100/50 active:dark:!bg-red-950/50',
|
||||
'disabled:!bg-red-100/50 disabled:dark:!bg-red-950/50',
|
||||
'!border-red-200 hover:!border-red-300',
|
||||
'dark:!border-red-900/75 dark:hover:!border-red-900',
|
||||
'text-red-500! dark:text-red-600!',
|
||||
'active:bg-red-100/50! dark:active:bg-red-950/50!',
|
||||
'disabled:bg-red-100/50! dark:disabled:bg-red-950/50!',
|
||||
'border-red-200! hover:border-red-300!',
|
||||
'dark:border-red-900/75! dark:hover:border-red-900!',
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -35,11 +35,11 @@ export default function DeleteFormButton (
|
||||
spinnerColor="text"
|
||||
className={clsx(
|
||||
className,
|
||||
'!text-red-500 dark:!text-red-600',
|
||||
'active:!bg-red-100/50 active:dark:!bg-red-950/50',
|
||||
'disabled:!bg-red-100/50 disabled:dark:!bg-red-950/50',
|
||||
'!border-red-200 hover:!border-red-300',
|
||||
'dark:!border-red-900/75 dark:hover:!border-red-900',
|
||||
'text-red-500! dark:text-red-600!',
|
||||
'active:bg-red-100/50! dark:active:bg-red-950/50!',
|
||||
'disabled:bg-red-100/50! dark:disabled:bg-red-950/50!',
|
||||
'border-red-200! hover:border-red-300!',
|
||||
'dark:border-red-900/75! dark:hover:border-red-900!',
|
||||
)}
|
||||
onFormSubmit={onFormSubmit}
|
||||
/>;
|
||||
|
||||
@ -49,7 +49,7 @@ export default function GitHubForkStatusBadgeClient({
|
||||
'border transition-colors',
|
||||
'select-none',
|
||||
'pl-[4.5px] pr-2.5 py-[3px]',
|
||||
'rounded-full shadow-sm',
|
||||
'rounded-full shadow-xs',
|
||||
classNameForStyle(),
|
||||
)}>
|
||||
{!label
|
||||
|
||||
@ -43,7 +43,7 @@ export default function ComponentsPage() {
|
||||
</div>
|
||||
<div className={clsx(
|
||||
'flex gap-1',
|
||||
'[&>*]:inline-flex [&>*]:gap-1 [&_input]:-translate-y-0.5',
|
||||
'*:inline-flex *:gap-1 [&_input]:-translate-y-0.5',
|
||||
)}>
|
||||
<FieldSetWithStatus
|
||||
id="grid"
|
||||
@ -62,7 +62,7 @@ export default function ComponentsPage() {
|
||||
</div>
|
||||
</h1>
|
||||
<DivDebugBaselineGrid className="flex gap-8">
|
||||
<div className="[&>*]:flex">
|
||||
<div className="*:flex">
|
||||
<div>
|
||||
<LabeledIcon
|
||||
icon={<FaCamera size={12} />}
|
||||
@ -229,8 +229,8 @@ export default function ComponentsPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx(
|
||||
debugComponents && '[&>*]:bg-gray-300 [&>*]:dark:bg-gray-700',
|
||||
'[&>*]:flex',
|
||||
debugComponents && '*:bg-gray-300 dark:*:bg-gray-700',
|
||||
'*:flex',
|
||||
)}>
|
||||
{DEBUG_LINES.map((_, i) =>
|
||||
<div key={i}>
|
||||
@ -239,8 +239,8 @@ export default function ComponentsPage() {
|
||||
)}
|
||||
</div>
|
||||
<div className={clsx(
|
||||
debugComponents && '[&>*]:bg-gray-300 [&>*]:dark:bg-gray-700',
|
||||
'[&>*]:flex',
|
||||
debugComponents && '*:bg-gray-300 dark:*:bg-gray-700',
|
||||
'*:flex',
|
||||
)}>
|
||||
{DEBUG_LINES.map((_, i) =>
|
||||
<PhotoCamera
|
||||
|
||||
@ -39,7 +39,7 @@ export default function FilmPage() {
|
||||
</div>
|
||||
<div className={clsx(
|
||||
'absolute top-0 left-[-2px] right-0 bottom-0',
|
||||
'bg-gradient-to-t',
|
||||
'bg-linear-to-t',
|
||||
'from-white to-[rgba(255,255,255,0.5)]',
|
||||
'dark:from-black dark:to-[rgba(0,0,0,0.5)]',
|
||||
)} />
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Analytics } from '@vercel/analytics/react';
|
||||
import { SpeedInsights } from '@vercel/speed-insights/react';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { IBM_Plex_Mono } from 'next/font/google';
|
||||
import {
|
||||
BASE_URL,
|
||||
DEFAULT_THEME,
|
||||
@ -25,12 +24,6 @@ import '../site/globals.css';
|
||||
import '../site/sonner.css';
|
||||
import '../site/viewerjs.css';
|
||||
|
||||
const ibmPlexMono = IBM_Plex_Mono({
|
||||
subsets: ['latin'],
|
||||
weight: ['400', '500', '700'],
|
||||
variable: '--font-ibm-plex-mono',
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: SITE_TITLE,
|
||||
description: SITE_DESCRIPTION,
|
||||
@ -79,7 +72,7 @@ export default function RootLayout({
|
||||
// Suppress hydration errors due to next-themes behavior
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<body className={ibmPlexMono.variable}>
|
||||
<body>
|
||||
<AppStateProvider>
|
||||
<SwrConfigClient>
|
||||
<ThemeProvider
|
||||
|
||||
@ -35,7 +35,7 @@ function AdminChildPage({
|
||||
)}>
|
||||
<div className={clsx(
|
||||
'flex items-center gap-x-1.5 sm:gap-x-3 gap-y-1',
|
||||
'flex-grow',
|
||||
'grow',
|
||||
breadcrumbEllipsis ? 'min-w-0' : 'flex-wrap',
|
||||
)}>
|
||||
{backPath &&
|
||||
|
||||
@ -53,7 +53,7 @@ export default function Badge({
|
||||
<span className={clsx(
|
||||
'max-w-full inline-flex',
|
||||
// Truncate 1 + 2 levels deep
|
||||
'truncate [&>*]:truncate',
|
||||
'truncate *:truncate',
|
||||
dimContent && 'opacity-50',
|
||||
)}>
|
||||
{children}
|
||||
|
||||
@ -33,7 +33,7 @@ export default function ChecklistRow({
|
||||
: optional ? 'optional' : 'missing'}
|
||||
loading={isPending}
|
||||
/>
|
||||
<div className="flex flex-col min-w-0 flex-grow">
|
||||
<div className="flex flex-col min-w-0 grow">
|
||||
<div className={clsx(
|
||||
'flex flex-wrap items-center gap-2 pb-0.5',
|
||||
'font-bold dark:text-gray-300',
|
||||
|
||||
@ -79,7 +79,7 @@ export default function ImageInput({
|
||||
ref={inputRef}
|
||||
id={INPUT_ID}
|
||||
type="file"
|
||||
className="!hidden"
|
||||
className="hidden!"
|
||||
accept={ACCEPTED_PHOTO_FILE_TYPES.join(',')}
|
||||
disabled={loading}
|
||||
multiple
|
||||
|
||||
@ -60,7 +60,7 @@ export default function OGTile({
|
||||
className={clsx(
|
||||
'group',
|
||||
'block w-full rounded-md overflow-hidden',
|
||||
'border shadow-sm',
|
||||
'border shadow-xs',
|
||||
'border-gray-200 dark:border-gray-800',
|
||||
riseOnHover && 'hover:-translate-y-1.5 transition-transform',
|
||||
)}
|
||||
@ -78,7 +78,7 @@ export default function OGTile({
|
||||
</div>}
|
||||
{loadingState === 'failed' &&
|
||||
<div className={clsx(
|
||||
'absolute top-0 left-0 right-0 bottom-0 z-[11]',
|
||||
'absolute top-0 left-0 right-0 bottom-0 z-11',
|
||||
'flex items-center justify-center',
|
||||
'text-red-400',
|
||||
)}>
|
||||
@ -121,8 +121,8 @@ export default function OGTile({
|
||||
'h-full flex flex-col gap-0.5 p-3',
|
||||
'font-sans leading-tight',
|
||||
'bg-gray-50 dark:bg-gray-900/50',
|
||||
'group-active:bg-gray-50 group-active:dark:bg-gray-900/50',
|
||||
'group-hover:bg-gray-100 group-hover:dark:bg-gray-900/70',
|
||||
'group-active:bg-gray-50 dark:group-active:bg-gray-900/50',
|
||||
'group-hover:bg-gray-100 dark:group-hover:bg-gray-900/70',
|
||||
'border-t border-gray-200 dark:border-gray-800',
|
||||
)}>
|
||||
<div className="text-gray-800 dark:text-white font-medium">
|
||||
|
||||
@ -17,7 +17,7 @@ export default function SelectTileOverlay({
|
||||
return (
|
||||
<div className={clsx(
|
||||
'absolute w-full h-full cursor-pointer',
|
||||
'active:bg-gray-950/40 active:dark:bg-gray-950/60',
|
||||
'active:bg-gray-950/40 dark:active:bg-gray-950/60',
|
||||
isPerformingSelectEdit && 'pointer-events-none',
|
||||
)}>
|
||||
{/* Admin Select Border */}
|
||||
|
||||
@ -16,7 +16,7 @@ export default function Switcher({
|
||||
type === 'regular'
|
||||
? 'border-gray-300 dark:border-gray-800'
|
||||
: 'border-transparent',
|
||||
type === 'regular' && 'shadow-sm',
|
||||
type === 'regular' && 'shadow-xs',
|
||||
)}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@ -33,7 +33,7 @@ export default function SwitcherItem({
|
||||
? 'text-black dark:text-white'
|
||||
: 'text-gray-400 dark:text-gray-600',
|
||||
active
|
||||
? 'hover:text-black hover:dark:text-white'
|
||||
? 'hover:text-black dark:hover:text-white'
|
||||
: 'hover:text-gray-700 dark:hover:text-gray-400',
|
||||
);
|
||||
|
||||
|
||||
@ -225,7 +225,7 @@ export default function TagInput({
|
||||
aria-controls={ARIA_ID_TAG_CONTROL}
|
||||
className={clsx(
|
||||
className,
|
||||
'w-full control !px-2 !py-2',
|
||||
'w-full control px-2! py-2!',
|
||||
'outline-1 outline-blue-600',
|
||||
'group-focus-within:outline group-active:outline',
|
||||
'inline-flex flex-wrap items-center gap-2',
|
||||
@ -247,7 +247,7 @@ export default function TagInput({
|
||||
'px-1.5 py-0.5',
|
||||
'bg-gray-200/60 dark:bg-gray-800',
|
||||
'active:bg-gray-200 dark:active:bg-gray-900',
|
||||
'rounded-sm',
|
||||
'rounded-xs',
|
||||
)}
|
||||
onClick={() => removeOption(option)}
|
||||
>
|
||||
@ -258,8 +258,8 @@ export default function TagInput({
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
className={clsx(
|
||||
'grow !min-w-0 !p-0 -my-2 text-xl',
|
||||
'!border-none !ring-transparent',
|
||||
'grow min-w-0! p-0! -my-2 text-xl',
|
||||
'border-none! ring-transparent!',
|
||||
'placeholder:text-dim placeholder:text-[14px]',
|
||||
'placeholder:translate-x-[2px]',
|
||||
'placeholder:translate-y-[-1.5px]',
|
||||
@ -287,7 +287,7 @@ export default function TagInput({
|
||||
role="listbox"
|
||||
ref={optionsRef}
|
||||
className={clsx(
|
||||
'control absolute top-0 mt-3 w-full z-10 !px-1.5 !py-1.5',
|
||||
'control absolute top-0 mt-3 w-full z-10 px-1.5! py-1.5!',
|
||||
'max-h-[8rem] overflow-y-auto',
|
||||
'flex flex-col gap-y-1',
|
||||
'text-xl shadow-lg dark:shadow-xl',
|
||||
@ -310,13 +310,13 @@ export default function TagInput({
|
||||
'text-base',
|
||||
'group flex items-center gap-1',
|
||||
'cursor-pointer select-none',
|
||||
'px-1.5 py-1 rounded-sm',
|
||||
'px-1.5 py-1 rounded-xs',
|
||||
'hover:bg-gray-100 dark:hover:bg-gray-800',
|
||||
'active:bg-gray-50 dark:active:bg-gray-900',
|
||||
'focus:bg-gray-100 dark:focus:bg-gray-800',
|
||||
index === 0 && selectedOptionIndex === undefined &&
|
||||
'bg-gray-100 dark:bg-gray-800',
|
||||
'outline-none',
|
||||
'outline-hidden',
|
||||
)}
|
||||
onClick={() => {
|
||||
addOptions([value]);
|
||||
|
||||
@ -377,13 +377,13 @@ export default function CommandKClient({
|
||||
<Command.Input
|
||||
onChangeCapture={(e) => setQueryLive(e.currentTarget.value)}
|
||||
className={clsx(
|
||||
'w-full !min-w-0',
|
||||
'w-full min-w-0!',
|
||||
'focus:ring-0',
|
||||
isPlaceholderVisible || isLoading && '!pr-8',
|
||||
'!border-gray-200 dark:!border-gray-800',
|
||||
'focus:border-gray-200 focus:dark:border-gray-800',
|
||||
isPlaceholderVisible || isLoading && 'pr-8!',
|
||||
'border-gray-200! dark:border-gray-800!',
|
||||
'focus:border-gray-200 dark:focus:border-gray-800',
|
||||
'placeholder:text-gray-400/80',
|
||||
'placeholder:dark:text-gray-700',
|
||||
'dark:placeholder:text-gray-700',
|
||||
isPending && 'opacity-20',
|
||||
)}
|
||||
placeholder="Search photos, views, settings ..."
|
||||
|
||||
@ -32,13 +32,13 @@ export default function CommandKItem({
|
||||
'px-2',
|
||||
accessory ? 'py-2' : 'py-1',
|
||||
'rounded-md cursor-pointer tracking-wide',
|
||||
'active:!bg-gray-200/75 active:dark:!bg-gray-800/55',
|
||||
'active:bg-gray-200/75! dark:active:bg-gray-800/55!',
|
||||
...loading
|
||||
? [
|
||||
'data-[selected=true]:dark:bg-gray-900/50',
|
||||
'dark:data-[selected=true]:bg-gray-900/50',
|
||||
'data-[selected=true]:bg-gray-100/50',
|
||||
] : [
|
||||
'data-[selected=true]:dark:bg-gray-900/75',
|
||||
'dark:data-[selected=true]:bg-gray-900/75',
|
||||
'data-[selected=true]:bg-gray-100',
|
||||
],
|
||||
disabled && 'opacity-15',
|
||||
|
||||
@ -21,9 +21,9 @@ export default function MoreMenu({
|
||||
<button
|
||||
className={clsx(
|
||||
buttonClassName,
|
||||
'p-1 min-h-0 border-none shadow-none hover:outline-none',
|
||||
'p-1 min-h-0 border-none shadow-none hover:outline-hidden',
|
||||
'hover:bg-gray-100 active:bg-gray-100',
|
||||
'hover:dark:bg-gray-800/75 active:dark:bg-gray-900',
|
||||
'dark:hover:bg-gray-800/75 dark:active:bg-gray-900',
|
||||
'text-dim',
|
||||
)}
|
||||
aria-label={ariaLabel}
|
||||
|
||||
@ -36,9 +36,9 @@ export default function MoreMenuItem({
|
||||
className={clsx(
|
||||
'flex items-center h-8',
|
||||
'px-2 py-1.5 rounded-[3px]',
|
||||
'select-none hover:outline-none',
|
||||
'select-none hover:outline-hidden',
|
||||
'hover:bg-gray-50 active:bg-gray-100',
|
||||
'hover:dark:bg-gray-900/75 active:dark:bg-gray-900',
|
||||
'dark:hover:bg-gray-900/75 dark:active:bg-gray-900',
|
||||
'whitespace-nowrap',
|
||||
isLoading
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
|
||||
@ -96,7 +96,7 @@ export default function EntityLink({
|
||||
{renderLabel()}
|
||||
</Badge>
|
||||
: <span className={clsx(
|
||||
truncate && 'inline-flex max-w-full [&>*]:truncate',
|
||||
truncate && 'inline-flex max-w-full *:truncate',
|
||||
)}>
|
||||
{renderLabel()}
|
||||
</span>}
|
||||
|
||||
@ -44,10 +44,11 @@ export default function LoaderButton(props: {
|
||||
}
|
||||
}}
|
||||
className={clsx(
|
||||
'font-mono',
|
||||
...(styleAs !== 'button'
|
||||
? [
|
||||
'link h-4 active:text-medium',
|
||||
'disabled:!bg-transparent',
|
||||
'disabled:bg-transparent!',
|
||||
]
|
||||
: ['h-9']
|
||||
),
|
||||
@ -55,7 +56,7 @@ export default function LoaderButton(props: {
|
||||
styleAs === 'link-without-hover' && 'hover:text-main',
|
||||
'inline-flex items-center gap-2 self-start whitespace-nowrap',
|
||||
primary && 'primary',
|
||||
hideFocusOutline && 'focus:outline-none',
|
||||
hideFocusOutline && 'focus:outline-hidden',
|
||||
className,
|
||||
)}
|
||||
disabled={isLoading || disabled}
|
||||
|
||||
@ -16,7 +16,7 @@ export default function MenuSurface({
|
||||
className={clsx(
|
||||
'component-surface',
|
||||
'px-2 py-1.5 max-w-[14rem]',
|
||||
'shadow-sm',
|
||||
'shadow-xs',
|
||||
'text-[0.8rem] leading-tight',
|
||||
'text-balance text-center',
|
||||
className,
|
||||
|
||||
@ -36,8 +36,8 @@ export default function PhotoGridPage({
|
||||
className={clsx(
|
||||
'absolute left-0 right-0',
|
||||
side === 'top'
|
||||
? 'top-0 bg-gradient-to-b from-white dark:from-black'
|
||||
: 'bottom-0 bg-gradient-to-t from-white dark:from-black',
|
||||
? 'top-0 bg-linear-to-b from-white dark:from-black'
|
||||
: 'bottom-0 bg-linear-to-t from-white dark:from-black',
|
||||
'h-6 z-10 pointer-events-none',
|
||||
)}
|
||||
/>;
|
||||
|
||||
@ -131,7 +131,7 @@ export default function PhotoLarge({
|
||||
const renderPhotoLink = () =>
|
||||
<PhotoLink
|
||||
photo={photo}
|
||||
className="font-bold uppercase flex-grow"
|
||||
className="font-bold uppercase grow"
|
||||
prefetch={prefetch}
|
||||
/>;
|
||||
|
||||
@ -171,7 +171,7 @@ export default function PhotoLarge({
|
||||
</div>;
|
||||
|
||||
const largePhotoContainerClassName = clsx(arePhotosMatted &&
|
||||
'flex items-center justify-center aspect-[3/2] bg-gray-100',
|
||||
'flex items-center justify-center aspect-3/2 bg-gray-100',
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -54,7 +54,7 @@ export default function PhotoMedium({
|
||||
{isLoading &&
|
||||
<div className={clsx(
|
||||
'absolute inset-0 flex items-center justify-center',
|
||||
'text-white bg-black/25 backdrop-blur-sm',
|
||||
'text-white bg-black/25 backdrop-blur-xs',
|
||||
'animate-fade-in',
|
||||
'z-10',
|
||||
)}>
|
||||
|
||||
@ -388,7 +388,7 @@ export default function PhotoForm({
|
||||
<div className={clsx(
|
||||
'absolute -top-16 -left-2 right-0 bottom-0 -z-10',
|
||||
'pointer-events-none',
|
||||
'bg-gradient-to-t',
|
||||
'bg-linear-to-t',
|
||||
'from-white/90 from-60%',
|
||||
'dark:from-black/90 dark:from-50%',
|
||||
)} />
|
||||
|
||||
@ -64,7 +64,7 @@ export default function ShareModal({
|
||||
'text-2xl leading-snug',
|
||||
)}>
|
||||
<TbPhotoShare size={22} className="hidden xs:block" />
|
||||
<div className="flex-grow">
|
||||
<div className="grow">
|
||||
{title}
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
@ -37,7 +37,7 @@ export default function Footer() {
|
||||
'flex items-center gap-1',
|
||||
'text-dim min-h-10',
|
||||
)}>
|
||||
<div className="flex gap-x-3 xs:gap-x-4 flex-grow flex-wrap">
|
||||
<div className="flex gap-x-3 xs:gap-x-4 grow flex-wrap">
|
||||
{isPathAdmin(pathname)
|
||||
? <>
|
||||
{userEmail === undefined &&
|
||||
|
||||
@ -76,7 +76,7 @@ export default function Nav({
|
||||
showAdmin={isUserSignedIn}
|
||||
/>
|
||||
<div className={clsx(
|
||||
'flex-grow text-right min-w-0',
|
||||
'grow text-right min-w-0',
|
||||
'hidden xs:block',
|
||||
'translate-y-[-1px]',
|
||||
)}>
|
||||
|
||||
@ -1,6 +1,179 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&display=swap");
|
||||
|
||||
@import 'tailwindcss';
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
--font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||
|
||||
--breakpoint-xs: 390px;
|
||||
--breakpoint-3xl: 1640px;
|
||||
|
||||
--text-xs: 0.75rem;
|
||||
--text-xs--line-height: 1rem;
|
||||
--text-sm: 0.84375rem;
|
||||
--text-sm--line-height: 1.1875rem;
|
||||
--text-base: 0.875rem;
|
||||
--text-base--line-height: 1.25rem;
|
||||
--text-lg: 1rem;
|
||||
--text-lg--line-height: 1.25rem;
|
||||
--text-xl: 1.125rem;
|
||||
--text-xl--line-height: 1.25rem;
|
||||
--text-2xl: 1.25rem;
|
||||
--text-2xl--line-height: 1.25rem;
|
||||
--text-3xl: 1.5rem;
|
||||
--text-3xl--line-height: 1.5rem;
|
||||
|
||||
--animate-fade-in: fade-in 0.5s linear both running;
|
||||
@keyframes fade-in {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
--animate-fade-in-from-top: fade-in-from-top 0.25s ease-in-out;
|
||||
@keyframes fade-in-from-top {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
--animate-fade-in-from-bottom: fade-in-from-bottom 0.25s ease-in-out;
|
||||
@keyframes fade-in-from-bottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
--animate-rotate-pulse: rotate-pulse 0.75s linear infinite normal both running;
|
||||
@keyframes rotate-pulse {
|
||||
0% { transform: rotate(0deg) scale(1) }
|
||||
50% { transform: rotate(180deg) scale(0.8) }
|
||||
100% { transform: rotate(360deg) scale(1) }
|
||||
}
|
||||
|
||||
--animate-hover-drift: hover-drift 8s linear infinite;
|
||||
@keyframes hover-drift {
|
||||
0% { transform: translate(0, 0) }
|
||||
20% { transform: translate(1px, -2px) }
|
||||
40% { transform: translate(1px, 1.5px) }
|
||||
60% { transform: translate(-1px, 2px) }
|
||||
80% { transform: translate(-1.5px, -1.75px) }
|
||||
100% { transform: translate(0, 0) }
|
||||
}
|
||||
|
||||
--animate-hover-wobble: hover-wobble 6s linear infinite normal both running;
|
||||
@keyframes hover-wobble {
|
||||
0% { transform: rotate(0deg) }
|
||||
20% { transform: rotate(3.5deg) }
|
||||
40% { transform: rotate(-2deg) }
|
||||
60% { transform: rotate(2.5deg) }
|
||||
80% { transform: rotate(-2.5deg) }
|
||||
100% { transform: rotate(0deg) }
|
||||
}
|
||||
}
|
||||
|
||||
/* Text */
|
||||
@utility text-main {
|
||||
@apply
|
||||
text-gray-900 dark:text-gray-100
|
||||
}
|
||||
@utility text-invert {
|
||||
@apply
|
||||
text-white dark:text-black
|
||||
}
|
||||
@utility text-medium {
|
||||
@apply
|
||||
text-gray-500 dark:text-gray-400
|
||||
}
|
||||
@utility text-dim {
|
||||
@apply
|
||||
text-gray-400 dark:text-gray-500
|
||||
}
|
||||
@utility text-extra-dim {
|
||||
@apply
|
||||
text-gray-400/80 dark:text-gray-400/50
|
||||
}
|
||||
@utility text-extra-extra-dim {
|
||||
@apply
|
||||
text-gray-200 dark:text-gray-800
|
||||
}
|
||||
@utility text-icon {
|
||||
@apply
|
||||
text-gray-800 dark:text-gray-200
|
||||
}
|
||||
@utility text-error {
|
||||
@apply
|
||||
text-red-500 dark:text-red-400
|
||||
}
|
||||
/* Border */
|
||||
@utility border-main {
|
||||
@apply border-gray-200 dark:border-gray-700
|
||||
}
|
||||
@utility border-subtle {
|
||||
@apply
|
||||
border border-gray-200 dark:border-gray-800
|
||||
}
|
||||
/* Background */
|
||||
@utility bg-main {
|
||||
@apply
|
||||
bg-white dark:bg-black
|
||||
}
|
||||
@utility bg-dim {
|
||||
@apply
|
||||
bg-gray-100 dark:bg-gray-900/75
|
||||
}
|
||||
@utility bg-content {
|
||||
@apply
|
||||
bg-white border-gray-200
|
||||
dark:bg-black dark:border-gray-800
|
||||
}
|
||||
@utility bg-invert {
|
||||
@apply
|
||||
bg-gray-900 dark:bg-gray-100
|
||||
}
|
||||
/* Baseline Grid */
|
||||
@utility space-y-baseline {
|
||||
@apply
|
||||
space-y-[1.1875rem] md:space-y-[1.25rem]
|
||||
}
|
||||
@utility gap-y-baseline {
|
||||
@apply
|
||||
gap-y-[1.1875rem] md:gap-y-[1.25rem]
|
||||
}
|
||||
@utility gap-baseline {
|
||||
@apply
|
||||
gap-[1.1875rem] md:gap-[1.25rem]
|
||||
}
|
||||
@utility h-baseline {
|
||||
@apply
|
||||
h-[1.1875rem] md:h-[1.25rem]
|
||||
}
|
||||
@utility max-h-baseline {
|
||||
@apply
|
||||
max-h-[1.1875rem] md:max-h-[1.25rem]
|
||||
}
|
||||
@utility -mt-baseline {
|
||||
@apply
|
||||
-mt-[1.1875rem] md:-mt-[1.25rem]
|
||||
}
|
||||
@utility bg-baseline-grid {
|
||||
@apply
|
||||
bg-[repeating-linear-gradient(to_bottom,#eee,#eee_1px,transparent_1px,transparent_1.1875rem)]
|
||||
md:bg-[repeating-linear-gradient(to_bottom,#eee,#eee_1px,transparent_1px,transparent_1.25rem)]
|
||||
dark:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.1875rem)]
|
||||
dark:md:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.25rem)]
|
||||
}
|
||||
|
||||
@layer base {
|
||||
/* Core */
|
||||
@ -25,7 +198,7 @@
|
||||
border rounded-md
|
||||
bg-main
|
||||
border-gray-200 dark:border-gray-700
|
||||
font-mono text-base leading-tight
|
||||
font-mono text-base leading-tight
|
||||
}
|
||||
input[type=text], input[type=email], input[type=password], select, textarea {
|
||||
@apply
|
||||
@ -56,7 +229,7 @@
|
||||
file:border-solid file:border
|
||||
file:border-gray-200 dark:file:border-gray-700
|
||||
file:cursor-pointer
|
||||
file:shadow-sm
|
||||
file:shadow-xs
|
||||
file:active:bg-gray-100
|
||||
file:disabled:bg-gray-100
|
||||
file:hover:border-gray-300 file:dark:hover:border-gray-600
|
||||
@ -80,7 +253,7 @@
|
||||
inline-flex gap-2 items-center
|
||||
px-3
|
||||
text-base
|
||||
shadow-sm
|
||||
shadow-xs
|
||||
active:bg-gray-100 dark:active:bg-gray-900
|
||||
hover:border-gray-300 dark:hover:border-gray-600
|
||||
disabled:cursor-not-allowed
|
||||
@ -127,95 +300,4 @@
|
||||
bg-content border border-main
|
||||
rounded-lg
|
||||
}
|
||||
/* Utilities: Text */
|
||||
.text-main {
|
||||
@apply
|
||||
text-gray-900 dark:text-gray-100
|
||||
}
|
||||
.text-invert {
|
||||
@apply
|
||||
text-white dark:text-black
|
||||
}
|
||||
.text-medium {
|
||||
@apply
|
||||
text-gray-500 dark:text-gray-400
|
||||
}
|
||||
.text-dim {
|
||||
@apply
|
||||
text-gray-400 dark:text-gray-500
|
||||
}
|
||||
.text-extra-dim {
|
||||
@apply
|
||||
text-gray-400/80 dark:text-gray-400/50
|
||||
}
|
||||
.text-extra-extra-dim {
|
||||
@apply
|
||||
text-gray-200 dark:text-gray-800
|
||||
}
|
||||
.text-icon {
|
||||
@apply
|
||||
text-gray-800 dark:text-gray-200
|
||||
}
|
||||
.text-error {
|
||||
@apply
|
||||
text-red-500 dark:text-red-400
|
||||
}
|
||||
/* Utilities: Border */
|
||||
.border-main {
|
||||
@apply border-gray-200 dark:border-gray-700
|
||||
}
|
||||
.border-subtle {
|
||||
@apply
|
||||
border border-gray-200 dark:border-gray-800
|
||||
}
|
||||
/* Utilities: Background */
|
||||
.bg-main {
|
||||
@apply
|
||||
bg-white dark:bg-black
|
||||
}
|
||||
.bg-dim {
|
||||
@apply
|
||||
bg-gray-100 dark:bg-gray-900/75
|
||||
}
|
||||
.bg-content {
|
||||
@apply
|
||||
bg-white border-gray-200
|
||||
dark:bg-black dark:border-gray-800
|
||||
}
|
||||
.bg-invert {
|
||||
@apply
|
||||
bg-gray-900 dark:bg-gray-100
|
||||
}
|
||||
/* Utilities: Baseline Grid */
|
||||
.space-y-baseline {
|
||||
@apply
|
||||
space-y-[1.1875rem] md:space-y-[1.25rem]
|
||||
}
|
||||
.gap-y-baseline {
|
||||
@apply
|
||||
gap-y-[1.1875rem] md:gap-y-[1.25rem]
|
||||
}
|
||||
.gap-baseline {
|
||||
@apply
|
||||
gap-[1.1875rem] md:gap-[1.25rem]
|
||||
}
|
||||
.h-baseline {
|
||||
@apply
|
||||
h-[1.1875rem] md:h-[1.25rem]
|
||||
}
|
||||
.max-h-baseline {
|
||||
@apply
|
||||
max-h-[1.1875rem] md:max-h-[1.25rem]
|
||||
}
|
||||
.-mt-baseline {
|
||||
@apply
|
||||
-mt-[1.1875rem] md:-mt-[1.25rem]
|
||||
}
|
||||
.bg-baseline-grid {
|
||||
@apply
|
||||
bg-[repeating-linear-gradient(to_bottom,#eee,#eee_1px,transparent_1px,transparent_1.1875rem)]
|
||||
md:bg-[repeating-linear-gradient(to_bottom,#eee,#eee_1px,transparent_1px,transparent_1.25rem)]
|
||||
dark:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.1875rem)]
|
||||
dark:md:bg-[repeating-linear-gradient(to_bottom,#222,#222_1px,transparent_1px,transparent_1.25rem)]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./src/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
screens: {
|
||||
'xs': '390px',
|
||||
...defaultTheme.screens,
|
||||
'3xl': '1640px',
|
||||
},
|
||||
fontSize: {
|
||||
'xs': ['0.75rem', '1rem'], // 12px on 16px
|
||||
'sm': ['0.84375rem', '1.1875rem'], // 13.5px on 19px [Default: mobile]
|
||||
'base': ['0.875rem', '1.25rem'], // 14px on 20px [Default: desktop]
|
||||
'lg': ['1rem', '1.25rem'], // 16px on 20px
|
||||
'xl': ['1.125rem', '1.25rem'], // 18px on 20px
|
||||
'2xl': ['1.25rem', '1.25rem'], // 20px on 20px
|
||||
'3xl': ['1.5rem', '1.5rem'], // 24px on 24px
|
||||
},
|
||||
extend: {
|
||||
fontFamily: {
|
||||
'mono': ['var(--font-ibm-plex-mono)', ...defaultTheme.fontFamily.mono],
|
||||
},
|
||||
animation: {
|
||||
'rotate-pulse':
|
||||
'rotate-pulse 0.75s linear infinite normal both running',
|
||||
'fade-in':
|
||||
'fade-in 0.5s linear both running',
|
||||
'fade-in-from-top':
|
||||
'fade-in-from-top 0.25s ease-in-out',
|
||||
'fade-in-from-bottom':
|
||||
'fade-in-from-bottom 0.25s ease-in-out',
|
||||
'hover-drift':
|
||||
'hover-drift 8s linear infinite',
|
||||
'hover-wobble':
|
||||
'hover-wobble 6s linear infinite normal both running',
|
||||
},
|
||||
keyframes: {
|
||||
'fade-in': {
|
||||
'0%': { opacity: '0' },
|
||||
'100%': { opacity: '1' },
|
||||
},
|
||||
'fade-in-from-top': {
|
||||
'0%': {
|
||||
opacity: '0',
|
||||
transform: 'translateY(-10px)',
|
||||
},
|
||||
'100%': {
|
||||
opacity: '1',
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
},
|
||||
'fade-in-from-bottom': {
|
||||
'0%': {
|
||||
opacity: '0',
|
||||
transform: 'translateY(10px)',
|
||||
},
|
||||
'100%': {
|
||||
opacity: '1',
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
},
|
||||
'rotate-pulse': {
|
||||
'0%': { transform: 'rotate(0deg) scale(1)' },
|
||||
'50%': { transform: 'rotate(180deg) scale(0.8)' },
|
||||
'100%': { transform: 'rotate(360deg) scale(1)' },
|
||||
},
|
||||
'hover-drift': {
|
||||
'0%': { transform: 'translate(0, 0)' },
|
||||
'20%': { transform: 'translate(1px, -2px)' },
|
||||
'40%': { transform: 'translate(1px, 1.5px)' },
|
||||
'60%': { transform: 'translate(-1px, 2px)' },
|
||||
'80%': { transform: 'translate(-1.5px, -1.75px)' },
|
||||
'100%': { transform: 'translate(0, 0)' },
|
||||
},
|
||||
'hover-wobble': {
|
||||
'0%': { transform: 'rotate(0deg)' },
|
||||
'20%': { transform: 'rotate(3.5deg)' },
|
||||
'40%': { transform: 'rotate(-2deg)' },
|
||||
'60%': { transform: 'rotate(2.5deg)' },
|
||||
'80%': { transform: 'rotate(-2.5deg)' },
|
||||
'100%': { transform: 'rotate(0deg)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
future: {
|
||||
hoverOnlyWhenSupported: true,
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
require('@tailwindcss/container-queries'),
|
||||
],
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user