feat(theme): add halloween special theme

This commit is contained in:
Tommaso Casaburi
2026-05-29 17:45:18 +07:00
parent e082c7b428
commit 0469f61a45
14 changed files with 301 additions and 35 deletions
+18
View File
@@ -29,6 +29,7 @@ const testState = vi.hoisted(() => ({
initSnowMock: vi.fn(), initSnowMock: vi.fn(),
isMobile: false, isMobile: false,
isSpecialEnabled: false, isSpecialEnabled: false,
shouldShowSnow: true,
removeSnowMock: vi.fn(), removeSnowMock: vi.fn(),
replyModalState: { replyModalState: {
activeCid: null, activeCid: null,
@@ -104,6 +105,7 @@ vi.mock('../stores/use-special-theme-store', () => ({
vi.mock('../lib/snow', () => ({ vi.mock('../lib/snow', () => ({
initSnow: (options: unknown) => testState.initSnowMock(options), initSnow: (options: unknown) => testState.initSnowMock(options),
removeSnow: () => testState.removeSnowMock(), removeSnow: () => testState.removeSnowMock(),
shouldShowSnow: () => testState.shouldShowSnow,
})); }));
vi.mock('../lib/utils/preload-utils', () => ({ vi.mock('../lib/utils/preload-utils', () => ({
@@ -339,6 +341,7 @@ describe('App', () => {
]; ];
testState.isMobile = false; testState.isMobile = false;
testState.isSpecialEnabled = false; testState.isSpecialEnabled = false;
testState.shouldShowSnow = true;
testState.replyModalState = { testState.replyModalState = {
activeCid: null, activeCid: null,
closeModal: vi.fn(), closeModal: vi.fn(),
@@ -542,4 +545,19 @@ describe('App', () => {
root = createRoot(container); root = createRoot(container);
}); });
it('does not start snow for non-christmas special themes', async () => {
testState.isSpecialEnabled = true;
testState.shouldShowSnow = false;
await renderApp('/mu');
expect(testState.initSnowMock).not.toHaveBeenCalled();
expect(testState.closeCreateBoardModalMock).toHaveBeenCalledTimes(1);
act(() => root.unmount());
expect(testState.removeSnowMock).toHaveBeenCalled();
root = createRoot(container);
});
}); });
+3 -3
View File
@@ -2,7 +2,7 @@ import { lazy, Suspense, useEffect } from 'react';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import { Navigate, Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom'; import { Navigate, Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
import { useAccount, useCommunity } from '@bitsocial/bitsocial-react-hooks'; import { useAccount, useCommunity } from '@bitsocial/bitsocial-react-hooks';
import { initSnow, removeSnow } from './lib/snow'; import { initSnow, removeSnow, shouldShowSnow } from './lib/snow';
import { isAllView, isCatalogView, isModView, isSubscriptionsView } from './lib/utils/view-utils'; import { isAllView, isCatalogView, isModView, isSubscriptionsView } from './lib/utils/view-utils';
import { preloadReplyModal, preloadThemeAssets } from './lib/utils/preload-utils'; import { preloadReplyModal, preloadThemeAssets } from './lib/utils/preload-utils';
import { hasModQueueAccessRole } from './lib/utils/mod-access'; import { hasModQueueAccessRole } from './lib/utils/mod-access';
@@ -89,10 +89,10 @@ const BoardLayout = () => {
const shouldRenderOutlet = isOnPostRoute || isOnPendingPostRoute || isOnModQueueRoute || isOnArchiveRoute || isOnDirectoryRoute; const shouldRenderOutlet = isOnPostRoute || isOnPendingPostRoute || isOnModQueueRoute || isOnArchiveRoute || isOnDirectoryRoute;
const shouldRenderBoardBlotter = !isOnArchiveRoute && !isOnDirectoryRoute && !isOnModQueueRoute; const shouldRenderBoardBlotter = !isOnArchiveRoute && !isOnDirectoryRoute && !isOnModQueueRoute;
const isInCatalogView = isCatalogView(pathname, params); const isInCatalogView = isCatalogView(pathname, params);
// Christmas theme // Christmas snow
const { isEnabled: isSpecialEnabled } = useSpecialThemeStore(); const { isEnabled: isSpecialEnabled } = useSpecialThemeStore();
useEffect(() => { useEffect(() => {
if (isSpecialEnabled && !isMobile) { if (isSpecialEnabled && shouldShowSnow() && !isMobile) {
initSnow({ flakeCount: 150 }); initSnow({ flakeCount: 150 });
} }
return () => { return () => {
@@ -1,27 +1,25 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import useTheme from '../../hooks/use-theme'; import useTheme from '../../hooks/use-theme';
import useSpecialThemeStore from '../../stores/use-special-theme-store'; import useSpecialThemeStore from '../../stores/use-special-theme-store';
import { isChristmas } from '../../lib/utils/time-utils'; import { getActiveSpecialTheme } from '../../lib/utils/time-utils';
import styles from './style-selector.module.css'; import styles from './style-selector.module.css';
const StyleSelector = () => { const StyleSelector = () => {
const [theme, setTheme] = useTheme(); const [theme, setTheme] = useTheme();
const { isEnabled, setIsEnabled } = useSpecialThemeStore(); const { isEnabled, setIsEnabled } = useSpecialThemeStore();
const isChristmasTime = isChristmas(); const activeSpecialTheme = getActiveSpecialTheme();
useEffect(() => { useEffect(() => {
if (!isChristmasTime && isEnabled) { if (!activeSpecialTheme && isEnabled) {
setIsEnabled(false); setIsEnabled(false);
setTheme('tomorrow');
} }
}, [isChristmasTime, isEnabled, setIsEnabled, setTheme]); }, [activeSpecialTheme, isEnabled, setIsEnabled]);
const handleThemeChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const handleThemeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const newTheme = e.target.value; const newTheme = e.target.value;
if (newTheme === 'special') { if (newTheme === 'special') {
setIsEnabled(true); setIsEnabled(true);
setTheme('tomorrow');
} else { } else {
setIsEnabled(false); setIsEnabled(false);
setTheme(newTheme); setTheme(newTheme);
@@ -36,7 +34,7 @@ const StyleSelector = () => {
<option value='burichan'>Burichan</option> <option value='burichan'>Burichan</option>
<option value='tomorrow'>Tomorrow</option> <option value='tomorrow'>Tomorrow</option>
<option value='photon'>Photon</option> <option value='photon'>Photon</option>
{isChristmasTime && <option value='special'>Special</option>} {activeSpecialTheme && <option value='special'>Special</option>}
</select> </select>
); );
}; };
+9 -10
View File
@@ -5,12 +5,12 @@ import useThemeStore from '../stores/use-theme-store';
import { useDirectories } from './use-directories'; import { useDirectories } from './use-directories';
import { useResolvedCommunityAddress } from './use-resolved-community-address'; import { useResolvedCommunityAddress } from './use-resolved-community-address';
import useSpecialThemeStore from '../stores/use-special-theme-store'; import useSpecialThemeStore from '../stores/use-special-theme-store';
import { isChristmas } from '../lib/utils/time-utils'; import { getActiveSpecialTheme, getSpecialThemeClass } from '../lib/utils/time-utils';
import { isSfwBoard, updateFavicon } from '../lib/update-favicon'; import { isSfwBoard, updateFavicon } from '../lib/update-favicon';
import useSafeAccountComment from './use-safe-account-comment'; import useSafeAccountComment from './use-safe-account-comment';
import { getCommentCommunityAddress } from '../lib/utils/comment-utils'; import { getCommentCommunityAddress } from '../lib/utils/comment-utils';
const themeClasses = ['yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon']; const themeClasses = ['yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon', 'spooky'];
const updateThemeClass = (newTheme: string) => { const updateThemeClass = (newTheme: string) => {
document.body.classList.remove(...themeClasses); document.body.classList.remove(...themeClasses);
@@ -39,16 +39,15 @@ const useTheme = (): [string, (theme: string) => void] => {
const routeIdentifier = params.boardIdentifier; const routeIdentifier = params.boardIdentifier;
const resolvedAddress = useResolvedCommunityAddress(); const resolvedAddress = useResolvedCommunityAddress();
const communityAddress = resolvedAddress || pendingPostCommunityAddress || routeIdentifier; const communityAddress = resolvedAddress || pendingPostCommunityAddress || routeIdentifier;
const activeSpecialTheme = getActiveSpecialTheme();
useEffect(() => { useEffect(() => {
const isChristmasTime = isChristmas(); if (activeSpecialTheme && isEnabled === null && communityAddress && !isInAllView && !isInSubscriptionsView && !isInModView) {
if (isChristmasTime && isEnabled === null && communityAddress && !isInAllView && !isInSubscriptionsView && !isInModView) {
setIsEnabled(true); setIsEnabled(true);
} else if (!isChristmasTime && isEnabled) { } else if (!activeSpecialTheme && isEnabled) {
setIsEnabled(false); setIsEnabled(false);
} }
}, [isEnabled, setIsEnabled, communityAddress, isInAllView, isInSubscriptionsView, isInModView]); }, [activeSpecialTheme, isEnabled, setIsEnabled, communityAddress, isInAllView, isInSubscriptionsView, isInModView]);
const currentTheme = useMemo(() => { const currentTheme = useMemo(() => {
if (location.pathname === '/') { if (location.pathname === '/') {
@@ -59,8 +58,8 @@ const useTheme = (): [string, (theme: string) => void] => {
return 'yotsuba'; return 'yotsuba';
} }
if (isEnabled) { if (isEnabled && activeSpecialTheme) {
return 'tomorrow'; return getSpecialThemeClass(activeSpecialTheme);
} }
let storedTheme = null; let storedTheme = null;
@@ -76,7 +75,7 @@ const useTheme = (): [string, (theme: string) => void] => {
} }
return storedTheme || 'yotsuba'; return storedTheme || 'yotsuba';
}, [location.pathname, isEnabled, isInAllView, isInSubscriptionsView, isInModView, communityAddress, directories, themes]); }, [location.pathname, isEnabled, activeSpecialTheme, isInAllView, isInSubscriptionsView, isInModView, communityAddress, directories, themes]);
const sfw = isSfwBoard({ const sfw = isSfwBoard({
pathname: location.pathname, pathname: location.pathname,
+11 -3
View File
@@ -26,10 +26,13 @@ describe('snow', () => {
mathRandomSpy.mockRestore(); mathRandomSpy.mockRestore();
}); });
it('prefers the special theme store and otherwise falls back to christmas dates', async () => { it('shows snow only for christmas special theme dates', async () => {
const useSpecialThemeStore = (await import('../../stores/use-special-theme-store')).default; const useSpecialThemeStore = (await import('../../stores/use-special-theme-store')).default;
const { shouldShowSnow } = await import('../snow'); const { shouldShowSnow } = await import('../snow');
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-12-24T00:00:00Z'));
useSpecialThemeStore.setState({ isEnabled: true }); useSpecialThemeStore.setState({ isEnabled: true });
expect(shouldShowSnow()).toBe(true); expect(shouldShowSnow()).toBe(true);
@@ -37,10 +40,15 @@ describe('snow', () => {
expect(shouldShowSnow()).toBe(false); expect(shouldShowSnow()).toBe(false);
useSpecialThemeStore.setState({ isEnabled: null }); useSpecialThemeStore.setState({ isEnabled: null });
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-12-24T00:00:00Z'));
expect(shouldShowSnow()).toBe(true); expect(shouldShowSnow()).toBe(true);
vi.setSystemTime(new Date('2024-10-31T00:00:00Z'));
useSpecialThemeStore.setState({ isEnabled: true });
expect(shouldShowSnow()).toBe(false);
useSpecialThemeStore.setState({ isEnabled: null });
expect(shouldShowSnow()).toBe(false);
vi.setSystemTime(new Date('2024-07-04T00:00:00Z')); vi.setSystemTime(new Date('2024-07-04T00:00:00Z'));
expect(shouldShowSnow()).toBe(false); expect(shouldShowSnow()).toBe(false);
}); });
+4 -5
View File
@@ -1,4 +1,5 @@
import useSpecialThemeStore from '../stores/use-special-theme-store'; import useSpecialThemeStore from '../stores/use-special-theme-store';
import { getActiveSpecialTheme } from './utils/time-utils';
interface SnowOptions { interface SnowOptions {
flakeCount: number; flakeCount: number;
@@ -84,10 +85,11 @@ export const removeSnow = (): void => {
export const shouldShowSnow = (): boolean => { export const shouldShowSnow = (): boolean => {
const isEnabled = useSpecialThemeStore.getState().isEnabled; const isEnabled = useSpecialThemeStore.getState().isEnabled;
const activeSpecialTheme = getActiveSpecialTheme();
// Check store value first // Check store value first
if (isEnabled !== null) { if (isEnabled !== null) {
return isEnabled; return isEnabled && activeSpecialTheme === 'christmas';
} }
// Check manual override second // Check manual override second
@@ -95,8 +97,5 @@ export const shouldShowSnow = (): boolean => {
return manualSnowOverride; return manualSnowOverride;
} }
const today = new Date(); return activeSpecialTheme === 'christmas';
const month = today.getMonth();
const day = today.getDate();
return month === 11 && (day === 24 || day === 25);
}; };
+10 -1
View File
@@ -12,7 +12,7 @@ import {
} from '../replies-preview-utils'; } from '../replies-preview-utils';
import { getQuotedCidsFromContent, mergeQuotedCids } from '../reply-quote-utils'; import { getQuotedCidsFromContent, mergeQuotedCids } from '../reply-quote-utils';
import { formatUserIDForDisplay, truncateWithEllipsisInMiddle } from '../string-utils'; import { formatUserIDForDisplay, truncateWithEllipsisInMiddle } from '../string-utils';
import { getFormattedDate, getFormattedTimeAgo, isChristmas } from '../time-utils'; import { getActiveSpecialTheme, getFormattedDate, getFormattedTimeAgo, getSpecialThemeClass, isChristmas, isHalloween } from '../time-utils';
const testState = vi.hoisted(() => ({ const testState = vi.hoisted(() => ({
language: 'en', language: 'en',
@@ -276,8 +276,17 @@ describe('misc utils', () => {
vi.setSystemTime(new Date('2024-12-24T00:00:00Z')); vi.setSystemTime(new Date('2024-12-24T00:00:00Z'));
expect(isChristmas()).toBe(true); expect(isChristmas()).toBe(true);
expect(getActiveSpecialTheme()).toBe('christmas');
expect(getSpecialThemeClass('christmas')).toBe('tomorrow');
vi.setSystemTime(new Date('2024-10-31T00:00:00Z'));
expect(isHalloween()).toBe(true);
expect(getActiveSpecialTheme()).toBe('halloween');
expect(getSpecialThemeClass('halloween')).toBe('spooky');
vi.setSystemTime(new Date('2024-07-04T00:00:00Z')); vi.setSystemTime(new Date('2024-07-04T00:00:00Z'));
expect(isChristmas()).toBe(false); expect(isChristmas()).toBe(false);
expect(isHalloween()).toBe(false);
expect(getActiveSpecialTheme()).toBeNull();
}); });
}); });
+27
View File
@@ -92,3 +92,30 @@ export const isChristmas = (): boolean => {
const day = today.getDate(); const day = today.getDate();
return month === 11 && (day === 24 || day === 25); return month === 11 && (day === 24 || day === 25);
}; };
export const isHalloween = (): boolean => {
const today = new Date();
const month = today.getMonth();
const day = today.getDate();
return month === 9 && day === 31;
};
export type ActiveSpecialTheme = 'christmas' | 'halloween';
export type SpecialThemeClass = 'tomorrow' | 'spooky';
export const getActiveSpecialTheme = (): ActiveSpecialTheme | null => {
if (isChristmas()) {
return 'christmas';
}
if (isHalloween()) {
return 'halloween';
}
return null;
};
export const getSpecialThemeClass = (specialTheme: ActiveSpecialTheme): SpecialThemeClass => {
if (specialTheme === 'halloween') {
return 'spooky';
}
return 'tomorrow';
};
@@ -104,7 +104,7 @@ describe('persisted extra stores', () => {
expect(useModQueueStore.getState().getAlertThresholdSeconds()).toBe(900); expect(useModQueueStore.getState().getAlertThresholdSeconds()).toBe(900);
}); });
it('blocks special theme enablement outside christmas and clears persisted enabled state on rehydrate', async () => { it('blocks special theme enablement outside active special dates and clears persisted enabled state on rehydrate', async () => {
localStorage.setItem( localStorage.setItem(
'Special-theme-storage', 'Special-theme-storage',
JSON.stringify({ JSON.stringify({
@@ -121,12 +121,20 @@ describe('persisted extra stores', () => {
expect(useSpecialThemeStore.getState().isEnabled).toBeNull(); expect(useSpecialThemeStore.getState().isEnabled).toBeNull();
}); });
it('allows opting into the special theme during christmas', async () => { it('allows opting into the special theme during christmas and halloween', async () => {
const useSpecialThemeStore = await loadSpecialThemeStore('2024-12-24T00:00:00Z'); const useSpecialThemeStore = await loadSpecialThemeStore('2024-12-24T00:00:00Z');
expect(useSpecialThemeStore.getState().isEnabled).toBeNull(); expect(useSpecialThemeStore.getState().isEnabled).toBeNull();
useSpecialThemeStore.getState().setIsEnabled(true); useSpecialThemeStore.getState().setIsEnabled(true);
expect(useSpecialThemeStore.getState().isEnabled).toBe(true); expect(useSpecialThemeStore.getState().isEnabled).toBe(true);
localStorage.clear();
const useHalloweenSpecialThemeStore = await loadSpecialThemeStore('2024-10-31T00:00:00Z');
expect(useHalloweenSpecialThemeStore.getState().isEnabled).toBeNull();
useHalloweenSpecialThemeStore.getState().setIsEnabled(true);
expect(useHalloweenSpecialThemeStore.getState().isEnabled).toBe(true);
}); });
}); });
+3 -3
View File
@@ -1,6 +1,6 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { isChristmas } from '../lib/utils/time-utils'; import { getActiveSpecialTheme } from '../lib/utils/time-utils';
interface SpecialThemeStore { interface SpecialThemeStore {
isEnabled: boolean | null; isEnabled: boolean | null;
@@ -12,7 +12,7 @@ const useSpecialThemeStore = create(
(set) => ({ (set) => ({
isEnabled: null, isEnabled: null,
setIsEnabled: (value: boolean) => { setIsEnabled: (value: boolean) => {
if (value && !isChristmas()) { if (value && !getActiveSpecialTheme()) {
return; return;
} }
set({ isEnabled: value }); set({ isEnabled: value });
@@ -22,7 +22,7 @@ const useSpecialThemeStore = create(
name: 'Special-theme-storage', name: 'Special-theme-storage',
onRehydrateStorage: () => { onRehydrateStorage: () => {
return (state) => { return (state) => {
if (state && !isChristmas()) { if (state && !getActiveSpecialTheme()) {
state.isEnabled = null; state.isEnabled = null;
} }
}; };
+162 -1
View File
@@ -929,7 +929,8 @@
--mod-queue-alert-color: #d00; --mod-queue-alert-color: #d00;
} }
:root .tomorrow { :root .tomorrow,
:root .spooky {
--color-scheme: dark; --color-scheme: dark;
/* avatar */ /* avatar */
@@ -1184,6 +1185,166 @@
--mod-queue-alert-color: #cc6666; --mod-queue-alert-color: #cc6666;
} }
:root .spooky {
--body-background-base-color: #140814;
--body-font-color: #96b4aa;
--post-backlink-text-color: #d98518;
--post-backlink-text-color-hover: #ff9d1c;
--reply-backlink-mobile-background-color: #241023;
--reply-backlink-mobile-border-top: 1px solid #3a263c;
--board-title-text-color: #d98518;
--catalog-post-menu-desktop-btn-color: #d98518;
--catalog-post-menu-desktop-btn-hover-color: #ff9d1c;
--catalog-post-preview-background-color: #120c20;
--catalog-post-preview-color: #96b4aa;
--catalog-post-preview-subject-color: #b35968;
--catalog-post-preview-author-color: #d98518;
--catalog-post-preview-last-color: #96b4aa;
--challenge-modal-background-color: #241023;
--challenge-modal-border: 1px solid #3a263c;
--challenge-modal-title-background-color: #241023;
--challenge-modal-title-text-color: #d98518;
--button-desktop-text-color: #d98518;
--button-desktop-text-color-hover: #ff9d1c;
--pagelist-background-color: #241023;
--pagelist-item-background-color: #241023;
--pagelist-item-border-color: #3a263c;
--pagelist-item-text-color: #d98518;
--pagelist-item-text-color-hover: #ff9d1c;
--pagelist-item-text-color-current: #96b4aa;
--homepage-infobox-text-color: #96b4aa;
--homepage-infobox-bar-text-color: #d98518;
--homepage-infobox-bar-background-color: #241023;
--homepage-box-background-color: #1b1024;
--homepage-box-bar-background-color: #241023;
--homepage-box-border-color: #3a263c;
--homepage-box-bar-text-color: #d98518;
--homepage-box-bar-text-color-hover: #ff9d1c;
--directory-modal-background-color: #241023;
--directory-modal-border: 1px solid #3a263c;
--directory-modal-text-color: #96b4aa;
--directory-modal-header-background-color: #241023;
--directory-modal-header-text-color: #d98518;
--hr-border-top: 1px solid #3a263c;
--post-link-text-color: #d98518;
--post-link-text-color-hover: #ff9d1c;
--media-thumbnail-background-color: rgba(255, 157, 28, 0.05);
--button-text-color-mobile: #d98518;
--button-background-color-mobile: #241023;
--button-border-mobile: 1px solid #3a263c;
--post-capcode-mod-text-color: #d98518;
--post-subject-text-color: #b35968;
--post-name-text-color: #d98518;
--post-menu-desktop-button-color: #d98518;
--post-menu-desktop-button-color-hover: #ff9d1c;
--post-greentext-color: #789922;
--post-form-field-title-background-color: #241023;
--post-form-field-title-color: #d98518;
--post-form-field-border: 1px solid #3a263c;
--post-form-field-input-border: 1px solid #3a263c;
--post-form-field-input-focus-border: 1px solid #d98518;
--post-form-field-input-background-color: #120c20;
--post-form-field-input-color: #96b4aa;
--post-menu-desktop-border-bottom: 1px solid #3a263c;
--post-menu-desktop-background-color: #241023;
--post-menu-desktop-item-border-color: #3a263c;
--post-menu-desktop-item-background-color: #241023;
--post-menu-desktop-item-hover-background-color: #241023;
--post-menu-mobile-background-color: #241023;
--post-menu-mobile-border: 1px solid #3a263c;
--post-menu-mobile-item-border-bottom: 1px solid #3a263c;
--post-menu-mobile-item-hover-background-color: #241023;
--post-mobile-background-color: #1b1024;
--post-mobile-info-border-top: 1px solid #3a263c;
--post-mobile-info-border-bottom: 1px solid #3a263c;
--post-mobile-info-background-color: #241023;
--post-mobile-info-text-color: #96b4aa;
--post-mobile-menu-button-color: #d98518;
--post-mobile-name-text-color: #d98518;
--post-mobile-subject-text-color: #b35968;
--post-mobile-link-background-color: #241023;
--post-mobile-link-border-top: 1px solid #3a263c;
--post-mobile-link-border-bottom: 1px solid #3a263c;
--post-mobile-link-info-text-color: #96b4aa;
--post-mobile-abbr-text-color: #96b4aa;
--post-mobile-file-info-text-color: #96b4aa;
--post-quotelink-text-color: #d98518;
--post-quotelink-text-color-hover: #ff9d1c;
--quote-preview-background: #241023;
--quote-preview-border: 1px solid #3a263c;
--quote-preview-border-right: 1px solid #3a263c;
--quote-preview-border-bottom: 1px solid #3a263c;
--side-arrows-color: #96b4aa;
--reply-desktop-background-color: #241023;
--reply-desktop-border: 1px solid #3a263c;
--reply-desktop-border-left: 1px solid #3a263c;
--reply-desktop-border-top: 1px solid #3a263c;
--reply-modal-field-input-border: 1px solid #3a263c;
--reply-modal-field-border-focus: 1px solid #d98518;
--reply-modal-field-input-border-focus: 1px solid #d98518;
--reply-highlight-background-color: #2b1630;
--reply-highlight-border: 1px solid #6f3d22;
--reply-highlight-border-left: 1px solid #6f3d22;
--reply-highlight-border-top: 1px solid #6f3d22;
--reply-double-highlight-background-color: #33173b;
--reply-double-highlight-border: 1px solid #d98518;
--reply-double-highlight-border-left: 1px solid #d98518;
--reply-double-highlight-border-top: 1px solid #d98518;
--settings-modal-background-color: #241023;
--settings-modal-border-top: 1px solid #3a263c;
--settings-modal-border-right: 1px solid #3a263c;
--settings-modal-border-bottom: 1px solid #3a263c;
--settings-modal-border-left: 1px solid #3a263c;
--settings-modal-header-border-bottom: 1px solid #3a263c;
--tooltip-background-color: #120c20;
--boardsbar-separator-color: #96b4aa;
--boardsbar-desktop-link-text-color: #d98518;
--boardsbar-desktop-link-text-color-hover: #ff9d1c;
--boardsbar-mobile-background-color: #120c20;
--boardsbar-mobile-border-bottom: 2px solid #3a263c;
--boardsbar-mobile-button-text-color: #d98518;
--boardsbar-mobile-button-color: #d98518;
--mod-queue-table-border: 1px solid #3a263c;
--mod-queue-header-background-color: #241023;
--mod-queue-header-border-bottom: 1px solid #3a263c;
--mod-queue-row-background-color: #1b1024;
--mod-queue-row-border-bottom: 1px solid #3a263c;
--mod-queue-row-hover-background-color: #1b1024;
--mod-queue-button-text-color: #d98518;
--mod-queue-button-text-color-hover: #ff9d1c;
--mod-queue-approve-color: #d98518;
--mod-queue-approve-color-hover: #789922;
--mod-queue-reject-color: #d98518;
--mod-queue-reject-color-hover: #b35968;
--mod-queue-time-color: #96b4aa;
--mod-queue-alert-color: #b35968;
}
:root .photon { :root .photon {
--color-scheme: light; --color-scheme: light;
+11
View File
@@ -212,6 +212,10 @@
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
} }
:global(body.spooky) .rowOdd td {
background: #1b1024;
}
:global(body.tomorrow) .flashListing thead th, :global(body.tomorrow) .flashListing thead th,
:global(body.tomorrow) .flashListing thead td { :global(body.tomorrow) .flashListing thead td {
background: var(--post-form-field-title-background-color); background: var(--post-form-field-title-background-color);
@@ -219,6 +223,13 @@
border: none; border: none;
} }
:global(body.spooky) .flashListing thead th,
:global(body.spooky) .flashListing thead td {
background: var(--post-form-field-title-background-color);
color: var(--post-form-field-title-color);
border: none;
}
:global(body.photon) .rowOdd td { :global(body.photon) .rowOdd td {
background: #888; background: #888;
} }
+11
View File
@@ -307,6 +307,10 @@
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
} }
:global(body.spooky) .rowOdd td {
background: #1b1024;
}
:global(body.tomorrow) .flashListing thead th, :global(body.tomorrow) .flashListing thead th,
:global(body.tomorrow) .flashListing thead td { :global(body.tomorrow) .flashListing thead td {
background: var(--post-form-field-title-background-color); background: var(--post-form-field-title-background-color);
@@ -314,6 +318,13 @@
border: none; border: none;
} }
:global(body.spooky) .flashListing thead th,
:global(body.spooky) .flashListing thead td {
background: var(--post-form-field-title-background-color);
color: var(--post-form-field-title-color);
border: none;
}
:global(body.photon) .rowOdd td { :global(body.photon) .rowOdd td {
background: #888; background: #888;
} }
+17
View File
@@ -204,6 +204,16 @@
background-color: rgba(255, 255, 255, 0.1); background-color: rgba(255, 255, 255, 0.1);
} }
:global(body.spooky) .rowOdd .number,
:global(body.spooky) .rowOdd .board,
:global(body.spooky) .rowOdd .excerpt,
:global(body.spooky) .rowOdd .time,
:global(body.spooky) .rowOdd .type,
:global(body.spooky) .rowOdd .image,
:global(body.spooky) .rowOdd .actions {
background-color: #1b1024;
}
:global(body.photon) .rowOdd .number, :global(body.photon) .rowOdd .number,
:global(body.photon) .rowOdd .board, :global(body.photon) .rowOdd .board,
:global(body.photon) .rowOdd .excerpt, :global(body.photon) .rowOdd .excerpt,
@@ -609,6 +619,13 @@ span.reject:hover {
:global(body.tomorrow) .rowOdd .type, :global(body.tomorrow) .rowOdd .type,
:global(body.tomorrow) .rowOdd .image, :global(body.tomorrow) .rowOdd .image,
:global(body.tomorrow) .rowOdd .actions, :global(body.tomorrow) .rowOdd .actions,
:global(body.spooky) .rowOdd .number,
:global(body.spooky) .rowOdd .board,
:global(body.spooky) .rowOdd .excerpt,
:global(body.spooky) .rowOdd .time,
:global(body.spooky) .rowOdd .type,
:global(body.spooky) .rowOdd .image,
:global(body.spooky) .rowOdd .actions,
:global(body.photon) .rowOdd .number, :global(body.photon) .rowOdd .number,
:global(body.photon) .rowOdd .board, :global(body.photon) .rowOdd .board,
:global(body.photon) .rowOdd .excerpt, :global(body.photon) .rowOdd .excerpt,