42b33234bee213325c2b6eede35edbb4fb2e566c max Thu Sep 3 06:40:30 2026 -0700 hgBlat: spinner while a search runs, shorter example, left-justified result columns Four items from Lou's note-37 on #38086. The search is a plain form POST, so nothing on screen changed between Submit and the arrival of the results - on an assembly served by a dynamic BLAT server that is a ten second wait with no sign that anything is happening. A busy overlay now covers the form: a generic .gbBusyBg/.gbSpinner in gbModern.css (CSS-drawn, since the page is already navigating away when it appears), put up by blatFormBusyWire() 350 ms after submit so a sub-second hg38 search does not flash a scrim, and swapping its text after 8 s to say that an index is probably being loaded on demand. Skipped for Clear and for an empty query; a pageshow handler takes it down again after a Back navigation. The example sequence goes from 13.8 kb of the SOD1 locus to 2.5 kb of PTP4A3, hg38 chr8:141,424,501-141,427,000. That window is carried by both chr8_KI270819v1_alt and chr8_MU273363v1_fix, so the example now demonstrates the alt and fix rows and their FAQ icons instead of returning one plain hit, and it covers two PTP4A3 exons so the result track is named after the gene. Every column of the results table is left-justified; the numeric ones keep tabular figures, and the score value gets a fixed width so its bars stay in a straight line. The alt/fix explanation icon is red rather than faint grey, and is drawn as the browser's own info-icon SVG rather than the U+24D8 glyph, which some system fonts do not have. refs #38086 diff --git src/hg/htdocs/style/gbModern.css src/hg/htdocs/style/gbModern.css index f11b1dcb5d7..648c047be5f 100644 --- src/hg/htdocs/style/gbModern.css +++ src/hg/htdocs/style/gbModern.css @@ -1,134 +1,153 @@ /* gbModern.css - shared UCSC "modern tool" components in the house style. * * The generic, non-tool-specific pieces of the hgBlat facelift (#37996), promoted here so other * client-rendered CGI pages (hgSession, ...) can reuse them by class name instead of copying rules. * UCSC house style (per the project UI Style Guide): deep-navy nav, tan/wheat title band, steel-blue * section bars, plain Helvetica/Arial, navy links with maroon hover, square corners everywhere. * * Load with webIncludeResourceFile("gbModern.css"). Put the design tokens on a wrapper element by * giving it class "gbApp"; the components below read those tokens via var(). Keep any modal markup * as a DOM descendant of the .gbApp wrapper so the custom properties inherit into it. * * Tool-specific layout (column widths, bespoke widgets) stays in the tool's own stylesheet * (hgBlat.css, hgSession.css); this file is only the shared vocabulary. */ /* Plain white page, 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 tool sits on one uniform background. */ body.cgi { background:#fff; } table.hgInside { background:#fff; } /* Design tokens - the house palette in one place. Applied to a wrapper with class "gbApp". */ .gbApp { --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; } /* Main page header, styled like gb.css .gbTrackTitleBanner: black title on the house gold. The * framework's #sectTtl holds the section title emitted by webNewSection/cartWebStart. */ .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; } /* Card / block. */ .gbCard { 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; } /* Horizontal stat/summary strip. */ .gbStrip { display:flex; align-items:center; gap:24px; flex-wrap:wrap; padding:11px 18px; border-bottom:1px solid var(--line); background:var(--panel); } .gbStat { display:flex; flex-direction:column; gap:1px; } .gbStat .k { font-size:12px; color:var(--muted); font-weight:700; } .gbStat .v { font-size:14px; font-weight:700; color:var(--ink); } .gbDiv { width:1px; height:28px; background:var(--line); } .gbStripActions { margin-left:auto; display:flex; gap:8px; align-items:center; flex-wrap:wrap; } /* Buttons - square (radius 0), navy primary, white secondary. Scoped to .gbApp and .subheadingBar * so the framework's link styles (which would add an underline) can't beat them. */ .gbApp .gbPill, .subheadingBar .gbPill, .gbApp .gbStripActions input[type=submit], .gbApp .gbStripActions 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; } .gbApp .gbPill:hover, .subheadingBar .gbPill:hover, .gbApp .gbStripActions input[type=submit]:hover, .gbApp .gbStripActions input[type=button]:hover { background:var(--btnHover); text-decoration:none; } .gbApp .gbPill.primary, .subheadingBar .gbPill.primary { background:var(--btnDark); color:#fff; border-color:var(--btnDark); } .gbApp .gbPill.primary:hover, .subheadingBar .gbPill.primary:hover { background:var(--btnDarkHover); } /* In the gold header the framework's link styles would otherwise beat .gbPill; re-assert with * id-level specificity (the framework's title lives in #sectTtl). */ .subheadingBar #sectTtl .gbPill { color:#003a72; background:#fff; border:1px solid #999; text-decoration:none; } .subheadingBar #sectTtl .gbPill:hover { background:#eef2f7; text-decoration:none; } .subheadingBar #sectTtl .gbPill.primary { background:#0a2b6b; color:#fff; border-color:#0a2b6b; } .subheadingBar #sectTtl .gbPill.primary:hover { background:#0a2350; } /* A muted/danger pill variant for destructive actions (Delete). */ .gbApp .gbPill.danger { color:#8b1a1a; } .gbApp .gbPill.danger:hover { background:#fdf2f2; border-color:#b45a5a; } /* Steel-blue section bar - white bold title. */ .gbSection { background:var(--section); color:#fff; padding:6px 13px; font-size:15px; font-weight:700; margin:0 0 14px; } /* Experimental "try the new / classic page" banner. */ .gbBanner { background:#fbf3e2; border:1px solid var(--titleLine); padding:10px 14px; margin:12px 0 0; font-size:14px; color:var(--ink); } .gbBanner a { color:var(--accent); } .gbBanner a:hover { color:var(--accentHover); } /* Share / copy-link row. */ .gbShareBox { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:11px 18px; border-bottom:1px solid var(--line); background:var(--panel); } .gbShareMsg { font-size:14px; color:var(--muted); } .gbShareFull { flex:1 1 100%; } .gbShareInput { flex:1; min-width:260px; font-size:13px; padding:5px 8px; border:1px solid var(--btnLine); border-radius:0; background:#fff; } /* Modal dialog - fixed full-viewport backdrop centering a square white card. Keep the markup a DOM * descendant of the .gbApp wrapper so the tokens inherit in. */ .gbModalBg { 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; } .gbModal { 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; } .gbModalTitle { font-size:16px; font-weight:700; color:var(--ink); margin-bottom:12px; } .gbModalText { font-size:13px; color:var(--ink); line-height:1.5; max-width:430px; margin-bottom:14px; } .gbModalText.gbModalNote { margin-top:14px; } .gbModalText a { color:var(--accent); } .gbModalText a:hover { color:var(--accentHover); } .gbModalLabel { display:block; font-size:12px; font-weight:700; color:var(--muted); margin:10px 0 3px; } .gbModalInput { 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; } .gbModalBtns { display:flex; gap:8px; justify-content:flex-end; margin-top:18px; } +/* Busy overlay - a scrim over a spinner card, shown while a request that navigates the page away is + * in flight. A plain form POST gives no feedback of its own, and a BLAT run against a dynamic + * server can sit there for many seconds, so without this the page looks dead after Submit. Same + * placement rules as the modal: keep the markup inside the .gbApp wrapper. Add class "on" to show + * it; there is no way to dismiss it, since the only thing that ends the wait is the new page. */ +.gbBusyBg { position:fixed; top:0; right:0; bottom:0; left:0; z-index:1100; + background:rgba(255,255,255,.75); display:none; align-items:center; justify-content:center; } +.gbBusyBg.on { display:flex; } +.gbBusyCard { background:#fff; border:1px solid var(--line); box-shadow:0 4px 18px rgba(0,0,0,.28); + padding:22px 26px; display:flex; align-items:center; gap:16px; max-width:92vw; } +.gbBusyTitle { font-size:15px; font-weight:700; color:var(--ink); } +.gbBusyText { font-size:13px; color:var(--muted); margin-top:4px; max-width:380px; line-height:1.4; } +/* Drawn in CSS rather than loaded as images/loading.gif: the page is already navigating away when + * this appears, so a request for an image is exactly what we cannot count on finishing. */ +.gbSpinner { flex:none; width:28px; height:28px; border:3px solid var(--lineSoft); + border-top-color:var(--navy); border-radius:50%; animation:gbSpin .8s linear infinite; } +@keyframes gbSpin { to { transform:rotate(360deg); } } +@media (prefers-reduced-motion: reduce) { .gbSpinner { animation-duration:2.4s; } } + /* Generic dense results table - steel-tint header, zebra rows, navy selection bar. Apply class * "gbTable" to the ; the tool may add its own column classes. */ .gbTable { font-size:13px; width:100%; border-collapse:collapse; } .gbTable 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; text-align:left; } .gbTable tbody td { padding:8px 12px; border-bottom:1px solid var(--lineSoft); white-space:nowrap; vertical-align:middle; } .gbTable tbody tr:nth-child(even) { background:var(--stripe); } .gbTable tbody tr:hover { background:var(--hover); } .gbTable tbody tr.gbSel { background:var(--sel); box-shadow:inset 3px 0 0 var(--navy); } .gbTable td.num, .gbTable th.num { text-align:right; font-variant-numeric:tabular-nums; } .gbTable a { color:var(--accent); text-decoration:none; } .gbTable a:hover { color:var(--accentHover); text-decoration:underline; } /* Timing dialog (gbShowTimingDialog in utils.js): a wider modal holding a gbTable whose last column * is a proportional bar. Shown when a page is loaded with &measureTiming=1. */ .gbModal.gbTimingModal { max-width:560px; } .gbTimingTable td.gbTimeBarCell { width:45%; } /* The bar track fills the cell; the fill width is set inline as a percentage of the total time. */ .gbTimeBar { position:relative; height:14px; background:var(--stripe); } .gbTimeBar > span { position:absolute; top:0; left:0; height:100%; background:var(--section); } .gbTimingTable tr.gbTimeTotal td { font-weight:700; border-top:2px solid var(--headrowLine); } .gbTimingTable tr.gbTimeTotal .gbTimeBar > span { background:var(--navy); } /* A subhead row separating the "server" and "client" groups. */ .gbTimingTable tr.gbTimeGroup td { background:var(--headrow); color:var(--navy); font-weight:700; font-size:12px; }