fix(design-v2): side rail subtitle + footer signature + theme swatch harbor
CI / test (push) Has been cancelled

Mockup vs live diff revealed:
- Side rail brand area: mockup has italic '— home/library/settings' subtitle
  below WeVisto, live had none. Added via reactive activeSub computed.
- Side rail footer: mockup has 'a frame, gifted · v 0.4' italic at the
  bottom of the rail. Added top-nav__foot element; hidden in horizontal
  layouts, shown in v2 side rail via design-v2.scss
- Side rail mark size: bumped from 36px to 44px to match mockup
- Theme swatch harbor preview wasn't winning the cascade fight against
  Vue scoped styles (equal-specificity tie, Vue cascades later). Adding
  !important on the few preview properties — v2 is opt-in so this is
  appropriate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 15:38:42 -04:00
parent 7081d2bee7
commit 0bc6f389cc
10 changed files with 83 additions and 16 deletions
+46 -1
View File
@@ -2,7 +2,10 @@
<header class="top-nav" aria-label="Main navigation">
<RouterLink to="/" class="top-nav__brand">
<span class="top-nav__mark"><img :src="markUrl" alt=""></span>
<span class="top-nav__wordmark">WeVisto</span>
<span class="top-nav__title-group">
<span class="top-nav__wordmark">WeVisto</span>
<span class="top-nav__sub"> {{ activeSub }}</span>
</span>
</RouterLink>
<nav class="top-nav__tabs">
<RouterLink
@@ -20,6 +23,7 @@
>{{ imagesStore.pendingCount > 9 ? '9+' : imagesStore.pendingCount }}</span>
</RouterLink>
</nav>
<div class="top-nav__foot">a frame, gifted <span class="top-nav__foot-sep">·</span> v 0.4</div>
</header>
</template>
@@ -27,10 +31,22 @@
import { useRoute, type RouteLocationNormalizedLoaded } from 'vue-router'
import { useImagesStore } from '@/stores/images'
import { computed } from 'vue'
const route = useRoute()
const imagesStore = useImagesStore()
const markUrl = '/build/icons/apple-touch-icon.png'
// "— home" / "— library" / "— settings" subtitle on the brand area.
// Matches the approved mockup; hidden at mobile/tablet via CSS.
const activeSub = computed(() => {
const p = route.path
if (p.startsWith('/library')) return 'library'
if (p.startsWith('/settings')) return 'settings'
if (p.startsWith('/upload')) return 'add photograph'
return 'home'
})
interface Tab {
name: string
label: string
@@ -96,6 +112,12 @@ const tabs: Tab[] = [
}
&__mark img { width: 100%; height: 100%; display: block; }
&__title-group {
display: flex;
flex-direction: column;
line-height: 1.1;
}
&__wordmark {
font-size: var(--text-lg);
font-weight: 800;
@@ -103,6 +125,29 @@ const tabs: Tab[] = [
color: var(--color-text);
}
// Italic subtitle "— home / library / settings" beside the wordmark.
// Hidden at horizontal (mobile/tablet) layouts; v2 side rail shows it.
&__sub {
display: none;
font-size: 12px;
color: var(--color-text-muted);
margin-top: 2px;
font-style: italic;
}
// Bottom-of-rail signature — only renders inside the side-rail layout
// (positioned absolutely there). Hidden in the horizontal default state.
&__foot {
display: none;
font-size: 12px;
font-style: italic;
color: var(--color-text-muted);
text-align: center;
padding: 16px 0 4px;
border-top: 1px solid var(--color-border);
}
&__foot-sep { color: var(--brand-yellow, var(--color-primary)); }
&__tabs {
display: flex;
gap: var(--space-1);
+27 -5
View File
@@ -39,8 +39,26 @@
padding-bottom: 20px;
margin-bottom: 16px;
border-bottom: 1px solid var(--color-border);
align-items: flex-start;
}
&__mark {
width: 44px !important;
height: 44px !important;
border-radius: 10px !important;
}
&__wordmark { font-size: var(--text-xl); font-family: var(--font-display-v2); font-weight: 400; }
&__sub {
display: block;
font-family: var(--font-accent-v2);
}
&__foot {
display: block;
margin-top: auto;
font-family: var(--font-accent-v2);
border-top-color: var(--color-border);
}
&__wordmark { font-size: var(--text-xl); }
&__tabs {
flex-direction: column;
@@ -124,18 +142,22 @@
.design-toggle__sub { color: var(--color-text-muted); }
// ── Theme swatches preview their dusk via the harbor.jpg ───────────
// `!important` needed because Vue's scoped style on SettingsView
// (.theme-swatch__preview[data-v-xxx] { background: var(--swatch-bg) })
// ties on specificity and cascades later. v2 is opt-in so overriding is OK.
.theme-swatch {
border-color: var(--color-border);
background: var(--color-surface);
background: var(--color-surface) !important;
color: var(--color-text);
&--active { border-color: var(--brand-yellow); }
&__label { color: var(--color-text); }
&__preview {
background-image: url('/build/assets/harbor.jpg');
background-size: cover;
background-position: center;
background: transparent !important;
background-image: url('/build/assets/harbor.jpg') !important;
background-size: cover !important;
background-position: center !important;
position: relative;
overflow: hidden;
&::after {