mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
The standalone page header (/tools/:slug, /privacy, /r/:id) scrolled horizontally at 320px. The row is a flex line whose only shrinkable item is the breadcrumb title, so once that collapsed to zero the row still needed brand (132.3px) + separator + three gaps + px-4 + the back button, which is 148.2px in en and 178.5px in pt-BR. Making the back button icon-only below sm frees that budget: overflow goes to zero in every locale, and the breadcrumb gets 91.2px at 320px, so it renders text there for the first time instead of a dangling "/". sr-only rather than hidden keeps the button's accessible name in every locale at every width, and being out of flow it costs no gap-2. The sm and wider layout is unchanged. Closes #445.
60 lines
3.0 KiB
Vue
60 lines
3.0 KiB
Vue
<template>
|
|
<!-- Slim sticky header shared by the standalone pages (/tools/:slug,
|
|
/privacy, /r/:id). Brand → home, an optional breadcrumb title, and a
|
|
back-to-home button. This is NOT the homepage nav (Nav.vue) — that one
|
|
carries the full navigation, user menu, mobile drawer, and hides on
|
|
scroll; this one stays pinned. It carries the iOS safe-area inset itself
|
|
(body.jn-standalone-page in index.html reserves nothing), so when sticky
|
|
pins it at the viewport top the row sits below the Dynamic Island and
|
|
the blurred background paints the status-bar strip. -->
|
|
<header class="sticky top-0 z-40 border-b bg-background/80 supports-[backdrop-filter:blur(0px)]:bg-background/60 backdrop-blur pt-[env(safe-area-inset-top)]">
|
|
<div class="mx-auto flex w-full max-w-[1600px] items-center gap-2 px-4 h-14">
|
|
<RouterLink to="/"
|
|
class="inline-flex items-center gap-1.5 rounded-md px-1 py-1 text-lg font-semibold text-foreground no-underline hover:opacity-80 transition-opacity"
|
|
aria-label="IPCheck.ing">
|
|
<brandIcon />
|
|
<span class="tracking-tight">
|
|
<span class="font-bold">IP</span><span class="font-extralight">Check.</span><span
|
|
class="font-extralight">ing</span>
|
|
</span>
|
|
</RouterLink>
|
|
<!-- Breadcrumb: " / <title>" — only when the page passes a title. -->
|
|
<template v-if="title">
|
|
<span class="text-muted-foreground" aria-hidden="true">/</span>
|
|
<span class="min-w-0 truncate font-medium">{{ title }}</span>
|
|
</template>
|
|
<!-- Button `as-child` renders the RouterLink as its root, so this is a
|
|
single <a> styled as a button — not a <button> nested in an <a>.
|
|
Icon-only below `sm`: brand (132px) + this button's label (up to
|
|
178px in pt-BR) + gaps + px-4 exceeded 320px, and since the title is
|
|
the only item that can shrink, it collapsed to 0 and the row still
|
|
overflowed — a dangling "/" plus a clipped button. `sr-only` (not
|
|
`hidden`) keeps the accessible name in every locale, and being out of
|
|
flow it costs no `gap-2`. -->
|
|
<Button as-child variant="default" size="icon" class="ml-auto shrink-0 cursor-pointer sm:w-auto sm:px-4">
|
|
<RouterLink to="/">
|
|
<ArrowLeft class="size-4" />
|
|
<span class="sr-only sm:not-sr-only">{{ t('advancedtools.BackToHome') }}</span>
|
|
</RouterLink>
|
|
</Button>
|
|
</div>
|
|
</header>
|
|
</template>
|
|
|
|
<script setup>
|
|
// Shared slim header for standalone pages. The breadcrumb label is passed in as
|
|
// `title` (already localized by the caller); everything else is fixed chrome.
|
|
import { useI18n } from 'vue-i18n';
|
|
import brandIcon from '@/components/svgicons/Brand.vue';
|
|
import { ArrowLeft } from '@lucide/vue';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
defineProps({
|
|
// Breadcrumb text shown after the brand (e.g. "🌐 MTR Test" or "Privacy").
|
|
// Empty string renders just the brand + back button.
|
|
title: { type: String, default: '' },
|
|
});
|
|
|
|
const { t } = useI18n();
|
|
</script>
|