Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/docs/src/components/code-sandbox/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@
background-color: #161616;
}

@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
.browser iframe {
border: 1px solid #333;
border-top: none;
}

.browser .bar {
background-color: #1f1f1f;
}

.browser .url {
background-color: #161616;
}
.browser .bar > ul > li.edit > a:hover {
color: #aaa;
}
}
}

.browser .bar > ul {
display: flex;
flex-direction: row;
Expand Down
9 changes: 4 additions & 5 deletions packages/docs/src/components/router-head/theme-script.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ export const themeStorageKey = 'theme-preference';
export const ThemeScript = () => {
const themeScript = `
try {
document.firstElementChild
.setAttribute('data-theme',
localStorage.getItem('${themeStorageKey}') ??
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
);
const getItem = localStorage.getItem('${themeStorageKey}')
if(getItem === 'light' || getItem === 'dark'){
document.firstElementChild.setAttribute('data-theme', getItem);
}
} catch (err) { }`;
return <script dangerouslySetInnerHTML={themeScript} />;
};
19 changes: 19 additions & 0 deletions packages/docs/src/components/theme-toggle/Brilliance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { component$ } from '@builder.io/qwik';

interface BrillianceIconProps {
class?: string;
}

export const BrillianceIcon = component$<BrillianceIconProps>(({ class: className, ...props }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
class={className}
viewBox="0 0 16 16"
{...props}
>
<path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16M1 8a7 7 0 0 0 7 7 3.5 3.5 0 1 0 0-7 3.5 3.5 0 1 1 0-7 7 7 0 0 0-7 7" />
</svg>
);
});
25 changes: 25 additions & 0 deletions packages/docs/src/components/theme-toggle/Moon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { component$ } from '@builder.io/qwik';

interface MoonIconProps {
class?: string;
}

export const MoonIcon = component$<MoonIconProps>(({ class: className, ...props }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class={className}
{...props}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"
/>
</svg>
);
});
25 changes: 25 additions & 0 deletions packages/docs/src/components/theme-toggle/Sun.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { component$ } from '@builder.io/qwik';

interface SunIconProps {
class?: string;
}

export const SunIcon = component$<SunIconProps>(({ class: className, ...props }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class={className}
{...props}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z"
/>
</svg>
);
});
84 changes: 0 additions & 84 deletions packages/docs/src/components/theme-toggle/sun-and-moon.css

This file was deleted.

25 changes: 0 additions & 25 deletions packages/docs/src/components/theme-toggle/sun-and-moon.tsx

This file was deleted.

40 changes: 12 additions & 28 deletions packages/docs/src/components/theme-toggle/theme-toggle.css
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
.theme-toggle {
--size: 22px;
--icon-fill: hsl(210 10% 15%);
--icon-fill-hover: hsl(210 10% 30%);

display: block;

background: none;
border: none;
padding: 0;

inline-size: var(--size);
block-size: var(--size);
aspect-ratio: 1;
border-radius: 50%;

cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;

outline-offset: 5px;
.themeIcon {
opacity: 0;
transition: opacity 400ms ease-in-out;
width: 25px;
height: 25px;
}

[data-theme='dark'] .theme-toggle {
--icon-fill: hsl(210 10% 100%);
--icon-fill-hover: hsl(210 15% 70%);
.themeIcon.auto {
width: 21px;
height: 21px;
}

.theme-toggle > svg {
inline-size: 100%;
block-size: 100%;
stroke-linecap: round;
html[data-theme='light'] .themeIcon.light,
html[data-theme='dark'] .themeIcon.dark,
html:not([data-theme]) .themeIcon.auto {
opacity: 1;
}
92 changes: 45 additions & 47 deletions packages/docs/src/components/theme-toggle/theme-toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
import { component$, event$, useContext, useStyles$ } from '@builder.io/qwik';
import { SunAndMoon } from './sun-and-moon';
import { component$, event$, isServer, useStyles$ } from '@builder.io/qwik';
import { themeStorageKey } from '../router-head/theme-script';
import themeToggle from './theme-toggle.css?inline';
import { GlobalStore } from '../../context';
import { SunIcon } from './Sun';
import { MoonIcon } from './Moon';
import { BrillianceIcon } from './Brilliance';
export type ThemePreference = 'dark' | 'light' | 'auto';
export const setPreference = (theme: ThemePreference) => {
if (theme === 'auto') {
document.firstElementChild?.removeAttribute('data-theme');
} else {
document.firstElementChild?.setAttribute('data-theme', theme!);
}

localStorage.setItem(themeStorageKey, theme);
};

export type ThemePreference = 'dark' | 'light';
export const getColorPreference = (): ThemePreference => {
if (isServer) {
return 'auto';
}
let theme;
try {
theme = localStorage.getItem(themeStorageKey);
} catch {
//
}
return (theme as ThemePreference) || 'auto';
};

export const colorSchemeChangeListener = (onColorSchemeChange: (isDark: boolean) => void) => {
const listener = ({ matches: isDark }: MediaQueryListEvent) => {
Expand All @@ -18,56 +40,32 @@ export const colorSchemeChangeListener = (onColorSchemeChange: (isDark: boolean)
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', listener);
};

export const setPreference = (theme: ThemePreference) => {
localStorage.setItem(themeStorageKey, theme);
reflectPreference(theme);
};

export const reflectPreference = (theme: ThemePreference) => {
document.firstElementChild?.setAttribute('data-theme', theme);
};

export const getColorPreference = (): ThemePreference => {
let theme;
try {
theme = localStorage.getItem(themeStorageKey);
} catch (err) {
//
}
if (theme) {
return theme as ThemePreference;
} else {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
};

export const ThemeToggle = component$(() => {
useStyles$(themeToggle);
const state = useContext(GlobalStore);

const onClick$ = event$(() => {
state.theme = state.theme === 'light' ? 'dark' : 'light';
setPreference(state.theme);
let currentTheme = getColorPreference();
if (currentTheme === 'dark') {
currentTheme = 'light';
} else if (currentTheme === 'light') {
currentTheme = 'auto';
} else {
currentTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'light' : 'dark';
}
setPreference(currentTheme);
});

return (
<>
<span class="lg:hidden">
<button onClick$={onClick$}>{state.theme === 'light' ? 'Dark' : 'Light'} theme</button>
</span>
<span class="hidden lg:block">
<button
type="button"
class="theme-toggle"
id="theme-toggle"
title="Toggles light & dark"
aria-label={state.theme}
aria-live="polite"
onClick$={onClick$}
>
<SunAndMoon />
</button>
</span>
<button
onClick$={onClick$}
class="group relative flex h-8 w-8 items-center justify-center rounded-md bg-background text-foreground hover:opacity-60"
>
<div class="absolute inset-0 grid place-items-center transition-transform duration-200 ease-out group-hover:scale-110 group-active:scale-75">
<SunIcon class="themeIcon light col-start-1 row-start-1" />
<MoonIcon class="themeIcon dark col-start-1 row-start-1" />
<BrillianceIcon class="themeIcon auto col-start-1 row-start-1" />
</div>
</button>
</>
);
});
Loading
Loading