feat: remove dark mode toggle, switch to prefers-color-scheme
This means it's not possible to manually change the theme, but all major operating systems support global dark mode now, so it shouldn't be a huge problem. Will re-add the dark mode toggle if the Sec-CH-Prefers-Color-Scheme header gets added to Firefox and Safari.
This commit is contained in:
parent
862a64840e
commit
0f3ab19f6f
9 changed files with 25 additions and 64 deletions
|
@ -1,20 +1,7 @@
|
||||||
$font-family-sans-serif:
|
@use "bootstrap/scss/bootstrap" with (
|
||||||
"FiraGO",
|
$color-mode-type: media-query,
|
||||||
system-ui,
|
$font-family-sans-serif: ("FiraGO", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"),
|
||||||
-apple-system,
|
);
|
||||||
"Segoe UI",
|
|
||||||
Roboto,
|
|
||||||
"Helvetica Neue",
|
|
||||||
"Noto Sans",
|
|
||||||
"Liberation Sans",
|
|
||||||
Arial,
|
|
||||||
sans-serif,
|
|
||||||
"Apple Color Emoji",
|
|
||||||
"Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol",
|
|
||||||
"Noto Color Emoji" !default;
|
|
||||||
|
|
||||||
@import "bootstrap/scss/bootstrap";
|
|
||||||
|
|
||||||
@import "@fontsource/firago/400.css";
|
@import "@fontsource/firago/400.css";
|
||||||
@import "@fontsource/firago/400-italic.css";
|
@import "@fontsource/firago/400-italic.css";
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { Nav, Navbar } from "react-bootstrap";
|
||||||
import { Link } from "@remix-run/react";
|
import { Link } from "@remix-run/react";
|
||||||
import Logo from "~/components/nav/Logo";
|
import Logo from "~/components/nav/Logo";
|
||||||
|
|
||||||
export default function BaseNavbar({ children, theme }: { children?: ReactNode; theme: string }) {
|
export default function BaseNavbar({ children }: { children?: ReactNode; }) {
|
||||||
return (
|
return (
|
||||||
<Navbar expand="lg" className={`mb-4 mx-2 bg-${theme}`} color={theme} variant={theme}>
|
<Navbar expand="lg" className={`mb-4 mx-2`}>
|
||||||
<Navbar.Brand to="/" as={Link}>
|
<Navbar.Brand to="/" as={Link}>
|
||||||
<Logo />
|
<Logo />
|
||||||
</Navbar.Brand>
|
</Navbar.Brand>
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
import { Link, useFetcher } from "@remix-run/react";
|
import { Link, useFetcher } from "@remix-run/react";
|
||||||
import Meta from "~/lib/api/meta";
|
import Meta from "~/lib/api/meta";
|
||||||
import { User, UserSettings } from "~/lib/api/user";
|
import { User } from "~/lib/api/user";
|
||||||
|
|
||||||
import { Nav, NavDropdown } from "react-bootstrap";
|
import { Nav, NavDropdown } from "react-bootstrap";
|
||||||
import { BrightnessHigh, BrightnessHighFill, MoonFill } from "react-bootstrap-icons";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import BaseNavbar from "~/components/nav/BaseNavbar";
|
import BaseNavbar from "~/components/nav/BaseNavbar";
|
||||||
|
|
||||||
export default function MainNavbar({
|
export default function MainNavbar({
|
||||||
user,
|
user,
|
||||||
settings,
|
|
||||||
}: {
|
}: {
|
||||||
meta: Meta;
|
meta: Meta;
|
||||||
user?: User;
|
user?: User;
|
||||||
settings: UserSettings;
|
|
||||||
}) {
|
}) {
|
||||||
const fetcher = useFetcher();
|
const fetcher = useFetcher();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -39,38 +36,9 @@ export default function MainNavbar({
|
||||||
</Nav.Link>
|
</Nav.Link>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ThemeIcon =
|
|
||||||
settings.dark_mode === null
|
|
||||||
? BrightnessHigh
|
|
||||||
: settings.dark_mode
|
|
||||||
? MoonFill
|
|
||||||
: BrightnessHighFill;
|
|
||||||
|
|
||||||
const theme = settings.dark_mode ? "dark" : "light";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseNavbar theme={theme}>
|
<BaseNavbar>
|
||||||
{userMenu}
|
{userMenu}
|
||||||
<fetcher.Form method="POST" action="/dark-mode">
|
|
||||||
<NavDropdown
|
|
||||||
title={
|
|
||||||
<>
|
|
||||||
<ThemeIcon /> {t("navbar.theme")}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
align="end"
|
|
||||||
>
|
|
||||||
<NavDropdown.Item as="button" name="theme" value="auto" type="submit">
|
|
||||||
{t("navbar.theme-auto")}
|
|
||||||
</NavDropdown.Item>
|
|
||||||
<NavDropdown.Item as="button" name="theme" value="dark" type="submit">
|
|
||||||
{t("navbar.theme-dark")}
|
|
||||||
</NavDropdown.Item>
|
|
||||||
<NavDropdown.Item as="button" name="theme" value="light" type="submit">
|
|
||||||
{t("navbar.theme-light")}
|
|
||||||
</NavDropdown.Item>
|
|
||||||
</NavDropdown>
|
|
||||||
</fetcher.Form>
|
|
||||||
</BaseNavbar>
|
</BaseNavbar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Layout({ children }: { children: ReactNode }) {
|
export function Layout({ children }: { children: ReactNode }) {
|
||||||
const { locale, settings } = useRouteLoaderData<typeof loader>("root") || {
|
const { locale } = useRouteLoaderData<typeof loader>("root") || {
|
||||||
meta: {
|
meta: {
|
||||||
users: {
|
users: {
|
||||||
total: 0,
|
total: 0,
|
||||||
|
@ -77,7 +77,6 @@ export function Layout({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<html
|
<html
|
||||||
lang={locale || "en"}
|
lang={locale || "en"}
|
||||||
data-bs-theme={settings?.dark_mode ? "dark" : "light"}
|
|
||||||
dir={i18n.dir()}
|
dir={i18n.dir()}
|
||||||
>
|
>
|
||||||
<head>
|
<head>
|
||||||
|
@ -119,10 +118,10 @@ export function ErrorBoundary() {
|
||||||
<Links />
|
<Links />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{data?.meUser && data?.settings && data?.meta ? (
|
{data?.meUser && data?.meta ? (
|
||||||
<Navbar meta={data.meta} user={data.meUser} settings={data.settings} />
|
<Navbar meta={data.meta} user={data.meUser} />
|
||||||
) : (
|
) : (
|
||||||
<BaseNavbar theme="light" />
|
<BaseNavbar />
|
||||||
)}
|
)}
|
||||||
<Container>{errorElem}</Container>
|
<Container>{errorElem}</Container>
|
||||||
<Scripts />
|
<Scripts />
|
||||||
|
|
|
@ -13,7 +13,7 @@ import PronounLink from "~/components/PronounLink";
|
||||||
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
||||||
const { user } = data!;
|
const { user } = data!;
|
||||||
|
|
||||||
return [{ title: `@${user.username} - pronouns.cc` }];
|
return [{ title: `@${user.username} • pronouns.cc` }];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import ErrorAlert from "~/components/ErrorAlert";
|
||||||
import i18n from "~/i18next.server";
|
import i18n from "~/i18next.server";
|
||||||
|
|
||||||
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
||||||
return [{ title: `${data?.meta.title || "Log in"} - pronouns.cc` }];
|
return [{ title: `${data?.meta.title || "Log in"} • pronouns.cc` }];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shouldRevalidate: ShouldRevalidateFunction = ({ actionResult }) => {
|
export const shouldRevalidate: ShouldRevalidateFunction = ({ actionResult }) => {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import ErrorAlert from "~/components/ErrorAlert";
|
||||||
import { User } from "~/lib/api/user";
|
import { User } from "~/lib/api/user";
|
||||||
|
|
||||||
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
||||||
return [{ title: `${data?.meta.title || "Log in"} - pronouns.cc` }];
|
return [{ title: `${data?.meta.title || "Log in"} • pronouns.cc` }];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shouldRevalidate: ShouldRevalidateFunction = ({ actionResult }) => {
|
export const shouldRevalidate: ShouldRevalidateFunction = ({ actionResult }) => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Link, useLoaderData } from "@remix-run/react";
|
||||||
import { Button } from "react-bootstrap";
|
import { Button } from "react-bootstrap";
|
||||||
|
|
||||||
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
||||||
return [{ title: `${data?.meta.title || "Welcome"} - pronouns.cc` }];
|
return [{ title: `${data?.meta.title || "Welcome"} • pronouns.cc` }];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||||
|
|
|
@ -4,6 +4,13 @@ import serverRequest, { getCookie, writeCookie } from "~/lib/request.server";
|
||||||
|
|
||||||
// Handles theme switching
|
// Handles theme switching
|
||||||
// Remix itself handles redirecting back to the original page after the setting is set
|
// Remix itself handles redirecting back to the original page after the setting is set
|
||||||
|
//
|
||||||
|
// Note: this function is currently unused. Bootstrap only lets us switch themes with either prefers-color-scheme
|
||||||
|
// *or* a programmatic switch using data-bs-theme, not both.
|
||||||
|
// If the Sec-CH-Prefers-Color-Scheme header (https://caniuse.com/mdn-http_headers_sec-ch-prefers-color-scheme)
|
||||||
|
// is added to Firefox and Safari, the dark mode setting should be reworked to use it instead.
|
||||||
|
// As it stands, using prefers-color-scheme is the only way
|
||||||
|
// to respect the operating system's dark mode setting without using JavaScript.
|
||||||
export const action: ActionFunction = async ({ request }) => {
|
export const action: ActionFunction = async ({ request }) => {
|
||||||
const body = await request.formData();
|
const body = await request.formData();
|
||||||
const theme = (body.get("theme") as string | null) || "auto";
|
const theme = (body.get("theme") as string | null) || "auto";
|
||||||
|
@ -13,7 +20,7 @@ export const action: ActionFunction = async ({ request }) => {
|
||||||
await serverRequest<UserSettings>("PATCH", "/users/@me/settings", {
|
await serverRequest<UserSettings>("PATCH", "/users/@me/settings", {
|
||||||
token,
|
token,
|
||||||
body: {
|
body: {
|
||||||
dark_mode: theme === "auto" ? null : theme === "dark" ? true : false,
|
dark_mode: theme === "auto" ? null : theme === "dark",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue