---
title: SC Theme Image Switcher
description: Swaps light and dark images live, from system or Framer theme.
url: https://secretocho.design/lab/sc-theme-image-switcher/
type: SoftwareSourceCode
status: working
published: 2025-09-01
updated: 2025-09-01
---

# SC Theme Image Switcher

> Swaps light and dark images live, from system or Framer theme.

component 04 / 28 · WORKING

- **type**: Code Component
- **platform**: Framer
- **last update**: 2025-09-01
- **license**: SecretoCho Design

![A still of the SC Theme Image Switcher demo, as its module draws it with reduced motion.](https://secretocho.design/_astro/still.B0ZU13aM_Z1Kqpt3.webp)

*fig 00 · live demo, ported from **SC\_Image\_swither.tsx***

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

- theme
- dark mode
- image
- matchMedia
- mutation observer
- framer

### Code comment

```tsx
/**
 * 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

1. Drop the component onto the canvas.
2. Provide **Light Mode Image** and **Dark Mode Image**.
3. Choose **Theme Detection**: system, framer, or both (recommended).
4. Adjust **Width**, **Height**, **Image Fit**, and **Corner Radius**.
5. 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

*the CMS Code-Comment · SC\_Image\_swither.tsx has no doc comment*

```tsx
/**
 * 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
 */
```

[props \[REF\]](#reference)

### Key excerpt

Two detectors, Framer’s theme attributes on html and body and the system’s prefers-color-scheme; “both” asks Framer first.

*SC\_Image\_swither.tsx · lines 33–72 · tsx*

```tsx
        // 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

- [Creative coding](https://secretocho.design/about/#capabilities)

## 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](mailto:sc@secretocho.design)

[read the CV](https://secretocho.design/cv/) · [LinkedIn](https://www.linkedin.com/in/secretocho/)
