fix(ui): one continuous chrome gradient across nav + sticky sub-headers (#1478)
The TopNav and each sticky sub-header pinned beneath it (Gallery's filter bar, the Browse/Series/Settings/Subscriptions tabs bars) each painted their OWN dark-to-transparent gradient (Gallery) or a solid surface band (the rest), so the fade read as happening twice — dark, fade out, then dark again — instead of one gradient flowing from the nav down through the sub-nav. Operator asked to treat the sub-nav as part of the nav with a single gradient. New shared .fc-chrome-continues primitive (app.css): the nav fades from opaque to a shared --fc-chrome-seam alpha (on views flagged meta.stickyChrome), and the sub-header continues from that exact seam alpha to transparent over its own height. Both reference the same var so the alphas meet at the 64px boundary — no re-darkening, no doubling. Percentage stops keep it spanning the filter bar's expanding refine panel; the primitive's blur keeps tabs/controls legible where the old solid bars had none. --fc-chrome-seam is the single tuning knob. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<header class="fc-topnav">
|
||||
<header class="fc-topnav" :class="{ 'fc-topnav--chrome': hasStickyChrome }">
|
||||
<div class="fc-nav-left">
|
||||
<RouterLink :to="FRONT_DOOR" class="fc-brand" aria-label="FabledCurator home">
|
||||
<img src="/favicon.svg" alt="" class="fc-brand__glyph" width="22" height="22" />
|
||||
@@ -65,6 +65,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import router, { FRONT_DOOR } from '../router.js'
|
||||
import { useSystemStore } from '../stores/system.js'
|
||||
import PipelineStatusChip from './PipelineStatusChip.vue'
|
||||
@@ -72,6 +73,13 @@ import PipelineStatusChip from './PipelineStatusChip.vue'
|
||||
const system = useSystemStore()
|
||||
onMounted(() => system.refreshHealth())
|
||||
|
||||
// Views that pin a sticky sub-header (filter bar / tabs) directly under the nav
|
||||
// declare `meta.stickyChrome`. On those, the nav doesn't fade to transparent at
|
||||
// its bottom — it hands off at the shared seam alpha so the sub-header can
|
||||
// continue the SAME fade (see .fc-chrome-continues in app.css). One gradient.
|
||||
const route = useRoute()
|
||||
const hasStickyChrome = computed(() => !!route.meta?.stickyChrome)
|
||||
|
||||
// Every route with a meta.title is a nav entry. Order by meta.navOrder —
|
||||
// router.getRoutes() does NOT guarantee declaration order, so explicit numbers
|
||||
// pin the sequence (e.g. Explore after Gallery). Routes without one fall to the
|
||||
@@ -129,6 +137,20 @@ const health = computed(() => {
|
||||
backdrop-filter: blur(2px);
|
||||
-webkit-backdrop-filter: blur(2px);
|
||||
}
|
||||
/* On a view with a sticky sub-header pinned beneath (meta.stickyChrome), the nav
|
||||
stops fading at the shared seam alpha instead of going fully transparent — the
|
||||
sub-header (.fc-chrome-continues) picks the fade up from there, so the two read
|
||||
as one continuous gradient. Compound selector out-specifies .fc-topnav so it
|
||||
wins regardless of Vite's production CSS ordering. --fc-chrome-* come from the
|
||||
global :root in app.css (custom props inherit into scoped styles). */
|
||||
.fc-topnav.fc-topnav--chrome {
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(var(--fc-chrome-rgb), 0.92) 0%,
|
||||
rgba(var(--fc-chrome-rgb), 0.72) 55%,
|
||||
rgba(var(--fc-chrome-rgb), var(--fc-chrome-seam)) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.fc-brand {
|
||||
display: flex;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="fc-filterbar-wrap">
|
||||
<div class="fc-filterbar-wrap fc-chrome-continues">
|
||||
<div class="fc-filterbar">
|
||||
<v-autocomplete
|
||||
v-model="selected"
|
||||
@@ -313,20 +313,10 @@ function pushFilter(mutate) {
|
||||
and a gap shows through when scrolled to the top. */
|
||||
margin-top: -8px;
|
||||
margin-bottom: 12px;
|
||||
/* EXACT same gradiated obsidian (#14171A = 20,23,26) frost as the TopNav so
|
||||
the two read as one continuous piece of chrome — images scroll visibly
|
||||
under both. The nav's gradient fades to transparent at ITS bottom; this
|
||||
bar re-darkens at its top, so a faint seam (the page/image showing through
|
||||
the nav's transparent edge) separates them when scrolled to the very top,
|
||||
while under-scroll they frost as one. */
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(20, 23, 26, 0.92) 0%,
|
||||
rgba(20, 23, 26, 0.65) 60%,
|
||||
rgba(20, 23, 26, 0) 100%
|
||||
);
|
||||
backdrop-filter: blur(2px);
|
||||
-webkit-backdrop-filter: blur(2px);
|
||||
/* The frost itself (obsidian fade + blur) is the shared .fc-chrome-continues
|
||||
primitive: it CONTINUES the TopNav's fade from the seam alpha to transparent
|
||||
rather than re-darkening, so the nav + bar read as one gradient (operator
|
||||
2026-07-13). This block only owns the sticky positioning now. */
|
||||
}
|
||||
.fc-filterbar {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user