) => {
+ if (e.target === e.currentTarget) {
+ closeTopbarEditModal();
+ }
+ };
+
+ const handleSubscriptionToggle = (address: string) => {
+ const newSet = new Set(localSubscriptionVisibility);
+ if (newSet.has(address)) {
+ newSet.delete(address);
+ } else {
+ newSet.add(address);
+ }
+ setLocalSubscriptionVisibility(newSet);
+ };
+
+ const handleSave = () => {
+ // Parse directory input and apply changes
+ const inputDirectories = stringToDirectories(localDirectoryInput);
+ const allCodes = getAllBoardCodes();
+
+ // Hide all directories first, then show only the ones in the input
+ allCodes.forEach((code) => {
+ setDirectoryVisibility(code, inputDirectories.has(code));
+ });
+
+ // Apply subscription visibility changes
+ subscriptions.forEach((address: string) => {
+ setSubscriptionVisibility(address, localSubscriptionVisibility.has(address));
+ });
+
+ closeTopbarEditModal();
+ };
+
+ const handleCancel = () => {
+ // Reset local state to match store
+ setLocalDirectoryInput(directoriesToString(visibleDirectories));
+ setLocalSubscriptionVisibility(new Set(visibleSubscriptions));
+ closeTopbarEditModal();
+ };
+
+ return (
+
+
+
+
Custom Board List
+
+
+
+
+
Directory Boards
+
Enter directory codes separated by spaces (e.g., "jp tg mu"):
+
setLocalDirectoryInput(e.target.value)}
+ />
+
+
+ {subscriptions.length > 0 && (
+
+
Subscriptions
+
Select which subscriptions to show in the topbar:
+
+ {subscriptions.map((address: string) => {
+ const boardPath = getBoardPath(address, defaultSubplebbits);
+ const displayText = address.endsWith('.eth') || address.endsWith('.sol') ? address : Plebbit.getShortAddress(address);
+ const isChecked = localSubscriptionVisibility.has(address);
+ return (
+
+ handleSubscriptionToggle(address)} />
+
+
+ );
+ })}
+
+
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+export default TopbarEditModal;
diff --git a/src/components/topbar-edit-modal/topbar-edit-modal.types.ts b/src/components/topbar-edit-modal/topbar-edit-modal.types.ts
new file mode 100644
index 00000000..f215e160
--- /dev/null
+++ b/src/components/topbar-edit-modal/topbar-edit-modal.types.ts
@@ -0,0 +1,13 @@
+// Types for topbar edit modal component
+
+export interface BoardCodeCheckbox {
+ code: string;
+ label: string;
+ checked: boolean;
+}
+
+export interface SubscriptionCheckbox {
+ address: string;
+ displayName: string;
+ checked: boolean;
+}
diff --git a/src/components/topbar/topbar.module.css b/src/components/topbar/topbar.module.css
index d989114d..0990c96f 100644
--- a/src/components/topbar/topbar.module.css
+++ b/src/components/topbar/topbar.module.css
@@ -22,6 +22,11 @@
cursor: pointer;
}
+.placeholder {
+ text-decoration: line-through;
+ color: var(--topbar-desktop-link-text-color);
+}
+
.navTopRight {
float: right;
text-transform: capitalize;
diff --git a/src/components/topbar/topbar.tsx b/src/components/topbar/topbar.tsx
index dbd488f7..c9a3d746 100644
--- a/src/components/topbar/topbar.tsx
+++ b/src/components/topbar/topbar.tsx
@@ -4,11 +4,15 @@ import { useTranslation } from 'react-i18next';
import Plebbit from '@plebbit/plebbit-js';
import { useAccount, useAccountComment, useAccountSubplebbits } from '@plebbit/plebbit-react-hooks';
import { isAllView, isCatalogView, isSubscriptionsView } from '../../lib/utils/view-utils';
-import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
+import { useDefaultSubplebbitAddresses, useDefaultSubplebbits, MultisubSubplebbit } from '../../hooks/use-default-subplebbits';
import { useBoardPath, useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address';
-import { getBoardPath } from '../../lib/utils/route-utils';
+import { getBoardPath, extractDirectoryFromTitle } from '../../lib/utils/route-utils';
import { TimeFilter } from '../board-buttons';
import useCreateBoardModalStore from '../../stores/use-create-board-modal-store';
+import useTopbarEditModalStore from '../../stores/use-topbar-edit-modal-store';
+import useTopbarVisibilityStore from '../../stores/use-topbar-visibility-store';
+import useDirectoryModalStore from '../../stores/use-directory-modal-store';
+import { BOARD_CODE_GROUPS } from '../../constants/board-codes';
import styles from './topbar.module.css';
import _, { debounce } from 'lodash';
@@ -71,6 +75,16 @@ const SearchBar = ({ setShowSearchBar }: { setShowSearchBar: (show: boolean) =>
);
};
+// Helper function to find board address by directory code
+const findBoardAddressByCode = (code: string, defaultSubplebbits: MultisubSubplebbit[]): string | null => {
+ const entry = defaultSubplebbits.find((subplebbit) => {
+ if (!subplebbit.title) return false;
+ const directory = extractDirectoryFromTitle(subplebbit.title);
+ return directory === code;
+ });
+ return entry?.address || null;
+};
+
const TopBarDesktop = () => {
const { t } = useTranslation();
const account = useAccount();
@@ -79,13 +93,72 @@ const TopBarDesktop = () => {
const isInCatalogView = isCatalogView(location.pathname, params);
const [showSearchBar, setShowSearchBar] = useState(false);
const { openCreateBoardModal } = useCreateBoardModalStore();
+ const { openTopbarEditModal } = useTopbarEditModalStore();
+ const { openDirectoryModal } = useDirectoryModalStore();
+ const { visibleDirectories, visibleSubscriptions } = useTopbarVisibilityStore();
const defaultSubplebbits = useDefaultSubplebbits();
- const subscriptions = account?.subscriptions;
-
+ const subscriptions = account?.subscriptions || [];
const { accountSubplebbits } = useAccountSubplebbits();
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
+ // Filter subscriptions to only show visible ones
+ const visibleSubscriptionAddresses = subscriptions.filter((address: string) => visibleSubscriptions.has(address));
+
+ // Initialize visibility store on mount
+ useEffect(() => {
+ useTopbarVisibilityStore.getState().initialize();
+ }, []);
+
+ // Render a board code link or placeholder
+ const renderBoardCode = (code: string, isLastInGroup: boolean) => {
+ const address = findBoardAddressByCode(code, defaultSubplebbits);
+ const isPlaceholder = !address;
+
+ const handleClick = (e: React.MouseEvent) => {
+ // If no address exists, prevent navigation and open directory modal
+ if (!address) {
+ e.preventDefault();
+ e.stopPropagation();
+ openDirectoryModal();
+ }
+ };
+
+ const linkContent = (
+ <>
+ {isPlaceholder ? (
+
+ {code}
+
+ ) : (
+
+ {code}
+
+ )}
+ >
+ );
+
+ return (
+
+ {linkContent}
+ {!isLastInGroup && ' / '}
+
+ );
+ };
+
+ // Render a subscription link
+ const renderSubscription = (address: string, index: number, total: number) => {
+ const boardPath = getBoardPath(address, defaultSubplebbits);
+ const displayText = address.endsWith('.eth') || address.endsWith('.sol') ? address : Plebbit.getShortAddress(address);
+
+ return (
+
+ {boardPath && boardPath.trim() ? {displayText} : {displayText}}
+ {index !== total - 1 && ' / '}
+
+ );
+ };
+
return (
@@ -97,24 +170,20 @@ const TopBarDesktop = () => {
>
)}
]{' '}
- {subscriptions?.length > 0 && (
- <>
- [
- {subscriptions.map((address: any, index: any) => {
- const boardPath = getBoardPath(address, defaultSubplebbits);
- const displayText = address.endsWith('.eth') || address.endsWith('.sol') ? address : address.slice(0, 10).concat('...');
- return (
-
- {index === 0 ? null : ' '}
- {boardPath && boardPath.trim() ? {displayText} : {displayText}}
- {index !== subscriptions?.length - 1 ? ' /' : null}
-
- );
- })}
- ]{' '}
- >
+ {BOARD_CODE_GROUPS.map((group, groupIndex) => {
+ const visibleCodes = group.filter((code) => visibleDirectories.has(code));
+ if (visibleCodes.length === 0) return null;
+
+ return [{visibleCodes.map((code, codeIndex) => renderBoardCode(code, codeIndex === visibleCodes.length - 1))}] ;
+ })}
+ {visibleSubscriptionAddresses.length > 0 && (
+ <>[{visibleSubscriptionAddresses.map((address: string, index: number) => renderSubscription(address, index, visibleSubscriptionAddresses.length))}] >
)}
[
+ openTopbarEditModal()} style={{ cursor: 'pointer' }}>
+ {_.capitalize(t('edit'))}
+
+ ] [
openCreateBoardModal()} style={{ cursor: 'pointer' }}>
{t('create_board')}
diff --git a/src/constants/board-codes.ts b/src/constants/board-codes.ts
new file mode 100644
index 00000000..64b18b1d
--- /dev/null
+++ b/src/constants/board-codes.ts
@@ -0,0 +1,147 @@
+/**
+ * Mapping of 4chan board codes to 5chan directory names
+ * Used for displaying board links in the topbar
+ */
+export const BOARD_CODE_TO_DIRECTORY: Record = {
+ a: 'Anime & Manga',
+ b: 'Random',
+ c: 'Anime/Cute',
+ d: 'Mecha',
+ e: 'Ecchi',
+ f: 'Flash',
+ g: 'Technology',
+ gif: 'Adult GIF',
+ h: 'Hentai',
+ hr: 'High Resolution',
+ k: 'Traditional Games',
+ m: 'Mecha',
+ o: 'Auto',
+ p: 'Photography',
+ r: 'Request',
+ s: 'Sexy Beautiful Women',
+ t: 'Torrents',
+ u: 'Yuri',
+ v: 'Video Games',
+ vg: 'Video Game Generals',
+ vm: 'Video Games/Multiplayer',
+ vmg: 'Video Games/Mobile',
+ vr: 'Retro Games',
+ vrpg: 'Video Games/RPG',
+ vst: 'Video Games/Strategy',
+ w: 'Anime/Wallpapers',
+ wg: 'Worksafe GIF',
+ i: 'Oekaki',
+ ic: 'Artwork/Critique',
+ r9k: 'ROBOT9001',
+ s4s: 'Shit 4chan Says',
+ vip: 'Very Important Posts',
+ cm: 'Cute/Male',
+ hm: 'Handsome Men',
+ lgbt: 'LGBT',
+ y: 'Yaoi',
+ '3': '3DCG',
+ aco: 'Anime/Cute',
+ adv: 'Advertisement',
+ an: 'Animals & Nature',
+ bant: 'International/Random',
+ biz: 'Business & Finance',
+ cgl: 'Cosplay & EGL',
+ ck: 'Food & Cooking',
+ co: 'Comics & Cartoons',
+ diy: 'Do-It-Yourself',
+ fa: 'Fashion',
+ fit: 'Fitness',
+ gd: 'Graphic Design',
+ hc: 'Hardcore',
+ his: 'History & Humanities',
+ int: 'International',
+ jp: 'Otaku Culture',
+ lit: 'Literature',
+ mlp: 'Pony',
+ mu: 'Music',
+ n: 'Transportation',
+ news: 'Current News',
+ out: 'Outdoors',
+ po: 'Papercraft & Origami',
+ pol: 'Politically Incorrect',
+ pw: 'Professional Wrestling',
+ qst: 'Quests',
+ sci: 'Science & Math',
+ soc: 'Cams & Meetups',
+ sp: 'Sports',
+ tg: 'Traditional Games',
+ toy: 'Toys',
+ trv: 'Travel',
+ tv: 'Television & Film',
+ vp: 'Pokémon',
+ vt: 'Virtual YouTubers',
+ wsg: 'Worksafe Requests',
+ wsr: 'Worksafe Requests',
+ x: 'Adult Cartoons',
+ xs: 'Extreme Sports',
+};
+
+/**
+ * Board code groups matching 4chan's bracket structure
+ * Each group represents boards displayed together in brackets
+ */
+export const BOARD_CODE_GROUPS: string[][] = [
+ // Group 1: [a / b / c / d / e / f / g / gif / h / hr / k / m / o / p / r / s / t / u / v / vg / vm / vmg / vr / vrpg / vst / w / wg]
+ ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'gif', 'h', 'hr', 'k', 'm', 'o', 'p', 'r', 's', 't', 'u', 'v', 'vg', 'vm', 'vmg', 'vr', 'vrpg', 'vst', 'w', 'wg'],
+ // Group 2: [i / ic]
+ ['i', 'ic'],
+ // Group 3: [r9k / s4s / vip]
+ ['r9k', 's4s', 'vip'],
+ // Group 4: [cm / hm / lgbt / y]
+ ['cm', 'hm', 'lgbt', 'y'],
+ // Group 5: [3 / aco / adv / an / bant / biz / cgl / ck / co / diy / fa / fit / gd / hc / his / int / jp / lit / mlp / mu / n / news / out / po / pol / pw / qst / sci / soc / sp / tg / toy / trv / tv / vp / vt / wsg / wsr / x / xs]
+ [
+ '3',
+ 'aco',
+ 'adv',
+ 'an',
+ 'bant',
+ 'biz',
+ 'cgl',
+ 'ck',
+ 'co',
+ 'diy',
+ 'fa',
+ 'fit',
+ 'gd',
+ 'hc',
+ 'his',
+ 'int',
+ 'jp',
+ 'lit',
+ 'mlp',
+ 'mu',
+ 'n',
+ 'news',
+ 'out',
+ 'po',
+ 'pol',
+ 'pw',
+ 'qst',
+ 'sci',
+ 'soc',
+ 'sp',
+ 'tg',
+ 'toy',
+ 'trv',
+ 'tv',
+ 'vp',
+ 'vt',
+ 'wsg',
+ 'wsr',
+ 'x',
+ 'xs',
+ ],
+];
+
+/**
+ * Get all board codes as a flat array
+ */
+export const getAllBoardCodes = (): string[] => {
+ return BOARD_CODE_GROUPS.flat();
+};
diff --git a/src/stores/use-disclaimer-modal-store.ts b/src/stores/use-disclaimer-modal-store.ts
index cc0be158..3a63e61e 100644
--- a/src/stores/use-disclaimer-modal-store.ts
+++ b/src/stores/use-disclaimer-modal-store.ts
@@ -33,12 +33,17 @@ const useDisclaimerModalStore = create((set) => ({
targetAddress: null,
targetBoardPath: null,
- showDisclaimerModal: (address: string, navigate: NavigateFunction, boardPath?: string) => {
+ showDisclaimerModal: (address: string, navigate: NavigateFunction, boardPath?: string | null) => {
// Check if user has already accepted the disclaimer
if (hasAcceptedDisclaimer()) {
- // Navigate directly without showing modal
- const path = boardPath || address;
- navigate(`/${path}`);
+ // If there's a valid board path, navigate directly
+ if (boardPath) {
+ navigate(`/${boardPath}`);
+ return;
+ }
+ // If no board path (placeholder), don't navigate - we'll handle this separately
+ // Don't show disclaimer modal either since it's already accepted
+ // Caller should handle showing directory modal or other UI
return;
}
diff --git a/src/stores/use-topbar-edit-modal-store.ts b/src/stores/use-topbar-edit-modal-store.ts
new file mode 100644
index 00000000..3e2e6272
--- /dev/null
+++ b/src/stores/use-topbar-edit-modal-store.ts
@@ -0,0 +1,25 @@
+import { create } from 'zustand';
+
+interface TopbarEditModalState {
+ showModal: boolean;
+ openTopbarEditModal: () => void;
+ closeTopbarEditModal: () => void;
+}
+
+const useTopbarEditModalStore = create((set) => ({
+ showModal: false,
+
+ openTopbarEditModal: () => {
+ set({
+ showModal: true,
+ });
+ },
+
+ closeTopbarEditModal: () => {
+ set({
+ showModal: false,
+ });
+ },
+}));
+
+export default useTopbarEditModalStore;
diff --git a/src/stores/use-topbar-visibility-store.ts b/src/stores/use-topbar-visibility-store.ts
new file mode 100644
index 00000000..5feb1a7c
--- /dev/null
+++ b/src/stores/use-topbar-visibility-store.ts
@@ -0,0 +1,117 @@
+import { create } from 'zustand';
+import { getAllBoardCodes } from '../constants/board-codes';
+
+const LOCALSTORAGE_KEY_DIRECTORIES = '5chan-topbar-directories-visible';
+const LOCALSTORAGE_KEY_SUBSCRIPTIONS = '5chan-topbar-subscriptions-visible';
+
+interface TopbarVisibilityState {
+ // Directory codes that are visible (all visible by default)
+ visibleDirectories: Set;
+ // Subscription addresses that are visible in topbar (all hidden by default)
+ visibleSubscriptions: Set;
+ // Actions
+ toggleDirectory: (code: string) => void;
+ toggleSubscription: (address: string) => void;
+ setDirectoryVisibility: (code: string, visible: boolean) => void;
+ setSubscriptionVisibility: (address: string, visible: boolean) => void;
+ // Initialize from localStorage
+ initialize: () => void;
+}
+
+const loadFromLocalStorage = (key: string, defaultValue: Set): Set => {
+ try {
+ const stored = localStorage.getItem(key);
+ if (stored) {
+ const array = JSON.parse(stored);
+ return new Set(array);
+ }
+ } catch (e) {
+ console.warn(`Failed to load ${key} from localStorage:`, e);
+ }
+ return defaultValue;
+};
+
+const saveToLocalStorage = (key: string, set: Set) => {
+ try {
+ const array = Array.from(set);
+ localStorage.setItem(key, JSON.stringify(array));
+ } catch (e) {
+ console.warn(`Failed to save ${key} to localStorage:`, e);
+ }
+};
+
+const useTopbarVisibilityStore = create((set, get) => {
+ // Initialize with all directories visible by default
+ const allBoardCodes = getAllBoardCodes();
+ const defaultVisibleDirectories = new Set(allBoardCodes);
+ const defaultVisibleSubscriptions = new Set();
+
+ return {
+ visibleDirectories: loadFromLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, defaultVisibleDirectories),
+ visibleSubscriptions: loadFromLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, defaultVisibleSubscriptions),
+
+ toggleDirectory: (code: string) => {
+ set((state) => {
+ const newSet = new Set(state.visibleDirectories);
+ if (newSet.has(code)) {
+ newSet.delete(code);
+ } else {
+ newSet.add(code);
+ }
+ saveToLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, newSet);
+ return { visibleDirectories: newSet };
+ });
+ },
+
+ toggleSubscription: (address: string) => {
+ set((state) => {
+ const newSet = new Set(state.visibleSubscriptions);
+ if (newSet.has(address)) {
+ newSet.delete(address);
+ } else {
+ newSet.add(address);
+ }
+ saveToLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, newSet);
+ return { visibleSubscriptions: newSet };
+ });
+ },
+
+ setDirectoryVisibility: (code: string, visible: boolean) => {
+ set((state) => {
+ const newSet = new Set(state.visibleDirectories);
+ if (visible) {
+ newSet.add(code);
+ } else {
+ newSet.delete(code);
+ }
+ saveToLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, newSet);
+ return { visibleDirectories: newSet };
+ });
+ },
+
+ setSubscriptionVisibility: (address: string, visible: boolean) => {
+ set((state) => {
+ const newSet = new Set(state.visibleSubscriptions);
+ if (visible) {
+ newSet.add(address);
+ } else {
+ newSet.delete(address);
+ }
+ saveToLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, newSet);
+ return { visibleSubscriptions: newSet };
+ });
+ },
+
+ initialize: () => {
+ // Load from localStorage on initialization
+ const directories = loadFromLocalStorage(LOCALSTORAGE_KEY_DIRECTORIES, defaultVisibleDirectories);
+ const subscriptions = loadFromLocalStorage(LOCALSTORAGE_KEY_SUBSCRIPTIONS, defaultVisibleSubscriptions);
+ set({
+ visibleDirectories: directories,
+ visibleSubscriptions: subscriptions,
+ });
+ },
+ };
+});
+
+export default useTopbarVisibilityStore;
diff --git a/src/themes.css b/src/themes.css
index a9a20642..eb6b8394 100644
--- a/src/themes.css
+++ b/src/themes.css
@@ -72,7 +72,13 @@
--homepage-box-link-text-color-hover: #e00;
--homepage-box-link-text-decoration: none;
--homepage-box-link-text-decoration-hover: underline;
- --disclaimer-modal-close-button-background-image: url("/assets/buttons/icon-close-red.png");
+ --directory-modal-background-color: rgb(255, 255, 255);
+ --directory-modal-border: 1px solid rgb(136, 0, 0);
+ --directory-modal-text-color: rgb(136, 0, 0);
+ --directory-modal-header-background-color: rgb(136, 0, 0);
+ --directory-modal-header-text-color: rgb(255, 255, 255);
+ --directory-modal-close-button-background-image: url("/assets/buttons/cross-red.png");
+
/* footer */
--footer-item-background-color-desktop: #fed;
@@ -285,7 +291,12 @@
--homepage-box-link-text-color-hover: #d00;
--homepage-box-link-text-decoration: none;
--homepage-box-link-text-decoration-hover: underline;
- --disclaimer-modal-close-button-background-image: url("/assets/buttons/icon-close-red.png");
+ --directory-modal-background-color: #d6daf0;
+ --directory-modal-border: 1px solid #b7c5d9;
+ --directory-modal-text-color: #000;
+ --directory-modal-header-background-color: #98e;
+ --directory-modal-header-text-color: #000;
+ --directory-modal-close-button-background-image: url("/assets/buttons/cross-blue.png");
/* horizontal rule */
--hr-border: none;
@@ -485,6 +496,14 @@
--close-button-background-image: url("/assets/buttons/cross-red.png");
--open-help-button-background-image: url("/assets/buttons/help-red.png");
+ /* directory modal */
+ --directory-modal-background-color: #f0e0d6;
+ --directory-modal-border: 1px solid rgba(0, 0, 0, 0.20);
+ --directory-modal-text-color: #800;
+ --directory-modal-header-background-color: #ea8;
+ --directory-modal-header-text-color: #800;
+ --directory-modal-close-button-background-image: url("/assets/buttons/cross-red.png");
+
/* footer */
--footer-item-background-color-desktop: #fed;
@@ -675,7 +694,12 @@
--homepage-box-bar-text-color: #000;
--homepage-box-bar-background-color: #98e;
--homepage-box-bar-text-color-hover: #fff;
- --disclaimer-modal-close-button-background-image: url("/assets/buttons/icon-close-red.png");
+ --directory-modal-background-color: #d6daf0;
+ --directory-modal-border: 1px solid rgba(0, 0, 0, 0.20);
+ --directory-modal-text-color: #000;
+ --directory-modal-header-background-color: #98e;
+ --directory-modal-header-text-color: #000;
+ --directory-modal-close-button-background-image: url("/assets/buttons/cross-blue.png");
/* media */
--media-thumbnail-background-color: rgba(0, 0, 0, 0.05);
@@ -867,7 +891,12 @@
--homepage-box-border-color: rgb(45, 47, 51);
--homepage-box-bar-text-color: #c5c8c6;
--homepage-box-bar-text-color-hover: #c5c8c6;
- --disclaimer-modal-close-button-background-image: url("/assets/buttons/icon-close-red.png");
+ --directory-modal-background-color: #282a2e;
+ --directory-modal-border: 1px solid #111;
+ --directory-modal-text-color: #c5c8c6;
+ --directory-modal-header-background-color: #282a2e;
+ --directory-modal-header-text-color: #c5c8c6;
+ --directory-modal-close-button-background-image: url("/assets/buttons/cross-dark.png");
/* horizontal rule */
--hr-border: none;
@@ -1207,6 +1236,14 @@
--settings-modal-show-button-background-image: url("/assets/buttons/plus-photon.png");
--settings-modal-hide-button-background-image: url("/assets/buttons/minus-photon.png");
+ /* directory modal */
+ --directory-modal-background-color: #ddd;
+ --directory-modal-border: 1px solid #ccc;
+ --directory-modal-text-color: #333;
+ --directory-modal-header-background-color: #ddd;
+ --directory-modal-header-text-color: #333;
+ --directory-modal-close-button-background-image: url("/assets/buttons/cross-photon.png");
+
/* stats */
--stats-font-size: 11px;