Black mode
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
c24fc10c93
commit
067fc9e45d
44 changed files with 77 additions and 63 deletions
|
@ -293,7 +293,7 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
|||
data-testid='dropdown-menu'
|
||||
ref={refs.setFloating}
|
||||
className={
|
||||
clsx('z-[1001] w-56 rounded-md bg-white py-1 shadow-lg transition-opacity duration-100 focus:outline-none dark:bg-gray-900 dark:ring-2 dark:ring-primary-700', {
|
||||
clsx('z-[1001] w-56 rounded-md bg-white py-1 shadow-lg transition-opacity duration-100 focus:outline-none black:bg-black dark:bg-gray-900 dark:ring-2 dark:ring-primary-700', {
|
||||
'opacity-0 pointer-events-none': !isOpen,
|
||||
})
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
|||
<div
|
||||
ref={arrowRef}
|
||||
style={arrowProps}
|
||||
className='pointer-events-none absolute z-[-1] h-3 w-3 bg-white dark:bg-gray-900'
|
||||
className='pointer-events-none absolute z-[-1] h-3 w-3 bg-white black:bg-black dark:bg-gray-900'
|
||||
/>
|
||||
</div>
|
||||
</Portal>
|
||||
|
|
|
@ -51,7 +51,7 @@ const EventPreview: React.FC<IEventPreview> = ({ status, className, hideAction,
|
|||
));
|
||||
|
||||
return (
|
||||
<div className={clsx('relative w-full overflow-hidden rounded-lg bg-gray-100 dark:bg-primary-800', className)}>
|
||||
<div className={clsx('relative w-full overflow-hidden rounded-lg bg-gray-100 black:border black:border-gray-800 black:bg-black dark:bg-primary-800', className)}>
|
||||
<div className='absolute right-3 top-28'>
|
||||
{floatingAction && action}
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@ interface IGroupCard {
|
|||
const GroupCard: React.FC<IGroupCard> = ({ group }) => {
|
||||
return (
|
||||
<Stack
|
||||
className='relative h-[240px] rounded-lg border border-solid border-gray-300 bg-white dark:border-primary-800 dark:bg-primary-900'
|
||||
className='relative h-[240px] rounded-lg border border-solid border-gray-300 bg-white black:bg-white dark:border-primary-800 dark:bg-primary-900'
|
||||
data-testid='group-card'
|
||||
>
|
||||
{/* Group Cover Image */}
|
||||
|
|
|
@ -45,7 +45,7 @@ const GroupPopover = (props: IGroupPopoverContainer) => {
|
|||
content={
|
||||
<Stack space={4} className='w-80 pb-4'>
|
||||
<Stack
|
||||
className='relative h-60 rounded-lg bg-white dark:border-primary-800 dark:bg-primary-900'
|
||||
className='relative h-60 rounded-lg bg-white black:bg-white dark:border-primary-800 dark:bg-primary-900'
|
||||
data-testid='group-card'
|
||||
>
|
||||
{/* Group Cover Image */}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
|
||||
/** Fullscreen gradient used as a backdrop to public pages. */
|
||||
const LandingGradient: React.FC = () => (
|
||||
<div className='fixed h-screen w-full bg-gradient-to-tr from-primary-50 via-white to-gradient-end/10 dark:from-primary-900/50 dark:via-primary-900 dark:to-primary-800/50' />
|
||||
<div className='fixed h-screen w-full bg-gradient-to-tr from-primary-50 via-white to-gradient-end/10 black:hidden dark:from-primary-900/50 dark:via-primary-900 dark:to-primary-800/50' />
|
||||
);
|
||||
|
||||
export default LandingGradient;
|
||||
|
|
|
@ -232,7 +232,7 @@ const ModalRoot: React.FC<IModalRoot> = ({ children, onCancel, onClose, type })
|
|||
<div
|
||||
role='presentation'
|
||||
id='modal-overlay'
|
||||
className='fixed inset-0 bg-gray-500/90 backdrop-blur dark:bg-gray-700/90'
|
||||
className='fixed inset-0 bg-gray-500/90 backdrop-blur black:bg-gray-900/90 dark:bg-gray-700/90'
|
||||
onClick={handleOnClose}
|
||||
/>
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
}
|
||||
>
|
||||
<div
|
||||
className='fixed inset-0 bg-gray-500/90 dark:bg-gray-700/90'
|
||||
className='fixed inset-0 bg-gray-500/90 black:bg-gray-900/90 dark:bg-gray-700/90'
|
||||
role='button'
|
||||
onClick={handleClose}
|
||||
/>
|
||||
|
@ -153,7 +153,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
<div
|
||||
className={
|
||||
clsx({
|
||||
'flex flex-col flex-1 bg-white dark:bg-primary-900 -translate-x-full rtl:translate-x-full w-full max-w-xs': true,
|
||||
'flex flex-col flex-1 bg-white black:bg-black dark:bg-primary-900 -translate-x-full rtl:translate-x-full w-full max-w-xs': true,
|
||||
'!translate-x-0': sidebarOpen,
|
||||
})
|
||||
}
|
||||
|
@ -350,7 +350,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
</button>
|
||||
|
||||
{switcher && (
|
||||
<div className='border-t-2 border-solid border-gray-100 dark:border-gray-800'>
|
||||
<div className='border-t-2 border-solid border-gray-100 black:border-t dark:border-gray-800'>
|
||||
{otherAccounts.map(account => renderAccount(account))}
|
||||
|
||||
<NavLink className='flex items-center space-x-1 py-2' to='/login/add' onClick={handleClose}>
|
||||
|
|
|
@ -49,7 +49,7 @@ const SidebarNavigationLink = React.forwardRef((props: ISidebarNavigationLink, r
|
|||
count={count}
|
||||
countMax={countMax}
|
||||
className={clsx('h-5 w-5', {
|
||||
'text-gray-600 dark:text-gray-500 group-hover:text-primary-500 dark:group-hover:text-primary-400': !isActive,
|
||||
'text-gray-600 black:text-white dark:text-gray-500 group-hover:text-primary-500 dark:group-hover:text-primary-400': !isActive,
|
||||
'text-primary-500 dark:text-primary-400': isActive,
|
||||
})}
|
||||
/>
|
||||
|
|
|
@ -15,7 +15,7 @@ const SiteLogo: React.FC<ISiteLogo> = ({ className, theme, ...rest }) => {
|
|||
const { logo, logoDarkMode } = useSoapboxConfig();
|
||||
const { demo } = useSettings();
|
||||
|
||||
let darkMode = useTheme() === 'dark';
|
||||
let darkMode = ['dark', 'black'].includes(useTheme());
|
||||
if (theme === 'dark') darkMode = true;
|
||||
|
||||
/** Soapbox logo. */
|
||||
|
|
|
@ -232,7 +232,7 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
placeholderCount={20}
|
||||
ref={node}
|
||||
className={clsx('divide-y divide-solid divide-gray-200 dark:divide-gray-800', {
|
||||
'divide-none': divideType !== 'border',
|
||||
'divide-none black:divide-solid': divideType !== 'border',
|
||||
})}
|
||||
itemClassName={clsx({
|
||||
'pb-3': divideType !== 'border',
|
||||
|
|
|
@ -35,7 +35,7 @@ const ThumbNavigationLink: React.FC<IThumbNavigationLink> = ({ count, countMax,
|
|||
src={src}
|
||||
className={clsx({
|
||||
'h-5 w-5': true,
|
||||
'text-gray-600': !active,
|
||||
'text-gray-600 black:text-white': !active,
|
||||
'text-primary-500': active,
|
||||
})}
|
||||
count={count}
|
||||
|
@ -46,7 +46,7 @@ const ThumbNavigationLink: React.FC<IThumbNavigationLink> = ({ count, countMax,
|
|||
src={src}
|
||||
className={clsx({
|
||||
'h-5 w-5': true,
|
||||
'text-gray-600': !active,
|
||||
'text-gray-600 black:text-white': !active,
|
||||
'text-primary-500': active,
|
||||
})}
|
||||
/>
|
||||
|
|
|
@ -38,9 +38,10 @@ const Card = React.forwardRef<HTMLDivElement, ICard>(({ children, variant = 'def
|
|||
ref={ref}
|
||||
{...filteredProps}
|
||||
className={clsx({
|
||||
'bg-white dark:bg-primary-900 text-gray-900 dark:text-gray-100 shadow-lg dark:shadow-none': variant === 'rounded',
|
||||
'bg-white dark:bg-primary-900 black:bg-black text-gray-900 dark:text-gray-100 shadow-lg dark:shadow-none': variant === 'rounded',
|
||||
[sizes[size]]: variant === 'rounded',
|
||||
'py-4': variant === 'slim',
|
||||
'black:rounded-none': size !== 'xl',
|
||||
}, className)}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -102,8 +102,8 @@ const Column: React.FC<IColumn> = React.forwardRef((props, ref: React.ForwardedR
|
|||
backHref={backHref}
|
||||
className={clsx({
|
||||
'rounded-t-3xl': !isScrolled && !transparent,
|
||||
'sticky top-12 z-10 bg-white/90 dark:bg-primary-900/90 backdrop-blur lg:top-16': !transparent,
|
||||
'p-4 sm:p-0 sm:pb-4': transparent,
|
||||
'sticky top-12 z-10 bg-white/90 dark:bg-primary-900/90 black:bg-black/90 backdrop-blur lg:top-16': !transparent,
|
||||
'p-4 sm:p-0 sm:pb-4 black:p-4': transparent,
|
||||
'-mt-4 -mx-4 p-4': size !== 'lg' && !transparent,
|
||||
'-mt-4 -mx-4 p-4 sm:-mt-6 sm:-mx-6 sm:p-6': size === 'lg' && !transparent,
|
||||
})}
|
||||
|
|
|
@ -13,7 +13,7 @@ interface IDivider {
|
|||
const Divider = ({ text, textSize = 'md' }: IDivider) => (
|
||||
<div className='relative' data-testid='divider'>
|
||||
<div className='absolute inset-0 flex items-center' aria-hidden='true'>
|
||||
<div className='w-full border-t-2 border-solid border-gray-100 dark:border-gray-800' />
|
||||
<div className='w-full border-t-2 border-solid border-gray-100 black:border-t dark:border-gray-800' />
|
||||
</div>
|
||||
|
||||
{text && (
|
||||
|
|
|
@ -21,7 +21,7 @@ interface LayoutComponent extends React.FC<ILayout> {
|
|||
|
||||
/** Layout container, to hold Sidebar, Main, and Aside. */
|
||||
const Layout: LayoutComponent = ({ children }) => (
|
||||
<div className='relative sm:pt-4'>
|
||||
<div className='relative grow black:pt-0 sm:pt-4'>
|
||||
<div className='mx-auto max-w-3xl sm:px-6 md:grid md:max-w-7xl md:grid-cols-12 md:gap-8 md:px-8'>
|
||||
{children}
|
||||
</div>
|
||||
|
@ -41,7 +41,7 @@ const Sidebar: React.FC<ISidebar> = ({ children }) => (
|
|||
const Main: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ children, className }) => (
|
||||
<main
|
||||
className={clsx({
|
||||
'md:col-span-12 lg:col-span-9 xl:col-span-6 pb-36': true,
|
||||
'md:col-span-12 lg:col-span-9 xl:col-span-6 pb-36 black:border-gray-800 lg:black:border-l xl:black:border-r': true,
|
||||
}, className)}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -28,7 +28,7 @@ const MenuList: React.FC<IMenuList> = (props) => {
|
|||
<MenuItems
|
||||
onKeyDown={(event) => event.nativeEvent.stopImmediatePropagation()}
|
||||
className={
|
||||
clsx(className, 'shadow-menu rounded-lg bg-white py-1 dark:bg-primary-900')
|
||||
clsx(className, 'shadow-menu rounded-lg bg-white py-1 black:bg-black dark:bg-primary-900')
|
||||
}
|
||||
{...filteredProps}
|
||||
/>
|
||||
|
@ -37,6 +37,6 @@ const MenuList: React.FC<IMenuList> = (props) => {
|
|||
};
|
||||
|
||||
/** Divides menu items. */
|
||||
const MenuDivider = () => <hr className='mx-2 my-1 border-t-2 border-gray-100 dark:border-gray-800' />;
|
||||
const MenuDivider = () => <hr className='mx-2 my-1 border-t-2 border-gray-100 black:border-t dark:border-gray-800' />;
|
||||
|
||||
export { Menu, MenuButton, MenuDivider, MenuItems, MenuItem, MenuList, MenuLink };
|
||||
|
|
|
@ -95,7 +95,7 @@ const Modal = React.forwardRef<HTMLDivElement, IModal>(({
|
|||
<div
|
||||
ref={ref}
|
||||
data-testid='modal'
|
||||
className={clsx(className, 'pointer-events-auto mx-auto block w-full rounded-2xl bg-white p-6 text-start align-middle text-gray-900 shadow-xl transition-all dark:bg-primary-900 dark:text-gray-100', widths[width])}
|
||||
className={clsx(className, 'pointer-events-auto mx-auto block w-full rounded-2xl bg-white p-6 text-start align-middle text-gray-900 shadow-xl transition-all black:bg-black dark:bg-primary-900 dark:text-gray-100', widths[width])}
|
||||
>
|
||||
<div className='w-full justify-between sm:flex sm:items-start'>
|
||||
<div className='w-full'>
|
||||
|
|
|
@ -106,7 +106,7 @@ const Toast = (props: IToast) => {
|
|||
data-testid='toast'
|
||||
className={
|
||||
clsx({
|
||||
'p-4 pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white dark:bg-gray-900 shadow-lg dark:ring-2 dark:ring-gray-800': true,
|
||||
'p-4 pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white black:bg-black dark:bg-gray-900 shadow-lg dark:ring-2 dark:ring-gray-800': true,
|
||||
'animate-enter': t.visible,
|
||||
'animate-leave': !t.visible,
|
||||
})
|
||||
|
|
|
@ -110,7 +110,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
return (
|
||||
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6'>
|
||||
<div>
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
<div className='relative h-32 w-full bg-gray-200 black:rounded-t-none md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
</div>
|
||||
|
||||
<div className='px-4 sm:px-6'>
|
||||
|
@ -620,7 +620,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
)}
|
||||
|
||||
<div>
|
||||
<div className='relative isolate flex h-32 w-full flex-col justify-center overflow-hidden bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50'>
|
||||
<div className='relative isolate flex h-32 w-full flex-col justify-center overflow-hidden bg-gray-200 black:rounded-t-none md:rounded-t-xl lg:h-48 dark:bg-gray-900/50'>
|
||||
{renderHeader()}
|
||||
|
||||
<div className='absolute left-2 top-2'>
|
||||
|
|
|
@ -60,15 +60,15 @@ const ChatPage: React.FC<IChatPage> = ({ chatId }) => {
|
|||
<div
|
||||
ref={containerRef}
|
||||
style={{ height }}
|
||||
className='h-screen overflow-hidden bg-white text-gray-900 shadow-lg sm:rounded-t-xl dark:bg-primary-900 dark:text-gray-100 dark:shadow-none'
|
||||
className='h-screen overflow-hidden bg-white text-gray-900 shadow-lg black:bg-transparent sm:rounded-t-xl dark:bg-primary-900 dark:text-gray-100 dark:shadow-none'
|
||||
>
|
||||
{isOnboarded ? (
|
||||
<div
|
||||
className='grid h-full grid-cols-9 overflow-hidden dark:divide-x-2 dark:divide-solid dark:divide-gray-800'
|
||||
className='grid h-full grid-cols-9 overflow-hidden black:divide-x dark:divide-x-2 dark:divide-solid dark:divide-gray-800'
|
||||
data-testid='chat-page'
|
||||
>
|
||||
<Stack
|
||||
className={clsx('dark:inset col-span-9 overflow-hidden bg-gradient-to-r from-white to-gray-100 sm:col-span-3 dark:bg-gray-900 dark:bg-none', {
|
||||
className={clsx('dark:inset col-span-9 overflow-hidden bg-gradient-to-r from-white to-gray-100 black:bg-black sm:col-span-3 dark:bg-gray-900 dark:bg-none', {
|
||||
'hidden sm:block': isSidebarHidden,
|
||||
})}
|
||||
>
|
||||
|
|
|
@ -12,7 +12,7 @@ interface IPane {
|
|||
const Pane: React.FC<IPane> = ({ isOpen = false, children }) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx('fixed bottom-0 z-[99] flex w-96 flex-col rounded-t-lg bg-white shadow-3xl ltr:right-5 rtl:left-5 dark:bg-gray-900', {
|
||||
className={clsx('fixed bottom-0 z-[99] flex w-96 flex-col rounded-t-lg bg-white shadow-3xl black:border black:border-b-0 black:border-gray-800 black:bg-black ltr:right-5 rtl:left-5 dark:bg-gray-900', {
|
||||
'h-[550px] max-h-[100vh]': isOpen,
|
||||
'h-16': !isOpen,
|
||||
})}
|
||||
|
|
|
@ -80,7 +80,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
|||
return (
|
||||
<>
|
||||
<div className='-mx-4 -mt-4'>
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
<div className='relative h-32 w-full bg-gray-200 black:rounded-t-none md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
</div>
|
||||
|
||||
<PlaceholderEventHeader />
|
||||
|
@ -363,13 +363,13 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
|||
return (
|
||||
<>
|
||||
<div className='-mx-4 -mt-4'>
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50'>
|
||||
<div className='relative h-32 w-full bg-gray-200 black:rounded-t-none md:rounded-t-xl lg:h-48 dark:bg-gray-900/50'>
|
||||
{banner && (
|
||||
<a href={banner.url} onClick={handleHeaderClick} target='_blank'>
|
||||
<StillImage
|
||||
src={banner.url}
|
||||
alt={intl.formatMessage(messages.bannerHeader)}
|
||||
className='absolute inset-0 h-full object-cover md:rounded-t-xl'
|
||||
className='absolute inset-0 h-full object-cover black:rounded-t-none md:rounded-t-xl'
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
|
|
|
@ -36,7 +36,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
|||
return (
|
||||
<div className='-mx-4 -mt-4 sm:-mx-6 sm:-mt-6' data-testid='group-header-missing'>
|
||||
<div>
|
||||
<div className='relative h-32 w-full bg-gray-200 md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
<div className='relative h-32 w-full bg-gray-200 black:rounded-t-none md:rounded-t-xl lg:h-48 dark:bg-gray-900/50' />
|
||||
</div>
|
||||
|
||||
<div className='px-4 sm:px-6'>
|
||||
|
|
|
@ -9,7 +9,7 @@ const PlaceholderEventPreview = () => {
|
|||
const nameLength = randomIntFromInterval(5, 15);
|
||||
|
||||
return (
|
||||
<div className='relative w-full animate-pulse overflow-hidden rounded-lg bg-gray-100 text-primary-50 dark:bg-primary-800 dark:text-primary-800'>
|
||||
<div className='relative w-full animate-pulse overflow-hidden rounded-lg bg-gray-100 text-primary-50 black:border black:border-gray-800 black:bg-black dark:bg-primary-800 dark:text-primary-800'>
|
||||
<div className='h-40 bg-primary-200 dark:bg-gray-600' />
|
||||
<Stack className='p-2.5' space={2}>
|
||||
<Text weight='semibold'>{generateText(eventNameLength)}</Text>
|
||||
|
|
|
@ -9,7 +9,7 @@ const PlaceholderGroupCard = () => {
|
|||
|
||||
return (
|
||||
<div className='animate-pulse'>
|
||||
<Stack className='relative h-[240px] rounded-lg border border-solid border-gray-300 bg-white dark:border-primary-800 dark:bg-primary-900'>
|
||||
<Stack className='relative h-[240px] rounded-lg border border-solid border-gray-300 bg-white black:bg-white dark:border-primary-800 dark:bg-primary-900'>
|
||||
{/* Group Cover Image */}
|
||||
<div className='relative grow basis-1/2 rounded-t-lg bg-gray-300 dark:bg-gray-800' />
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import PlaceholderStatusContent from './placeholder-status-content';
|
|||
|
||||
/** Fake notification to display while data is loading. */
|
||||
const PlaceholderNotification = () => (
|
||||
<div className='bg-white px-4 py-6 sm:p-6 dark:bg-primary-900'>
|
||||
<div className='bg-white px-4 py-6 black:bg-black sm:p-6 dark:bg-primary-900'>
|
||||
<div className='w-full animate-pulse'>
|
||||
<div className='mb-2'>
|
||||
<PlaceholderStatusContent minLength={20} maxLength={20} />
|
||||
|
|
|
@ -15,7 +15,7 @@ interface IPlaceholderStatus {
|
|||
const PlaceholderStatus: React.FC<IPlaceholderStatus> = ({ variant }) => (
|
||||
<div
|
||||
className={clsx({
|
||||
'status-placeholder bg-white dark:bg-primary-900': true,
|
||||
'status-placeholder bg-white black:bg-black dark:bg-primary-900': true,
|
||||
'shadow-xl dark:shadow-none sm:rounded-xl px-4 py-6 sm:p-5': variant === 'rounded',
|
||||
'py-4': variant === 'slim',
|
||||
})}
|
||||
|
|
|
@ -57,7 +57,7 @@ const CommunityTimeline = () => {
|
|||
<Column className='-mt-3 sm:mt-0' label={intl.formatMessage(messages.title)} transparent>
|
||||
<PinnedHostsPicker />
|
||||
|
||||
{showExplanationBox && <div className='mb-4'>
|
||||
{showExplanationBox && <div className='mb-4 black:mx-4'>
|
||||
<Accordion
|
||||
headline={<FormattedMessage id='fediverse_tab.explanation_box.title' defaultMessage='What is the Fediverse?' />}
|
||||
action={dismissExplanationBox}
|
||||
|
|
|
@ -22,7 +22,7 @@ const SitePreview: React.FC<ISitePreview> = ({ soapbox }) => {
|
|||
const userTheme = settings.get('themeMode');
|
||||
const systemTheme = useSystemTheme();
|
||||
|
||||
const dark = userTheme === 'dark' || (userTheme === 'system' && systemTheme === 'dark');
|
||||
const dark = ['dark', 'black'].includes(userTheme as string) || (userTheme === 'system' && systemTheme === 'dark');
|
||||
|
||||
const bodyClass = clsx(
|
||||
'site-preview',
|
||||
|
|
|
@ -8,7 +8,7 @@ interface IBackgroundShapes {
|
|||
|
||||
/** Gradient that appears in the background of the UI. */
|
||||
const BackgroundShapes: React.FC<IBackgroundShapes> = ({ position = 'fixed' }) => (
|
||||
<div className={clsx(position, 'pointer-events-none inset-x-0 top-0 flex justify-center overflow-hidden')}>
|
||||
<div className={clsx(position, 'pointer-events-none inset-x-0 top-0 flex justify-center overflow-hidden black:hidden')}>
|
||||
<div className='bg-gradient-sm lg:bg-gradient-light lg:dark:bg-gradient-dark h-screen w-screen' />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -70,7 +70,7 @@ const Navbar = () => {
|
|||
if (mfaToken) return <Redirect to={`/login?token=${encodeURIComponent(mfaToken)}`} />;
|
||||
|
||||
return (
|
||||
<nav className='sticky top-0 z-50 bg-white shadow dark:bg-primary-900' ref={node} data-testid='navbar'>
|
||||
<nav className='sticky top-0 z-50 bg-white shadow black:border-b black:border-b-gray-800 black:bg-black dark:bg-primary-900' ref={node} data-testid='navbar'>
|
||||
<div className='mx-auto max-w-7xl px-2 sm:px-6 lg:px-8'>
|
||||
<div className='relative flex h-12 justify-between lg:h-16'>
|
||||
{account && (
|
||||
|
|
|
@ -124,7 +124,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
|
|||
{visible && (
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
className='z-[1003] mt-2 max-w-xs rounded-md bg-white shadow-lg focus:outline-none dark:bg-gray-900 dark:ring-2 dark:ring-primary-700'
|
||||
className='z-[1003] mt-2 max-w-xs rounded-md bg-white shadow-lg focus:outline-none black:bg-black dark:bg-gray-900 dark:ring-2 dark:ring-primary-700'
|
||||
style={{
|
||||
position: strategy,
|
||||
top: y ?? 0,
|
||||
|
|
|
@ -6,6 +6,7 @@ import { Icon, Select } from 'soapbox/components/ui';
|
|||
const messages = defineMessages({
|
||||
light: { id: 'theme_toggle.light', defaultMessage: 'Light' },
|
||||
dark: { id: 'theme_toggle.dark', defaultMessage: 'Dark' },
|
||||
black: { id: 'theme_toggle.black', defaultMessage: 'Pure Black' },
|
||||
system: { id: 'theme_toggle.system', defaultMessage: 'System' },
|
||||
});
|
||||
|
||||
|
@ -26,6 +27,8 @@ const ThemeSelector: React.FC<IThemeSelector> = ({ value, onChange }) => {
|
|||
return require('@tabler/icons/sun.svg');
|
||||
case 'dark':
|
||||
return require('@tabler/icons/moon.svg');
|
||||
case 'black':
|
||||
return require('@tabler/icons/moon-off.svg');
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -50,6 +53,7 @@ const ThemeSelector: React.FC<IThemeSelector> = ({ value, onChange }) => {
|
|||
<option value='system'>{intl.formatMessage(messages.system)}</option>
|
||||
<option value='light'>{intl.formatMessage(messages.light)}</option>
|
||||
<option value='dark'>{intl.formatMessage(messages.dark)}</option>
|
||||
<option value='black'>{intl.formatMessage(messages.black)}</option>
|
||||
</Select>
|
||||
|
||||
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3'>
|
||||
|
|
|
@ -480,7 +480,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
|
||||
<BackgroundShapes />
|
||||
|
||||
<div className='z-10 flex flex-col'>
|
||||
<div className='z-10 flex min-h-screen flex-col'>
|
||||
<Navbar />
|
||||
|
||||
<Layout>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useSettings } from './useSettings';
|
||||
import { useSystemTheme } from './useSystemTheme';
|
||||
|
||||
type Theme = 'light' | 'dark';
|
||||
type Theme = 'light' | 'dark' | 'black';
|
||||
|
||||
/**
|
||||
* Returns the actual theme being displayed (eg "light" or "dark")
|
||||
|
@ -11,8 +11,7 @@ const useTheme = (): Theme => {
|
|||
const { themeMode } = useSettings();
|
||||
const systemTheme = useSystemTheme();
|
||||
|
||||
const darkMode = themeMode === 'dark' || (themeMode === 'system' && systemTheme === 'dark');
|
||||
return darkMode ? 'dark' : 'light';
|
||||
return themeMode === 'system' ? systemTheme : themeMode;
|
||||
};
|
||||
|
||||
export { useTheme };
|
||||
|
|
|
@ -22,12 +22,12 @@ const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
|
|||
const { locale, direction } = useLocale();
|
||||
const { demo, reduceMotion, underlineLinks, demetricator } = useSettings();
|
||||
const soapboxConfig = useSoapboxConfig();
|
||||
const theme = useTheme();
|
||||
|
||||
const darkMode = useTheme() === 'dark';
|
||||
const themeCss = generateThemeCss(demo ? normalizeSoapboxConfig({ brandColor: '#0482d8' }) : soapboxConfig);
|
||||
const dsn = soapboxConfig.sentryDsn;
|
||||
|
||||
const bodyClass = clsx('h-full bg-white text-base dark:bg-gray-800', {
|
||||
const bodyClass = clsx('h-full bg-white text-base black:bg-black dark:bg-gray-800', {
|
||||
'no-reduce-motion': !reduceMotion,
|
||||
'underline-links': underlineLinks,
|
||||
'demetricator': demetricator,
|
||||
|
@ -42,10 +42,10 @@ const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
|
|||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<html lang={locale} className={clsx('h-full', { dark: darkMode })} />
|
||||
<html lang={locale} className={clsx('h-full', { 'dark': theme === 'dark', 'dark black': theme === 'black' })} />
|
||||
<body className={bodyClass} dir={direction} />
|
||||
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
||||
{darkMode && <style type='text/css'>{':root { color-scheme: dark; }'}</style>}
|
||||
{['dark', 'black'].includes(theme) && <style type='text/css'>{':root { color-scheme: dark; }'}</style>}
|
||||
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
||||
</Helmet>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ interface IChatsPage {
|
|||
/** Custom layout for chats on desktop. */
|
||||
const ChatsPage: React.FC<IChatsPage> = ({ children }) => {
|
||||
return (
|
||||
<div className='md:col-span-12 lg:col-span-9'>
|
||||
<div className='black:border-gray-800 md:col-span-12 lg:col-span-9 lg:black:border-l'>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -50,10 +50,10 @@ const HomePage: React.FC<IHomePage> = ({ children }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Layout.Main className='space-y-3 pt-3 sm:pt-0 dark:divide-gray-800'>
|
||||
<Layout.Main className='space-y-3 pt-3 black:space-y-0 sm:pt-0 dark:divide-gray-800'>
|
||||
{me && (
|
||||
<Card
|
||||
className={clsx('relative z-[1] transition', {
|
||||
className={clsx('relative z-[1] transition black:border-b black:border-gray-800', {
|
||||
'border-2 border-primary-600 border-dashed z-[99]': isDragging,
|
||||
'ring-2 ring-offset-2 ring-primary-600': isDraggedOver,
|
||||
})}
|
||||
|
|
|
@ -23,7 +23,7 @@ const settingsSchema = z.object({
|
|||
missingDescriptionModal: z.boolean().catch(false),
|
||||
defaultPrivacy: z.enum(['public', 'unlisted', 'private', 'direct']).catch('public'),
|
||||
defaultContentType: z.enum(['text/plain', 'text/markdown']).catch('text/plain'),
|
||||
themeMode: z.enum(['system', 'light', 'dark']).catch('system'),
|
||||
themeMode: z.enum(['system', 'light', 'dark', 'black']).catch('system'),
|
||||
locale: z.string().catch(navigator.language).pipe(z.enum(locales)).catch('en'),
|
||||
showExplanationBox: z.boolean().catch(true),
|
||||
explanationBox: z.boolean().catch(true),
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.react-datepicker {
|
||||
@apply dark:bg-gray-900 dark:border-gray-700 p-4 font-sans text-xs text-gray-900 dark:text-gray-300 border border-solid border-gray-200 rounded-lg;
|
||||
@apply black:bg-black dark:bg-gray-900 dark:border-gray-700 p-4 font-sans text-xs text-gray-900 dark:text-gray-300 border border-solid border-gray-200 rounded-lg;
|
||||
}
|
||||
|
||||
.react-datepicker__input-container > input {
|
||||
@apply dark:bg-gray-900 dark:text-gray-100 block w-full sm:text-sm border-gray-400 dark:border-gray-800 rounded-md focus:ring-primary-500 focus:border-primary-500;
|
||||
@apply black:bg-black dark:bg-gray-900 dark:text-gray-100 block w-full sm:text-sm border-gray-400 dark:border-gray-800 rounded-md focus:ring-primary-500 focus:border-primary-500;
|
||||
|
||||
&.has-error {
|
||||
@apply text-red-600 border-red-600;
|
||||
|
@ -24,7 +24,7 @@
|
|||
}
|
||||
|
||||
.react-datepicker__header {
|
||||
@apply bg-white dark:bg-gray-900 border-b-0 py-1 px-0;
|
||||
@apply bg-white black:bg-black dark:bg-gray-900 border-b-0 py-1 px-0;
|
||||
}
|
||||
|
||||
.react-datepicker__current-month,
|
||||
|
@ -85,11 +85,13 @@
|
|||
}
|
||||
|
||||
.react-datepicker__time {
|
||||
@apply dark:bg-gray-900;
|
||||
&-container & {
|
||||
@apply dark:bg-gray-900 black:bg-black;
|
||||
}
|
||||
}
|
||||
|
||||
.react-datepicker__time-container {
|
||||
@apply dark:border-gray-700;
|
||||
@apply dark:border-gray-700 black:border-gray-800;
|
||||
}
|
||||
|
||||
.react-datepicker__day-name,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.thread {
|
||||
@apply bg-white dark:bg-primary-900;
|
||||
@apply bg-white black:bg-black dark:bg-primary-900;
|
||||
|
||||
&__status {
|
||||
@apply relative pb-4;
|
||||
|
|
|
@ -8,3 +8,7 @@ em-emoji-picker {
|
|||
.dark em-emoji-picker {
|
||||
--rgb-background: var(--color-primary-900);
|
||||
}
|
||||
|
||||
.black em-emoji-picker {
|
||||
--rgb-background: var(--color-gray-900);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.thumb-navigation {
|
||||
@apply fixed lg:hidden bottom-0 bg-white/90 dark:bg-primary-900/90 backdrop-blur-md border-t border-solid border-gray-200 dark:border-gray-800 left-0 right-0 shadow-2xl w-full flex z-50;
|
||||
@apply fixed lg:hidden bottom-0 bg-white/90 black:bg-black/90 dark:bg-primary-900/90 backdrop-blur-md border-t border-solid border-gray-200 dark:border-gray-800 left-0 right-0 shadow-2xl w-full flex z-50;
|
||||
padding-bottom: env(safe-area-inset-bottom); /* iOS PWA */
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
|
|
|
@ -2,9 +2,12 @@ import aspectRatioPlugin from '@tailwindcss/aspect-ratio';
|
|||
import formsPlugin from '@tailwindcss/forms';
|
||||
import typographyPlugin from '@tailwindcss/typography';
|
||||
import { type Config } from 'tailwindcss';
|
||||
import plugin from 'tailwindcss/plugin';
|
||||
|
||||
import { parseColorMatrix } from './tailwind/colors';
|
||||
|
||||
const blackVariantPlugin = plugin(({ addVariant }) => addVariant('black', '.black &'));
|
||||
|
||||
const config: Config = {
|
||||
content: ['./src/**/*.{html,js,ts,tsx}', './custom/instance/**/*.html', './index.html'],
|
||||
darkMode: 'class',
|
||||
|
@ -105,6 +108,7 @@ const config: Config = {
|
|||
aspectRatioPlugin,
|
||||
formsPlugin,
|
||||
typographyPlugin,
|
||||
blackVariantPlugin,
|
||||
],
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue