mirror of
https://github.com/amdhj22/rbr.obsidian.git
synced 2026-07-22 04:40:27 +00:00
- lift hit-zone stacking above the side dock (sidedock+1) so open panels no longer cover the ribbon's hover band - size each hover zone to its expanded region: ribbon width, tab --header-height, status bar height - keep icons/content above the bands so wider zones don't eat clicks - drop ribbon double-interpolation (animate --ribbon-width only) - speed up defaults: hover delay 200->100ms, duration 100->80ms
1656 lines
65 KiB
CSS
1656 lines
65 KiB
CSS
/*
|
||
* RBR — Red Bull Racing colorscheme for Obsidian
|
||
*
|
||
* Three-accent palette:
|
||
* - Kerb Red (#e84a55) — the active/selected thing (default accent)
|
||
* - RB Yellow (#ffd84d) — attention / cursor / modifications
|
||
* - Chequer White (#ffffff) — functions / methods (third dominant accent)
|
||
*
|
||
* Background is low-saturation navy so the trio dominates without harshness.
|
||
* For the design rules behind every color choice, see the upstream
|
||
* STYLE-GUIDE.md at https://github.com/Amdhj22/rbr
|
||
*
|
||
* RBR currently ships only the "classic" (dark) flavor. The theme applies
|
||
* the same dark palette under both .theme-dark and .theme-light so users
|
||
* with the global light mode still get a coherent experience.
|
||
*
|
||
* --- User options (Style Settings plugin) ---
|
||
* Install the "Style Settings" community plugin to expose:
|
||
* - Primary accent color (red / yellow / white)
|
||
* - Heading style (trio / mono / accent-only)
|
||
* - Cursor color (yellow / warm / follow accent)
|
||
* - Active tab indicator (top / bottom / fill)
|
||
* - Code font size slider
|
||
* - Highlight (==text==) intensity slider
|
||
* - Auto hide toggles (tab bar / status bar / ribbon / vault profile)
|
||
* - Card layout toggle + corner-radius / gap sliders
|
||
* Without the plugin, defaults apply and nothing breaks.
|
||
*/
|
||
|
||
|
||
/* @settings
|
||
|
||
name: RBR
|
||
id: rbr-theme
|
||
settings:
|
||
-
|
||
id: rbr-section-accent
|
||
title: Accent
|
||
type: heading
|
||
level: 2
|
||
collapsed: false
|
||
|
||
-
|
||
id: rbr-accent-choice
|
||
title: Primary accent color
|
||
description: |
|
||
활성/선택 항목 강조에 쓰일 메인 색.
|
||
RBR 기본 룰은 Kerb Red. 커서는 별개 — 아래 Cursor 옵션 참고.
|
||
type: class-select
|
||
default: rbr-accent-red
|
||
allowEmpty: false
|
||
options:
|
||
-
|
||
label: Kerb Red (기본)
|
||
value: rbr-accent-red
|
||
-
|
||
label: RB Yellow
|
||
value: rbr-accent-yellow
|
||
-
|
||
label: Chequer White
|
||
value: rbr-accent-white
|
||
|
||
-
|
||
id: rbr-section-headings
|
||
title: Headings
|
||
type: heading
|
||
level: 2
|
||
collapsed: false
|
||
|
||
-
|
||
id: rbr-headings-choice
|
||
title: Heading style
|
||
description: |
|
||
h1~h3 색상 패턴.
|
||
Trio : h1=accent · h2=yellow · h3=white (RBR 기본)
|
||
Mono : 모든 헤딩이 본문 색 (강조 최소화)
|
||
Accent only : h1만 accent, 나머지는 본문 색
|
||
type: class-select
|
||
default: rbr-headings-trio
|
||
allowEmpty: false
|
||
options:
|
||
-
|
||
label: Trio (기본)
|
||
value: rbr-headings-trio
|
||
-
|
||
label: Monochrome
|
||
value: rbr-headings-mono
|
||
-
|
||
label: Accent only
|
||
value: rbr-headings-accent-only
|
||
|
||
-
|
||
id: rbr-section-cursor
|
||
title: Cursor
|
||
type: heading
|
||
level: 2
|
||
collapsed: false
|
||
|
||
-
|
||
id: rbr-cursor-choice
|
||
title: Cursor color
|
||
description: |
|
||
에디터 커서 색.
|
||
RB Yellow : RBR 기본 — 항상 주목 픽셀
|
||
RB Warm : 한 단계 덜 자극적인 따뜻한 노랑
|
||
Follow accent : 위 Primary accent를 따라감
|
||
type: class-select
|
||
default: rbr-cursor-yellow
|
||
allowEmpty: false
|
||
options:
|
||
-
|
||
label: RB Yellow (기본)
|
||
value: rbr-cursor-yellow
|
||
-
|
||
label: RB Warm
|
||
value: rbr-cursor-warm
|
||
-
|
||
label: Follow accent
|
||
value: rbr-cursor-accent
|
||
|
||
-
|
||
id: rbr-section-tab
|
||
title: Tab indicator
|
||
type: heading
|
||
level: 2
|
||
collapsed: false
|
||
|
||
-
|
||
id: rbr-tab-choice
|
||
title: Active tab indicator
|
||
description: |
|
||
활성 탭 강조 방식.
|
||
Top : 위쪽에 accent 색 띠 (RBR 기본)
|
||
Bottom : 아래쪽 띠
|
||
Fill : 탭 전체 배경에 accent 옅게 깔기
|
||
type: class-select
|
||
default: rbr-tab-top
|
||
allowEmpty: false
|
||
options:
|
||
-
|
||
label: Top border (기본)
|
||
value: rbr-tab-top
|
||
-
|
||
label: Bottom border
|
||
value: rbr-tab-bottom
|
||
-
|
||
label: Background fill
|
||
value: rbr-tab-fill
|
||
|
||
-
|
||
id: rbr-section-typography
|
||
title: Typography
|
||
type: heading
|
||
level: 2
|
||
collapsed: false
|
||
|
||
-
|
||
id: rbr-code-scale
|
||
title: Code font size
|
||
description: 코드 블록과 인라인 코드의 폰트 크기 (em).
|
||
type: variable-number-slider
|
||
default: 0.92
|
||
min: 0.80
|
||
max: 1.10
|
||
step: 0.01
|
||
format: em
|
||
|
||
-
|
||
id: rbr-highlight-alpha
|
||
title: Highlight intensity
|
||
description: |
|
||
==하이라이트== 배경 alpha. 검색 매치도 같은 값을 사용.
|
||
0.10 = 거의 안 보임, 0.60 = 강한 강조.
|
||
type: variable-number-slider
|
||
default: 0.30
|
||
min: 0.10
|
||
max: 0.60
|
||
step: 0.05
|
||
|
||
-
|
||
id: rbr-section-autohide
|
||
title: Auto hide
|
||
description: |
|
||
마우스를 올렸을 때만 보이도록 UI 요소를 숨긴다.
|
||
호버가 없는 모바일에서는 자동 비활성.
|
||
type: heading
|
||
level: 2
|
||
collapsed: true
|
||
|
||
-
|
||
id: rbr-autohide-tabs
|
||
title: Auto hide tab bar
|
||
description: 탭 헤더 바를 숨기고 상단/타이틀바에 호버하면 펼침.
|
||
type: class-toggle
|
||
|
||
-
|
||
id: rbr-autohide-statusbar
|
||
title: Auto hide status bar
|
||
description: 하단 상태바를 가는 띠로 줄이고 호버 시 펼침.
|
||
type: class-toggle
|
||
|
||
-
|
||
id: rbr-autohide-ribbon
|
||
title: Auto hide left ribbon
|
||
description: 좌측 리본 메뉴를 가는 띠로 줄이고 호버 시 펼침.
|
||
type: class-toggle
|
||
|
||
-
|
||
id: rbr-autohide-vault-profile
|
||
title: Auto hide vault profile
|
||
description: 좌측 사이드바 하단의 vault 프로필 영역 숨김. 사이드바 호버 시 펼침.
|
||
type: class-toggle
|
||
|
||
-
|
||
id: rbr-autohide-delay
|
||
title: Hover delay before expanding
|
||
description: |
|
||
호버 후 펼쳐지기까지 대기 시간 (ms).
|
||
커서가 잠깐 지나가는 것만으로 깜빡이지 않게 하는 hover-intent 지연.
|
||
0 = 즉시 펼침, 100 = 살짝만 확인(기본), 400+ = 의도적으로 머무를 때만.
|
||
type: variable-number-slider
|
||
default: 100
|
||
min: 0
|
||
max: 600
|
||
step: 50
|
||
format: ms
|
||
|
||
-
|
||
id: rbr-autohide-duration
|
||
title: Expand/collapse animation duration
|
||
description: |
|
||
자동 숨김 영역이 펼쳐지고 닫히는 애니메이션 길이 (ms).
|
||
짧을수록 가볍고 스냅함. 기본 80ms. 더 스냅하게 60까지 내릴 수 있음.
|
||
layout-property transition 비용을 줄여 마우스 hover 시
|
||
메인 스레드 부담을 직접 낮춤.
|
||
type: variable-number-slider
|
||
default: 80
|
||
min: 60
|
||
max: 280
|
||
step: 20
|
||
format: ms
|
||
|
||
-
|
||
id: rbr-autohide-hitzone
|
||
title: Tab strip top hover zone (above strip)
|
||
description: |
|
||
접힌 탭 strip 위쪽(타이틀바 방향) hover 영역의 높이 (px).
|
||
아래쪽 hover 영역은 "펼친 탭 높이"에 자동으로 맞춰지므로 이 값과
|
||
무관하다 — 이 슬라이더는 위쪽 밴드/타이틀바 트리거 높이만 조절.
|
||
메인 에디터/좌우 사이드 패널 strip 모두에 적용.
|
||
type: variable-number-slider
|
||
default: 12
|
||
min: 4
|
||
max: 100
|
||
step: 4
|
||
format: px
|
||
|
||
-
|
||
id: rbr-ribbon-expanded
|
||
title: Expanded ribbon width (= hover hit zone)
|
||
description: |
|
||
접힌 ribbon 위로 마우스를 올렸을 때 펼쳐지는 폭 (px).
|
||
hover hit zone 이 이 값과 항상 동일하게 맞춰짐 — 즉 "펼쳐질 리본이
|
||
차지할 자리"가 그대로 hover 영역이 된다. 그 자리에 마우스를 올리면
|
||
리본이 딱 그 자리로 펼쳐짐.
|
||
값을 키우면 리본이 더 넓게 펼쳐지면서 hover 영역도 같이 넓어짐.
|
||
아이콘은 hit zone 밴드 위에 있어서 폭을 키워도 클릭은 안 막힘.
|
||
주의: 접힌 6px 를 넘는 hover 밴드 구간의 클릭은 ribbon 으로 캡처됨.
|
||
type: variable-number-slider
|
||
default: 44
|
||
min: 32
|
||
max: 200
|
||
step: 4
|
||
format: px
|
||
|
||
-
|
||
id: rbr-statusbar-expanded
|
||
title: Expanded status bar height (= hover hit zone)
|
||
description: |
|
||
상태 바가 hover 시 펼쳐지는 높이 (px). hover hit zone 이 이 값과
|
||
동일하게 맞춰져 "펼쳐질 자리"가 그대로 hover 영역이 된다 — 리본과
|
||
같은 방식. 값을 키우면 상태 바가 더 높게 펼쳐지고 hover 영역도
|
||
같이 넓어짐.
|
||
type: variable-number-slider
|
||
default: 24
|
||
min: 12
|
||
max: 120
|
||
step: 4
|
||
format: px
|
||
|
||
-
|
||
id: rbr-section-card
|
||
title: Card layout
|
||
description: |
|
||
워크스페이스 패널을 카드처럼 분리해서 표시.
|
||
여백 + 둥근 모서리 + 부드러운 그림자가 추가된다.
|
||
type: heading
|
||
level: 2
|
||
collapsed: true
|
||
|
||
-
|
||
id: rbr-card-layout
|
||
title: Enable card layout
|
||
description: 카드 레이아웃 켜기/끄기.
|
||
type: class-toggle
|
||
|
||
-
|
||
id: rbr-card-radius
|
||
title: Card corner radius
|
||
description: 카드 모서리 라운딩 (px).
|
||
type: variable-number-slider
|
||
default: 8
|
||
min: 0
|
||
max: 20
|
||
step: 1
|
||
format: px
|
||
|
||
-
|
||
id: rbr-card-gap
|
||
title: Card gap
|
||
description: 카드 사이 간격 (px).
|
||
type: variable-number-slider
|
||
default: 12
|
||
min: 0
|
||
max: 24
|
||
step: 1
|
||
format: px
|
||
|
||
*/
|
||
|
||
|
||
/* ========================================================================
|
||
Palette tokens (single source of truth)
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
/* ---- Neutral layers (lightness ladder) ---- */
|
||
--rbr-crust: #05091a; /* ANSI black, gutter shadow */
|
||
--rbr-mantle: #070d1f; /* sidebar, secondary panes */
|
||
--rbr-base: #0a1128; /* primary background */
|
||
--rbr-surface-0: #121a36; /* floating panels, tooltips */
|
||
--rbr-surface-1: #15203f; /* current-line, hover */
|
||
--rbr-surface-2: #1f2a52; /* selection, active tab fill */
|
||
--rbr-overlay-0: #3a4466; /* line numbers, inactive borders */
|
||
--rbr-overlay-1: #4a5580; /* medium borders */
|
||
--rbr-overlay-2: #565f80; /* strong borders */
|
||
--rbr-subtext-0: #6a7495; /* comments, placeholders */
|
||
--rbr-subtext-1: #8590ae; /* secondary text */
|
||
--rbr-text: #c8d0e8; /* primary text */
|
||
--rbr-track-line: #e8ecf5; /* hottest white */
|
||
|
||
/* ---- Brand trio + accent extras ---- */
|
||
--rbr-kerb-red: #e84a55;
|
||
--rbr-kerb-red-bright: #f56570;
|
||
--rbr-kerb-red-pure: #cc1e4a;
|
||
|
||
--rbr-rb-yellow: #ffd84d;
|
||
--rbr-rb-warm: #f5c842;
|
||
--rbr-rb-pure: #ffc906;
|
||
|
||
--rbr-chequer-white: #ffffff;
|
||
|
||
--rbr-oracle-blue: #82a0d8;
|
||
--rbr-sky-blue: #a4bde8;
|
||
--rbr-track-green: #9cc080;
|
||
--rbr-paddock-green: #b8d49e;
|
||
--rbr-cool-grey: #a8a8b0;
|
||
--rbr-light-grey: #c8c8d0;
|
||
--rbr-livery-blue: #2563eb;
|
||
--rbr-livery-blue-bright:#4D7EFF;
|
||
|
||
/* RGB triplets for places Obsidian expects --…-rgb tokens */
|
||
--rbr-base-rgb: 10, 17, 40;
|
||
--rbr-kerb-red-rgb: 232, 74, 85;
|
||
--rbr-kerb-bright-rgb: 245, 101, 112;
|
||
--rbr-rb-yellow-rgb: 255, 216, 77;
|
||
--rbr-rb-warm-rgb: 245, 200, 66;
|
||
--rbr-paddock-green-rgb: 184, 212, 158;
|
||
--rbr-oracle-blue-rgb: 130, 160, 216;
|
||
--rbr-chequer-white-rgb: 255, 255, 255;
|
||
|
||
/* ---- Indirect "active accent" slot — Style Settings retargets this ----
|
||
Defaults to kerb red; body.rbr-accent-{yellow,white} below override. */
|
||
--rbr-accent: var(--rbr-kerb-red);
|
||
--rbr-accent-rgb: var(--rbr-kerb-red-rgb);
|
||
--rbr-accent-hover: var(--rbr-kerb-red-bright);
|
||
|
||
/* ---- Indirect "cursor" slot — defaults to RB Yellow.
|
||
The -rgb variant is also exposed so selection backgrounds
|
||
(incl. vim visual mode) can alpha-blend the same color. */
|
||
--rbr-cursor: var(--rbr-rb-yellow);
|
||
--rbr-cursor-rgb: var(--rbr-rb-yellow-rgb);
|
||
}
|
||
|
||
/* Style Settings injects body.<value-of-class-select> on <body>. */
|
||
|
||
/* Accent overrides */
|
||
body.rbr-accent-red {
|
||
--rbr-accent: var(--rbr-kerb-red);
|
||
--rbr-accent-rgb: var(--rbr-kerb-red-rgb);
|
||
--rbr-accent-hover: var(--rbr-kerb-red-bright);
|
||
}
|
||
body.rbr-accent-yellow {
|
||
--rbr-accent: var(--rbr-rb-yellow);
|
||
--rbr-accent-rgb: var(--rbr-rb-yellow-rgb);
|
||
--rbr-accent-hover: var(--rbr-rb-pure);
|
||
}
|
||
body.rbr-accent-white {
|
||
--rbr-accent: var(--rbr-chequer-white);
|
||
--rbr-accent-rgb: var(--rbr-chequer-white-rgb);
|
||
--rbr-accent-hover: var(--rbr-track-line);
|
||
}
|
||
|
||
/* Cursor overrides — variable level (color + rgb triplet for selection alpha) */
|
||
body.rbr-cursor-yellow {
|
||
--rbr-cursor: var(--rbr-rb-yellow);
|
||
--rbr-cursor-rgb: var(--rbr-rb-yellow-rgb);
|
||
}
|
||
body.rbr-cursor-warm {
|
||
--rbr-cursor: var(--rbr-rb-warm);
|
||
--rbr-cursor-rgb: var(--rbr-rb-warm-rgb);
|
||
}
|
||
body.rbr-cursor-accent {
|
||
--rbr-cursor: var(--rbr-accent);
|
||
--rbr-cursor-rgb: var(--rbr-accent-rgb);
|
||
}
|
||
|
||
/* Cursor overrides — direct selector enforcement.
|
||
CodeMirror 6 paints the caret as a border on .cm-cursor, which doesn't
|
||
always pick up --caret-color via cascade. Force both caret-color and
|
||
border-left-color so the cursor changes regardless. */
|
||
body.rbr-cursor-yellow .cm-content,
|
||
body.rbr-cursor-yellow .cm-cursor,
|
||
body.rbr-cursor-yellow .cm-cursor.cm-cursor-primary,
|
||
body.rbr-cursor-yellow .CodeMirror-cursor {
|
||
caret-color: var(--rbr-rb-yellow) !important;
|
||
border-left-color: var(--rbr-rb-yellow) !important;
|
||
}
|
||
body.rbr-cursor-warm .cm-content,
|
||
body.rbr-cursor-warm .cm-cursor,
|
||
body.rbr-cursor-warm .cm-cursor.cm-cursor-primary,
|
||
body.rbr-cursor-warm .CodeMirror-cursor {
|
||
caret-color: var(--rbr-rb-warm) !important;
|
||
border-left-color: var(--rbr-rb-warm) !important;
|
||
}
|
||
body.rbr-cursor-accent .cm-content,
|
||
body.rbr-cursor-accent .cm-cursor,
|
||
body.rbr-cursor-accent .cm-cursor.cm-cursor-primary,
|
||
body.rbr-cursor-accent .CodeMirror-cursor {
|
||
caret-color: var(--rbr-accent) !important;
|
||
border-left-color: var(--rbr-accent) !important;
|
||
}
|
||
|
||
/* Vim mode block cursor — normal/visual modes draw .cm-fat-cursor as a
|
||
filled block, not a thin caret. Different paint property (background +
|
||
outline), so we have to target it separately.
|
||
|
||
.cm-fat-cursor — focused primary cursor (filled block)
|
||
.cm-fat-cursor:not(.cm-cursor-primary)
|
||
— unfocused / secondary (outlined only)
|
||
The --rbr-cursor variable already follows the user's choice
|
||
(yellow / warm / accent) so we just plug it in. */
|
||
.cm-fat-cursor,
|
||
.cm-fat-cursor.cm-cursor-primary {
|
||
background-color: var(--rbr-cursor) !important;
|
||
outline-color: var(--rbr-cursor) !important;
|
||
border-color: var(--rbr-cursor) !important;
|
||
/* keep the character readable underneath the block fill */
|
||
color: var(--rbr-base) !important;
|
||
}
|
||
|
||
.cm-fat-cursor:not(.cm-cursor-primary) {
|
||
background-color: transparent !important;
|
||
outline: 1px solid var(--rbr-cursor) !important;
|
||
color: inherit !important;
|
||
}
|
||
|
||
/* Some Obsidian builds toggle .cm-fat-cursor on the editor host instead of
|
||
the cursor element — cover both cases */
|
||
.cm-editor.cm-fat-cursor .cm-cursor,
|
||
.cm-editor.cm-fat-cursor .cm-cursor.cm-cursor-primary {
|
||
background-color: var(--rbr-cursor) !important;
|
||
outline-color: var(--rbr-cursor) !important;
|
||
border-color: var(--rbr-cursor) !important;
|
||
}
|
||
|
||
/* ========================================================================
|
||
Core Obsidian variables
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
/* ---- Backgrounds ---- */
|
||
--background-primary: var(--rbr-base);
|
||
--background-primary-alt: var(--rbr-mantle);
|
||
--background-secondary: var(--rbr-mantle);
|
||
--background-secondary-alt: var(--rbr-crust);
|
||
|
||
--background-modifier-border: var(--rbr-surface-1);
|
||
--background-modifier-border-hover: var(--rbr-overlay-0);
|
||
--background-modifier-border-focus: var(--rbr-accent);
|
||
--background-modifier-hover: var(--rbr-surface-1);
|
||
--background-modifier-active-hover: var(--rbr-surface-2);
|
||
--background-modifier-form-field: var(--rbr-surface-0);
|
||
--background-modifier-form-field-highlighted: var(--rbr-surface-1);
|
||
--background-modifier-box-shadow: rgba(5, 9, 26, 0.5);
|
||
|
||
--background-modifier-error: rgba(var(--rbr-kerb-bright-rgb), 0.20);
|
||
--background-modifier-error-rgb: var(--rbr-kerb-bright-rgb);
|
||
--background-modifier-error-hover: rgba(var(--rbr-kerb-bright-rgb), 0.30);
|
||
--background-modifier-success: rgba(var(--rbr-paddock-green-rgb), 0.20);
|
||
--background-modifier-message: rgba(10, 17, 40, 0.9);
|
||
|
||
/* ---- Text ---- */
|
||
--text-normal: var(--rbr-text);
|
||
--text-muted: var(--rbr-subtext-1);
|
||
--text-faint: var(--rbr-subtext-0);
|
||
--text-on-accent: var(--rbr-base);
|
||
--text-error: var(--rbr-kerb-red-bright);
|
||
--text-warning: var(--rbr-rb-yellow);
|
||
--text-success: var(--rbr-paddock-green);
|
||
--text-accent: var(--rbr-accent);
|
||
--text-accent-hover: var(--rbr-accent-hover);
|
||
|
||
--text-selection: rgba(var(--rbr-accent-rgb), 0.25);
|
||
|
||
/* Highlight intensity — driven by --rbr-highlight-alpha slider (default 0.30) */
|
||
--text-highlight-bg: rgba(var(--rbr-rb-yellow-rgb), var(--rbr-highlight-alpha, 0.30));
|
||
--text-highlight-bg-active: rgba(var(--rbr-rb-yellow-rgb), calc(var(--rbr-highlight-alpha, 0.30) + 0.15));
|
||
|
||
/* ---- Interactive ---- */
|
||
--interactive-normal: var(--rbr-surface-0);
|
||
--interactive-hover: var(--rbr-surface-1);
|
||
--interactive-accent: var(--rbr-accent);
|
||
--interactive-accent-hover: var(--rbr-accent-hover);
|
||
--interactive-accent-rgb: var(--rbr-accent-rgb);
|
||
--interactive-success: var(--rbr-paddock-green);
|
||
|
||
/* ---- Caret — driven by --rbr-cursor (default RB Yellow) ---- */
|
||
--caret-color: var(--rbr-cursor);
|
||
|
||
/* ---- Borders / focus ---- */
|
||
--divider-color: var(--rbr-surface-1);
|
||
--divider-color-hover: var(--rbr-overlay-0);
|
||
|
||
/* ---- Indentation guides ---- */
|
||
--indentation-guide: var(--rbr-overlay-0);
|
||
--indentation-guide-active: var(--rbr-accent);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Headings — Style Settings (rbr-headings-*) controls the pattern.
|
||
Default trio: h1=accent · h2=yellow · h3=white.
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--h1-color: var(--rbr-accent);
|
||
--h2-color: var(--rbr-rb-yellow);
|
||
--h3-color: var(--rbr-chequer-white);
|
||
--h4-color: var(--rbr-paddock-green);
|
||
--h5-color: var(--rbr-oracle-blue);
|
||
--h6-color: var(--rbr-sky-blue);
|
||
|
||
--h1-weight: 800;
|
||
--h2-weight: 700;
|
||
--h3-weight: 700;
|
||
--h4-weight: 600;
|
||
--h5-weight: 600;
|
||
--h6-weight: 600;
|
||
}
|
||
|
||
/* Monochrome — every heading uses normal text color */
|
||
body.rbr-headings-mono {
|
||
--h1-color: var(--rbr-text);
|
||
--h2-color: var(--rbr-text);
|
||
--h3-color: var(--rbr-text);
|
||
--h4-color: var(--rbr-text);
|
||
--h5-color: var(--rbr-text);
|
||
--h6-color: var(--rbr-text);
|
||
}
|
||
|
||
/* Accent only — h1 keeps the accent, everything else is normal text */
|
||
body.rbr-headings-accent-only {
|
||
--h1-color: var(--rbr-accent);
|
||
--h2-color: var(--rbr-text);
|
||
--h3-color: var(--rbr-text);
|
||
--h4-color: var(--rbr-text);
|
||
--h5-color: var(--rbr-text);
|
||
--h6-color: var(--rbr-text);
|
||
}
|
||
|
||
/* Direct selector enforcement — covers reading view, live preview (CM6),
|
||
and source mode. Obsidian/CodeMirror occasionally resolve heading color
|
||
without going through --h1-color, so we paint the elements directly too. */
|
||
|
||
/* Reading view + rendered markdown */
|
||
body.rbr-headings-mono .markdown-rendered h1,
|
||
body.rbr-headings-mono .markdown-rendered h2,
|
||
body.rbr-headings-mono .markdown-rendered h3,
|
||
body.rbr-headings-mono .markdown-rendered h4,
|
||
body.rbr-headings-mono .markdown-rendered h5,
|
||
body.rbr-headings-mono .markdown-rendered h6,
|
||
body.rbr-headings-mono .markdown-preview-view h1,
|
||
body.rbr-headings-mono .markdown-preview-view h2,
|
||
body.rbr-headings-mono .markdown-preview-view h3,
|
||
body.rbr-headings-mono .markdown-preview-view h4,
|
||
body.rbr-headings-mono .markdown-preview-view h5,
|
||
body.rbr-headings-mono .markdown-preview-view h6,
|
||
/* Live preview (CM6 HyperMD line classes) */
|
||
body.rbr-headings-mono .HyperMD-header,
|
||
body.rbr-headings-mono .HyperMD-header-1,
|
||
body.rbr-headings-mono .HyperMD-header-2,
|
||
body.rbr-headings-mono .HyperMD-header-3,
|
||
body.rbr-headings-mono .HyperMD-header-4,
|
||
body.rbr-headings-mono .HyperMD-header-5,
|
||
body.rbr-headings-mono .HyperMD-header-6,
|
||
/* Source mode token classes */
|
||
body.rbr-headings-mono .cm-header,
|
||
body.rbr-headings-mono .cm-header-1,
|
||
body.rbr-headings-mono .cm-header-2,
|
||
body.rbr-headings-mono .cm-header-3,
|
||
body.rbr-headings-mono .cm-header-4,
|
||
body.rbr-headings-mono .cm-header-5,
|
||
body.rbr-headings-mono .cm-header-6,
|
||
/* Inline title in the editor header */
|
||
body.rbr-headings-mono .inline-title {
|
||
color: var(--rbr-text) !important;
|
||
}
|
||
|
||
/* Accent-only — h2..h6 + their CM equivalents fall back to text */
|
||
body.rbr-headings-accent-only .markdown-rendered h2,
|
||
body.rbr-headings-accent-only .markdown-rendered h3,
|
||
body.rbr-headings-accent-only .markdown-rendered h4,
|
||
body.rbr-headings-accent-only .markdown-rendered h5,
|
||
body.rbr-headings-accent-only .markdown-rendered h6,
|
||
body.rbr-headings-accent-only .markdown-preview-view h2,
|
||
body.rbr-headings-accent-only .markdown-preview-view h3,
|
||
body.rbr-headings-accent-only .markdown-preview-view h4,
|
||
body.rbr-headings-accent-only .markdown-preview-view h5,
|
||
body.rbr-headings-accent-only .markdown-preview-view h6,
|
||
body.rbr-headings-accent-only .HyperMD-header-2,
|
||
body.rbr-headings-accent-only .HyperMD-header-3,
|
||
body.rbr-headings-accent-only .HyperMD-header-4,
|
||
body.rbr-headings-accent-only .HyperMD-header-5,
|
||
body.rbr-headings-accent-only .HyperMD-header-6,
|
||
body.rbr-headings-accent-only .cm-header-2,
|
||
body.rbr-headings-accent-only .cm-header-3,
|
||
body.rbr-headings-accent-only .cm-header-4,
|
||
body.rbr-headings-accent-only .cm-header-5,
|
||
body.rbr-headings-accent-only .cm-header-6 {
|
||
color: var(--rbr-text) !important;
|
||
}
|
||
|
||
/* Accent-only — h1 + inline title keep the accent */
|
||
body.rbr-headings-accent-only .markdown-rendered h1,
|
||
body.rbr-headings-accent-only .markdown-preview-view h1,
|
||
body.rbr-headings-accent-only .HyperMD-header-1,
|
||
body.rbr-headings-accent-only .cm-header-1,
|
||
body.rbr-headings-accent-only .inline-title {
|
||
color: var(--rbr-accent) !important;
|
||
}
|
||
|
||
/* Trio — RBR's default. Forced via direct selectors so external rules
|
||
(Minimal's `colorful-headings` leftover, plugin overrides, snippets)
|
||
can't collapse all headings to one color. */
|
||
body.rbr-headings-trio .markdown-rendered h1,
|
||
body.rbr-headings-trio .markdown-preview-view h1,
|
||
body.rbr-headings-trio .HyperMD-header-1,
|
||
body.rbr-headings-trio .cm-header-1,
|
||
body.rbr-headings-trio .inline-title {
|
||
color: var(--rbr-accent) !important;
|
||
}
|
||
body.rbr-headings-trio .markdown-rendered h2,
|
||
body.rbr-headings-trio .markdown-preview-view h2,
|
||
body.rbr-headings-trio .HyperMD-header-2,
|
||
body.rbr-headings-trio .cm-header-2 {
|
||
color: var(--rbr-rb-yellow) !important;
|
||
}
|
||
body.rbr-headings-trio .markdown-rendered h3,
|
||
body.rbr-headings-trio .markdown-preview-view h3,
|
||
body.rbr-headings-trio .HyperMD-header-3,
|
||
body.rbr-headings-trio .cm-header-3 {
|
||
color: var(--rbr-chequer-white) !important;
|
||
}
|
||
body.rbr-headings-trio .markdown-rendered h4,
|
||
body.rbr-headings-trio .markdown-preview-view h4,
|
||
body.rbr-headings-trio .HyperMD-header-4,
|
||
body.rbr-headings-trio .cm-header-4 {
|
||
color: var(--rbr-paddock-green) !important;
|
||
}
|
||
body.rbr-headings-trio .markdown-rendered h5,
|
||
body.rbr-headings-trio .markdown-preview-view h5,
|
||
body.rbr-headings-trio .HyperMD-header-5,
|
||
body.rbr-headings-trio .cm-header-5 {
|
||
color: var(--rbr-oracle-blue) !important;
|
||
}
|
||
body.rbr-headings-trio .markdown-rendered h6,
|
||
body.rbr-headings-trio .markdown-preview-view h6,
|
||
body.rbr-headings-trio .HyperMD-header-6,
|
||
body.rbr-headings-trio .cm-header-6 {
|
||
color: var(--rbr-sky-blue) !important;
|
||
}
|
||
|
||
/* ========================================================================
|
||
Inline markup — links, bold, italic, blockquote, hr
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--link-color: var(--rbr-oracle-blue);
|
||
--link-color-hover: var(--rbr-sky-blue);
|
||
--link-external-color: var(--rbr-livery-blue-bright);
|
||
--link-external-color-hover: var(--rbr-sky-blue);
|
||
--link-unresolved-color: var(--rbr-kerb-red-bright);
|
||
--link-unresolved-opacity: 0.85;
|
||
--link-unresolved-filter: none;
|
||
|
||
--bold-color: var(--rbr-chequer-white);
|
||
--bold-weight: 700;
|
||
--italic-color: var(--rbr-livery-blue-bright);
|
||
|
||
--blockquote-border-color: var(--rbr-accent);
|
||
--blockquote-background-color: rgba(var(--rbr-accent-rgb), 0.06);
|
||
--blockquote-color: var(--rbr-kerb-red-pure);
|
||
|
||
--hr-color: var(--rbr-overlay-0);
|
||
--hr-thickness: 2px;
|
||
}
|
||
|
||
/* Strikethrough — fade into subtext.
|
||
Obsidian has no dedicated variable for ~~text~~, so use direct selectors. */
|
||
.markdown-rendered del,
|
||
.markdown-rendered s,
|
||
.markdown-preview-view del,
|
||
.markdown-preview-view s,
|
||
.cm-s-obsidian .cm-strikethrough {
|
||
color: var(--rbr-subtext-0);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Code — inline + block. Syntax tokens stay locked to RBR doctrine
|
||
regardless of the active accent (keeps code legible).
|
||
--code-size is driven by the --rbr-code-scale slider (default 0.92em).
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--code-background: var(--rbr-mantle);
|
||
--code-normal: var(--rbr-text);
|
||
--code-keyword: var(--rbr-kerb-red);
|
||
--code-string: var(--rbr-paddock-green);
|
||
--code-number: var(--rbr-light-grey);
|
||
--code-function: var(--rbr-chequer-white);
|
||
--code-property: var(--rbr-oracle-blue);
|
||
--code-tag: var(--rbr-kerb-red);
|
||
--code-comment: var(--rbr-subtext-0);
|
||
--code-operator: var(--rbr-subtext-1);
|
||
--code-punctuation: var(--rbr-subtext-1);
|
||
--code-value: var(--rbr-paddock-green);
|
||
--code-important: var(--rbr-rb-yellow);
|
||
--code-size: var(--rbr-code-scale, 0.92em);
|
||
}
|
||
|
||
/* CodeMirror live-preview / source-mode token overrides. */
|
||
.cm-s-obsidian span.cm-keyword,
|
||
.cm-s-obsidian span.cm-atom,
|
||
.cm-s-obsidian span.cm-builtin { color: var(--rbr-kerb-red); }
|
||
.cm-s-obsidian span.cm-string,
|
||
.cm-s-obsidian span.cm-string-2 { color: var(--rbr-paddock-green); }
|
||
.cm-s-obsidian span.cm-number { color: var(--rbr-light-grey); }
|
||
.cm-s-obsidian span.cm-def,
|
||
.cm-s-obsidian span.cm-property { color: var(--rbr-chequer-white); }
|
||
.cm-s-obsidian span.cm-variable { color: var(--rbr-text); }
|
||
.cm-s-obsidian span.cm-variable-2 { color: var(--rbr-sky-blue); }
|
||
.cm-s-obsidian span.cm-variable-3,
|
||
.cm-s-obsidian span.cm-type { color: var(--rbr-rb-yellow); }
|
||
.cm-s-obsidian span.cm-comment { color: var(--rbr-subtext-0); font-style: italic; }
|
||
.cm-s-obsidian span.cm-meta,
|
||
.cm-s-obsidian span.cm-qualifier { color: var(--rbr-sky-blue); }
|
||
.cm-s-obsidian span.cm-operator { color: var(--rbr-subtext-1); }
|
||
.cm-s-obsidian span.cm-tag { color: var(--rbr-kerb-red); }
|
||
.cm-s-obsidian span.cm-attribute { color: var(--rbr-rb-yellow); }
|
||
.cm-s-obsidian span.cm-bracket { color: var(--rbr-subtext-1); }
|
||
.cm-s-obsidian span.cm-link { color: var(--rbr-oracle-blue); }
|
||
.cm-s-obsidian span.cm-error { color: var(--rbr-kerb-red-bright); }
|
||
.cm-s-obsidian span.cm-header { color: var(--rbr-accent); }
|
||
.cm-s-obsidian span.cm-hr { color: var(--rbr-overlay-0); }
|
||
.cm-s-obsidian span.cm-quote { color: var(--blockquote-color); }
|
||
|
||
/* Live-preview / source-mode quote line — Obsidian wraps each quoted line
|
||
in .HyperMD-quote, which doesn't go through --blockquote-color by default.
|
||
Force it so Reading view and Live preview match. */
|
||
.cm-s-obsidian .HyperMD-quote,
|
||
.cm-s-obsidian .HyperMD-quote-1,
|
||
.cm-s-obsidian .HyperMD-quote-2,
|
||
.cm-s-obsidian .HyperMD-quote-3 {
|
||
color: var(--blockquote-color) !important;
|
||
}
|
||
|
||
/* Reading view enforcement — some plugins/snippets paint blockquote color
|
||
directly on the <blockquote> rather than letting --blockquote-color cascade */
|
||
.markdown-rendered blockquote,
|
||
.markdown-preview-view blockquote {
|
||
color: var(--blockquote-color) !important;
|
||
}
|
||
|
||
/* ========================================================================
|
||
Tables
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--table-background: transparent;
|
||
--table-header-background: var(--rbr-mantle);
|
||
--table-header-background-hover: var(--rbr-surface-0);
|
||
--table-row-background: transparent;
|
||
--table-row-alt-background: rgba(var(--rbr-base-rgb), 0.4);
|
||
--table-row-background-hover: var(--rbr-surface-1);
|
||
--table-border-color: var(--rbr-surface-1);
|
||
--table-header-color: var(--rbr-rb-yellow);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Tags — small pill badges. Yellow because tags are a "look here" cue.
|
||
Foreground is forced to black for legibility against the yellow fill.
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--tag-color: #000000;
|
||
--tag-color-hover: #000000;
|
||
--tag-background: var(--rbr-rb-yellow);
|
||
--tag-background-hover: var(--rbr-rb-pure);
|
||
--tag-border-color: transparent;
|
||
--tag-border-color-hover: transparent;
|
||
--tag-radius: 10px;
|
||
--tag-weight: 600;
|
||
}
|
||
|
||
/* Direct selector enforcement — CodeMirror's hashtag spans inherit syntax
|
||
token rules (.cm-meta / .cm-tag) that ignore --tag-color. Force black
|
||
on the tag pill in reading view, live preview, and source mode. */
|
||
.tag,
|
||
a.tag,
|
||
.cm-s-obsidian span.cm-hashtag,
|
||
.cm-s-obsidian span.cm-hashtag-begin,
|
||
.cm-s-obsidian span.cm-hashtag-end,
|
||
.cm-s-obsidian span.cm-meta.cm-hashtag,
|
||
.cm-s-obsidian span.cm-tag.cm-hashtag {
|
||
color: #000000 !important;
|
||
background-color: var(--rbr-rb-yellow) !important;
|
||
font-weight: 600 !important;
|
||
}
|
||
|
||
.tag:hover,
|
||
a.tag:hover,
|
||
.cm-s-obsidian span.cm-hashtag:hover {
|
||
color: #000000 !important;
|
||
background-color: var(--rbr-rb-pure) !important;
|
||
}
|
||
|
||
/* ========================================================================
|
||
Checkboxes — follow the active accent
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--checkbox-color: var(--rbr-accent);
|
||
--checkbox-color-hover: var(--rbr-accent-hover);
|
||
--checkbox-border-color: var(--rbr-overlay-2);
|
||
--checkbox-border-color-hover: var(--rbr-accent);
|
||
--checkbox-marker-color: var(--rbr-base);
|
||
--checklist-done-color: var(--rbr-subtext-0);
|
||
--checklist-done-decoration: line-through;
|
||
}
|
||
|
||
/* ========================================================================
|
||
Modals, popovers, tooltips
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--modal-background: var(--rbr-surface-0);
|
||
--modal-border-color: var(--rbr-surface-2);
|
||
--modal-radius: 10px;
|
||
--popover-background: var(--rbr-surface-0);
|
||
--popover-border-color: var(--rbr-surface-2);
|
||
--tooltip-background: var(--rbr-mantle);
|
||
--tooltip-text-color: var(--rbr-text);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Title bar / status bar
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--titlebar-background: var(--rbr-mantle);
|
||
--titlebar-background-focused: var(--rbr-mantle);
|
||
--titlebar-border-color: var(--rbr-surface-1);
|
||
--titlebar-text-color: var(--rbr-text);
|
||
--titlebar-text-color-focused: var(--rbr-text);
|
||
--titlebar-text-color-blurred: var(--rbr-subtext-1);
|
||
|
||
--status-bar-background: var(--rbr-mantle);
|
||
--status-bar-text-color: var(--rbr-subtext-1);
|
||
--status-bar-border-color: var(--rbr-surface-1);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Tabs — base variables. Indicator style chosen by Style Settings below.
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--tab-background-active: var(--rbr-base);
|
||
--tab-text-color: var(--rbr-subtext-1);
|
||
--tab-text-color-active: var(--rbr-text);
|
||
--tab-text-color-focused: var(--rbr-text);
|
||
--tab-text-color-focused-active: var(--rbr-text);
|
||
--tab-text-color-focused-active-current: var(--rbr-text);
|
||
--tab-text-color-active-inactive-current: var(--rbr-subtext-1);
|
||
--tab-divider-color: var(--rbr-surface-1);
|
||
--tab-outline-color: var(--rbr-surface-1);
|
||
--tab-container-background: var(--rbr-mantle);
|
||
}
|
||
|
||
/* Reset all indicators by default — only the chosen style draws */
|
||
.workspace-tab-header.is-active::before {
|
||
background-color: transparent;
|
||
}
|
||
|
||
/* Top border (default) */
|
||
body.rbr-tab-top .workspace-tab-header.is-active::before {
|
||
background-color: var(--rbr-accent);
|
||
top: 0;
|
||
bottom: auto;
|
||
height: 2px;
|
||
}
|
||
|
||
/* Bottom border */
|
||
body.rbr-tab-bottom .workspace-tab-header.is-active::before {
|
||
background-color: var(--rbr-accent);
|
||
top: auto;
|
||
bottom: 0;
|
||
height: 2px;
|
||
}
|
||
|
||
/* Background fill — soft accent-tinted fill, no border bar */
|
||
body.rbr-tab-fill .workspace-tab-header.is-active {
|
||
background-color: rgba(var(--rbr-accent-rgb), 0.18);
|
||
}
|
||
body.rbr-tab-fill .workspace-tab-header.is-active::before {
|
||
background-color: transparent;
|
||
}
|
||
|
||
/* ========================================================================
|
||
Scrollbars
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--scrollbar-bg: transparent;
|
||
--scrollbar-thumb-bg: var(--rbr-surface-2);
|
||
--scrollbar-active-thumb-bg: var(--rbr-accent);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Search highlights — alpha follows --rbr-highlight-alpha slider
|
||
======================================================================== */
|
||
.search-result-file-matched-text,
|
||
.search-result-file-matched-text-highlighted {
|
||
background-color: rgba(var(--rbr-rb-yellow-rgb), var(--rbr-highlight-alpha, 0.30)) !important;
|
||
color: var(--rbr-rb-yellow) !important;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.cm-s-obsidian span.obsidian-search-match-highlight {
|
||
background-color: rgba(var(--rbr-rb-yellow-rgb), var(--rbr-highlight-alpha, 0.30));
|
||
border-bottom: 1px solid var(--rbr-rb-yellow);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Graph view — node colors map to file states
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--graph-line: var(--rbr-overlay-0);
|
||
--graph-node: var(--rbr-text);
|
||
--graph-node-tag: var(--rbr-rb-yellow);
|
||
--graph-node-attachment: var(--rbr-paddock-green);
|
||
--graph-node-unresolved: var(--rbr-kerb-red-bright);
|
||
--graph-node-focused: var(--rbr-accent);
|
||
--graph-text: var(--rbr-text);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Callouts — semantic colors lock to RBR diagnostics, ignore accent choice
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--callout-info: var(--rbr-oracle-blue-rgb);
|
||
--callout-tip: var(--rbr-paddock-green-rgb);
|
||
--callout-success: var(--rbr-paddock-green-rgb);
|
||
--callout-question: 130, 160, 216;
|
||
--callout-warning: var(--rbr-rb-yellow-rgb);
|
||
--callout-todo: var(--rbr-rb-yellow-rgb);
|
||
--callout-bug: var(--rbr-kerb-bright-rgb);
|
||
--callout-fail: var(--rbr-kerb-bright-rgb);
|
||
--callout-error: var(--rbr-kerb-bright-rgb);
|
||
--callout-example: var(--rbr-paddock-green-rgb);
|
||
--callout-quote: 133, 144, 174;
|
||
--callout-abstract: var(--rbr-oracle-blue-rgb);
|
||
--callout-note: var(--rbr-oracle-blue-rgb);
|
||
--callout-important: var(--rbr-kerb-red-rgb);
|
||
--callout-cite: 133, 144, 174;
|
||
}
|
||
|
||
/* ========================================================================
|
||
File explorer — current file gets an active-accent bar on the left
|
||
======================================================================== */
|
||
.nav-file-title.is-active,
|
||
.nav-folder-title.is-active {
|
||
color: var(--rbr-text);
|
||
background-color: var(--rbr-surface-1);
|
||
position: relative;
|
||
}
|
||
|
||
.nav-file-title.is-active::before,
|
||
.nav-folder-title.is-active::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 2px;
|
||
background-color: var(--rbr-accent);
|
||
}
|
||
|
||
.nav-file-title.has-focus,
|
||
.nav-folder-title.has-focus {
|
||
background-color: var(--rbr-surface-2);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Mobile — keep the trio working on small screens
|
||
======================================================================== */
|
||
.is-mobile {
|
||
--background-primary: var(--rbr-base);
|
||
--background-secondary: var(--rbr-mantle);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Small refinements — focus ring, drag handle, slider, vault name
|
||
======================================================================== */
|
||
.theme-dark,
|
||
.theme-light {
|
||
--slider-thumb-border-active: var(--rbr-accent);
|
||
--slider-track-background: var(--rbr-surface-1);
|
||
--slider-thumb-background: var(--rbr-text);
|
||
--toggle-thumb-color: var(--rbr-text);
|
||
--toggle-border-color: var(--rbr-overlay-1);
|
||
--toggle-bg-color: var(--rbr-surface-1);
|
||
--toggle-on-bg-color: var(--rbr-accent);
|
||
--toggle-on-thumb-color: var(--rbr-base);
|
||
|
||
--vault-name-color: var(--rbr-rb-yellow);
|
||
--vault-name-font-weight: 700;
|
||
}
|
||
|
||
/* Active line — STYLE-GUIDE: surface1 (dimmed navy, one step above base).
|
||
Quiet "you are here" without yelling. Cover both class orderings + the
|
||
CM6 single-class form, plus the matching gutter line. */
|
||
.cm-line.cm-active,
|
||
.cm-active.cm-line,
|
||
.cm-activeLine {
|
||
background-color: var(--rbr-surface-1) !important;
|
||
}
|
||
|
||
.cm-activeLineGutter,
|
||
.cm-gutters .cm-activeLineGutter {
|
||
background-color: var(--rbr-surface-1) !important;
|
||
color: var(--rbr-text);
|
||
}
|
||
|
||
/* Selected text — alpha-blended cursor color so vim visual mode +
|
||
regular drag-select share the same accent. 30% alpha keeps the
|
||
characters under the selection legible. Reading-view text selection
|
||
(--text-selection) stays on the active accent — that path doesn't
|
||
go through CM6 and shouldn't share the cursor color. */
|
||
.cm-s-obsidian .cm-selectionBackground,
|
||
.cm-s-obsidian .CodeMirror-selected,
|
||
.cm-editor.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground {
|
||
background-color: rgba(var(--rbr-cursor-rgb), 0.30) !important;
|
||
}
|
||
|
||
/* When the editor isn't focused, dim the selection a bit more so it doesn't
|
||
compete visually with the active pane */
|
||
.cm-editor:not(.cm-focused) .cm-selectionBackground {
|
||
background-color: rgba(var(--rbr-cursor-rgb), 0.18) !important;
|
||
}
|
||
|
||
/* Matching brackets — follow active accent */
|
||
.cm-s-obsidian .CodeMirror-matchingbracket {
|
||
color: var(--rbr-accent) !important;
|
||
border-bottom: 1px solid var(--rbr-accent);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Calendar / properties panel / inline title
|
||
======================================================================== */
|
||
.inline-title {
|
||
color: var(--rbr-accent);
|
||
font-weight: 800;
|
||
}
|
||
|
||
.metadata-property-key-input {
|
||
color: var(--rbr-rb-yellow);
|
||
}
|
||
|
||
.metadata-property-value {
|
||
color: var(--rbr-text);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Auto hide — Style Settings class-toggles. Reduce UI chrome to a sliver
|
||
and reveal on hover. Mobile is excluded since it has no hover affordance.
|
||
|
||
Each toggle is independent — combine freely (e.g. ribbon + statusbar).
|
||
|
||
Perf notes:
|
||
- All durations route through --rbr-autohide-duration so users can dial
|
||
them down (default 100ms). Animating height/width is unavoidable here
|
||
because the surrounding layout must reflow — but a shorter window
|
||
shrinks the cost proportionally.
|
||
- `contain: layout` is applied to each region (see the rule directly
|
||
below) so reflow stays inside the region and doesn't bubble up to
|
||
the workspace tree on every mousemove or typing-time DOM mutation.
|
||
- `will-change` is applied **only on :hover**, not always-on. Permanent
|
||
will-change keeps a composite layer allocated forever, which makes
|
||
the compositor redo work on every paint — including paints triggered
|
||
by CodeMirror DOM updates while typing. By gating it behind :hover
|
||
we trade a tiny first-frame layer-allocation cost for typing-time
|
||
compositor calm.
|
||
- The ribbon keeps the longer Material curve for a heftier slide — its
|
||
width swing is the largest of the four regions, so a snappier
|
||
duration would look abrupt. Other regions get the linear ease-out.
|
||
======================================================================== */
|
||
|
||
/* Reflow isolation. `contain: layout` walls off the auto-hide region's
|
||
reflow so it doesn't propagate up through the workspace tree on every
|
||
mousemove or DOM mutation. `style` is intentionally NOT included — it
|
||
can interfere with custom-property inheritance (notably --ribbon-width
|
||
via @property), and the layout containment alone is what we want.
|
||
|
||
`will-change` is NOT applied here. Always-on `will-change` keeps a
|
||
composite layer permanently allocated, which forces the compositor to
|
||
redo layer work on every paint — including paints driven by
|
||
CodeMirror's typing-time DOM mutations. We instead promote the layer
|
||
only when a hover is actually in flight (see the :hover rules per
|
||
region) so the GPU layer exists only while it's needed. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container,
|
||
body:not(.is-mobile).rbr-autohide-statusbar .status-bar,
|
||
body:not(.is-mobile).rbr-autohide-ribbon .workspace-ribbon.side-dock-ribbon.mod-left,
|
||
body:not(.is-mobile).rbr-autohide-vault-profile .workspace-sidedock-vault-profile {
|
||
contain: layout;
|
||
}
|
||
|
||
/* will-change is set only when the region is actually about to animate
|
||
(i.e. the user has hovered it). Setting will-change at :hover allocates
|
||
the composite layer just-in-time; when hover ends, the property is
|
||
removed and the layer is released. This keeps typing fast because the
|
||
editor pane no longer competes with always-promoted sibling layers. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container:hover {
|
||
will-change: height;
|
||
}
|
||
body:not(.is-mobile).rbr-autohide-statusbar .status-bar:hover {
|
||
will-change: height;
|
||
}
|
||
body:not(.is-mobile).rbr-autohide-ribbon .workspace-ribbon.side-dock-ribbon.mod-left:hover {
|
||
/* The animated property is --ribbon-width, not width — hint that one
|
||
so the value is right. (Layout-driving props get limited compositor
|
||
benefit either way, but the hint should at least name what moves.) */
|
||
will-change: --ribbon-width;
|
||
}
|
||
body:not(.is-mobile).rbr-autohide-vault-profile .workspace-split.mod-left-split:hover .workspace-sidedock-vault-profile,
|
||
body:not(.is-mobile).rbr-autohide-vault-profile .workspace-sidedock-vault-profile:hover {
|
||
will-change: height;
|
||
}
|
||
|
||
/* ---- Tab bar ----
|
||
Collapse every tab header strip (main + left/right sidebars) into a
|
||
slim 16px bar. Following Obsidian-Border's design: the strip itself
|
||
is the hover target — no pseudo-element hit zones, no overflow
|
||
overrides, no pointer-events juggling. Simpler = smoother.
|
||
|
||
Trade-off: the hover target is exactly the 16px strip (plus the
|
||
titlebar via the secondary trigger below). No "generous band"
|
||
above/below. In exchange, transitions feel snappy because the
|
||
browser isn't recomputing hit-testing over giant viewport-wide
|
||
pseudo-element overlays on every frame.
|
||
|
||
Each strip expands independently on its own :hover (this used to be
|
||
a synchronized expand via `:has()`, but measurement showed the
|
||
`:has()` selectors caused thousands of editor nodes to re-style on
|
||
every keystroke — see the expand rule below for the full rationale).
|
||
|
||
The faint accent hairline at the bottom of the collapsed strip is
|
||
a visible "aim here" cue (otherwise a 16px transparent area is
|
||
easy to miss). */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container {
|
||
position: relative;
|
||
/* z-index lifts the strip (and its ::after hit-zone band) above the
|
||
tab CONTENT that sits below it in the SAME .workspace-tabs.mod-top.
|
||
The mod-top z-index:10 below only raises the whole strip+content
|
||
group over OTHER splits — it does nothing for the header-vs-content
|
||
order inside this group, where the content renders later in DOM and
|
||
would otherwise win the hit-test over the ::after band (measured:
|
||
the band was 100% covered without this). Verified against real
|
||
app.css: content leaves are z-auto, so 2 clears them. */
|
||
z-index: 2;
|
||
height: 12px;
|
||
min-height: 12px;
|
||
overflow: visible;
|
||
border-bottom: 1px solid rgba(var(--rbr-accent-rgb), 0.35);
|
||
/* Only transition height + min-height. border-color is left to
|
||
change instantly (the hairline disappears on expand) — eliminating
|
||
it from the transition list shaves work off every animation frame. */
|
||
transition: height var(--rbr-autohide-duration, 80ms) ease-out,
|
||
min-height var(--rbr-autohide-duration, 80ms) ease-out;
|
||
transition-delay: 0ms; /* collapse direction: snap back immediately */
|
||
}
|
||
|
||
/* Invisible hover-extension above the strip — the strip itself is only
|
||
12px visible, but the ::before adds an invisible band above so the
|
||
effective hit zone is taller. Height is controlled via
|
||
--rbr-autohide-hitzone (Style Settings slider) so it's dial-able
|
||
per-user — bigger zone = easier to aim, smaller = fewer accidental
|
||
expands. Default 12px → total 24px effective hit zone.
|
||
|
||
left/right -100vw is kept for vertical-stack visual continuity, but
|
||
the lateral extension is now clipped at the panel boundary (we no
|
||
longer set overflow:visible on .workspace-split — that was expanding
|
||
the editor's paint damage rect). The band still meets the titlebar at
|
||
the top, which is the primary hover target.
|
||
|
||
No z-index, no pointer-events tricks, no dynamic resize — just one
|
||
thin static band per strip. Each strip's :hover triggers only that
|
||
strip's expand (synchronized expand was dropped — see expand rule). */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container::before {
|
||
content: "";
|
||
position: absolute;
|
||
/* Bounded to the panel's own width — `-100vw` extension was removed
|
||
after a cross-panel bug: with side panel splits now overflow:visible
|
||
for the vertical band, `-100vw` lateral extension let the right
|
||
sidebar's pseudo reach the left ribbon area, so hovering near the
|
||
ribbon was firing the right sidebar's :hover. The viewport-wide
|
||
top hit zone is owned by the titlebar pseudo (see below); each
|
||
strip's own pseudo stays within its panel. */
|
||
left: 0;
|
||
right: 0;
|
||
top: calc(-1 * var(--rbr-autohide-hitzone, 12px));
|
||
height: var(--rbr-autohide-hitzone, 12px);
|
||
}
|
||
|
||
/* `.workspace-tabs.mod-top` (the strip's direct parent) is opened so the
|
||
strip's ::before can extend vertically above it.
|
||
|
||
Side panel splits (mod-left-split, mod-right-split) ALSO get opened
|
||
so the side panel strip's ::before extends laterally beyond the
|
||
panel boundary — this brings the side panel hit zone in line with
|
||
the main editor's. The main editor split (.mod-root) is intentionally
|
||
NOT opened: it's the editor's ancestor, and `overflow: visible` there
|
||
would expand CodeMirror's paint damage rect on every keystroke. The
|
||
main editor's wide top hit zone is handled by the titlebar pseudo
|
||
below instead, which lives outside .mod-root entirely. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tabs.mod-top,
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-split.mod-left-split,
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-split.mod-right-split {
|
||
overflow: visible;
|
||
}
|
||
|
||
/* Raise the strip's wrapper above the content area's stacking. The
|
||
strip will get a ::after pseudo that extends DOWN into the content
|
||
area (file explorer / editor / etc.), and that pseudo needs to be
|
||
on top of the content for hover hit-testing to fire. Without this,
|
||
the content (rendered later in DOM order) wins the hit-test and the
|
||
pseudo's hover never triggers. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tabs.mod-top {
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
/* Below-strip hit zone — locked to the EXPANDED strip height
|
||
(--header-height) rather than the small hitzone slider, so the band
|
||
covers exactly the area the tab buttons (Files/Search/Bookmarks/...)
|
||
will occupy once expanded. Hover the spot the strip is about to fill
|
||
and it opens into that spot — the same "hit zone == expanded size"
|
||
behaviour the ribbon uses.
|
||
|
||
Trade: clicks inside this band are captured by the strip, not the
|
||
content beneath (file in file explorer, top line of editor). The band
|
||
is only as deep as the expanded header, which is exactly the region a
|
||
user reaching for tab buttons expects to be interactive anyway. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container::after {
|
||
content: "";
|
||
position: absolute;
|
||
/* See the ::before rule above for why we bound to panel width
|
||
(`left: 0; right: 0`) instead of `-100vw`. */
|
||
left: 0;
|
||
right: 0;
|
||
top: 100%;
|
||
height: var(--header-height, 40px);
|
||
}
|
||
|
||
/* ---- Window-wide top hit zone (titlebar-anchored) ----
|
||
|
||
The strips themselves are clipped by their respective workspace
|
||
splits. We get a generous full-width top hit zone by adding an
|
||
invisible band on the titlebar — .titlebar sits OUTSIDE all the
|
||
workspace splits, at the very top of the window, so its pseudo
|
||
isn't clipped by any of them.
|
||
|
||
How it works:
|
||
1. .titlebar gets position: relative + overflow: visible so its
|
||
::after can extend downward past its own bounds.
|
||
2. The ::after is an absolutely-positioned band immediately below
|
||
the titlebar, full window width, height controlled by the same
|
||
--rbr-autohide-hitzone slider that controls the per-strip zones.
|
||
3. The pseudo is part of the titlebar element for hit-testing, so
|
||
hovering it triggers .titlebar:hover.
|
||
4. The .titlebar:hover ~ * .workspace-tab-header-container rule
|
||
uses a general-sibling combinator to expand all three strips
|
||
(main + left/right sidebars) at once. Sibling combinators don't
|
||
have the body-level :has() invalidation cost we removed earlier,
|
||
so this synchronized expand is perf-safe.
|
||
|
||
Trade: clicks inside the pseudo band are captured by the titlebar
|
||
instead of falling through to whatever's underneath. With a
|
||
moderate hit zone height (12–40px) this is invisible in practice
|
||
since the area is the titlebar/workspace top. At 100px the band
|
||
reaches deep into the workspace and click capture becomes
|
||
noticeable — dial accordingly. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .titlebar {
|
||
position: relative;
|
||
overflow: visible;
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-tabs .titlebar::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 0;
|
||
right: 0;
|
||
height: var(--rbr-autohide-hitzone, 12px);
|
||
z-index: 100;
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-tabs .titlebar:hover ~ * .workspace-tab-header-container {
|
||
height: var(--header-height, 40px);
|
||
min-height: var(--header-height, 40px);
|
||
border-bottom-color: transparent;
|
||
transition-delay: var(--rbr-autohide-delay, 100ms);
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-tabs .titlebar:hover ~ * .workspace-tab-header-container > * {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transition: opacity var(--rbr-autohide-duration, 80ms) ease-out calc(var(--rbr-autohide-delay, 100ms) + 30ms);
|
||
}
|
||
|
||
/* Inner button row fades. pointer-events:none is needed because
|
||
overflow:visible lets the (full-height) buttons render past the
|
||
12px clip line — without it, hovers on those overflowing buttons
|
||
would fire phantom expands. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container > * {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity var(--rbr-autohide-duration, 80ms) ease-out 0ms;
|
||
}
|
||
|
||
/* Expand on hover — each strip responds to its OWN :hover state. The
|
||
"synchronized expand" behavior (hover any one strip → all three open)
|
||
was implemented with `:has()` selectors, but measurement showed those
|
||
selectors caused the browser to re-evaluate style for thousands of
|
||
editor DOM nodes on every CodeMirror keystroke. The trade is real but
|
||
clear: drop synchronization (each tab strip expands alone), keep
|
||
typing fast. This is the same behavior Border ships with — the design
|
||
we're inspired by — so the trade is well-precedented.
|
||
|
||
The transition-delay is the hover-intent gate: when the cursor first
|
||
enters the hover state, transitions wait --rbr-autohide-delay before
|
||
kicking in. If the cursor leaves before that window elapses, the
|
||
transitions never start — no flicker from drive-by hovers. On exit,
|
||
the default rules above apply (0ms delay), so collapse happens
|
||
immediately. */
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container:hover {
|
||
height: var(--header-height, 40px);
|
||
min-height: var(--header-height, 40px);
|
||
border-bottom-color: transparent;
|
||
transition-delay: var(--rbr-autohide-delay, 100ms);
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-tabs .workspace-tab-header-container:hover > * {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transition: opacity var(--rbr-autohide-duration, 80ms) ease-out calc(var(--rbr-autohide-delay, 100ms) + 30ms);
|
||
}
|
||
|
||
/* ---- Status bar ----
|
||
Collapse to a 4px sliver; reveal on hover. The hover hit zone is a
|
||
::before band ABOVE the sliver, exactly as tall as the expanded bar
|
||
(--rbr-statusbar-expanded), so you aim at the space the bar will fill
|
||
— the same "hit zone == expanded size" pattern as the ribbon/tab bar.
|
||
|
||
overflow is visible (not hidden) so the ::before can escape the 4px
|
||
box; the collapsed items are kept from stealing clicks via
|
||
pointer-events:none below. opacity:0 hides the sliver visually while
|
||
still letting the pseudo hit-test (opacity doesn't disable hit-test).
|
||
The bar is z-index:var(--layer-status-bar)==15 by default, above the
|
||
editor content (0), so the band is never covered. */
|
||
body:not(.is-mobile).rbr-autohide-statusbar .status-bar {
|
||
height: 4px;
|
||
opacity: 0;
|
||
overflow: visible;
|
||
transition: height var(--rbr-autohide-duration, 80ms) ease-out,
|
||
opacity var(--rbr-autohide-duration, 80ms) ease-out;
|
||
transition-delay: 0ms;
|
||
}
|
||
|
||
/* Collapsed items must not capture clicks in the sliver / overflow. */
|
||
body:not(.is-mobile).rbr-autohide-statusbar .status-bar > * {
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Hover hit zone: band above the sliver, height == expanded bar. */
|
||
body:not(.is-mobile).rbr-autohide-statusbar .status-bar::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
/* Sit directly ABOVE the bar regardless of its collapsed height:
|
||
top:0 is the bar's top edge, translateY(-100%) lifts the band up by
|
||
its own height. Anchoring to bottom:0 failed because the collapsed
|
||
bar isn't a predictable 4px, so the band stayed buried inside it. */
|
||
top: 0;
|
||
transform: translateY(-100%);
|
||
height: var(--rbr-statusbar-expanded, 24px);
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-statusbar .status-bar:hover {
|
||
height: var(--rbr-statusbar-expanded, 24px);
|
||
opacity: 1;
|
||
transition-delay: var(--rbr-autohide-delay, 100ms);
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-statusbar .status-bar:hover > * {
|
||
pointer-events: auto;
|
||
}
|
||
|
||
/* ---- Left ribbon ----
|
||
Drive ribbon width via the --ribbon-width CSS variable so Obsidian's
|
||
flex layout reflows alongside (a direct `width:` would shrink the
|
||
ribbon visually but leave the column reserved).
|
||
|
||
For smooth motion we register --ribbon-width as a typed @property.
|
||
By default custom properties aren't transitionable — declaring the
|
||
syntax as <length> tells the engine to interpolate it numerically,
|
||
so changing the value over a `transition` produces a fluid slide
|
||
instead of an instant snap.
|
||
|
||
Easing: cubic-bezier(0.4, 0, 0.2, 1) is the Material "standard" curve
|
||
— slow start, snappy middle, gentle landing. Feels natural for this
|
||
kind of UI panel. Icons fade in slightly delayed so the ribbon has
|
||
already started widening when they appear. */
|
||
/* Critical: inherits is FALSE. A typed custom property registered with
|
||
`inherits: true` participates in the cascade for every descendant of
|
||
the element it's set on. Setting it on body meant CodeMirror's 1000+
|
||
.cm-line nodes paid a per-node cascade cost on every style recalc —
|
||
which CodeMirror triggers constantly during typing. With `inherits:
|
||
false`, the property is scoped to the element it's declared on
|
||
(ribbon), and the editor subtree no longer sees it during recalc. */
|
||
@property --ribbon-width {
|
||
syntax: '<length>';
|
||
inherits: false;
|
||
initial-value: 44px;
|
||
}
|
||
|
||
/* --ribbon-width is declared directly on the ribbon element (not body)
|
||
so the transition runs on the element that actually consumes the
|
||
value. background-color is dropped from the transition list; it had
|
||
no visible payoff and was compositing on every frame. Ribbon slides
|
||
38px (6→44) so we scale the duration up by 1.6× — keeps the curve
|
||
readable while smaller-motion regions stay snappy. */
|
||
body:not(.is-mobile).rbr-autohide-ribbon .workspace-ribbon.side-dock-ribbon.mod-left {
|
||
--ribbon-width: 6px;
|
||
/* Expanded ribbon width lives in --rbr-ribbon-expanded (Style Settings
|
||
slider, fallback 44px). It is NOT declared here on purpose: declaring
|
||
it on this element would out-specify the body-level value Style
|
||
Settings injects, so the slider would do nothing. We only READ it
|
||
(via var()) in the :hover width and the ::before hit-zone below.
|
||
Because both read the same variable, the hover zone always matches
|
||
EXACTLY the space the expanded ribbon fills — hover the spot the
|
||
ribbon is about to occupy and it expands into that exact spot. */
|
||
/* position: relative anchors the ::before hit-zone overlay below.
|
||
z-index must sit ONE ABOVE the side panels. Obsidian's ribbon AND
|
||
its side docks both default to z-index:var(--layer-sidedock) (==10).
|
||
When the left sidebar is open, its split shares that layer, and
|
||
since it renders LATER in DOM order it wins the hover hit-test over
|
||
our ::before band — collapsing the effective zone to the ribbon's
|
||
own ~6px (the "only a thin sliver is hoverable" bug). A plain
|
||
`z-index:10` did nothing because it equalled the default. Bumping
|
||
to sidedock+1 lifts the ribbon (and its band) just above the side
|
||
panel while staying below status-bar(15)/popover(30)/menu(65)/
|
||
modal(50)/tooltip(70), so those still layer over the ribbon.
|
||
Verified against the real Obsidian app.css + workspace DOM. */
|
||
position: relative;
|
||
z-index: calc(var(--layer-sidedock, 10) + 1);
|
||
/* overflow: visible lets the ::before extend past the ribbon's own
|
||
6px width into the editor area on the right. */
|
||
overflow: visible;
|
||
/* ONLY animate --ribbon-width. Obsidian derives both width and
|
||
flex-basis from it (width: var(--ribbon-width); flex: 0 0 var(...)),
|
||
so animating the variable slides everything in lock-step. The old
|
||
rule ALSO transitioned `width` and `min-width`: that made width lag
|
||
behind the (non-transitioned) flex-basis, so the two moved on
|
||
different curves — a subtle jitter — while width had no visual
|
||
effect anyway (flex-basis wins sizing under flex:0 0). Dropping them
|
||
removes the double-interpolation and the wasted per-frame work. */
|
||
transition: --ribbon-width calc(var(--rbr-autohide-duration, 80ms) * 1.6) cubic-bezier(0.4, 0, 0.2, 1);
|
||
transition-delay: 0ms;
|
||
}
|
||
|
||
/* Invisible hover-extension to the right of the ribbon. Its width is
|
||
locked to --rbr-ribbon-expanded — i.e. the hover zone is exactly as
|
||
wide as the ribbon becomes when expanded. Hovering the area the ribbon
|
||
will fill triggers the expand, and the ribbon then lands right there.
|
||
|
||
z-index below the ribbon's icons (they get z-index:2, see the `> *`
|
||
rule) but above the neighbour panel — so the band overlays the
|
||
editor/sidebar for hit-testing yet never swallows icon clicks on the
|
||
expanded ribbon.
|
||
|
||
Trade: clicks in the band's overflow (the part past the 6px collapsed
|
||
ribbon, over the editor/sidebar) go to the ribbon, not that panel. */
|
||
body:not(.is-mobile).rbr-autohide-ribbon .workspace-ribbon.side-dock-ribbon.mod-left::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: var(--rbr-ribbon-expanded, 44px);
|
||
z-index: 1;
|
||
}
|
||
|
||
/* On hover, expand to --rbr-ribbon-expanded (same variable the hit-zone
|
||
uses) so icons fit and the ribbon fills exactly the hovered zone.
|
||
The hover-intent delay below means a drive-by hover doesn't trigger
|
||
the expand — only intentional dwelling does. */
|
||
body:not(.is-mobile).rbr-autohide-ribbon .workspace-ribbon.side-dock-ribbon.mod-left:hover {
|
||
--ribbon-width: var(--rbr-ribbon-expanded, 44px);
|
||
transition-delay: var(--rbr-autohide-delay, 100ms);
|
||
}
|
||
|
||
/* position:relative + z-index:2 stacks the ribbon's real children (icons,
|
||
collapse button) ABOVE the ::before hit-zone band. Without this a wide
|
||
band would paint over the expanded icons and steal their clicks. With
|
||
it, the band is purely a hover catcher in the empty gutter and the
|
||
icons stay fully clickable no matter how far the band extends. */
|
||
body:not(.is-mobile).rbr-autohide-ribbon .workspace-ribbon.side-dock-ribbon.mod-left > * {
|
||
position: relative;
|
||
z-index: 2;
|
||
opacity: 0;
|
||
transition: opacity calc(var(--rbr-autohide-duration, 80ms) * 1.2) ease-out 0ms;
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-ribbon .workspace-ribbon.side-dock-ribbon.mod-left:hover > * {
|
||
opacity: 1;
|
||
transition: opacity calc(var(--rbr-autohide-duration, 80ms) * 1.2) ease-out calc(var(--rbr-autohide-delay, 100ms) + 80ms);
|
||
}
|
||
|
||
/* ---- Vault profile ----
|
||
Hide the vault-name footer in the left sidebar. Reveal when the sidebar
|
||
pane itself is hovered (the bar is too thin to land on directly). */
|
||
body:not(.is-mobile).rbr-autohide-vault-profile .workspace-sidedock-vault-profile {
|
||
height: 0;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
padding-top: 0;
|
||
padding-bottom: 0;
|
||
/* padding isn't in the transition list — it's clipped by overflow:hidden
|
||
until height opens up, so an instant padding snap is invisible. */
|
||
transition: height var(--rbr-autohide-duration, 80ms) ease-out,
|
||
opacity var(--rbr-autohide-duration, 80ms) ease-out;
|
||
transition-delay: 0ms;
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-autohide-vault-profile .workspace-split.mod-left-split:hover .workspace-sidedock-vault-profile,
|
||
body:not(.is-mobile).rbr-autohide-vault-profile .workspace-sidedock-vault-profile:hover {
|
||
height: auto;
|
||
opacity: 1;
|
||
padding-top: var(--size-4-2, 8px);
|
||
padding-bottom: var(--size-4-2, 8px);
|
||
transition-delay: var(--rbr-autohide-delay, 100ms);
|
||
}
|
||
|
||
/* ========================================================================
|
||
Card layout — give every workspace pane card-like separation:
|
||
gaps between splits, rounded corners, and a soft shadow on each tab
|
||
container. The shadow layers a 1px border on top of a subtle drop
|
||
shadow for depth without harshness.
|
||
|
||
Sliders (--rbr-card-radius, --rbr-card-gap) drive the metrics so users
|
||
can dial it from "barely there" to "tile UI" without editing CSS.
|
||
======================================================================== */
|
||
|
||
/* Gap between vertically stacked splits (panes side-by-side) */
|
||
body:not(.is-mobile).rbr-card-layout .workspace-split.mod-vertical {
|
||
gap: var(--rbr-card-gap, 12px);
|
||
}
|
||
|
||
/* Sidedock margins so cards don't touch the ribbon or right sidebar */
|
||
body:not(.is-mobile).rbr-card-layout .workspace.is-left-sidedock-open .workspace-split.mod-horizontal.mod-left-split {
|
||
margin-right: var(--rbr-card-gap, 12px);
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-card-layout .workspace.is-right-sidedock-open .workspace-split.mod-horizontal.mod-right-split {
|
||
margin-left: var(--rbr-card-gap, 12px);
|
||
}
|
||
|
||
/* The card itself — rounded + shadowed. overflow:hidden clips content
|
||
to the radius; the parent .workspace-tabs / .workspace-split must stay
|
||
overflow:visible so the drop shadow isn't cut off. */
|
||
body:not(.is-mobile).rbr-card-layout .workspace-tab-container {
|
||
border-radius: var(--rbr-card-radius, 8px) !important;
|
||
overflow: hidden !important;
|
||
box-shadow:
|
||
0 0 0 1px var(--rbr-surface-1),
|
||
0 4px 14px rgba(0, 0, 0, 0.28);
|
||
}
|
||
|
||
body:not(.is-mobile).rbr-card-layout .workspace-tabs,
|
||
body:not(.is-mobile).rbr-card-layout .workspace-split {
|
||
overflow: visible !important;
|
||
}
|
||
|
||
/* Tab strip blends into the canvas instead of looking like a chrome strip
|
||
on top of the card */
|
||
body:not(.is-mobile).rbr-card-layout .workspace-tabs,
|
||
body:not(.is-mobile).rbr-card-layout .workspace-tab-header-container {
|
||
background-color: transparent !important;
|
||
}
|
||
|
||
/* The default ::before drop-shadow line under the title bar is meant for
|
||
the flat layout — hide it once cards take over that role */
|
||
body:not(.is-mobile).rbr-card-layout .workspace::before {
|
||
display: none;
|
||
}
|
||
|
||
/* Active tab card — the active leaf gets a hint of accent on the border
|
||
so it's still distinguishable when every pane has a card outline */
|
||
body:not(.is-mobile).rbr-card-layout .workspace-tabs.mod-active .workspace-tab-container {
|
||
box-shadow:
|
||
0 0 0 1px rgba(var(--rbr-accent-rgb), 0.45),
|
||
0 4px 14px rgba(0, 0, 0, 0.32);
|
||
}
|