diff --git a/src/app/admin/baseline/page.tsx b/src/app/admin/baseline/page.tsx new file mode 100644 index 00000000..60383037 --- /dev/null +++ b/src/app/admin/baseline/page.tsx @@ -0,0 +1,193 @@ +'use client'; + +import FieldSetWithStatus from '@/components/FieldSetWithStatus'; +import EntityLink from '@/components/primitives/EntityLink'; +import LabeledIcon from '@/components/primitives/LabeledIcon'; +import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon'; +import { clsx } from 'clsx/lite'; +import { useState } from 'react'; +import { FaCamera, FaHandSparkles, FaUserAltSlash } from 'react-icons/fa'; +import { IoMdCamera } from 'react-icons/io'; +import { IoImageSharp } from 'react-icons/io5'; + +const DEBUG_LINES = new Array(22).fill(null); + +export default function ComponentsPage() { + const [_debugGrid, setDebugGrid] = useState('true'); + const [_debugComponents, setDebugComponents] = useState('false'); + + const debugGrid = _debugGrid === 'true'; + const debugComponents = _debugComponents === 'true'; + + return ( + <> +

+
+ Baseline Grid: + + 13px / 18px + 14px / 20px + +
+
*]:inline-flex [&>*]:gap-1 [&_input]:-translate-y-0.5', + )}> + + +
+

