mirror of
https://github.com/kleokl7/Claude-Code-Obsidian-Theme.git
synced 2026-07-22 04:50:29 +00:00
- release.sh guard: escape version dots and require the versions.json entry to match manifest.json's minAppVersion (stale copy-paste trap) - chevron: declare position:relative ourselves instead of leaning on stock; drop the dead background-image:none; honest comment about mirrored stock metrics - body --font-weight fallback removed — dead since both schemes now bind it to their tuning dial - install script: usage on empty input; read without -r so drag-and-dropped (backslash-escaped) vault paths work - roadmap: note the pre-existing callout-table border gap; point at README for the release procedure instead of duplicating it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1737 lines
74 KiB
CSS
1737 lines
74 KiB
CSS
/* =====================================================================
|
||
CLAUDE CODE ORANGE — an Obsidian theme
|
||
---------------------------------------------------------------------
|
||
A faithful recreation of the look & feel of Claude Code in the Claude
|
||
mobile app: warm cream / charcoal backgrounds, the coral accent,
|
||
Styrene-style grotesque type, and a calm reading rhythm.
|
||
|
||
Fonts are free lookalikes for Claude's proprietary typefaces, loaded
|
||
from Google Fonts over the web (intentionally not self-hosted):
|
||
• Hanken Grotesk ≈ Styrene B (interface + body)
|
||
• Source Serif 4 ≈ Tiempos Text (optional serif reading mode)
|
||
• JetBrains Mono ≈ code typeface (code blocks / inline code)
|
||
If you own the real fonts (Styrene B / Tiempos Text), they sit at the
|
||
front of every stack and will be used automatically.
|
||
|
||
Works in both Light and Dark. Supports the Style Settings plugin.
|
||
===================================================================== */
|
||
|
||
@import url('https://fonts.googleapis.com/css2?family=Hanken+Grotesk:ital,wght@0,300..800;1,300..800&family=Source+Serif+4:ital,opsz,wght@0,8..60,300..700;1,8..60,300..700&family=JetBrains+Mono:ital,wght@0,400..700;1,400..700&display=swap');
|
||
|
||
/* ---------------------------------------------------------------------
|
||
STYLE SETTINGS (https://github.com/mgmeyers/obsidian-style-settings)
|
||
--------------------------------------------------------------------- */
|
||
/* @settings
|
||
|
||
name: Claude Code Orange
|
||
id: claude-code-theme
|
||
settings:
|
||
-
|
||
id: cc-typography
|
||
title: Typography
|
||
type: heading
|
||
level: 1
|
||
collapsed: false
|
||
-
|
||
id: claude-serif-body
|
||
title: Serif reading mode
|
||
description: Use a Tiempos-style serif for note body text, like Claude's serif reading mode. Headings and the interface stay sans.
|
||
type: class-toggle
|
||
default: false
|
||
-
|
||
id: claude-serif-headings
|
||
title: Serif headings
|
||
description: Render headings in the serif as well (pairs nicely with serif reading mode).
|
||
type: class-toggle
|
||
default: false
|
||
-
|
||
id: cc-body-font-size
|
||
title: Body font size (px)
|
||
description: Base font size for note text.
|
||
type: variable-number-slider
|
||
default: 18
|
||
min: 13
|
||
max: 22
|
||
step: 1
|
||
format: px
|
||
-
|
||
id: cc-line-height
|
||
title: Line height
|
||
description: Body line height.
|
||
type: variable-number-slider
|
||
default: 1.7
|
||
min: 1.2
|
||
max: 2
|
||
step: 0.05
|
||
-
|
||
id: cc-line-width
|
||
title: Readable line length (rem)
|
||
description: Maximum width of note text when a line-length limit is active (Obsidian's "Readable line length" or the toggle below).
|
||
type: variable-number-slider
|
||
default: 42
|
||
min: 30
|
||
max: 60
|
||
step: 1
|
||
format: rem
|
||
-
|
||
id: cc-force-line-width
|
||
title: Limit line length
|
||
description: Keep note text at a readable width even when Obsidian's "Readable line length" setting is off.
|
||
type: class-toggle
|
||
default: false
|
||
-
|
||
id: cc-nav-font-size
|
||
title: File & folder name size (px)
|
||
description: Font size for names in the file explorer.
|
||
type: variable-number-slider
|
||
default: 16
|
||
min: 11
|
||
max: 20
|
||
step: 1
|
||
format: px
|
||
-
|
||
id: cc-folder-weight
|
||
title: Folder name weight
|
||
description: Weight of folder names in the file explorer.
|
||
type: variable-select
|
||
default: '700'
|
||
options:
|
||
-
|
||
label: Medium
|
||
value: '500'
|
||
-
|
||
label: Semibold
|
||
value: '600'
|
||
-
|
||
label: Bold
|
||
value: '700'
|
||
-
|
||
id: cc-file-weight
|
||
title: File name weight
|
||
description: Weight of file names in the file explorer.
|
||
type: variable-select
|
||
default: '500'
|
||
options:
|
||
-
|
||
label: Regular
|
||
value: '400'
|
||
-
|
||
label: Medium
|
||
value: '500'
|
||
-
|
||
label: Semibold
|
||
value: '600'
|
||
-
|
||
id: cc-color
|
||
title: Color
|
||
type: heading
|
||
level: 1
|
||
collapsed: false
|
||
-
|
||
id: cc-accent-light
|
||
title: Accent color (light mode)
|
||
description: The Claude coral. Drives links, active elements and highlights.
|
||
type: variable-color
|
||
format: hex
|
||
default: '#c15f3c'
|
||
-
|
||
id: cc-accent-dark
|
||
title: Accent color (dark mode)
|
||
description: The Claude coral for dark mode.
|
||
type: variable-color
|
||
format: hex
|
||
default: '#d97757'
|
||
-
|
||
id: cc-loud-code
|
||
title: Loud code blocks
|
||
description: On — code blocks use a blue frame so they stand out. Off — code blocks stay warm, in line with the Claude coral theme.
|
||
type: class-toggle
|
||
default: true
|
||
-
|
||
id: claude-mono-headings
|
||
title: Monospace headings
|
||
description: Give headings a terminal / code feel by setting them in the monospace font.
|
||
type: class-toggle
|
||
default: false
|
||
-
|
||
id: cc-light-tuning
|
||
title: Light mode tuning
|
||
type: heading
|
||
level: 1
|
||
collapsed: false
|
||
-
|
||
id: cc-light-text-depth
|
||
title: Text darkness (%)
|
||
description: Black mixed into light-mode body text. 0 = original warm ink, higher = darker and stronger.
|
||
type: variable-number-slider
|
||
default: 0
|
||
min: 0
|
||
max: 30
|
||
step: 1
|
||
format: '%'
|
||
-
|
||
id: cc-light-font-weight
|
||
title: Text weight
|
||
description: Light-mode body font weight. Needs a variable font — the bundled Hanken Grotesk and Source Serif 4 both are; static fonts round to the nearest weight.
|
||
type: variable-number-slider
|
||
default: 400
|
||
min: 380
|
||
max: 500
|
||
step: 5
|
||
-
|
||
id: cc-light-bg-depth
|
||
title: Background depth (%)
|
||
description: Black mixed into the light-mode backgrounds. 0 = default warm cream, higher = deeper paper. All light surfaces deepen together so their hierarchy is preserved.
|
||
type: variable-number-slider
|
||
default: 0
|
||
min: 0
|
||
max: 15
|
||
step: 1
|
||
format: '%'
|
||
-
|
||
id: cc-dark-tuning
|
||
title: Dark mode tuning
|
||
type: heading
|
||
level: 1
|
||
collapsed: false
|
||
-
|
||
id: cc-dark-text-bright
|
||
title: Text brightness (%)
|
||
description: White mixed into dark-mode body text. 0 = original warm off-white, higher = brighter.
|
||
type: variable-number-slider
|
||
default: 15
|
||
min: 0
|
||
max: 40
|
||
step: 1
|
||
format: '%'
|
||
-
|
||
id: cc-dark-font-weight
|
||
title: Text weight
|
||
description: Dark-mode body font weight. Needs a variable font — the bundled Hanken Grotesk and Source Serif 4 both are; static fonts round to the nearest weight.
|
||
type: variable-number-slider
|
||
default: 430
|
||
min: 380
|
||
max: 500
|
||
step: 5
|
||
-
|
||
id: cc-dark-bg-depth
|
||
title: Background darkness (%)
|
||
description: Black mixed into the dark-mode backgrounds. 0 = default warm charcoal, higher = deeper. Raises contrast without making the text glow.
|
||
type: variable-number-slider
|
||
default: 0
|
||
min: 0
|
||
max: 30
|
||
step: 1
|
||
format: '%'
|
||
-
|
||
id: cc-editor
|
||
title: Editor
|
||
type: heading
|
||
level: 1
|
||
collapsed: false
|
||
-
|
||
id: cc-active-line
|
||
title: Highlight active line
|
||
description: Add a very light tint behind the line your cursor is currently on in the editor.
|
||
type: class-toggle
|
||
default: true
|
||
-
|
||
id: cc-scroll-progress
|
||
title: Scroll progress bar
|
||
description: A thin coral bar across the top of the note (bottom of the screen on mobile) showing how far you've scrolled. Pure CSS — pairs with the Claude Scroll Map companion plugin for clickable heading dots.
|
||
type: class-toggle
|
||
default: true
|
||
-
|
||
id: cc-plugins
|
||
title: Plugins
|
||
type: heading
|
||
level: 1
|
||
collapsed: false
|
||
-
|
||
id: cc-sticky-in-header
|
||
title: Sticky headings in the title bar
|
||
description: With the "Another Sticky Headings" plugin, show the heading trail as a clickable breadcrumb in the note title bar (replacing the note title while you're scrolled into a section) instead of a block above the note. Desktop only — on mobile the plugin keeps its regular stacked view. No effect if the plugin isn't installed.
|
||
type: class-toggle
|
||
default: true
|
||
*/
|
||
|
||
/* =====================================================================
|
||
1. GLOBAL — fonts, radii, shared tokens
|
||
===================================================================== */
|
||
body {
|
||
/* --- Font stacks: real Claude fonts first, free lookalikes next --- */
|
||
--cc-font-sans: "Styrene B", "Styrene A", "Hanken Grotesk", "Schibsted Grotesk",
|
||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
--cc-font-serif: "Tiempos Text", "Tiempos", "Source Serif 4", "Source Serif Pro",
|
||
Charter, Georgia, "Times New Roman", serif;
|
||
--cc-font-mono: "Söhne Mono", "Berkeley Mono", "JetBrains Mono", "Fira Code",
|
||
ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
||
|
||
--font-interface-theme: var(--cc-font-sans);
|
||
--font-text-theme: var(--cc-font-sans);
|
||
--font-monospace-theme: var(--cc-font-mono);
|
||
|
||
/* --- Sizing (Style-Settings driven) --- */
|
||
--cc-body-font-size: 18px;
|
||
/* 1.7 rather than the classic 1.5–1.6: full-width panes mean long lines,
|
||
and long lines need extra leading to track back to the next line. */
|
||
--cc-line-height: 1.7;
|
||
--cc-line-width: 42rem;
|
||
--font-text-size: var(--cc-body-font-size);
|
||
--line-height-normal: var(--cc-line-height);
|
||
--file-line-width: var(--cc-line-width);
|
||
|
||
/* Dark-mode tuning dials (Style Settings → Dark mode tuning). Declared
|
||
here on body so the plugin's body-level overrides win; only consumed
|
||
inside .theme-dark. Defaults reproduce the current dark palette. */
|
||
--cc-dark-text-bright: 15%;
|
||
--cc-dark-font-weight: 430;
|
||
--cc-dark-bg-depth: 0%;
|
||
|
||
/* Light-mode tuning dials (Style Settings → Light mode tuning). Same
|
||
pattern; only consumed inside .theme-light. Defaults reproduce the
|
||
classic cream palette exactly. */
|
||
--cc-light-text-depth: 0%;
|
||
--cc-light-font-weight: 400;
|
||
--cc-light-bg-depth: 0%;
|
||
|
||
/* Spacing — Obsidian's defaults (--p-spacing 1rem, --heading-spacing
|
||
~2.4rem) cut by 50% */
|
||
--p-spacing: 0.5rem;
|
||
--heading-spacing: 1.2rem;
|
||
|
||
/* --- Shape: Claude uses generous, soft corners --- */
|
||
--radius-s: 6px;
|
||
--radius-m: 10px;
|
||
--cc-radius: 12px;
|
||
|
||
--bold-weight: 700;
|
||
/* --font-weight is set per scheme: .theme-light and .theme-dark each
|
||
bind it to their tuning dial (defaults 400 / 430). */
|
||
|
||
/* File explorer name weights + size (Style-Settings driven) */
|
||
--cc-folder-weight: 700;
|
||
--cc-file-weight: 500;
|
||
--cc-nav-font-size: 16px;
|
||
--h1-weight: 700;
|
||
--h2-weight: 700;
|
||
--h3-weight: 600;
|
||
--h4-weight: 600;
|
||
--h5-weight: 600;
|
||
--h6-weight: 600;
|
||
}
|
||
|
||
/* =====================================================================
|
||
2. LIGHT MODE — warm paper palette
|
||
===================================================================== */
|
||
.theme-light {
|
||
--cc-accent-light: #c15f3c; /* Crail — brand coral for UI elements */
|
||
--cc-accent-bright: #d97757; /* Claude coral */
|
||
--cc-accent: var(--cc-accent-light); /* picker-driven base for all tints */
|
||
--cc-link-light: #b0532f; /* darker coral for link TEXT only — 4.6:1 on cream (AA) */
|
||
|
||
/* Backgrounds: warm paper cream, deepened by the "Background depth"
|
||
dial (0% = the classic cream palette). All four move together so the
|
||
surface hierarchy is preserved. Muted/faint text and fixed companions
|
||
(form fields, scrollbar thumbs, code parchment) don't move with the
|
||
dial, which is why it caps at 15% — beyond that they drift apart. */
|
||
--cc-bg: color-mix(in srgb, black var(--cc-light-bg-depth), #f9f3e7); /* main note surface (warm paper) */
|
||
--cc-bg-alt: color-mix(in srgb, black var(--cc-light-bg-depth), #f0e9da); /* sidebars / secondary */
|
||
--cc-bg-deep: color-mix(in srgb, black var(--cc-light-bg-depth), #e8dfcc); /* code / inset surfaces */
|
||
--cc-bg-hover: color-mix(in srgb, black var(--cc-light-bg-depth), #e7decb);
|
||
|
||
/* Body text: warm dark ink, deepened toward black by the "Text
|
||
darkness" dial (default 0% = #34322c). */
|
||
--cc-text: color-mix(in srgb, black var(--cc-light-text-depth), #34322c);
|
||
--cc-text-muted: #6e6a5c;
|
||
--cc-text-faint: #9a9684;
|
||
--cc-heading: #1a1915; /* near-black warm heading */
|
||
|
||
/* Dial: "Text weight" (default 400 = unchanged). */
|
||
--font-weight: var(--cc-light-font-weight);
|
||
--cc-border: #e6e3d8;
|
||
--cc-border-soft:#efece3;
|
||
--cc-selection: color-mix(in srgb, var(--cc-accent) 18%, transparent);
|
||
--cc-active-line-bg: color-mix(in srgb, var(--cc-accent) 5%, transparent);
|
||
|
||
/* Scroll-map markers. Unread dots fade by opacity over the note bg; on
|
||
cream that washes them toward beige, so light mode holds more of the
|
||
coral than dark does. The unread track also needs a darker line than
|
||
the general border to stay visible on paper. */
|
||
--cc-dot-fade: 0.6;
|
||
--cc-track-color: #d9d3c4;
|
||
|
||
color-scheme: light;
|
||
|
||
--accent-h: 16;
|
||
--accent-s: 56%;
|
||
--accent-l: 50%;
|
||
|
||
--background-primary: var(--cc-bg);
|
||
--background-primary-alt: var(--cc-bg-alt);
|
||
--background-secondary: var(--cc-bg-alt);
|
||
--background-secondary-alt: var(--cc-bg-deep);
|
||
--background-modifier-hover: var(--cc-bg-hover);
|
||
--background-modifier-active-hover: var(--cc-selection);
|
||
--background-modifier-border: var(--cc-border);
|
||
--background-modifier-border-hover: #dcd8cc;
|
||
--background-modifier-border-focus: var(--cc-accent-light);
|
||
--background-modifier-form-field: #fffdf7;
|
||
|
||
--text-normal: var(--cc-text);
|
||
--text-muted: var(--cc-text-muted);
|
||
--text-faint: var(--cc-text-faint);
|
||
--text-on-accent: #fffaf5;
|
||
--text-accent: var(--cc-link-light); /* AA-safe link text; brand coral stays on UI elements */
|
||
--text-accent-hover: var(--cc-accent-bright);
|
||
--text-selection: var(--cc-selection);
|
||
--text-highlight-bg: color-mix(in srgb, var(--cc-accent) 22%, transparent);
|
||
|
||
--interactive-accent: var(--cc-accent-light);
|
||
--interactive-accent-hover: var(--cc-accent-bright);
|
||
--interactive-normal: var(--cc-bg-alt);
|
||
--interactive-hover: var(--cc-bg-hover);
|
||
|
||
--titlebar-background: var(--cc-bg-alt);
|
||
--titlebar-background-focused:var(--cc-bg-alt);
|
||
--tab-background-active: color-mix(in srgb, var(--cc-accent) 16%, var(--cc-bg));
|
||
|
||
--quote-opacity: 1;
|
||
--hr-color: var(--cc-border);
|
||
--tag-color: var(--cc-accent-light);
|
||
--tag-background: color-mix(in srgb, var(--cc-accent) 12%, transparent);
|
||
--tag-background-hover: color-mix(in srgb, var(--cc-accent) 20%, transparent);
|
||
--blockquote-border-color: var(--cc-accent-bright);
|
||
--blockquote-background-color: transparent;
|
||
--scrollbar-thumb-bg: rgba(52, 50, 44, 0.16);
|
||
--scrollbar-active-thumb-bg: rgba(52, 50, 44, 0.32);
|
||
|
||
/* Code frame — warm by default; "Loud code blocks" switches it to blue */
|
||
--cc-code-bg: #f0e8d6; /* warm parchment inset */
|
||
--cc-code-border: #e1d5bd;
|
||
--cc-code-text: #5c4f3b; /* warm code text */
|
||
--code-background: var(--cc-code-bg);
|
||
--code-normal: var(--cc-code-text);
|
||
|
||
--list-marker-color: var(--cc-accent-light); /* orange bullets */
|
||
--cc-table-bg: #f7f1e5; /* table body — quiet warm */
|
||
--cc-table-header: #f1dcc4; /* header — keeps the orange */
|
||
--cc-table-border: #e6dcc9; /* soft, muted grid lines */
|
||
|
||
/* Graph view — coral nodes, clearly visible warm links */
|
||
--graph-line: #c0ad93;
|
||
--graph-node: #c15f3c;
|
||
--graph-node-focused: #d97757;
|
||
--graph-node-tag: #9a8f78;
|
||
--graph-node-attachment: #b3a78e;
|
||
--graph-node-unresolved: #bcb4a2;
|
||
--graph-text: #34322c;
|
||
|
||
--bold-color: var(--cc-heading);
|
||
--italic-color: inherit;
|
||
--metadata-label-text-color: var(--cc-text-muted);
|
||
}
|
||
|
||
/* =====================================================================
|
||
3. DARK MODE — warm charcoal palette
|
||
===================================================================== */
|
||
.theme-dark {
|
||
--cc-accent-dark: #d97757; /* Claude coral */
|
||
--cc-accent-bright: #e8916f; /* lifted coral for links on dark */
|
||
--cc-accent: var(--cc-accent-dark); /* picker-driven base for all tints */
|
||
|
||
/* Backgrounds: warm charcoal, deepened by the "Background darkness"
|
||
dial (0% = the classic Claude palette). All four move together so
|
||
the surface hierarchy is preserved. */
|
||
--cc-bg: color-mix(in srgb, black var(--cc-dark-bg-depth), #262624); /* main note surface */
|
||
--cc-bg-alt: color-mix(in srgb, black var(--cc-dark-bg-depth), #1f1e1d); /* sidebars / secondary */
|
||
--cc-bg-deep: color-mix(in srgb, black var(--cc-dark-bg-depth), #1a1917); /* code / inset surfaces */
|
||
--cc-bg-hover: color-mix(in srgb, black var(--cc-dark-bg-depth), #302f2c);
|
||
|
||
/* Body text: warm off-white base, lifted toward white by the "Text
|
||
brightness" dial (default 15% ≈ #eae8e0). */
|
||
--cc-text: color-mix(in srgb, white var(--cc-dark-text-bright), #e6e4dc);
|
||
--cc-text-muted: #a8a496;
|
||
--cc-text-faint: #767264;
|
||
--cc-heading: #f6f4ee; /* bright warm heading */
|
||
|
||
/* Light-on-dark reads thinner than dark-on-light; a variable-font nudge
|
||
past 400 compensates. Dial: "Text weight". */
|
||
--font-weight: var(--cc-dark-font-weight);
|
||
--cc-border: #393833;
|
||
--cc-border-soft:#322f2a;
|
||
--cc-selection: color-mix(in srgb, var(--cc-accent) 26%, transparent);
|
||
--cc-active-line-bg: color-mix(in srgb, var(--cc-accent) 8%, transparent);
|
||
|
||
/* Scroll-map markers: on the dark charcoal a 0.45 coral already reads as
|
||
a clear dim dot, and the general border works as the unread track. */
|
||
--cc-dot-fade: 0.45;
|
||
--cc-track-color: var(--cc-border);
|
||
|
||
color-scheme: dark;
|
||
|
||
--accent-h: 16;
|
||
--accent-s: 62%;
|
||
--accent-l: 59%;
|
||
|
||
--background-primary: var(--cc-bg);
|
||
--background-primary-alt: var(--cc-bg-alt);
|
||
--background-secondary: var(--cc-bg-alt);
|
||
--background-secondary-alt: var(--cc-bg-deep);
|
||
--background-modifier-hover: var(--cc-bg-hover);
|
||
--background-modifier-active-hover: var(--cc-selection);
|
||
--background-modifier-border: var(--cc-border);
|
||
--background-modifier-border-hover: #4a4942;
|
||
--background-modifier-border-focus: var(--cc-accent-dark);
|
||
--background-modifier-form-field: #1c1b19;
|
||
|
||
--text-normal: var(--cc-text);
|
||
--text-muted: var(--cc-text-muted);
|
||
--text-faint: var(--cc-text-faint);
|
||
--text-on-accent: #1a1915;
|
||
--text-accent: var(--cc-accent-bright);
|
||
--text-accent-hover: #f2a587;
|
||
--text-selection: var(--cc-selection);
|
||
--text-highlight-bg: color-mix(in srgb, var(--cc-accent) 28%, transparent);
|
||
|
||
--interactive-accent: var(--cc-accent-dark);
|
||
--interactive-accent-hover: var(--cc-accent-bright);
|
||
--interactive-normal: var(--cc-bg-alt);
|
||
--interactive-hover: var(--cc-bg-hover);
|
||
|
||
--titlebar-background: var(--cc-bg-alt);
|
||
--titlebar-background-focused:var(--cc-bg-alt);
|
||
--tab-background-active: color-mix(in srgb, var(--cc-accent) 16%, var(--cc-bg));
|
||
|
||
--quote-opacity: 1;
|
||
--hr-color: var(--cc-border);
|
||
--tag-color: var(--cc-accent-bright);
|
||
--tag-background: color-mix(in srgb, var(--cc-accent) 16%, transparent);
|
||
--tag-background-hover: color-mix(in srgb, var(--cc-accent) 26%, transparent);
|
||
--blockquote-border-color: var(--cc-accent-dark);
|
||
--blockquote-background-color: transparent;
|
||
--scrollbar-thumb-bg: rgba(230, 228, 220, 0.16);
|
||
--scrollbar-active-thumb-bg: rgba(230, 228, 220, 0.3);
|
||
|
||
/* Code frame — warm by default; "Loud code blocks" switches it to blue */
|
||
--cc-code-bg: #1f1b15; /* warm dark inset */
|
||
--cc-code-border: #382f23;
|
||
--cc-code-text: #d8cbb3; /* warm code text */
|
||
--code-background: var(--cc-code-bg);
|
||
--code-normal: var(--cc-code-text);
|
||
|
||
--list-marker-color: var(--cc-accent-dark); /* orange bullets */
|
||
--cc-table-bg: #2b2824; /* table body — quiet warm lift */
|
||
--cc-table-header: #3a3128; /* header — clean warm (not muddy) */
|
||
--cc-table-border: #423a2f; /* soft, muted grid lines */
|
||
|
||
/* Graph view — salmon nodes, clearly visible links on charcoal */
|
||
--graph-line: #5f594f;
|
||
--graph-node: #e8916f;
|
||
--graph-node-focused: #f0a487;
|
||
--graph-node-tag: #8a8174;
|
||
--graph-node-attachment: #6f685d;
|
||
--graph-node-unresolved: #6e685d;
|
||
--graph-text: #e6e4dc;
|
||
|
||
--bold-color: var(--cc-heading);
|
||
--italic-color: inherit;
|
||
--metadata-label-text-color: var(--cc-text-muted);
|
||
}
|
||
|
||
/* =====================================================================
|
||
4. TYPOGRAPHY — headings, body, bold, italics, links
|
||
===================================================================== */
|
||
|
||
/* Headings: tight, confident, like Claude's response titles */
|
||
.markdown-rendered h1, .markdown-rendered h2, .markdown-rendered h3,
|
||
.markdown-rendered h4, .markdown-rendered h5, .markdown-rendered h6,
|
||
.cm-header, .inline-title,
|
||
.HyperMD-header {
|
||
font-family: var(--cc-font-sans);
|
||
letter-spacing: -0.018em;
|
||
color: var(--cc-heading);
|
||
}
|
||
|
||
.inline-title { font-weight: 700; letter-spacing: -0.02em; }
|
||
|
||
:is(.markdown-rendered, .markdown-source-view) h1,
|
||
.HyperMD-header-1 { --h1-size: 1.9em; line-height: 1.2; }
|
||
:is(.markdown-rendered, .markdown-source-view) h2,
|
||
.HyperMD-header-2 { --h2-size: 1.5em; line-height: 1.25; }
|
||
:is(.markdown-rendered, .markdown-source-view) h3,
|
||
.HyperMD-header-3 { --h3-size: 1.25em; line-height: 1.3; }
|
||
:is(.markdown-rendered, .markdown-source-view) h4,
|
||
.HyperMD-header-4 { --h4-size: 1.08em; }
|
||
:is(.markdown-rendered, .markdown-source-view) h5,
|
||
.HyperMD-header-5 { --h5-size: 0.95em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--cc-text-muted); }
|
||
:is(.markdown-rendered, .markdown-source-view) h6,
|
||
.HyperMD-header-6 { --h6-size: 0.88em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--cc-text-faint); }
|
||
|
||
/* The # markers stay quietly out of the way */
|
||
.HyperMD-header .cm-formatting-header,
|
||
.markdown-rendered .heading-collapse-indicator { color: var(--cc-text-faint); }
|
||
|
||
/* Active line — a very light tint behind the row the cursor is on.
|
||
Toggle: Style Settings → Claude Code Orange → Editor → Highlight active line. */
|
||
body.cc-active-line .markdown-source-view.mod-cm6 .cm-active.cm-line,
|
||
body.cc-active-line .markdown-source-view.mod-cm6 .cm-active.cm-gutterElement {
|
||
background-color: var(--cc-active-line-bg);
|
||
border-radius: var(--radius-s);
|
||
}
|
||
|
||
/* Body rhythm */
|
||
.markdown-preview-view, .markdown-source-view {
|
||
font-family: var(--cc-font-sans);
|
||
text-rendering: optimizeLegibility;
|
||
}
|
||
/* Grayscale antialiasing thins light-on-dark strokes on macOS, so it stays
|
||
light-mode only; dark mode keeps the heavier default rendering. */
|
||
.theme-light .markdown-preview-view,
|
||
.theme-light .markdown-source-view {
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
.markdown-rendered p { line-height: var(--cc-line-height); }
|
||
|
||
/* Opt-in width cap independent of Obsidian's "Readable line length" setting.
|
||
Toggle: Style Settings → Typography → Limit line length. */
|
||
body.cc-force-line-width .markdown-source-view.mod-cm6 .cm-sizer,
|
||
body.cc-force-line-width .markdown-preview-view .markdown-preview-sizer {
|
||
max-width: var(--file-line-width);
|
||
margin-inline: auto;
|
||
}
|
||
|
||
/* Bold — Claude leans on weight + a touch more color contrast */
|
||
.markdown-rendered strong, .cm-strong {
|
||
font-weight: var(--bold-weight);
|
||
color: var(--bold-color);
|
||
}
|
||
|
||
/* Italics keep the body color, just slanted */
|
||
.markdown-rendered em, .cm-em { font-style: italic; color: var(--italic-color); }
|
||
|
||
/* Strikethrough — de-emphasised: muted text with a soft line through */
|
||
.markdown-rendered del, .markdown-rendered s,
|
||
.cm-s-obsidian .cm-strikethrough {
|
||
color: var(--cc-text-muted);
|
||
text-decoration-line: line-through;
|
||
text-decoration-color: var(--cc-text-faint);
|
||
text-decoration-thickness: 1px;
|
||
}
|
||
|
||
/* Highlight (==text==) — coral-tinted mark, body text on top */
|
||
.markdown-rendered mark {
|
||
background-color: var(--text-highlight-bg);
|
||
color: var(--cc-text);
|
||
border-radius: 3px;
|
||
padding: 0.05em 0.18em;
|
||
}
|
||
.cm-s-obsidian .cm-highlight {
|
||
background-color: var(--text-highlight-bg);
|
||
color: var(--cc-text);
|
||
}
|
||
|
||
/* Link decoration is driven through Obsidian's own variables so nothing can
|
||
silently re-underline: internal/page links never underline; external do. */
|
||
body {
|
||
--link-decoration: none;
|
||
--link-decoration-hover: none;
|
||
--link-external-decoration: underline;
|
||
--link-external-decoration-hover: underline;
|
||
}
|
||
|
||
/* All links use the Claude coral */
|
||
.markdown-rendered a,
|
||
.cm-link, .cm-url {
|
||
color: var(--text-accent);
|
||
transition: color 120ms ease, text-decoration-color 120ms ease;
|
||
}
|
||
.markdown-rendered a:hover,
|
||
.cm-link:hover, .cm-url:hover {
|
||
color: var(--text-accent-hover);
|
||
text-decoration-color: var(--text-accent-hover);
|
||
}
|
||
|
||
/* External links: underlined in coral with a soft offset */
|
||
.markdown-rendered a.external-link,
|
||
.cm-link, .cm-url {
|
||
text-decoration-line: underline;
|
||
text-decoration-color: color-mix(in srgb, var(--cc-accent) 45%, transparent);
|
||
text-underline-offset: 0.18em;
|
||
text-decoration-thickness: 1px;
|
||
}
|
||
a.external-link { background-image: none; padding-right: 0; }
|
||
|
||
/* Internal / page links: bold + orange, never underlined (reading + edit) */
|
||
a.internal-link,
|
||
a.internal-link.is-unresolved,
|
||
.markdown-rendered a.internal-link,
|
||
.markdown-preview-view a.internal-link,
|
||
.cm-hmd-internal-link,
|
||
.cm-s-obsidian .cm-hmd-internal-link {
|
||
font-weight: 700 !important;
|
||
color: var(--text-accent) !important;
|
||
text-decoration: none !important;
|
||
}
|
||
/* The span Obsidian uses to draw the internal-link underline in live preview */
|
||
.cm-s-obsidian .cm-underline,
|
||
.markdown-rendered a.internal-link .cm-underline {
|
||
text-decoration: none !important;
|
||
}
|
||
a.internal-link:hover,
|
||
.markdown-rendered a.internal-link:hover,
|
||
.cm-hmd-internal-link:hover {
|
||
color: var(--text-accent-hover) !important;
|
||
text-decoration: none !important;
|
||
}
|
||
|
||
/* =====================================================================
|
||
5. SERIF READING MODE (Style Settings toggles)
|
||
===================================================================== */
|
||
body.claude-serif-body {
|
||
--font-text-theme: var(--cc-font-serif);
|
||
}
|
||
body.claude-serif-body .markdown-preview-view,
|
||
body.claude-serif-body .markdown-source-view {
|
||
font-family: var(--cc-font-serif);
|
||
--cc-line-height: 1.7;
|
||
}
|
||
body.claude-serif-headings :is(.markdown-rendered h1, .markdown-rendered h2,
|
||
.markdown-rendered h3, .markdown-rendered h4, .markdown-rendered h5,
|
||
.markdown-rendered h6, .cm-header, .inline-title) {
|
||
font-family: var(--cc-font-serif);
|
||
letter-spacing: -0.01em;
|
||
}
|
||
body.claude-mono-headings :is(.markdown-rendered h1, .markdown-rendered h2,
|
||
.markdown-rendered h3, .markdown-rendered h4, .markdown-rendered h5,
|
||
.markdown-rendered h6, .cm-header, .inline-title) {
|
||
font-family: var(--cc-font-mono);
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
/* =====================================================================
|
||
6. CODE — the heart of Claude Code
|
||
===================================================================== */
|
||
|
||
/* "Loud code blocks" (Style Settings, on by default): blue frame + text so
|
||
code stands out. Off → the warm defaults above, in line with the theme. */
|
||
body.cc-loud-code.theme-light {
|
||
--cc-code-bg: #eaf1fd; /* light blue */
|
||
--cc-code-border: #cadcf3;
|
||
--cc-code-text: #11589f; /* deep azure */
|
||
}
|
||
body.cc-loud-code.theme-dark {
|
||
--cc-code-bg: #182234; /* deep blue */
|
||
--cc-code-border: #2b3c59;
|
||
--cc-code-text: #7db7ff; /* sky blue */
|
||
}
|
||
|
||
/* Inline code: pill in the same family as code blocks (consistent) */
|
||
.markdown-rendered code,
|
||
:not(pre) > code {
|
||
font-family: var(--cc-font-mono);
|
||
font-size: 0.86em;
|
||
background-color: var(--cc-code-bg);
|
||
color: var(--cc-code-text);
|
||
border: 1px solid var(--cc-code-border);
|
||
border-radius: var(--radius-s);
|
||
padding: 0.12em 0.38em;
|
||
}
|
||
/* Live Preview inline code is a `.cm-inline-code` span, not a <code> element —
|
||
give it the same face and colors. No border or horizontal padding: the text
|
||
can split across several spans (backticks, cursor edits), and per-span
|
||
borders/padding would draw seams mid-pill. */
|
||
.cm-s-obsidian span.cm-inline-code {
|
||
font-family: var(--cc-font-mono);
|
||
font-size: 0.86em;
|
||
background-color: var(--cc-code-bg);
|
||
color: var(--cc-code-text);
|
||
border-radius: var(--radius-s);
|
||
}
|
||
|
||
/* Code blocks: framed card with soft corners */
|
||
.markdown-rendered pre,
|
||
.cm-s-obsidian .HyperMD-codeblock {
|
||
font-family: var(--cc-font-mono);
|
||
}
|
||
.markdown-rendered pre {
|
||
position: relative;
|
||
background-color: var(--cc-code-bg);
|
||
border: 1px solid var(--cc-code-border);
|
||
border-radius: var(--cc-radius);
|
||
padding: 2.5em 1.1em 1em;
|
||
font-size: 0.85em;
|
||
line-height: 1.55;
|
||
}
|
||
.markdown-rendered pre > code {
|
||
background: none;
|
||
border: none;
|
||
padding: 0;
|
||
color: var(--cc-code-text);
|
||
}
|
||
/* Plain (un-highlighted) code text in live preview also reads blue */
|
||
.cm-s-obsidian .HyperMD-codeblock { color: var(--cc-code-text); }
|
||
|
||
/* Live-preview code block frame */
|
||
.markdown-source-view.mod-cm6 .HyperMD-codeblock-bg {
|
||
background-color: var(--cc-code-bg);
|
||
}
|
||
.markdown-source-view.mod-cm6 .HyperMD-codeblock {
|
||
border-left: 1px solid var(--cc-code-border);
|
||
border-right: 1px solid var(--cc-code-border);
|
||
}
|
||
.markdown-source-view.mod-cm6 .HyperMD-codeblock-begin-bg {
|
||
border-radius: var(--cc-radius) var(--cc-radius) 0 0;
|
||
border-top: 1px solid var(--cc-code-border);
|
||
}
|
||
.markdown-source-view.mod-cm6 .HyperMD-codeblock-end-bg {
|
||
border-radius: 0 0 var(--cc-radius) var(--cc-radius);
|
||
border-bottom: 1px solid var(--cc-code-border);
|
||
}
|
||
|
||
/* Code block header — language label (top-left) + copy button (top-right).
|
||
Applies to every code block so the copy button never overlaps the code. */
|
||
.markdown-rendered pre::before {
|
||
content: "code";
|
||
position: absolute;
|
||
top: 0.7em;
|
||
left: 1.1em;
|
||
font-family: var(--cc-font-mono);
|
||
font-size: 0.72em;
|
||
font-weight: 600;
|
||
letter-spacing: 0.05em;
|
||
text-transform: uppercase;
|
||
color: var(--cc-text-faint);
|
||
}
|
||
.markdown-rendered pre::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 2.2em; left: 1.1em; right: 1.1em;
|
||
border-top: 1px solid var(--cc-code-border);
|
||
}
|
||
/* Common language labels */
|
||
.markdown-rendered pre.language-javascript::before, .markdown-rendered pre.language-js::before { content: "javascript"; }
|
||
.markdown-rendered pre.language-typescript::before, .markdown-rendered pre.language-ts::before { content: "typescript"; }
|
||
.markdown-rendered pre.language-jsx::before { content: "jsx"; }
|
||
.markdown-rendered pre.language-tsx::before { content: "tsx"; }
|
||
.markdown-rendered pre.language-python::before, .markdown-rendered pre.language-py::before { content: "python"; }
|
||
.markdown-rendered pre.language-bash::before, .markdown-rendered pre.language-sh::before, .markdown-rendered pre.language-shell::before, .markdown-rendered pre.language-zsh::before { content: "shell"; }
|
||
.markdown-rendered pre.language-json::before { content: "json"; }
|
||
.markdown-rendered pre.language-yaml::before, .markdown-rendered pre.language-yml::before { content: "yaml"; }
|
||
.markdown-rendered pre.language-toml::before { content: "toml"; }
|
||
.markdown-rendered pre.language-html::before, .markdown-rendered pre.language-xml::before { content: "html"; }
|
||
.markdown-rendered pre.language-css::before, .markdown-rendered pre.language-scss::before { content: "css"; }
|
||
.markdown-rendered pre.language-java::before { content: "java"; }
|
||
.markdown-rendered pre.language-c::before { content: "c"; }
|
||
.markdown-rendered pre.language-cpp::before { content: "c++"; }
|
||
.markdown-rendered pre.language-csharp::before, .markdown-rendered pre.language-cs::before { content: "c#"; }
|
||
.markdown-rendered pre.language-go::before { content: "go"; }
|
||
.markdown-rendered pre.language-rust::before, .markdown-rendered pre.language-rs::before { content: "rust"; }
|
||
.markdown-rendered pre.language-ruby::before, .markdown-rendered pre.language-rb::before { content: "ruby"; }
|
||
.markdown-rendered pre.language-php::before { content: "php"; }
|
||
.markdown-rendered pre.language-sql::before { content: "sql"; }
|
||
.markdown-rendered pre.language-markdown::before, .markdown-rendered pre.language-md::before { content: "markdown"; }
|
||
.markdown-rendered pre.language-dockerfile::before { content: "dockerfile"; }
|
||
.markdown-rendered pre.language-kotlin::before { content: "kotlin"; }
|
||
.markdown-rendered pre.language-swift::before { content: "swift"; }
|
||
.markdown-rendered pre.language-diff::before { content: "diff"; }
|
||
.markdown-rendered pre.language-ini::before { content: "ini"; }
|
||
.markdown-rendered pre.language-lua::before { content: "lua"; }
|
||
.markdown-rendered pre.language-powershell::before, .markdown-rendered pre.language-ps1::before { content: "powershell"; }
|
||
.markdown-rendered pre.language-text::before, .markdown-rendered pre.language-plaintext::before, .markdown-rendered pre.language-txt::before { content: "text"; }
|
||
|
||
.markdown-rendered button.copy-code-button {
|
||
top: 0.5em;
|
||
right: 0.6em;
|
||
color: var(--cc-text-muted);
|
||
background-color: var(--cc-bg-alt);
|
||
border: 1px solid var(--cc-code-border);
|
||
border-radius: var(--radius-s);
|
||
padding: 0.2em 0.55em;
|
||
font-size: 0.74em;
|
||
opacity: 0;
|
||
transition: opacity 120ms ease, color 120ms ease;
|
||
}
|
||
.markdown-rendered pre:hover button.copy-code-button,
|
||
.markdown-rendered button.copy-code-button:focus-visible { opacity: 1; }
|
||
.markdown-rendered button.copy-code-button:hover { color: var(--text-accent); border-color: var(--text-accent); }
|
||
/* Touch screens have no hover — keep the copy button always visible there */
|
||
@media (hover: none) {
|
||
.markdown-rendered button.copy-code-button { opacity: 1; }
|
||
}
|
||
|
||
/* Syntax highlighting — a restrained, Claude-warm palette */
|
||
.theme-light {
|
||
--code-comment: #9a9684;
|
||
--code-keyword: #b0532f;
|
||
--code-function: #2f6f6a;
|
||
--code-string: #5c7a3f;
|
||
--code-property: #8a5a2b;
|
||
--code-value: #355f8a;
|
||
--code-operator: #6e6a5c;
|
||
--code-tag: #b0532f;
|
||
--code-important: #c15f3c;
|
||
}
|
||
.theme-dark {
|
||
--code-comment: #767264;
|
||
--code-keyword: #e8916f;
|
||
--code-function: #7fc4bd;
|
||
--code-string: #a7c98a;
|
||
--code-property: #e0b483;
|
||
--code-value: #8fb6e0;
|
||
--code-operator: #a8a496;
|
||
--code-tag: #e8916f;
|
||
--code-important: #e8a587;
|
||
}
|
||
.token.comment, .token.prolog, .token.doctype, .token.cdata { color: var(--code-comment); font-style: italic; }
|
||
.token.keyword, .token.atrule, .token.rule { color: var(--code-keyword); }
|
||
.token.function, .token.class-name { color: var(--code-function); }
|
||
.token.string, .token.char, .token.attr-value { color: var(--code-string); }
|
||
.token.property, .token.attr-name, .token.symbol { color: var(--code-property); }
|
||
.token.number, .token.boolean, .token.constant, .token.builtin { color: var(--code-value); }
|
||
.token.operator, .token.punctuation, .token.entity { color: var(--code-operator); }
|
||
.token.tag, .token.selector { color: var(--code-tag); }
|
||
.token.important, .token.bold { color: var(--code-important); font-weight: 700; }
|
||
|
||
/* CodeMirror live-preview tokens follow suit */
|
||
.cm-s-obsidian span.cm-comment { color: var(--code-comment); font-style: italic; }
|
||
.cm-s-obsidian span.cm-keyword { color: var(--code-keyword); }
|
||
.cm-s-obsidian span.cm-def, .cm-s-obsidian span.cm-property { color: var(--code-function); }
|
||
.cm-s-obsidian span.cm-string, .cm-s-obsidian span.cm-string-2 { color: var(--code-string); }
|
||
.cm-s-obsidian span.cm-number, .cm-s-obsidian span.cm-atom { color: var(--code-value); }
|
||
.cm-s-obsidian span.cm-operator { color: var(--code-operator); }
|
||
.cm-s-obsidian span.cm-tag { color: var(--code-tag); }
|
||
|
||
/* =====================================================================
|
||
7. BLOCKQUOTES & CALLOUTS
|
||
===================================================================== */
|
||
.markdown-rendered blockquote {
|
||
border-left: 3px solid var(--blockquote-border-color);
|
||
border-radius: 0 var(--radius-s) var(--radius-s) 0;
|
||
background-color: color-mix(in srgb, var(--cc-accent) 6%, transparent);
|
||
padding: 0.5em 1em;
|
||
color: var(--cc-text-muted);
|
||
font-style: normal;
|
||
}
|
||
|
||
/* Callouts — soft warm cards, tinted and titled by their type color */
|
||
.callout {
|
||
--callout-radius: var(--cc-radius);
|
||
border-radius: var(--cc-radius);
|
||
border: 1px solid rgba(var(--callout-color), 0.28);
|
||
background-color: rgba(var(--callout-color), 0.07);
|
||
mix-blend-mode: normal;
|
||
padding: 4px 10px;
|
||
margin: 1em 0;
|
||
}
|
||
.callout-title {
|
||
padding: 0.5em 0.4em;
|
||
font-weight: 700;
|
||
gap: 0.5em;
|
||
color: rgb(var(--callout-color));
|
||
}
|
||
.callout-icon .svg-icon { color: rgb(var(--callout-color)); }
|
||
.callout-content { padding: 0 0.4em 0.3em; color: var(--text-normal); }
|
||
/* Callout type colors. Two sets: titles sit directly on the note background,
|
||
so light mode wants deep shades (readable on cream) and dark mode wants
|
||
lifted ones (readable on charcoal) — one shared set fails contrast on one
|
||
side or the other. All pairs reach ~4:1+ on their surface. */
|
||
.callout { --callout-color: 110, 106, 92; } /* default (quote/abstract/etc.) */
|
||
.callout[data-callout="note"], .callout[data-callout="info"] { --callout-color: 193, 95, 60; }
|
||
.callout[data-callout="tip"], .callout[data-callout="success"], .callout[data-callout="done"] { --callout-color: 92, 122, 63; }
|
||
.callout[data-callout="warning"], .callout[data-callout="caution"], .callout[data-callout="attention"] { --callout-color: 166, 106, 20; }
|
||
.callout[data-callout="danger"], .callout[data-callout="error"], .callout[data-callout="bug"], .callout[data-callout="failure"] { --callout-color: 178, 56, 44; }
|
||
.callout[data-callout="question"], .callout[data-callout="help"], .callout[data-callout="faq"] { --callout-color: 154, 110, 60; }
|
||
.callout[data-callout="example"] { --callout-color: 120, 100, 150; }
|
||
|
||
.theme-dark .callout { --callout-color: 168, 164, 150; }
|
||
.theme-dark .callout[data-callout="note"], .theme-dark .callout[data-callout="info"] { --callout-color: 232, 145, 111; }
|
||
.theme-dark .callout[data-callout="tip"], .theme-dark .callout[data-callout="success"], .theme-dark .callout[data-callout="done"] { --callout-color: 143, 182, 107; }
|
||
.theme-dark .callout[data-callout="warning"], .theme-dark .callout[data-callout="caution"], .theme-dark .callout[data-callout="attention"] { --callout-color: 224, 169, 90; }
|
||
.theme-dark .callout[data-callout="danger"], .theme-dark .callout[data-callout="error"], .theme-dark .callout[data-callout="bug"], .theme-dark .callout[data-callout="failure"] { --callout-color: 230, 116, 94; }
|
||
.theme-dark .callout[data-callout="question"], .theme-dark .callout[data-callout="help"], .theme-dark .callout[data-callout="faq"] { --callout-color: 212, 169, 122; }
|
||
.theme-dark .callout[data-callout="example"] { --callout-color: 178, 158, 208; }
|
||
|
||
/* =====================================================================
|
||
8. LISTS, TASKS, TABLES, HR, TAGS
|
||
===================================================================== */
|
||
|
||
/* Task checkboxes — coral box with a white tick when done.
|
||
Driven through Obsidian's own checkbox variables rather than a custom
|
||
appearance reset + ::after tick. Obsidian draws the check (colored by
|
||
--checkbox-marker-color) on the same code path it uses on mobile, so the
|
||
white tick actually shows there instead of the native dark glyph. These
|
||
variables also style completed-task text in both reading view and Live
|
||
Preview, so no separate strikethrough rules are needed. */
|
||
body {
|
||
--checkbox-size: 1.5em;
|
||
--checkbox-radius: 0.4em;
|
||
--checkbox-marker-color: #ffffff;
|
||
--checkbox-color: var(--interactive-accent);
|
||
--checkbox-color-hover: var(--interactive-accent);
|
||
--checkbox-border-color: var(--cc-text-faint);
|
||
--checkbox-border-color-hover: var(--text-accent);
|
||
--checklist-done-color: var(--cc-text-faint);
|
||
--checklist-done-decoration: line-through;
|
||
}
|
||
|
||
/* In-progress task `- [/]` — a half-filled coral box, no tick.
|
||
The diagonal fill echoes the slash you type and reads instantly as
|
||
"started, not finished": clearly distinct from the empty box and from
|
||
the ticked done box, while staying in the coral family. Drawn with an
|
||
explicit appearance reset (prefixed + unprefixed) so the custom fill
|
||
shows on mobile too, where Obsidian otherwise falls back to a native
|
||
glyph. Custom data-task states aren't "done", so the text stays normal. */
|
||
.markdown-rendered input.task-list-item-checkbox[data-task="/"],
|
||
.markdown-source-view.mod-cm6 input.task-list-item-checkbox[data-task="/"] {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
border: 1.5px solid var(--interactive-accent);
|
||
background-color: var(--background-primary);
|
||
background-image: linear-gradient(135deg,
|
||
var(--interactive-accent) 0 50%,
|
||
transparent 50%);
|
||
}
|
||
/* Suppress Obsidian's native tick for the in-progress state */
|
||
.markdown-rendered input.task-list-item-checkbox[data-task="/"]::after,
|
||
.markdown-source-view.mod-cm6 input.task-list-item-checkbox[data-task="/"]::after {
|
||
display: none;
|
||
}
|
||
|
||
/* Cancelled task `- [-]` — muted box with a centered dash and struck-through,
|
||
faded text. Deliberately greyscale (no coral) so it reads as "dropped",
|
||
never as "done". Obsidian only strikes x/X items, so the text styling here
|
||
is applied by hand. */
|
||
.markdown-rendered input.task-list-item-checkbox[data-task="-"],
|
||
.markdown-source-view.mod-cm6 input.task-list-item-checkbox[data-task="-"] {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
border: 1.5px solid var(--cc-text-faint);
|
||
background-color: var(--background-primary);
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: 55% 100%;
|
||
background-image: linear-gradient(to bottom,
|
||
transparent 0 44%,
|
||
var(--cc-text-muted) 44% 56%,
|
||
transparent 56%);
|
||
}
|
||
.markdown-rendered input.task-list-item-checkbox[data-task="-"]::after,
|
||
.markdown-source-view.mod-cm6 input.task-list-item-checkbox[data-task="-"]::after {
|
||
display: none;
|
||
}
|
||
.markdown-rendered .task-list-item[data-task="-"],
|
||
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task="-"] {
|
||
color: var(--cc-text-faint);
|
||
text-decoration: line-through;
|
||
}
|
||
|
||
/* Forwarded / scheduled task `- [>]` — a coral right-chevron in a coral box.
|
||
Text stays normal: the task isn't finished, just deferred to another day.
|
||
The chevron is drawn as a masked ::after (same technique as the H1
|
||
starburst and Obsidian's own tick — which stock also masks on mobile,
|
||
so parity holds there) and colored by --interactive-accent, so the
|
||
Style Settings accent pickers recolor it. One glyph for both schemes;
|
||
the SVG's own color is irrelevant (masks read only alpha). The rule
|
||
declares the full geometry itself (including the positioning context)
|
||
so it renders even where the stock :checked::after rule doesn't apply,
|
||
while deliberately mirroring the stock tick's metrics — re-verify here
|
||
if a checkbox looks off after an Obsidian update. */
|
||
.markdown-rendered input.task-list-item-checkbox[data-task=">"],
|
||
.markdown-source-view.mod-cm6 input.task-list-item-checkbox[data-task=">"] {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
position: relative; /* anchor for the ::after glyph; matches stock */
|
||
border: 1.5px solid var(--interactive-accent);
|
||
background-color: var(--background-primary);
|
||
}
|
||
.markdown-rendered input.task-list-item-checkbox[data-task=">"]::after,
|
||
.markdown-source-view.mod-cm6 input.task-list-item-checkbox[data-task=">"]::after {
|
||
content: "";
|
||
display: block;
|
||
position: absolute;
|
||
top: -1px;
|
||
inset-inline-start: -1px;
|
||
width: var(--checkbox-size);
|
||
height: var(--checkbox-size);
|
||
background-color: var(--interactive-accent);
|
||
/* Both prefixed + unprefixed shorthands: old engines only honor
|
||
-webkit-mask; where both parse, the later unprefixed one wins. */
|
||
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 5 16 12 9 19'/%3E%3C/svg%3E") center / 60% no-repeat;
|
||
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 5 16 12 9 19'/%3E%3C/svg%3E") center / 60% no-repeat;
|
||
}
|
||
|
||
/* List bullets: orange marker, normal-colored text */
|
||
.markdown-rendered ul > li::marker { color: var(--interactive-accent); }
|
||
.markdown-rendered ol > li::marker { color: var(--cc-text-muted); }
|
||
/* Live-preview bullets */
|
||
.markdown-source-view.mod-cm6 .cm-formatting-list-ul,
|
||
.markdown-source-view.mod-cm6 .HyperMD-list-line .list-bullet::after {
|
||
color: var(--interactive-accent);
|
||
}
|
||
.markdown-source-view.mod-cm6 .list-bullet { color: var(--interactive-accent); }
|
||
|
||
/* Tables — light-orange body, more-orange header, visible orange grid lines.
|
||
border-collapse stays `separate`: Chromium ignores border-radius on
|
||
collapsed tables, which squares off the card corners. Grid lines are drawn
|
||
as right/bottom borders per cell (dropped on the last column/row) so they
|
||
still read as single 1px lines against the table's own outer border. */
|
||
|
||
/* Live Preview tables — color parity via Obsidian's own table variables;
|
||
no layout/selector overrides (stability). Verified against Obsidian
|
||
1.12.7: the LP table widget (.cm-embed-block.cm-table-widget) carries
|
||
.markdown-rendered, so every class rule below already applies to it.
|
||
The one gap is the stock `thead tr > th` rule, which outranks
|
||
`.markdown-rendered th` and paints header borders with
|
||
--table-header-border-color — in BOTH views. Mapping the stock
|
||
variables to the theme's table tokens closes it everywhere. */
|
||
body {
|
||
--table-border-color: var(--cc-table-border);
|
||
--table-header-border-color: var(--cc-table-border);
|
||
--table-add-button-border-color: var(--cc-table-border);
|
||
}
|
||
|
||
.markdown-rendered table {
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
width: 100%;
|
||
background-color: var(--cc-table-bg);
|
||
border: 1px solid var(--cc-table-border);
|
||
border-radius: var(--cc-radius);
|
||
overflow: hidden;
|
||
}
|
||
.markdown-rendered thead tr,
|
||
.markdown-rendered th {
|
||
background-color: var(--cc-table-header);
|
||
}
|
||
.markdown-rendered th,
|
||
.markdown-rendered td {
|
||
border: none;
|
||
border-right: 1px solid var(--cc-table-border);
|
||
border-bottom: 1px solid var(--cc-table-border);
|
||
padding: 0.55em 0.85em;
|
||
}
|
||
.markdown-rendered th:last-child,
|
||
.markdown-rendered td:last-child { border-right: none; }
|
||
.markdown-rendered tbody tr:last-child > td { border-bottom: none; }
|
||
.markdown-rendered th {
|
||
color: var(--cc-heading);
|
||
font-weight: 700;
|
||
text-align: left;
|
||
border-bottom-width: 2px;
|
||
}
|
||
.markdown-rendered td {
|
||
background: transparent;
|
||
vertical-align: top;
|
||
}
|
||
.markdown-rendered tbody tr:hover td { background-color: color-mix(in srgb, var(--cc-accent) 5%, transparent); }
|
||
|
||
/* Horizontal rule — slim and soft */
|
||
.markdown-rendered hr {
|
||
border: none;
|
||
border-top: 1px solid var(--hr-color);
|
||
margin: 2em 0;
|
||
}
|
||
|
||
/* Tags — coral pills.
|
||
In Live Preview a tag is split into two spans (a `#` span + the text span),
|
||
so giving each a full rounded pill drew them as two capsules with a gap.
|
||
Keep the shared fill, but below we round only the OUTER corners and drop the
|
||
inner padding so `#tag` reads as one seamless pill. Reading view (`a.tag`)
|
||
is already a single element. */
|
||
.markdown-rendered a.tag,
|
||
.cm-s-obsidian .cm-hashtag {
|
||
color: var(--tag-color);
|
||
background-color: var(--tag-background);
|
||
border: none;
|
||
border-radius: 999px;
|
||
padding: 0.1em 0.6em;
|
||
font-size: 0.82em;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
}
|
||
.markdown-rendered a.tag:hover { background-color: var(--tag-background-hover); }
|
||
/* Live Preview: weld the split #/text spans into a single continuous pill */
|
||
.cm-s-obsidian .cm-hashtag-begin {
|
||
border-radius: 999px 0 0 999px;
|
||
padding-right: 0;
|
||
}
|
||
.cm-s-obsidian .cm-hashtag-end {
|
||
border-radius: 0 999px 999px 0;
|
||
padding-left: 0;
|
||
}
|
||
|
||
/* =====================================================================
|
||
9. CHROME — sidebar, tabs, titlebar, inputs, scrollbars
|
||
===================================================================== */
|
||
/* Tabs — idle tabs sit muted; the active tab gets a soft coral-tinted fill,
|
||
set through Obsidian's own --tab-background-active variable (in the palette
|
||
blocks above). A background fills the whole tab regardless of its internal
|
||
structure, so it's robust where a wrapper underline kept missing the close-
|
||
button area. Plus bold, heading-colored text; inactive tabs brighten on
|
||
hover. */
|
||
body { --tab-text-color: var(--cc-text-muted); }
|
||
.workspace-tab-header:hover { --tab-text-color: var(--cc-text); }
|
||
.workspace-tab-header.is-active {
|
||
--tab-text-color-focused-active: var(--cc-heading);
|
||
}
|
||
.workspace-tab-header.is-active .workspace-tab-header-inner-title { font-weight: 600; }
|
||
|
||
/* File explorer hierarchy: bold folders, medium files (same font + size) */
|
||
.workspace-leaf-content[data-type="file-explorer"] .nav-folder-title-content,
|
||
.workspace-leaf-content[data-type="file-explorer"] .nav-file-title-content {
|
||
font-size: var(--cc-nav-font-size);
|
||
}
|
||
.workspace-leaf-content[data-type="file-explorer"] .nav-folder-title-content {
|
||
font-weight: var(--cc-folder-weight);
|
||
color: var(--cc-heading);
|
||
}
|
||
.workspace-leaf-content[data-type="file-explorer"] .nav-file-title-content {
|
||
font-weight: var(--cc-file-weight);
|
||
}
|
||
|
||
/* Active file/folder in the explorer: compact rounded rectangle with a
|
||
flush, full-height coral spine (matches the spine's height, not a tall pill) */
|
||
.tree-item-self { position: relative; --nav-item-border-radius: 6px; }
|
||
.nav-file-title.is-active,
|
||
.nav-folder-title.is-active,
|
||
.tree-item-self.is-active,
|
||
.tree-item-self.is-active.has-focus,
|
||
.tree-item-self.has-focus {
|
||
background-color: var(--cc-selection);
|
||
color: var(--cc-heading);
|
||
border-radius: 6px;
|
||
}
|
||
.tree-item-self.is-active::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0; top: 0; bottom: 0;
|
||
width: 3px;
|
||
border-radius: 6px 0 0 6px;
|
||
background-color: var(--interactive-accent);
|
||
}
|
||
|
||
/* Inputs / search */
|
||
input[type="text"], input[type="search"], textarea, .prompt-input {
|
||
border-radius: var(--radius-m) !important;
|
||
background-color: var(--background-modifier-form-field);
|
||
border: 1px solid var(--cc-border);
|
||
}
|
||
input[type="text"]:focus, input[type="search"]:focus, textarea:focus {
|
||
border-color: var(--interactive-accent);
|
||
box-shadow: 0 0 0 2px var(--cc-selection);
|
||
}
|
||
|
||
/* Properties / frontmatter — sit flat on the page, no out-of-place boxes.
|
||
(Overrides the global input styling above for metadata fields.) */
|
||
.metadata-container {
|
||
background: transparent;
|
||
border: none;
|
||
padding: 0.4em 0;
|
||
}
|
||
.metadata-properties-heading { border: none; padding-left: 0; }
|
||
.metadata-content { padding: 0; }
|
||
.metadata-property {
|
||
background: transparent;
|
||
border-radius: var(--radius-s);
|
||
}
|
||
.metadata-property:hover { background: var(--background-modifier-hover); }
|
||
.metadata-property-key-input,
|
||
.metadata-property-key-input:hover,
|
||
.metadata-property-key-input:focus,
|
||
.metadata-property-value input[type="text"],
|
||
.metadata-property-value input[type="text"]:hover,
|
||
.metadata-property-value input[type="text"]:focus,
|
||
.metadata-property-value .metadata-input-longtext {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
border-radius: var(--radius-s) !important;
|
||
}
|
||
.metadata-property-key { color: var(--cc-text-muted); }
|
||
.metadata-property-icon { color: var(--cc-text-faint); }
|
||
.metadata-add-button { color: var(--cc-text-muted); }
|
||
|
||
/* Buttons */
|
||
button:not(.clickable-icon),
|
||
.mod-cta {
|
||
border-radius: var(--radius-m);
|
||
}
|
||
button.mod-cta { font-weight: 600; }
|
||
|
||
/* Command palette & suggestion menus — soft cards */
|
||
.suggestion-container, .prompt {
|
||
border-radius: var(--cc-radius);
|
||
border: 1px solid var(--cc-border);
|
||
}
|
||
.suggestion-item.is-selected { background-color: var(--cc-selection); }
|
||
|
||
/* Graph view in theme colors */
|
||
.graph-view.color-fill { color: var(--graph-node); }
|
||
.graph-view.color-line { color: var(--graph-line); }
|
||
.graph-view.color-text { color: var(--graph-text); }
|
||
.graph-view.color-circle { color: var(--graph-node-focused); }
|
||
.graph-view.color-fill-focused { color: var(--graph-node-focused); }
|
||
.graph-view.color-fill-tag { color: var(--graph-node-tag); }
|
||
.graph-view.color-fill-attachment { color: var(--graph-node-attachment); }
|
||
.graph-view.color-fill-unresolved { color: var(--graph-node-unresolved); }
|
||
.graph-view.color-arrow { color: var(--graph-line); }
|
||
|
||
/* Scrollbars stay quiet */
|
||
::-webkit-scrollbar-thumb { border-radius: 999px; }
|
||
|
||
/* Status bar / breadcrumbs */
|
||
.status-bar { background-color: var(--cc-bg-alt); border-top: 1px solid var(--cc-border); }
|
||
|
||
/* Embedded images & embeds — soft rounded corners */
|
||
.markdown-rendered img,
|
||
.markdown-rendered .image-embed img,
|
||
.markdown-rendered video {
|
||
border-radius: var(--radius-m);
|
||
}
|
||
.markdown-rendered .internal-embed.markdown-embed {
|
||
border-radius: var(--cc-radius);
|
||
border: 1px solid var(--cc-border);
|
||
background-color: var(--cc-bg-alt);
|
||
padding: 0.2em 0.8em;
|
||
}
|
||
|
||
/* Mobile toolbar & navbar — match the theme chrome */
|
||
.is-mobile .mobile-toolbar,
|
||
.is-mobile .mobile-navbar {
|
||
background-color: var(--cc-bg-alt);
|
||
border-top: 1px solid var(--cc-border);
|
||
}
|
||
.is-mobile .mobile-toolbar-option:active { color: var(--text-accent); }
|
||
|
||
/* Selection */
|
||
::selection { background-color: var(--text-selection); }
|
||
|
||
/* Settings window — bump the UI text +2px for readability */
|
||
.modal.mod-settings { font-size: calc(var(--font-ui-small) + 2px); }
|
||
.modal.mod-settings .vertical-tab-nav-item,
|
||
.modal.mod-settings .vertical-tab-header-group-title {
|
||
font-size: calc(var(--font-ui-small) + 2px);
|
||
}
|
||
.modal.mod-settings .setting-item-name { font-size: calc(var(--font-ui-medium) + 2px); }
|
||
.modal.mod-settings .setting-item-description { font-size: calc(var(--font-ui-smaller) + 2px); }
|
||
|
||
/* =====================================================================
|
||
10. PLUGIN INTEGRATIONS — Another Sticky Headings
|
||
---------------------------------------------------------------------
|
||
Moves the plugin's heading trail out of the note body and into the
|
||
view header, rendered as a single-line clickable breadcrumb where the
|
||
note title normally sits. While scrolled into a section the title
|
||
fades out and the trail takes its place; back at the top of the note
|
||
the plain title returns. Toggle: Style Settings → Plugins.
|
||
Desktop only — on mobile (phone + tablet) the plugin's regular stacked
|
||
view is kept: the mobile header is too cramped for a breadcrumb.
|
||
Plugin DOM: .sticky-headings-root > .sticky-headings-container >
|
||
.sticky-headings-item[data-indent-level] (+ a .sticky-headings-shadow
|
||
copy used only for height measurement — left untouched).
|
||
===================================================================== */
|
||
|
||
/* Hide the note title while a heading trail is active (visibility too,
|
||
so the invisible title can't catch clicks meant for the breadcrumb).
|
||
Both the file-name title AND the parent-folder breadcrumb
|
||
(.view-header-title-parent, e.g. "Folder / Subfolder /") must go —
|
||
for a nested note the parent crumb is non-empty and would otherwise
|
||
collide with the trail. Asymmetric timing: hiding is immediate,
|
||
un-hiding waits 160ms — the plugin unmounts the trail for ~a frame
|
||
while re-rendering it, and without the delay every re-render flashes
|
||
the title over the trail. */
|
||
body.cc-sticky-in-header:not(.is-mobile) .view-header-title,
|
||
body.cc-sticky-in-header:not(.is-mobile) .view-header-title-parent {
|
||
transition: opacity 120ms ease 160ms, visibility 0s linear 160ms;
|
||
}
|
||
body.cc-sticky-in-header:not(.is-mobile)
|
||
.workspace-leaf:has(.sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-item[data-indent-level])
|
||
.view-header-title,
|
||
body.cc-sticky-in-header:not(.is-mobile)
|
||
.workspace-leaf:has(.sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-item[data-indent-level])
|
||
.view-header-title-parent {
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transition: opacity 120ms ease, visibility 0s linear;
|
||
}
|
||
|
||
/* Let the repositioned trail escape the note area and overlay the header.
|
||
min-height: 0 is required: overflow:visible switches the flex item's
|
||
automatic minimum size to content-size, which would otherwise squeeze
|
||
the sibling .view-header. */
|
||
body.cc-sticky-in-header:not(.is-mobile) .workspace-leaf-content[data-type="markdown"] .view-content {
|
||
overflow: visible;
|
||
min-height: 0;
|
||
}
|
||
|
||
/* The trail block: lift it up onto the view header row */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow) {
|
||
top: calc(-1 * var(--header-height)) !important;
|
||
height: var(--header-height);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
/* clear the nav arrows (left) and view actions (right) */
|
||
padding: 0 110px !important;
|
||
background: none !important;
|
||
z-index: 5;
|
||
/* transparent gutters must not block header buttons underneath */
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Container: stacked tree → single centered row, no plugin theming */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-container {
|
||
display: flex !important;
|
||
flex-flow: row nowrap;
|
||
align-items: center;
|
||
justify-content: center;
|
||
max-width: 100% !important;
|
||
min-width: 0;
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
background: none !important;
|
||
backdrop-filter: none !important;
|
||
border-radius: 0 !important;
|
||
color: var(--text-normal) !important;
|
||
font-size: var(--font-ui-small) !important;
|
||
overflow: hidden;
|
||
}
|
||
/* the blur theme paints gradients via pseudo-elements — remove them */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-container::before,
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-container::after {
|
||
display: none !important;
|
||
}
|
||
|
||
/* Items: uniform title-style text, no indent, clickable, ellipsized */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-item {
|
||
padding-left: 0 !important;
|
||
flex: 0 1 auto;
|
||
min-width: 0;
|
||
line-height: var(--header-height);
|
||
font-weight: var(--font-medium, 500);
|
||
color: var(--text-normal);
|
||
pointer-events: auto;
|
||
}
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-item:hover {
|
||
color: var(--text-accent);
|
||
}
|
||
|
||
/* Page-name crumb — the plugin's file-name item (autoShowFileName) is the
|
||
only item without a data-indent-level. Coral + semibold so the trail
|
||
reads "where am I" at a glance: Page › section › subsection. */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-item:not([data-indent-level]) {
|
||
color: var(--text-accent);
|
||
font-weight: var(--font-semibold, 600);
|
||
}
|
||
/* ...but never alone: with no heading crumbs next to it the native note
|
||
title stays visible instead (also masks the plugin's stale-file-name
|
||
bug when switching notes within a tab). */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-item:not([data-indent-level]):only-child {
|
||
display: none !important;
|
||
}
|
||
|
||
/* › separators between crumbs */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-item + .sticky-headings-item::before {
|
||
content: "›";
|
||
flex: none;
|
||
margin: 0 0.5em;
|
||
color: var(--text-faint);
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* Uniform text: drop the per-level H badges */
|
||
body.cc-sticky-in-header:not(.is-mobile) .sticky-headings-root:not(.sticky-headings-shadow)
|
||
.sticky-headings-icon {
|
||
display: none !important;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------
|
||
Scroll progress bar (Style Settings → Editor → Scroll progress bar)
|
||
---------------------------------------------------------------------
|
||
Pure CSS via scroll-driven animations (Chromium): the note's scroller
|
||
drives a named scroll timeline; a coral fill at the seam between the
|
||
title row and the note scales with scroll progress. Desktop: top of
|
||
the note, full editor width (resizes with the sidebars for free, since
|
||
it lives inside the leaf). Mobile: pinned to the bottom of the editor
|
||
area instead — there's no window chrome row to sit under.
|
||
The Claude Scroll Map companion plugin drops heading dots onto the
|
||
same strip (.cc-scroll-map, styled further below).
|
||
--------------------------------------------------------------------- */
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"] {
|
||
timeline-scope: --cc-note-scroll;
|
||
}
|
||
/* Only the visible mode's scroller has a box, so exactly one element
|
||
provides the timeline at any moment (duplicates would void the name) */
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"]
|
||
.markdown-source-view.mod-cm6 .cm-scroller,
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"]
|
||
.markdown-preview-view {
|
||
scroll-timeline: --cc-note-scroll block;
|
||
}
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"] .view-content {
|
||
position: relative;
|
||
/* let centered markers straddle the line without clipping;
|
||
min-height keeps flex from squeezing the header (see breadcrumb) */
|
||
overflow: visible;
|
||
min-height: 0;
|
||
}
|
||
/* The line runs along y = 5.5px — track, fill and markers all center on
|
||
it, metro-map style. */
|
||
/* Track — doubles as the instrument's backdrop: a band of note
|
||
background fading downward masks any scrolled text passing under the
|
||
markers, with the 3px track line painted on top of it. */
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"] .view-content::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: 0; left: 0;
|
||
width: 100%; height: 15px;
|
||
background:
|
||
linear-gradient(var(--cc-track-color, var(--cc-border)), var(--cc-track-color, var(--cc-border)))
|
||
0 4px / 100% 3px no-repeat,
|
||
linear-gradient(to bottom, var(--cc-bg) 60%, transparent);
|
||
z-index: 8;
|
||
pointer-events: none;
|
||
transition: height 130ms ease;
|
||
}
|
||
/* Fill */
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"] .view-content::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 4px; left: 0;
|
||
width: 100%; height: 3px;
|
||
border-radius: 2px;
|
||
background: var(--cc-accent);
|
||
transform: scaleX(0); /* short notes with no scroll: empty */
|
||
transform-origin: 0 50%;
|
||
z-index: 9;
|
||
pointer-events: none;
|
||
animation: cc-scroll-progress-fill linear both;
|
||
animation-timeline: --cc-note-scroll;
|
||
transition: height 130ms ease, top 130ms ease;
|
||
}
|
||
/* Hovering the scroll-map strip thickens the line, still centered.
|
||
The .cc-map-hover class is toggled by the companion plugin on strip
|
||
enter/leave — cheaper than :has(:hover), which re-evaluates on every
|
||
pointer move. */
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"]
|
||
.view-content.cc-map-hover::before {
|
||
height: 27px;
|
||
background:
|
||
linear-gradient(var(--cc-track-color, var(--cc-border)), var(--cc-track-color, var(--cc-border)))
|
||
0 3px / 100% 5px no-repeat,
|
||
linear-gradient(to bottom, var(--cc-bg) 60%, transparent);
|
||
}
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"]
|
||
.view-content.cc-map-hover::after {
|
||
top: 3px;
|
||
height: 5px;
|
||
}
|
||
/* The leading edge tracks DOCUMENT position, not raw scroll progress: it
|
||
scales to --cc-fill-max (= range/scrollHeight, set per-view by the
|
||
companion plugin) so progress×fillMax = scrollTop/scrollHeight. That keeps
|
||
the edge aligned with the heading dots (which sit in document space) — the
|
||
line reaches a dot exactly as the dot fills, neither racing ahead nor
|
||
trailing. The final 5% then snaps to a full bar so the very bottom of the
|
||
note still reads as 100% (the last screenful has no scroll range of its
|
||
own to map). Falls back to a plain 0→100% fill when --cc-fill-max is
|
||
unset (short/reading-mode notes, or any other theme's markup). */
|
||
@keyframes cc-scroll-progress-fill {
|
||
from { transform: scaleX(0); }
|
||
95% { transform: scaleX(calc(var(--cc-fill-max, 1) * 0.95)); }
|
||
to { transform: scaleX(1); }
|
||
}
|
||
/* Mobile: bottom of the visible editor area, a touch thicker; no dots
|
||
means no backdrop band — plain track line */
|
||
body.cc-scroll-progress.is-mobile .workspace-leaf-content[data-type="markdown"] .view-content::before,
|
||
body.cc-scroll-progress.is-mobile .workspace-leaf-content[data-type="markdown"] .view-content::after {
|
||
top: auto; bottom: 0;
|
||
height: 4px;
|
||
}
|
||
body.cc-scroll-progress.is-mobile .workspace-leaf-content[data-type="markdown"] .view-content::before {
|
||
/* like desktop's band: bg fading upward masks text passing under the
|
||
markers, with the 4px track painted at the bottom of it */
|
||
height: 18px;
|
||
background:
|
||
linear-gradient(var(--cc-border), var(--cc-border))
|
||
0 100% / 100% 4px no-repeat,
|
||
linear-gradient(to top, var(--cc-bg) 40%, transparent);
|
||
}
|
||
|
||
/* --- Claude Scroll Map (companion plugin) — heading dots on the bar ---
|
||
The plugin renders .cc-scroll-map > .cc-scroll-dot[data-level] into
|
||
each note and handles positioning/clicks; all cosmetics live here. */
|
||
body .cc-scroll-map {
|
||
position: absolute;
|
||
top: 0; left: 0; right: 0;
|
||
height: 12px; /* hover zone for the strip */
|
||
z-index: 10;
|
||
transition: height 130ms ease;
|
||
}
|
||
/* With the progress bar on, the theme's track pseudo paints the backdrop
|
||
band — a bg on the map would sit above the bar and hide it. With the
|
||
bar toggled off, the plugin's own gradient band stays so markers never
|
||
float naked over note text. */
|
||
body.cc-scroll-progress .cc-scroll-map { background: none; }
|
||
body .cc-scroll-map:hover { height: 24px; } /* roomier target while aiming */
|
||
/* Mobile: markers ride the theme's BOTTOM progress bar, cut at their
|
||
middle — the map bottom-docks over the bar and overflow-hidden slices
|
||
every marker at the screen edge. Purely visual: no tap-to-jump, no
|
||
hover (pointer-events off). Without the progress bar toggle there is
|
||
no bar to ride, so the map stays hidden. */
|
||
body.is-mobile .cc-scroll-map { display: none; }
|
||
body.cc-scroll-progress.is-mobile .cc-scroll-map {
|
||
display: block;
|
||
top: auto;
|
||
bottom: 0;
|
||
height: 14px;
|
||
overflow: hidden; /* the cut: bottom half never renders */
|
||
background: none;
|
||
pointer-events: none;
|
||
}
|
||
body.cc-scroll-progress.is-mobile .cc-scroll-map .cc-scroll-dot {
|
||
top: 100%; /* equator on the clip edge: true half */
|
||
pointer-events: none;
|
||
cursor: default;
|
||
}
|
||
/* mobile sizes: a touch bigger than desktop — only the top half shows */
|
||
body.cc-scroll-progress.is-mobile .cc-scroll-map .cc-scroll-dot { width: 9px; height: 9px; }
|
||
body.cc-scroll-progress.is-mobile .cc-scroll-map .cc-scroll-dot[data-level="1"] { width: 20px; height: 20px; }
|
||
body.cc-scroll-progress.is-mobile .cc-scroll-map .cc-scroll-dot[data-level="3"] { width: 7px; height: 7px; }
|
||
/* Headings as brand marks: the Claude starburst for H1 landmarks,
|
||
soft coral circles for H2/H3. Every marker is full coral once read;
|
||
unread markers are the same coral mixed toward the note background —
|
||
OPAQUE on purpose, so the progress line never shows through. Each
|
||
marker punches a gap in the line (halo ring / backing disc): the line
|
||
never touches a marker in any state. */
|
||
/* Markers start FADED and turn full coral the moment the progress line
|
||
reaches them — the same scroll timeline as the fill, with a per-marker
|
||
animation-range at the marker's own position (--cc-dot-frac, set by
|
||
the plugin). The element itself is an opaque note-bg disc (that disc
|
||
is the gap in the line); the coral lives on ::before. */
|
||
body .cc-scroll-map .cc-scroll-dot {
|
||
position: absolute;
|
||
top: 5.5px; /* the line's center */
|
||
/* --cc-mag: dock-style fisheye magnification, set by the plugin on
|
||
strip mousemove; size changes stay centered */
|
||
transform: translate(-50%, -50%) scale(var(--cc-mag, 1));
|
||
width: 7px; height: 7px; /* H2 default */
|
||
padding: 0;
|
||
border: none;
|
||
border-radius: 50%;
|
||
--cc-dot-color: var(--cc-accent);
|
||
background: var(--cc-bg);
|
||
box-shadow: 0 0 0 2px var(--cc-bg); /* halo ring widens the gap */
|
||
cursor: pointer;
|
||
transition: height 130ms ease, width 130ms ease,
|
||
transform 60ms ease-out;
|
||
}
|
||
/* The fade is OPACITY, not a color-mix: Chromium's scroll-driven color
|
||
interpolation miscomputes between color-mix() and hex endpoints (the
|
||
fill came out neon yellow); opacity is numeric and safe. 45% coral
|
||
over the opaque bg disc looks identical to a 45% mix toward bg. */
|
||
body .cc-scroll-map .cc-scroll-dot::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: 50%;
|
||
background: var(--cc-dot-color);
|
||
opacity: var(--cc-dot-fade, 0.45);
|
||
animation: cc-scroll-dot-pass linear both;
|
||
animation-timeline: --cc-note-scroll;
|
||
animation-range:
|
||
calc(var(--cc-dot-frac, 0) * 100%)
|
||
calc(var(--cc-dot-frac, 0) * 100% + 0.5%);
|
||
}
|
||
@keyframes cc-scroll-dot-pass {
|
||
to { opacity: 1; }
|
||
}
|
||
/* H1: the Claude starburst — ::before trades its disc for an SVG mask.
|
||
Masks clip box-shadow, so the element disc alone provides the gap. */
|
||
body .cc-scroll-map .cc-scroll-dot[data-level="1"] {
|
||
width: 16px; height: 16px;
|
||
border-radius: 50%; /* explicit: outranks plugin baseline level rule */
|
||
background: var(--cc-bg);
|
||
box-shadow: none;
|
||
}
|
||
body .cc-scroll-map .cc-scroll-dot[data-level="1"]::before {
|
||
inset: 2px;
|
||
border-radius: 0;
|
||
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-12 -12 24 24'%3E%3Cpath d='M0 0L10 0M0 0L6.7 4.4M0 0L3.9 8.7M0 0L-1.3 7.9M0 0L-6.7 7.4M0 0L-8.2 2.2M0 0L-9 -2.9M0 0L-5 -6.2M0 0L-1.1 -10M0 0L3.9 -7.6M0 0L7.8 -4.5' stroke='black' stroke-width='2.8' stroke-linecap='round' fill='none'/%3E%3C/svg%3E") center / contain no-repeat;
|
||
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-12 -12 24 24'%3E%3Cpath d='M0 0L10 0M0 0L6.7 4.4M0 0L3.9 8.7M0 0L-1.3 7.9M0 0L-6.7 7.4M0 0L-8.2 2.2M0 0L-9 -2.9M0 0L-5 -6.2M0 0L-1.1 -10M0 0L3.9 -7.6M0 0L7.8 -4.5' stroke='black' stroke-width='2.8' stroke-linecap='round' fill='none'/%3E%3C/svg%3E") center / contain no-repeat;
|
||
}
|
||
body .cc-scroll-map .cc-scroll-dot[data-level="3"] {
|
||
width: 6px; height: 6px;
|
||
border-radius: 50%; /* explicit: outranks plugin baseline level rule */
|
||
background: var(--cc-bg);
|
||
}
|
||
/* Hovering the strip: uniform growth — circles can't stretch tall like
|
||
the old blocks; the fisheye handles crowding where you're aiming */
|
||
body .cc-scroll-map:hover .cc-scroll-dot { width: 9px; height: 9px; }
|
||
body .cc-scroll-map:hover .cc-scroll-dot[data-level="1"] { width: 20px; height: 20px; }
|
||
body .cc-scroll-map:hover .cc-scroll-dot[data-level="3"] { width: 8px; height: 8px; }
|
||
/* !important: the pass animation's fill mode owns `opacity` in the
|
||
cascade, so the highlight has to outrank it */
|
||
body .cc-scroll-map .cc-scroll-dot:hover::before,
|
||
body .cc-scroll-map .cc-scroll-dot.cc-hovered::before {
|
||
background: var(--cc-accent-bright, var(--cc-accent)) !important;
|
||
opacity: 1 !important;
|
||
}
|
||
/* Heading-name tooltip — 60% of body text size, tracks the tick height */
|
||
body .cc-scroll-map .cc-scroll-dot::after {
|
||
content: attr(data-label);
|
||
position: absolute;
|
||
top: calc(100% + 5px);
|
||
left: 50%;
|
||
/* counter-scale so the fisheye doesn't zoom the tooltip text */
|
||
transform: translateX(-50%) scale(calc(1 / var(--cc-mag, 1)));
|
||
transform-origin: 50% 0;
|
||
display: none;
|
||
max-width: 32ch;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: calc(var(--font-text-size, 16px) * 0.6);
|
||
line-height: 1.4;
|
||
font-weight: 500;
|
||
color: var(--text-normal);
|
||
background: var(--cc-bg-alt);
|
||
border: 1px solid var(--cc-border);
|
||
border-radius: 6px;
|
||
padding: 2px 8px;
|
||
box-shadow: var(--shadow-s, 0 2px 8px rgb(0 0 0 / 0.15));
|
||
}
|
||
body .cc-scroll-map .cc-scroll-dot:hover::after, body .cc-scroll-map .cc-scroll-dot.cc-hovered::after { display: block; }
|
||
|
||
/* =====================================================================
|
||
11. REDUCED MOTION — honour the OS "reduce motion" setting
|
||
---------------------------------------------------------------------
|
||
When the user has asked their system to minimise motion, collapse all
|
||
transitions/animations to near-instant. Duration stays > 0 so any
|
||
transitionend / animationend listeners still fire.
|
||
===================================================================== */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
transition-duration: 0.01ms !important;
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
/* The scroll progress fill and the marker fill-in are scroll-linked,
|
||
not autonomous motion — a forced time duration would pin them at
|
||
100%. Restore the timeline mapping. */
|
||
body.cc-scroll-progress .workspace-leaf-content[data-type="markdown"] .view-content::after,
|
||
body .cc-scroll-map .cc-scroll-dot,
|
||
body .cc-scroll-map .cc-scroll-dot::before {
|
||
animation-duration: auto !important;
|
||
}
|
||
}
|