fix: replace dark mode hack with bootstrap's own dark mode

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-05-31 18:36:47 +02:00
parent 3f42798965
commit 0993372290
77 changed files with 244 additions and 365 deletions

View file

@ -1,30 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
body.dark {
@import "variables.dark";
/* redefine theme color variables */
@each $color, $value in $theme-colors {
--#{$prefix}#{$color}: #{$value};
}
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value");
@each $color, $value in $theme-colors-rgb {
--#{$prefix}#{$color}-rgb: #{$value};
}
--#{$prefix}body-color: #{$body-color};
--#{$prefix}body-bg: #{$body-bg};
@import "colors-only-bootstrap/bootstrap";
.btn-close {
filter: invert(1);
}
}

View file

@ -6,6 +6,6 @@
@import '~highlight.js/styles/github'; @import '~highlight.js/styles/github';
body.dark { [data-bs-theme="dark"] {
@import '~highlight.js/styles/github-dark'; @import '~highlight.js/styles/github-dark';
} }

View file

@ -26,10 +26,6 @@
cursor: pointer; cursor: pointer;
} }
body {
background-color: $dark;
}
*:focus { *:focus {
outline: 0 !important; outline: 0 !important;
} }

View file

@ -1,134 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
$white: #fff;
$gray-100: #f8f9fa;
$gray-200: #ebebeb;
$gray-300: #dee2e6;
$gray-400: #ced4da;
$gray-500: #adb5bd;
$gray-600: #888;
$gray-700: #444;
$gray-800: #303030;
$gray-900: #222;
$black: #000;
$blue: #337ab7;
$indigo: #6610f2;
$purple: #6f42c1;
$pink: #e83e8c;
$red: #e74c3c;
$orange: #fd7e14;
$yellow: #f39c12;
$green: #00bc8c;
$teal: #20c997;
$cyan: #5EB7E0;
$primary: $blue;
$secondary: $white;
$success: $green;
$info: $cyan;
$warning: $yellow;
$danger: $red;
$light: $gray-900;
$dark: $white;
$yiq-contrasted-threshold: 175;
// Body
$body-bg: $light;
$body-color: $dark;
// Links
$link-color: $cyan;
// Fonts
$text-muted: $gray-400;
// Tables
$table-accent-bg: $gray-800;
$table-border-color: $gray-700;
// Forms
$input-border-color: $body-bg;
$input-group-addon-color: $gray-500;
$input-group-addon-bg: $gray-700;
$input-bg: $gray-700;
$input-placeholder-color: $gray-500;
$input-color: $white;
$input-disabled-bg: $gray-900;
$custom-file-color: $gray-500;
$custom-file-border-color: $body-bg;
// Dropdowns
$dropdown-bg: $gray-900;
$dropdown-border-color: $gray-700;
$dropdown-divider-bg: $gray-700;
$dropdown-link-color: $white;
$dropdown-link-hover-color: $white;
$dropdown-link-hover-bg: $primary;
// Navs
$nav-link-disabled-color: $gray-500;
$nav-tabs-border-color: $gray-700;
$nav-tabs-link-hover-border-color: $nav-tabs-border-color $nav-tabs-border-color transparent;
$nav-tabs-link-active-color: $white;
$nav-tabs-link-active-border-color: $nav-tabs-border-color $nav-tabs-border-color transparent;
// Navbar
$navbar-dark-color: rgba($white, .6);
$navbar-dark-hover-color: $white;
$navbar-light-color: rgba($gray-900, .7);
$navbar-light-hover-color: $gray-900;
$navbar-light-active-color: $gray-900;
$navbar-light-toggler-border-color: rgba($gray-900, .1);
// Jumbotron
$jumbotron-bg: $gray-800;
// Cards
$card-cap-bg: $gray-700;
$card-bg: $gray-800;
// Popovers
$popover-bg: $gray-800;
$popover-header-bg: $gray-700;
// Toasts
$toast-background-color: $gray-700;
$toast-header-background-color: $gray-800;
// Modals
$modal-content-color: $dark;
$modal-content-bg: $gray-800;
$modal-content-border-color: $gray-700;
$modal-header-border-color: $gray-700;
// Progress bars
$progress-bg: $gray-700;
// List group
$list-group-bg: $gray-800;
$list-group-border-color: $gray-700;
$list-group-hover-bg: $gray-700;
$list-group-action-hover-color: $white;
// Breadcrumbs
$breadcrumb-bg: $gray-700;
// Close
$close-color: $white;
$close-text-shadow: none;
// Code
$pre-color: $dark;
$list-group-color: $dark;
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/variables";

View file

@ -7,10 +7,3 @@
$blue: #337ab7 !default; $blue: #337ab7 !default;
$cyan: #5EB7E0 !default; $cyan: #5EB7E0 !default;
$dark: #222222 !default; $dark: #222222 !default;
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/variables";
$toast-background-color: $white;

View file

