Skip to content

Commit dc41be4

Browse files
wassimooCopilotunatasha8
authored
feat: add announcements banner (#2549)
* feat: add AnnouncementBanner component with announcement content Co-authored-by: Copilot <copilot@github.com> * chore: update announcement content * docs: add turn banner on/off logic * docs: ran make format * Revert "docs: add turn banner on/off logic" This reverts commit b8dae8a. * fix: update announcement banner to disabled state --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: unatasha8 <una.cogavin@ory.sh>
1 parent 3ea6862 commit dc41be4

4 files changed

Lines changed: 125 additions & 0 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.announcementContent a {
2+
text-decoration: underline;
3+
text-underline-offset: 0.2rem;
4+
transition: color 0.2s ease;
5+
}
6+
7+
.announcementContent a:hover {
8+
color: #581c87;
9+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import React from "react"
2+
3+
import AnnouncementContent from "@site/src/content/announcement-banner.mdx"
4+
import * as announcementModule from "@site/src/content/announcement-banner.mdx"
5+
import styles from "./AnnouncementBanner.module.css"
6+
7+
type AnnouncementLevel = "info" | "warning" | "error" | "success"
8+
9+
function getLevelClasses(level: AnnouncementLevel) {
10+
switch (level) {
11+
case "warning":
12+
return "border-amber-200 bg-amber-50 text-amber-950"
13+
case "error":
14+
return "border-red-200 bg-red-50 text-red-950"
15+
case "success":
16+
return "border-emerald-200 bg-emerald-50 text-emerald-950"
17+
case "info":
18+
default:
19+
return "border-purple-200 bg-purple-50 text-purple-950"
20+
}
21+
}
22+
23+
function getDismissStorageKey(id: string) {
24+
return `ory_docs_announcement_dismissed:${id}`
25+
}
26+
27+
export default function AnnouncementBanner() {
28+
const announcement = (announcementModule as any).announcement as
29+
| {
30+
enabled?: boolean
31+
id?: string
32+
level?: string
33+
}
34+
| undefined
35+
36+
const enabled = Boolean(announcement?.enabled)
37+
const id = announcement?.id
38+
const level = (announcement?.level ?? "info") as AnnouncementLevel
39+
40+
const [ready, setReady] = React.useState(false)
41+
const [dismissed, setDismissed] = React.useState(false)
42+
43+
React.useEffect(() => {
44+
if (!enabled || !id) {
45+
setReady(true)
46+
return
47+
}
48+
try {
49+
const isDismissed = window.localStorage.getItem(getDismissStorageKey(id))
50+
setDismissed(Boolean(isDismissed))
51+
} catch {
52+
// ignore (private browsing / blocked storage)
53+
} finally {
54+
setReady(true)
55+
}
56+
}, [enabled, id])
57+
58+
if (!enabled || !id) return null
59+
if (!ready) return null
60+
if (dismissed) return null
61+
62+
return (
63+
<div
64+
role="region"
65+
aria-label="Announcement"
66+
className={[
67+
"border-b",
68+
getLevelClasses(level),
69+
// keep it readable across layouts
70+
"text-base",
71+
].join(" ")}
72+
>
73+
<div className="mx-auto flex max-w-screen-xl items-start gap-3 px-4 py-3">
74+
<div
75+
className={`min-w-0 flex-1 leading-5 pt-3 text-center ${styles.announcementContent}`}
76+
>
77+
<AnnouncementContent />
78+
</div>
79+
<button
80+
type="button"
81+
className={[
82+
"shrink-0 rounded-md p-1.5 mt-3",
83+
"border-none bg-transparent cursor-pointer",
84+
"text-current opacity-60 hover:opacity-100 hover:bg-black/5",
85+
"transition-all duration-200",
86+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-white",
87+
"focus-visible:ring-purple-600",
88+
].join(" ")}
89+
aria-label="Dismiss announcement"
90+
onClick={() => {
91+
setDismissed(true)
92+
try {
93+
window.localStorage.setItem(getDismissStorageKey(id), "1")
94+
} catch {
95+
// ignore
96+
}
97+
}}
98+
>
99+
<span aria-hidden="true" className="text-lg font-light leading-none">
100+
101+
</span>
102+
</button>
103+
</div>
104+
</div>
105+
)
106+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const announcement = {
2+
enabled: false, // Toggle the banner on/off.
3+
id: "example-2026-05-08", // Change this for every new announcement.
4+
level: "info", // Visual emphasis: "info" | "warning" | "error" | "success"
5+
}
6+
7+
We're happy to announce the release of our new product: name - description, read
8+
more about it [here](/docs/getting-started/overview).

src/theme/Root.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import React from "react"
55
import KapaWidget from "./KapaWidget"
6+
import AnnouncementBanner from "@site/src/components/AnnouncementBanner/AnnouncementBanner"
67
import { Buffer } from "buffer"
78

89
// Inject Buffer globally (works for browser + weird runtimes)
@@ -13,6 +14,7 @@ if (typeof globalThis !== "undefined" && !globalThis.Buffer) {
1314
function Root({ children }) {
1415
return (
1516
<>
17+
<AnnouncementBanner />
1618
{children}
1719
<KapaWidget />
1820
</>

0 commit comments

Comments
 (0)