@domternal/theme 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/dist/domternal-theme.css +1 -1
- package/dist/domternal-theme.expanded.css +1437 -99
- package/package.json +1 -1
- package/src/_base.scss +2 -2
- package/src/_block-colors.scss +178 -0
- package/src/_block-handle.scss +189 -0
- package/src/_bubble-menu.scss +5 -2
- package/src/_color-palette.scss +3 -3
- package/src/_content.scss +51 -1
- package/src/_context-menu.scss +141 -0
- package/src/_details.scss +5 -5
- package/src/_floating-menu.scss +145 -8
- package/src/_image.scss +6 -6
- package/src/_inline-colors.scss +204 -0
- package/src/_invisible-chars.scss +2 -2
- package/src/_link-popover.scss +2 -2
- package/src/_notion-mode.scss +72 -0
- package/src/_prosemirror.scss +1 -1
- package/src/_slash-command.scss +174 -0
- package/src/_table-controls.scss +3 -3
- package/src/_task-list.scss +51 -12
- package/src/_toc.scss +450 -0
- package/src/_toolbar.scss +5 -5
- package/src/_variables.scss +191 -0
- package/src/index.scss +21 -4
- package/src/themes/_dark.scss +118 -3
- package/src/themes/_light.scss +1 -1
package/package.json
CHANGED
package/src/_base.scss
CHANGED
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
// Shared floating element fade-in animation
|
|
49
49
|
// =============================================================================
|
|
50
50
|
// Used by elements conditionally inserted into the DOM (emoji picker,
|
|
51
|
-
// suggestion dropdown, toolbar dropdowns). Uses scale instead of translateY
|
|
51
|
+
// suggestion dropdown, toolbar dropdowns). Uses scale instead of translateY:
|
|
52
52
|
// scale doesn't shift position so it won't conflict with floating-ui.
|
|
53
53
|
// Elements using data-show (bubble menu, floating menu, popovers) use CSS
|
|
54
|
-
// opacity transitions instead
|
|
54
|
+
// opacity transitions instead - see their respective stylesheets.
|
|
55
55
|
// =============================================================================
|
|
56
56
|
|
|
57
57
|
@keyframes dm-fade-in {
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Block Colors
|
|
3
|
+
// Notion-style block-level backgrounds and text colors. Applied when the
|
|
4
|
+
// BlockColor extension (@domternal/core) sets `data-bg-color` /
|
|
5
|
+
// `data-text-color` attributes on block elements. Token definitions live
|
|
6
|
+
// in `_variables.scss` (light) and `themes/_dark.scss` (dark).
|
|
7
|
+
// =============================================================================
|
|
8
|
+
|
|
9
|
+
// -----------------------------------------------------------------------------
|
|
10
|
+
// Background
|
|
11
|
+
// Tint the whole block. Scoped under `.dm-editor .ProseMirror` so it only
|
|
12
|
+
// applies inside a Domternal editor instance - won't leak to host content.
|
|
13
|
+
// Small inline padding so the tint visually "hugs" the text instead of
|
|
14
|
+
// bleeding edge-to-edge on narrow blocks.
|
|
15
|
+
// -----------------------------------------------------------------------------
|
|
16
|
+
.dm-editor .ProseMirror {
|
|
17
|
+
[data-bg-color="gray"] { background-color: var(--dm-block-bg-gray); }
|
|
18
|
+
[data-bg-color="brown"] { background-color: var(--dm-block-bg-brown); }
|
|
19
|
+
[data-bg-color="orange"] { background-color: var(--dm-block-bg-orange); }
|
|
20
|
+
[data-bg-color="yellow"] { background-color: var(--dm-block-bg-yellow); }
|
|
21
|
+
[data-bg-color="green"] { background-color: var(--dm-block-bg-green); }
|
|
22
|
+
[data-bg-color="blue"] { background-color: var(--dm-block-bg-blue); }
|
|
23
|
+
[data-bg-color="purple"] { background-color: var(--dm-block-bg-purple); }
|
|
24
|
+
[data-bg-color="pink"] { background-color: var(--dm-block-bg-pink); }
|
|
25
|
+
[data-bg-color="red"] { background-color: var(--dm-block-bg-red); }
|
|
26
|
+
|
|
27
|
+
// Inline padding is pre-applied to colorable blocks in `_content.scss`
|
|
28
|
+
// (and to the editor itself for lists/blockquotes) so toggling a color
|
|
29
|
+
// does NOT shift text - the tint paints into existing padding. This
|
|
30
|
+
// rule is therefore minimal: only the rounded corners + a hair of
|
|
31
|
+
// vertical padding so the tint reads as a "pill" around short text
|
|
32
|
+
// instead of an edge-to-edge band.
|
|
33
|
+
[data-bg-color] {
|
|
34
|
+
padding-block: 0.125rem;
|
|
35
|
+
border-radius: 0.25rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[data-text-color="gray"] { color: var(--dm-block-text-gray); }
|
|
39
|
+
[data-text-color="brown"] { color: var(--dm-block-text-brown); }
|
|
40
|
+
[data-text-color="orange"] { color: var(--dm-block-text-orange); }
|
|
41
|
+
[data-text-color="yellow"] { color: var(--dm-block-text-yellow); }
|
|
42
|
+
[data-text-color="green"] { color: var(--dm-block-text-green); }
|
|
43
|
+
[data-text-color="blue"] { color: var(--dm-block-text-blue); }
|
|
44
|
+
[data-text-color="purple"] { color: var(--dm-block-text-purple); }
|
|
45
|
+
[data-text-color="pink"] { color: var(--dm-block-text-pink); }
|
|
46
|
+
[data-text-color="red"] { color: var(--dm-block-text-red); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// -----------------------------------------------------------------------------
|
|
50
|
+
// Swatch row - horizontal strip of color swatches, one per row in the
|
|
51
|
+
// BlockContextMenu Colors section (one for text, one for background).
|
|
52
|
+
// -----------------------------------------------------------------------------
|
|
53
|
+
.dm-block-color-row {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-wrap: wrap;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 0.25rem;
|
|
58
|
+
padding: 0.25rem 0.5rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.dm-block-color-row-label {
|
|
62
|
+
// Visually hidden but readable by screen readers. The visible row label
|
|
63
|
+
// comes from `.dm-block-context-menu-group-label` ("Colors") above.
|
|
64
|
+
position: absolute;
|
|
65
|
+
width: 1px;
|
|
66
|
+
height: 1px;
|
|
67
|
+
padding: 0;
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
clip: rect(0, 0, 0, 0);
|
|
70
|
+
white-space: nowrap;
|
|
71
|
+
border: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// -----------------------------------------------------------------------------
|
|
75
|
+
// Color swatches - used by BlockContextMenu's color picker and available
|
|
76
|
+
// for host apps that render their own pickers. One ring per palette entry
|
|
77
|
+
// plus a neutral "clear" swatch.
|
|
78
|
+
// -----------------------------------------------------------------------------
|
|
79
|
+
.dm-block-color-swatch {
|
|
80
|
+
display: inline-flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
width: 1.5rem;
|
|
84
|
+
height: 1.5rem;
|
|
85
|
+
padding: 0;
|
|
86
|
+
border: 1px solid var(--dm-border-color, #e5e7eb);
|
|
87
|
+
border-radius: 50%;
|
|
88
|
+
background: transparent;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
transition: transform 0.08s ease, box-shadow 0.08s ease;
|
|
91
|
+
|
|
92
|
+
&:hover {
|
|
93
|
+
transform: scale(1.1);
|
|
94
|
+
box-shadow: 0 0 0 2px var(--dm-accent-surface, rgba(37, 99, 235, 0.15));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
&:focus-visible,
|
|
98
|
+
// Currently-applied color for the targeted block. BlockContextMenu writes
|
|
99
|
+
// `aria-pressed="true"` on the swatch whose value matches the block's
|
|
100
|
+
// current text/bg color (`null` for "Default"); mirror the inline picker's
|
|
101
|
+
// `.dm-ncp-active` treatment so the active state reads the same in both UIs.
|
|
102
|
+
&[aria-pressed="true"] {
|
|
103
|
+
// OUTSET ring (offset > 0): swatch dot fills almost the entire button
|
|
104
|
+
// surface, so an inset outline would render UNDER the dot. Pulling the
|
|
105
|
+
// ring outward keeps it visible against any palette tint.
|
|
106
|
+
outline: 2px solid var(--dm-accent, #2563eb);
|
|
107
|
+
outline-offset: 1px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Inner tint dot - used for both bg and text variants so rows look uniform.
|
|
111
|
+
&::before {
|
|
112
|
+
content: '';
|
|
113
|
+
display: block;
|
|
114
|
+
width: 1rem;
|
|
115
|
+
height: 1rem;
|
|
116
|
+
border-radius: 50%;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// "No color" swatch - strike-through slash rendered via gradient.
|
|
120
|
+
&[data-color="null"]::before {
|
|
121
|
+
background:
|
|
122
|
+
linear-gradient(
|
|
123
|
+
to top right,
|
|
124
|
+
transparent calc(50% - 1px),
|
|
125
|
+
var(--dm-muted, #999) calc(50% - 1px),
|
|
126
|
+
var(--dm-muted, #999) calc(50% + 1px),
|
|
127
|
+
transparent calc(50% + 1px)
|
|
128
|
+
);
|
|
129
|
+
border: 1px solid var(--dm-border-color, #e5e7eb);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Background swatches render the tint as the dot interior.
|
|
134
|
+
.dm-block-color-swatch--bg {
|
|
135
|
+
&[data-color="gray"]::before { background: var(--dm-block-bg-gray); }
|
|
136
|
+
&[data-color="brown"]::before { background: var(--dm-block-bg-brown); }
|
|
137
|
+
&[data-color="orange"]::before { background: var(--dm-block-bg-orange); }
|
|
138
|
+
&[data-color="yellow"]::before { background: var(--dm-block-bg-yellow); }
|
|
139
|
+
&[data-color="green"]::before { background: var(--dm-block-bg-green); }
|
|
140
|
+
&[data-color="blue"]::before { background: var(--dm-block-bg-blue); }
|
|
141
|
+
&[data-color="purple"]::before { background: var(--dm-block-bg-purple); }
|
|
142
|
+
&[data-color="pink"]::before { background: var(--dm-block-bg-pink); }
|
|
143
|
+
&[data-color="red"]::before { background: var(--dm-block-bg-red); }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Text swatches render a capital "A" in the named color on a neutral dot
|
|
147
|
+
// background, mirroring Notion's convention so users can tell at a glance
|
|
148
|
+
// which row sets text vs background. The "A" comes from the `content`
|
|
149
|
+
// pseudo-element; the dot background stays neutral (var(--dm-surface)) so
|
|
150
|
+
// the colored glyph is readable regardless of palette tint.
|
|
151
|
+
.dm-block-color-swatch--text {
|
|
152
|
+
&::before {
|
|
153
|
+
content: 'A';
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
font-family: var(--dm-editor-font-family, system-ui, sans-serif);
|
|
158
|
+
font-size: 0.75rem;
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
background: var(--dm-surface, #f8f9fa);
|
|
161
|
+
color: var(--dm-editor-text, #333);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// "Default" text swatch keeps its "A" and gets the diagonal slash from the
|
|
165
|
+
// base `[data-color="null"]::before` rule painted as the dot's background.
|
|
166
|
+
// The "A" glyph sits on top of the slash so users see "letterform crossed
|
|
167
|
+
// out" rather than an empty stroke (mirrors the inline-picker treatment).
|
|
168
|
+
|
|
169
|
+
&[data-color="gray"]::before { color: var(--dm-block-text-gray); }
|
|
170
|
+
&[data-color="brown"]::before { color: var(--dm-block-text-brown); }
|
|
171
|
+
&[data-color="orange"]::before { color: var(--dm-block-text-orange); }
|
|
172
|
+
&[data-color="yellow"]::before { color: var(--dm-block-text-yellow); }
|
|
173
|
+
&[data-color="green"]::before { color: var(--dm-block-text-green); }
|
|
174
|
+
&[data-color="blue"]::before { color: var(--dm-block-text-blue); }
|
|
175
|
+
&[data-color="purple"]::before { color: var(--dm-block-text-purple); }
|
|
176
|
+
&[data-color="pink"]::before { color: var(--dm-block-text-pink); }
|
|
177
|
+
&[data-color="red"]::before { color: var(--dm-block-text-red); }
|
|
178
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Block Handle
|
|
3
|
+
// Left-gutter handle (⋮⋮ drag + + insert) rendered on hover over each
|
|
4
|
+
// top-level block when BlockHandle extension is enabled.
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
// When the extension mounts it adds `dm-editor--has-block-handle` to the
|
|
8
|
+
// editor wrapper. Two things happen:
|
|
9
|
+
//
|
|
10
|
+
// 1) ProseMirror reserves gutter space with `padding-left` so text does
|
|
11
|
+
// not flow under the handle.
|
|
12
|
+
// 2) The editor opens its horizontal overflow so the handle - positioned
|
|
13
|
+
// with a negative `left` - can render in the page margin rather than
|
|
14
|
+
// eating into the content column.
|
|
15
|
+
//
|
|
16
|
+
// Gutter default is 4rem (64px). Handle is 8px OUTSIDE the editor's
|
|
17
|
+
// left edge (see `.dm-block-handle { left: -0.5rem }`) and ~46px wide,
|
|
18
|
+
// so it ends ~38px inside the editor; the remaining ~26px of gutter is
|
|
19
|
+
// breathing room before text, matching Notion's feel.
|
|
20
|
+
.dm-editor--has-block-handle {
|
|
21
|
+
overflow: visible;
|
|
22
|
+
|
|
23
|
+
.ProseMirror {
|
|
24
|
+
padding-left: var(--dm-block-handle-gutter, 4rem);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Selected-node visual indicator: the base theme's `outline: 2px solid`
|
|
29
|
+
// (see `_prosemirror.scss`) paints OUTSIDE the block's border-box and
|
|
30
|
+
// bleeds over anything adjacent - including the `.notion-page` right
|
|
31
|
+
// border in the Notion demo, making it look like the border is missing.
|
|
32
|
+
// When BlockHandle is active, suppress the outline and replace it with a
|
|
33
|
+
// subtle `::before` halo that sits BEHIND the block content (`z-index: -1`)
|
|
34
|
+
// and therefore never overlaps surrounding chrome.
|
|
35
|
+
.dm-editor--has-block-handle .ProseMirror-selectednode,
|
|
36
|
+
.dm-editor--has-block-handle .ProseMirror-selectednoderange {
|
|
37
|
+
position: relative;
|
|
38
|
+
outline: none;
|
|
39
|
+
|
|
40
|
+
&::before {
|
|
41
|
+
content: '';
|
|
42
|
+
position: absolute;
|
|
43
|
+
inset: -0.25rem;
|
|
44
|
+
background-color: var(--dm-block-selected-halo, rgba(112, 207, 248, 0.25));
|
|
45
|
+
border-radius: 0.25rem;
|
|
46
|
+
pointer-events: none;
|
|
47
|
+
z-index: -1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.dm-block-handle {
|
|
52
|
+
position: absolute;
|
|
53
|
+
// Tokenised so demos with a centered narrower content column can move
|
|
54
|
+
// the handle further out (e.g., `--dm-block-handle-left: -3.5rem`) to
|
|
55
|
+
// place it fully in the surrounding whitespace. Default `-0.5rem` keeps
|
|
56
|
+
// the handle just outside the editor's left border for full-width
|
|
57
|
+
// editors with the standard `--dm-block-handle-gutter` reservation on
|
|
58
|
+
// ProseMirror. Requires `overflow: visible` (set above on
|
|
59
|
+
// `.dm-editor--has-block-handle`) so it isn't clipped.
|
|
60
|
+
left: var(--dm-block-handle-left, -0.5rem);
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
gap: 2px;
|
|
64
|
+
padding: 2px;
|
|
65
|
+
opacity: 0;
|
|
66
|
+
visibility: hidden;
|
|
67
|
+
pointer-events: none;
|
|
68
|
+
transition: opacity 0.12s ease, visibility 0.12s;
|
|
69
|
+
z-index: var(--dm-z-handle, 25);
|
|
70
|
+
|
|
71
|
+
&[data-show] {
|
|
72
|
+
opacity: 1;
|
|
73
|
+
visibility: visible;
|
|
74
|
+
pointer-events: auto;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Individual buttons (⋮⋮ drag, + insert)
|
|
78
|
+
&-btn {
|
|
79
|
+
display: inline-flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
width: 1.25rem;
|
|
83
|
+
height: 1.5rem;
|
|
84
|
+
padding: 0;
|
|
85
|
+
border: none;
|
|
86
|
+
background: transparent;
|
|
87
|
+
color: var(--dm-muted, #999);
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
border-radius: 3px;
|
|
90
|
+
transition: background-color 0.1s, color 0.1s;
|
|
91
|
+
// Defensive: stop text selection / touch pan from hijacking the
|
|
92
|
+
// pointer during mousedown-to-drag. Not strictly required on
|
|
93
|
+
// Chrome/Firefox but avoids edge cases on Safari + touch devices.
|
|
94
|
+
user-select: none;
|
|
95
|
+
-webkit-user-select: none;
|
|
96
|
+
touch-action: none;
|
|
97
|
+
|
|
98
|
+
&:hover {
|
|
99
|
+
background: var(--dm-button-hover-bg, rgba(0, 0, 0, 0.04));
|
|
100
|
+
color: var(--dm-editor-text, inherit);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&:focus-visible {
|
|
104
|
+
// INSET ring (-1px): block handle buttons sit in the gutter; an outset
|
|
105
|
+
// ring would extend into adjacent content / next sibling block.
|
|
106
|
+
outline: 2px solid var(--dm-accent, #2563eb);
|
|
107
|
+
outline-offset: -1px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
svg {
|
|
111
|
+
width: 1rem;
|
|
112
|
+
height: 1rem;
|
|
113
|
+
pointer-events: none;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Drag handle - grab cursor + slight nudge active state
|
|
118
|
+
&-drag {
|
|
119
|
+
cursor: grab;
|
|
120
|
+
|
|
121
|
+
&:active {
|
|
122
|
+
cursor: grabbing;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Insert (+) button - subtle accent on hover
|
|
127
|
+
&-plus {
|
|
128
|
+
&:hover {
|
|
129
|
+
color: var(--dm-accent, #2563eb);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// =============================================================================
|
|
135
|
+
// Drop indicator
|
|
136
|
+
// Custom horizontal drop line drawn during drag-from-handle. Positioned
|
|
137
|
+
// absolutely inside `.dm-editor` (which is `position: relative`) by JS,
|
|
138
|
+
// only made visible via the `data-show` attribute. Spans the resolved
|
|
139
|
+
// drop-target block's width so the user sees exactly which block (and
|
|
140
|
+
// which side) the drop will land at - including the difference between
|
|
141
|
+
// "into the list" and "as a sibling block" outcomes when dragging in
|
|
142
|
+
// the gap between a list and the next block.
|
|
143
|
+
// =============================================================================
|
|
144
|
+
.dm-block-drop-indicator {
|
|
145
|
+
position: absolute;
|
|
146
|
+
height: 2px;
|
|
147
|
+
background: var(--dm-accent, #2563eb);
|
|
148
|
+
border-radius: 1px;
|
|
149
|
+
pointer-events: none;
|
|
150
|
+
z-index: 5;
|
|
151
|
+
// Hidden by default; JS adds `data-show` to reveal.
|
|
152
|
+
display: none;
|
|
153
|
+
// Slight glow so the line reads on top of any background colour.
|
|
154
|
+
box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.15);
|
|
155
|
+
// Nudge the line a few pixels DOWN from the upper block's bottom edge so
|
|
156
|
+
// it visually sits in the gap between blocks rather than glued to the
|
|
157
|
+
// block above. Visual-only (transform doesn't shift layout) so the
|
|
158
|
+
// computed `top` from JS still reflects the true anchor - the indicator
|
|
159
|
+
// and the actual drop position stay in sync.
|
|
160
|
+
transform: translateY(3px);
|
|
161
|
+
// Smooth glide when the indicator moves between targets or flips
|
|
162
|
+
// between sibling and nested mode mid-drag. Only position properties
|
|
163
|
+
// animate; the categorical style swap (filled bar ↔ dashed border)
|
|
164
|
+
// intentionally stays instant to keep the mode boundary readable.
|
|
165
|
+
// Short duration so the line never feels laggy on fast drags.
|
|
166
|
+
transition: left 80ms ease-out, width 80ms ease-out, top 80ms ease-out, transform 80ms ease-out;
|
|
167
|
+
|
|
168
|
+
&[data-show] {
|
|
169
|
+
display: block;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Nested-mode visual: dashed line indented to the children-zone start
|
|
173
|
+
// of the target list item. Communicates "this drop becomes a nested
|
|
174
|
+
// child appended at the end" rather than "a sibling between blocks".
|
|
175
|
+
// JS positions `top` at the rect's BOTTOM edge and `left` indented by
|
|
176
|
+
// 24px (mirrors `--dm-block-children-indent`), so the dashed line
|
|
177
|
+
// visually sits where a child block would render inside the item.
|
|
178
|
+
&[data-mode='nested'] {
|
|
179
|
+
height: 0;
|
|
180
|
+
background: transparent;
|
|
181
|
+
border-radius: 0;
|
|
182
|
+
border-top: 2px dashed var(--dm-accent, #2563eb);
|
|
183
|
+
box-shadow: none;
|
|
184
|
+
// Lift slightly so the line sits INSIDE the listItem's bottom edge
|
|
185
|
+
// rather than in the gap below - cancels the global +3px nudge and
|
|
186
|
+
// pulls the line 2px above rect.bottom.
|
|
187
|
+
transform: translateY(-2px);
|
|
188
|
+
}
|
|
189
|
+
}
|
package/src/_bubble-menu.scss
CHANGED
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
--dm-button-active-color: var(--dm-accent, #2563eb);
|
|
15
15
|
--dm-button-disabled-opacity: 0.4;
|
|
16
16
|
|
|
17
|
-
// Smaller icons to match compact buttons
|
|
18
|
-
.
|
|
17
|
+
// Smaller icons to match compact buttons. `:not(.dm-dropdown-caret)`
|
|
18
|
+
// preserves the dropdown caret's own 0.625rem sizing from `_toolbar.scss`
|
|
19
|
+
// - without this exclusion the bubble menu's selector wins on specificity
|
|
20
|
+
// and the caret renders the same size as a normal toolbar icon.
|
|
21
|
+
.dm-toolbar-button svg:not(.dm-dropdown-caret) {
|
|
19
22
|
width: 1rem;
|
|
20
23
|
height: 1rem;
|
|
21
24
|
}
|
package/src/_color-palette.scss
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
width: max-content;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// "Default" reset button
|
|
14
|
+
// "Default" reset button - spans the full width of the grid
|
|
15
15
|
.dm-color-palette-reset {
|
|
16
16
|
grid-column: 1 / -1;
|
|
17
17
|
display: flex;
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
0 0 0 3px var(--dm-accent, #2563eb);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
// Active state
|
|
73
|
+
// Active state - checkmark indicator
|
|
74
74
|
&--active {
|
|
75
75
|
box-shadow: 0 0 0 2px var(--dm-toolbar-bg, #f8f9fa),
|
|
76
76
|
0 0 0 3px var(--dm-accent, #2563eb);
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
// Dark theme
|
|
89
|
+
// Dark theme - adjust swatch border for dark backgrounds
|
|
90
90
|
.dm-theme-dark .dm-color-swatch,
|
|
91
91
|
.dm-theme-auto .dm-color-swatch {
|
|
92
92
|
border-color: rgba(255, 255, 255, 0.15);
|
package/src/_content.scss
CHANGED
|
@@ -6,6 +6,19 @@
|
|
|
6
6
|
// =============================================================================
|
|
7
7
|
|
|
8
8
|
.dm-editor .ProseMirror {
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Block inline padding
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Pre-applied so that toggling `data-bg-color` on a block does NOT shift
|
|
13
|
+
// text - the colored background paints into existing padding instead of
|
|
14
|
+
// adding new padding (`_block-colors.scss` only sets `background-color`
|
|
15
|
+
// + `border-radius`, no padding). Mirrors Notion's layout. Wrapped in
|
|
16
|
+
// `:where()` so specificity stays at 0 and host apps can override per
|
|
17
|
+
// tag without selector wars.
|
|
18
|
+
:where(p, h1, h2, h3, h4, h5, h6) {
|
|
19
|
+
padding-inline: var(--dm-block-inline-padding);
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
// ---------------------------------------------------------------------------
|
|
10
23
|
// Headings
|
|
11
24
|
// ---------------------------------------------------------------------------
|
|
@@ -78,7 +91,11 @@
|
|
|
78
91
|
border-left: var(--dm-blockquote-border);
|
|
79
92
|
color: var(--dm-blockquote-color);
|
|
80
93
|
margin: 0.5em 0;
|
|
81
|
-
padding
|
|
94
|
+
// Right padding picks up the same `--dm-block-inline-padding` token
|
|
95
|
+
// so colored blockquotes get breathing room from the right edge.
|
|
96
|
+
// Left stays at 0.8em - that gap is for the border-left bar, not
|
|
97
|
+
// text inset.
|
|
98
|
+
padding: 0.1em var(--dm-block-inline-padding) 0.1em 0.8em;
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
// ---------------------------------------------------------------------------
|
|
@@ -89,12 +106,45 @@
|
|
|
89
106
|
ol {
|
|
90
107
|
margin: 0.75em 0;
|
|
91
108
|
padding-left: 1.5em;
|
|
109
|
+
// Right padding so colored lists get breathing room on the right edge,
|
|
110
|
+
// matching the inline padding paragraphs and headings get above.
|
|
111
|
+
padding-right: var(--dm-block-inline-padding);
|
|
112
|
+
}
|
|
92
113
|
|
|
114
|
+
// Notion-style "children zone" indent for plain bullet/ordered lists ONLY.
|
|
115
|
+
// Scoped to `ul:not([data-type="taskList"]), ol` so it does NOT match the
|
|
116
|
+
// taskList wrapper - taskItems render via `<li><label/><div/></li>` and the
|
|
117
|
+
// generic rule would mistakenly apply margin-inline-start to that wrapper
|
|
118
|
+
// div, pushing the entire taskItem content (and the absolutely-positioned
|
|
119
|
+
// checkbox's containing block) one indent step to the right. Task list
|
|
120
|
+
// children-zone styling lives in `_task-list.scss` and is rooted on the
|
|
121
|
+
// inner `<div>` instead.
|
|
122
|
+
ul:not([data-type="taskList"]),
|
|
123
|
+
ol {
|
|
93
124
|
li {
|
|
94
125
|
margin: 0.25em 0;
|
|
95
126
|
|
|
96
127
|
> p {
|
|
128
|
+
// List item paragraphs reset inline-end padding (bullet indentation
|
|
129
|
+
// is owned by the list container; doubling up shifts text under the
|
|
130
|
+
// bullet's hanging indent), and add a small inline-start padding
|
|
131
|
+
// (~5px at the editor's default font size) so the text breathes
|
|
132
|
+
// away from the bullet glyph instead of butting against it.
|
|
97
133
|
margin: 0.1em 0;
|
|
134
|
+
padding-inline: 0.3em 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Post-label blocks (heading, codeBlock, blockquote, etc.) sit
|
|
138
|
+
// indented one level below the label paragraph aligned with the
|
|
139
|
+
// bullet. ALL nested `<ul>` and `<ol>` (plain and taskList) are
|
|
140
|
+
// excluded because each already gains the same visual indent
|
|
141
|
+
// through its own `padding-left: 1.5em`, so adding this margin
|
|
142
|
+
// would push nested taskLists further right than nested plain
|
|
143
|
+
// bullet lists. `margin-inline-start` flips the indent to the
|
|
144
|
+
// right edge in RTL.
|
|
145
|
+
> :not(p:first-child):not(ul):not(ol) {
|
|
146
|
+
margin-inline-start: var(--dm-block-children-indent, 1.5rem);
|
|
147
|
+
margin-top: 0.25rem;
|
|
98
148
|
}
|
|
99
149
|
}
|
|
100
150
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Block Context Menu
|
|
3
|
+
// Compact popup shown when the user clicks the ⋮⋮ BlockHandle drag button
|
|
4
|
+
// without dragging. Offers Delete / Duplicate / Turn into options for the
|
|
5
|
+
// targeted top-level block.
|
|
6
|
+
// =============================================================================
|
|
7
|
+
|
|
8
|
+
.dm-block-context-menu {
|
|
9
|
+
position: absolute;
|
|
10
|
+
display: none;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
min-width: 12rem;
|
|
13
|
+
max-height: 20rem;
|
|
14
|
+
overflow-y: auto;
|
|
15
|
+
// Stop wheel-delta chaining to the page once the menu hits its
|
|
16
|
+
// own scroll edges. The plugin's JS gate handles the no-overflow
|
|
17
|
+
// case where this property doesn't apply.
|
|
18
|
+
overscroll-behavior: contain;
|
|
19
|
+
padding: 0.25rem;
|
|
20
|
+
background: var(--dm-toolbar-bg, var(--dm-bg, #ffffff));
|
|
21
|
+
border: 1px solid var(--dm-border-color, #e5e7eb);
|
|
22
|
+
border-radius: var(--dm-toolbar-border-radius, 0.5rem);
|
|
23
|
+
box-shadow: var(--dm-popover-shadow,
|
|
24
|
+
0 10px 25px rgba(0, 0, 0, 0.08),
|
|
25
|
+
0 4px 10px rgba(0, 0, 0, 0.04));
|
|
26
|
+
z-index: var(--dm-z-popover, 50);
|
|
27
|
+
|
|
28
|
+
&[data-show] {
|
|
29
|
+
display: flex;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Slim scrollbar (matches slash-command + floating-menu).
|
|
33
|
+
scrollbar-width: thin;
|
|
34
|
+
scrollbar-color: var(--dm-scrollbar-thumb, rgba(0, 0, 0, 0.18)) transparent;
|
|
35
|
+
|
|
36
|
+
&::-webkit-scrollbar {
|
|
37
|
+
width: 6px;
|
|
38
|
+
}
|
|
39
|
+
&::-webkit-scrollbar-track {
|
|
40
|
+
background: transparent;
|
|
41
|
+
}
|
|
42
|
+
&::-webkit-scrollbar-thumb {
|
|
43
|
+
background: var(--dm-scrollbar-thumb, rgba(0, 0, 0, 0.18));
|
|
44
|
+
border-radius: 3px;
|
|
45
|
+
}
|
|
46
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
47
|
+
background: var(--dm-scrollbar-thumb-hover, rgba(0, 0, 0, 0.28));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&-group {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: 1px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Label doubles as the visual separator above its group.
|
|
57
|
+
// `.dm-block-context-menu-group-label` is only emitted by the plugin for
|
|
58
|
+
// the "Turn into" section, so it naturally appears above the second
|
|
59
|
+
// group. Using the label as the divider (instead of styling the group
|
|
60
|
+
// itself) avoids the "double border" case where a labeled group follows
|
|
61
|
+
// another labeled group.
|
|
62
|
+
&-group-label {
|
|
63
|
+
padding: 0.5rem 0.5rem 0.125rem;
|
|
64
|
+
margin-top: 0.25rem;
|
|
65
|
+
border-top: 1px solid var(--dm-border-color, #e5e7eb);
|
|
66
|
+
font-size: 0.6875rem;
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
letter-spacing: 0.04em;
|
|
69
|
+
text-transform: uppercase;
|
|
70
|
+
color: var(--dm-muted, #999);
|
|
71
|
+
user-select: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&-item {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: 0.5rem;
|
|
78
|
+
width: 100%;
|
|
79
|
+
padding: 0.3125rem 0.5rem;
|
|
80
|
+
border: none;
|
|
81
|
+
border-radius: var(--dm-button-border-radius, 0.375rem);
|
|
82
|
+
background: transparent;
|
|
83
|
+
color: var(--dm-editor-text, inherit);
|
|
84
|
+
text-align: left;
|
|
85
|
+
font: inherit;
|
|
86
|
+
font-size: 0.875rem;
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
transition: background-color 0.1s;
|
|
89
|
+
|
|
90
|
+
&:hover,
|
|
91
|
+
&:focus-visible {
|
|
92
|
+
background: var(--dm-button-hover-bg, rgba(0, 0, 0, 0.04));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&:focus-visible {
|
|
96
|
+
// INSET ring (-2px): menu items are tightly packed; an outset ring
|
|
97
|
+
// would overlap the neighbour. -2px keeps the ring fully inside the
|
|
98
|
+
// hover background fill.
|
|
99
|
+
outline: 2px solid var(--dm-accent, #2563eb);
|
|
100
|
+
outline-offset: -2px;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
&-item-icon {
|
|
105
|
+
display: inline-flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
width: 1.125rem;
|
|
109
|
+
height: 1.125rem;
|
|
110
|
+
flex-shrink: 0;
|
|
111
|
+
color: var(--dm-muted, #999);
|
|
112
|
+
|
|
113
|
+
svg {
|
|
114
|
+
width: 1rem;
|
|
115
|
+
height: 1rem;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&-item-label {
|
|
120
|
+
flex: 1;
|
|
121
|
+
min-width: 0;
|
|
122
|
+
white-space: nowrap;
|
|
123
|
+
overflow: hidden;
|
|
124
|
+
text-overflow: ellipsis;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// =============================================================================
|
|
129
|
+
// Active block highlight
|
|
130
|
+
// Class applied via PM Decoration on the block whose BlockContextMenu is open.
|
|
131
|
+
// =============================================================================
|
|
132
|
+
|
|
133
|
+
.ProseMirror .dm-block-context-active {
|
|
134
|
+
background-color: var(--dm-block-context-active-bg, rgba(55, 53, 47, 0.06));
|
|
135
|
+
border-radius: 0.25rem;
|
|
136
|
+
// box-shadow expands the tint past the block's content box without
|
|
137
|
+
// affecting layout (negative margins would shift neighbours).
|
|
138
|
+
box-shadow: 0 0 0 4px var(--dm-block-context-active-bg, rgba(55, 53, 47, 0.06));
|
|
139
|
+
transition: background-color 0.12s ease, box-shadow 0.12s ease;
|
|
140
|
+
}
|
|
141
|
+
|