fix(cms): apply dark mode to CMS editor, public CMS, and admin modals

This commit is contained in:
Luca
2026-05-05 16:07:21 +02:00
parent 5a162fc8be
commit d2a10f6523
9 changed files with 98 additions and 79 deletions
@@ -25,13 +25,13 @@ export const BulkArchiveModal: React.FC<BulkArchiveModalProps> = ({
<Card className="w-full max-w-md"> <Card className="w-full max-w-md">
<div className="p-6"> <div className="p-6">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-semibold text-neutral-900">Confirm Bulk Archive</h2> <h2 className="text-xl font-semibold text-neutral-900 dark:text-neutral-100">Confirm Bulk Archive</h2>
<button <button
onClick={onClose} onClick={onClose}
className="p-1 hover:bg-neutral-100 rounded-lg transition-colors" className="p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700 rounded-lg transition-colors"
disabled={isLoading} disabled={isLoading}
> >
<X className="w-5 h-5 text-neutral-500" /> <X className="w-5 h-5 text-neutral-500 dark:text-neutral-400" />
</button> </button>
</div> </div>
+39 -48
View File
@@ -143,8 +143,10 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
<button <button
onClick={onClick} onClick={onClick}
disabled={disabled} disabled={disabled}
className={`p-2 rounded hover:bg-neutral-100 transition-colors ${ className={`p-2 rounded hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors ${
active ? 'bg-primary-100 text-primary-700' : 'text-neutral-700' active
? 'bg-primary-100 dark:bg-primary-900/40 text-primary-700 dark:text-primary-300'
: 'text-neutral-700 dark:text-neutral-200'
} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`} } ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}
title={title} title={title}
type="button" type="button"
@@ -172,44 +174,32 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
}); });
}; };
// Reusable view-mode chip — three states (edit/preview/split). Shared
// styling block extracted as a const so the dark variants stay in sync.
const viewModeChipClass = (mode: typeof viewMode) =>
`px-3 py-1.5 text-sm font-medium rounded transition-colors ${
viewMode === mode
? 'bg-primary-100 dark:bg-primary-900/40 text-primary-700 dark:text-primary-300'
: 'text-neutral-600 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-700'
}`;
return ( return (
<div className={`relative ${isFullscreen ? 'fixed inset-0 z-50 bg-white' : ''}`}> <div className={`relative ${isFullscreen ? 'fixed inset-0 z-50 bg-white dark:bg-neutral-900' : ''}`}>
<div className="border border-neutral-300 rounded-lg overflow-hidden h-full flex flex-col"> <div className="border border-neutral-300 dark:border-neutral-700 rounded-lg overflow-hidden h-full flex flex-col bg-white dark:bg-neutral-900">
{/* Top Toolbar */} {/* Top Toolbar */}
<div className="border-b border-neutral-200 bg-neutral-50"> <div className="border-b border-neutral-200 dark:border-neutral-700 bg-neutral-50 dark:bg-neutral-800">
{/* View Mode Controls */} {/* View Mode Controls */}
<div className="flex items-center justify-between p-2 border-b border-neutral-200"> <div className="flex items-center justify-between p-2 border-b border-neutral-200 dark:border-neutral-700">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button onClick={() => setViewMode('edit')} className={viewModeChipClass('edit')}>
onClick={() => setViewMode('edit')}
className={`px-3 py-1.5 text-sm font-medium rounded transition-colors ${
viewMode === 'edit'
? 'bg-primary-100 text-primary-700'
: 'text-neutral-600 hover:bg-neutral-100'
}`}
>
<Edit3 className="w-4 h-4 inline-block mr-1" /> <Edit3 className="w-4 h-4 inline-block mr-1" />
Edit Edit
</button> </button>
<button <button onClick={() => setViewMode('preview')} className={viewModeChipClass('preview')}>
onClick={() => setViewMode('preview')}
className={`px-3 py-1.5 text-sm font-medium rounded transition-colors ${
viewMode === 'preview'
? 'bg-primary-100 text-primary-700'
: 'text-neutral-600 hover:bg-neutral-100'
}`}
>
<Eye className="w-4 h-4 inline-block mr-1" /> <Eye className="w-4 h-4 inline-block mr-1" />
Preview Preview
</button> </button>
<button <button onClick={() => setViewMode('split')} className={viewModeChipClass('split')}>
onClick={() => setViewMode('split')}
className={`px-3 py-1.5 text-sm font-medium rounded transition-colors ${
viewMode === 'split'
? 'bg-primary-100 text-primary-700'
: 'text-neutral-600 hover:bg-neutral-100'
}`}
>
<Columns className="w-4 h-4 inline-block mr-1" /> <Columns className="w-4 h-4 inline-block mr-1" />
Split Split
</button> </button>
@@ -295,7 +285,7 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
<Heading6 className="w-4 h-4" /> <Heading6 className="w-4 h-4" />
</MenuButton> </MenuButton>
<div className="w-px h-6 bg-neutral-300 mx-1" /> <div className="w-px h-6 bg-neutral-300 dark:bg-neutral-600 mx-1" />
<MenuButton <MenuButton
onClick={() => editor.chain().focus().toggleBold().run()} onClick={() => editor.chain().focus().toggleBold().run()}
@@ -329,7 +319,7 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
<Code2 className="w-4 h-4" /> <Code2 className="w-4 h-4" />
</MenuButton> </MenuButton>
<div className="w-px h-6 bg-neutral-300 mx-1" /> <div className="w-px h-6 bg-neutral-300 dark:bg-neutral-600 mx-1" />
<MenuButton <MenuButton
onClick={() => editor.chain().focus().toggleBulletList().run()} onClick={() => editor.chain().focus().toggleBulletList().run()}
@@ -355,7 +345,7 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
<Quote className="w-4 h-4" /> <Quote className="w-4 h-4" />
</MenuButton> </MenuButton>
<div className="w-px h-6 bg-neutral-300 mx-1" /> <div className="w-px h-6 bg-neutral-300 dark:bg-neutral-600 mx-1" />
<MenuButton <MenuButton
onClick={() => setShowLinkDialog(true)} onClick={() => setShowLinkDialog(true)}
@@ -372,7 +362,7 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
<Minus className="w-4 h-4" /> <Minus className="w-4 h-4" />
</MenuButton> </MenuButton>
<div className="w-px h-6 bg-neutral-300 mx-1" /> <div className="w-px h-6 bg-neutral-300 dark:bg-neutral-600 mx-1" />
<MenuButton <MenuButton
onClick={() => editor.chain().focus().setTextAlign('left').run()} onClick={() => editor.chain().focus().setTextAlign('left').run()}
@@ -406,7 +396,7 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
<AlignJustify className="w-4 h-4" /> <AlignJustify className="w-4 h-4" />
</MenuButton> </MenuButton>
<div className="w-px h-6 bg-neutral-300 mx-1" /> <div className="w-px h-6 bg-neutral-300 dark:bg-neutral-600 mx-1" />
<MenuButton <MenuButton
onClick={() => editor.chain().focus().clearNodes().unsetAllMarks().run()} onClick={() => editor.chain().focus().clearNodes().unsetAllMarks().run()}
@@ -415,7 +405,7 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
<RemoveFormatting className="w-4 h-4" /> <RemoveFormatting className="w-4 h-4" />
</MenuButton> </MenuButton>
<div className="w-px h-6 bg-neutral-300 mx-1" /> <div className="w-px h-6 bg-neutral-300 dark:bg-neutral-600 mx-1" />
<MenuButton <MenuButton
onClick={() => editor.chain().focus().undo().run()} onClick={() => editor.chain().focus().undo().run()}
@@ -438,14 +428,14 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
{/* Link Dialog */} {/* Link Dialog */}
{showLinkDialog && ( {showLinkDialog && (
<div className="p-3 bg-primary-50 border-b border-primary-200 flex items-center gap-2"> <div className="p-3 bg-primary-50 dark:bg-primary-900/30 border-b border-primary-200 dark:border-primary-800 flex items-center gap-2">
<input <input
type="url" type="url"
value={linkUrl} value={linkUrl}
onChange={(e) => setLinkUrl(e.target.value)} onChange={(e) => setLinkUrl(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addLink()} onKeyPress={(e) => e.key === 'Enter' && addLink()}
placeholder="Enter URL..." placeholder="Enter URL..."
className="flex-1 px-3 py-1 border border-primary-300 rounded-md focus:ring-2 focus:ring-primary-500" className="flex-1 px-3 py-1 border border-primary-300 dark:border-primary-700 bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-100 rounded-md focus:ring-2 focus:ring-primary-500"
autoFocus autoFocus
/> />
<Button size="sm" onClick={addLink}>Add Link</Button> <Button size="sm" onClick={addLink}>Add Link</Button>
@@ -460,21 +450,22 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
{/* Editor Content Area */} {/* Editor Content Area */}
<div className="flex-1 flex overflow-hidden"> <div className="flex-1 flex overflow-hidden">
{/* Editor */} {/* Editor — prose-invert in dark mode flips the prose typography
palette without us having to override every prose-* class. */}
{viewMode !== 'preview' && ( {viewMode !== 'preview' && (
<div className={`${viewMode === 'split' ? 'w-1/2 border-r border-neutral-200' : 'w-full'} overflow-auto`}> <div className={`${viewMode === 'split' ? 'w-1/2 border-r border-neutral-200 dark:border-neutral-700' : 'w-full'} overflow-auto bg-white dark:bg-neutral-900`}>
<EditorContent <EditorContent
editor={editor} editor={editor}
className="min-h-[400px] p-4 prose prose-neutral max-w-none focus:outline-none [&_.ProseMirror]:min-h-[400px] [&_.ProseMirror]:outline-none [&_.ProseMirror_p.is-editor-empty:first-child::before]:content-[attr(data-placeholder)] [&_.ProseMirror_p.is-editor-empty:first-child::before]:text-neutral-400 [&_.ProseMirror_p.is-editor-empty:first-child::before]:pointer-events-none [&_.ProseMirror_p.is-editor-empty:first-child::before]:float-left [&_.ProseMirror_p.is-editor-empty:first-child::before]:h-0 [&_.ProseMirror_br.hard-break]:display-block [&_.ProseMirror_br.hard-break]:content-[''] [&_.ProseMirror_br.hard-break]:margin-[0.5em_0] [&_.ProseMirror_pre]:bg-neutral-100 [&_.ProseMirror_pre]:rounded-md [&_.ProseMirror_pre]:p-4 [&_.ProseMirror_pre]:overflow-x-auto [&_.ProseMirror_code]:bg-neutral-100 [&_.ProseMirror_code]:rounded [&_.ProseMirror_code]:px-1 [&_.ProseMirror_code]:py-0.5 [&_.ProseMirror_code]:text-sm [&_.ProseMirror_pre_code]:bg-transparent [&_.ProseMirror_pre_code]:p-0" className="min-h-[400px] p-4 prose prose-neutral dark:prose-invert max-w-none focus:outline-none [&_.ProseMirror]:min-h-[400px] [&_.ProseMirror]:outline-none [&_.ProseMirror]:text-neutral-900 dark:[&_.ProseMirror]:text-neutral-100 [&_.ProseMirror_p.is-editor-empty:first-child::before]:content-[attr(data-placeholder)] [&_.ProseMirror_p.is-editor-empty:first-child::before]:text-neutral-400 dark:[&_.ProseMirror_p.is-editor-empty:first-child::before]:text-neutral-500 [&_.ProseMirror_p.is-editor-empty:first-child::before]:pointer-events-none [&_.ProseMirror_p.is-editor-empty:first-child::before]:float-left [&_.ProseMirror_p.is-editor-empty:first-child::before]:h-0 [&_.ProseMirror_br.hard-break]:display-block [&_.ProseMirror_br.hard-break]:content-[''] [&_.ProseMirror_br.hard-break]:margin-[0.5em_0] [&_.ProseMirror_pre]:bg-neutral-100 dark:[&_.ProseMirror_pre]:bg-neutral-800 [&_.ProseMirror_pre]:rounded-md [&_.ProseMirror_pre]:p-4 [&_.ProseMirror_pre]:overflow-x-auto [&_.ProseMirror_code]:bg-neutral-100 dark:[&_.ProseMirror_code]:bg-neutral-800 [&_.ProseMirror_code]:rounded [&_.ProseMirror_code]:px-1 [&_.ProseMirror_code]:py-0.5 [&_.ProseMirror_code]:text-sm [&_.ProseMirror_pre_code]:bg-transparent [&_.ProseMirror_pre_code]:p-0"
/> />
</div> </div>
)} )}
{/* Preview */} {/* Preview */}
{viewMode !== 'edit' && ( {viewMode !== 'edit' && (
<div className={`${viewMode === 'split' ? 'w-1/2' : 'w-full'} overflow-auto bg-neutral-50 p-4`}> <div className={`${viewMode === 'split' ? 'w-1/2' : 'w-full'} overflow-auto bg-neutral-50 dark:bg-neutral-800 p-4`}>
<div <div
className="prose prose-neutral max-w-none" className="prose prose-neutral dark:prose-invert max-w-none"
dangerouslySetInnerHTML={{ __html: getPreviewContent() }} dangerouslySetInnerHTML={{ __html: getPreviewContent() }}
/> />
</div> </div>
@@ -482,12 +473,12 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
</div> </div>
{/* Status Bar */} {/* Status Bar */}
<div className="flex items-center justify-between px-4 py-2 bg-neutral-50 border-t border-neutral-200 text-sm text-neutral-600"> <div className="flex items-center justify-between px-4 py-2 bg-neutral-50 dark:bg-neutral-800 border-t border-neutral-200 dark:border-neutral-700 text-sm text-neutral-600 dark:text-neutral-300">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<span>{wordCount} words</span> <span>{wordCount} words</span>
<span>{charCount} characters</span> <span>{charCount} characters</span>
</div> </div>
<div className="text-xs text-neutral-500"> <div className="text-xs text-neutral-500 dark:text-neutral-400">
Press Shift+Enter for line break, Enter for new paragraph Press Shift+Enter for line break, Enter for new paragraph
</div> </div>
</div> </div>
@@ -496,7 +487,7 @@ export const CMSEditor: React.FC<CMSEditorProps> = ({ content, onChange, onSave,
{/* Help Modal */} {/* Help Modal */}
{showHelp && ( {showHelp && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg max-w-2xl w-full max-h-[80vh] overflow-auto"> <div className="bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-100 rounded-lg max-w-2xl w-full max-h-[80vh] overflow-auto">
<div className="p-6"> <div className="p-6">
<h2 className="text-xl font-semibold mb-4">Editor Help & Keyboard Shortcuts</h2> <h2 className="text-xl font-semibold mb-4">Editor Help & Keyboard Shortcuts</h2>
@@ -158,11 +158,11 @@ export const EventRenameDialog: React.FC<EventRenameDialogProps> = ({
</div> </div>
{renameResult.newShareLink && ( {renameResult.newShareLink && (
<div className="p-3 bg-neutral-50 rounded-lg"> <div className="p-3 bg-neutral-50 dark:bg-neutral-800 rounded-lg">
<p className="text-sm font-medium text-neutral-700 mb-1"> <p className="text-sm font-medium text-neutral-700 dark:text-neutral-200 mb-1">
{t('events.rename.newLink', 'New Gallery Link')} {t('events.rename.newLink', 'New Gallery Link')}
</p> </p>
<p className="text-sm text-neutral-900 break-all">{renameResult.newShareLink}</p> <p className="text-sm text-neutral-900 dark:text-neutral-100 break-all">{renameResult.newShareLink}</p>
</div> </div>
)} )}
@@ -94,28 +94,28 @@ export const FeedbackModerationPanel: React.FC<FeedbackModerationPanelProps> = (
{!hasPending ? ( {!hasPending ? (
<div className="text-center py-8"> <div className="text-center py-8">
<CheckCircle className="w-12 h-12 text-green-500 mx-auto mb-3" /> <CheckCircle className="w-12 h-12 text-green-500 mx-auto mb-3" />
<p className="text-neutral-600">{t('feedback.noPendingComments', 'No comments pending moderation')}</p> <p className="text-neutral-600 dark:text-neutral-300">{t('feedback.noPendingComments', 'No comments pending moderation')}</p>
</div> </div>
) : ( ) : (
<div className="space-y-3"> <div className="space-y-3">
{pendingComments.slice(0, showAll ? undefined : maxItems).map((item) => ( {pendingComments.slice(0, showAll ? undefined : maxItems).map((item) => (
<div key={item.id} className="border border-neutral-200 rounded-lg p-4 hover:bg-neutral-50"> <div key={item.id} className="border border-neutral-200 dark:border-neutral-700 rounded-lg p-4 hover:bg-neutral-50 dark:hover:bg-neutral-800">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<div className="w-10 h-10 bg-neutral-100 rounded-full flex items-center justify-center"> <div className="w-10 h-10 bg-neutral-100 dark:bg-neutral-800 rounded-full flex items-center justify-center">
<User className="w-5 h-5 text-neutral-600" /> <User className="w-5 h-5 text-neutral-600 dark:text-neutral-300" />
</div> </div>
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-2"> <div className="flex items-start justify-between gap-2">
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-2 text-sm"> <div className="flex items-center gap-2 text-sm">
<span className="font-medium text-neutral-900"> <span className="font-medium text-neutral-900 dark:text-neutral-100">
{item.guest_name || t('feedback.anonymous', 'Anonymous')} {item.guest_name || t('feedback.anonymous', 'Anonymous')}
</span> </span>
<span className="text-neutral-500"></span> <span className="text-neutral-500 dark:text-neutral-400"></span>
<span className="text-neutral-500"> <span className="text-neutral-500 dark:text-neutral-400">
{format( {format(
typeof item.created_at === 'string' typeof item.created_at === 'string'
? parseISO(item.created_at) ? parseISO(item.created_at)
@@ -135,7 +135,7 @@ export const MandatoryPasswordChangeModal: React.FC = () => {
<button <button
type="button" type="button"
onClick={() => setShowPasswords(prev => ({ ...prev, current: !prev.current }))} onClick={() => setShowPasswords(prev => ({ ...prev, current: !prev.current }))}
className="absolute right-3 top-2 p-1 hover:bg-neutral-100 rounded" className="absolute right-3 top-2 p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700 rounded"
> >
{showPasswords.current ? {showPasswords.current ?
<EyeOff className="w-4 h-4 text-neutral-500" /> : <EyeOff className="w-4 h-4 text-neutral-500" /> :
@@ -163,7 +163,7 @@ export const MandatoryPasswordChangeModal: React.FC = () => {
<button <button
type="button" type="button"
onClick={() => setShowPasswords(prev => ({ ...prev, new: !prev.new }))} onClick={() => setShowPasswords(prev => ({ ...prev, new: !prev.new }))}
className="absolute right-3 top-2 p-1 hover:bg-neutral-100 rounded" className="absolute right-3 top-2 p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700 rounded"
> >
{showPasswords.new ? {showPasswords.new ?
<EyeOff className="w-4 h-4 text-neutral-500" /> : <EyeOff className="w-4 h-4 text-neutral-500" /> :
@@ -191,7 +191,7 @@ export const MandatoryPasswordChangeModal: React.FC = () => {
<button <button
type="button" type="button"
onClick={() => setShowPasswords(prev => ({ ...prev, confirm: !prev.confirm }))} onClick={() => setShowPasswords(prev => ({ ...prev, confirm: !prev.confirm }))}
className="absolute right-3 top-2 p-1 hover:bg-neutral-100 rounded" className="absolute right-3 top-2 p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700 rounded"
> >
{showPasswords.confirm ? {showPasswords.confirm ?
<EyeOff className="w-4 h-4 text-neutral-500" /> : <EyeOff className="w-4 h-4 text-neutral-500" /> :
@@ -231,7 +231,7 @@ export const PasswordResetModal: React.FC<PasswordResetModalProps> = ({
type="text" type="text"
value={resultPassword} value={resultPassword}
readOnly readOnly
className="flex-1 px-3 py-2 bg-neutral-50 border border-neutral-300 rounded-lg font-mono text-sm" className="flex-1 px-3 py-2 bg-neutral-50 dark:bg-neutral-800 border border-neutral-300 dark:border-neutral-700 text-neutral-900 dark:text-neutral-100 rounded-lg font-mono text-sm"
/> />
<Button <Button
variant="outline" variant="outline"
@@ -50,7 +50,10 @@ export const CMSContentBlock: React.FC<CMSContentBlockProps> = ({ slug, fallback
if (isLoading) { if (isLoading) {
return ( return (
<div className="min-h-screen bg-neutral-50 flex items-center justify-center"> <div
className="min-h-screen flex items-center justify-center"
style={{ backgroundColor: 'var(--color-background, #fafafa)' }}
>
<Loading size="lg" /> <Loading size="lg" />
</div> </div>
); );
@@ -80,11 +83,20 @@ export const CMSContentBlock: React.FC<CMSContentBlockProps> = ({ slug, fallback
<main className="flex-1 flex items-start justify-center px-4"> <main className="flex-1 flex items-start justify-center px-4">
<div className="max-w-2xl w-full"> <div className="max-w-2xl w-full">
<Card padding="lg"> <Card padding="lg">
<h1 className="text-2xl sm:text-3xl font-bold text-neutral-900 dark:text-neutral-100 mb-6"> {/*
* Heading + body now read from theme tokens so dark themes
* (and force-dark mode) render correctly without dark: variants
* fighting the CSS variables.
*/}
<h1
className="text-2xl sm:text-3xl font-bold mb-6"
style={{ color: 'var(--color-text)' }}
>
{page.title} {page.title}
</h1> </h1>
<div <div
className="prose prose-neutral dark:prose-invert max-w-none" className="prose prose-neutral dark:prose-invert max-w-none"
style={{ color: 'var(--color-text)' }}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(page.content, { __html: DOMPurify.sanitize(page.content, {
ALLOWED_TAGS, ALLOWED_TAGS,
@@ -97,7 +109,8 @@ export const CMSContentBlock: React.FC<CMSContentBlockProps> = ({ slug, fallback
<div className="mt-8"> <div className="mt-8">
<Link <Link
to="/" to="/"
className="text-sm font-medium text-primary-600 hover:text-primary-700" className="text-sm font-medium hover:underline"
style={{ color: 'var(--color-accent)' }}
> >
{lang === 'de' ? '← Zur Startseite' : '← Back to home'} {lang === 'de' ? '← Zur Startseite' : '← Back to home'}
</Link> </Link>
@@ -106,13 +119,16 @@ export const CMSContentBlock: React.FC<CMSContentBlockProps> = ({ slug, fallback
</div> </div>
</main> </main>
<footer className="py-8 text-center text-xs text-neutral-500"> <footer
className="py-8 text-center text-xs"
style={{ color: 'var(--color-muted-text)' }}
>
<div className="flex justify-center gap-4"> <div className="flex justify-center gap-4">
<Link to="/impressum" className="hover:text-neutral-700"> <Link to="/impressum" className="hover:underline">
{lang === 'de' ? 'Impressum' : 'Legal Notice'} {lang === 'de' ? 'Impressum' : 'Legal Notice'}
</Link> </Link>
<span className="text-neutral-400"></span> <span style={{ color: 'var(--color-surface-border)' }}></span>
<Link to="/datenschutz" className="hover:text-neutral-700"> <Link to="/datenschutz" className="hover:underline">
{lang === 'de' ? 'Datenschutz' : 'Privacy Policy'} {lang === 'de' ? 'Datenschutz' : 'Privacy Policy'}
</Link> </Link>
</div> </div>
+3 -3
View File
@@ -487,7 +487,7 @@ export const CMSPage: React.FC = () => {
<span className="text-xs text-neutral-500 dark:text-neutral-400">{t('settings.publicSite.previewSandboxed')}</span> <span className="text-xs text-neutral-500 dark:text-neutral-400">{t('settings.publicSite.previewSandboxed')}</span>
</div> </div>
{publicSiteEnabled ? ( {publicSiteEnabled ? (
<div className="rounded-xl border border-neutral-200 overflow-hidden shadow-sm bg-white"> <div className="rounded-xl border border-neutral-200 dark:border-neutral-700 overflow-hidden shadow-sm bg-white dark:bg-neutral-800">
<iframe <iframe
title="public-site-preview" title="public-site-preview"
sandbox="allow-same-origin" sandbox="allow-same-origin"
@@ -576,7 +576,7 @@ export const CMSPage: React.FC = () => {
<Card padding="md" className="mt-4"> <Card padding="md" className="mt-4">
<div className="text-sm"> <div className="text-sm">
{isAutoSaving && ( {isAutoSaving && (
<div className="flex items-center gap-2 text-neutral-600"> <div className="flex items-center gap-2 text-neutral-600 dark:text-neutral-300">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" /> <div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
Auto-saving... Auto-saving...
</div> </div>
@@ -637,7 +637,7 @@ export const CMSPage: React.FC = () => {
<label className="flex items-start gap-3 cursor-pointer"> <label className="flex items-start gap-3 cursor-pointer">
<input <input
type="checkbox" type="checkbox"
className="mt-1 h-4 w-4 rounded border-neutral-300 text-primary-600 focus:ring-primary-500" className="mt-1 h-4 w-4 rounded border-neutral-300 dark:border-neutral-600 text-primary-600 focus:ring-primary-500"
checked={!!editForm.use_external_url} checked={!!editForm.use_external_url}
onChange={(e) => handleUseExternalUrlChange(e.target.checked)} onChange={(e) => handleUseExternalUrlChange(e.target.checked)}
/> />
+15 -3
View File
@@ -27,9 +27,14 @@
margin-bottom: 0; margin-bottom: 0;
} }
/* Code block styling */ /*
* Code block styling — uses the 8-token CI palette so blocks read against
* both light and dark surfaces. The variables fall back to the previous
* #f5f5f5 if a host page hasn't loaded the theme yet.
*/
.prose pre { .prose pre {
background-color: #f5f5f5; background-color: var(--color-elevated, #f5f5f5);
border: 1px solid var(--color-surface-border, #e5e5e5);
border-radius: 0.375rem; border-radius: 0.375rem;
padding: 1rem; padding: 1rem;
overflow-x: auto; overflow-x: auto;
@@ -45,13 +50,20 @@
/* Inline code styling */ /* Inline code styling */
.prose code { .prose code {
background-color: #f5f5f5; background-color: var(--color-elevated, #f5f5f5);
padding: 0.125rem 0.375rem; padding: 0.125rem 0.375rem;
border-radius: 0.25rem; border-radius: 0.25rem;
font-size: 0.875em; font-size: 0.875em;
font-weight: 400; font-weight: 400;
} }
/* Blockquote styling — themed left rule that flips with dark mode. */
.prose blockquote {
border-left: 3px solid var(--color-accent, #017C7C);
padding-left: 1rem;
color: var(--color-muted-text, #737373);
}
/* Text alignment classes */ /* Text alignment classes */
.prose .text-left { .prose .text-left {
text-align: left !important; text-align: left !important;