summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/assets/themes/blurple-dark.css92
-rw-r--r--web/assets/themes/blurple-light.css94
-rw-r--r--web/assets/themes/midnight-trip.css159
-rw-r--r--web/assets/themes/soft.css124
-rw-r--r--web/assets/themes/sunset-light.css95
-rw-r--r--web/source/settings/lib/query/user/index.ts7
-rw-r--r--web/source/settings/lib/types/theme.ts24
-rw-r--r--web/source/settings/style.css8
-rw-r--r--web/source/settings/user/profile.tsx39
9 files changed, 637 insertions, 5 deletions
diff --git a/web/assets/themes/blurple-dark.css b/web/assets/themes/blurple-dark.css
new file mode 100644
index 000000000..7e4a9cc9a
--- /dev/null
+++ b/web/assets/themes/blurple-dark.css
@@ -0,0 +1,92 @@
+/*
+ theme-title: Blurple (dark)
+ theme-description: Official dark blurple theme
+*/
+
+:root {
+ /* Define our nice blurple palette */
+ --blurple1: #ffffff;
+ --blurple2: #ebe6f8;
+ --blurple3: #d6cceb;
+ --blurple4: #c2b3e1;
+ --blurple5: #ad99d7;
+ --blurple6: #9980cd;
+ --blurple7: #8566c2;
+ --blurple8: #704db8;
+ --blurple9: #5c33ae;
+ --blurple10: #471aa4;
+ --blurple11: #33009a;
+ --blurple12: #170044;
+
+ /* Restyle basic colors to use blurple */
+ --blue1: var(--blurple1);
+ --blue2: var(--blurple2);
+ --blue3: var(--blurple3);
+
+ /* Basic page styling (background + foreground) */
+ --bg: var(--blurple12);
+ --bg-accent: var(--blurple11);
+ --fg: var(--blurple1);
+ --fg-reduced: var(--blurple3);
+
+ /* Profile page styling (light) */
+ --profile-bg: var(--blurple11);
+
+ /* Blurpleize buttons */
+ --button-bg: var(--blurple2);
+ --button-fg: var(--blurple11);
+
+ /* Blurpleize statuses */
+ --status-bg: var(--blurple11);
+ --status-focus-bg: var(--blurple11);
+ --status-info-bg: var(--blurple9);
+ --status-focus-info-bg: var(--blurple9);
+
+ /* Used around statuses + other items */
+ --boxshadow-border: 0.08rem solid black;
+}
+
+/* Scroll bar */
+html, body {
+ scrollbar-color: var(--blurple8) var(--blurple12);
+}
+
+/* Profile fields */
+.profile .about-user .fields .field {
+ border-bottom: 0.1rem solid var(--blurple8);
+}
+.profile .about-user .fields .field:first-child {
+ border-top: 0.1rem solid var(--blurple8);
+}
+
+/* Status media */
+.status .media .media-wrapper {
+ border: 0.08rem solid var(--blurple9);
+}
+.status .media .media-wrapper details .unknown-attachment .placeholder {
+ color: var(--blue2);
+}
+.status .media .media-wrapper details video.plyr-video {
+ background: var(--blurple11);
+}
+
+/* Status polls */
+.status .text .poll {
+ background-color: var(--bg);
+}
+.status .text .poll .poll-info {
+ background-color: var(--blurple11);
+}
+
+/* Code snippets */
+pre, pre[class*="language-"],
+code, code[class*="language-"] {
+ background-color: var(--blurple12);
+ color: var(--fg-reduced);
+}
+
+/* Block quotes */
+blockquote {
+ background-color: var(--blurple12);
+ color: var(--fg-reduced);
+}
diff --git a/web/assets/themes/blurple-light.css b/web/assets/themes/blurple-light.css
new file mode 100644
index 000000000..7f5811401
--- /dev/null
+++ b/web/assets/themes/blurple-light.css
@@ -0,0 +1,94 @@
+/*
+ theme-title: Blurple (light)
+ theme-description: Official light blurple theme
+*/
+
+:root {
+ /* Define our nice blurple palette */
+ --blurple1: #ffffff;
+ --blurple2: #ebe6f8;
+ --blurple3: #d6cceb;
+ --blurple4: #c2b3e1;
+ --blurple5: #ad99d7;
+ --blurple6: #9980cd;
+ --blurple7: #8566c2;
+ --blurple8: #704db8;
+ --blurple9: #5c33ae;
+ --blurple10: #471aa4;
+ --blurple11: #33009a;
+ --blurple12: #170044;
+
+ /* Restyle basic colors to use blurple */
+ --white1: var(--blurple2);
+ --white2: var(--blurple3);
+ --blue1: var(--blurple6);
+ --blue2: var(--blurple8);
+ --blue3: var(--blurple10);
+
+ /* Basic page styling (background + foreground) */
+ --bg: linear-gradient(var(--blurple2), var(--blurple1));
+ --bg-accent: var(--white2);
+ --fg: var(--gray1);
+ --fg-reduced: var(--gray2);
+
+ /* Profile page styling (light) */
+ --profile-bg: var(--white2);
+
+ /* Blurpleize buttons */
+ --button-bg: var(--blue2);
+ --button-fg: var(--white1);
+
+ /* Blurpleize statuses */
+ --status-bg: var(--white1);
+ --status-focus-bg: var(--white1);
+ --status-info-bg: var(--white2);
+ --status-focus-info-bg: var(--white2);
+
+ /* Used around statuses + other items */
+ --boxshadow-border: 0.08rem solid var(--blurple10);
+}
+
+/* Scroll bar */
+html, body {
+ scrollbar-color: var(--blurple8) var(--blurple2);
+}
+
+/* Profile fields */
+.profile .about-user .fields .field {
+ border-bottom: 0.1rem solid var(--blurple10);
+}
+.profile .about-user .fields .field:first-child {
+ border-top: 0.1rem solid var(--blurple10);
+}
+
+/* Status media */
+.status .media .media-wrapper {
+ border: 0.08rem solid var(--blurple10);
+}
+.status .media .media-wrapper details .unknown-attachment .placeholder {
+ color: var(--blue2);
+}
+.status .media .media-wrapper details video.plyr-video {
+ background: var(--blurple2);
+}
+
+/* Status polls */
+.status .text .poll {
+ background-color: var(--white2);
+}
+.status .text .poll .poll-info {
+ background-color: var(--white1);
+}
+
+/* Code snippets */
+pre, pre[class*="language-"],
+code, code[class*="language-"] {
+ background-color: var(--blurple12);
+ color: var(--blurple2);
+}
+
+/* Block quotes */
+blockquote {
+ background-color: var(--blurple1);
+ color: var(--blurple12);
+}
diff --git a/web/assets/themes/midnight-trip.css b/web/assets/themes/midnight-trip.css
new file mode 100644
index 000000000..c6b1623ee
--- /dev/null
+++ b/web/assets/themes/midnight-trip.css
@@ -0,0 +1,159 @@
+/*
+ theme-title: Midnight Trip
+ theme-description: Woah
+*/
+
+/* Theme colors */
+:root {
+ --acid-green: rgb(63, 255, 0);
+ --acid-green-light: #79FF4D;
+ --acid-green-dark: #269900;
+ --magenta: rgb(153, 50, 204);
+ --darkred: rgb(58, 0, 15);
+ --darkblue: rgb(0, 0, 58);
+ --darkmagenta: rgb(47, 1, 65);
+
+ /* Override */
+ --orange2: var(--acid-green);
+ --gray1: rgb(20, 21, 23);
+ --blue1: var(--acid-green-dark);
+ --blue2: var(--acid-green-light);
+ --blue3: var(--acid-green);
+}
+
+body {
+ background: linear-gradient(-45deg, black, var(--darkmagenta), var(--darkblue), var(--darkred));
+ background-size: 400% 400%;
+ height: 100%;
+}
+
+@media not (prefers-reduced-motion) {
+ body {
+ animation: gradient 30s ease infinite;
+ }
+
+ @keyframes gradient {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+ }
+}
+
+html, body {
+ /* Funky scroll bar */
+ scrollbar-color: var(--acid-green) var(--gray1);
+}
+
+/* Instance display name */
+.page-header {
+ grid-column: 2;
+ align-self: start;
+ margin: 1rem 0 1rem 0;
+ background-color: var(--gray1);
+ border: 0.25rem solid var(--magenta);
+ border-radius: var(--br);
+}
+
+/* Header card */
+.profile .profile-header {
+ background-color: var(--gray1);
+ border: 0.25rem solid var(--magenta);
+}
+
+/* About + Pinned posts headers */
+.profile .col-header {
+ background: var(--gray1);
+ border: 0.25rem solid var(--magenta);
+}
+
+.profile .about-user .col-header {
+ border-bottom: none;
+ margin-bottom: 0;
+}
+
+/* Make about sections transparent */
+.profile .about-user .fields, .profile .about-user .bio, .profile .about-user .accountstats {
+ background: var(--gray1);
+ border-left: 0.25rem solid var(--magenta);
+ border-right: 0.25rem solid var(--magenta);
+}
+
+/* Fiddle around with borders on about sections */
+.profile .about-user .fields .field:first-child {
+ border-top: 0.25rem dashed var(--magenta);
+}
+.profile .about-user .fields .field {
+ border-bottom: 0.25rem dashed var(--magenta);
+}
+.profile .about-user .accountstats {
+ border-top: 0.25rem dashed var(--magenta);
+ border-bottom: 0.25rem solid var(--magenta);
+}
+
+/* Statuses + threads */
+
+/* Thread column header */
+.thread .col-header {
+ background: var(--gray1);
+ border: 0.25rem solid var(--magenta);
+}
+
+/* Main status body */
+.status, .status.expanded {
+ background: var(--gray1);
+ border: 0.25rem solid var(--magenta);
+}
+
+/* Code snippets */
+.status .text .content pre, .status .text .content code {
+ background: black;
+ color: var(--white2);
+}
+
+/* Block quotes */
+.status .text .content blockquote {
+ background-color: black;
+}
+
+/* Media wrapper for attachments */
+.status .media .media-wrapper {
+ background: var(--bg-nearly-opaque);
+}
+.status .media .media-wrapper details .unknown-attachment .placeholder {
+ border: 0.2rem dashed var(--magenta);
+}
+
+/* Polls */
+.status .text .poll {
+ background-color: black;
+ border: 0.25rem solid var(--magenta);
+}
+
+.status .text .poll .poll-info {
+ background-color: black;
+}
+
+/* Status info bars */
+.status .status-info, .status.expanded .status-info {
+ background: black;
+}
+
+/* Back + next links */
+.backnextlinks {
+ background: var(--gray1);
+ padding: 0.5rem;
+ border: 0.25rem solid var(--magenta);
+ border-radius: var(--br);
+}
+
+.page-footer {
+ margin-top: 2rem;
+ background-color: var(--gray1);
+ border-top: 0.25rem solid var(--magenta);
+}
diff --git a/web/assets/themes/soft.css b/web/assets/themes/soft.css
new file mode 100644
index 000000000..6507fa701
--- /dev/null
+++ b/web/assets/themes/soft.css
@@ -0,0 +1,124 @@
+/*
+ theme-title: Soft
+ theme-description: Pastel pink and blue with dark magenta trim
+*/
+
+:root {
+ /* Define our palette */
+ --soft-pink: rgb(255, 199, 234);
+ --soft-pink-translucent: rgb(255, 199, 234, 30%);
+ --soft-lilac: #D8B4F8;
+ --soft-lilac-translucent: rgb(216, 180, 248, 30%);
+ --soft-blue: #d6f1ff;
+
+ /* Override */
+ --blue1: #7f16de;
+ --blue2: #7514cc;
+ --blue3: #6b12ba;
+ --orange2: var(--blue1);
+ --br: 0.8rem;
+ --br-inner: 0.4rem;
+
+ /* Basic page styling (background + foreground) */
+ --bg: linear-gradient(-90deg, var(--soft-blue), var(--soft-pink), white, var(--soft-pink), var(--soft-blue));
+ --bg-accent: var(--soft-pink-translucent);
+ --fg: var(--gray1);
+ --fg-reduced: var(--gray3);
+
+ /* Profile page styling (light) */
+ --profile-bg: var(--soft-pink-translucent);
+
+ /* Statuses */
+ --status-bg: var(--soft-pink-translucent);
+ --status-focus-bg: var(--soft-pink-translucent);
+ --status-info-bg: var(--soft-lilac-translucent);
+ --status-focus-info-bg: var(--soft-lilac-translucent);
+
+ /* Boot-on */
+ --button-fg: var(--white1);
+
+ /* Used around statuses + other items */
+ --boxshadow-border: 0.08rem solid var(--gray8);
+}
+
+/* Scroll bar */
+html, body {
+ scrollbar-color: var(--orange2) var(--soft-pink);
+}
+
+/* Header card */
+.profile .profile-header {
+ border: var(--boxshadow-border);
+}
+
+.profile .profile-header .basic-info .namerole .role {
+ border: var(--boxshadow-border);
+}
+
+/* About + Pinned posts headers */
+.profile .col-header {
+ border: var(--boxshadow-border);
+}
+
+.profile .about-user .col-header {
+ margin-bottom: initial;
+ border-bottom: none;
+ border-top: var(--boxshadow-border);
+ border-left: var(--boxshadow-border);
+ border-right: var(--boxshadow-border);
+}
+
+/* Profile fields + bio */
+.profile .about-user .fields {
+ border-left: var(--boxshadow-border);
+ border-right: var(--boxshadow-border);
+}
+.profile .about-user .fields .field {
+ border-bottom: 0.1rem dashed var(--blue3);
+}
+.profile .about-user .fields .field:first-child {
+ border-top: 0.1rem dashed var(--blue3);
+}
+.profile .about-user .bio {
+ border-left: var(--boxshadow-border);
+ border-right: var(--boxshadow-border);
+}
+.profile .about-user .accountstats {
+ background: var(--soft-lilac-translucent);
+ border-bottom: var(--boxshadow-border);
+ border-left: var(--boxshadow-border);
+ border-right: var(--boxshadow-border);
+ border-bottom-left-radius: var(--br);
+ border-bottom-right-radius: var(--br);
+}
+
+/* Status media */
+.status .media .media-wrapper {
+ border: 0.08rem solid var(--blue3);
+}
+.status .media .media-wrapper details .unknown-attachment .placeholder {
+ color: var(--blue2);
+}
+.status .media .media-wrapper details video.plyr-video {
+ background: var(--soft-pink-translucent);
+}
+
+/* Status polls */
+.status .text .poll {
+ background-color: var(--soft-lilac-translucent);
+}
+.status .text .poll .poll-info {
+ background: var(--bg);
+}
+
+/* Code snippets */
+pre, pre[class*="language-"],
+code, code[class*="language-"] {
+ background-color: var(--gray1);
+ color: white;
+}
+
+/* Block quotes */
+blockquote {
+ background-color: var(--soft-lilac-translucent);
+}
diff --git a/web/assets/themes/sunset-light.css b/web/assets/themes/sunset-light.css
new file mode 100644
index 000000000..c9612e36b
--- /dev/null
+++ b/web/assets/themes/sunset-light.css
@@ -0,0 +1,95 @@
+/*
+ theme-title: Sunset (light)
+ theme-description: Official light orange/yellow theme.
+*/
+
+:root {
+ /* Define our palette */
+ --eggshell: #fff6eb;
+ --yellow: #FFAF45;
+ --orange: #FB6D48;
+ --pink: #D74B76;
+ --eggplant1: #5c385e;
+ --eggplant2: #523254;
+ --eggplant3: #482c49;
+ --eggplant4: #29192a;
+
+ /* Restyle basic colors */
+ --white1: var(--eggshell);
+ --white2: var(--yellow);
+ --blue1: var(--eggplant1);
+ --blue2: var(--eggplant2);
+ --blue3: var(--eggplant3);
+ --orange2: var(--pink);
+
+ /* Basic page styling (background + foreground) */
+ --bg: linear-gradient(var(--eggplant1), var(--pink), var(--orange), var(--yellow), var(--eggshell));
+ --bg-accent: var(--white2);
+ --fg: var(--eggplant4);
+ --fg-reduced: var(--eggplant3);
+
+ /* Profile page styling (light) */
+ --profile-bg: var(--white2);
+
+ /* Buttons */
+ --button-bg: var(--blue2);
+ --button-fg: var(--white1);
+
+ /* Statuses */
+ --status-bg: var(--white1);
+ --status-focus-bg: var(--white1);
+ --status-info-bg: var(--white2);
+ --status-focus-info-bg: var(--white2);
+
+ /* Used around statuses + other items */
+ --boxshadow-border: 0.08rem solid var(--orange);
+}
+
+/* Scroll bar */
+html, body {
+ scrollbar-color: var(--pink) var(--eggshell);
+}
+
+.page-header a h1 {
+ color: var(--eggshell);
+}
+
+/* Profile fields */
+.profile .about-user .fields .field {
+ border-bottom: 0.1rem solid var(--orange);
+}
+.profile .about-user .fields .field:first-child {
+ border-top: 0.1rem solid var(--orange);
+}
+
+/* Status media */
+.status .media .media-wrapper {
+ border: 0.08rem solid var(--orange);
+}
+.status .media .media-wrapper details .unknown-attachment .placeholder {
+ color: var(--blue2);
+}
+.status .media .media-wrapper details video.plyr-video {
+ background: var(--eggshell);
+}
+
+/* Status polls */
+.status .text .poll {
+ background-color: var(--white2);
+}
+.status .text .poll .poll-info {
+ background-color: var(--white1);
+}
+
+/* Code snippets */
+pre, pre[class*="language-"],
+code, code[class*="language-"] {
+ background-color: var(--eggplant4);
+ color: var(--white1);
+}
+
+/* Block quotes */
+blockquote {
+ background-color: var(--yellow);
+ color: var(--eggplant4);
+}
diff --git a/web/source/settings/lib/query/user/index.ts b/web/source/settings/lib/query/user/index.ts
index 8cf64197b..8c4e5215b 100644
--- a/web/source/settings/lib/query/user/index.ts
+++ b/web/source/settings/lib/query/user/index.ts
@@ -23,6 +23,7 @@ import type {
MoveAccountFormData,
UpdateAliasesFormData
} from "../../types/migration";
+import type { Theme } from "../../types/theme";
const extended = gtsApi.injectEndpoints({
endpoints: (build) => ({
@@ -66,6 +67,11 @@ const extended = gtsApi.injectEndpoints({
url: `/api/v1/accounts/move`,
body: data
})
+ }),
+ accountThemes: build.query<Theme[], void>({
+ query: () => ({
+ url: `/api/v1/accounts/themes`
+ })
})
})
});
@@ -75,4 +81,5 @@ export const {
usePasswordChangeMutation,
useAliasAccountMutation,
useMoveAccountMutation,
+ useAccountThemesQuery,
} = extended;
diff --git a/web/source/settings/lib/types/theme.ts b/web/source/settings/lib/types/theme.ts
new file mode 100644
index 000000000..e605192f8
--- /dev/null
+++ b/web/source/settings/lib/types/theme.ts
@@ -0,0 +1,24 @@
+/*
+ GoToSocial
+ Copyright (C) GoToSocial Authors admin@gotosocial.org
+ SPDX-License-Identifier: AGPL-3.0-or-later
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+export interface Theme {
+ title: string;
+ description: string;
+ file_name: string;
+}
diff --git a/web/source/settings/style.css b/web/source/settings/style.css
index 501e9dbef..372031203 100644
--- a/web/source/settings/style.css
+++ b/web/source/settings/style.css
@@ -439,7 +439,7 @@ section.with-sidebar > div, section.with-sidebar > form {
display: grid;
max-width: 60rem;
grid-template-columns: 70% 30%;
- grid-template-rows: 100%;
+ grid-template-rows: auto;
gap: 1rem;
.files {
@@ -465,6 +465,12 @@ section.with-sidebar > div, section.with-sidebar > form {
gap: 0.5rem;
}
}
+
+ .theme, .form-field.radio {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ }
}
.migration-details {
diff --git a/web/source/settings/user/profile.tsx b/web/source/settings/user/profile.tsx
index a03d4d247..cd4b17227 100644
--- a/web/source/settings/user/profile.tsx
+++ b/web/source/settings/user/profile.tsx
@@ -23,7 +23,8 @@ import {
useTextInput,
useFileInput,
useBoolInput,
- useFieldArrayInput
+ useFieldArrayInput,
+ useRadioInput
} from "../lib/form";
import useFormSubmit from "../lib/form/submit";
@@ -33,14 +34,15 @@ import {
TextInput,
TextArea,
FileInput,
- Checkbox
+ Checkbox,
+ RadioGroup
} from "../components/form/inputs";
import FormWithData from "../lib/form/form-with-data";
import FakeProfile from "../components/fake-profile";
import MutationButton from "../components/form/mutation-button";
-import { useInstanceV1Query } from "../lib/query";
+import { useAccountThemesQuery, useInstanceV1Query } from "../lib/query";
import { useUpdateCredentialsMutation } from "../lib/query/user";
import { useVerifyCredentialsQuery } from "../lib/query/oauth";
@@ -64,6 +66,7 @@ function UserProfileForm({ data: profile }) {
- file header
- bool enable_rss
- string custom_css (if enabled)
+ - string theme
*/
const { data: instance } = useInstanceV1Query();
@@ -73,13 +76,24 @@ function UserProfileForm({ data: profile }) {
maxPinnedFields: instance?.configuration?.accounts?.max_profile_fields ?? 6
};
}, [instance]);
+
+ // Parse out available theme options into nice format.
+ const { data: themes } = useAccountThemesQuery();
+ let themeOptions = { "": "Default" };
+ themes?.forEach((theme) => {
+ let key = theme.file_name;
+ let value = theme.title;
+ if (theme.description) {
+ value += " - " + theme.description;
+ }
+ themeOptions[key] = value;
+ });
const form = {
avatar: useFileInput("avatar", { withPreview: true }),
header: useFileInput("header", { withPreview: true }),
displayName: useTextInput("display_name", { source: profile }),
note: useTextInput("note", { source: profile, valueSelector: (p) => p.source?.note }),
- customCSS: useTextInput("custom_css", { source: profile, nosubmit: !instanceConfig.allowCustomCSS }),
bot: useBoolInput("bot", { source: profile }),
locked: useBoolInput("locked", { source: profile }),
discoverable: useBoolInput("discoverable", { source: profile}),
@@ -88,6 +102,11 @@ function UserProfileForm({ data: profile }) {
defaultValue: profile?.source?.fields,
length: instanceConfig.maxPinnedFields
}),
+ customCSS: useTextInput("custom_css", { source: profile, nosubmit: !instanceConfig.allowCustomCSS }),
+ theme: useRadioInput("theme", {
+ source: profile,
+ options: themeOptions,
+ }),
};
const [submitForm, result] = useFormSubmit(form, useUpdateCredentialsMutation(), {
@@ -125,6 +144,18 @@ function UserProfileForm({ data: profile }) {
/>
</div>
</div>
+
+ <div className="theme">
+ <div>
+ <b id="theme-label">Theme</b>
+ <br/>
+ <span>After choosing theme and saving, <a href={profile.url} target="_blank">open your profile</a> and refresh to see changes.</span>
+ </div>
+ <RadioGroup
+ aria-labelledby="theme-label"
+ field={form.theme}
+ />
+ </div>
</div>
<div className="form-section-docs">