Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 29c3aec

Browse files
authored
sync dark mode with block (frame stuff) (#346)
1 parent 7cda818 commit 29c3aec

3 files changed

Lines changed: 78 additions & 2 deletions

File tree

app/components/docs/visual_code_example.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ def render_preview_tab(&block)
7878
end
7979

8080
def iframe_preview
81-
div(class: "relative aspect-[4/2.5] w-full overflow-hidden rounded-md border") do
81+
div(class: "relative aspect-[4/2.5] w-full overflow-hidden rounded-md border", data: {controller: "iframe-theme"}) do
8282
div(class: "absolute inset-0 hidden w-[1600px] bg-background md:block") do
83-
iframe(src: @src, class: "size-full")
83+
iframe(src: @src, class: "size-full", data: {iframe_theme_target: "iframe"})
8484
end
8585
end
8686
end
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { Controller } from "@hotwired/stimulus"
2+
3+
export default class extends Controller {
4+
static targets = ["iframe"]
5+
6+
connect() {
7+
this.setupThemeObserver()
8+
this.setupIframeListeners()
9+
this.syncAllIframes()
10+
}
11+
12+
disconnect() {
13+
this.observer?.disconnect()
14+
}
15+
16+
iframeTargetConnected(iframe) {
17+
this.setupIframeListener(iframe)
18+
this.syncThemeToIframe(iframe)
19+
}
20+
21+
setupThemeObserver() {
22+
this.observer = new MutationObserver(() => this.syncAllIframes())
23+
this.observer.observe(document.documentElement, {
24+
attributes: true,
25+
attributeFilter: ['class']
26+
})
27+
}
28+
29+
setupIframeListeners() {
30+
this.iframeTargets.forEach(iframe => this.setupIframeListener(iframe))
31+
}
32+
33+
setupIframeListener(iframe) {
34+
if (!iframe.src) return
35+
36+
iframe.addEventListener('load', () => this.syncThemeToIframe(iframe))
37+
38+
if (this.isIframeLoaded(iframe)) {
39+
this.syncThemeToIframe(iframe)
40+
}
41+
}
42+
43+
syncAllIframes() {
44+
this.iframeTargets.forEach(iframe => this.syncThemeToIframe(iframe))
45+
}
46+
47+
syncThemeToIframe(iframe) {
48+
if (!iframe.src) return
49+
50+
const iframeDoc = this.getIframeDocument(iframe)
51+
if (!iframeDoc?.documentElement) return
52+
53+
iframeDoc.documentElement.classList.toggle('dark', this.isDarkMode)
54+
}
55+
56+
getIframeDocument(iframe) {
57+
try {
58+
return iframe.contentDocument || iframe.contentWindow?.document
59+
} catch (e) {
60+
return null
61+
}
62+
}
63+
64+
isIframeLoaded(iframe) {
65+
const doc = this.getIframeDocument(iframe)
66+
return doc && doc.readyState === 'complete'
67+
}
68+
69+
get isDarkMode() {
70+
return document.documentElement.classList.contains('dark')
71+
}
72+
}
73+

app/javascript/controllers/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,8 @@ application.register("ruby-ui--theme-toggle", RubyUi__ThemeToggleController)
8282
import RubyUi__TooltipController from "./ruby_ui/tooltip_controller"
8383
application.register("ruby-ui--tooltip", RubyUi__TooltipController)
8484

85+
import IframeThemeController from "./iframe_theme_controller"
86+
application.register("iframe-theme", IframeThemeController)
87+
8588
import SidebarMenuController from "./sidebar_menu_controller"
8689
application.register("sidebar-menu", SidebarMenuController)

0 commit comments

Comments
 (0)