component 04 / 28WORKING
SC Theme Image Switcher
- type
- Code Component
- platform
- Framer
- last update
- license
- SecretoCho Design
demo · runs with JavaScript
Auto-swap images for light/dark themes using system and/or Framer theme detection with live updates.
A theme-aware image component for Framer that automatically swaps between light and dark images. It can detect the current theme via system preference, Framer’s theme attributes, or both (recommended), and updates live on changes. Supports configurable size, object-fit, and corner radius, with graceful empty-state hints when an image is missing.
- UI
- Media
- Utilities
- Theming
Main
Overview
- Built With
- React, DOM APIs (matchMedia, MutationObserver), Framer Code Component API
- Layout Support
- Fixed width/height (via props)
- Orientation
- N/A
- Ideal Use Case
- Logos, illustrations, screenshots that must invert/swap per theme
Tags
Code comment
/** * SC Theme Image Switcher * © SecretoCho Design 2025 (https://secretocho.design) * Swaps between light/dark images based on theme (system / Framer / both). * Listens to matchMedia and DOM attribute mutations for live updates. * @framerDisableUnlink */Dependencies
- Technologies
- None (pure React + Web APIs)
- Notes
- Relies on browser support for prefers-color-scheme and MutationObserver.
Complexity
- Logic Level
- Low–Medium (multi-source theme detection + observers)
- Runtime Cost
- Very Low (no animations; simple state updates)
- Component Size
- ~2–3 KB TSX
- Notes
- Cleans up media/observer listeners on unmount.
Guide
Instructions
- Drop the component onto the canvas.
- Provide Light Mode Image and Dark Mode Image.
- Choose Theme Detection: system, framer, or both (recommended).
- Adjust Width, Height, Image Fit, and Corner Radius.
- Preview theme switching by toggling your OS/Framer theme.
Examples / Presets
Preset A – Logo Swap
- detect
- both
- fit
- contain
- radius
- 0
- width
- 240
- height
- 120
Preset B – Illustration Cover
- detect
- system
- fit
- cover
- radius
- 16
- width
- 600
- height
- 400
Preset C – Framer Only
- detect
- framer
- fit
- scale-down
- radius
- 12
- width
- 320
- height
- 240
Mechanics
How it Works
- System detection: window.matchMedia("(prefers-color-scheme: dark)").
- Framer detection: checks documentElement/body for data-theme, data-framer-theme, toggle-theme, and .dark class.
- Both: first tries Framer attributes; if not dark, falls back to system preference.
- Subscribes to media query changes and attribute mutations to update state live.
- Renders selected img with object-fit and rounded corners; fades via a short opacity transition.
Performance & Tuning
- Observers watch only specific attributes; negligible overhead.
- Prefer both for resilient detection across editors and published sites.
- Use appropriate image dimensions to avoid browser scaling artifacts.
Compatibility & Limits
- Modern browsers supporting prefers-color-scheme and MutationObserver.
- If Framer uses custom theme hooks not covered by attributes/classes, extend the checks in code.
- Empty state shows a neutral placeholder prompting which image is missing.
Quality
Accessibility
- Provide meaningful alt text if the image conveys information (replace empty string).
- Ensure adequate contrast in surrounding UI; component itself doesn’t alter image content.
Security & Privacy
- No network calls (beyond the browser fetching your image URLs).
- No dangerouslySetInnerHTML / eval.
- All listeners/observers are removed on unmount.
Reference
Props
| name | type | default | description |
|---|---|---|---|
lightImage | image-url | ”” | Image to show when theme is light. |
darkImage | image-url | ”” | Image to show when theme is dark. |
width | number | 200 | Rendered width of the image. |
height | number | 200 | Rendered height of the image. |
objectFit | enum | “cover” | How the image fits its box. |
radius | number | 0 | Corner radius applied to the <img>. |
detectMethod | enum | “both” | Theme source; both tries Framer first, then system. |
Notes & Tips
- If only one of the images is provided, the placeholder prompts you to add the other.
- For logos, prefer contain; for photos, prefer cover.
- If your design system sets .dark on <html> or <body>, framer detection will catch it.
Troubleshooting / FAQ
No switching occurs
Ensure detectMethod matches your environment (try both).
Dark mode not detected
Confirm that .dark class or data-theme="dark" is actually applied, or verify OS setting.
Blurred image
Provide higher-resolution assets or adjust width/height to native size.
Source
Header comment
/** * SC Theme Image Switcher * © SecretoCho Design 2025 (https://secretocho.design) * Swaps between light/dark images based on theme (system / Framer / both). * Listens to matchMedia and DOM attribute mutations for live updates. * @framerDisableUnlink */Key excerpt
Two detectors, Framer’s theme attributes on html and body and the system’s prefers-color-scheme; “both” asks Framer first.
// Method 2: Check Framer's theme attributes (multiple possible implementations) const checkFramerTheme = () => { // Check various possible theme attributes Framer might use const htmlElement = document.documentElement // Common Framer theme attributes const theme1 = htmlElement.getAttribute("data-theme") const theme2 = htmlElement.getAttribute("data-framer-theme") const theme3 = htmlElement.getAttribute("toggle-theme") const theme4 = htmlElement.classList.contains("dark") // Also check body element const bodyTheme = document.body.getAttribute("data-theme") const bodyDarkClass = document.body.classList.contains("dark") return ( theme1 === "dark" || theme2 === "dark" || theme3 === "dark" || theme4 || bodyTheme === "dark" || bodyDarkClass ) } // Determine which detection method to use const updateTheme = () => { let isDark = false if (detectMethod === "system") { isDark = checkSystemTheme() } else if (detectMethod === "framer") { isDark = checkFramerTheme() } else { // "both" - Framer first, fallback to system isDark = checkFramerTheme() || checkSystemTheme() } setIsDarkMode(isDark) }Credits
Credits
Implementation & Framer adaptation by SecretoCho Design.
- inspired by
- SecretoCho Design
- license
- SecretoCho Design
SecretoCho Design 2025
Disclaimer
Provided “as is” for visual/UX purposes. Validate asset licensing, responsive behavior, and accessibility in your project.
Proves
Hiring a design engineer?
I’m open to senior design engineer roles: remote from Belgrade, or relocation with visa sponsorship. Write to me, or read the CV first. Client work stays selective, one project at a time.
sc@secretocho.design