0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

AdminX dark mode (#18035)

refs. https://github.com/TryGhost/Product/issues/3349

- added basic support for dark mode
This commit is contained in:
Peter Zimon 2023-09-08 21:53:41 +03:00 committed by GitHub
parent 7f6bd5ec31
commit 7ebbf9d56e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 162 additions and 90 deletions

View file

@ -20,13 +20,31 @@ const preview: Preview = {
}, },
}, },
decorators: [ decorators: [
(Story) => ( (Story, context) => {
<div className="admin-x-settings" style={{ padding: '24px' }}> let {scheme} = context.globals;
return (
<div className={`admin-x-settings ${scheme === 'dark' ? 'dark' : ''}`} style={{
padding: '24px',
background: (scheme === 'dark' ? '#131416' : '')
}}>
{/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */} {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */}
<Story /> <Story />
</div> </div>);
), },
], ],
globalTypes: {
scheme: {
name: "Scheme",
description: "Select light or dark mode",
defaultValue: "light",
toolbar: {
icon: "mirror",
items: ["light", "dark"],
dynamicTitle: true
}
}
}
}; };
export default preview; export default preview;

View file

@ -5,6 +5,7 @@ import NiceModal from '@ebay/nice-modal-react';
import RoutingProvider, {ExternalLink} from './components/providers/RoutingProvider'; import RoutingProvider, {ExternalLink} from './components/providers/RoutingProvider';
import Settings from './components/Settings'; import Settings from './components/Settings';
import Sidebar from './components/Sidebar'; import Sidebar from './components/Sidebar';
import clsx from 'clsx';
import {GlobalDirtyStateProvider} from './hooks/useGlobalDirtyState'; import {GlobalDirtyStateProvider} from './hooks/useGlobalDirtyState';
import {OfficialTheme, ServicesProvider} from './components/providers/ServiceProvider'; import {OfficialTheme, ServicesProvider} from './components/providers/ServiceProvider';
import {QueryClient, QueryClientProvider} from '@tanstack/react-query'; import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
@ -16,6 +17,7 @@ interface AppProps {
officialThemes: OfficialTheme[]; officialThemes: OfficialTheme[];
zapierTemplates: ZapierTemplate[]; zapierTemplates: ZapierTemplate[];
externalNavigate: (link: ExternalLink) => void; externalNavigate: (link: ExternalLink) => void;
darkMode?: boolean;
} }
const queryClient = new QueryClient({ const queryClient = new QueryClient({
@ -28,14 +30,19 @@ const queryClient = new QueryClient({
} }
}); });
function App({ghostVersion, officialThemes, zapierTemplates, externalNavigate}: AppProps) { function App({ghostVersion, officialThemes, zapierTemplates, externalNavigate, darkMode = false}: AppProps) {
const appClassName = clsx(
'admin-x-settings h-[100vh] w-full overflow-y-auto',
darkMode && 'dark'
);
return ( return (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ServicesProvider ghostVersion={ghostVersion} officialThemes={officialThemes} zapierTemplates={zapierTemplates}> <ServicesProvider ghostVersion={ghostVersion} officialThemes={officialThemes} zapierTemplates={zapierTemplates}>
<GlobalDataProvider> <GlobalDataProvider>
<RoutingProvider externalNavigate={externalNavigate}> <RoutingProvider externalNavigate={externalNavigate}>
<GlobalDirtyStateProvider> <GlobalDirtyStateProvider>
<div className="admin-x-settings h-[100vh] w-full overflow-y-auto" id="admin-x-root" style={{ <div className={appClassName} id="admin-x-root" style={{
height: '100vh', height: '100vh',
width: '100%' width: '100%'
}} }}
@ -54,12 +61,12 @@ function App({ghostVersion, officialThemes, zapierTemplates, externalNavigate}:
<div className='-mx-6 h-[84px] bg-white px-6 tablet:m-0 tablet:bg-transparent tablet:p-0'> <div className='-mx-6 h-[84px] bg-white px-6 tablet:m-0 tablet:bg-transparent tablet:p-0'>
<Heading>Settings</Heading> <Heading>Settings</Heading>
</div> </div>
<div className="relative mt-[-32px] w-full overflow-x-hidden after:absolute after:inset-x-0 after:top-0 after:hidden after:h-[40px] after:bg-gradient-to-b after:from-white after:to-transparent after:content-[''] tablet:w-[260px] tablet:after:!visible tablet:after:!block"> <div className="relative mt-[-32px] w-full overflow-x-hidden after:absolute after:inset-x-0 after:top-0 after:hidden after:h-[40px] after:bg-gradient-to-b after:from-white after:to-transparent after:content-[''] dark:after:from-black tablet:w-[260px] tablet:after:!visible tablet:after:!block">
<Sidebar /> <Sidebar />
</div> </div>
</div> </div>
<div className="relative flex-auto pt-[3vmin] tablet:ml-[300px] tablet:pt-[85px]"> <div className="relative flex-auto pt-[3vmin] tablet:ml-[300px] tablet:pt-[85px]">
<div className='pointer-events-none fixed inset-x-0 top-0 z-[5] hidden h-[80px] bg-gradient-to-t from-transparent to-white to-60% tablet:!visible tablet:!block'></div> <div className='pointer-events-none fixed inset-x-0 top-0 z-[5] hidden h-[80px] bg-gradient-to-t from-transparent to-white to-60% dark:to-black tablet:!visible tablet:!block'></div>
<Settings /> <Settings />
</div> </div>
</div> </div>

View file

@ -37,6 +37,13 @@ export const Black: Story = {
} }
}; };
export const Grey: Story = {
args: {
label: 'Button',
color: 'grey'
}
};
export const Green: Story = { export const Green: Story = {
args: { args: {
label: 'Button', label: 'Button',

View file

@ -50,10 +50,10 @@ const Button: React.FC<ButtonProps> = ({
switch (color) { switch (color) {
case 'black': case 'black':
styles += link ? ' text-black hover:text-grey-800' : ` bg-black text-white ${!disabled && 'hover:bg-grey-900'}`; styles += link ? ' text-black dark:text-white hover:text-grey-800' : ` bg-black text-white dark:bg-white dark:text-black ${!disabled && 'hover:bg-grey-900'}`;
break; break;
case 'grey': case 'grey':
styles += link ? ' text-black hover:text-grey-800' : ` bg-grey-100 text-black ${!disabled && 'hover:!bg-grey-300'}`; styles += link ? ' text-black dark:text-white hover:text-grey-800' : ` bg-grey-100 text-black dark:bg-grey-900 dark:text-white ${!disabled && 'hover:!bg-grey-300 dark:hover:!bg-grey-800'}`;
break; break;
case 'green': case 'green':
styles += link ? ' text-green hover:text-green-400' : ` bg-green text-white ${!disabled && 'hover:bg-green-400'}`; styles += link ? ' text-green hover:text-green-400' : ` bg-green text-white ${!disabled && 'hover:bg-green-400'}`;
@ -62,13 +62,13 @@ const Button: React.FC<ButtonProps> = ({
styles += link ? ' text-red hover:text-red-400' : ` bg-red text-white ${!disabled && 'hover:bg-red-400'}`; styles += link ? ' text-red hover:text-red-400' : ` bg-red text-white ${!disabled && 'hover:bg-red-400'}`;
break; break;
case 'white': case 'white':
styles += link ? ' text-white hover:text-white' : ` bg-white text-black`; styles += link ? ' text-white hover:text-white dark:text-black dark:hover:text-grey-800' : ` bg-white dark:bg-black text-black dark:text-white`;
break; break;
case 'outline': case 'outline':
styles += link ? ' text-black hover:text-grey-800' : ` border border-grey-300 bg-transparent text-black ${!disabled && 'hover:!border-black'}`; styles += link ? ' text-black dark:text-white hover:text-grey-800' : `text-black border border-grey-300 bg-transparent dark:border-grey-800 dark:text-white ${!disabled && 'hover:!border-black dark:hover:!border-white'}`;
break; break;
default: default:
styles += link ? ' text-black hover:text-grey-800' : ` text-black ${!disabled && 'hover:bg-grey-200'}`; styles += link ? ' text-black dark:text-white hover:text-grey-800' : ` text-black dark:text-white dark:hover:bg-grey-900 ${!disabled && 'hover:bg-grey-200'}`;
break; break;
} }

View file

@ -39,7 +39,7 @@ type HeadingLabelProps = {
grey?: boolean } & HeadingBaseProps & React.LabelHTMLAttributes<HTMLLabelElement> grey?: boolean } & HeadingBaseProps & React.LabelHTMLAttributes<HTMLLabelElement>
export const Heading6Styles = 'text-2xs font-semibold uppercase tracking-wider'; export const Heading6Styles = 'text-2xs font-semibold uppercase tracking-wider';
export const Heading6StylesGrey = 'text-2xs font-semibold uppercase tracking-wider text-grey-800'; export const Heading6StylesGrey = 'text-2xs font-semibold uppercase tracking-wider text-grey-800 dark:text-grey-500';
const Heading: React.FC<Heading1to5Props | Heading6Props | HeadingLabelProps> = ({ const Heading: React.FC<Heading1to5Props | Heading6Props | HeadingLabelProps> = ({
level = 1, level = 1,
@ -78,6 +78,7 @@ const Heading: React.FC<Heading1to5Props | Heading6Props | HeadingLabelProps> =
className = clsx( className = clsx(
styles, styles,
'dark:text-white',
className className
); );

View file

@ -17,6 +17,20 @@ export const Default: Story = {
} }
}; };
export const Error: Story = {
args: {
children: 'This is a hint that should be red',
color: 'red'
}
};
export const Success: Story = {
args: {
children: 'This is a hint that should be green',
color: 'green'
}
};
export const UsingReactNode: Story = { export const UsingReactNode: Story = {
args: { args: {
children: ( children: (

View file

@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import clsx from 'clsx';
interface HintProps { interface HintProps {
children?: React.ReactNode; children?: React.ReactNode;
color?: string; color?: 'red' | 'green' | 'default' | '';
className?: string; className?: string;
} }
@ -11,8 +12,24 @@ const Hint: React.FC<HintProps> = ({children, color, className, ...props}) => {
return null; return null;
} }
let colorClassName = 'text-grey-700 dark:text-grey-600';
switch (color) {
case 'red':
colorClassName = 'text-red dark:text-red-500';
break;
case 'green':
colorClassName = 'text-green dark:text-green-500';
break;
}
className = clsx(
'mt-1 inline-block text-xs',
colorClassName,
className
);
return ( return (
<span className={`mt-1 inline-block text-xs ${color ? `text-${color}` : `text-grey-700`} ${className}`} {...props}>{children}</span> <span className={className} {...props}>{children}</span>
); );
}; };

View file

@ -43,8 +43,8 @@ const ListItem: React.FC<ListItemProps> = ({
const listItemClasses = clsx( const listItemClasses = clsx(
'group/list-item flex items-center justify-between', 'group/list-item flex items-center justify-between',
bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50', bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50 dark:hover:from-black dark:hover:to-grey-950',
separator ? 'border-b border-grey-100 last-of-type:border-b-transparent hover:border-grey-200' : 'border-y border-transparent hover:border-grey-200 first-of-type:hover:border-t-transparent', separator ? 'border-b border-grey-100 last-of-type:border-b-transparent hover:border-grey-200 dark:border-grey-800 dark:hover:border-grey-700' : 'border-y border-transparent hover:border-grey-200 first-of-type:hover:border-t-transparent dark:hover:border-grey-700',
className className
); );

View file

@ -4,7 +4,7 @@ interface SeparatorProps {
className?: string; className?: string;
} }
const Separator: React.FC<SeparatorProps> = ({className = 'border-grey-200'}) => { const Separator: React.FC<SeparatorProps> = ({className = 'border-grey-200 dark:border-grey-800'}) => {
return <hr className={className} />; return <hr className={className} />;
}; };

View file

@ -11,8 +11,8 @@ interface StickyFooterProps {
const StickyFooter: React.FC<StickyFooterProps> = ({ const StickyFooter: React.FC<StickyFooterProps> = ({
shiftY, shiftY,
footerBgColorClass = 'bg-white', footerBgColorClass = 'bg-white dark:bg-black',
contentBgColorClass = 'bg-white', contentBgColorClass = 'bg-white dark:bg-black',
height = 96, height = 96,
children children
}) => { }) => {

View file

@ -44,7 +44,7 @@ function TabView<ID extends string = string>({
width === 'narrow' && 'gap-3', width === 'narrow' && 'gap-3',
width === 'normal' && 'gap-5', width === 'normal' && 'gap-5',
width === 'wide' && 'gap-7', width === 'wide' && 'gap-7',
border && 'border-b border-grey-300' border && 'border-b border-grey-300 dark:border-grey-900'
); );
return ( return (
@ -55,9 +55,9 @@ function TabView<ID extends string = string>({
key={tab.id} key={tab.id}
aria-selected={selectedTab === tab.id} aria-selected={selectedTab === tab.id}
className={clsx( className={clsx(
'-m-b-px cursor-pointer appearance-none whitespace-nowrap py-1 text-sm transition-all after:invisible after:block after:h-px after:overflow-hidden after:font-bold after:text-transparent after:content-[attr(title)]', '-m-b-px cursor-pointer appearance-none whitespace-nowrap py-1 text-sm transition-all after:invisible after:block after:h-px after:overflow-hidden after:font-bold after:text-transparent after:content-[attr(title)] dark:text-white',
border && 'border-b-[3px]', border && 'border-b-[3px]',
selectedTab === tab.id && border ? 'border-black' : 'border-transparent hover:border-grey-500', selectedTab === tab.id && border ? 'border-black dark:border-white' : 'border-transparent hover:border-grey-500',
selectedTab === tab.id && 'font-bold' selectedTab === tab.id && 'font-bold'
)} )}
id={tab.id} id={tab.id}

View file

@ -26,9 +26,9 @@ const TableRow: React.FC<TableRowProps> = ({id, action, hideActions, className,
separator = (separator === undefined) ? true : separator; separator = (separator === undefined) ? true : separator;
const tableRowClasses = clsx( const tableRowClasses = clsx(
'group/table-row', 'group/table-row',
bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50', bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50 dark:hover:from-black dark:hover:to-grey-950',
onClick && 'cursor-pointer', onClick && 'cursor-pointer',
separator ? 'border-b border-grey-100 last-of-type:border-b-transparent hover:border-grey-200' : 'border-y border-transparent first-of-type:hover:border-t-transparent', separator ? 'border-b border-grey-100 last-of-type:border-b-transparent hover:border-grey-200 dark:border-grey-900 dark:hover:border-grey-800' : 'border-y border-transparent first-of-type:hover:border-t-transparent',
className className
); );

View file

@ -37,7 +37,7 @@ const Checkbox: React.FC<CheckboxProps> = ({title, label, value, onChange, disab
<label className={`flex cursor-pointer items-start ${title && '-mb-1 mt-1'}`} htmlFor={id}> <label className={`flex cursor-pointer items-start ${title && '-mb-1 mt-1'}`} htmlFor={id}>
<input <input
checked={isChecked} checked={isChecked}
className="relative float-left mt-[3px] h-4 w-4 appearance-none border-2 border-solid border-grey-200 bg-grey-200 outline-none checked:border-black checked:bg-black checked:after:absolute checked:after:-mt-px checked:after:ml-[3px] checked:after:block checked:after:h-[11px] checked:after:w-[6px] checked:after:rotate-45 checked:after:border-[2px] checked:after:border-l-0 checked:after:border-t-0 checked:after:border-solid checked:after:border-white checked:after:bg-transparent checked:after:content-[''] hover:cursor-pointer focus:shadow-none focus:transition-[border-color_0.2s] dark:border-grey-600 dark:checked:border-green dark:checked:bg-green" className="relative float-left mt-[3px] h-4 w-4 appearance-none border-2 border-solid border-grey-200 bg-grey-200 outline-none checked:border-black checked:bg-black checked:after:absolute checked:after:-mt-px checked:after:ml-[3px] checked:after:block checked:after:h-[11px] checked:after:w-[6px] checked:after:rotate-45 checked:after:border-[2px] checked:after:border-l-0 checked:after:border-t-0 checked:after:border-solid checked:after:border-white checked:after:bg-transparent checked:after:content-[''] hover:cursor-pointer focus:shadow-none focus:transition-[border-color_0.2s] dark:border-grey-800 dark:bg-grey-800 dark:checked:border-green dark:checked:bg-green"
disabled={disabled} disabled={disabled}
id={id} id={id}
type='checkbox' type='checkbox'
@ -45,7 +45,7 @@ const Checkbox: React.FC<CheckboxProps> = ({title, label, value, onChange, disab
onChange={handleOnChange} onChange={handleOnChange}
/> />
<div className={`ml-2 flex flex-col ${hint && 'mb-2'}`}> <div className={`ml-2 flex flex-col ${hint && 'mb-2'}`}>
<span className={`inline-block text-[1.425rem] ${hint && '-mb-1'}`}>{label}</span> <span className={`inline-block text-[1.425rem] dark:text-white ${hint && '-mb-1'}`}>{label}</span>
{hint && <Hint color={error ? 'red' : ''}>{hint}</Hint>} {hint && <Hint color={error ? 'red' : ''}>{hint}</Hint>}
</div> </div>
</label> </label>

View file

@ -94,7 +94,7 @@ const ColorIndicator: React.FC<{
<button aria-label="Pick color" className="relative h-6 w-6 cursor-pointer rounded-full border border-grey-200 dark:border-grey-800" type="button" onClick={onTogglePicker}> <button aria-label="Pick color" className="relative h-6 w-6 cursor-pointer rounded-full border border-grey-200 dark:border-grey-800" type="button" onClick={onTogglePicker}>
<div className='absolute inset-0 rounded-full bg-[conic-gradient(hsl(360,100%,50%),hsl(315,100%,50%),hsl(270,100%,50%),hsl(225,100%,50%),hsl(180,100%,50%),hsl(135,100%,50%),hsl(90,100%,50%),hsl(45,100%,50%),hsl(0,100%,50%))]' /> <div className='absolute inset-0 rounded-full bg-[conic-gradient(hsl(360,100%,50%),hsl(315,100%,50%),hsl(270,100%,50%),hsl(225,100%,50%),hsl(180,100%,50%),hsl(135,100%,50%),hsl(90,100%,50%),hsl(45,100%,50%),hsl(0,100%,50%))]' />
{value && !selectedSwatch && ( {value && !selectedSwatch && (
<div className="dark:border-grey-950 absolute inset-[3px] overflow-hidden rounded-full border border-white" style={{backgroundColor: value}}> <div className="absolute inset-[3px] overflow-hidden rounded-full border border-white dark:border-grey-950" style={{backgroundColor: value}}>
{value === 'transparent' && <div className="absolute left-[3px] top-[3px] z-10 w-[136%] origin-left rotate-45 border-b border-b-red" />} {value === 'transparent' && <div className="absolute left-[3px] top-[3px] z-10 w-[136%] origin-left rotate-45 border-b border-b-red" />}
</div> </div>
)} )}

View file

@ -30,7 +30,7 @@ const FileUpload: React.FC<FileUploadProps> = ({id, onUpload, children, style, u
<label htmlFor={id} style={style} {...props}> <label htmlFor={id} style={style} {...props}>
<input key={fileKey} id={id} type="file" hidden onChange={handleFileChange} /> <input key={fileKey} id={id} type="file" hidden onChange={handleFileChange} />
{(typeof children === 'string') ? {(typeof children === 'string') ?
<div className={!unstyled ? `inline-flex h-[34px] cursor-pointer items-center justify-center rounded px-4 text-sm font-semibold hover:bg-grey-100` : ''}> <div className={!unstyled ? `inline-flex h-[34px] cursor-pointer items-center justify-center rounded px-4 text-sm font-semibold hover:bg-grey-100 dark:text-white dark:hover:bg-grey-900` : ''}>
{children} {children}
</div> </div>
: :

View file

@ -64,7 +64,7 @@ const ImageUpload: React.FC<ImageUploadProps> = ({
); );
fileUploadClassName = clsx( fileUploadClassName = clsx(
'flex cursor-pointer items-center justify-center rounded border border-grey-100 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black', 'flex cursor-pointer items-center justify-center rounded border border-grey-100 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black dark:border-grey-900 dark:bg-grey-900 dark:text-grey-400',
fileUploadClassName fileUploadClassName
); );

View file

@ -26,9 +26,9 @@ interface MultiSelectProps {
const multiValueColor = (color?: MultiSelectColor) => { const multiValueColor = (color?: MultiSelectColor) => {
switch (color) { switch (color) {
case 'black': case 'black':
return 'bg-black text-white'; return 'bg-black text-white dark:bg-white dark:text-black';
case 'grey': case 'grey':
return 'bg-grey-300 text-black'; return 'bg-grey-300 text-black dark:bg-grey-500';
case 'green': case 'green':
return 'bg-green-500 text-white'; return 'bg-green-500 text-white';
case 'pink': case 'pink':
@ -40,7 +40,7 @@ const multiValueColor = (color?: MultiSelectColor) => {
const DropdownIndicator: React.FC<DropdownIndicatorProps<MultiSelectOption, true> & {clearBg: boolean}> = ({clearBg, ...props}) => ( const DropdownIndicator: React.FC<DropdownIndicatorProps<MultiSelectOption, true> & {clearBg: boolean}> = ({clearBg, ...props}) => (
<components.DropdownIndicator {...props}> <components.DropdownIndicator {...props}>
<div className={`absolute top-[14px] block h-2 w-2 rotate-45 border-[1px] border-l-0 border-t-0 border-grey-900 content-[''] ${clearBg ? 'right-0' : 'right-4'} `}></div> <div className={`absolute top-[14px] block h-2 w-2 rotate-45 border-[1px] border-l-0 border-t-0 border-grey-900 content-[''] dark:border-grey-400 ${clearBg ? 'right-0' : 'right-4'} `}></div>
</components.DropdownIndicator> </components.DropdownIndicator>
); );
@ -65,11 +65,11 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
const id = useId(); const id = useId();
const customClasses = { const customClasses = {
control: `w-full cursor-pointer appearance-none min-h-[40px] border-b ${!clearBg && 'bg-grey-75 px-[10px]'} py-2 outline-none ${error ? 'border-red' : 'border-grey-500 hover:border-grey-700'} ${(title && !clearBg) && 'mt-2'}`, control: `w-full cursor-pointer appearance-none min-h-[40px] border-b dark:text-white ${!clearBg && 'bg-grey-75 dark:bg-grey-950 px-[10px]'} py-2 outline-none ${error ? 'border-red' : 'border-grey-500 hover:border-grey-700 dark:border-grey-800 dark:hover:border-grey-700'} ${(title && !clearBg) && 'mt-2'}`,
valueContainer: 'gap-1', valueContainer: 'gap-1',
placeHolder: 'text-grey-600', placeHolder: 'text-grey-500 dark:text-grey-800',
menu: 'shadow py-2 rounded-b z-50 bg-white', menu: 'shadow py-2 rounded-b z-50 bg-white dark:bg-black dark:border dark:border-grey-900',
option: 'hover:cursor-pointer hover:bg-grey-100 px-3 py-[6px]', option: 'hover:cursor-pointer hover:bg-grey-100 px-3 py-[6px] dark:text-white dark:hover:bg-grey-900',
multiValue: (optionColor?: MultiSelectColor) => `rounded-sm items-center text-[14px] py-px pl-2 pr-1 gap-1.5 ${multiValueColor(optionColor || color)}`, multiValue: (optionColor?: MultiSelectColor) => `rounded-sm items-center text-[14px] py-px pl-2 pr-1 gap-1.5 ${multiValueColor(optionColor || color)}`,
noOptionsMessage: 'p-3 text-grey-600', noOptionsMessage: 'p-3 text-grey-600',
groupHeading: 'py-[6px] px-3 text-2xs font-semibold uppercase tracking-wide text-grey-700' groupHeading: 'py-[6px] px-3 text-2xs font-semibold uppercase tracking-wide text-grey-700'

View file

@ -33,7 +33,7 @@ const Radio: React.FC<RadioProps> = ({id, title, options, onSelect, error, hint,
<label key={option.value} className={`flex cursor-pointer items-start ${title && '-mb-1 mt-1'}`} htmlFor={option.value}> <label key={option.value} className={`flex cursor-pointer items-start ${title && '-mb-1 mt-1'}`} htmlFor={option.value}>
<input <input
checked={selectedOption === option.value} checked={selectedOption === option.value}
className="relative float-left mt-[3px] h-4 w-4 min-w-[16px] appearance-none rounded-full border-2 border-solid border-grey-300 after:absolute after:z-[1] after:block after:h-3 after:w-3 after:rounded-full after:content-[''] checked:border-green checked:after:absolute checked:after:left-1/2 checked:after:top-1/2 checked:after:h-[0.625rem] checked:after:w-[0.625rem] checked:after:rounded-full checked:after:border-green checked:after:bg-green checked:after:content-[''] checked:after:[transform:translate(-50%,-50%)] hover:cursor-pointer focus:shadow-none focus:outline-none focus:ring-0 checked:focus:border-green dark:border-grey-600 dark:checked:border-green dark:checked:after:border-green dark:checked:after:bg-green dark:checked:focus:border-green" className="relative float-left mt-[3px] h-4 w-4 min-w-[16px] appearance-none rounded-full border-2 border-solid border-grey-300 after:absolute after:z-[1] after:block after:h-3 after:w-3 after:rounded-full after:content-[''] checked:border-green checked:after:absolute checked:after:left-1/2 checked:after:top-1/2 checked:after:h-[0.625rem] checked:after:w-[0.625rem] checked:after:rounded-full checked:after:border-green checked:after:bg-green checked:after:content-[''] checked:after:[transform:translate(-50%,-50%)] hover:cursor-pointer focus:shadow-none focus:outline-none focus:ring-0 checked:focus:border-green dark:border-grey-800 dark:text-white dark:checked:border-green dark:checked:after:border-green dark:checked:after:bg-green dark:checked:focus:border-green"
id={option.value} id={option.value}
name={id} name={id}
type='radio' type='radio'
@ -41,7 +41,7 @@ const Radio: React.FC<RadioProps> = ({id, title, options, onSelect, error, hint,
onChange={handleOptionChange} onChange={handleOptionChange}
/> />
<div className={`ml-2 flex flex-col ${option.hint && 'mb-2'}`}> <div className={`ml-2 flex flex-col ${option.hint && 'mb-2'}`}>
<span className={`inline-block text-md ${option.hint && '-mb-1'}`}>{option.label}</span> <span className={`inline-block text-md dark:text-white ${option.hint && '-mb-1'}`}>{option.label}</span>
{option.hint && <Hint>{option.hint}</Hint>} {option.hint && <Hint>{option.hint}</Hint>}
</div> </div>
</label> </label>

View file

@ -63,8 +63,8 @@ const Select: React.FC<SelectProps> = ({
let containerClasses = ''; let containerClasses = '';
if (!unstyled) { if (!unstyled) {
containerClasses = clsx( containerClasses = clsx(
'relative w-full after:pointer-events-none', 'relative w-full after:pointer-events-none dark:text-white',
`after:absolute after:block after:h-2 after:w-2 after:rotate-45 after:border-[1px] after:border-l-0 after:border-t-0 after:border-grey-900 after:content-['']`, `after:absolute after:block after:h-2 after:w-2 after:rotate-45 after:border-[1px] after:border-l-0 after:border-t-0 after:border-grey-900 after:content-[''] dark:after:border-grey-500`,
size === 'xs' ? 'after:top-[6px]' : 'after:top-[14px]', size === 'xs' ? 'after:top-[6px]' : 'after:top-[14px]',
clearBg ? 'after:right-0' : 'after:right-4', clearBg ? 'after:right-0' : 'after:right-4',
disabled && 'opacity-40' disabled && 'opacity-40'
@ -82,7 +82,7 @@ const Select: React.FC<SelectProps> = ({
'w-full appearance-none outline-none', 'w-full appearance-none outline-none',
border && 'border-b', border && 'border-b',
!clearBg && 'bg-grey-75 px-[10px]', !clearBg && 'bg-grey-75 px-[10px]',
error ? 'border-red' : 'border-grey-500 focus:border-black', error ? '!border-red' : 'border-grey-500 focus:border-black dark:border-grey-800 dark:focus:border-grey-500',
disabled ? 'cursor-auto' : 'cursor-pointer hover:border-grey-700', disabled ? 'cursor-auto' : 'cursor-pointer hover:border-grey-700',
(title && !clearBg) && 'mt-2' (title && !clearBg) && 'mt-2'
); );

View file

@ -42,9 +42,9 @@ const TextArea: React.FC<TextAreaProps> = ({
const id = useId(); const id = useId();
let styles = clsx( let styles = clsx(
'peer order-2 rounded-sm border px-3 py-2', 'peer order-2 rounded-sm border px-3 py-2 dark:text-white',
clearBg ? 'bg-transparent' : 'bg-grey-75', clearBg ? 'bg-transparent' : 'bg-grey-75 dark:bg-grey-900',
error ? 'border-red' : 'border-grey-500 hover:border-grey-700 focus:border-grey-800', error ? 'border-red' : 'border-grey-500 placeholder:text-grey-500 hover:border-grey-700 focus:border-grey-800 dark:border-grey-800 dark:placeholder:text-grey-800 dark:hover:border-grey-700 dark:focus:border-grey-500',
title && 'mt-2', title && 'mt-2',
fontStyle === 'mono' && 'font-mono text-sm', fontStyle === 'mono' && 'font-mono text-sm',
className className
@ -81,7 +81,7 @@ const TextArea: React.FC<TextAreaProps> = ({
onChange={onChange} onChange={onChange}
{...props}> {...props}>
</textarea> </textarea>
{title && <Heading className={'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>} {title && <Heading className={'order-1 !text-grey-700 peer-focus:!text-black dark:!text-grey-300 dark:peer-focus:!text-white'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{hint && <Hint className='order-3' color={error ? 'red' : ''}>{hint}</Hint>} {hint && <Hint className='order-3' color={error ? 'red' : ''}>{hint}</Hint>}
{maxLength && <Hint>Max length is {maxLength}</Hint>} {maxLength && <Hint>Max length is {maxLength}</Hint>}
</div> </div>

View file

@ -33,6 +33,7 @@ export const Default: Story = {
export const Disabled: Story = { export const Disabled: Story = {
args: { args: {
placeholder: `Here's a disabled field`, placeholder: `Here's a disabled field`,
value: 'Hello disabled field',
title: 'Disabled', title: 'Disabled',
disabled: true disabled: true
} }

View file

@ -51,17 +51,17 @@ const TextField: React.FC<TextFieldProps> = ({
}) => { }) => {
const id = useId(); const id = useId();
const disabledBorderClasses = border && 'border-grey-300'; const disabledBorderClasses = border && 'border-grey-300 dark:border-grey-900';
const enabledBorderClasses = border && 'border-grey-500 hover:border-grey-700 focus:border-black'; const enabledBorderClasses = border && 'border-grey-500 hover:border-grey-700 focus:border-black dark:border-grey-800 dark:hover:border-grey-700 dark:focus:border-grey-500';
const textFieldClasses = !unstyled && clsx( const textFieldClasses = !unstyled && clsx(
'peer order-2 h-8 w-full py-1 text-sm md:h-10 md:py-2 md:text-base', 'peer order-2 h-8 w-full py-1 text-sm placeholder:text-grey-500 dark:text-white dark:placeholder:text-grey-800 md:h-10 md:py-2 md:text-base',
border && 'border-b', border && 'border-b',
!border && '-mb-1.5', !border && '-mb-1.5',
clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]', clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]',
error && border ? `border-red` : `${disabled ? disabledBorderClasses : enabledBorderClasses}`, error && border ? `border-red` : `${disabled ? disabledBorderClasses : enabledBorderClasses}`,
(title && !hideTitle && !clearBg) && `mt-2`, (title && !hideTitle && !clearBg) && `mt-2`,
(disabled ? 'cursor-not-allowed text-grey-700' : ''), (disabled ? 'cursor-not-allowed text-grey-700 opacity-60 dark:text-grey-800' : ''),
rightPlaceholder && 'w-0 grow', rightPlaceholder && 'w-0 grow',
className className
); );
@ -82,7 +82,7 @@ const TextField: React.FC<TextFieldProps> = ({
{...props} />; {...props} />;
if (rightPlaceholder) { if (rightPlaceholder) {
const rightPHEnabledBorderClasses = 'border-grey-500 peer-hover:border-grey-700 peer-focus:border-black'; const rightPHEnabledBorderClasses = 'border-grey-500 dark:border-grey-800 peer-hover:border-grey-700 peer-focus:border-black dark:peer-focus:border-grey-500';
const rightPHClasses = !unstyled && clsx( const rightPHClasses = !unstyled && clsx(
'order-3', 'order-3',
border && 'border-b', border && 'border-b',
@ -115,8 +115,8 @@ const TextField: React.FC<TextFieldProps> = ({
return ( return (
<div className={containerClassName}> <div className={containerClassName}>
{field} {field}
{title && <Heading className={hideTitle ? 'sr-only' : 'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>} {title && <Heading className={hideTitle ? 'sr-only' : 'order-1 !text-grey-700 peer-focus:!text-black dark:!text-grey-300 dark:peer-focus:!text-white'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{hint && <Hint className={hintClassName} color={error ? 'red' : ''}>{hint}</Hint>} {hint && <Hint className={hintClassName} color={error ? 'red' : 'default'}>{hint}</Hint>}
</div> </div>
); );
} else { } else {

View file

@ -81,9 +81,9 @@ const Toggle: React.FC<ToggleProps> = ({
return ( return (
<div> <div>
<div className={`group flex items-start gap-2 ${direction === 'rtl' && 'justify-between'} ${separator && 'pb-2'}`}> <div className={`group flex items-start gap-2 dark:text-white ${direction === 'rtl' && 'justify-between'} ${separator && 'pb-2'}`}>
<input checked={checked} <input checked={checked}
className={`${toggleBgClass} appearance-none rounded-full bg-grey-300 transition after:absolute after:ml-0.5 after:mt-0.5 after:rounded-full after:border-none after:bg-white after:transition-[background-color_0.2s,transform_0.2s] after:content-[''] checked:after:absolute checked:after:rounded-full checked:after:border-none checked:after:bg-white checked:after:transition-[background-color_0.2s,transform_0.2s] checked:after:content-[''] hover:cursor-pointer group-hover:opacity-80 ${sizeStyles} ${direction === 'rtl' && ' order-2'}`} className={`${toggleBgClass} appearance-none rounded-full bg-grey-300 transition after:absolute after:ml-0.5 after:mt-0.5 after:rounded-full after:border-none after:bg-white after:transition-[background-color_0.2s,transform_0.2s] after:content-[''] checked:after:absolute checked:after:rounded-full checked:after:border-none checked:after:bg-white checked:after:transition-[background-color_0.2s,transform_0.2s] checked:after:content-[''] hover:cursor-pointer group-hover:opacity-80 dark:bg-grey-800 dark:checked:bg-green ${sizeStyles} ${direction === 'rtl' && ' order-2'}`}
id={id} id={id}
role="switch" role="switch"
type="checkbox" type="checkbox"
@ -96,7 +96,7 @@ const Toggle: React.FC<ToggleProps> = ({
: :
<span>{label}</span> <span>{label}</span>
} }
{hint && <span className={`text-xs ${error ? 'text-red' : 'text-grey-700'}`}>{hint}</span>} {hint && <span className={`text-xs ${error ? 'text-red' : 'text-grey-700 dark:text-grey-600'}`}>{hint}</span>}
</label> </label>
} }
</div> </div>

View file

@ -4,7 +4,7 @@ import URLSelect from './URLSelect';
import {SelectOption} from './Select'; import {SelectOption} from './Select';
const meta = { const meta = {
title: 'Global / Form / URL Select', title: 'Experimental / URL Select',
component: URLSelect, component: URLSelect,
tags: ['autodocs'] tags: ['autodocs']
} satisfies Meta<typeof URLSelect>; } satisfies Meta<typeof URLSelect>;

View file

@ -130,7 +130,7 @@ const Modal: React.FC<ModalProps> = ({
} }
let modalClasses = clsx( let modalClasses = clsx(
'relative z-50 mx-auto flex max-h-[100%] w-full flex-col justify-between overflow-x-hidden rounded bg-white', 'relative z-50 mx-auto flex max-h-[100%] w-full flex-col justify-between overflow-x-hidden rounded bg-white dark:bg-black',
formSheet ? 'shadow-md' : 'shadow-xl', formSheet ? 'shadow-md' : 'shadow-xl',
(animate && !formSheet) && 'animate-modal-in', (animate && !formSheet) && 'animate-modal-in',
formSheet && 'animate-modal-in-reverse', formSheet && 'animate-modal-in-reverse',
@ -256,7 +256,7 @@ const Modal: React.FC<ModalProps> = ({
(<> (<>
{title && <Heading level={3}>{title}</Heading>} {title && <Heading level={3}>{title}</Heading>}
<div className={`${topRightContent !== 'close' && 'md:!invisible md:!hidden'} ${hideXOnMobile && 'hidden'} absolute right-6 top-6`}> <div className={`${topRightContent !== 'close' && 'md:!invisible md:!hidden'} ${hideXOnMobile && 'hidden'} absolute right-6 top-6`}>
<Button className='-m-2 cursor-pointer p-2 opacity-50 hover:opacity-100' icon='close' size='sm' unstyled onClick={removeModal} /> <Button className='-m-2 cursor-pointer p-2 opacity-50 hover:opacity-100' icon='close' iconColorClass='text-black dark:text-white' size='sm' unstyled onClick={removeModal} />
</div> </div>
</>) </>)
: :

View file

@ -78,9 +78,9 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
if (saveState === 'unsaved') { if (saveState === 'unsaved') {
styles += ' border-green'; styles += ' border-green';
} else if (isEditing){ } else if (isEditing){
styles += ' border-grey-300'; styles += ' border-grey-300 dark:border-grey-800';
} else { } else {
styles += ' border-grey-200'; styles += ' border-grey-200 dark:border-grey-900';
} }
let viewButtons: ButtonProps[] = []; let viewButtons: ButtonProps[] = [];
@ -167,7 +167,7 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
border && 'border p-5 md:p-7', border && 'border p-5 md:p-7',
!checkVisible(keywords) ? 'hidden' : 'flex', !checkVisible(keywords) ? 'hidden' : 'flex',
highlight && 'before:pointer-events-none before:absolute before:inset-[1px] before:animate-setting-highlight-fade-out before:rounded before:shadow-[0_0_0_3px_rgba(48,207,67,0.45)]', highlight && 'before:pointer-events-none before:absolute before:inset-[1px] before:animate-setting-highlight-fade-out before:rounded before:shadow-[0_0_0_3px_rgba(48,207,67,0.45)]',
!isEditing && 'is-not-editing group', !isEditing && 'is-not-editing group/setting-group',
styles styles
); );

View file

@ -13,7 +13,7 @@ const SettingGroupHeader: React.FC<Props> = ({title, description, children}) =>
{(title || description) && {(title || description) &&
<div> <div>
<Heading level={5}>{title}</Heading> <Heading level={5}>{title}</Heading>
{description && <p className="mt-0.5 hidden max-w-lg text-sm group-[.is-not-editing]:!visible group-[.is-not-editing]:!block md:!visible md:!block">{description}</p>} {description && <p className="mt-0.5 hidden max-w-lg text-sm group-[.is-not-editing]/setting-group:!visible group-[.is-not-editing]/setting-group:!block md:!visible md:!block">{description}</p>}
</div> </div>
} }
<div className='-mt-0.5'> <div className='-mt-0.5'>

View file

@ -16,7 +16,7 @@ const SettingNavItem: React.FC<Props> = ({
const {scrolledRoute} = useRouting(); const {scrolledRoute} = useRouting();
const classNames = clsx( const classNames = clsx(
'block px-0 py-1 text-sm', 'block px-0 py-1 text-sm dark:text-white',
(scrolledRoute === navid) && 'font-bold' (scrolledRoute === navid) && 'font-bold'
); );

View file

@ -8,7 +8,7 @@ interface Props {
const SettingSectionHeader: React.FC<Props> = ({title, sticky = false}) => { const SettingSectionHeader: React.FC<Props> = ({title, sticky = false}) => {
let styles = 'pb-4 text-2xs font-semibold uppercase tracking-wider text-grey-700 z-10 '; let styles = 'pb-4 text-2xs font-semibold uppercase tracking-wider text-grey-700 z-10 ';
if (sticky) { if (sticky) {
styles += ' sticky top-0 -mt-4 pt-4 bg-white'; styles += ' sticky top-0 -mt-4 pt-4 bg-white dark:bg-black';
} }
return ( return (
<h2 className={styles}>{title}</h2> <h2 className={styles}>{title}</h2>

View file

@ -28,7 +28,7 @@ const Sidebar: React.FC = () => {
<div className='tablet:h-[calc(100vh-5vmin-84px)] tablet:w-[240px] tablet:overflow-y-scroll'> <div className='tablet:h-[calc(100vh-5vmin-84px)] tablet:w-[240px] tablet:overflow-y-scroll'>
<div className='relative mb-10 md:pt-4 tablet:pt-[32px]'> <div className='relative mb-10 md:pt-4 tablet:pt-[32px]'>
<Icon className='absolute top-2 md:top-6 tablet:top-10' colorClass='text-grey-500' name='magnifying-glass' size='sm' /> <Icon className='absolute top-2 md:top-6 tablet:top-10' colorClass='text-grey-500' name='magnifying-glass' size='sm' />
<TextField autoComplete="off" className='border-b border-grey-500 px-3 py-1.5 pl-[24px] text-sm' placeholder="Search" title="Search" value={filter} hideTitle unstyled onChange={e => setFilter(e.target.value)} /> <TextField autoComplete="off" className='border-b border-grey-500 bg-transparent px-3 py-1.5 pl-[24px] text-sm dark:text-white' placeholder="Search" title="Search" value={filter} hideTitle unstyled onChange={e => setFilter(e.target.value)} />
</div> </div>
<div className="hidden tablet:!visible tablet:!block"> <div className="hidden tablet:!visible tablet:!block">
<SettingNavSection title="General"> <SettingNavSection title="General">

View file

@ -59,7 +59,7 @@ const Labs: React.FC<{ keywords: string[] }> = ({keywords}) => {
<TabView<'labs-migration-options' | 'labs-alpha-features' | 'labs-beta-features'> selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} /> <TabView<'labs-migration-options' | 'labs-alpha-features' | 'labs-beta-features'> selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} />
: :
<div className='absolute inset-0 z-0 overflow-hidden opacity-70'> <div className='absolute inset-0 z-0 overflow-hidden opacity-70'>
<img className='absolute -right-6 -top-6' src={LabsBubbles} /> <img className='absolute -right-6 -top-6 dark:opacity-10' src={LabsBubbles} />
</div> </div>
} }
</SettingGroup> </SettingGroup>

View file

@ -54,16 +54,16 @@ const Facebook: React.FC<{ keywords: string[] }> = ({keywords}) => {
<FacebookLogo className='h-10 w-10' /> <FacebookLogo className='h-10 w-10' />
</div> </div>
<div> <div>
<div className="mb-1 font-semibold leading-none text-grey-900">{siteTitle}</div> <div className="mb-1 font-semibold leading-none text-grey-900 dark:text-grey-300">{siteTitle}</div>
<div className="leading-none text-grey-700">2h</div> <div className="leading-none text-grey-700">2h</div>
</div> </div>
</div> </div>
<div> <div>
<div className="mb-2 h-3 w-full rounded bg-grey-200"></div> <div className="mb-2 h-3 w-full rounded bg-grey-200 dark:bg-grey-900"></div>
<div className="mb-4 h-3 w-3/5 rounded bg-grey-200"></div> <div className="mb-4 h-3 w-3/5 rounded bg-grey-200 dark:bg-grey-900"></div>
<SettingGroupContent className="overflow-hidden rounded-md border border-grey-300"> <SettingGroupContent className="overflow-hidden rounded-md border border-grey-300 dark:border-grey-900">
<ImageUpload <ImageUpload
fileUploadClassName='flex cursor-pointer items-center justify-center rounded rounded-b-none border border-grey-100 border-b-0 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black' fileUploadClassName='flex cursor-pointer items-center justify-center rounded rounded-b-none border border-grey-100 border-b-0 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black dark:border-grey-900'
height='300px' height='300px'
id='facebook-image' id='facebook-image'
imageURL={facebookImage} imageURL={facebookImage}

View file

@ -32,13 +32,13 @@ const SearchEnginePreview: React.FC<SearchEnginePreviewProps> = ({
<GoogleLogo className='mr-7 h-7' /> <GoogleLogo className='mr-7 h-7' />
</div> </div>
<div className='grow'> <div className='grow'>
<div className='flex w-full items-center justify-end rounded-full bg-white p-3 px-4 shadow'> <div className='flex w-full items-center justify-end rounded-full bg-white p-3 px-4 shadow dark:bg-grey-900'>
<MagnifyingGlass className='h-4 w-4 text-blue-600' style={{strokeWidth: '2px'}} /> <MagnifyingGlass className='h-4 w-4 text-blue-600' style={{strokeWidth: '2px'}} />
</div> </div>
</div> </div>
</div> </div>
<div className='mt-4 flex items-center gap-2 border-t border-grey-200 pt-4'> <div className='mt-4 flex items-center gap-2 border-t border-grey-200 pt-4 dark:border-grey-900'>
<div className='flex h-7 w-7 items-center justify-center rounded-full bg-grey-200' style={{ <div className='flex h-7 w-7 items-center justify-center rounded-full bg-grey-200 dark:bg-grey-700' style={{
backgroundImage: icon ? `url(${icon})` : 'none' backgroundImage: icon ? `url(${icon})` : 'none'
}}> }}>
</div> </div>
@ -48,8 +48,8 @@ const SearchEnginePreview: React.FC<SearchEnginePreviewProps> = ({
</div> </div>
</div> </div>
<div className='mt-1 flex flex-col'> <div className='mt-1 flex flex-col'>
<span className='text-lg text-[#1a0dab]'>{title}</span> <span className='text-lg text-[#1a0dab] dark:text-blue'>{title}</span>
<span className='text-sm text-grey-900'>{description}</span> <span className='text-sm text-grey-900 dark:text-grey-700'>{description}</span>
</div> </div>
</div> </div>
); );

View file

@ -58,14 +58,14 @@ const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
</div> </div>
<div className="w-full md:mr-[52px]"> <div className="w-full md:mr-[52px]">
<div className="mb-2"> <div className="mb-2">
<span className="mr-1 font-semibold text-grey-900">{siteTitle}</span> <span className="mr-1 font-semibold text-grey-900 dark:text-grey-300">{siteTitle}</span>
<span className="text-grey-700">&#183; 2h</span> <span className="text-grey-700">&#183; 2h</span>
</div> </div>
<div className="mb-2 h-3 w-full rounded bg-grey-200"></div> <div className="mb-2 h-3 w-full rounded bg-grey-200 dark:bg-grey-900"></div>
<div className="mb-4 h-3 w-3/5 rounded bg-grey-200"></div> <div className="mb-4 h-3 w-3/5 rounded bg-grey-200 dark:bg-grey-900"></div>
<SettingGroupContent className="overflow-hidden rounded-md border border-grey-300"> <SettingGroupContent className="overflow-hidden rounded-md border border-grey-300 dark:border-grey-900">
<ImageUpload <ImageUpload
fileUploadClassName='flex cursor-pointer items-center justify-center rounded rounded-b-none border border-grey-100 border-b-0 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black' fileUploadClassName='flex cursor-pointer items-center justify-center rounded rounded-b-none border border-grey-100 border-b-0 bg-grey-75 p-3 text-sm font-semibold text-grey-800 hover:text-black dark:border-grey-900'
height='300px' height='300px'
id='twitter-image' id='twitter-image'
imageURL={twitterImage} imageURL={twitterImage}

View file

@ -11,7 +11,7 @@ import {useGlobalData} from '../../providers/GlobalDataProvider';
const StripeConnectedButton: React.FC<{className?: string; onClick: () => void;}> = ({className, onClick}) => { const StripeConnectedButton: React.FC<{className?: string; onClick: () => void;}> = ({className, onClick}) => {
className = clsx( className = clsx(
'group flex shrink-0 items-center justify-center whitespace-nowrap rounded border border-grey-300 px-3 py-1.5 text-sm font-semibold text-grey-900 transition-all hover:border-grey-500', 'group flex shrink-0 items-center justify-center whitespace-nowrap rounded border border-grey-300 px-3 py-1.5 text-sm font-semibold text-grey-900 transition-all hover:border-grey-500 dark:border-grey-900 dark:text-white',
className className
); );
return ( return (

View file

@ -75,9 +75,9 @@ const TipsOrDonations: React.FC<{ keywords: string[] }> = ({keywords}) => {
<Heading level={6}>Shareable link &mdash;</Heading> <Heading level={6}>Shareable link &mdash;</Heading>
<button className='text-2xs font-semibold uppercase tracking-wider text-green' type="button" onClick={openPreview}>Preview</button> <button className='text-2xs font-semibold uppercase tracking-wider text-green' type="button" onClick={openPreview}>Preview</button>
</div> </div>
<div className='w-100 group relative -m-1 mt-0 overflow-hidden rounded p-1 hover:bg-grey-50'> <div className='w-100 group relative -m-1 mt-0 overflow-hidden rounded p-1 hover:bg-grey-50 dark:hover:bg-grey-900'>
{donateUrl} {donateUrl}
<div className='invisible absolute right-0 top-[50%] flex translate-y-[-50%] gap-1 bg-white pl-1 group-hover:visible'> <div className='invisible absolute right-0 top-[50%] flex translate-y-[-50%] gap-1 bg-white pl-1 group-hover:visible dark:bg-black'>
<Button color='outline' label={copied ? 'Copied' : 'Copy'} size='sm' onClick={copyDonateUrl} /> <Button color='outline' label={copied ? 'Copied' : 'Copy'} size='sm' onClick={copyDonateUrl} />
</div> </div>
</div> </div>

View file

@ -16,7 +16,7 @@ interface TierCardProps {
tier: Tier; tier: Tier;
} }
const cardContainerClasses = 'tablet:group flex min-[900px]:min-h-[200px] flex-col items-start justify-between gap-4 self-stretch rounded-sm border border-grey-300 p-4 transition-all hover:border-grey-400'; const cardContainerClasses = 'group/tiercard flex min-[900px]:min-h-[200px] flex-col items-start justify-between gap-4 self-stretch rounded-sm border border-grey-300 p-4 transition-all hover:border-grey-400 dark:border-grey-900 dark:hover:border-grey-700';
const TierCard: React.FC<TierCardProps> = ({tier}) => { const TierCard: React.FC<TierCardProps> = ({tier}) => {
const {updateRoute} = useRouting(); const {updateRoute} = useRouting();
@ -41,11 +41,11 @@ const TierCard: React.FC<TierCardProps> = ({tier}) => {
</div> </div>
{tier.monthly_price && ( {tier.monthly_price && (
tier.active ? tier.active ?
<Button className='group group-hover:opacity-100 tablet:opacity-0' color='red' label='Archive' link onClick={() => { <Button className='group group-hover/tiercard:opacity-100 tablet:opacity-0' color='red' label='Archive' link onClick={() => {
updateTier({...tier, active: false}); updateTier({...tier, active: false});
}}/> }}/>
: :
<Button className='group group-hover:opacity-100 tablet:opacity-0' color='green' label='Activate' link onClick={() => { <Button className='group group-hover/tiercard:opacity-100 tablet:opacity-0' color='green' label='Activate' link onClick={() => {
updateTier({...tier, active: true}); updateTier({...tier, active: true});
}}/> }}/>
)} )}

View file

@ -61,4 +61,8 @@ html, body, #root {
width: 100%; width: 100%;
height: 100%; height: 100%;
letter-spacing: unset; letter-spacing: unset;
}
.dark {
color: #FAFAFB;
} }

View file

@ -3,6 +3,7 @@ module.exports = {
corePlugins: { corePlugins: {
preflight: false // we're providing our own scoped CSS reset preflight: false // we're providing our own scoped CSS reset
}, },
darkMode: 'class',
important: '.admin-x-settings', important: '.admin-x-settings',
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: { theme: {
@ -30,7 +31,8 @@ module.exports = {
600: '#95A1AD', 600: '#95A1AD',
700: '#7C8B9A', 700: '#7C8B9A',
800: '#626D79', 800: '#626D79',
900: '#394047' 900: '#394047',
950: '#222427'
}, },
green: { green: {
DEFAULT: '#30CF43', DEFAULT: '#30CF43',

View file

@ -297,6 +297,7 @@ export default class AdminXSettings extends Component {
officialThemes={officialThemes} officialThemes={officialThemes}
zapierTemplates={zapierTemplates} zapierTemplates={zapierTemplates}
externalNavigate={this.externalNavigate} externalNavigate={this.externalNavigate}
darkMode={this.feature.nightShift}
/> />
</Suspense> </Suspense>
</ErrorHandler> </ErrorHandler>