Skip to content

Commit 4f929ff

Browse files
dimfeldtechniq
authored andcommitted
add ThemeInit component that place script inside <head> tag (#176)
* add ThemeInit component that place script inside <head> tag * Fix bug * proper sorting * instantiate ThemeInit inside Settings component * add comment about stringified function
1 parent e84e719 commit 4f929ff

File tree

5 files changed

+48
-0
lines changed

5 files changed

+48
-0
lines changed

.changeset/friendly-pets-reflect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-ux": patch
3+
---
4+
5+
Add ThemeInit component to prevent flash of unstyled content when SSR is enabled
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
<script lang="ts">
2+
import ThemeInit from './ThemeInit.svelte';
23
import { settings as setSettings, type Settings } from './settings';
34
45
export let settings: Settings;
6+
/** Include the ThemeInit component to improve SSR compatibility. */
7+
export let themeInit = true;
58
69
setSettings(settings);
710
</script>
811

12+
{#if themeInit}
13+
<ThemeInit />
14+
{/if}
915
<slot />
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
import { createHeadSnippet } from '../styles/theme';
3+
import { getSettings } from './settings';
4+
5+
const darkThemes = getSettings().themes?.dark ?? [];
6+
7+
let headSnippet = createHeadSnippet(darkThemes);
8+
</script>
9+
10+
<svelte:head>
11+
{@html headSnippet}
12+
</svelte:head>

packages/svelte-ux/src/lib/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export { default as Tab } from './Tab.svelte';
8383
export { default as Tabs } from './Tabs.svelte';
8484
export { default as TextField } from './TextField.svelte';
8585
export { default as ThemeButton } from './ThemeButton.svelte';
86+
export { default as ThemeInit } from './ThemeInit.svelte';
8687
export { default as Tilt } from './Tilt.svelte';
8788
export { default as Toggle } from './Toggle.svelte';
8889
export { default as ToggleButton } from './ToggleButton.svelte';

packages/svelte-ux/src/lib/styles/theme.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,27 @@ export const colorNames = [
2020
'surface-300',
2121
'surface-content',
2222
];
23+
24+
/** Return a script tag that will set the initial theme from localStorage. This allows setting
25+
* the theme before anything starts rendering, even when SSR is in use.
26+
*
27+
* This feels a bit weird compared to just placing the function directly in svelte:head,
28+
* but it's the only way to inject the `darkThemes` array into the function.
29+
**/
30+
export function createHeadSnippet(darkThemes: string[]) {
31+
function _applyInitialStyle(darkThemes) {
32+
let theme = localStorage.getItem('theme');
33+
if (theme) {
34+
document.documentElement.dataset.theme = theme;
35+
if (darkThemes.includes(theme)) {
36+
document.documentElement.classList.add('dark');
37+
}
38+
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
39+
document.documentElement.classList.add('dark');
40+
}
41+
}
42+
43+
let darkThemeList = darkThemes.map((theme) => `'${theme}'`).join(', ');
44+
45+
return `<script>(${_applyInitialStyle.toString()})([${darkThemeList}])</script>`;
46+
}

0 commit comments

Comments
 (0)