diff --git a/src/App.css b/src/App.css index c2f564e5..8b793142 100644 --- a/src/App.css +++ b/src/App.css @@ -5344,17 +5344,31 @@ body.has-player .chat-fab { .playlist-detail-header-top { display: flex; - align-items: flex-start; + flex-direction: column; gap: 1rem; } +/* The cover is a banner over the title, cropped to 3:1 and no taller than + 14rem on wide screens, and the edit form's picker takes the same shape. It + stays inside the header's padding, so themes that outline panels with + --panel-ring keep their ring around it. */ +.playlist-detail-img, +.playlist-cover-banner { + display: block; + width: 100%; + aspect-ratio: 3 / 1; + max-height: 14rem; +} + .playlist-detail-img { - flex-shrink: 0; - width: 72px; - height: 72px; object-fit: cover; border-radius: var(--radius-lg); - border: 1px solid var(--color-border); +} + +/* Or the picker's image would hold it at its own height: a box sized by + aspect-ratio grows to fit its content. */ +.playlist-cover-banner { + min-height: 0; } .playlist-detail-title-row { diff --git a/src/components/playlist/PlaylistEditForm.tsx b/src/components/playlist/PlaylistEditForm.tsx index 647e522d..554d8feb 100644 --- a/src/components/playlist/PlaylistEditForm.tsx +++ b/src/components/playlist/PlaylistEditForm.tsx @@ -144,7 +144,8 @@ export function PlaylistEditForm( ? playlistImageUrl(playlist, mediaToken) : null)} alt="Cover" - size={72} + className="playlist-cover-banner" + borderRadius="var(--radius-lg)" onChange={(file) => { field.onChange(file); setImagePreview(URL.createObjectURL(file)); diff --git a/src/components/shared/ImagePicker.tsx b/src/components/shared/ImagePicker.tsx index 33c5fe31..5c598de1 100644 --- a/src/components/shared/ImagePicker.tsx +++ b/src/components/shared/ImagePicker.tsx @@ -16,9 +16,10 @@ export interface ImagePickerProps { /** Shows a "+" placeholder when null. */ src: string | null; alt?: string; - /** Sets the side, in pixels. */ + /** Sets the side, in pixels. Without it, `className` sizes the picker. */ size?: number; - borderRadius?: number; + /** In pixels when a number, or any CSS length, such as a radius token. */ + borderRadius?: number | string; /** Called with the file picked. The same file can be picked again. */ onChange: (file: File) => void; /** Disables the picker and shows "…" while the caller uploads. */ @@ -35,7 +36,7 @@ export interface ImagePickerProps { export function ImagePicker({ src, alt = "", - size = 80, + size, borderRadius = 8, onChange, uploading = false, @@ -43,7 +44,9 @@ export function ImagePicker({ className, }: ImagePickerProps): React.JSX.Element { const inputRef = useRef(null); - const sizeStyle = { width: size, height: size, borderRadius }; + const sizeStyle = size === undefined + ? { borderRadius } + : { width: size, height: size, borderRadius }; const pick = () => { if (!uploading) inputRef.current?.click(); };