revite/src/context/Theme.tsx

354 lines
10 KiB
TypeScript
Raw Normal View History

import rgba from "color-rgba";
import { observer } from "mobx-react-lite";
2021-07-05 06:23:23 -04:00
import { Helmet } from "react-helmet";
2021-06-18 10:35:35 -04:00
import { createGlobalStyle } from "styled-components";
2021-07-05 06:23:23 -04:00
import { useEffect } from "preact/hooks";
import { useApplicationState } from "../mobx/State";
2021-06-18 10:35:35 -04:00
export type Variables =
2021-07-05 06:25:20 -04:00
| "accent"
| "background"
| "foreground"
| "block"
| "message-box"
| "mention"
| "success"
| "warning"
| "error"
| "hover"
| "scrollbar-thumb"
| "scrollbar-track"
| "primary-background"
| "primary-header"
| "secondary-background"
| "secondary-foreground"
| "secondary-header"
| "tertiary-background"
| "tertiary-foreground"
| "tooltip"
2021-07-05 06:25:20 -04:00
| "status-online"
| "status-away"
| "status-busy"
| "status-streaming"
| "status-invisible";
2021-07-04 21:22:33 -04:00
// While this isn't used, it'd be good to keep this up to date as a reference or for future use
export type HiddenVariables =
2021-07-05 06:25:20 -04:00
| "font"
| "ligatures"
| "app-height"
| "sidebar-active"
| "monospace-font";
2021-07-05 06:23:23 -04:00
export type Fonts =
2021-07-05 06:25:20 -04:00
| "Open Sans"
| "Inter"
| "Atkinson Hyperlegible"
| "Roboto"
| "Noto Sans"
| "Lato"
| "Bree Serif"
| "Montserrat"
| "Poppins"
| "Raleway"
| "Ubuntu"
| "Comic Neue";
export type MonospaceFonts =
2021-07-05 06:25:20 -04:00
| "Fira Code"
| "Roboto Mono"
| "Source Code Pro"
| "Space Mono"
| "Ubuntu Mono"
| "JetBrains Mono";
export type Overrides = {
2021-07-05 06:25:20 -04:00
[variable in Variables]: string;
};
export type Theme = Overrides & {
2021-07-05 06:25:20 -04:00
light?: boolean;
font?: Fonts;
css?: string;
monospaceFont?: MonospaceFonts;
"min-opacity"?: number;
};
export type ComputedVariables = Theme & {
"header-height"?: string;
"effective-bottom-offset"?: string;
};
export interface ThemeOptions {
base?: string;
2021-07-05 06:25:20 -04:00
ligatures?: boolean;
custom?: Partial<Theme>;
}
2021-07-05 06:23:23 -04:00
export const FONTS: Record<Fonts, { name: string; load: () => void }> = {
2021-07-05 06:25:20 -04:00
"Open Sans": {
name: "Open Sans",
load: async () => {
await import("@fontsource/open-sans/300.css");
await import("@fontsource/open-sans/400.css");
await import("@fontsource/open-sans/600.css");
await import("@fontsource/open-sans/700.css");
await import("@fontsource/open-sans/400-italic.css");
},
},
Inter: {
name: "Inter",
load: async () => {
await import("@fontsource/inter/300.css");
await import("@fontsource/inter/400.css");
await import("@fontsource/inter/600.css");
await import("@fontsource/inter/700.css");
},
},
"Atkinson Hyperlegible": {
name: "Atkinson Hyperlegible",
load: async () => {
await import("@fontsource/atkinson-hyperlegible/400.css");
await import("@fontsource/atkinson-hyperlegible/700.css");
await import("@fontsource/atkinson-hyperlegible/400-italic.css");
},
},
Roboto: {
name: "Roboto",
load: async () => {
await import("@fontsource/roboto/400.css");
await import("@fontsource/roboto/700.css");
await import("@fontsource/roboto/400-italic.css");
},
},
"Noto Sans": {
name: "Noto Sans",
load: async () => {
await import("@fontsource/noto-sans/400.css");
await import("@fontsource/noto-sans/700.css");
await import("@fontsource/noto-sans/400-italic.css");
},
},
"Bree Serif": {
name: "Bree Serif",
load: () => import("@fontsource/bree-serif/400.css"),
},
Lato: {
name: "Lato",
load: async () => {
await import("@fontsource/lato/300.css");
await import("@fontsource/lato/400.css");
await import("@fontsource/lato/700.css");
await import("@fontsource/lato/400-italic.css");
},
},
Montserrat: {
name: "Montserrat",
load: async () => {
await import("@fontsource/montserrat/300.css");
await import("@fontsource/montserrat/400.css");
await import("@fontsource/montserrat/600.css");
await import("@fontsource/montserrat/700.css");
await import("@fontsource/montserrat/400-italic.css");
},
},
Poppins: {
name: "Poppins",
load: async () => {
await import("@fontsource/poppins/300.css");
await import("@fontsource/poppins/400.css");
await import("@fontsource/poppins/600.css");
await import("@fontsource/poppins/700.css");
await import("@fontsource/poppins/400-italic.css");
},
},
Raleway: {
name: "Raleway",
load: async () => {
await import("@fontsource/raleway/300.css");
await import("@fontsource/raleway/400.css");
await import("@fontsource/raleway/600.css");
await import("@fontsource/raleway/700.css");
await import("@fontsource/raleway/400-italic.css");
},
},
Ubuntu: {
name: "Ubuntu",
load: async () => {
await import("@fontsource/ubuntu/300.css");
await import("@fontsource/ubuntu/400.css");
await import("@fontsource/ubuntu/500.css");
await import("@fontsource/ubuntu/700.css");
await import("@fontsource/ubuntu/400-italic.css");
},
},
"Comic Neue": {
name: "Comic Neue",
load: async () => {
await import("@fontsource/comic-neue/300.css");
await import("@fontsource/comic-neue/400.css");
await import("@fontsource/comic-neue/700.css");
await import("@fontsource/comic-neue/400-italic.css");
},
},
};
export const MONOSPACE_FONTS: Record<
MonospaceFonts,
2021-07-05 06:25:20 -04:00
{ name: string; load: () => void }
2021-07-05 06:23:23 -04:00
> = {
2021-07-05 06:25:20 -04:00
"Fira Code": {
name: "Fira Code",
load: () => import("@fontsource/fira-code/400.css"),
},
"Roboto Mono": {
name: "Roboto Mono",
load: () => import("@fontsource/roboto-mono/400.css"),
},
"Source Code Pro": {
name: "Source Code Pro",
load: () => import("@fontsource/source-code-pro/400.css"),
},
"Space Mono": {
name: "Space Mono",
load: () => import("@fontsource/space-mono/400.css"),
},
"Ubuntu Mono": {
name: "Ubuntu Mono",
load: () => import("@fontsource/ubuntu-mono/400.css"),
},
"JetBrains Mono": {
name: "JetBrains Mono",
load: () => import("@fontsource/jetbrains-mono/400.css"),
},
};
export const FONT_KEYS = Object.keys(FONTS).sort();
export const MONOSPACE_FONT_KEYS = Object.keys(MONOSPACE_FONTS).sort();
2021-07-05 06:23:23 -04:00
export const DEFAULT_FONT = "Open Sans";
export const DEFAULT_MONO_FONT = "Fira Code";
// Generated from https://gitlab.insrt.uk/revolt/community/themes
export const PRESETS: Record<string, Theme> = {
2021-07-05 06:25:20 -04:00
light: {
accent: "#FD6671",
background: "#F6F6F6",
foreground: "#000000",
2021-07-05 06:25:20 -04:00
block: "#414141",
"message-box": "#F1F1F1",
mention: "rgba(251, 255, 0, 0.40)",
success: "#65E572",
warning: "#FAA352",
tooltip: "#FFF",
2021-07-07 19:14:23 -04:00
error: "#ED4245",
2021-07-05 06:25:20 -04:00
hover: "rgba(0, 0, 0, 0.2)",
"scrollbar-thumb": "#CA525A",
"scrollbar-track": "transparent",
"primary-background": "#FFFFFF",
"primary-header": "#F1F1F1",
"secondary-background": "#F1F1F1",
"secondary-foreground": "#1f1f1f",
2021-07-05 06:25:20 -04:00
"secondary-header": "#F1F1F1",
"tertiary-background": "#4D4D4D",
"tertiary-foreground": "#3a3a3a",
2021-07-05 06:25:20 -04:00
"status-online": "#3ABF7E",
"status-away": "#F39F00",
"status-busy": "#F84848",
"status-streaming": "#977EFF",
"status-invisible": "#A5A5A5",
},
dark: {
accent: "#FD6671",
background: "#191919",
foreground: "#F6F6F6",
block: "#2D2D2D",
"message-box": "#363636",
mention: "rgba(251, 255, 0, 0.06)",
success: "#65E572",
warning: "#FAA352",
tooltip: "#000000",
2021-07-07 19:14:23 -04:00
error: "#ED4245",
2021-07-05 06:25:20 -04:00
hover: "rgba(0, 0, 0, 0.1)",
"scrollbar-thumb": "#CA525A",
"scrollbar-track": "transparent",
"primary-background": "#242424",
"primary-header": "#363636",
"secondary-background": "#1E1E1E",
"secondary-foreground": "#C8C8C8",
"secondary-header": "#2D2D2D",
"tertiary-background": "#4D4D4D",
"tertiary-foreground": "#848484",
"status-online": "#3ABF7E",
"status-away": "#F39F00",
"status-busy": "#F84848",
"status-streaming": "#977EFF",
"status-invisible": "#A5A5A5",
},
2021-06-18 10:57:08 -04:00
};
2021-06-18 10:35:35 -04:00
const GlobalTheme = createGlobalStyle<{ theme: Theme }>`
2021-06-18 10:35:35 -04:00
:root {
${(props) => generateVariables(props.theme)}
2021-06-18 10:35:35 -04:00
}
`;
export const generateVariables = (theme: Theme) => {
return (Object.keys(theme) as Variables[]).map((key) => {
const colour = rgba(theme[key]);
if (colour) {
const [r, g, b] = colour;
return `--${key}: ${theme[key]}; --${key}-rgb: ${r}, ${g}, ${b};`;
} else {
return `--${key}: ${theme[key]};`;
}
});
};
export default observer(() => {
const settings = useApplicationState().settings;
const theme = settings.theme;
2021-07-05 06:25:20 -04:00
const root = document.documentElement.style;
useEffect(() => {
const font = theme.getFont() ?? DEFAULT_FONT;
2021-07-05 06:25:20 -04:00
root.setProperty("--font", `"${font}"`);
FONTS[font].load();
}, [root, theme.getFont()]);
2021-07-05 06:25:20 -04:00
useEffect(() => {
const font = theme.getMonospaceFont() ?? DEFAULT_MONO_FONT;
root.setProperty("--monospace-font", `"${font}"`);
MONOSPACE_FONTS[font].load();
}, [root, theme.getMonospaceFont()]);
2021-07-05 06:25:20 -04:00
useEffect(() => {
root.setProperty(
"--ligatures",
settings.get("appearance:ligatures") ? "normal" : "none",
);
}, [root, settings.get("appearance:ligatures")]);
2021-07-05 06:25:20 -04:00
useEffect(() => {
const resize = () =>
root.setProperty("--app-height", `${window.innerHeight}px`);
resize();
2021-07-05 06:25:20 -04:00
window.addEventListener("resize", resize);
return () => window.removeEventListener("resize", resize);
2021-08-05 09:47:00 -04:00
}, [root]);
const variables = theme.computeVariables();
2021-07-05 06:25:20 -04:00
return (
<>
2021-07-05 06:25:20 -04:00
<Helmet>
<meta name="theme-color" content={variables["background"]} />
2021-07-05 06:25:20 -04:00
</Helmet>
<GlobalTheme theme={variables} />
<style dangerouslySetInnerHTML={{ __html: theme.getCSS() ?? "" }} />
</>
2021-07-05 06:25:20 -04:00
);
});