mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-05-06 17:49:07 -05:00
- Create shared/ directory with centralized SCSS partials and site-head.html so site subsites (about, community, newsletter) no longer duplicate inline header config or reference book-only styles - Fix subscribe modal: change script src from broken relative path (../../book/quarto/assets/scripts/subscribe-modal.js) to absolute URL (https://mlsysbook.ai/vol1/assets/scripts/subscribe-modal.js) which the rewrite-dev-urls.sh script converts for dev preview automatically
717 lines
15 KiB
SCSS
717 lines
15 KiB
SCSS
// =============================================================================
|
|
// ECOSYSTEM BASE DARK — Shared dark mode styles for ALL MLSysBook projects
|
|
// =============================================================================
|
|
// This file contains universal dark mode rules. Theme-specific variables
|
|
// ($accent-dark) must be set by a theme file BEFORE this file is imported.
|
|
//
|
|
// Usage:
|
|
// @import 'themes/theme-harvard'; // sets $accent-dark
|
|
// @import 'ecosystem-base-dark'; // universal dark rules
|
|
//
|
|
// Projects that need textbook-specific dark styles (foldbox, announcement bar,
|
|
// listings) should additionally import '_book-only-dark'.
|
|
// =============================================================================
|
|
|
|
// Dark mode specific colors
|
|
$body-bg-dark: #1a1a1a;
|
|
$body-color-dark: #e6e6e6;
|
|
$link-color-dark: $accent-dark;
|
|
$link-hover-color-dark: lighten($accent-dark, 10%);
|
|
|
|
// Sidebar and navigation
|
|
$sidebar-bg-dark: #212529;
|
|
$navbar-bg-dark: #212529;
|
|
$border-color-dark: #454d55;
|
|
|
|
// Main body styling
|
|
body {
|
|
background-color: $body-bg-dark;
|
|
color: $body-color-dark;
|
|
}
|
|
|
|
// Links in dark mode - brighter accent with better contrast
|
|
a {
|
|
color: lighten($accent-dark, 10%) !important;
|
|
|
|
&:hover {
|
|
color: lighten($accent-dark, 25%) !important;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
&:visited {
|
|
color: lighten($accent-dark, 8%) !important;
|
|
}
|
|
}
|
|
|
|
// Content area
|
|
.content,
|
|
main,
|
|
article,
|
|
#quarto-content {
|
|
background-color: $body-bg-dark;
|
|
color: $body-color-dark;
|
|
}
|
|
|
|
// Sidebar - match TOC styling with muted colors - MAXIMUM SPECIFICITY
|
|
.sidebar,
|
|
.sidebar-navigation,
|
|
#quarto-sidebar,
|
|
.sidebar.sidebar-navigation {
|
|
background-color: $sidebar-bg-dark;
|
|
border-color: $border-color-dark;
|
|
|
|
// Part dividers (PART I, PART II, etc.) - keep accent color
|
|
.sidebar-item.sidebar-item-section > .sidebar-item-toggle,
|
|
.sidebar-item.sidebar-item-section .sidebar-item-toggle,
|
|
.part-divider {
|
|
color: $accent-dark !important;
|
|
font-weight: 600;
|
|
}
|
|
|
|
// ALL navigation links - very muted gray
|
|
a,
|
|
.sidebar-link,
|
|
.sidebar-item a,
|
|
.sidebar-item-text,
|
|
.menu-text,
|
|
.chapter-number {
|
|
color: #888888 !important;
|
|
font-weight: 400 !important;
|
|
|
|
&:hover {
|
|
color: $accent-dark !important;
|
|
background-color: rgba($accent-dark, 0.15) !important;
|
|
}
|
|
|
|
&.active,
|
|
&[aria-current="page"] {
|
|
color: $accent-dark !important;
|
|
background-color: rgba($accent-dark, 0.2) !important;
|
|
font-weight: 500 !important;
|
|
}
|
|
}
|
|
|
|
// Section containers
|
|
.sidebar-item-container,
|
|
.sidebar-item,
|
|
.sidebar-item-text {
|
|
color: #888888 !important;
|
|
}
|
|
}
|
|
|
|
// Additional high-specificity rules for stubborn elements
|
|
#quarto-sidebar .sidebar-menu a:not(.active),
|
|
#quarto-sidebar .sidebar-menu .sidebar-link:not(.active),
|
|
#quarto-sidebar .menu-text {
|
|
color: #888888 !important;
|
|
}
|
|
|
|
// Ensure active/hover states work
|
|
#quarto-sidebar a.active,
|
|
#quarto-sidebar a[aria-current="page"],
|
|
#quarto-sidebar .sidebar-link.active {
|
|
color: $accent-dark !important;
|
|
}
|
|
|
|
// TOC (table of contents) - Right sidebar "On this page"
|
|
.table-of-contents,
|
|
#TOC,
|
|
.quarto-toc,
|
|
nav[role="doc-toc"],
|
|
.sidebar.toc-left,
|
|
div[role="doc-toc"] {
|
|
background-color: transparent;
|
|
border-left-color: $accent-dark;
|
|
|
|
// TOC title
|
|
.toc-title,
|
|
.sidebar-title,
|
|
h2 {
|
|
color: #e6e6e6 !important;
|
|
border: none !important;
|
|
}
|
|
|
|
// All links in TOC - match sidebar muted level
|
|
a,
|
|
.nav-link {
|
|
color: #888888 !important;
|
|
|
|
&:hover {
|
|
color: $accent-dark !important;
|
|
}
|
|
}
|
|
|
|
.active,
|
|
.nav-link.active {
|
|
color: $accent-dark !important;
|
|
font-weight: 500 !important;
|
|
}
|
|
|
|
// Nested lists
|
|
ul, ol {
|
|
li {
|
|
color: #888888;
|
|
|
|
a {
|
|
color: #888888 !important;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Quarto sidebar toggle (mobile "On this page" button) - dark mode styling
|
|
.quarto-sidebar-toggle {
|
|
background-color: $sidebar-bg-dark !important;
|
|
border-color: $border-color-dark !important;
|
|
color: #e6e6e6 !important;
|
|
|
|
.quarto-sidebar-toggle-title {
|
|
color: #e6e6e6 !important;
|
|
background-color: $sidebar-bg-dark !important;
|
|
border-bottom-color: $border-color-dark !important;
|
|
}
|
|
|
|
.quarto-sidebar-toggle-icon {
|
|
color: #888888 !important;
|
|
}
|
|
|
|
.quarto-sidebar-toggle-contents {
|
|
background-color: $sidebar-bg-dark !important;
|
|
color: #e6e6e6 !important;
|
|
|
|
// Links in the toggle sidebar
|
|
a {
|
|
color: #888888 !important;
|
|
|
|
&:hover {
|
|
color: $accent-dark !important;
|
|
}
|
|
|
|
&.active {
|
|
color: $accent-dark !important;
|
|
font-weight: 500 !important;
|
|
}
|
|
}
|
|
|
|
// Lists in the toggle
|
|
ul, ol {
|
|
li {
|
|
color: #888888 !important;
|
|
|
|
a {
|
|
color: #888888 !important;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Headers - adjust border colors for dark mode
|
|
.content h2,
|
|
main h2,
|
|
article h2,
|
|
#quarto-content h2 {
|
|
border-left-color: $accent-dark;
|
|
border-bottom-color: rgba($accent-dark, 0.4);
|
|
color: #e6e6e6;
|
|
}
|
|
|
|
.content h3,
|
|
main h3,
|
|
article h3,
|
|
#quarto-content h3 {
|
|
border-left-color: $accent-dark;
|
|
border-bottom-color: rgba($accent-dark, 0.35);
|
|
color: #d0d0d0;
|
|
}
|
|
|
|
.content h4,
|
|
main h4,
|
|
article h4,
|
|
#quarto-content h4 {
|
|
border-left-color: $accent-dark;
|
|
border-bottom-color: rgba($accent-dark, 0.3);
|
|
color: #c0c0c0;
|
|
}
|
|
|
|
.content h5,
|
|
main h5,
|
|
article h5,
|
|
#quarto-content h5 {
|
|
border-left-color: $accent-dark;
|
|
border-bottom-color: rgba($accent-dark, 0.25);
|
|
color: #b0b0b0;
|
|
}
|
|
|
|
.content h6,
|
|
main h6,
|
|
article h6,
|
|
#quarto-content h6 {
|
|
border-left-color: $accent-dark;
|
|
border-bottom-color: rgba($accent-dark, 0.2);
|
|
color: #a0a0a0;
|
|
}
|
|
|
|
// Tables - improved contrast for dark mode
|
|
table {
|
|
background-color: #1a1a1a;
|
|
|
|
th {
|
|
background-color: #2c2c2c !important;
|
|
border-bottom: 2px solid $accent-dark !important;
|
|
color: #f0f0f0 !important;
|
|
font-weight: 600;
|
|
}
|
|
|
|
td {
|
|
background-color: #242424 !important;
|
|
border-bottom-color: #454d55 !important;
|
|
color: #e0e0e0 !important;
|
|
}
|
|
|
|
tbody tr:nth-child(even) {
|
|
background-color: #1e1e1e;
|
|
|
|
td {
|
|
background-color: #1e1e1e !important;
|
|
}
|
|
}
|
|
|
|
tbody tr:hover {
|
|
background-color: #2a2a2a !important;
|
|
|
|
td {
|
|
background-color: #2a2a2a !important;
|
|
}
|
|
}
|
|
|
|
td:first-child,
|
|
th:first-child {
|
|
background-color: #323232 !important;
|
|
border-right-color: #454d55;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
|
|
// Table container
|
|
.table-container,
|
|
.cell-output-display {
|
|
table {
|
|
th {
|
|
background-color: #2c2c2c !important;
|
|
color: #f0f0f0 !important;
|
|
}
|
|
|
|
td {
|
|
background-color: #242424 !important;
|
|
color: #e0e0e0 !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Code blocks
|
|
pre {
|
|
background-color: #2c2c2c;
|
|
color: #e6e6e6;
|
|
border: 1px solid #454d55;
|
|
}
|
|
|
|
// Inline code
|
|
code {
|
|
background-color: #2c2c2c;
|
|
color: #e94560; // Slightly pink/red for visibility
|
|
padding: 0.2em 0.4em;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
// Code blocks inside pre (override inline styling)
|
|
pre code {
|
|
color: #e6e6e6;
|
|
background-color: transparent;
|
|
padding: 0;
|
|
}
|
|
|
|
// Syntax highlighting colors for dark mode
|
|
// These override the light theme colors that become invisible on dark backgrounds
|
|
pre code,
|
|
.sourceCode {
|
|
// Operators (+=, +, *, etc.) - bright cyan for visibility
|
|
.op {
|
|
color: #56b6c2 !important;
|
|
}
|
|
|
|
// Variables - light gray instead of black
|
|
.va {
|
|
color: #e6e6e6 !important;
|
|
}
|
|
|
|
// Keywords (def, for, if, etc.) - keep purple but brighten
|
|
.kw {
|
|
color: #c678dd !important;
|
|
}
|
|
|
|
// Control flow (return, break, etc.)
|
|
.cf {
|
|
color: #e06c75 !important;
|
|
}
|
|
|
|
// Functions - bright blue/purple
|
|
.fu {
|
|
color: #61afef !important;
|
|
}
|
|
|
|
// Built-ins - orange
|
|
.bu {
|
|
color: #e5c07b !important;
|
|
}
|
|
|
|
// Strings - green
|
|
.st {
|
|
color: #98c379 !important;
|
|
}
|
|
|
|
// Comments - muted gray
|
|
.co {
|
|
color: #7f848e !important;
|
|
}
|
|
|
|
// Numbers (decimal values, floats)
|
|
.dv, .fl {
|
|
color: #d19a66 !important;
|
|
}
|
|
|
|
// Imports
|
|
.im {
|
|
color: #61afef !important;
|
|
}
|
|
|
|
// Attributes
|
|
.at {
|
|
color: #e06c75 !important;
|
|
}
|
|
|
|
// Data types
|
|
.dt {
|
|
color: #e5c07b !important;
|
|
}
|
|
|
|
// Special characters
|
|
.sc {
|
|
color: #56b6c2 !important;
|
|
}
|
|
|
|
// Preprocessor
|
|
.pp {
|
|
color: #e5c07b !important;
|
|
}
|
|
}
|
|
|
|
// Figures and captions
|
|
.figure-caption,
|
|
.caption,
|
|
figure figcaption,
|
|
.quarto-figure figcaption {
|
|
color: #c0c0c0 !important;
|
|
background-color: transparent !important;
|
|
}
|
|
|
|
// TikZ diagrams / images - add white background for visibility in dark mode
|
|
figure img,
|
|
.quarto-figure img {
|
|
background-color: white !important;
|
|
padding: 1rem !important;
|
|
border-radius: 8px !important;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
|
|
}
|
|
|
|
// Footnotes and margin notes - critical fix
|
|
.sidenote,
|
|
.marginnote,
|
|
.margin-note,
|
|
aside,
|
|
.aside,
|
|
.column-margin,
|
|
.column-margin p,
|
|
.margin-caption,
|
|
div[class*="margin"] {
|
|
color: #c0c0c0 !important;
|
|
background-color: transparent !important;
|
|
}
|
|
|
|
// Footnote references and content
|
|
.footnote-ref,
|
|
.footnotes,
|
|
.footnotes p,
|
|
.footnotes li,
|
|
#footnotes {
|
|
color: #c0c0c0 !important;
|
|
}
|
|
|
|
// Margin content (where footnotes appear)
|
|
.page-columns .column-margin {
|
|
color: #c0c0c0 !important;
|
|
}
|
|
|
|
// Quarto's margin content
|
|
.margin-caption,
|
|
.column-margin > * {
|
|
color: #c0c0c0 !important;
|
|
}
|
|
|
|
// Navbar
|
|
.navbar {
|
|
background-color: $navbar-bg-dark !important;
|
|
border-bottom: 1px solid $border-color-dark;
|
|
}
|
|
|
|
.navbar-nav .nav-link {
|
|
color: #adb5bd !important;
|
|
|
|
&:hover {
|
|
color: $link-color-dark !important;
|
|
}
|
|
|
|
&.active {
|
|
color: $link-color-dark !important;
|
|
}
|
|
|
|
// Ensure all Bootstrap icons in navbar have consistent color
|
|
.bi {
|
|
color: inherit !important;
|
|
fill: currentColor !important;
|
|
}
|
|
|
|
// Target SVG icons directly
|
|
svg {
|
|
fill: currentColor !important;
|
|
}
|
|
}
|
|
|
|
// Dark mode toggle - show sun icon when in dark mode (clicking switches TO light)
|
|
.quarto-color-scheme-toggle {
|
|
color: #adb5bd !important;
|
|
|
|
&:hover {
|
|
color: $link-color-dark !important;
|
|
}
|
|
}
|
|
|
|
// Sun icon in dark mode (indicates "switch to light mode")
|
|
// Try multiple selectors to ensure it works
|
|
.quarto-color-scheme-toggle.alternate .bi::before,
|
|
body.quarto-dark .quarto-color-scheme-toggle .bi::before,
|
|
html[data-bs-theme="dark"] .quarto-color-scheme-toggle .bi::before {
|
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="%23d1d5db" class="bi bi-sun-fill" viewBox="0 0 16 16"><path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.414a.5.5 0 1 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707z"/></svg>') !important;
|
|
background-size: contain !important;
|
|
background-repeat: no-repeat !important;
|
|
background-position: center !important;
|
|
content: "" !important;
|
|
display: inline-block !important;
|
|
width: 1em !important;
|
|
height: 1em !important;
|
|
}
|
|
|
|
// Footer - support all class names Quarto might use
|
|
.page-footer,
|
|
.footer,
|
|
.nav-footer,
|
|
footer {
|
|
background-color: $navbar-bg-dark !important;
|
|
border-top-color: $border-color-dark !important;
|
|
color: #adb5bd !important;
|
|
|
|
// Footer text elements
|
|
p, span, div {
|
|
color: #adb5bd !important;
|
|
}
|
|
|
|
// Footer links
|
|
a {
|
|
color: lighten($accent-dark, 10%) !important;
|
|
|
|
&:hover {
|
|
color: lighten($accent-dark, 25%) !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blockquotes - higher contrast but distinct from callouts
|
|
blockquote {
|
|
border-left-color: #8a93a0 !important;
|
|
background-color: #2a2a2a !important;
|
|
color: #e0e0e0 !important;
|
|
|
|
// Ensure nested elements inherit the brighter color
|
|
p, cite, em, strong {
|
|
color: #e0e0e0 !important;
|
|
}
|
|
|
|
// Attribution text
|
|
cite {
|
|
color: #c0c0c0 !important;
|
|
}
|
|
}
|
|
|
|
// Standard Quarto callouts - dark mode support
|
|
.callout,
|
|
.callout-note,
|
|
.callout-tip,
|
|
.callout-important,
|
|
.callout-caution,
|
|
.callout-warning,
|
|
.callout-example,
|
|
.callout-danger {
|
|
background-color: #212529 !important;
|
|
border-color: #454d55 !important;
|
|
color: #e6e6e6 !important;
|
|
|
|
// Callout headers
|
|
.callout-header,
|
|
.callout-title,
|
|
.callout-title-container {
|
|
color: #f0f0f0 !important;
|
|
background-color: rgba(255, 255, 255, 0.05) !important;
|
|
}
|
|
|
|
// Callout body content
|
|
.callout-body,
|
|
.callout-content {
|
|
color: #e6e6e6 !important;
|
|
|
|
p, li, span, div {
|
|
color: #e6e6e6 !important;
|
|
}
|
|
}
|
|
|
|
// Icons in callouts
|
|
.callout-icon {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
// Links in callouts
|
|
a {
|
|
color: lighten($accent-dark, 10%) !important;
|
|
|
|
&:hover {
|
|
color: lighten($accent-dark, 25%) !important;
|
|
}
|
|
}
|
|
|
|
// Code in callouts
|
|
code {
|
|
background-color: #2c2c2c !important;
|
|
color: #e94560 !important;
|
|
}
|
|
|
|
pre code {
|
|
color: #e6e6e6 !important;
|
|
}
|
|
}
|
|
|
|
// Specific callout type adjustments for dark mode
|
|
.callout-note {
|
|
border-left-color: #6b8cae !important;
|
|
|
|
.callout-header {
|
|
background-color: rgba(107, 140, 174, 0.12) !important;
|
|
}
|
|
}
|
|
|
|
.callout-tip {
|
|
border-left-color: #6a9c7b !important;
|
|
|
|
.callout-header {
|
|
background-color: rgba(106, 156, 123, 0.12) !important;
|
|
}
|
|
}
|
|
|
|
.callout-important {
|
|
border-left-color: lighten($accent-dark, 10%) !important;
|
|
|
|
.callout-header {
|
|
background-color: rgba($accent-dark, 0.15) !important;
|
|
}
|
|
}
|
|
|
|
.callout-caution,
|
|
.callout-warning {
|
|
border-left-color: #d4a017 !important;
|
|
|
|
.callout-header {
|
|
background-color: rgba(212, 160, 23, 0.12) !important;
|
|
}
|
|
}
|
|
|
|
.callout-example {
|
|
border-left-color: #2d9b94 !important;
|
|
|
|
.callout-header {
|
|
background-color: rgba(45, 155, 148, 0.12) !important;
|
|
}
|
|
}
|
|
|
|
// Video containers
|
|
.video-container {
|
|
border-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
// Embedded iframes (Google Looker Studio, etc.)
|
|
iframe {
|
|
background-color: #ffffff;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
// Wrapper for embedded content
|
|
div:has(> iframe) {
|
|
background-color: #2c2c2c;
|
|
padding: 1rem;
|
|
border-radius: 12px;
|
|
border: 1px solid #454d55;
|
|
}
|
|
|
|
// Support sections
|
|
.support-mission,
|
|
.podcast-section {
|
|
background-color: #232323;
|
|
border-color: #454d55;
|
|
|
|
p {
|
|
color: #d0d0d0;
|
|
}
|
|
}
|
|
|
|
// Button styling
|
|
.btn-primary {
|
|
background-color: $accent-dark;
|
|
border-color: $accent-dark;
|
|
color: #1a1a1a;
|
|
font-weight: 500;
|
|
|
|
&:hover {
|
|
background-color: lighten($accent-dark, 10%);
|
|
border-color: lighten($accent-dark, 10%);
|
|
}
|
|
}
|
|
|
|
.github-star-btn {
|
|
background-color: $accent-dark !important;
|
|
color: #1a1a1a !important;
|
|
|
|
&:hover {
|
|
background-color: lighten($accent-dark, 10%) !important;
|
|
}
|
|
}
|
|
|
|
// Back to top button
|
|
.back-to-top,
|
|
#quarto-back-to-top {
|
|
background-color: $accent-dark !important;
|
|
color: #1a1a1a !important;
|
|
border: 1px solid lighten($accent-dark, 10%);
|
|
|
|
&:hover {
|
|
background-color: lighten($accent-dark, 15%) !important;
|
|
}
|
|
}
|