@domternal/theme 0.6.2 → 0.7.1

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.
@@ -1,9 +1,10 @@
1
1
  // =============================================================================
2
2
  // Link Popover Styles
3
3
  // =============================================================================
4
- // The popover is appended to document.body (to escape overflow:hidden on
5
- // .dm-editor), so it can't inherit CSS variables from .dm-editor / .dm-toolbar.
6
- // We hardcode fallback values that match the light theme defaults.
4
+ // The popover is appended to document.body to escape overflow:hidden on
5
+ // .dm-editor. The popover JS copies the editor's `dm-theme-*` class onto
6
+ // the popover so the dark-tokens cascade applies on the element itself.
7
+ // All colours read from `--dm-*` tokens; no hardcoded theme values.
7
8
  // =============================================================================
8
9
 
9
10
  .dm-link-popover {
@@ -12,10 +13,10 @@
12
13
  align-items: center;
13
14
  gap: 0.25rem;
14
15
  padding: 0.25rem;
15
- background: #f8f9fa;
16
- border: 1px solid #e0e0e0;
16
+ background: var(--dm-surface);
17
+ border: 1px solid var(--dm-border-color);
17
18
  border-radius: 0.25rem;
18
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
19
+ box-shadow: var(--dm-popover-shadow, 0 4px 12px rgba(0, 0, 0, 0.12));
19
20
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
20
21
  Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
21
22
  visibility: hidden;
@@ -30,18 +31,18 @@
30
31
  }
31
32
 
32
33
  .dm-link-popover-input {
33
- border: 1px solid #e0e0e0;
34
+ border: 1px solid var(--dm-border-color);
34
35
  border-radius: 0.25rem;
35
36
  padding: 0.25rem 0.5rem;
36
37
  font-size: 0.8125rem;
37
38
  font-family: inherit;
38
39
  min-width: 14rem;
39
40
  outline: none;
40
- background: #fff;
41
- color: #1a1a1a;
41
+ background: var(--dm-bg);
42
+ color: var(--dm-text);
42
43
 
43
44
  &:focus {
44
- border-color: #2563eb;
45
+ border-color: var(--dm-accent);
45
46
  }
46
47
  }
47
48
 
@@ -55,16 +56,16 @@
55
56
  border: none;
56
57
  border-radius: 0.25rem;
57
58
  background: transparent;
58
- color: #1a1a1a;
59
+ color: var(--dm-text);
59
60
  cursor: pointer;
60
61
  transition: background-color 0.15s, color 0.15s;
61
62
 
62
63
  &:hover {
63
- background: rgba(0, 0, 0, 0.06);
64
+ background: var(--dm-hover);
64
65
  }
65
66
 
66
67
  &:focus-visible {
67
- outline: 2px solid var(--dm-accent, #2563eb);
68
+ outline: 2px solid var(--dm-accent);
68
69
  outline-offset: 1px;
69
70
  }
70
71
 
@@ -75,75 +76,18 @@
75
76
  }
76
77
 
77
78
  .dm-link-popover-apply:hover {
78
- color: #2563eb;
79
+ color: var(--dm-accent);
79
80
  }
80
81
 
82
+ // `--dm-danger` is not a semantic token yet, hardcode for now and bump
83
+ // when the design system introduces one.
81
84
  .dm-link-popover-remove:hover {
82
85
  color: #dc2626;
83
86
  }
84
87
 
85
- // Pending-link decoration highlights the text range while the popover
88
+ // Pending-link decoration - highlights the text range while the popover
86
89
  // input has focus (browser hides native selection on editor blur).
87
90
  .dm-link-pending {
88
- background-color: rgba(37, 99, 235, 0.12);
91
+ background-color: color-mix(in srgb, var(--dm-accent) 12%, transparent);
89
92
  border-radius: 1px;
90
93
  }
