@domternal/theme 0.2.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/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** CSS-only package - this import loads the editor stylesheet as a side effect. */
2
+ export {};
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@domternal/theme",
3
+ "version": "0.2.0",
4
+ "description": "Default themes and styles for Domternal editor",
5
+ "author": "https://github.com/ThomasNowHere",
6
+ "license": "MIT",
7
+ "type": "commonjs",
8
+ "sideEffects": true,
9
+ "types": "index.d.ts",
10
+ "style": "dist/domternal-theme.css",
11
+ "sass": "src/index.scss",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./index.d.ts",
15
+ "sass": "./src/index.scss",
16
+ "style": "./dist/domternal-theme.css",
17
+ "default": "./dist/domternal-theme.css"
18
+ },
19
+ "./css": "./dist/domternal-theme.css",
20
+ "./scss": "./src/index.scss"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src",
25
+ "index.d.ts"
26
+ ],
27
+ "scripts": {
28
+ "build": "sass src/index.scss dist/domternal-theme.css --style=compressed --no-source-map && sass src/index.scss dist/domternal-theme.expanded.css --style=expanded --no-source-map"
29
+ },
30
+ "devDependencies": {
31
+ "sass": "^1.89.0"
32
+ },
33
+ "keywords": [
34
+ "domternal",
35
+ "editor",
36
+ "theme",
37
+ "css",
38
+ "prosemirror"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/domternal/domternal.git",
43
+ "directory": "packages/theme"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/domternal/domternal/issues"
47
+ },
48
+ "homepage": "https://domternal.dev"
49
+ }
package/src/_base.scss ADDED
@@ -0,0 +1,60 @@
1
+ // =============================================================================
2
+ // Editor Wrapper Styles
3
+ // =============================================================================
4
+
5
+ .dm-editor {
6
+ display: block;
7
+ position: relative;
8
+ box-sizing: border-box;
9
+ background: var(--dm-editor-bg);
10
+ color: var(--dm-editor-text);
11
+ font-family: var(--dm-editor-font-family);
12
+ font-size: var(--dm-editor-font-size);
13
+ line-height: var(--dm-editor-line-height);
14
+ border: var(--dm-editor-border);
15
+ border-radius: var(--dm-editor-border-radius);
16
+ box-shadow: var(--dm-editor-shadow, none);
17
+ overflow: hidden;
18
+ color-scheme: var(--dm-color-scheme, light);
19
+
20
+ .ProseMirror {
21
+ padding: var(--dm-editor-padding);
22
+ min-height: 6rem;
23
+
24
+ &:focus {
25
+ outline: none;
26
+ }
27
+ }
28
+
29
+ // Focus ring on wrapper when editor is focused
30
+ &:focus-within {
31
+ box-shadow: var(--dm-editor-focus-ring, var(--dm-editor-shadow, none));
32
+ }
33
+
34
+ // Text selection colors
35
+ .ProseMirror ::selection {
36
+ background: var(--dm-selection);
37
+ }
38
+ }
39
+
40
+ // When toolbar is present above editor, remove top border-radius from editor
41
+ .dm-toolbar + .dm-editor {
42
+ border-top-left-radius: 0;
43
+ border-top-right-radius: 0;
44
+ border-top: none;
45
+ }
46
+
47
+ // =============================================================================
48
+ // Shared floating element fade-in animation
49
+ // =============================================================================
50
+ // Used by elements conditionally inserted into the DOM (emoji picker,
51
+ // suggestion dropdown, toolbar dropdowns). Uses scale instead of translateY —
52
+ // scale doesn't shift position so it won't conflict with floating-ui.
53
+ // Elements using data-show (bubble menu, floating menu, popovers) use CSS
54
+ // opacity transitions instead — see their respective stylesheets.
55
+ // =============================================================================
56
+
57
+ @keyframes dm-fade-in {
58
+ from { opacity: 0; }
59
+ to { opacity: 1; }
60
+ }
@@ -0,0 +1,49 @@
1
+ // =============================================================================
2
+ // Bubble Menu Styles
3
+ // =============================================================================
4
+
5
+ .dm-bubble-menu {
6
+ // Button tokens (compact sizing for bubble menu)
7
+ --dm-toolbar-padding: 0.25rem;
8
+ --dm-toolbar-gap: 0.125rem;
9
+ --dm-button-size: 1.75rem;
10
+ --dm-button-border-radius: 0.25rem;
11
+ --dm-button-color: var(--dm-text, #1a1a1a);
12
+ --dm-button-hover-bg: var(--dm-hover, rgba(0, 0, 0, 0.06));
13
+ --dm-button-active-bg: var(--dm-accent-surface, rgba(37, 99, 235, 0.1));
14
+ --dm-button-active-color: var(--dm-accent, #2563eb);
15
+ --dm-button-disabled-opacity: 0.4;
16
+
17
+ // Smaller icons to match compact buttons
18
+ .dm-toolbar-button svg {
19
+ width: 1rem;
20
+ height: 1rem;
21
+ }
22
+
23
+ .dm-toolbar-separator {
24
+ width: 1px;
25
+ height: 1.125rem;
26
+ background: var(--dm-separator-color, var(--dm-border-color, #e0e0e0));
27
+ margin: 0 0.125rem;
28
+ flex-shrink: 0;
29
+ }
30
+
31
+ position: absolute;
32
+ display: flex;
33
+ align-items: center;
34
+ gap: var(--dm-toolbar-gap);
35
+ padding: var(--dm-toolbar-padding);
36
+ background: var(--dm-bg, #fff);
37
+ border: 1px solid var(--dm-border-color, #e5e7eb);
38
+ border-radius: 0.5rem;
39
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04);
40
+ visibility: hidden;
41
+ opacity: 0;
42
+ z-index: 50;
43
+
44
+ &[data-show] {
45
+ visibility: visible;
46
+ opacity: 1;
47
+ transition: opacity 0.15s ease;
48
+ }
49
+ }
@@ -0,0 +1,89 @@
1
+ // =============================================================================
2
+ // Color Palette (grid-layout dropdown for TextColor / HighlightColor)
3
+ // =============================================================================
4
+
5
+ .dm-color-palette {
6
+ display: grid;
7
+ grid-template-columns: repeat(var(--dm-palette-columns, 10), 1fr);
8
+ gap: 0.375rem;
9
+ padding: 0.375rem;
10
+ min-width: auto;
11
+ width: max-content;
12
+ }
13
+
14
+ // "Default" reset button — spans the full width of the grid
15
+ .dm-color-palette-reset {
16
+ grid-column: 1 / -1;
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 0.375rem;
20
+ width: 100%;
21
+ padding: 0.375rem 0.5rem;
22
+ margin-bottom: 0.375rem;
23
+ border: none;
24
+ border-radius: 0.375rem;
25
+ background: transparent;
26
+ color: var(--dm-button-color, #1a1a1a);
27
+ cursor: pointer;
28
+ font-size: 0.8125rem;
29
+ line-height: 1.4;
30
+ text-align: left;
31
+ white-space: nowrap;
32
+ transition: background-color 0.15s;
33
+
34
+ &:hover {
35
+ background: var(--dm-button-hover-bg, rgba(0, 0, 0, 0.06));
36
+ }
37
+
38
+ svg {
39
+ width: 0.875rem;
40
+ height: 0.875rem;
41
+ flex-shrink: 0;
42
+ }
43
+ }
44
+
45
+ // Individual color swatch
46
+ .dm-color-swatch {
47
+ position: relative;
48
+ width: 1.5rem;
49
+ height: 1.5rem;
50
+ padding: 0;
51
+ border: 1px solid rgba(0, 0, 0, 0.06);
52
+ border-radius: 50%;
53
+ cursor: pointer;
54
+ transition: transform 0.1s, box-shadow 0.1s;
55
+
56
+ &:hover {
57
+ transform: scale(1.15);
58
+ z-index: 1;
59
+ box-shadow: 0 0 0 2px var(--dm-toolbar-bg, #f8f9fa),
60
+ 0 0 0 3px var(--dm-accent, #2563eb);
61
+ }
62
+
63
+ // Active state — checkmark indicator
64
+ &--active {
65
+ box-shadow: 0 0 0 2px var(--dm-toolbar-bg, #f8f9fa),
66
+ 0 0 0 3px var(--dm-accent, #2563eb);
67
+
68
+ &::after {
69
+ content: '';
70
+ position: absolute;
71
+ inset: 0;
72
+ border-radius: 50%;
73
+ // White checkmark with dark shadow for universal contrast
74
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M12 5L6.5 11 4 8.5' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' opacity='0.3'/%3E%3Cpath d='M12 5L6.5 11 4 8.5' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 80% no-repeat;
75
+ }
76
+ }
77
+ }
78
+
79
+ // Dark theme — adjust swatch border for dark backgrounds
80
+ .dm-theme-dark .dm-color-swatch,
81
+ .dm-theme-auto .dm-color-swatch {
82
+ border-color: rgba(255, 255, 255, 0.15);
83
+ }
84
+
85
+ @media (prefers-color-scheme: dark) {
86
+ .dm-theme-auto .dm-color-swatch {
87
+ border-color: rgba(255, 255, 255, 0.15);
88
+ }
89
+ }
@@ -0,0 +1,229 @@
1
+ // =============================================================================
2
+ // Content Typography
3
+ // =============================================================================
4
+ // Styles for rendered content inside the editor. Scoped to .dm-editor .ProseMirror
5
+ // so styles don't leak outside.
6
+ // =============================================================================
7
+
8
+ .dm-editor .ProseMirror {
9
+ // ---------------------------------------------------------------------------
10
+ // Paragraphs
11
+ // ---------------------------------------------------------------------------
12
+
13
+ p {
14
+ margin: 0.4em 0;
15
+
16
+ &:last-child {
17
+ margin-bottom: 0;
18
+ }
19
+ }
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Headings
23
+ // ---------------------------------------------------------------------------
24
+
25
+ h1,
26
+ h2,
27
+ h3,
28
+ h4,
29
+ h5,
30
+ h6 {
31
+ margin: 1.5em 0 0.5em;
32
+ font-weight: 700;
33
+ line-height: 1.25;
34
+
35
+ &:first-child {
36
+ margin-top: 0;
37
+ }
38
+ }
39
+
40
+ h1 {
41
+ font-size: 2em;
42
+ }
43
+
44
+ h2 {
45
+ font-size: 1.5em;
46
+ }
47
+
48
+ h3 {
49
+ font-size: 1.25em;
50
+ }
51
+
52
+ h4 {
53
+ font-size: 1.1em;
54
+ }
55
+
56
+ h5 {
57
+ font-size: 1em;
58
+ }
59
+
60
+ h6 {
61
+ font-size: 0.9em;
62
+ color: var(--dm-muted, #666);
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Inline code
67
+ // ---------------------------------------------------------------------------
68
+
69
+ code {
70
+ background: var(--dm-code-bg);
71
+ color: var(--dm-code-text);
72
+ font-family: var(--dm-code-font);
73
+ font-size: 0.875em;
74
+ padding: 0.15em 0.35em;
75
+ border: 1px solid var(--dm-border-color);
76
+ border-radius: var(--dm-code-border-radius);
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Code blocks (pre > code)
81
+ // ---------------------------------------------------------------------------
82
+
83
+ pre {
84
+ background: var(--dm-code-block-bg);
85
+ color: var(--dm-code-block-text);
86
+ font-family: var(--dm-code-font);
87
+ font-size: 0.875em;
88
+ padding: 1em;
89
+ border-radius: 0.375rem;
90
+ overflow-x: auto;
91
+ margin: 0.75em 0;
92
+
93
+ code {
94
+ background: none;
95
+ color: inherit;
96
+ padding: 0;
97
+ border: none;
98
+ border-radius: 0;
99
+ font-size: inherit;
100
+ }
101
+ }
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Blockquote
105
+ // ---------------------------------------------------------------------------
106
+
107
+ blockquote {
108
+ border-left: var(--dm-blockquote-border);
109
+ color: var(--dm-blockquote-color);
110
+ margin: 0.75em 0;
111
+ padding: 0.25em 0 0.25em 1em;
112
+
113
+ p {
114
+ margin-top: 0;
115
+ }
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Lists
120
+ // ---------------------------------------------------------------------------
121
+
122
+ ul,
123
+ ol {
124
+ margin: 0.75em 0;
125
+ padding-left: 1.5em;
126
+
127
+ li {
128
+ margin: 0.25em 0;
129
+
130
+ > p {
131
+ margin: 0.1em 0;
132
+ }
133
+ }
134
+ }
135
+
136
+ ol {
137
+ list-style-type: decimal;
138
+ }
139
+
140
+ ul {
141
+ list-style-type: disc;
142
+ }
143
+
144
+ // Nested lists
145
+ ul ul,
146
+ ol ul {
147
+ list-style-type: circle;
148
+ }
149
+
150
+ ul ul ul,
151
+ ol ul ul,
152
+ ol ol ul {
153
+ list-style-type: square;
154
+ }
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // Horizontal rule
158
+ // ---------------------------------------------------------------------------
159
+
160
+ hr {
161
+ border: none;
162
+ border-top: 2px solid var(--dm-hr-color);
163
+ margin: 1.5em 0;
164
+ }
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // Links
168
+ // ---------------------------------------------------------------------------
169
+
170
+ a {
171
+ color: var(--dm-link-color);
172
+ text-decoration: underline;
173
+ cursor: pointer;
174
+
175
+ &:hover {
176
+ color: var(--dm-link-hover-color);
177
+ }
178
+ }
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // Highlight / mark (fallback for pasted <mark> elements)
182
+ // ---------------------------------------------------------------------------
183
+
184
+ mark {
185
+ color: inherit;
186
+ }
187
+
188
+ // ---------------------------------------------------------------------------
189
+ // Images
190
+ // ---------------------------------------------------------------------------
191
+
192
+ img {
193
+ max-width: 100%;
194
+ height: auto;
195
+ display: block;
196
+ margin: 0.75em 0;
197
+
198
+ &.ProseMirror-selectednode {
199
+ outline: 2px solid var(--dm-accent, #2563eb);
200
+ }
201
+ }
202
+
203
+ // ---------------------------------------------------------------------------
204
+ // Tables
205
+ // ---------------------------------------------------------------------------
206
+
207
+ table {
208
+ border: var(--dm-table-border);
209
+ margin: 0.75em 0;
210
+
211
+ td,
212
+ th {
213
+ border: var(--dm-table-border);
214
+ padding: 0.5em 0.75em;
215
+ min-width: 100px;
216
+ }
217
+
218
+ th {
219
+ background: var(--dm-table-header-bg);
220
+ font-weight: 600;
221
+ text-align: left;
222
+ }
223
+
224
+ td > p,
225
+ th > p {
226
+ margin: 0;
227
+ }
228
+ }
229
+ }
@@ -0,0 +1,122 @@
1
+ // =============================================================================
2
+ // Details / Accordion Styles
3
+ // =============================================================================
4
+ // The NodeView renders:
5
+ // div[data-type="details"]
6
+ // button[type="button"] ← toggle (chevron)
7
+ // div ← contentDOM
8
+ // summary ← editable header (DetailsSummary)
9
+ // div[data-details-content] ← collapsible body (DetailsContent)
10
+ //
11
+ // Open state: .is-open on the outer div, [hidden] removed from content.
12
+ //
13
+ // Layout: CSS grid places the summary and button side-by-side in row 1,
14
+ // and the content spans full width in row 2. This lets the button
15
+ // stretch to the full summary height for vertical centering.
16
+ // =============================================================================
17
+
18
+ .dm-editor .ProseMirror {
19
+ div[data-type="details"] {
20
+ display: grid;
21
+ grid-template-columns: 1fr auto;
22
+ grid-template-rows: auto 1fr;
23
+ border: var(--dm-details-border);
24
+ border-radius: 0.375rem;
25
+ margin: 0.75em 0;
26
+
27
+ // contentDOM — contains summary (row 1) and content (row 2)
28
+ // Use display:contents so its children participate in the grid
29
+ > div {
30
+ display: contents;
31
+ }
32
+
33
+ // Toggle button — chevron on the right, round hover highlight
34
+ > button[type="button"] {
35
+ grid-column: 2;
36
+ grid-row: 1;
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ width: 1.5rem;
41
+ height: 1.5rem;
42
+ align-self: center;
43
+ margin-right: 10px;
44
+ border: none;
45
+ background: transparent;
46
+ cursor: pointer;
47
+ color: var(--dm-muted);
48
+ border-radius: 50%;
49
+
50
+ // CSS chevron (pointing right ›)
51
+ &::before {
52
+ content: '';
53
+ display: block;
54
+ width: 0.4em;
55
+ height: 0.4em;
56
+ border-right: 2px solid currentColor;
57
+ border-bottom: 2px solid currentColor;
58
+ transform: rotate(-45deg);
59
+ transition: transform 0.15s ease;
60
+ }
61
+
62
+ &:hover {
63
+ color: var(--dm-text);
64
+ background: var(--dm-hover);
65
+ }
66
+
67
+ &:focus {
68
+ outline: none;
69
+ }
70
+ }
71
+
72
+ // Summary row background — spans both columns so the button area
73
+ // also gets the surface colour behind the round toggle
74
+ &::before {
75
+ content: '';
76
+ grid-column: 1 / -1;
77
+ grid-row: 1;
78
+ background: var(--dm-details-bg);
79
+ border-radius: 0.375rem;
80
+ }
81
+
82
+ &.is-open::before {
83
+ border-radius: 0.375rem 0.375rem 0 0;
84
+ }
85
+
86
+ // Summary (editable header text)
87
+ summary {
88
+ grid-column: 1;
89
+ grid-row: 1;
90
+ display: block;
91
+ padding: 0.5em 0.5em 0.5em 0.75em;
92
+ font-weight: var(--dm-details-summary-font-weight);
93
+ min-height: 0;
94
+
95
+ // Remove native marker
96
+ &::-webkit-details-marker {
97
+ display: none;
98
+ }
99
+ list-style: none;
100
+ }
101
+
102
+ // Content area (hidden by default via [hidden] attribute from NodeView)
103
+ div[data-details-content] {
104
+ grid-column: 1 / -1;
105
+ grid-row: 2;
106
+ padding: 0.5em 0.75em;
107
+ border-top: var(--dm-details-border);
108
+ }
109
+
110
+ // Disable ProseMirror selected-node outline — the border is enough
111
+ &.ProseMirror-selectednode {
112
+ outline: none;
113
+ }
114
+
115
+ // Open state — rotate chevron to point down
116
+ &.is-open {
117
+ > button[type="button"]::before {
118
+ transform: rotate(45deg);
119
+ }
120
+ }
121
+ }
122
+ }