9f8d33c8b2b6bc61f6d02d781c4e02836f7099f9 max Fri Aug 21 02:05:42 2026 -0700 hgSession: new opt-in JavaScript "My Sessions" page; share gbModern.css with hgBlat. refs #38157 Applies the hgBlat facelift strategy (#37996) to hgSession: an opt-in, client-rendered "My Sessions" page gated by the sessionNewPage / sessionNewPageBanner hg.conf flags (mirroring blatNewForm / blatNewFormBanner), with a banner linking between the classic and new pages so neither is a one-way door. sessionNewPage also flips the site default. hgSession.c stays the data/action backend: it emits the session list and page config as an inline JSON global (hgSessionData) into an empty #sessionApp container, and the new hgSession.js builds the UI - a save-current-view card (name + optional description + "only I can load it"; empty name saves under a random share_ name), a "most recently saved session" one-click Update, a searchable/sortable/paged DataTable of sessions (assembly + position, created with last-used on hover, views, a lock icon on private sessions), inline Share (copy link / email / gallery), Edit (rename + description + private), Overwrite and Delete, and a bulk Select -> Delete-all-selected mode. The mutating actions POST to new JSON endpoints (hgS_doDeleteJson / doShareJson / doGalleryJson / doOverwriteJson / doDescribeJson) that run the same SQL as the classic full-page handlers and return JSON, so the table updates in place; loads, file up/downloads and custom-track backup stay as ordinary form submits/links. The Advanced panel keeps feature parity with the classic page (load another user's session, load from URL/file, save to file, back up custom tracks, reset), minus the login/ change-password links that now live in the top menu. Shared UCSC house-style components (design tokens, .gbPill, .gbCard, .gbStrip, .gbSection, .gbShareBox, .gbBanner, the .gbModal* dialog and a .gbTable) are factored into a new gbModern.css. hgBlat is migrated onto it: its generic .blat* classes are renamed to the shared .gb* names in hgBlat.css / hgBlat.js and the #blatResults / #blatFormBox containers get class="gbApp"; verified pixel-clean against the previous search form and results pages, including the rename modal. hgSession.css holds only session-specific layout. diff --git src/hg/htdocs/style/hgBlat.css src/hg/htdocs/style/hgBlat.css index b3033e1f327..7a5511a2662 100644 --- src/hg/htdocs/style/hgBlat.css +++ src/hg/htdocs/style/hgBlat.css @@ -1,210 +1,139 @@ -/* hgBlat.css - styling for the modern BLAT search form and results pages (hgBlat.js). - * UCSC house style (per the project's UCSC UI Style Guide): steel-blue section header, tan/white - * content, navy links with maroon hover, #dbe4ee table header, zebra rows, navy selection bar. - * Loaded on the new pages via webIncludeResourceFile("hgBlat.css"); the classic C-rendered pages - * emit their own small .blatBanner rule in hgBlat.c instead. */ +/* hgBlat.css - BLAT-specific styling for the modern search form and results pages (hgBlat.js). + * The shared UCSC house-style components (design tokens on .gbApp, plus .gbPill, .gbCard, .gbStrip, + * .gbStat, .gbSection, .gbShareBox, .gbBanner and the .gbModal* dialog) live in gbModern.css, which + * is loaded alongside this file via webIncludeResourceFile. This file holds only the pieces unique + * to BLAT: the results table, score/coverage bars, the detail panel, and the search-form layout. + * (The classic C-rendered pages emit their own small .blatBanner rule in hgBlat.c instead, since + * they do not load these stylesheets.) */ -/* Plain white page background, matching the track-settings model (gb.css). The framework body - * is cream (#FFF9D2) and the hgInside content table carries a BGCOLOR=#FFFEE8 attribute; override - * both to white so the results sit on one uniform white background. */ -body.cgi { background:#fff; } -table.hgInside { background:#fff; } -/* Main page header, styled like gb.css .gbTrackTitleBanner (hgGtexTrackSettings model): black - * title on the house gold. The framework's #sectTtl already holds "<assembly> BLAT Results". */ -.subheadingBar { background:#eaca92; padding:9px 16px; margin:0; border:0; box-sizing:border-box; } -.subheadingBar #sectTtl { color:#000; font-weight:700; font-size:18px; - display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px; } -#blatResults, #blatFormBox { --accent:#003a72; --accentHover:#8b1a1a; --navy:#0a2b6b; --ink:#1e2833; - --muted:#5b6572; --faint:#93a0ad; --line:#d0d0d0; --lineSoft:#ececec; --card:#ffffff; - --panel:#ffffff; --section:#4c759c; --headrow:#ededed; --headrowLine:#cccccc; - --sel:#cfe0f5; --hover:#f0f0f0; --stripe:#f7f7f7; - --title:#eaca92; --titleLine:#d9bd82; --titleSub:#5a4a24; - --btn:#ffffff; --btnLine:#999999; --btnText:#003a72; --btnHover:#eef2f7; - --btnDark:#0a2b6b; --btnDarkHover:#0a2350; - font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; color:var(--ink); font-size:14px; } -.blatCard { background:var(--card); border:1px solid var(--line); border-radius:0; overflow:hidden; - box-shadow:0 1px 2px rgba(20,40,70,.10); margin:12px 0 24px; } /* Page actions sit in the gold main header (#sectTtl); no separate toolbar. */ .blatHeadActions { display:flex; gap:8px; align-items:center; flex-wrap:wrap; } -.blatStrip { display:flex; align-items:center; gap:24px; flex-wrap:wrap; - padding:11px 18px; border-bottom:1px solid var(--line); background:var(--panel); } -.blatStat { display:flex; flex-direction:column; gap:1px; } -.blatStat .k { font-size:12px; color:var(--muted); font-weight:700; } -.blatStat .v { font-size:14px; font-weight:700; color:var(--ink); } -.blatDiv { width:1px; height:28px; background:var(--line); } -.blatStripActions { margin-left:auto; display:flex; gap:8px; align-items:center; flex-wrap:wrap; } -/* Buttons live both inside the card (#blatResults) and in the gold header (#sectTtl); scope to - * both ids so the framework's link styles (which would add an underline) can't beat them. */ -#blatResults .blatPill, #sectTtl .blatPill, #blatFormBox .blatPill, -#blatResults .blatStripActions input[type=submit], -#blatResults .blatStripActions input[type=button] { - font-family:inherit; font-size:13px; font-weight:700; color:var(--btnText); background:var(--btn); - border:1px solid var(--btnLine); border-radius:0; padding:5px 12px; cursor:pointer; - text-decoration:none; display:inline-block; line-height:1.5; } -#blatResults .blatPill:hover, #sectTtl .blatPill:hover, #blatFormBox .blatPill:hover, -#blatResults .blatStripActions input[type=submit]:hover, -#blatResults .blatStripActions input[type=button]:hover { background:var(--btnHover); text-decoration:none; } -#blatResults .blatPill.primary, #sectTtl .blatPill.primary, -#blatFormBox .blatPill.primary { background:var(--btnDark); color:#fff; border-color:var(--btnDark); } -#blatResults .blatPill.primary:hover, #sectTtl .blatPill.primary:hover, -#blatFormBox .blatPill.primary:hover { background:var(--btnDarkHover); } -/* In the gold header (#sectTtl) the framework's link styles would otherwise beat .blatPill, so - * re-assert the button look with id-level specificity. */ -#sectTtl .blatPill { color:#003a72; background:#fff; border:1px solid #999; text-decoration:none; } -#sectTtl .blatPill:hover { background:#eef2f7; text-decoration:none; } -#sectTtl .blatPill.primary { background:#0a2b6b; color:#fff; border-color:#0a2b6b; } -#sectTtl .blatPill.primary:hover { background:#0a2350; } .blatShareIcon { vertical-align:-2px; margin-right:6px; } -.blatShareBox { display:flex; align-items:center; gap:10px; flex-wrap:wrap; - padding:11px 18px; border-bottom:1px solid var(--line); background:var(--panel); } -.blatShareMsg { font-size:14px; color:var(--muted); } -.blatShareInput { flex:1; min-width:260px; font-size:13px; padding:5px 8px; - border:1px solid var(--btnLine); border-radius:0; background:#fff; } -.blatBanner { background:#fbf3e2; border:1px solid var(--titleLine); padding:10px 14px; - margin:12px 0 0; font-size:14px; color:var(--ink); } -.blatBanner a { color:var(--accent); } -.blatBanner a:hover { color:var(--accentHover); } .blatSeqBar { flex:1 1 100%; display:flex; gap:8px; align-items:center; flex-wrap:wrap; } .blatSeqText { flex:1 1 100%; min-height:150px; margin-top:10px; padding:8px; white-space:pre; font-family:'Roboto Mono','Courier New',monospace; font-size:13px; color:var(--ink); border:1px solid var(--btnLine); background:#fff; overflow:auto; } #blatTable { font-size:13px; width:100%; border-collapse:collapse; } /* Let the long text columns wrap so the table shrinks to fit smaller screens, while the fixed * bar columns keep a min-width so they never compress into an overlap; when even that won't fit, * #blatTable_wrapper scrolls horizontally. */ #blatTable td.blatPos, #blatTable td.queryCol { white-space:normal; word-break:break-word; } #blatTable td.scoreCol { min-width:118px; } #blatTable td.covCol { min-width:120px; } #blatTable thead th { background:var(--headrow); border-bottom:1px solid var(--headrowLine); font-size:12px; color:var(--navy); font-weight:700; padding:8px 12px; white-space:nowrap; } #blatTable tbody td { padding:8px 12px; border-bottom:1px solid var(--lineSoft); white-space:nowrap; vertical-align:middle; } #blatTable tbody tr { cursor:pointer; } #blatTable tbody tr:nth-child(even) { background:var(--stripe); } #blatTable tbody tr:hover { background:var(--hover); } #blatTable tbody tr.blatSel { background:var(--sel); box-shadow:inset 3px 0 0 var(--navy); } #blatTable td.num, #blatTable th.num { text-align:right; font-variant-numeric:tabular-nums; } #blatTable td.rankCol { color:var(--faint); } #blatTable td.strandCol { text-align:center; color:var(--muted); } #blatTable td.actionsCol a { color:var(--accent); font-weight:700; } #blatTable td.actionsCol .blatActSep { color:var(--line); margin:0 10px; } /* "New tab" shown as the usual box-with-arrow icon (via a CSS background so it isn't repeated in * every row's markup), sitting right after the position link with just a space between them. */ #blatTable a.blatNewTab { display:inline-block; width:11px; height:11px; margin-left:4px; vertical-align:-1px; background-repeat:no-repeat; background-position:center; background-size:contain; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23003a72' d='M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z'/%3E%3C/svg%3E"); } #blatTable a.blatNewTab:hover { opacity:.65; } #blatTable a { color:var(--accent); text-decoration:none; } #blatTable a:hover { color:var(--accentHover); text-decoration:underline; } .blatRowHint { padding:11px 18px 2px; font-size:13px; color:var(--muted); } .blatLocus { max-width:280px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .chrNote { color:var(--faint); text-decoration:none; margin-left:4px; } .chrNote:hover { color:var(--accent); } .blatScoreWrap { display:flex; align-items:center; gap:9px; justify-content:flex-end; } .blatScoreVal { font-size:13px; font-weight:700; text-align:right; font-variant-numeric:tabular-nums; } .blatScoreBar { flex:0 0 54px; height:8px; background:var(--headrow); border:1px solid var(--headrowLine); overflow:hidden; } .blatScoreBar > i { display:block; height:100%; background:var(--accent); } .blatIdPct { font-size:13px; font-weight:700; font-variant-numeric:tabular-nums; } .blatCov { position:relative; display:block; width:150px; max-width:100%; height:10px; background:var(--headrow); border:1px solid var(--headrowLine); } .blatCov > i { position:absolute; top:0; bottom:0; background:var(--accent); } #blatTable_filter { float:left; margin:0 0 10px; } #blatTable_filter input { width:300px; max-width:55vw; border:1px solid var(--btnLine); border-radius:0; padding:5px 9px; font-size:14px; } #blatTable_wrapper { padding:6px 0 4px; overflow-x:auto; } .blatDetail { border-bottom:1px solid var(--line); background:var(--panel); padding:14px 18px 16px; } .blatDetail .dhead { display:flex; align-items:baseline; gap:10px; margin-bottom:12px; flex-wrap:wrap; } .blatDetail .dhead .lab { font-size:13px; color:var(--muted); } .blatSelectHint { font-size:14px; color:var(--ink); } .blatSelectHint a { color:var(--accent); } .blatSelectHint a:hover { color:var(--accentHover); } .blatDetail .dhead .loc { font-size:14px; font-weight:700; color:var(--ink); } .blatDetailCard { display:flex; gap:26px; flex-wrap:wrap; padding:14px 16px; background:var(--card); border:1px solid var(--line); border-radius:0; } .blatTiles { display:grid; grid-template-columns:repeat(4,auto); gap:14px 26px; } .blatTile .k { font-size:13px; color:var(--muted); cursor:help; } .blatTile .v { font-size:14px; font-weight:700; color:var(--ink); } .blatDetailActions { display:flex; gap:8px; margin-top:14px; flex-wrap:wrap; } /* Detail panel layout (was inline styles in blatDetailSkeleton). */ .blatDetailCol { display:flex; flex-direction:column; gap:16px; min-width:250px; } .blatAlignBox { flex:1; min-width:320px; border-left:1px solid var(--line); padding-left:24px; display:flex; flex-direction:column; justify-content:center; } -.blatAlignBox .blatPill { align-self:flex-start; } +.blatAlignBox .gbPill { align-self:flex-start; } .blatAlignText { font-size:14px; color:var(--muted); line-height:1.55; margin:8px 0 14px; max-width:360px; } -/* Full-width message row inside the flex-wrap share box. */ -.blatShareFull { flex:1 1 100%; } /* Rename modal. On this page the old C-emitted inline rename form (#renameFormItem / #renameForm) - * is replaced by the JS button + modal below, so keep the C markup hidden even though hgBlat.c + * is replaced by the JS button + .gbModal dialog, so keep the C markup hidden even though hgBlat.c * flips its display to block after buildBigPsl (our !important beats that inline style). */ #renameFormItem, #renameForm { display:none !important; } -.blatModalBg { position:fixed; top:0; right:0; bottom:0; left:0; z-index:1000; - background:rgba(0,0,0,.4); display:flex; align-items:center; justify-content:center; } -.blatModal { background:#fff; border:1px solid var(--line); border-radius:0; - box-shadow:0 4px 18px rgba(0,0,0,.28); padding:18px 20px; min-width:340px; max-width:92vw; } -.blatModalTitle { font-size:16px; font-weight:700; color:var(--ink); margin-bottom:12px; } -.blatModalText { font-size:13px; color:var(--ink); line-height:1.5; max-width:430px; margin-bottom:14px; } -.blatModalText.blatModalNote { margin-top:14px; } -.blatModalText a { color:var(--accent); } -.blatModalText a:hover { color:var(--accentHover); } -.blatModalLabel { display:block; font-size:12px; font-weight:700; color:var(--muted); - margin:10px 0 3px; } -.blatModalInput { width:100%; box-sizing:border-box; border:1px solid var(--btnLine); - border-radius:0; padding:6px 8px; font-size:14px; font-family:inherit; background:#fff; } -.blatModalBtns { display:flex; gap:8px; justify-content:flex-end; margin-top:18px; } /* ---- search form (blatFormBuild) ------------------------------------- - * Shares the tokens, .blatPill buttons, .blatBanner and the gold .subheadingBar page header - * above with the results page, so the two pages read as one design. */ + * Shares the design tokens (.gbApp on #blatFormBox), .gbPill buttons, .gbSection bars, .gbBanner + * and the gold .subheadingBar header from gbModern.css with the results page, so the two pages + * read as one design. */ #blatFormBox { font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; color:var(--ink); font-size:14px; } #blatFormBox .blatFormGrid { display:grid; grid-template-columns:minmax(0,1fr) 300px; gap:28px; align-items:start; margin:12px 0 24px; } @media (max-width:900px) { #blatFormBox .blatFormGrid { grid-template-columns:minmax(0,1fr); } } -#blatFormBox .blatSection { background:var(--section); color:#fff; padding:6px 13px; font-size:15px; - font-weight:700; margin:0 0 14px; } /* flex-start, not flex-end: the genome field carries a "Current genome:" line under its input, * so bottom-aligning the row would push Query type down by the height of that line. */ #blatFormBox .blatRow { display:flex; align-items:flex-start; gap:16px; flex-wrap:wrap; margin-bottom:14px; } #blatFormBox .blatField { display:flex; flex-direction:column; gap:5px; } #blatFormBox .blatField > span { font-size:13px; font-weight:700; color:var(--muted); } #blatFormBox .blatField select { padding:6px 8px; font-size:14px; border:1px solid var(--btnLine); border-radius:0; background:#fff; color:var(--ink); font-family:inherit; } #blatFormBox .blatGenomeSlot { flex:1 1 650px; min-width:260px; max-width:650px; } #blatFormBox .blatChecks { display:flex; align-items:center; gap:22px; flex-wrap:wrap; margin-bottom:16px; font-size:14px; } #blatFormBox .blatCheck { display:flex; align-items:center; gap:6px; } #blatFormBox .blatCheck label { display:flex; align-items:center; gap:6px; cursor:pointer; } #blatFormBox .blatCheck input { margin:0; } #blatFormBox .blatInfo { display:inline-flex; align-items:center; cursor:help; } #blatFormBox .blatTabs { display:flex; border-bottom:1px solid var(--line); margin-bottom:12px; } #blatFormBox .blatTab { padding:8px 16px; font-size:14px; font-weight:700; cursor:pointer; background:none; border:none; border-bottom:3px solid transparent; color:var(--accent); font-family:inherit; } #blatFormBox .blatTab.on { border-bottom-color:var(--title); color:var(--ink); } #blatFormBox .blatPaneHint { display:flex; justify-content:space-between; align-items:baseline; gap:16px; flex-wrap:wrap; font-size:13px; color:var(--muted); margin-bottom:6px; } #blatFormBox textarea.blatSeq { width:100%; height:200px; resize:vertical; padding:9px 11px; box-sizing:border-box; font-family:'Roboto Mono','Courier New',monospace; font-size:13px; line-height:1.6; border:1px solid var(--btnLine); border-radius:0; color:var(--ink); background:#fff; } #blatFormBox .blatCount { display:flex; justify-content:space-between; align-items:center; gap:16px; padding:6px 1px 0; font-size:13px; color:var(--muted); } #blatFormBox .blatCount .over { color:#b1301f; font-weight:700; } #blatFormBox .blatDrop { border:2px dashed var(--headrowLine); background:#fafafa; padding:30px 20px; text-align:center; } #blatFormBox .blatDrop.hot { background:var(--hover); border-color:var(--accent); } #blatFormBox .blatDropTitle { font-size:15px; font-weight:700; color:var(--accent); margin-bottom:4px; } #blatFormBox .blatDropSub { font-size:13px; color:var(--muted); margin-bottom:14px; } #blatFormBox .blatFileName { margin-top:12px; font-size:13px; color:var(--ink); font-weight:700; } #blatFormBox .blatActions { display:flex; align-items:center; gap:10px; flex-wrap:wrap; border-top:1px solid var(--line); padding-top:16px; margin-top:16px; } -#blatFormBox .blatCard { border:1px solid var(--line); padding:14px 16px; background:var(--card); } -#blatFormBox .blatCard + .blatCard { margin-top:16px; } -#blatFormBox .blatCard h3 { margin:0 0 9px; font-size:14px; font-weight:700; color:var(--accent); } -#blatFormBox .blatCard div { font-size:13px; line-height:1.5; color:var(--ink); } -#blatFormBox .blatCard div + div { margin-top:9px; } -#blatFormBox .blatCard a { color:var(--accent); } -#blatFormBox .blatCard a:hover { color:var(--accentHover); } +/* Sidebar cards on the form add padding to the shared .gbCard (which supplies the border/shadow). */ +#blatFormBox .gbCard { border:1px solid var(--line); padding:14px 16px; background:var(--card); } +#blatFormBox .gbCard + .gbCard { margin-top:16px; } +#blatFormBox .gbCard h3 { margin:0 0 9px; font-size:14px; font-weight:700; color:var(--accent); } +#blatFormBox .gbCard div { font-size:13px; line-height:1.5; color:var(--ink); } +#blatFormBox .gbCard div + div { margin-top:9px; } +#blatFormBox .gbCard a { color:var(--accent); } +#blatFormBox .gbCard a:hover { color:var(--accentHover); } #blatFormBox .blatLimitRow { display:flex; justify-content:space-between; gap:12px; padding-bottom:7px; border-bottom:1px solid var(--lineSoft); font-size:13px; } /* The genome search bar is emitted by hgBlat.c (printGenomeSearchBar) and relocated into the * layout by JS, so it keeps its real autocomplete over every assembly. */ /* The genome picker is setupGenomeSelector (utils.js), which builds its own combobox; only * its width is a page concern. HGStyle.css does not style .gsWrap, so nothing to override. */ #blatFormBox .gsWrap { max-width:650px; }