Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
57 changes: 55 additions & 2 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
@import "tw-animate-css";
@import "shadcn/tailwind.css";

@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *, [data-theme="oled"], [data-theme="oled"] *));

:root {
color-scheme: light;
Expand Down Expand Up @@ -54,7 +54,8 @@
--danger-background: #fdeeee;
}

[data-theme="dark"] {
[data-theme="dark"],
[data-theme="oled"] {
color-scheme: dark;
--background: #070706;
--foreground: #f3f3ef;
Expand Down Expand Up @@ -101,6 +102,54 @@
--danger-background: #2c1514;
}

[data-theme="oled"] {
--background: #000000;
--foreground: #e6e6e6;
--card: #0a0a0a;
--card-foreground: #e6e6e6;
--popover: #101010;
--popover-foreground: #e6e6e6;
--primary: #dedede;
--primary-foreground: #000000;
--secondary: #161616;
--secondary-foreground: #e6e6e6;
--muted: #181818;
--muted-foreground: #a3a3a3;
--subtle-foreground: #949494;
--accent: #222222;
--accent-foreground: #e6e6e6;
--border: #242424;
--input: #303030;
--ring: #8a8a8a;
--chart-1: #dedede;
--chart-2: #9e9e9e;
--chart-3: #626262;
--chart-4: #383838;
--chart-5: #242424;
--sidebar: #060606;
--sidebar-foreground: #e6e6e6;
--sidebar-primary: #dedede;
--sidebar-primary-foreground: #000000;
--sidebar-accent: #1e1e1e;
--sidebar-accent-foreground: #e6e6e6;
--sidebar-border: #242424;
--sidebar-ring: #8a8a8a;
--surface-subtle: #121212;
--field: #161616;
--inverse: #121212;
--inverse-foreground: #e6e6e6;
--success-background: #06110b;
--info-background: #060d13;
--warning-background: #130f06;
--danger-background: #140908;
}

[data-theme="dark"] [data-sidebar-hover-target][aria-current="page"],
[data-theme="oled"] [data-sidebar-hover-target][aria-current="page"] {
background: var(--sidebar-accent);
color: var(--sidebar-accent-foreground);
}

@theme inline {
--font-heading: "Geist Sans", ui-sans-serif, system-ui, sans-serif;
--font-sans: "Geist Sans", ui-sans-serif, system-ui, sans-serif;
Expand Down Expand Up @@ -298,6 +347,10 @@
);
}

[data-theme="oled"] .auth-shell-glow {
display: none;
}

