kleokl7_Claude-Code-Obsidia.../theme.css
kleokl7 95a4e356bf Review fixes from the eight-angle pass
- 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>
2026-07-17 01:41:47 +02:00

1737 lines
74 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* =====================================================================
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.51.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;
}
}