@ -21,7 +21,7 @@ export interface LoadingScreenProps {
*/ */
export const LoadingScreen: React.FC<LoadingScreenProps> = ({ errorMessage }) => { export const LoadingScreen: React.FC<LoadingScreenProps> = ({ errorMessage }) => {
return ( return (
<div className={`${styles.loader} text-light`}> <div className={`${styles.loader} text-light bg-dark`}>
<div className='mb-3 text-light'> <div className='mb-3 text-light'>
<span className={`d-block`}> <span className={`d-block`}>
<LoadingAnimation error={!!errorMessage} /> <LoadingAnimation error={!!errorMessage} />

View file

@ -38,7 +38,6 @@ export const DisplayNameField: React.FC<DisplayNameFieldProps> = ({ onChange, va
isValid={isValid} isValid={isValid}
onChange={onChange} onChange={onChange}
placeholder={t('profile.displayName') ?? undefined} placeholder={t('profile.displayName') ?? undefined}
className='bg-dark text-light'
autoComplete='name' autoComplete='name'
required required
/> />

View file

@ -33,7 +33,6 @@ export const NewPasswordField: React.FC<CommonFieldProps> = ({ onChange, value,
isInvalid={hasError} isInvalid={hasError}
onChange={onChange} onChange={onChange}
placeholder={t('login.auth.password') ?? undefined} placeholder={t('login.auth.password') ?? undefined}
className='bg-dark text-light'
autoComplete='new-password' autoComplete='new-password'
required required
/> />

View file

@ -47,7 +47,6 @@ export const PasswordAgainField: React.FC<PasswordAgainFieldProps> = ({
isValid={isValid} isValid={isValid}
onChange={onChange} onChange={onChange}
placeholder={t('login.register.passwordAgain') ?? undefined} placeholder={t('login.register.passwordAgain') ?? undefined}
className='bg-dark text-light'
autoComplete='new-password' autoComplete='new-password'
required required
/> />

View file

@ -33,7 +33,6 @@ export const UsernameField: React.FC<CommonFieldProps> = ({ onChange, value }) =
isValid={isValid} isValid={isValid}
onChange={onChange} onChange={onChange}
placeholder={t('login.auth.username') ?? undefined} placeholder={t('login.auth.username') ?? undefined}
className='bg-dark text-light'
autoComplete='username' autoComplete='username'
autoFocus={true} autoFocus={true}
required required

View file

@ -3,11 +3,11 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { AsyncLoadingBoundary } from '../../../components/common/async-loading-boundary/async-loading-boundary'
import { CopyToClipboardButton } from '../../../components/common/copyable/copy-to-clipboard-button/copy-to-clipboard-button'
import { concatCssClasses } from '../../../utils/concat-css-classes' import { concatCssClasses } from '../../../utils/concat-css-classes'
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute' import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
import { testId } from '../../../utils/test-id' import { testId } from '../../../utils/test-id'
import { AsyncLoadingBoundary } from '../async-loading-boundary/async-loading-boundary'
import { CopyToClipboardButton } from '../copyable/copy-to-clipboard-button/copy-to-clipboard-button'
import styles from './highlighted-code.module.scss' import styles from './highlighted-code.module.scss'
import { useAsyncHighlightJsImport } from './hooks/use-async-highlight-js-import' import { useAsyncHighlightJsImport } from './hooks/use-async-highlight-js-import'
import { useAttachLineNumbers } from './hooks/use-attach-line-numbers' import { useAttachLineNumbers } from './hooks/use-attach-line-numbers'

View file

@ -3,7 +3,7 @@
exports[`ExternalLink renders an external link correctly 1`] = ` exports[`ExternalLink renders an external link correctly 1`] = `
<div> <div>
<a <a
class="text-light" class=""
dir="auto" dir="auto"
href="https://example.com" href="https://example.com"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -17,7 +17,7 @@ exports[`ExternalLink renders an external link correctly 1`] = `
exports[`ExternalLink renders an external link with a title 1`] = ` exports[`ExternalLink renders an external link with a title 1`] = `
<div> <div>
<a <a
class="text-light" class=""
dir="auto" dir="auto"
href="https://example.com" href="https://example.com"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -46,7 +46,7 @@ exports[`ExternalLink renders an external link with additional className 1`] = `
exports[`ExternalLink renders an external link with an icon 1`] = ` exports[`ExternalLink renders an external link with an icon 1`] = `
<div> <div>
<a <a
class="text-light" class=""
dir="auto" dir="auto"
href="https://example.com" href="https://example.com"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -62,7 +62,7 @@ exports[`ExternalLink renders an external link with an icon 1`] = `
exports[`ExternalLink renders an external link with an id 1`] = ` exports[`ExternalLink renders an external link with an id 1`] = `
<div> <div>
<a <a
class="text-light" class=""
dir="auto" dir="auto"
href="https://example.com" href="https://example.com"
id="testId" id="testId"

View file

@ -3,7 +3,7 @@
exports[`InternalLink renders an internal link correctly 1`] = ` exports[`InternalLink renders an internal link correctly 1`] = `
<div> <div>
<a <a
class="text-light" class=""
href="/test" href="/test"
> >
testText testText
@ -14,7 +14,7 @@ exports[`InternalLink renders an internal link correctly 1`] = `
exports[`InternalLink renders an internal link with a title 1`] = ` exports[`InternalLink renders an internal link with a title 1`] = `
<div> <div>
<a <a
class="text-light" class=""
href="/test" href="/test"
title="testTitle" title="testTitle"
> >
@ -37,7 +37,7 @@ exports[`InternalLink renders an internal link with additional className 1`] = `
exports[`InternalLink renders an internal link with an icon 1`] = ` exports[`InternalLink renders an internal link with an icon 1`] = `
<div> <div>
<a <a
class="text-light" class=""
href="/test" href="/test"
> >
BootstrapIconMock_Heart BootstrapIconMock_Heart
@ -50,7 +50,7 @@ exports[`InternalLink renders an internal link with an icon 1`] = `
exports[`InternalLink renders an internal link with an id 1`] = ` exports[`InternalLink renders an internal link with an id 1`] = `
<div> <div>
<a <a
class="text-light" class=""
href="/test" href="/test"
id="testId" id="testId"
> >

View file

@ -3,7 +3,7 @@
exports[`TranslatedExternalLink renders with i18nKey 1`] = ` exports[`TranslatedExternalLink renders with i18nKey 1`] = `
<div> <div>
<a <a
class="text-light" class=""
dir="auto" dir="auto"
href="https://example.com" href="https://example.com"
rel="noopener noreferrer" rel="noopener noreferrer"

View file

@ -3,7 +3,7 @@
exports[`TranslatedInternalLink renders with i18nKey 1`] = ` exports[`TranslatedInternalLink renders with i18nKey 1`] = `
<div> <div>
<a <a
class="text-light" class=""
href="/test" href="/test"
> >
testi18nKey testi18nKey

View file

@ -19,14 +19,7 @@ import React from 'react'
* @param className Additional class names added to the link object * @param className Additional class names added to the link object
* @param title The title of the link * @param title The title of the link
*/ */
export const ExternalLink: React.FC<LinkWithTextProps> = ({ export const ExternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = '', title }) => {
href,
text,
icon,
id,
className = 'text-light',
title
}) => {
return ( return (
<a href={href} target='_blank' rel='noopener noreferrer' id={id} className={className} title={title} dir='auto'> <a href={href} target='_blank' rel='noopener noreferrer' id={id} className={className} title={title} dir='auto'>
<UiIcon icon={icon} nbsp={true} /> <UiIcon icon={icon} nbsp={true} />

View file

@ -19,14 +19,7 @@ import React from 'react'
* @param className Additional class names added to the link object * @param className Additional class names added to the link object
* @param title The title of the link * @param title The title of the link
*/ */
export const InternalLink: React.FC<LinkWithTextProps> = ({ export const InternalLink: React.FC<LinkWithTextProps> = ({ href, text, icon, id, className = '', title }) => {
href,
text,
icon,
id,
className = 'text-light',
title
}) => {
return ( return (
<Link href={href} className={className} id={id} title={title}> <Link href={href} className={className} id={id} title={title}>
<UiIcon icon={icon} nbsp={true} /> <UiIcon icon={icon} nbsp={true} />

View file

@ -4,7 +4,7 @@ exports[`CommonModal does not render if show is false 1`] = `<div />`;
exports[`CommonModal render correctly in size lg 1`] = ` exports[`CommonModal render correctly in size lg 1`] = `
<div <div
class="modal-dialog text-dark modal-lg" class="modal-dialog modal-lg"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -26,7 +26,7 @@ exports[`CommonModal render correctly in size lg 1`] = `
exports[`CommonModal render correctly in size sm 1`] = ` exports[`CommonModal render correctly in size sm 1`] = `
<div <div
class="modal-dialog text-dark modal-sm" class="modal-dialog modal-sm"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -48,7 +48,7 @@ exports[`CommonModal render correctly in size sm 1`] = `
exports[`CommonModal render correctly in size xl 1`] = ` exports[`CommonModal render correctly in size xl 1`] = `
<div <div
class="modal-dialog text-dark modal-xl" class="modal-dialog modal-xl"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -70,7 +70,7 @@ exports[`CommonModal render correctly in size xl 1`] = `
exports[`CommonModal render correctly with additionalClasses 1`] = ` exports[`CommonModal render correctly with additionalClasses 1`] = `
<div <div
class="modal-dialog text-dark testClass" class="modal-dialog testClass"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -92,7 +92,7 @@ exports[`CommonModal render correctly with additionalClasses 1`] = `
exports[`CommonModal render correctly with i18nTitle 1`] = ` exports[`CommonModal render correctly with i18nTitle 1`] = `
<div <div
class="modal-dialog text-dark" class="modal-dialog"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -114,7 +114,7 @@ exports[`CommonModal render correctly with i18nTitle 1`] = `
exports[`CommonModal render correctly with title 1`] = ` exports[`CommonModal render correctly with title 1`] = `
<div <div
class="modal-dialog text-dark" class="modal-dialog"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -136,7 +136,7 @@ exports[`CommonModal render correctly with title 1`] = `
exports[`CommonModal render correctly with title icon 1`] = ` exports[`CommonModal render correctly with title icon 1`] = `
<div <div
class="modal-dialog text-dark" class="modal-dialog"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -160,7 +160,7 @@ exports[`CommonModal render correctly with title icon 1`] = `
exports[`CommonModal renders correctly and calls onHide, when close button is clicked 1`] = ` exports[`CommonModal renders correctly and calls onHide, when close button is clicked 1`] = `
<div <div
class="modal-dialog text-dark" class="modal-dialog"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div

View file

@ -2,7 +2,7 @@
exports[`DeletionModal disables deletion when user is not owner 1`] = ` exports[`DeletionModal disables deletion when user is not owner 1`] = `
<div <div
class="modal-dialog text-dark" class="modal-dialog"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -23,7 +23,7 @@ exports[`DeletionModal disables deletion when user is not owner 1`] = `
/> />
</div> </div>
<div <div
class="text-dark modal-body" class="modal-body"
> >
testText testText
</div> </div>
@ -44,7 +44,7 @@ exports[`DeletionModal disables deletion when user is not owner 1`] = `
exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = ` exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = `
<div <div
class="modal-dialog text-dark" class="modal-dialog"
data-testid="commonModal" data-testid="commonModal"
> >
<div <div
@ -65,7 +65,7 @@ exports[`DeletionModal renders correctly with deletionButtonI18nKey 1`] = `
/> />
</div> </div>
<div <div
class="text-dark modal-body" class="modal-body"
> >
testText testText
</div> </div>

View file

@ -74,7 +74,7 @@ export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
onHide={onHide} onHide={onHide}
animation={true} animation={true}
{...testId('commonModal')} {...testId('commonModal')}
dialogClassName={concatCssClasses('text-dark', additionalClasses)} dialogClassName={concatCssClasses(additionalClasses)}
size={modalSize}> size={modalSize}>
<Modal.Header closeButton={!!showCloseButton}> <Modal.Header closeButton={!!showCloseButton}>
<Modal.Title> <Modal.Title>

View file

@ -51,7 +51,7 @@ export const DeletionModal: React.FC<PropsWithChildren<DeletionModalProps>> = ({
titleIcon={titleIcon} titleIcon={titleIcon}
showCloseButton={true} showCloseButton={true}
{...props}> {...props}>
<Modal.Body className='text-dark'>{children}</Modal.Body> <Modal.Body>{children}</Modal.Body>
<Modal.Footer> <Modal.Footer>
<Button {...cypressId('deletionModal.confirmButton')} variant='danger' onClick={onConfirm} disabled={!isOwner}> <Button {...cypressId('deletionModal.confirmButton')} variant='danger' onClick={onConfirm} disabled={!isOwner}>
<Trans i18nKey={deletionButtonI18nKey} /> <Trans i18nKey={deletionButtonI18nKey} />

View file

@ -14,7 +14,7 @@ exports[`motd modal renders a modal if a motd was fetched and can dismiss it 1`]
This is a mock implementation of a Modal: This is a mock implementation of a Modal:
<dialog> <dialog>
<div <div
class="bg-light modal-body" class="modal-body"
> >
<span <span
data-testid="motd-renderer" data-testid="motd-renderer"

View file

@ -53,7 +53,7 @@ export const MotdModal: React.FC = () => {
show={!!lines && !loading && !error && !dismissed} show={!!lines && !loading && !error && !dismissed}
titleI18nKey={'motd.title'} titleI18nKey={'motd.title'}
{...cypressId('motd-modal')}> {...cypressId('motd-modal')}>
<Modal.Body className={'bg-light'}> <Modal.Body>
<EditorToRendererCommunicatorContextProvider> <EditorToRendererCommunicatorContextProvider>
<RendererIframe <RendererIframe
frameClasses={'w-100'} frameClasses={'w-100'}

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useApplicationState } from '../../../../hooks/common/use-application-state' import { useApplicationState } from '../../../../hooks/common/use-application-state'
import { DarkModePreference } from '../../../../redux/dark-mode/types'
import { useSendToRenderer } from '../../../render-page/window-post-message-communicator/hooks/use-send-to-renderer' import { useSendToRenderer } from '../../../render-page/window-post-message-communicator/hooks/use-send-to-renderer'
import { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message' import { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message'
import { useMemo } from 'react' import { useMemo } from 'react'
@ -13,27 +12,19 @@ import { useMemo } from 'react'
* Sends additional configuration options (dark mode, line break, etc.) to the renderer. * Sends additional configuration options (dark mode, line break, etc.) to the renderer.
* *
* @param rendererReady Defines if the target renderer is ready * @param rendererReady Defines if the target renderer is ready
* @param forcedDarkMode Overwrites the value from the global application states if set.
*/ */
export const useSendAdditionalConfigurationToRenderer = ( export const useSendAdditionalConfigurationToRenderer = (rendererReady: boolean): void => {
rendererReady: boolean,
forcedDarkMode: DarkModePreference = DarkModePreference.AUTO
): void => {
const darkModePreference = useApplicationState((state) => state.darkMode.darkModePreference) const darkModePreference = useApplicationState((state) => state.darkMode.darkModePreference)
const newlinesAreBreaks = useApplicationState((state) => state.noteDetails.frontmatter.newlinesAreBreaks) const newlinesAreBreaks = useApplicationState((state) => state.noteDetails.frontmatter.newlinesAreBreaks)
const darkMode = useMemo(() => {
return forcedDarkMode === DarkModePreference.AUTO ? darkModePreference : forcedDarkMode
}, [darkModePreference, forcedDarkMode])
useSendToRenderer( useSendToRenderer(
useMemo( useMemo(
() => ({ () => ({
type: CommunicationMessageType.SET_ADDITIONAL_CONFIGURATION, type: CommunicationMessageType.SET_ADDITIONAL_CONFIGURATION,
darkModePreference: darkMode, darkModePreference: darkModePreference,
newLinesAreBreaks: newlinesAreBreaks newLinesAreBreaks: newlinesAreBreaks
}), }),
[darkMode, newlinesAreBreaks] [darkModePreference, newlinesAreBreaks]
), ),
rendererReady rendererReady
) )

View file

@ -3,7 +3,6 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { DarkModePreference } from '../../../redux/dark-mode/types'
import { concatCssClasses } from '../../../utils/concat-css-classes' import { concatCssClasses } from '../../../utils/concat-css-classes'
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute' import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
import { Logger } from '../../../utils/logger' import { Logger } from '../../../utils/logger'
@ -27,11 +26,11 @@ import { useForceRenderPageUrlOnIframeLoadCallback } from './hooks/use-force-ren
import { useSendAdditionalConfigurationToRenderer } from './hooks/use-send-additional-configuration-to-renderer' import { useSendAdditionalConfigurationToRenderer } from './hooks/use-send-additional-configuration-to-renderer'
import { useSendMarkdownToRenderer } from './hooks/use-send-markdown-to-renderer' import { useSendMarkdownToRenderer } from './hooks/use-send-markdown-to-renderer'
import { useSendScrollState } from './hooks/use-send-scroll-state' import { useSendScrollState } from './hooks/use-send-scroll-state'
import styles from './style.module.scss'
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react' import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
export interface RendererIframeProps extends Omit<CommonMarkdownRendererProps & ScrollProps, 'baseUrl'> { export interface RendererIframeProps extends Omit<CommonMarkdownRendererProps & ScrollProps, 'baseUrl'> {
rendererType: RendererType rendererType: RendererType
forcedDarkMode?: DarkModePreference
frameClasses?: string frameClasses?: string
onRendererStatusChange?: undefined | ((rendererReady: boolean) => void) onRendererStatusChange?: undefined | ((rendererReady: boolean) => void)
adaptFrameHeightToContent?: boolean adaptFrameHeightToContent?: boolean
@ -51,7 +50,6 @@ const log = new Logger('RendererIframe')
* @param onMakeScrollSource Callback that is fired when the renderer requests to be set as the current scroll source * @param onMakeScrollSource Callback that is fired when the renderer requests to be set as the current scroll source
* @param frameClasses CSS classes that should be applied to the iframe * @param frameClasses CSS classes that should be applied to the iframe
* @param rendererType The {@link RendererType type} of the renderer to use. * @param rendererType The {@link RendererType type} of the renderer to use.
* @param forcedDarkMode If set, the dark mode will be set to the given value. Otherwise, the dark mode won't be changed.
* @param adaptFrameHeightToContent If set, the iframe height will be adjusted to the content height * @param adaptFrameHeightToContent If set, the iframe height will be adjusted to the content height
* @param onRendererStatusChange Callback that is fired when the renderer in the iframe is ready * @param onRendererStatusChange Callback that is fired when the renderer in the iframe is ready
*/ */
@ -62,7 +60,6 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
onMakeScrollSource, onMakeScrollSource,
frameClasses, frameClasses,
rendererType, rendererType,
forcedDarkMode,
adaptFrameHeightToContent, adaptFrameHeightToContent,
onRendererStatusChange onRendererStatusChange
}) => { }) => {
@ -142,7 +139,7 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
) )
useEffectOnRenderTypeChange(rendererType, onIframeLoad) useEffectOnRenderTypeChange(rendererType, onIframeLoad)
useSendAdditionalConfigurationToRenderer(rendererReady, forcedDarkMode) useSendAdditionalConfigurationToRenderer(rendererReady)
useSendMarkdownToRenderer(markdownContentLines, rendererReady) useSendMarkdownToRenderer(markdownContentLines, rendererReady)
useSendScrollState(scrollState, rendererReady) useSendScrollState(scrollState, rendererReady)
@ -169,7 +166,7 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
allowFullScreen={true} allowFullScreen={true}
ref={frameReference} ref={frameReference}
referrerPolicy={'no-referrer'} referrerPolicy={'no-referrer'}
className={concatCssClasses('border-0', frameClasses)} className={concatCssClasses('border-0', styles.frame, frameClasses)}
allow={'clipboard-write'} allow={'clipboard-write'}
{...cypressAttribute('renderer-ready', rendererReady ? 'true' : 'false')} {...cypressAttribute('renderer-ready', rendererReady ? 'true' : 'false')}
{...cypressAttribute('renderer-type', rendererType)} {...cypressAttribute('renderer-type', rendererType)}

View file

@ -0,0 +1,9 @@
/*!
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.frame {
color-scheme: initial;
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useApplicationState } from '../../../hooks/common/use-application-state' import { useApplicationState } from '../../../hooks/common/use-application-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { NewNoteButton } from '../../common/new-note-button/new-note-button' import { NewNoteButton } from '../../common/new-note-button/new-note-button'
import { ShowIf } from '../../common/show-if/show-if' import { ShowIf } from '../../common/show-if/show-if'
import { SignInButton } from '../../landing-layout/navigation/sign-in-button' import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
@ -35,9 +36,10 @@ export interface AppBarProps {
export const AppBar: React.FC<AppBarProps> = ({ mode }) => { export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
const userExists = useApplicationState((state) => !!state.user) const userExists = useApplicationState((state) => !!state.user)
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter) const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
const buttonVariant = useOutlineButtonVariant()
return ( return (
<Navbar expand={true} className={'bg-light px-3'}> <Navbar expand={true} className={'px-3'}>
<Nav className='me-auto d-flex align-items-center'> <Nav className='me-auto d-flex align-items-center'>
<NavbarBranding /> <NavbarBranding />
<ShowIf condition={mode === AppBarMode.EDITOR}> <ShowIf condition={mode === AppBarMode.EDITOR}>
@ -52,7 +54,7 @@ export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
</ShowIf> </ShowIf>
</Nav> </Nav>
<Nav className='d-flex gap-2 align-items-center text-secondary justify-content-end'> <Nav className='d-flex gap-2 align-items-center text-secondary justify-content-end'>
<SettingsButton variant={'outline-dark'} /> <SettingsButton variant={buttonVariant} />
<NewNoteButton /> <NewNoteButton />
<ShowIf condition={!userExists}> <ShowIf condition={!userExists}>
<SignInButton size={'sm'} /> <SignInButton size={'sm'} />

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useBooleanState } from '../../../../hooks/common/use-boolean-state' import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
import { useOutlineButtonVariant } from '../../../../hooks/dark-mode/use-outline-button-variant'
import { cypressId } from '../../../../utils/cypress-attribute' import { cypressId } from '../../../../utils/cypress-attribute'
import { CommonModal } from '../../../common/modals/common-modal' import { CommonModal } from '../../../common/modals/common-modal'
import { CheatsheetContent } from './cheatsheet-content' import { CheatsheetContent } from './cheatsheet-content'
@ -18,6 +19,7 @@ import { Trans, useTranslation } from 'react-i18next'
export const CheatsheetButton: React.FC = () => { export const CheatsheetButton: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const [modalVisibility, showModal, closeModal] = useBooleanState() const [modalVisibility, showModal, closeModal] = useBooleanState()
const buttonVariant = useOutlineButtonVariant()
return ( return (
<Fragment> <Fragment>
@ -25,7 +27,7 @@ export const CheatsheetButton: React.FC = () => {
{...cypressId('open.cheatsheet-button')} {...cypressId('open.cheatsheet-button')}
title={t('cheatsheet.button') ?? undefined} title={t('cheatsheet.button') ?? undefined}
className={'mx-2'} className={'mx-2'}
variant='outline-dark' variant={buttonVariant}
size={'sm'} size={'sm'}
onClick={showModal}> onClick={showModal}>
<Trans i18nKey={'cheatsheet.button'}></Trans> <Trans i18nKey={'cheatsheet.button'}></Trans>

View file

@ -3,6 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useOutlineButtonVariant } from '../../../../hooks/dark-mode/use-outline-button-variant'
import { IconButton } from '../../../common/icon-button/icon-button' import { IconButton } from '../../../common/icon-button/icon-button'
import type { MouseEvent } from 'react' import type { MouseEvent } from 'react'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
@ -28,6 +29,8 @@ export const CheatsheetInNewTabButton: React.FC<CheatsheetInNewTabButtonProps> =
const { t } = useTranslation() const { t } = useTranslation()
const buttonVariant = useOutlineButtonVariant()
return ( return (
<IconButton <IconButton
size={'sm'} size={'sm'}
@ -36,7 +39,7 @@ export const CheatsheetInNewTabButton: React.FC<CheatsheetInNewTabButtonProps> =
onClick={openPopUp} onClick={openPopUp}
icon={BoxArrowUpRight} icon={BoxArrowUpRight}
className={'p-2 border-0'} className={'p-2 border-0'}
variant={'outline-dark'} variant={buttonVariant}
target={'_blank'} target={'_blank'}
title={t('cheatsheet.modal.popup') ?? ''} title={t('cheatsheet.modal.popup') ?? ''}
/> />

View file

@ -23,7 +23,7 @@ export const ReadMoreLinkItem: React.FC<ReadMoreLinkGroupProps> = ({ url }) => {
<h4> <h4>
<Trans i18nKey={'cheatsheet.modal.headlines.readMoreLink'} /> <Trans i18nKey={'cheatsheet.modal.headlines.readMoreLink'} />
</h4> </h4>
<ExternalLink className={'text-dark'} text={url.toString()} href={url.toString()}></ExternalLink> <ExternalLink text={url.toString()} href={url.toString()}></ExternalLink>
</ListGroupItem> </ListGroupItem>
) )
} }

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useBooleanState } from '../../../../hooks/common/use-boolean-state' import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
import { useOutlineButtonVariant } from '../../../../hooks/dark-mode/use-outline-button-variant'
import { cypressId } from '../../../../utils/cypress-attribute' import { cypressId } from '../../../../utils/cypress-attribute'
import { IconButton } from '../../../common/icon-button/icon-button' import { IconButton } from '../../../common/icon-button/icon-button'
import { HelpModal } from './help-modal' import { HelpModal } from './help-modal'
@ -17,6 +18,7 @@ import { Trans, useTranslation } from 'react-i18next'
export const HelpButton: React.FC = () => { export const HelpButton: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const [modalVisibility, showModal, closeModal] = useBooleanState() const [modalVisibility, showModal, closeModal] = useBooleanState()
const buttonVariant = useOutlineButtonVariant()
return ( return (
<Fragment> <Fragment>
@ -26,7 +28,7 @@ export const HelpButton: React.FC = () => {
title={t('editor.documentBar.help') ?? undefined} title={t('editor.documentBar.help') ?? undefined}
className='ms-2' className='ms-2'
size='sm' size='sm'
variant='outline-dark' variant={buttonVariant}
onClick={showModal}> onClick={showModal}>
<Trans i18nKey={'editor.documentBar.help'} /> <Trans i18nKey={'editor.documentBar.help'} />
</IconButton> </IconButton>

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useApplicationState } from '../../../hooks/common/use-application-state' import { useApplicationState } from '../../../hooks/common/use-application-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { UiIcon } from '../../common/icons/ui-icon' import { UiIcon } from '../../common/icons/ui-icon'
import Link from 'next/link' import Link from 'next/link'
import React from 'react' import React from 'react'
@ -17,14 +18,11 @@ import { useTranslation } from 'react-i18next'
export const ReadOnlyModeButton: React.FC = () => { export const ReadOnlyModeButton: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress) const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
const buttonVariant = useOutlineButtonVariant()
return ( return (
<Link href={`/s/${noteIdentifier}`} target='_blank'> <Link href={`/s/${noteIdentifier}`} target='_blank'>
<Button <Button title={t('editor.documentBar.readOnlyMode') ?? undefined} size='sm' variant={buttonVariant}>
title={t('editor.documentBar.readOnlyMode') ?? undefined}
className='ms-2 text-secondary'
size='sm'
variant='outline-light'>
<UiIcon icon={IconFileEarmarkTextFill} /> <UiIcon icon={IconFileEarmarkTextFill} />
</Button> </Button>
</Link> </Link>

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useApplicationState } from '../../../hooks/common/use-application-state' import { useApplicationState } from '../../../hooks/common/use-application-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { UiIcon } from '../../common/icons/ui-icon' import { UiIcon } from '../../common/icons/ui-icon'
import Link from 'next/link' import Link from 'next/link'
import React from 'react' import React from 'react'
@ -17,6 +18,7 @@ import { useTranslation } from 'react-i18next'
export const SlideModeButton: React.FC = () => { export const SlideModeButton: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress) const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
const buttonVariant = useOutlineButtonVariant()
return ( return (
<Link href={`/p/${noteIdentifier}`} target='_blank'> <Link href={`/p/${noteIdentifier}`} target='_blank'>
@ -24,7 +26,7 @@ export const SlideModeButton: React.FC = () => {
title={t('editor.documentBar.slideMode') ?? undefined} title={t('editor.documentBar.slideMode') ?? undefined}
className='ms-2 text-secondary' className='ms-2 text-secondary'
size='sm' size='sm'
variant='outline-light'> variant={buttonVariant}>
<UiIcon icon={IconTv} /> <UiIcon icon={IconTv} />
</Button> </Button>
</Link> </Link>

View file

@ -16,7 +16,7 @@ import React from 'react'
*/ */
export const StatusBar: React.FC = () => { export const StatusBar: React.FC = () => {
return ( return (
<div className={`d-flex flex-row border-secondary border-top small bg-light px-2`}> <div className={`d-flex flex-row border-secondary border-top small px-2`}>
<div> <div>
<CursorPositionInfo /> <CursorPositionInfo />
<SelectedCharacters /> <SelectedCharacters />

View file

@ -5,9 +5,7 @@
*/ */
.tooltip { .tooltip {
&, :global(body.dark) & {
--bs-popover-max-width: 100%; --bs-popover-max-width: 100%;
--bs-popover-body-padding-y: 0; --bs-popover-body-padding-y: 0;
--bs-popover-body-padding-x: 0; --bs-popover-body-padding-x: 0;
} }
}

View file

@ -5,9 +5,6 @@
*/ */
:global(.btn-toolbar).toolbar { :global(.btn-toolbar).toolbar {
border-bottom: 1px solid #ededed;
border-top: 1px solid #ededed;
:global(.btn) { :global(.btn) {
padding: 0.1875rem 0.5rem; padding: 0.1875rem 0.5rem;
min-width: 30px; min-width: 30px;

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useMayEdit } from '../../../../hooks/common/use-may-edit' import { useMayEdit } from '../../../../hooks/common/use-may-edit'
import { concatCssClasses } from '../../../../utils/concat-css-classes'
import { ShowIf } from '../../../common/show-if/show-if' import { ShowIf } from '../../../common/show-if/show-if'
import { BoldButton } from './buttons/bold-button' import { BoldButton } from './buttons/bold-button'
import { CheckListButton } from './buttons/check-list-button' import { CheckListButton } from './buttons/check-list-button'
@ -39,7 +40,7 @@ export const ToolBar: React.FC = () => {
return ( return (
<ShowIf condition={mayEdit}> <ShowIf condition={mayEdit}>
<ButtonToolbar className={`bg-light ${styles.toolbar}`}> <ButtonToolbar className={concatCssClasses(styles.toolbar, 'mb-2')}>
<ButtonGroup className={'mx-1 flex-wrap'}> <ButtonGroup className={'mx-1 flex-wrap'}>
<BoldButton /> <BoldButton />
<ItalicButton /> <ItalicButton />

View file

@ -41,7 +41,8 @@ export const ToolbarButton: React.FC<PropsWithChildren<ToolbarButtonProps>> = ({
return ( return (
<Button <Button
variant={'light'} variant={'outline-secondary'}
className={'text-body-emphasis'}
onClick={onClick} onClick={onClick}
title={title} title={title}
ref={buttonRef} ref={buttonRef}

View file

@ -18,7 +18,7 @@
padding: 0; padding: 0;
transition: height 0.2s, flex-basis 0.2s; transition: height 0.2s, flex-basis 0.2s;
overflow: hidden; overflow: hidden;
color: var(--bs-dark); color: var(--bs-emphasis-color);
&.hide { &.hide {
flex-basis: 0; flex-basis: 0;

View file

@ -7,7 +7,7 @@
.slide-sidebar { .slide-sidebar {
--sidebar-entry-height: 44px; --sidebar-entry-height: 44px;
--sidebar-menu-width: 280px; --sidebar-menu-width: 280px;
--sidebar-separator-color: rgba(0, 0, 0, 0.15); --sidebar-separator-color: var(--bs-secondary);
:global(body.dark) & { :global(body.dark) & {
--sidebar-separator-color: rgba(255, 255, 255, 0.2); --sidebar-separator-color: rgba(255, 255, 255, 0.2);
@ -35,6 +35,6 @@
left: calc(0px - var(--sidebar-menu-width) + var(--sidebar-entry-height)); left: calc(0px - var(--sidebar-menu-width) + var(--sidebar-entry-height));
} }
background: var(--bs-light); background: var(--bs-body-bg);
} }
} }

View file

@ -16,11 +16,11 @@ exports[`Splitter resize can change size with mouse 1`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -75,11 +75,11 @@ exports[`Splitter resize can change size with mouse 2`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -134,11 +134,11 @@ exports[`Splitter resize can change size with mouse 3`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -193,11 +193,11 @@ exports[`Splitter resize can change size with mouse 4`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -252,11 +252,11 @@ exports[`Splitter resize can change size with touch 1`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -311,11 +311,11 @@ exports[`Splitter resize can change size with touch 2`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -370,11 +370,11 @@ exports[`Splitter resize can change size with touch 3`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -429,11 +429,11 @@ exports[`Splitter resize can change size with touch 4`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"
@ -488,11 +488,11 @@ exports[`Splitter resize can react to shortcuts 1`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle shift-right" class="middle shift-right"
> >
<div <div
class="buttons" class="buttons"
@ -547,11 +547,11 @@ exports[`Splitter resize can react to shortcuts 2`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle shift-left" class="middle shift-left"
> >
<div <div
class="buttons" class="buttons"
@ -606,11 +606,11 @@ exports[`Splitter resize can react to shortcuts 3`] = `
</div> </div>
</div> </div>
<div <div
class="split-divider" class="divider"
data-testid="splitter-divider" data-testid="splitter-divider"
> >
<div <div
class="bg-light middle " class="middle"
> >
<div <div
class="buttons" class="buttons"

View file

@ -4,19 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
.split-divider { .divider {
width: 15px; width: 15px;
background: white; background: var(--bs-body-bg);
z-index: 1; z-index: 1;
box-shadow: 3px 0 6px #e7e7e7; box-shadow: 0 0 6px var(--bs-gray-400);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
:global(body.dark) & {
box-shadow: 3px 0 6px #7b7b7b;
}
} }
.grabber { .grabber {
@ -29,7 +25,8 @@
z-index: 100000; z-index: 100000;
position: absolute; position: absolute;
border-radius: 90px; border-radius: 90px;
border: solid 1px #d5d5d5; border: solid 1px var(--bs-gray-500);
background: var(--bs-body-bg);
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View file

@ -3,10 +3,11 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { concatCssClasses } from '../../../../utils/concat-css-classes'
import { testId } from '../../../../utils/test-id' import { testId } from '../../../../utils/test-id'
import { UiIcon } from '../../../common/icons/ui-icon' import { UiIcon } from '../../../common/icons/ui-icon'
import styles from './split-divider.module.scss' import styles from './split-divider.module.scss'
import React from 'react' import React, { useMemo } from 'react'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import { ArrowLeftRight as IconArrowLeftRight } from 'react-bootstrap-icons' import { ArrowLeftRight as IconArrowLeftRight } from 'react-bootstrap-icons'
import { ArrowLeft as IconArrowLeft } from 'react-bootstrap-icons' import { ArrowLeft as IconArrowLeft } from 'react-bootstrap-icons'
@ -49,12 +50,17 @@ export const SplitDivider: React.FC<SplitDividerProps> = ({
focusRight, focusRight,
forceOpen forceOpen
}) => { }) => {
const shiftClass = dividerButtonsShift == '' ? '' : styles[dividerButtonsShift] const className = useMemo(() => {
return concatCssClasses(styles.middle, {
[styles.open]: forceOpen,
[styles[dividerButtonsShift]]: dividerButtonsShift !== ''
})
}, [dividerButtonsShift, forceOpen])
return ( return (
<div className={`${styles['split-divider']}`} {...testId('splitter-divider')}> <div className={styles.divider} {...testId('splitter-divider')}>
<div className={`bg-light ${styles['middle']} ${forceOpen ? styles['open'] : ''} ${shiftClass}`}> <div className={className}>
<div className={styles['buttons']}> <div className={styles.buttons}>
<Button variant={focusLeft ? 'secondary' : 'light'} onClick={onLeftButtonClick}> <Button variant={focusLeft ? 'secondary' : 'light'} onClick={onLeftButtonClick}>
<UiIcon icon={IconArrowLeft} /> <UiIcon icon={IconArrowLeft} />
</Button> </Button>

View file

@ -51,7 +51,7 @@ export const EntryMenu: React.FC<EntryMenuProps> = ({
return ( return (
<Dropdown className={`d-inline-flex ${className || ''}`} {...cypressId('history-entry-menu')}> <Dropdown className={`d-inline-flex ${className || ''}`} {...cypressId('history-entry-menu')}>
<Dropdown.Toggle <Dropdown.Toggle
variant={'light'} variant={'secondary'}
id={`dropdown-card-${id}`} id={`dropdown-card-${id}`}
className={`no-arrow ${styles['history-menu']} d-inline-flex align-items-center`}> className={`no-arrow ${styles['history-menu']} d-inline-flex align-items-center`}>
<UiIcon icon={IconThreeDots} /> <UiIcon icon={IconThreeDots} />

View file

@ -3,6 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useDarkModeState } from '../../../hooks/dark-mode/use-dark-mode-state'
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute' import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
import { UiIcon } from '../../common/icons/ui-icon' import { UiIcon } from '../../common/icons/ui-icon'
import { EntryMenu } from '../entry-menu/entry-menu' import { EntryMenu } from '../entry-menu/entry-menu'
@ -45,16 +46,19 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
const entryTitle = useHistoryEntryTitle(entry) const entryTitle = useHistoryEntryTitle(entry)
const darkModeState = useDarkModeState()
const tags = useMemo( const tags = useMemo(
() => () =>
entry.tags.map((tag) => ( entry.tags.map((tag) => {
<Badge className={'bg-dark me-1 mb-1'} key={tag}> return (
<Badge className={'bg-secondary text-light me-1 mb-1'} key={tag}>
{tag} {tag}
</Badge> </Badge>
)), )
}),
[entry.tags] [entry.tags]
) )
const lastVisited = useMemo(() => formatHistoryDate(entry.lastVisitedAt), [entry.lastVisitedAt]) const lastVisited = useMemo(() => formatHistoryDate(entry.lastVisitedAt), [entry.lastVisitedAt])
return ( return (
@ -62,18 +66,18 @@ export const HistoryCard: React.FC<HistoryEntryProps & HistoryEventHandlers> = (
className='p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4' className='p-2 col-xs-12 col-sm-6 col-md-6 col-lg-4'
{...cypressId('history-card')} {...cypressId('history-card')}
{...cypressAttribute('card-title', entryTitle)}> {...cypressAttribute('card-title', entryTitle)}>
<Card className={styles['card-min-height']} text={'dark'} bg={'light'}> <Card className={`${styles['card-min-height']}`} bg={darkModeState ? 'dark' : 'light'}>
<Card.Body className='p-2 d-flex flex-row justify-content-between'> <Card.Body className='p-2 d-flex flex-row justify-content-between'>
<div className={'d-flex flex-column'}> <div className={'d-flex flex-column'}>
<PinButton isDark={false} isPinned={entry.pinStatus} onPinClick={onPinEntry} /> <PinButton isDark={false} isPinned={entry.pinStatus} onPinClick={onPinEntry} />
</div> </div>
<Link href={`/n/${entry.identifier}`} className='text-decoration-none flex-fill text-dark'> <Link href={`/n/${entry.identifier}`} className='text-decoration-none text-body-emphasis flex-fill'>
<div className={'d-flex flex-column justify-content-between'}> <div className={'d-flex flex-column justify-content-between'}>
<Card.Title className='m-0 mt-1dot5' {...cypressId('history-entry-title')}> <Card.Title className='m-0 mt-1dot5' {...cypressId('history-entry-title')}>
{entryTitle} {entryTitle}
</Card.Title> </Card.Title>
<div> <div>
<div className='text-black-50 mt-2'> <div className='mt-2'>
<UiIcon icon={IconClock} /> {DateTime.fromISO(entry.lastVisitedAt).toRelative()} <UiIcon icon={IconClock} /> {DateTime.fromISO(entry.lastVisitedAt).toRelative()}
<br /> <br />
{lastVisited} {lastVisited}

View file

@ -44,14 +44,14 @@ export const HistoryTableRow: React.FC<HistoryEntryProps & HistoryEventHandlers>
return ( return (
<tr {...cypressAttribute('entry-title', entryTitle)}> <tr {...cypressAttribute('entry-title', entryTitle)}>
<td> <td>
<Link href={`/n/${entry.identifier}`} className='text-light' {...cypressId('history-entry-title')}> <Link href={`/n/${entry.identifier}`} className='text-secondary' {...cypressId('history-entry-title')}>
{entryTitle} {entryTitle}
</Link> </Link>
</td> </td>
<td>{formatHistoryDate(entry.lastVisitedAt)}</td> <td>{formatHistoryDate(entry.lastVisitedAt)}</td>
<td> <td>
{entry.tags.map((tag) => ( {entry.tags.map((tag) => (
<Badge className={'bg-light me-1 mb-1'} key={tag}> <Badge className={'me-1 mb-1'} key={tag}>
{tag} {tag}
</Badge> </Badge>
))} ))}

View file

@ -3,6 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useDarkModeState } from '../../../hooks/dark-mode/use-dark-mode-state'
import { cypressId } from '../../../utils/cypress-attribute' import { cypressId } from '../../../utils/cypress-attribute'
import { Pager } from '../../common/pagination/pager' import { Pager } from '../../common/pagination/pager'
import type { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content' import type { HistoryEntriesProps, HistoryEventHandlers } from '../history-content/history-content'
@ -44,13 +45,15 @@ export const HistoryTable: React.FC<HistoryEntriesProps & HistoryEventHandlers>
)) ))
}, [entries, onPinClick, onRemoveEntryClick, onDeleteNoteClick]) }, [entries, onPinClick, onRemoveEntryClick, onDeleteNoteClick])
const darkModeState = useDarkModeState()
return ( return (
<Table <Table
striped striped
bordered bordered
hover hover
size='sm' size='sm'
variant='dark' variant={darkModeState ? 'dark' : 'light'}
className={styles['history-table']} className={styles['history-table']}
{...cypressId('history-table')}> {...cypressId('history-table')}>
<thead> <thead>

View file

@ -36,7 +36,7 @@ export const ClearHistoryButton: React.FC = () => {
return ( return (
<Fragment> <Fragment>
<Button <Button
variant={'light'} variant={'secondary'}
title={t('landing.history.toolbar.clear') ?? undefined} title={t('landing.history.toolbar.clear') ?? undefined}
onClick={showModal} onClick={showModal}
{...cypressId('history-clear-button')}> {...cypressId('history-clear-button')}>

View file

@ -17,7 +17,7 @@ export const ExportHistoryButton: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<Button variant={'light'} title={t('landing.history.toolbar.export') ?? undefined} onClick={downloadHistory}> <Button variant={'secondary'} title={t('landing.history.toolbar.export') ?? undefined} onClick={downloadHistory}>
<UiIcon icon={IconDownload} /> <UiIcon icon={IconDownload} />
</Button> </Button>
) )

View file

@ -19,7 +19,7 @@ export const HistoryRefreshButton: React.FC = () => {
const refreshHistory = useSafeRefreshHistoryStateCallback() const refreshHistory = useSafeRefreshHistoryStateCallback()
return ( return (
<Button variant={'light'} title={t('landing.history.toolbar.refresh') ?? undefined} onClick={refreshHistory}> <Button variant={'secondary'} title={t('landing.history.toolbar.refresh') ?? undefined} onClick={refreshHistory}>
<UiIcon icon={IconArrowRepeat} /> <UiIcon icon={IconArrowRepeat} />
</Button> </Button>
) )

View file

@ -90,7 +90,7 @@ export const HistoryToolbar: React.FC = () => {
<ShowIf condition={userExists}> <ShowIf condition={userExists}>
<div className={'me-1 mb-1'}> <div className={'me-1 mb-1'}>
<Button <Button
variant={'light'} variant={'secondary'}
title={t('landing.history.toolbar.uploadAll') ?? undefined} title={t('landing.history.toolbar.uploadAll') ?? undefined}
onClick={onUploadAllToRemote}> onClick={onUploadAllToRemote}>
<UiIcon icon={IconCloudUpload} /> <UiIcon icon={IconCloudUpload} />

View file

@ -34,13 +34,13 @@ export const HistoryViewModeToggleButton: React.FC = () => {
<ToggleButtonGroup type='radio' name='options' dir='auto' className={'button-height'} onChange={onViewStateChange}> <ToggleButtonGroup type='radio' name='options' dir='auto' className={'button-height'} onChange={onViewStateChange}>
<Button <Button
title={t('landing.history.toolbar.cards') ?? undefined} title={t('landing.history.toolbar.cards') ?? undefined}
variant={historyToolbarState.viewState === ViewStateEnum.CARD ? 'light' : 'outline-light'} variant={historyToolbarState.viewState === ViewStateEnum.CARD ? 'secondary' : 'outline-secondary'}
onClick={() => onViewStateChange(ViewStateEnum.CARD)}> onClick={() => onViewStateChange(ViewStateEnum.CARD)}>
<UiIcon icon={IconStickyFill} className={'fa-fix-line-height'} /> <UiIcon icon={IconStickyFill} className={'fa-fix-line-height'} />
</Button> </Button>
<Button <Button
{...cypressId('history-mode-table')} {...cypressId('history-mode-table')}
variant={historyToolbarState.viewState === ViewStateEnum.TABLE ? 'light' : 'outline-light'} variant={historyToolbarState.viewState === ViewStateEnum.TABLE ? 'secondary' : 'outline-secondary'}
title={t('landing.history.toolbar.table') ?? undefined} title={t('landing.history.toolbar.table') ?? undefined}
onClick={() => onViewStateChange(ViewStateEnum.TABLE)}> onClick={() => onViewStateChange(ViewStateEnum.TABLE)}>
<UiIcon icon={IconTable} className={'fa-fix-line-height'} /> <UiIcon icon={IconTable} className={'fa-fix-line-height'} />

View file

@ -128,7 +128,7 @@ export const ImportHistoryButton: React.FC = () => {
{...cypressId('import-history-file-input')} {...cypressId('import-history-file-input')}
/> />
<Button <Button
variant={'light'} variant={'secondary'}
title={t('landing.history.toolbar.import') ?? undefined} title={t('landing.history.toolbar.import') ?? undefined}
onClick={onUploadButtonClick} onClick={onUploadButtonClick}
{...cypressId('import-history-file-button')}> {...cypressId('import-history-file-button')}>

View file

@ -28,7 +28,7 @@ export interface PinButtonProps {
export const PinButton: React.FC<PinButtonProps> = ({ isPinned, onPinClick, isDark, className }) => { export const PinButton: React.FC<PinButtonProps> = ({ isPinned, onPinClick, isDark, className }) => {
return ( return (
<Button <Button
variant={isDark ? 'secondary' : 'light'} variant={isDark ? 'secondary' : 'secondary'}
className={`${styles['history-pin']} ${className || ''} ${isPinned ? styles['pinned'] : ''}`} className={`${styles['history-pin']} ${className || ''} ${isPinned ? styles['pinned'] : ''}`}
onClick={onPinClick} onClick={onPinClick}
{...cypressId('history-entry-pin-button')} {...cypressId('history-entry-pin-button')}

View file

@ -64,7 +64,7 @@ export const SortButton: React.FC<SortButtonProps> = ({ children, onDirectionCha
}, [direction]) }, [direction])
return ( return (
<IconButton onClick={toggleSort} variant={'light'} icon={icon} iconSize={1.5} border={true}> <IconButton onClick={toggleSort} variant={'secondary'} icon={icon} iconSize={1.5} border={true}>
{children} {children}
</IconButton> </IconButton>
) )

View file

@ -3,7 +3,6 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { DarkModePreference } from '../../redux/dark-mode/types'
import { Logger } from '../../utils/logger' import { Logger } from '../../utils/logger'
import { AsyncLoadingBoundary } from '../common/async-loading-boundary/async-loading-boundary' import { AsyncLoadingBoundary } from '../common/async-loading-boundary/async-loading-boundary'
import { RendererIframe } from '../common/renderer-iframe/renderer-iframe' import { RendererIframe } from '../common/renderer-iframe/renderer-iframe'
@ -32,7 +31,6 @@ export const IntroCustomContent: React.FC = () => {
frameClasses={'w-100 overflow-y-hidden'} frameClasses={'w-100 overflow-y-hidden'}
markdownContentLines={value as string[]} markdownContentLines={value as string[]}
rendererType={RendererType.SIMPLE} rendererType={RendererType.SIMPLE}
forcedDarkMode={DarkModePreference.DARK}
adaptFrameHeightToContent={true} adaptFrameHeightToContent={true}
/> />
</AsyncLoadingBoundary> </AsyncLoadingBoundary>

View file

@ -12,7 +12,7 @@ import React from 'react'
*/ */
export const Footer: React.FC = () => { export const Footer: React.FC = () => {
return ( return (
<footer className='text-light small'> <footer className='small'>
<PoweredByLinks /> <PoweredByLinks />
<SocialLink /> <SocialLink />
</footer> </footer>

View file

@ -18,12 +18,7 @@ export const VersionInfoLink: React.FC = () => {
return ( return (
<Fragment> <Fragment>
<Button <Button size={'sm'} variant={'link'} {...cypressId('show-version-modal')} className={'p-0'} onClick={showModal}>
size={'sm'}
variant={'link'}
{...cypressId('show-version-modal')}
className={'text-light p-0'}
onClick={showModal}>
<Trans i18nKey={'landing.versionInfo.versionInfo'} /> <Trans i18nKey={'landing.versionInfo.versionInfo'} />
</Button> </Button>
<VersionInfoModal onHide={closeModal} show={modalVisibility} /> <VersionInfoModal onHide={closeModal} show={modalVisibility} />

View file

@ -3,11 +3,13 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useApplyDarkModeStyle } from '../../hooks/dark-mode/use-apply-dark-mode-style'
import { useSaveDarkModePreferenceToLocalStorage } from '../../hooks/dark-mode/use-save-dark-mode-preference-to-local-storage'
import { MotdModal } from '../common/motd-modal/motd-modal' import { MotdModal } from '../common/motd-modal/motd-modal'
import { Footer } from './footer/footer' import { Footer } from './footer/footer'
import { HeaderBar } from './navigation/header-bar/header-bar' import { HeaderBar } from './navigation/header-bar/header-bar'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
import React, { Fragment } from 'react' import React from 'react'
import { Container } from 'react-bootstrap' import { Container } from 'react-bootstrap'
/** /**
@ -15,17 +17,20 @@ import { Container } from 'react-bootstrap'
* *
* @param children The children that should be rendered on the page. * @param children The children that should be rendered on the page.
*/ */
export const LandingLayout: React.FC<PropsWithChildren<unknown>> = ({ children }) => { export const LandingLayout: React.FC<PropsWithChildren> = ({ children }) => {
useApplyDarkModeStyle()
useSaveDarkModePreferenceToLocalStorage()
return ( return (
<Fragment> <div>
<MotdModal /> <MotdModal />
<Container className='text-light d-flex flex-column mvh-100'> <Container className='d-flex flex-column mvh-100'>
<HeaderBar /> <HeaderBar />
<div className={'d-flex flex-column justify-content-between flex-fill text-center'}> <div className={'d-flex flex-column justify-content-between flex-fill text-center'}>
<main>{children}</main> <main>{children}</main>
<Footer /> <Footer />
</div> </div>
</Container> </Container>
</Fragment> </div>
) )
} }

View file

@ -32,7 +32,7 @@ const HeaderBar: React.FC = () => {
</HeaderNavLink> </HeaderNavLink>
</div> </div>
<div className='d-inline-flex gap-2'> <div className='d-inline-flex gap-2'>
<SettingsButton variant={'outline-light'} /> <SettingsButton variant={'outline-dark'} />
<NewNoteButton /> <NewNoteButton />
{!userExists ? <SignInButton size='sm' /> : <UserDropdown />} {!userExists ? <SignInButton size='sm' /> : <UserDropdown />}
</div> </div>

View file

@ -4,10 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
.nav-link { .link {
border-bottom: 2px solid transparent border-bottom: 2px solid transparent
} }
.nav-link-active { .active {
border-bottom-color: #fff; border-bottom-color: var(--bs-primary);
} }

View file

@ -3,6 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { concatCssClasses } from '../../../../utils/concat-css-classes'
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute' import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
import { cypressId } from '../../../../utils/cypress-attribute' import { cypressId } from '../../../../utils/cypress-attribute'
import styles from './header-nav-link.module.scss' import styles from './header-nav-link.module.scss'
@ -26,17 +27,19 @@ export interface HeaderNavLinkProps extends PropsWithDataCypressId {
export const HeaderNavLink: React.FC<PropsWithChildren<HeaderNavLinkProps>> = ({ to, children, ...props }) => { export const HeaderNavLink: React.FC<PropsWithChildren<HeaderNavLinkProps>> = ({ to, children, ...props }) => {
const { route } = useRouter() const { route } = useRouter()
const activeClass = useMemo(() => { const className = useMemo(() => {
return route === to ? styles['nav-link-active'] : '' return concatCssClasses(
{
[styles.active]: route === to
},
'nav-link',
styles.link
)
}, [route, to]) }, [route, to])
return ( return (
<Nav.Item> <Nav.Item>
<Link <Link href={to} passHref={true} className={className} {...cypressId(props)}>
href={to}
passHref={true}
className={`nav-link text-light ${activeClass} ${styles['nav-link']}`}
{...cypressId(props)}>
{children} {children}
</Link> </Link>
</Nav.Item> </Nav.Item>

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { useBooleanState } from '../../../hooks/common/use-boolean-state' import { useBooleanState } from '../../../hooks/common/use-boolean-state'
import { useOutlineButtonVariant } from '../../../hooks/dark-mode/use-outline-button-variant'
import { cypressId } from '../../../utils/cypress-attribute' import { cypressId } from '../../../utils/cypress-attribute'
import { IconButton } from '../../common/icon-button/icon-button' import { IconButton } from '../../common/icon-button/icon-button'
import { SettingsModal } from './settings-modal' import { SettingsModal } from './settings-modal'
@ -17,9 +18,17 @@ export type SettingsButtonProps = Omit<ButtonProps, 'onClick'>
*/ */
export const SettingsButton: React.FC<SettingsButtonProps> = (props) => { export const SettingsButton: React.FC<SettingsButtonProps> = (props) => {
const [show, showModal, hideModal] = useBooleanState(false) const [show, showModal, hideModal] = useBooleanState(false)
const buttonVariant = useOutlineButtonVariant()
return ( return (
<Fragment> <Fragment>
<IconButton {...props} {...cypressId('settingsButton')} onClick={showModal} icon={IconGear} /> <IconButton
{...props}
{...cypressId('settingsButton')}
onClick={showModal}
icon={IconGear}
variant={buttonVariant}
/>
<SettingsModal show={show} onHide={hideModal} /> <SettingsModal show={show} onHide={hideModal} />
</Fragment> </Fragment>
) )

View file

@ -25,7 +25,6 @@ export const PasswordField: React.FC<AuthFieldProps> = ({ onChange, invalid }) =
size='sm' size='sm'
placeholder={t('login.auth.password') ?? undefined} placeholder={t('login.auth.password') ?? undefined}
onChange={onChange} onChange={onChange}
className='bg-dark text-light'
autoComplete='current-password' autoComplete='current-password'
/> />
</Form.Group> </Form.Group>

View file

@ -32,7 +32,6 @@ export const UsernameField: React.FC<UsernameFieldProps> = ({ onChange, invalid,
size='sm' size='sm'
placeholder={t('login.auth.username') ?? undefined} placeholder={t('login.auth.username') ?? undefined}
onChange={onChange} onChange={onChange}
className='bg-dark text-light'
autoComplete='username' autoComplete='username'
/> />
</Form.Group> </Form.Group>

View file

@ -49,7 +49,7 @@ export const ViaLocal: React.FC = () => {
const onPasswordChange = useOnInputChange(setPassword) const onPasswordChange = useOnInputChange(setPassword)
return ( return (
<Card className='bg-dark mb-4'> <Card className='mb-4'>
<Card.Body> <Card.Body>
<Card.Title> <Card.Title>
<Trans i18nKey='login.signInVia' values={{ service: t('login.auth.username') }} /> <Trans i18nKey='login.signInVia' values={{ service: t('login.auth.username') }} />

View file

@ -5,7 +5,7 @@
*/ */
.image-drop { .image-drop {
border: 3px dashed var(--bs-dark); border: 3px dashed var(--bs-primary);
border-radius: 3px; border-radius: 3px;
transition: background-color 50ms, color 50ms; transition: background-color 50ms, color 50ms;

View file

@ -97,7 +97,7 @@ export const ImagePlaceholder: React.FC<PlaceholderImageFrameProps> = ({
onChange={onChangeHandler} onChange={onChangeHandler}
ref={fileInputReference} ref={fileInputReference}
/> />
<div className={'align-items-center flex-column justify-content-center flex-fill d-flex'}> <div className={'align-items-center flex-column justify-content-center flex-fill d-flex text-body-emphasis'}>
<div className={'d-flex flex-column'}> <div className={'d-flex flex-column'}>
<span className='my-2'> <span className='my-2'>
<Trans i18nKey={'editor.embeddings.placeholderImage.placeholderText'} /> <Trans i18nKey={'editor.embeddings.placeholderImage.placeholderText'} />

View file

@ -13,11 +13,16 @@ export const useApplyDarkModeStyle = (): void => {
const darkMode = useDarkModeState() const darkMode = useDarkModeState()
useEffect(() => { useEffect(() => {
if (darkMode) { if (darkMode) {
window.document.body.classList.add('dark') window.document.body.dataset.bsTheme = 'dark'
} else { } else {
window.document.body.classList.remove('dark') window.document.body.dataset.bsTheme = 'light'
} }
}, [darkMode]) }, [darkMode])
useEffect(() => () => window.document.body.classList.remove('dark'), []) useEffect(
() => () => {
window.document.body.dataset.bsTheme = 'light'
},
[]
)
} }

View file

@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as UseDarkModeStateModule from './use-dark-mode-state'
import { useOutlineButtonVariant } from './use-outline-button-variant'
import { render, screen } from '@testing-library/react'
import React from 'react'
jest.mock('./use-dark-mode-state')
describe('useOutlineButtonVariant', () => {
const TestComponent: React.FC = () => {
return useOutlineButtonVariant()
}
it('returns the correct variant for dark mode', async () => {
jest.spyOn(UseDarkModeStateModule, 'useDarkModeState').mockReturnValue(true)
render(<TestComponent />)
await screen.findByText('outline-light')
})
it('returns the correct variant for light mode', async () => {
jest.spyOn(UseDarkModeStateModule, 'useDarkModeState').mockReturnValue(false)
render(<TestComponent />)
await screen.findByText('outline-dark')
})
})

View file

@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useDarkModeState } from './use-dark-mode-state'
export const useOutlineButtonVariant = (): 'outline-light' | 'outline-dark' => {
return useDarkModeState() ? 'outline-light' : 'outline-dark'
}

View file

@ -3,7 +3,6 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import '../../global-styles/dark.scss'
import '../../global-styles/index.scss' import '../../global-styles/index.scss'
import type { FrontendConfig } from '../api/config/types' import type { FrontendConfig } from '../api/config/types'
import { ApplicationLoader } from '../components/application-loader/application-loader' import { ApplicationLoader } from '../components/application-loader/application-loader'

View file

@ -72,7 +72,7 @@ export const LoginPage: React.FC = () => {
</ShowIf> </ShowIf>
<ShowIf condition={oneClickProviders.length > 0}> <ShowIf condition={oneClickProviders.length > 0}>
<Col xs={12} sm={10} lg={4}> <Col xs={12} sm={10} lg={4}>
<Card className='bg-dark mb-4'> <Card className='mb-4'>
<Card.Body> <Card.Body>
<Card.Title> <Card.Title>
<Trans i18nKey='login.signInVia' values={{ service: '' }} /> <Trans i18nKey='login.signInVia' values={{ service: '' }} />

View file

@ -7,6 +7,7 @@ import { NoteLoadingBoundary } from '../../components/common/note-loading-bounda
import { EditorPageContent } from '../../components/editor-page/editor-page-content' import { EditorPageContent } from '../../components/editor-page/editor-page-content'
import { EditorToRendererCommunicatorContextProvider } from '../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider' import { EditorToRendererCommunicatorContextProvider } from '../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider'
import { ResetRealtimeStateBoundary } from '../../components/editor-page/reset-realtime-state-boundary' import { ResetRealtimeStateBoundary } from '../../components/editor-page/reset-realtime-state-boundary'
import { useApplyDarkModeStyle } from '../../hooks/dark-mode/use-apply-dark-mode-style'
import type { NextPage } from 'next' import type { NextPage } from 'next'
import React from 'react' import React from 'react'
@ -14,6 +15,8 @@ import React from 'react'
* Renders a page that is used by the user to edit markdown notes. It contains the editor and a renderer. * Renders a page that is used by the user to edit markdown notes. It contains the editor and a renderer.
*/ */
export const EditorPage: NextPage = () => { export const EditorPage: NextPage = () => {
useApplyDarkModeStyle()
return ( return (
<ResetRealtimeStateBoundary> <ResetRealtimeStateBoundary>
<NoteLoadingBoundary> <NoteLoadingBoundary>

View file

@ -84,7 +84,7 @@ export const RegisterPage: NextPage = () => {
</h1> </h1>
<Row className='h-100 d-flex justify-content-center'> <Row className='h-100 d-flex justify-content-center'>
<Col lg={6}> <Col lg={6}>
<Card className='bg-dark mb-4 text-start'> <Card className='mb-4 text-start'>
<Card.Body> <Card.Body>
<Form onSubmit={doRegisterSubmit} className={'d-flex flex-column gap-3'}> <Form onSubmit={doRegisterSubmit} className={'d-flex flex-column gap-3'}>
<UsernameField onChange={onUsernameChange} value={username} /> <UsernameField onChange={onUsernameChange} value={username} />

View file

@ -7,12 +7,17 @@ import { RendererToEditorCommunicatorContextProvider } from '../components/edito
import { RenderPageContent } from '../components/render-page/render-page-content' import { RenderPageContent } from '../components/render-page/render-page-content'
import { useApplyDarkModeStyle } from '../hooks/dark-mode/use-apply-dark-mode-style' import { useApplyDarkModeStyle } from '../hooks/dark-mode/use-apply-dark-mode-style'
import type { NextPage } from 'next' import type { NextPage } from 'next'
import React from 'react' import React, { useEffect } from 'react'
/** /**
* Renders the actual markdown renderer that receives the content and metadata via iframe communication. * Renders the actual markdown renderer that receives the content and metadata via iframe communication.
*/ */
export const RenderPage: NextPage = () => { export const RenderPage: NextPage = () => {
useEffect(() => {
document.body.classList.add('bg-transparent')
return () => document.body.classList.remove('bg-transparent')
}, [])
useApplyDarkModeStyle() useApplyDarkModeStyle()
return ( return (

View file

@ -25,7 +25,7 @@ export const DocumentReadOnlyPage: React.FC = () => {
<NoteLoadingBoundary> <NoteLoadingBoundary>
<HeadMetaProperties /> <HeadMetaProperties />
<MotdModal /> <MotdModal />
<div className={'d-flex flex-column mvh-100 bg-light'}> <div className={'d-flex flex-column mvh-100'}>
<AppBar mode={AppBarMode.BASIC} /> <AppBar mode={AppBarMode.BASIC} />
<DocumentReadOnlyPageContent /> <DocumentReadOnlyPageContent />
</div> </div>