-
-
Notifications
You must be signed in to change notification settings - Fork 70
docs: add cross-site announcement banner #857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
7792edc
8be55d1
1015829
9b7a45d
240c4d7
dea56f9
741a9db
6851fce
93d7361
622e8db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| .jdx-banner { | ||
| position: relative; | ||
| z-index: 60; | ||
|
cursor[bot] marked this conversation as resolved.
Outdated
|
||
| display: flex; | ||
| gap: 0.75rem; | ||
| align-items: center; | ||
| padding: 0.5rem 1rem; | ||
| background: var(--vp-c-brand-1, #3451b2); | ||
| color: #fff; | ||
| font-size: 0.9rem; | ||
| line-height: 1.4; | ||
| } | ||
| .jdx-banner a { | ||
| color: #fff; | ||
| text-decoration: underline; | ||
| font-weight: 500; | ||
| } | ||
| .jdx-banner button { | ||
| margin-left: auto; | ||
| background: transparent; | ||
| border: 0; | ||
| color: #fff; | ||
| font-size: 1.25rem; | ||
| cursor: pointer; | ||
| line-height: 1; | ||
| padding: 0 0.25rem; | ||
| opacity: 0.85; | ||
| } | ||
| .jdx-banner button:hover { | ||
| opacity: 1; | ||
| } | ||
| @media (max-width: 640px) { | ||
| .jdx-banner { | ||
| font-size: 0.85rem; | ||
| padding: 0.4rem 0.75rem; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import "./banner.css"; | ||
|
|
||
| interface BannerData { | ||
| id: string; | ||
| enabled: boolean; | ||
| message: string; | ||
| link?: string; | ||
| linkText?: string; | ||
| } | ||
|
|
||
| const ENDPOINT = "https://jdx.dev/banner.json"; | ||
| const STORAGE_KEY = "jdx-banner-dismissed"; | ||
|
|
||
| export function initBanner(): void { | ||
| if (typeof window === "undefined") return; | ||
| fetch(ENDPOINT, { cache: "no-cache" }) | ||
| .then((r) => (r.ok ? (r.json() as Promise<BannerData>) : null)) | ||
| .then((b) => { | ||
| if (!b || !b.enabled) return; | ||
| if (localStorage.getItem(STORAGE_KEY) === b.id) return; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Accessing try {
if (localStorage.getItem(STORAGE_KEY) === b.id) return
} catch (e) {
// ignore storage errors
} |
||
| render(b); | ||
| }) | ||
| .catch(() => {}); | ||
| } | ||
|
|
||
| function isHttpUrl(value: string): boolean { | ||
| try { | ||
| const u = new URL(value, window.location.href); | ||
| return u.protocol === "http:" || u.protocol === "https:"; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| function render(b: BannerData): void { | ||
| const el = document.createElement("div"); | ||
| el.className = "jdx-banner"; | ||
| el.setAttribute("role", "region"); | ||
| el.setAttribute("aria-label", "Site announcement"); | ||
|
|
||
| const msg = document.createElement("span"); | ||
| msg.textContent = b.message; | ||
| el.appendChild(msg); | ||
|
|
||
| if (b.link && isHttpUrl(b.link)) { | ||
| const a = document.createElement("a"); | ||
| a.href = b.link; | ||
| a.target = "_blank"; | ||
| a.rel = "noopener noreferrer"; | ||
| a.textContent = b.linkText || "Learn more"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the retrieval, setting an item in try {
localStorage.setItem(STORAGE_KEY, b.id)
} catch (e) {
// ignore storage errors
} |
||
| el.appendChild(a); | ||
| } | ||
|
|
||
| const btn = document.createElement("button"); | ||
| btn.type = "button"; | ||
| btn.setAttribute("aria-label", "Dismiss"); | ||
| btn.textContent = "\u00d7"; | ||
| btn.addEventListener("click", () => { | ||
| localStorage.setItem(STORAGE_KEY, b.id); | ||
| el.remove(); | ||
| document.documentElement.style.removeProperty("--vp-layout-top-height"); | ||
| }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unhandled localStorage.setItem exception blocks banner dismissalLow Severity The dismiss button's click handler calls Reviewed by Cursor Bugbot for commit 622e8db. Configure here. |
||
| el.appendChild(btn); | ||
|
|
||
| document.body.prepend(el); | ||
|
|
||
| requestAnimationFrame(() => { | ||
| document.documentElement.style.setProperty( | ||
| "--vp-layout-top-height", | ||
| `${el.offsetHeight}px`, | ||
| ); | ||
| }); | ||
|
greptile-apps[bot] marked this conversation as resolved.
Outdated
cursor[bot] marked this conversation as resolved.
Outdated
|
||
| } | ||


Uh oh!
There was an error while loading. Please reload this page.