fix(design): the table header takes the B9 fade utility, and every size is dvh, not vh (E28 S3d4)
The table header carried an arbitrary-value copy of fade-below; a contract refuses a hand gradient of a surface colour. 100vh is the viewport with a tablet browser's toolbar retracted, so h-screen / min-h-screen / static vh hid the last rows under it; a contract holds dvh (the Plex player stage changes with S3f, where it is played on the tablet).
This commit is contained in:
@@ -30,7 +30,7 @@ import { LoadingState, StateMessage } from "./ui/QueryState";
|
||||
* Suspense fallback after it and the first page's own loading state keep one clock (see LoadingState). */
|
||||
export function BootScreen() {
|
||||
return (
|
||||
<div className="min-h-screen grid place-items-center">
|
||||
<div className="min-h-dvh grid place-items-center">
|
||||
<LoadingState since={0} delayMs={400} />
|
||||
</div>
|
||||
);
|
||||
@@ -121,7 +121,7 @@ export default function BootGate({ children }: { children: ReactNode }) {
|
||||
: null;
|
||||
if (failed)
|
||||
return (
|
||||
<div className="min-h-screen grid place-items-center">
|
||||
<div className="min-h-dvh grid place-items-center">
|
||||
<StateMessage tone="error" onRetry={() => failed.refetch()}>
|
||||
{t("common.somethingWrong")}
|
||||
</StateMessage>
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class ErrorBoundary extends Component<Props, State> {
|
||||
if (this.state.crashed) {
|
||||
if (reloadingForNewBuild()) return null;
|
||||
return (
|
||||
<div className="min-h-screen grid place-items-center text-muted p-6 text-center">
|
||||
<div className="min-h-dvh grid place-items-center text-muted p-6 text-center">
|
||||
<div>
|
||||
<div className="text-lg font-semibold text-fg mb-1">
|
||||
{i18n.t("errors.boundary.title")}
|
||||
|
||||
@@ -756,7 +756,7 @@ function ReviewList({
|
||||
{t("recorder.composer.reviewDone")}
|
||||
</Button>
|
||||
</div>
|
||||
<ul className="max-h-[60vh] overflow-y-auto rounded-xl border border-border bg-bg/60 font-mono text-xs">
|
||||
<ul className="max-h-[60dvh] overflow-y-auto rounded-xl border border-border bg-bg/60 font-mono text-xs">
|
||||
{rows.map((row) => {
|
||||
const tag = LEVEL_TAG[row.level];
|
||||
const off = excluded.has(row.id);
|
||||
@@ -813,7 +813,7 @@ function IssuePreview({
|
||||
return (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<p className="text-xs text-muted">{t("recorder.composer.previewLocal")}</p>
|
||||
<pre className="max-h-[60vh] overflow-auto whitespace-pre-wrap rounded-xl border border-border bg-bg/60 p-3 font-mono text-xs">
|
||||
<pre className="max-h-[60dvh] overflow-auto whitespace-pre-wrap rounded-xl border border-border bg-bg/60 p-3 font-mono text-xs">
|
||||
{text}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function LibraryPlayer({ job, onClose }: { job: DownloadJob; onCl
|
||||
controls
|
||||
autoPlay
|
||||
playsInline
|
||||
className="w-full max-h-[70vh] rounded-lg bg-black object-contain"
|
||||
className="w-full max-h-[70dvh] rounded-lg bg-black object-contain"
|
||||
>
|
||||
{t("downloads.player.unsupported")}
|
||||
</video>
|
||||
|
||||
@@ -141,7 +141,7 @@ export default function PlexSearchBox() {
|
||||
id={listId}
|
||||
role="listbox"
|
||||
aria-label={t("plex.suggest.aria")}
|
||||
className={`${SURFACE.menu} absolute left-0 right-0 top-full z-menu mt-2 max-h-[70vh] overflow-y-auto animate-[popIn_0.16s_ease]`}
|
||||
className={`${SURFACE.menu} absolute left-0 right-0 top-full z-menu mt-2 max-h-[70dvh] overflow-y-auto animate-[popIn_0.16s_ease]`}
|
||||
>
|
||||
{suggestions.length === 0 && suggestQ.isFetching && (
|
||||
<div className="px-3 py-2 text-sm text-muted">{t("plex.loading")}</div>
|
||||
|
||||
@@ -305,7 +305,7 @@ export default function SetupWizard() {
|
||||
}
|
||||
|
||||
function Centered({ children }: { children: React.ReactNode }) {
|
||||
return <div className="min-h-screen bg-bg text-fg grid place-items-center p-4">{children}</div>;
|
||||
return <div className="min-h-dvh bg-bg text-fg grid place-items-center p-4">{children}</div>;
|
||||
}
|
||||
|
||||
function Section({
|
||||
|
||||
@@ -395,7 +395,7 @@ export default function VideoEditor({ job, onClose }: { job: DownloadJob; onClos
|
||||
onTimeUpdate={onTimeUpdate}
|
||||
onPlay={() => setPlaying(true)}
|
||||
onPause={() => setPlaying(false)}
|
||||
className="w-full max-h-[42vh] mx-auto block object-contain"
|
||||
className="w-full max-h-[42dvh] mx-auto block object-contain"
|
||||
playsInline
|
||||
/>
|
||||
{cropOn && picRect && (
|
||||
|
||||
@@ -70,7 +70,7 @@ export default function Welcome({ verify }: { verify: VerifyEntry }) {
|
||||
const [zoomed, setZoomed] = useState<{ src: string; label: string } | null>(null);
|
||||
const open = (src: string, label: string) => setZoomed({ src, label });
|
||||
return (
|
||||
<div className="min-h-screen bg-bg text-fg overflow-y-auto">
|
||||
<div className="min-h-dvh bg-bg text-fg overflow-y-auto">
|
||||
<header className="flex items-center justify-between px-5 sm:px-8 py-4">
|
||||
<div className="text-xl font-bold tracking-tight">
|
||||
Sift<span className="text-accent">lode</span>
|
||||
@@ -324,7 +324,7 @@ function Lightbox({ src, label, onClose }: { src: string; label: string; onClose
|
||||
<img
|
||||
src={src}
|
||||
alt={label}
|
||||
className="max-h-[82vh] max-w-[92vw] w-auto h-auto rounded-xl border border-border object-contain shadow-2xl"
|
||||
className="max-h-[82dvh] max-w-[92vw] w-auto h-auto rounded-xl border border-border object-contain shadow-2xl"
|
||||
/>
|
||||
<figcaption className="text-sm text-muted">{label}</figcaption>
|
||||
</figure>
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function LegalLayout({ title, children }: { title: string; childr
|
||||
document.title = `${title} · Siftlode`;
|
||||
}, [title]);
|
||||
return (
|
||||
<div className="min-h-screen bg-bg text-fg">
|
||||
<div className="min-h-dvh bg-bg text-fg">
|
||||
<div className="mx-auto w-[min(92vw,720px)] py-12">
|
||||
<div className="mb-8 flex items-center justify-between gap-4">
|
||||
<a href="/" className="inline-block text-2xl font-bold tracking-tight">
|
||||
|
||||
@@ -173,7 +173,7 @@ function CommandPaletteBody({ onClose, groups }: { onClose: () => void; groups:
|
||||
const from = Math.max(activeIndex, 0);
|
||||
setPicked(flat[(from + step + flat.length) % flat.length]!.id);
|
||||
} else if (e.key === "PageDown" || e.key === "PageUp") {
|
||||
// A page at a time, stopping at the ends (lib/listPaging). The list is 50vh and group headings
|
||||
// A page at a time, stopping at the ends (lib/listPaging). The list is 50dvh and group headings
|
||||
// eat into it, so the page is measured. Home/End stay the field's own (caret to the query's ends).
|
||||
e.preventDefault();
|
||||
const list = listRef.current;
|
||||
@@ -230,7 +230,7 @@ function CommandPaletteBody({ onClose, groups }: { onClose: () => void; groups:
|
||||
role="listbox"
|
||||
aria-label={t("palette.title")}
|
||||
hidden={flat.length === 0}
|
||||
className="mt-3 max-h-[50vh] overflow-y-auto"
|
||||
className="mt-3 max-h-[50dvh] overflow-y-auto"
|
||||
>
|
||||
{visible.map((g) => (
|
||||
<div
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function Modal({
|
||||
drawer
|
||||
? "justify-end"
|
||||
: top
|
||||
? "items-start justify-center p-4 pt-[8vh] sm:p-6 sm:pt-[10vh]"
|
||||
? "items-start justify-center p-4 pt-[8dvh] sm:p-6 sm:pt-[10dvh]"
|
||||
: "items-center justify-center p-4 sm:p-6"
|
||||
}`}
|
||||
onMouseDown={(e) => {
|
||||
|
||||
@@ -211,7 +211,7 @@ export function ColumnHead<T>({
|
||||
const [openFilter, setOpenFilter] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<thead className="sticky top-0 z-base after:pointer-events-none after:absolute after:inset-x-0 after:top-full after:h-4 after:bg-[linear-gradient(to_bottom,var(--bg),transparent)] after:content-['']">
|
||||
<thead className="sticky top-0 z-base fade-below">
|
||||
<tr className="text-muted [&>th]:shadow-[inset_0_-1px_0_var(--border)]">
|
||||
{columns.map((col) => {
|
||||
const isSorted = sort?.key === col.key;
|
||||
|
||||
Reference in New Issue
Block a user