Files
changedetection.io/changedetectionio/static/styles/diff.css
T
43b07d5b5a Keep history diff title and controls visible while scrolling
The watch label, version selectors, diff options and tabs move into a
sticky #diff-header, and the top menu above it (watch URL, EDIT, theme
and GitHub links) becomes sticky too, so the whole header stays on screen
while the diff scrolls. The minimap and anchor jumps offset by the
measured height of both bars instead of a fixed value. Heights are
measured on load and kept current with a ResizeObserver where it is
available, falling back to the window resize and hashchange events as the
restock graph and queue sparkline already do, plus one deferred measure at
load - the tab switch hides #settings and so resizes the header without a
window resize, and opening a link already at #screenshot does the same
with no hashchange to follow. The app header is stored before the diff
header is measured, since the latter's cap is derived from the former.

A sticky bar must be opaque to the content scrolling beneath it, but the
page's own backdrop is a fixed full-viewport gradient layer over the page
colour, so a flat --color-background panel reads as a white band cutting
across it. Instead each bar repaints that same backdrop via a shared
page-surface-gradient mixin: the three gradient stops pre-composited
against --color-background-page with color-mix() at the layer's 0.91
opacity, with background-attachment: fixed so they stay registered with
body::after. Children of #diff-header are centred with a flex column,
mirroring the align-items: center that section.content applies to the
non-sticky siblings, so the controls panel and tabs keep their intrinsic
width and standard colours.

The top menu is made sticky only under body.difference-page. Upstream
already wants this globally (see the @todo in parts/_top_menu.scss) but
held off because the bar has no background of its own; scoping it here
keeps that decision separate. Sticky makes .header a stacking context, so
its mobile drawer is capped at the bar's z-index of 30 - high enough to
cover #diff-header, low enough that the action rail's hover flyout and
toast notifications still paint over the bar as before. The activity
strip is fixed on body in the root stacking context, so it would have
painted through the capped drawer; on this page it drops just below the
bar rather than raising the bar past the rail.

Dropping the 40px section.content padding and tightening the title's own
padding closes the gap between the two bars, and #diff-header's
half-viewport cap now excludes the top menu so the sticky stack stays
within the same budget on short viewports. .app-main's 0.55rem gap goes
too on this page: it sits between the two sticky bars, so the diff header
would otherwise start 8.8px below the top menu and slide up to meet it
over the first 8.8px of scroll. That gap cannot survive sticking - it is
outside both bars, so the diff would scroll through it - and the bars are
flush at every scroll position instead.

Co-authored-by: Engineer <engineer@agents.matrixsi.com>
Co-authored-by: Jeff Hedlund <jhedlund@gmail.com>
Signed-off-by: Jeff Hedlund <jhedlund@gmail.com>
2026-09-09 15:59:39 -04:00

2 lines
5.3 KiB
CSS