91
-
92
- // Dark theme override
93
- [data-theme="dark"] .dm-link-popover,
94
- .dm-theme-dark .dm-link-popover {
95
- background: #2a2a2a;
96
- border-color: #404040;
97
-
98
- .dm-link-popover-input {
99
- background: #1a1a1a;
100
- border-color: #404040;
101
- color: #e0e0e0;
102
-
103
- &:focus {
104
- border-color: #3b82f6;
105
- }
106
- }
107
-
108
- .dm-link-popover-btn {
109
- color: #e0e0e0;
110
-
111
- &:hover {
112
- background: rgba(255, 255, 255, 0.1);
113
- }
114
- }
115
-
116
- .dm-link-popover-apply:hover {
117
- color: #3b82f6;
118
- }
119
- }
120
-
121
- // Auto dark mode — follows system preference
122
- @media (prefers-color-scheme: dark) {
123
- .dm-theme-auto .dm-link-popover {
124
- background: #2a2a2a;
125
- border-color: #404040;
126
-
127
- .dm-link-popover-input {
128
- background: #1a1a1a;
129
- border-color: #404040;
130
- color: #e0e0e0;
131
-
132
- &:focus {
133
- border-color: #3b82f6;
134
- }
135
- }
136
-
137
- .dm-link-popover-btn {
138
- color: #e0e0e0;
139
-
140
- &:hover {
141
- background: rgba(255, 255, 255, 0.1);
142
- }
143
- }
144
-
145
- .dm-link-popover-apply:hover {
146
- color: #3b82f6;
147
- }
148
- }
149
- }
@@ -0,0 +1,82 @@
1
+ // =============================================================================
2
+ // Notion Mode
3
+ // Opt-in stylistic preset: borderless surface, centered narrow column,
4
+ // generous reading rhythm, BlockHandle in the surrounding gutter.
5
+ // Apply with class `dm-notion-mode` on the editor host element.
6
+ // =============================================================================
7
+
8
+ .dm-editor.dm-notion-mode {
9
+ // Drop the default chrome - Notion pages have no card outline.
10
+ border: none;
11
+ border-radius: 0;
12
+ box-shadow: none;
13
+ background: transparent;
14
+
15
+ // Centered, comfortably narrow reading column. The surrounding margin
16
+ // is where the BlockHandle sits (see token overrides below). Consumers
17
+ // are expected to control the surrounding container width; on viewports
18
+ // narrower than 38rem the column overflows by design (Notion mode is
19
+ // opt-in).
20
+ max-width: 38rem;
21
+ margin: 0 auto;
22
+
23
+ // Generous reading line-height; padding zeroed because the page wrapper
24
+ // supplies the white space around the column. Body font-size inherits
25
+ // classic 1rem so toggling between modes doesn't shift text size.
26
+ --dm-editor-line-height: 1.7;
27
+ --dm-editor-padding: 0;
28
+
29
+ // Handle lives fully OUTSIDE the centered content column. Zeroing the
30
+ // ProseMirror gutter token reclaims the reserved column (the handle no
31
+ // longer overlaps content - there is nothing to reserve). The negative
32
+ // `left` token pulls the handle left of the column with a few px of
33
+ // breathing room before the content's left edge.
34
+ --dm-block-handle-gutter: 0;
35
+ --dm-block-handle-left: -3.5rem;
36
+
37
+ // The `top` checkbox tuning in `_task-list.scss` is for the default
38
+ // 1.6 line-height. Notion mode lifts line-height to 1.7, so nudge the
39
+ // checkbox down to keep it on the x-height of the first label line.
40
+ --dm-task-checkbox-top: 0.45em;
41
+
42
+ .ProseMirror {
43
+ min-height: 60vh;
44
+ outline: none;
45
+
46
+ // Tighter heading rhythm than the default content stylesheet.
47
+ // Sizes inherit classic (browser defaults / `_content.scss`) so the
48
+ // mode toggle shifts layout, not text scale.
49
+ h1 {
50
+ font-weight: 700;
51
+ margin-top: 1.5rem;
52
+ margin-bottom: 0.75rem;
53
+ }
54
+ h2 {
55
+ font-weight: 600;
56
+ margin-top: 1.5rem;
57
+ margin-bottom: 0.5rem;
58
+ }
59
+ h3 {
60
+ font-weight: 600;
61
+ margin-top: 1.25rem;
62
+ margin-bottom: 0.375rem;
63
+ }
64
+ p {
65
+ margin: 0.25rem 0;
66
+ }
67
+
68
+ // Paragraphs inside table cells stay flush; otherwise the higher
69
+ // specificity of the rule above adds 4px top/bottom and visibly puffs
70
+ // up cells compared to classic mode.
71
+ td > p,
72
+ th > p {
73
+ margin: 0;
74
+ }
75
+ }
76
+
77
+ // Slash-command placeholders wrap in narrow table cells; the cell is the editable unit.
78
+ .ProseMirror td .is-empty::before,
79
+ .ProseMirror th .is-empty::before {
80
+ content: none;
81
+ }
82
+ }
@@ -166,7 +166,7 @@ img.ProseMirror-separator {
166
166
  }
