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"> <header class="top-nav" aria-label="Main navigation">
<RouterLink to="/" class="top-nav__brand"> <RouterLink to="/" class="top-nav__brand">
<span class="top-nav__mark"><img :src="markUrl" alt=""></span> <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> </RouterLink>
<nav class="top-nav__tabs"> <nav class="top-nav__tabs">
<RouterLink <RouterLink
@@ -20,6 +23,7 @@
>{{ imagesStore.pendingCount > 9 ? '9+' : imagesStore.pendingCount }}</span> >{{ imagesStore.pendingCount > 9 ? '9+' : imagesStore.pendingCount }}</span>
</RouterLink> </RouterLink>
</nav> </nav>
<div class="top-nav__foot">a frame, gifted <span class="top-nav__foot-sep">·</span> v 0.4</div>
</header> </header>
</template> </template>
@@ -27,10 +31,22 @@
import { useRoute, type RouteLocationNormalizedLoaded } from 'vue-router' import { useRoute, type RouteLocationNormalizedLoaded } from 'vue-router'
import { useImagesStore } from '@/stores/images' import { useImagesStore } from '@/stores/images'
import { computed } from 'vue'
const route = useRoute() const route = useRoute()
const imagesStore = useImagesStore() const imagesStore = useImagesStore()
const markUrl = '/build/icons/apple-touch-icon.png' 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 { interface Tab {
name: string name: string
label: string label: string
@@ -96,6 +112,12 @@ const tabs: Tab[] = [
} }
&__mark img { width: 100%; height: 100%; display: block; } &__mark img { width: 100%; height: 100%; display: block; }
&__title-group {
display: flex;
flex-direction: column;
line-height: 1.1;
}
&__wordmark { &__wordmark {
font-size: var(--text-lg); font-size: var(--text-lg);
font-weight: 800; font-weight: 800;
@@ -103,6 +125,29 @@ const tabs: Tab[] = [
color: var(--color-text); 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 { &__tabs {
display: flex; display: flex;
gap: var(--space-1); gap: var(--space-1);
+27 -5
View File
@@ -39,8 +39,26 @@
padding-bottom: 20px; padding-bottom: 20px;
margin-bottom: 16px; margin-bottom: 16px;
border-bottom: 1px solid var(--color-border); 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 { &__tabs {
flex-direction: column; flex-direction: column;
@@ -124,18 +142,22 @@
.design-toggle__sub { color: var(--color-text-muted); } .design-toggle__sub { color: var(--color-text-muted); }
// ── Theme swatches preview their dusk via the harbor.jpg ─────────── // ── 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 { .theme-swatch {
border-color: var(--color-border); border-color: var(--color-border);
background: var(--color-surface); background: var(--color-surface) !important;
color: var(--color-text); color: var(--color-text);
&--active { border-color: var(--brand-yellow); } &--active { border-color: var(--brand-yellow); }
&__label { color: var(--color-text); } &__label { color: var(--color-text); }
&__preview { &__preview {
background-image: url('/build/assets/harbor.jpg'); background: transparent !important;
background-size: cover; background-image: url('/build/assets/harbor.jpg') !important;
background-position: center; background-size: cover !important;
background-position: center !important;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
&::after { &::after {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -17,9 +17,9 @@
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" /> <meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="WeVisto" /> <meta name="apple-mobile-web-app-title" content="WeVisto" />
<script type="module" crossorigin src="/build/assets/index-mIDwNQXq.js"></script> <script type="module" crossorigin src="/build/assets/index-zMEmYJqN.js"></script>
<link rel="modulepreload" crossorigin href="/build/assets/_plugin-vue_export-helper-BNDVmFr7.js"> <link rel="modulepreload" crossorigin href="/build/assets/_plugin-vue_export-helper-BNDVmFr7.js">
<link rel="stylesheet" crossorigin href="/build/assets/index-m9rEzKBq.css"> <link rel="stylesheet" crossorigin href="/build/assets/index-BdKVu3-n.css">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>