#diff-form{background:rgba(0,0,0,.05);padding:1em;border-radius:10px;margin-bottom:1em;color:#fff;font-size:.9rem;text-align:center}#diff-form label.from-to-label{width:4rem;text-decoration:none;padding:.5rem}#diff-form label.from-to-label#change-from{color:#b30000;background:#fadad7}#diff-form label.from-to-label#change-to{background:#eaf2c2;color:#406619}#diff-form #diff-style>span{display:inline-block;padding:.3em}#diff-form #diff-style>span label{font-weight:normal}#diff-form *{vertical-align:middle}body.difference-page .header{position:sticky;top:0;z-index:30;background:var(--color-background-gradient-second);background:linear-gradient(130deg, color-mix(in srgb, var(--color-background-gradient-first) 91%, var(--color-background-page)), color-mix(in srgb, var(--color-background-gradient-second) 91%, var(--color-background-page)) 41.07%, color-mix(in srgb, var(--color-background-gradient-third) 91%, var(--color-background-page)) 84.05%);background-attachment:fixed}body.difference-page .app-main{gap:0}body.difference-page .tab-pane-inner{scroll-margin-top:calc(var(--app-header-height, 0px) + var(--diff-header-height, 0px) + 1rem)}body.difference-page #pure-menu-horizontal-spinner{z-index:29}#diff-header{position:sticky;top:var(--app-header-height, 0px);z-index:20;width:100%;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;max-height:calc(50vh - var(--app-header-height, 0px));max-height:calc(50dvh - var(--app-header-height, 0px));overflow:auto;background:var(--color-background-gradient-second);background:linear-gradient(130deg, color-mix(in srgb, var(--color-background-gradient-first) 91%, var(--color-background-page)), color-mix(in srgb, var(--color-background-gradient-second) 91%, var(--color-background-page)) 41.07%, color-mix(in srgb, var(--color-background-gradient-third) 91%, var(--color-background-page)) 84.05%);background-attachment:fixed}#diff-header>*{max-width:100%}#diff-header #diff-watch-title{margin:0;padding:.25rem 1rem;font-size:1.25rem;overflow-wrap:anywhere;text-align:center;color:#fff}#diff-header #diff-form{margin-bottom:.5rem}#diff-header .diff-fieldset{min-width:0;display:flex;justify-content:center;flex-wrap:wrap;gap:.5rem}#diff-header .diff-fieldset>span{max-width:100%;min-width:0;flex:0 1 auto;display:flex;align-items:center;flex-wrap:wrap}#diff-header .diff-fieldset select{min-width:0;max-width:100%;flex:1}#diff-header .diff-fieldset label{flex-shrink:0}#diff-ui{background:var(--color-background);padding:1rem;border-radius:5px}@media(min-width: 767px){#diff-ui{min-width:50%}}#diff-ui #text{font-size:11px}#diff-ui pre{white-space:break-spaces;overflow-wrap:anywhere}#diff-ui h1{display:inline;font-size:100%}#diff-ui #result{white-space:pre-wrap;word-break:break-word;overflow-wrap:break-word}#diff-ui .source{position:absolute;right:1%;top:.2em}@-moz-document url-prefix(){#diff-ui body{height:99%}}#diff-ui td#diff-col div{text-align:justify;white-space:pre-wrap}#diff-ui .ignored{background-color:#ccc;opacity:.7}#diff-ui .triggered{background-color:#1b98f8}#diff-ui .ignored.triggered{background-color:red}#diff-ui .tab-pane-inner#screenshot{text-align:center}#diff-ui .tab-pane-inner#screenshot img{max-width:99%}#diff-ui .pure-form button.reset-margin{margin:0px}#diff-ui .diff-fieldset{display:flex;align-items:center;gap:4px;flex-wrap:wrap}#diff-ui ul#highlightSnippetActions{list-style-type:none;display:flex;align-items:center;justify-content:center;gap:1.5rem;flex-wrap:wrap;padding:0;margin:0}#diff-ui ul#highlightSnippetActions li{display:flex;flex-direction:column;align-items:center;text-align:center;padding:.5rem;gap:.3rem}#diff-ui ul#highlightSnippetActions li button,#diff-ui ul#highlightSnippetActions li a{white-space:nowrap}#diff-ui ul#highlightSnippetActions span{font-size:.8rem;color:var(--color-text-input-description)}#diff-ui #cell-diff-jump-visualiser{display:flex;flex-direction:row;gap:1px;background:var(--color-background);border-radius:3px;overflow-x:hidden;position:sticky;top:calc(var(--app-header-height, 0px) + var(--diff-header-height, 0px));z-index:10;padding-top:1rem;padding-bottom:1rem;justify-content:center}#diff-ui #cell-diff-jump-visualiser>div{flex:1;min-width:1px;max-width:10px;height:10px;background:var(--color-background-button-cancel);opacity:.3;border-radius:1px;transition:opacity .2s;position:relative}#diff-ui #cell-diff-jump-visualiser>div.deletion{background:#b30000;opacity:1}#diff-ui #cell-diff-jump-visualiser>div.insertion{background:#406619;opacity:1}#diff-ui #cell-diff-jump-visualiser>div.note{background:#406619;opacity:1}#diff-ui #cell-diff-jump-visualiser>div.mixed{background:linear-gradient(to right, #b30000 50%, #406619 50%);opacity:1}#diff-ui #cell-diff-jump-visualiser>div.current-position::after{content:"";position:absolute;bottom:-6px;left:50%;transform:translateX(-50%);width:0;height:0;border-left:4px solid rgba(0,0,0,0);border-right:4px solid rgba(0,0,0,0);border-bottom:4px solid var(--color-text)}#diff-ui #cell-diff-jump-visualiser>div:hover{opacity:.8;cursor:pointer}#text-diff-heading-area .snapshot-age{padding:4px;margin:.5rem 0;background-color:var(--color-background-snapshot-age);border-radius:3px;font-weight:bold;margin-bottom:4px}#text-diff-heading-area .snapshot-age.error{background-color:var(--color-error-background-snapshot-age);color:var(--color-error-text-snapshot-age)}#text-diff-heading-area .snapshot-age>*{padding-right:1rem}