Skip to content

Commit fe4d237

Browse files
committed
fix(platform): apply system appearance before hydration
1 parent 0c2124a commit fe4d237

5 files changed

Lines changed: 53 additions & 3 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { expect, test } from '@playwright/test'
2+
3+
test.use({ javaScriptEnabled: false })
4+
5+
test('system appearance follows the OS before hydration while an explicit appearance stays fixed', async ({
6+
page,
7+
}) => {
8+
const surface = page.locator('[data-platform-ui]')
9+
const canvas = { light: 'rgb(255, 255, 255)', dark: 'rgb(20, 20, 20)' }
10+
for (const preference of ['system', 'light', 'dark'] as const) {
11+
await page.goto(`http://127.0.0.1:3181/home?theme=${preference}`)
12+
for (const os of ['dark', 'light'] as const) {
13+
await page.emulateMedia({ colorScheme: os })
14+
const appearance = preference === 'system' ? os : preference
15+
await expect(surface).toHaveCSS('background-color', canvas[appearance])
16+
await expect(surface).toHaveCSS('color-scheme', appearance)
17+
}
18+
}
19+
})

‎apps/sim/platform/design-system/components/platform-surface.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import type { ReactNode } from 'react'
44
import { useInterfacePreferences } from '@/platform/design-system/components/interface-preferences'
5+
import type { Appearance } from '@/platform/design-system/navigation'
56

67
interface PlatformSurfaceProps {
7-
theme: 'light' | 'dark'
8+
theme: Appearance
89
children: ReactNode
910
}
1011

‎apps/sim/platform/design-system/components/product-surface.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function ProductSurface({ preference, children }: ProductSurfaceProps) {
2525
const theme = preference === 'system' ? (dark ? 'dark' : 'light') : preference
2626
return (
2727
<ThemeContext.Provider value={theme}>
28-
<PlatformSurface theme={theme}>{children}</PlatformSurface>
28+
<PlatformSurface theme={preference}>{children}</PlatformSurface>
2929
</ThemeContext.Provider>
3030
)
3131
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/** Reuse explicit Platform theme rules for system appearance before React hydrates. */
2+
module.exports = {
3+
postcssPlugin: 'sim-system-appearance',
4+
OnceExit(root, { postcss }) {
5+
root.walkRules(/\[data-platform-ui\]\[data-theme=/, (rule) => {
6+
for (const scheme of ['dark', 'light']) {
7+
const explicit = new RegExp(`^\\[data-platform-ui\\]\\[data-theme=["']?${scheme}["']?\\]`)
8+
const selectors = rule.selectors
9+
.filter((selector) => explicit.test(selector))
10+
.map((selector) => selector.replace(explicit, '[data-platform-ui][data-theme="system"]'))
11+
if (selectors.length) {
12+
const media = postcss.atRule({
13+
name: 'media',
14+
params: `(prefers-color-scheme: ${scheme})`,
15+
})
16+
rule.after(media.append(rule.clone({ selectors })))
17+
}
18+
}
19+
})
20+
},
21+
}
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
1-
export default { plugins: { '@tailwindcss/postcss': {} } }
1+
import { createRequire } from 'node:module'
2+
3+
const require = createRequire(import.meta.url)
4+
5+
export default {
6+
plugins: {
7+
'@tailwindcss/postcss': {},
8+
[require.resolve('./design-system/styles/system-appearance.cjs')]: {},
9+
},
10+
}

0 commit comments

Comments
 (0)