@keyframes folio-dashboard-enter {
from {
opacity: 0;
Expand Down
6 changes: 3 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const themeBootstrap = `
(() => {
try {
const stored = localStorage.getItem("folio-theme");
const preference = stored === "light" || stored === "dark" || stored === "system"
const preference = stored === "light" || stored === "dark" || stored === "oled" || stored === "system"
? stored
: "light";
const resolved = preference === "system"
Expand All @@ -35,9 +35,9 @@ const themeBootstrap = `
const root = document.documentElement;
root.dataset.theme = resolved;
root.dataset.themePreference = preference;
root.style.colorScheme = resolved;
root.style.colorScheme = resolved === "light" ? "light" : "dark";
document.querySelector('meta[name="theme-color"]')
?.setAttribute("content", resolved === "dark" ? "#070706" : "#f7f7f5");
?.setAttribute("content", resolved === "oled" ? "#000000" : resolved === "dark" ? "#070706" : "#f7f7f5");
} catch {
document.documentElement.dataset.theme = "light";
document.documentElement.dataset.themePreference = "light";
Expand Down
22 changes: 15 additions & 7 deletions src/components/folio/app-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import {
ChartNoAxesCombined,
FullMoon,
Monitor,
Package,
} from "@/components/ui/icons";
Expand Down Expand Up @@ -435,6 +436,7 @@ function NavigationLink({
const themeOptions = [
{ label: "Light", value: "light", icon: Sun03Icon },
{ label: "Dark", value: "dark", icon: Moon02Icon },
{ label: "OLED", value: "oled", icon: FullMoon },
{ label: "System", value: "system", icon: Monitor },
] satisfies readonly {
icon: NavigationIcon
Expand All @@ -449,7 +451,9 @@ function ThemeMenu({
}) {
const { preference, resolvedTheme, setPreference } = useTheme()
const themeIconRef = useRef<AnimatedIconHandle>(null)
const ThemeIcon = resolvedTheme === "dark" ? Moon02Icon : Sun03Icon
const ThemeIcon = resolvedTheme === "light" ? Sun03Icon : Moon02Icon
const themeIconClassName =
"size-3.5 shrink-0 motion-safe:transition-transform motion-safe:duration-150 motion-safe:group-hover:scale-110 motion-safe:group-active:scale-95 [&_svg]:size-full"
const selectedLabel =
themeOptions.find((option) => option.value === preference)?.label ??
"Light"
Expand Down Expand Up @@ -480,12 +484,16 @@ function ThemeMenu({
onPointerLeave={stopIconAnimation}
type="button"
>
<ThemeIcon
ref={themeIconRef}
aria-hidden="true"
className="size-3.5 shrink-0 motion-safe:transition-transform motion-safe:duration-150 motion-safe:group-hover:scale-110 motion-safe:group-active:scale-95 [&_svg]:size-full"
size={14}
/>
{resolvedTheme === "oled" ? (
<FullMoon aria-hidden="true" className={themeIconClassName} size={14} />
) : (
<ThemeIcon
ref={themeIconRef}
aria-hidden="true"
className={themeIconClassName}
size={14}
/>
)}
<span>Appearance</span>
<span className="ml-auto text-[11px] font-normal text-subtle-foreground">
{selectedLabel}
Expand Down
17 changes: 10 additions & 7 deletions src/components/folio/theme-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
type ReactNode,
} from "react"

type ThemePreference = "light" | "dark" | "system"
type ResolvedTheme = "light" | "dark"
type ThemePreference = "light" | "dark" | "oled" | "system"
type ResolvedTheme = "light" | "dark" | "oled"

type ThemeContextValue = {
preference: ThemePreference
Expand All @@ -24,7 +24,7 @@ const SERVER_THEME_SNAPSHOT = "light:light"
const ThemeContext = createContext<ThemeContextValue | null>(null)

function isThemePreference(value: string | null): value is ThemePreference {
return value === "light" || value === "dark" || value === "system"
return value === "light" || value === "dark" || value === "oled" || value === "system"
}

function systemTheme(mediaQuery: MediaQueryList): ResolvedTheme {
Expand All @@ -37,10 +37,13 @@ function applyTheme(preference: ThemePreference, mediaQuery: MediaQueryList) {
const root = document.documentElement
root.dataset.theme = resolved
root.dataset.themePreference = preference
root.style.colorScheme = resolved
root.style.colorScheme = resolved === "light" ? "light" : "dark"
document
.querySelector<HTMLMetaElement>('meta[name="theme-color"]')
?.setAttribute("content", resolved === "dark" ? "#070706" : "#f7f7f5")
?.setAttribute(
"content",
resolved === "oled" ? "#000000" : resolved === "dark" ? "#070706" : "#f7f7f5",
)
return resolved
}

Expand All @@ -50,8 +53,8 @@ function getThemeSnapshot() {
const preference = isThemePreference(storedPreference)
? storedPreference
: "light"
const resolvedTheme =
document.documentElement.dataset.theme === "dark" ? "dark" : "light"
const theme = document.documentElement.dataset.theme
const resolvedTheme = theme === "dark" || theme === "oled" ? theme : "light"
return `${preference}:${resolvedTheme}`
}

Expand Down
2 changes: 2 additions & 0 deletions src/components/ui/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import Key01Data from "@hugeicons/core-free-icons/Key01Icon";
import Loading03Data from "@hugeicons/core-free-icons/Loading03Icon";
import Location01Data from "@hugeicons/core-free-icons/Location01Icon";
import Mail01Data from "@hugeicons/core-free-icons/Mail01Icon";
import Moon01Data from "@hugeicons/core-free-icons/Moon01Icon";
import PackageData from "@hugeicons/core-free-icons/PackageIcon";
import PackageAddData from "@hugeicons/core-free-icons/PackageAddIcon";
import PauseData from "@hugeicons/core-free-icons/PauseIcon";
Expand Down Expand Up @@ -113,6 +114,7 @@ export const FileImage = createIcon(FileImageData, "FileImage");
export const FilePlus2 = createIcon(FileAddData, "FilePlus2");
export const FileText = createIcon(Invoice02Data, "FileText");
export const FileTextIcon = FileText;
export const FullMoon = createIcon(Moon01Data, "FullMoon");
export const House = createIcon(Home01Data, "House");
export const ImagePlus = createIcon(ImageAdd01Data, "ImagePlus");
export const KeyRoundIcon = createIcon(Key01Data, "KeyRoundIcon");
Expand Down