+
+
+
+ } + debug={debugComponents} + > + Camera
Line two +
+
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } + label="Image" + debug={debugComponents} + /> +
+
+ } + label="Image" + badged + debug={debugComponents} + /> +
+
+ } + debug={debugComponents} + > + Canon Mark III + +
+
+ } + label="Astia/Soft" + type="icon-last" + iconWide + badged + debug={debugComponents} + /> +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+ } + label="Astia/Soft" + type="icon-last" + badged + debug={debugComponents} + /> +
+
+ } debug={debugComponents}> + Image + +
+
+ } debug={debugComponents}> + Image + +
+
+
*]:bg-gray-800', + '[&>*]:flex', + )}> + {DEBUG_LINES.map((_, i) => +
+ Line {(i + 1).toString().padStart(2, '0')} +
+ )} +
+
+ + ); +} diff --git a/src/camera/CameraHeader.tsx b/src/camera/CameraHeader.tsx index 657c0e5a..35f7b9ef 100644 --- a/src/camera/CameraHeader.tsx +++ b/src/camera/CameraHeader.tsx @@ -21,7 +21,7 @@ export default function CameraHeader({ const camera = cameraFromPhoto(photos[0], cameraProp); return ( } + entity={} entityVerb="Photo" entityDescription={ descriptionForCameraPhotos(photos, undefined, count, dateRange)} diff --git a/src/camera/PhotoCamera.tsx b/src/camera/PhotoCamera.tsx index 1f9eb84c..e6e8511a 100644 --- a/src/camera/PhotoCamera.tsx +++ b/src/camera/PhotoCamera.tsx @@ -2,7 +2,9 @@ import { AiFillApple } from 'react-icons/ai'; import { pathForCamera } from '@/site/paths'; import { IoMdCamera } from 'react-icons/io'; import { Camera, formatCameraText } from '.'; -import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; +import EntityLink, { + EntityLinkExternalProps, +} from '@/components/primitives/EntityLink'; export default function PhotoCamera({ camera, @@ -31,7 +33,7 @@ export default function PhotoCamera({ /> : } type={showAppleIcon && isCameraApple ? 'icon-first' : type} badged={badged} diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 8a942928..02124a22 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -27,8 +27,9 @@ export default function Badge({ ); case 'small': return clsx( - 'px-[0.3rem] py-1 rounded-[0.25rem]', - 'text-[0.7rem] font-medium', + 'h-max-baseline', + 'px-[5px] py-[2.75px]', + 'text-[0.7rem] font-medium rounded-[0.25rem]', highContrast ? 'text-invert bg-invert' : 'text-medium bg-gray-300/30 dark:bg-gray-700/50', diff --git a/src/components/EntityLink.tsx b/src/components/EntityLink.tsx deleted file mode 100644 index da271fd6..00000000 --- a/src/components/EntityLink.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import Link from 'next/link'; -import { ReactNode } from 'react'; -import Badge from './Badge'; -import { clsx } from 'clsx/lite'; - -export interface EntityLinkExternalProps { - type?: 'icon-last' | 'icon-first' | 'icon-only' | 'text-only' - badged?: boolean - contrast?: 'low' | 'medium' | 'high' -} - -export default function EntityLink({ - label, - labelSmall, - href, - icon, - title, - type = 'icon-first', - badged, - contrast = 'high', - hoverEntity, -}: { - label: ReactNode - labelSmall?: ReactNode - href: string - icon?: ReactNode - title?: string - hoverEntity?: ReactNode -} & EntityLinkExternalProps) { - const renderLabel = () => <> - - {labelSmall ?? label} - - - {label} - - ; - - const classForContrast = () => { - switch (contrast) { - case 'low': - return 'text-dim'; - case 'high': - return 'text-main'; - default: - return 'text-medium'; - } - }; - - return ( - - - {type !== 'icon-only' && <> - {badged - ? - - {renderLabel()} - - - : - {renderLabel()} - } - } - {icon && type !== 'text-only' && - - {icon} - } - - {hoverEntity !== undefined && - - {hoverEntity} - } - - ); -} diff --git a/src/components/OGTile.tsx b/src/components/OGTile.tsx index 8afa1603..46938167 100644 --- a/src/components/OGTile.tsx +++ b/src/components/OGTile.tsx @@ -110,9 +110,8 @@ export default function OGTile({ />}
diff --git a/src/components/primitives/EntityLink.tsx b/src/components/primitives/EntityLink.tsx new file mode 100644 index 00000000..9ccd22fc --- /dev/null +++ b/src/components/primitives/EntityLink.tsx @@ -0,0 +1,89 @@ +import { ReactNode } from 'react'; +import LabeledIcon, { LabeledIconType } from './LabeledIcon'; +import Badge from '../Badge'; +import { clsx } from 'clsx/lite'; + +export interface EntityLinkExternalProps { + type?: LabeledIconType + badged?: boolean + contrast?: 'low' | 'medium' | 'high' +} + +export default function EntityLink({ + icon, + label, + labelSmall, + iconWide, + type, + badged, + contrast = 'medium', + href, + prefetch, + title, + hoverEntity, + debug, +}: { + icon: ReactNode + label: ReactNode + labelSmall?: ReactNode + iconWide?: boolean + href?: string + prefetch?: boolean + title?: string + hoverEntity?: ReactNode + debug?: boolean +} & EntityLinkExternalProps) { + const classForContrast = () => { + switch (contrast) { + case 'low': + return 'text-dim'; + case 'high': + return 'text-main'; + default: + return 'text-medium'; + } + }; + + const renderLabel = () => <> + + {labelSmall ?? label} + + + {label} + + ; + + return ( + + + {badged + ? + {renderLabel()} + + : renderLabel()} + + {hoverEntity !== undefined && + + {hoverEntity} + } + + ); +} diff --git a/src/components/primitives/Icon.tsx b/src/components/primitives/Icon.tsx new file mode 100644 index 00000000..57adbe06 --- /dev/null +++ b/src/components/primitives/Icon.tsx @@ -0,0 +1,35 @@ +import { ReactNode } from 'react'; +import { clsx } from 'clsx/lite'; +import Spinner from '../Spinner'; + +export default function Icon({ + children, + className, + iconClassName, + wide, + loading, + debug, +}: { + children: ReactNode + className?: string + iconClassName?: string + wide?: boolean + loading?: boolean + debug?: boolean, +}) { + return ( + + {loading + ? + : + {children} + } + + ); +} diff --git a/src/components/primitives/LabeledIcon.tsx b/src/components/primitives/LabeledIcon.tsx new file mode 100644 index 00000000..ca249680 --- /dev/null +++ b/src/components/primitives/LabeledIcon.tsx @@ -0,0 +1,60 @@ +import { ComponentProps, ReactNode } from 'react'; +import Icon from './Icon'; +import { clsx } from 'clsx/lite'; +import Link from 'next/link'; + +export type LabeledIconType = + 'icon-first' | + 'icon-last' | + 'icon-only' | + 'text-only'; + +export default function LabeledIcon({ + icon, + type = 'icon-first', + className: classNameProp, + children, + iconWide, + href, + prefetch, + debug, +}: { + icon?: ReactNode, + type?: LabeledIconType, + className?: string, + children: ReactNode, + iconWide?:boolean, + debug?: boolean, +} & Partial>) { + const className = clsx( + 'inline-flex gap-x-1 md:gap-x-1.5', + classNameProp, + debug && 'border border-green-500 m-[-1px]', + ); + + const renderContent = () => <> + {icon && type !== 'text-only' && + + {icon} + } + {children && type !== 'icon-only' && + + {children} + } + ; + + return href + ? + {renderContent()} + + :
+ {renderContent()} +
; +} diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index c54b0088..72dadd64 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -65,10 +65,9 @@ export default function PhotoLarge({ contentSide={
{/* Meta */} @@ -88,7 +87,7 @@ export default function PhotoLarge({
-
+
{photo.caption &&
{photo.caption} @@ -106,7 +105,7 @@ export default function PhotoLarge({
{/* EXIF Data */} -
+
{showExifContent && <>
    @@ -134,8 +133,8 @@ export default function PhotoLarge({ />} }
    } + icon={} title={`Film Simulation: ${large}`} type={type} badged={badged} contrast={contrast} hoverEntity={countOnHover} + iconWide /> ); } diff --git a/src/site/FooterClient.tsx b/src/site/FooterClient.tsx index ab114e7c..966b008e 100644 --- a/src/site/FooterClient.tsx +++ b/src/site/FooterClient.tsx @@ -38,7 +38,7 @@ export default function FooterClient({ 'flex items-center', 'text-dim min-h-[4rem]', )}> -
    +
    {isPathAdmin(pathname) ? <> {userEmail === undefined && diff --git a/src/site/NavClient.tsx b/src/site/NavClient.tsx index 9ff3f75c..a34d4350 100644 --- a/src/site/NavClient.tsx +++ b/src/site/NavClient.tsx @@ -57,7 +57,6 @@ export default function NavClient({ className={clsx( 'flex items-center', 'w-full min-h-[4rem]', - 'leading-none', )}>
    ; return ( -
    +
    } diff --git a/src/site/globals.css b/src/site/globals.css index 9803be2a..9579ee8e 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -116,7 +116,7 @@ hover:text-gray-600 hover:dark:text-gray-400 } - /* Common Utilities: Text */ + /* Utilities: Text */ .text-main { @apply text-gray-900 dark:text-gray-100 @@ -145,7 +145,7 @@ @apply text-red-500 dark:text-red-400 } - /* Common Utilities: Background */ + /* Utilities: Background */ .bg-main { @apply bg-white dark:bg-black @@ -159,4 +159,28 @@ @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] + } + .max-h-baseline { + @apply + max-h-[1.1875rem] md:max-h-[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)] + } } diff --git a/src/tag/FavsTag.tsx b/src/tag/FavsTag.tsx index 5496a276..65fc5d8e 100644 --- a/src/tag/FavsTag.tsx +++ b/src/tag/FavsTag.tsx @@ -1,8 +1,10 @@ import { FaStar } from 'react-icons/fa'; -import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; import { TAG_FAVS } from '.'; import { pathForTag } from '@/site/paths'; import { clsx } from 'clsx/lite'; +import EntityLink, { + EntityLinkExternalProps, +} from '@/components/primitives/EntityLink'; export default function FavsTag({ type, @@ -30,7 +32,7 @@ export default function FavsTag({ size={12} className={clsx( 'text-amber-500', - 'translate-x-[-1px] translate-y-[3.5px]', + 'translate-x-[-1px] translate-y-[-0.5px]', )} />} type={type} diff --git a/src/tag/PhotoTag.tsx b/src/tag/PhotoTag.tsx index 56fab6a6..0c1e4546 100644 --- a/src/tag/PhotoTag.tsx +++ b/src/tag/PhotoTag.tsx @@ -1,7 +1,9 @@ import { pathForTag } from '@/site/paths'; import { FaTag } from 'react-icons/fa'; import { formatTag } from '.'; -import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; +import EntityLink, { + EntityLinkExternalProps, +} from '@/components/primitives/EntityLink'; export default function PhotoTag({ tag, @@ -19,7 +21,7 @@ export default function PhotoTag({ href={pathForTag(tag)} icon={} type={type} badged={badged} diff --git a/src/tag/PhotoTags.tsx b/src/tag/PhotoTags.tsx index ef48121e..6eec2af3 100644 --- a/src/tag/PhotoTags.tsx +++ b/src/tag/PhotoTags.tsx @@ -1,7 +1,7 @@ import PhotoTag from '@/tag/PhotoTag'; import { isTagFavs } from '.'; import FavsTag from './FavsTag'; -import { EntityLinkExternalProps } from '@/components/EntityLink'; +import { EntityLinkExternalProps } from '@/components/primitives/EntityLink'; export default function PhotoTags({ tags, @@ -10,13 +10,13 @@ export default function PhotoTags({ tags: string[] } & EntityLinkExternalProps) { return ( -
    +
    {tags.map(tag => -
    + <> {isTagFavs(tag) - ? - : } -
    )} + ? + : } + )}
    ); } diff --git a/src/tag/TagHeader.tsx b/src/tag/TagHeader.tsx index f320de7d..fe97f856 100644 --- a/src/tag/TagHeader.tsx +++ b/src/tag/TagHeader.tsx @@ -22,7 +22,7 @@ export default function TagHeader({ - : } + : } entityVerb="Tagged" entityDescription={descriptionForTaggedPhotos(photos, undefined, count)} photos={photos} diff --git a/tailwind.config.js b/tailwind.config.js index 9816930c..23b349c6 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -12,13 +12,13 @@ module.exports = { ...defaultTheme.screens, }, fontSize: { - 'xs': '0.75rem', - 'sm': ['0.825rem', '1.15rem'], - 'base': ['0.875rem', '1.275rem'], - 'lg': ['0.925rem', '1.05rem'], - 'xl': '1rem', - '2xl': '1.1rem', - '3xl': ['1.3rem', '1.7rem'], + '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: {