167
167
  }
168
168
 
169
- // Hide native text selection inside selected cells the ::after overlay is sufficient
169
+ // Hide native text selection inside selected cells - the ::after overlay is sufficient
170
170
  .dm-editor .selectedCell,
171
171
  .dm-editor .selectedCell * {
172
172
  &::selection {
@@ -0,0 +1,174 @@
1
+ // =============================================================================
2
+ // Slash Command Menu
3
+ // Popup shown when the user types `/`. Rendered by
4
+ // `createSlashSuggestionRenderer()` and positioned at the cursor.
5
+ // Visual vocabulary mirrors the floating menu for consistency.
6
+ //
7
+ // Class names follow the renderer's output verbatim:
8
+ // - `dm-slash-command-menu` (outer)
9
+ // - `dm-slash-command-group` / `-group-label`
10
+ // - `dm-slash-command-item` / `-item-icon` / `-item-text` / `-item-label`
11
+ // / `-item-description` / `-item-shortcut`
12
+ // - `dm-slash-command-empty`
13
+ // - `dm-slash-command-query` (inline decoration on the active `/query`)
14
+ //
15
+ // We intentionally do NOT nest the inner elements under `.dm-slash-command-menu`
16
+ // (a previous version did so via SCSS `&-item` concatenation, which produced
17
+ // `.dm-slash-command-menu-item-*` selectors that the renderer never emitted -
18
+ // items rendered without size constraints, ballooning the menu).
19
+ // =============================================================================
20
+
21
+ .dm-slash-command-menu {
22
+ position: absolute;
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 0.25rem;
26
+ min-width: 17rem;
27
+ max-height: 22rem;
28
+ overflow-y: auto;
29
+ padding: 0.375rem;
30
+ background: var(--dm-toolbar-bg, var(--dm-bg, #ffffff));
31
+ border: 1px solid var(--dm-border-color, #e5e7eb);
32
+ border-radius: var(--dm-toolbar-border-radius, 0.5rem);
33
+ box-shadow: var(--dm-popover-shadow,
34
+ 0 10px 25px rgba(0, 0, 0, 0.08),
35
+ 0 4px 10px rgba(0, 0, 0, 0.04));
36
+ z-index: var(--dm-z-popover, 50);
37
+
38
+ // Slim custom scrollbar - matches the floating-menu styling.
39
+ scrollbar-width: thin;
40
+ scrollbar-color: var(--dm-scrollbar-thumb, rgba(0, 0, 0, 0.18)) transparent;
41
+
42
+ &::-webkit-scrollbar {
43
+ width: 6px;
44
+ }
45
+ &::-webkit-scrollbar-track {
46
+ background: transparent;
47
+ margin: 0.375rem 0;
48
+ }
49
+ &::-webkit-scrollbar-thumb {
50
+ background: var(--dm-scrollbar-thumb, rgba(0, 0, 0, 0.18));
51
+ border-radius: 3px;
52
+ transition: background-color 0.15s;
53
+ }
54
+ &::-webkit-scrollbar-thumb:hover {
55
+ background: var(--dm-scrollbar-thumb-hover, rgba(0, 0, 0, 0.28));
56
+ }
57
+ }
58
+
59
+ // Inline decoration on the active `/query` range (highlight subtle).
60
+ .dm-slash-command-query {
61
+ color: var(--dm-accent, #2563eb);
62
+ }
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Group heading
66
+ // ---------------------------------------------------------------------------
67
+ .dm-slash-command-group-label {
68
+ padding: 0.375rem 0.5rem 0.125rem;
69
+ font-size: 0.6875rem;
70
+ font-weight: 600;
71
+ letter-spacing: 0.04em;
72
+ text-transform: uppercase;
73
+ color: var(--dm-muted, #999);
74
+ user-select: none;
75
+ }
76
+
77
+ .dm-slash-command-group {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: 0.125rem;
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // Menu item button
85
+ // ---------------------------------------------------------------------------
86
+ .dm-slash-command-item {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 0.5rem;
90
+ width: 100%;
91
+ padding: 0.375rem 0.5rem;
92
+ border: none;
93
+ border-radius: var(--dm-button-border-radius, 0.375rem);
94
+ background: transparent;
95
+ color: var(--dm-editor-text, inherit);
96
+ text-align: left;
97
+ font: inherit;
98
+ cursor: pointer;
99
+ transition: background-color 0.1s;
100
+
101
+ // Selected state set imperatively by the renderer so keyboard and
102
+ // mouse selection share the same visual.
103
+ &[data-selected] {
104
+ background: var(--dm-button-hover-bg, rgba(0, 0, 0, 0.04));
105
+ }
106
+
107
+ &:focus-visible {
108
+ // INSET ring (-2px): slash items are tightly packed; outset would overlap
109
+ // the neighbour. Matches the context-menu item rule.
110
+ outline: 2px solid var(--dm-accent, #2563eb);
111
+ outline-offset: -2px;
112
+ }
113
+ }
114
+
115
+ .dm-slash-command-item-icon {
116
+ display: inline-flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ width: 1.25rem;
120
+ height: 1.25rem;
121
+ flex-shrink: 0;
122
+ color: var(--dm-editor-text, inherit);
123
+
124
+ svg {
125
+ width: 1.125rem;
126
+ height: 1.125rem;
127
+ }
128
+ }
129
+
130
+ .dm-slash-command-item-text {
131
+ display: flex;
132
+ flex-direction: column;
133
+ flex: 1;
134
+ min-width: 0;
135
+ line-height: 1.25;
136
+ }
137
+
138
+ .dm-slash-command-item-label {
139
+ font-size: 0.875rem;
140
+ font-weight: 500;
141
+ color: var(--dm-editor-text, inherit);
142
+ white-space: nowrap;
143
+ overflow: hidden;
144
+ text-overflow: ellipsis;
145
+ }
146
+
147
+ .dm-slash-command-item-description {
148
+ font-size: 0.75rem;
149
+ color: var(--dm-muted, #999);
150
+ white-space: nowrap;
151
+ overflow: hidden;
152
+ text-overflow: ellipsis;
153
+ }
154
+
155
+ // Shortcut hint chip (e.g. `# `, `> `)
156
+ .dm-slash-command-item-shortcut {
157
+ flex-shrink: 0;
158
+ padding: 0.0625rem 0.375rem;
159
+ font-family: var(--dm-code-font, ui-monospace, monospace);
160
+ font-size: 0.6875rem;
161
+ color: var(--dm-muted, #999);
162
+ background: var(--dm-surface, #f8f9fa);
163
+ border: 1px solid var(--dm-border-color, #e5e7eb);
164
+ border-radius: 0.25rem;
165
+ user-select: none;
166
+ }
167
+
168
+ // Shown when the filter returns no items
169
+ .dm-slash-command-empty {
170
+ padding: 0.5rem 0.75rem;
171
+ font-size: 0.8125rem;
172
+ color: var(--dm-muted, #999);
173
+ user-select: none;
174
+ }
@@ -5,7 +5,7 @@
5
5
  // above columns and beside rows. Clicking opens a dropdown with operations.
6
6
  // =============================================================================
7
7
 
8
- // Outer container holds handles + tableWrapper
8
+ // Outer container - holds handles + tableWrapper
9
9
  .dm-editor .dm-table-container {
10
10
  position: relative;
11
11
  }
@@ -54,7 +54,7 @@
54
54
  height: 24px;
55
55
  }
56
56
 
57
- // Cell handle small circle at cell corner (appears when cursor is in a cell)
57
+ // Cell handle - small circle at cell corner (appears when cursor is in a cell)
58
58
  .dm-table-cell-handle {
59
59
  position: absolute;
60
60
  z-index: 10;
@@ -246,9 +246,13 @@
246
246
  width: 100%;
247
247
  padding: 0.375rem 0.5rem;
248
248
  border: none;
249
- border-radius: var(--dm-button-border-radius);
249
+ // Component tokens (--dm-button-*) are defined on .dm-editor. This
250
+ // dropdown is portaled to document.body, so those tokens may be
251
+ // undefined here. Fall back to base tokens (which the theme sets via
252
+ // the .dm-theme-dark/.dm-theme-auto class copied onto the dropdown).
253
+ border-radius: var(--dm-button-border-radius, 0.375rem);
250
254
  background: transparent;
251
- color: var(--dm-button-color);
255
+ color: var(--dm-button-color, var(--dm-text));
252
256
  cursor: pointer;
253
257
  font-size: 0.8125rem;
254
258
  line-height: 1.4;
@@ -277,7 +281,7 @@
277
281
  }
278
282
  }
279
283
 
280
- // Cell dropdown color palette
284
+ // Cell dropdown - color palette
281
285
  .dm-table-cell-dropdown {
282
286
  min-width: auto;
283
287
  width: max-content;
@@ -3,23 +3,38 @@
3
3
  // =============================================================================
4
4
  // TaskList renders as ul[data-type="taskList"]
5
5
  // TaskItem renders as li[data-type="taskItem"] with label > input[checkbox] + div
6
+ //
7
+ // Visual goal: render task items the same way bullet items render. The checkbox
8
+ // hangs in the padding-left area of the wrapper (like a native bullet marker)
9
+ // and the label paragraph starts at the same column as bullet-list text. This
10
+ // is achieved by giving the wrapper the same `padding-left: 1.5em` as <ul> and
11
+ // absolutely positioning the label so it visually replaces the bullet glyph.
6
12
  // =============================================================================
7
13
 
8
14
  .dm-editor .ProseMirror {
9
15
  ul[data-type="taskList"] {
10
16
  list-style: none;
11
- padding-left: 0;
17
+ padding-left: 1.5em;
12
18
 
13
19
  li[data-type="taskItem"] {
14
- display: flex;
15
- align-items: flex-start;
16
- gap: 0.5em;
20
+ position: relative;
21
+ margin: 0.25em 0;
17
22
 
18
23
  > label {
24
+ position: absolute;
25
+ // Position is driven by tokens (defined in `_variables.scss`):
26
+ // - `left` hangs the checkbox in the bullet column (paired with
27
+ // the wrapper's `padding-left: 1.5em` and the checkbox's
28
+ // 1em width, the right edge of the checkbox sits at
29
+ // `li.left - 0.25em` and text starts at `li.left`).
30
+ // - `top` aligns with the first text line's x-height, so the
31
+ // checkbox visually rests next to lower-case glyphs the way
32
+ // a bullet glyph does. Override the token alongside
33
+ // `--dm-editor-line-height` to keep alignment.
34
+ left: var(--dm-task-checkbox-left, -1.15em);
35
+ top: var(--dm-task-checkbox-top, 0.45em);
19
36
  display: flex;
20
37
  align-items: center;
21
- flex-shrink: 0;
22
- margin-top: 0.4em;
23
38
  user-select: none;
24
39
 
25
40
  input[type="checkbox"] {
@@ -33,16 +48,40 @@
33
48
  }
34
49
 
35
50
  > div {
36
- flex: 1;
37
- min-width: 0;
38
-
39
51
  > p {
40
- margin: 0;
52
+ // Small breathing gap (~5px at the editor's default font size)
53
+ // between the checkbox and the paragraph text, applied only on
54
+ // the paragraph so nested ul/ol siblings stay flush with li.left
55
+ // and visually align with bullet-list nesting.
56
+ margin: 0.1em 0;
57
+ padding-inline: 0.3em 0;
58
+ }
59
+
60
+ // Notion-style "children zone" indent: post-label blocks
61
+ // (heading, codeBlock, blockquote, etc.) render indented one
62
+ // level below the label paragraph aligned with the checkbox.
63
+ //
64
+ // Excluded: ALL nested `<ul>` and `<ol>` (plain and taskList).
65
+ // Each list type already gains the same visual indent through
66
+ // its own `padding-left: 1.5em`, so adding the children-zone
67
+ // margin would push nested taskLists ~1.5em further right than
68
+ // nested plain bullet lists. `margin-inline-start` (logical
69
+ // property) flips the indent to the right edge in RTL.
70
+ > :not(p:first-child):not(ul):not(ol) {
71
+ margin-inline-start: var(--dm-block-children-indent, 1.5rem);
72
+ margin-top: 0.25rem;
41
73
  }
42
74
  }
43
75
 
44
- // Checked state strikethrough and dimmed
45
- &[data-checked="true"] > div {
76
+ // Checked state: strikethrough and dim ONLY the label paragraph
77
+ // (the first child of the children-zone div). Targeting `> div`
78
+ // would propagate `text-decoration` and `opacity` into the
79
+ // entire children-zone subtree, including nested taskItems and
80
+ // children-zone notes paragraphs. Notion-style: checking a
81
+ // parent task marks ONLY that task's label, never bleeds into
82
+ // children. Schema enforces paragraph-as-first-child via
83
+ // `taskItem.content = 'paragraph block*'`.
84
+ &[data-checked="true"] > div > p:first-child {
46
85
  text-decoration: line-through;
47
86
  opacity: 0.6;
48
87
  }