@open-wysiwyg-editor/angular 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +225 -2
- package/content.css +323 -0
- package/content.min.css +2 -0
- package/fesm2022/open-wysiwyg-editor-angular.mjs +144 -0
- package/fesm2022/open-wysiwyg-editor-angular.mjs.map +1 -0
- package/package.json +56 -3
- package/style.css +1165 -0
- package/style.min.css +2 -0
- package/types/open-wysiwyg-editor-angular.d.ts +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 alhassan-ahmed and open-wysiwyg-editor contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,226 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center"><a href="https://alhassan73.github.io/open-wysiwyg-editor/"><img src="https://raw.githubusercontent.com/alhassan73/open-wysiwyg-editor/main/.github/assets/banner.png" alt="Open WYSIWYG Editor — accessible, RTL-first rich text editor for every framework" width="100%"></a></p>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# @open-wysiwyg-editor/angular
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@open-wysiwyg-editor/angular)
|
|
6
|
+
[](https://github.com/alhassan73/open-wysiwyg-editor/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
**[Live demo →](https://alhassan73.github.io/open-wysiwyg-editor/)** · [GitHub](https://github.com/alhassan73/open-wysiwyg-editor) · [npm](https://www.npmjs.com/package/@open-wysiwyg-editor/angular)
|
|
9
|
+
|
|
10
|
+
Angular component for [open-wysiwyg-editor](https://github.com/alhassan73/open-wysiwyg-editor#readme): an accessible (WCAG 2.2 AA), RTL-first, CSP-safe rich text editor. It is a standalone component that works with `[(ngModel)]`, reactive forms, signals and Angular SSR.
|
|
11
|
+
|
|
12
|
+
Requires **Angular 21+**.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @open-wysiwyg-editor/angular
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This also installs the core package `open-wysiwyg-editor`. The package re-exports the whole core API (`createEditor`, `StarterKit`, types…), so you don't need to import from the core package yourself.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
Add the stylesheet to `angular.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
"styles": [
|
|
28
|
+
"node_modules/@open-wysiwyg-editor/angular/style.css",
|
|
29
|
+
"src/styles.css"
|
|
30
|
+
]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or import it in your global `styles.css`:
|
|
34
|
+
|
|
35
|
+
```css
|
|
36
|
+
@import "@open-wysiwyg-editor/angular/style.css";
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`style.css` styles the toolbar and the editor. If you render saved HTML outside the editor, add `content.css` (the same content styles) to that page. Minified `style.min.css` and `content.min.css` are included.
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
### Standalone component with `[(ngModel)]`
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { Component } from "@angular/core";
|
|
47
|
+
import { FormsModule } from "@angular/forms";
|
|
48
|
+
import { RichTextEditorComponent } from "@open-wysiwyg-editor/angular";
|
|
49
|
+
|
|
50
|
+
@Component({
|
|
51
|
+
selector: "app-post",
|
|
52
|
+
imports: [FormsModule, RichTextEditorComponent],
|
|
53
|
+
template: `
|
|
54
|
+
<owe-rich-text-editor [(ngModel)]="html" placeholder="Write something…" />
|
|
55
|
+
<pre>{{ html }}</pre>
|
|
56
|
+
`,
|
|
57
|
+
})
|
|
58
|
+
export class PostComponent {
|
|
59
|
+
html = "<p>Hello <strong>world</strong></p>";
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Reactive forms
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { Component, inject } from "@angular/core";
|
|
67
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
|
68
|
+
import { RichTextEditorComponent } from "@open-wysiwyg-editor/angular";
|
|
69
|
+
|
|
70
|
+
@Component({
|
|
71
|
+
selector: "app-compose",
|
|
72
|
+
imports: [ReactiveFormsModule, RichTextEditorComponent],
|
|
73
|
+
template: `
|
|
74
|
+
<form [formGroup]="form" (ngSubmit)="save()">
|
|
75
|
+
<owe-rich-text-editor formControlName="body" />
|
|
76
|
+
@if (form.controls.body.touched && form.controls.body.errors) {
|
|
77
|
+
<p role="alert">Write something first.</p>
|
|
78
|
+
}
|
|
79
|
+
<button>Save</button>
|
|
80
|
+
</form>
|
|
81
|
+
`,
|
|
82
|
+
})
|
|
83
|
+
export class ComposeComponent {
|
|
84
|
+
form = inject(FormBuilder).nonNullable.group({
|
|
85
|
+
body: ["", [Validators.required, Validators.minLength(10)]],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
save() {
|
|
89
|
+
console.log(this.form.getRawValue().body);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The control is marked touched when the editor loses focus. `form.controls.body.disable()` makes the editor read-only.
|
|
95
|
+
|
|
96
|
+
### Without forms: `[(value)]`
|
|
97
|
+
|
|
98
|
+
```html
|
|
99
|
+
<owe-rich-text-editor [(value)]="html" />
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Signals
|
|
103
|
+
|
|
104
|
+
`value` is a regular input and `valueChange` is a regular output, so `[(value)]` works with a `model()` or `signal()`:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { Component, signal } from "@angular/core";
|
|
108
|
+
import { RichTextEditorComponent } from "@open-wysiwyg-editor/angular";
|
|
109
|
+
|
|
110
|
+
@Component({
|
|
111
|
+
selector: "app-note",
|
|
112
|
+
imports: [RichTextEditorComponent],
|
|
113
|
+
template: `
|
|
114
|
+
<owe-rich-text-editor [(value)]="html" />
|
|
115
|
+
<p>{{ html().length }} characters of HTML</p>
|
|
116
|
+
`,
|
|
117
|
+
})
|
|
118
|
+
export class NoteComponent {
|
|
119
|
+
html = signal("<p>Start here</p>");
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Options
|
|
124
|
+
|
|
125
|
+
`[options]` takes anything `createEditor()` accepts, except `element`. It is read **once, when the editor starts**. `placeholder` and `readonly` follow changes.
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<owe-rich-text-editor
|
|
129
|
+
[(ngModel)]="html"
|
|
130
|
+
[options]="{ language: 'ar', dir: 'rtl', autofocus: true }"
|
|
131
|
+
placeholder="اكتب هنا…"
|
|
132
|
+
/>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
See the [core docs](https://github.com/alhassan73/open-wysiwyg-editor#readme) for all options (extensions, labels, languages, URL policy, input rules…).
|
|
136
|
+
|
|
137
|
+
### Get the editor: `(ready)`
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import type { Editor } from "@open-wysiwyg-editor/angular";
|
|
141
|
+
|
|
142
|
+
onReady(editor: Editor) {
|
|
143
|
+
editor.focus();
|
|
144
|
+
console.log(editor.getJSON());
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```html
|
|
149
|
+
<owe-rich-text-editor [(ngModel)]="html" (ready)="onReady($event)" />
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
You can also read it with a `viewChild`: `editor()?.editor` is `null` until the editor has started.
|
|
153
|
+
|
|
154
|
+
### Read-only and disabled
|
|
155
|
+
|
|
156
|
+
```html
|
|
157
|
+
<owe-rich-text-editor [value]="html" readonly />
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
With forms, `FormControl.disable()` or `[disabled]` on the control does the same.
|
|
161
|
+
|
|
162
|
+
### Server-side rendering
|
|
163
|
+
|
|
164
|
+
Nothing to configure. The editor starts in `afterNextRender`, so it never runs on the server, and it works with Angular SSR and hydration. The server renders an empty container, and the editor mounts in the browser. Its DOM listeners run outside the Angular zone, so typing doesn't trigger change detection except when the value changes.
|
|
165
|
+
|
|
166
|
+
## API
|
|
167
|
+
|
|
168
|
+
### Inputs
|
|
169
|
+
|
|
170
|
+
| Input | Type | Default | Notes |
|
|
171
|
+
| --- | --- | --- | --- |
|
|
172
|
+
| `value` | `string` | `undefined` | HTML content. Two-way with `[(value)]`. |
|
|
173
|
+
| `options` | `RichTextEditorOptions` | `{}` | `createEditor()` options without `element`. Read once at start. |
|
|
174
|
+
| `placeholder` | `string` | `undefined` | Overrides `options.placeholder`. Updates live. |
|
|
175
|
+
| `readonly` | `boolean` | `false` | Disables editing. Updates live. |
|
|
176
|
+
|
|
177
|
+
### Outputs
|
|
178
|
+
|
|
179
|
+
| Output | Payload | Notes |
|
|
180
|
+
| --- | --- | --- |
|
|
181
|
+
| `valueChange` | `string` | The HTML after every change. |
|
|
182
|
+
| `ready` | `Editor` | Emitted once, in the browser, after the editor starts. |
|
|
183
|
+
|
|
184
|
+
### Members
|
|
185
|
+
|
|
186
|
+
| Member | Notes |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `editor` | The live `Editor`, or `null` before it starts (and on the server). |
|
|
189
|
+
|
|
190
|
+
Forms: implements `ControlValueAccessor` (value, disabled state, touched on blur).
|
|
191
|
+
|
|
192
|
+
## Alternative: the `<owe-editor>` web component
|
|
193
|
+
|
|
194
|
+
The core package also ships a form-associated custom element. Use it if you prefer no Angular wrapper. Angular binds it with `ngDefaultControl`.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
// main.ts
|
|
198
|
+
import "open-wysiwyg-editor/element";
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { CUSTOM_ELEMENTS_SCHEMA, Component } from "@angular/core";
|
|
203
|
+
import { FormsModule } from "@angular/forms";
|
|
204
|
+
|
|
205
|
+
@Component({
|
|
206
|
+
selector: "app-post",
|
|
207
|
+
imports: [FormsModule],
|
|
208
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
209
|
+
template: `<owe-editor ngDefaultControl [(ngModel)]="html" language="ar"></owe-editor>`,
|
|
210
|
+
})
|
|
211
|
+
export class PostComponent {
|
|
212
|
+
html = "";
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
You still need `style.css` from the setup above. See the core docs for the element's attributes and events.
|
|
217
|
+
|
|
218
|
+
## Links
|
|
219
|
+
|
|
220
|
+
- [Core docs and options](https://github.com/alhassan73/open-wysiwyg-editor#readme)
|
|
221
|
+
- [Live demo and website](https://alhassan73.github.io/open-wysiwyg-editor/)
|
|
222
|
+
- [Report an issue](https://github.com/alhassan73/open-wysiwyg-editor/issues)
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT © alhassan-ahmed
|
package/content.css
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/*! open-wysiwyg-editor | MIT License */
|
|
2
|
+
/*
|
|
3
|
+
* Design tokens. Everything lives in the `owe` cascade layer, so any unlayered host CSS wins
|
|
4
|
+
* without !important. Override tokens on .owe (or :root) to theme the editor.
|
|
5
|
+
* Contrast: text >= 4.5:1, borders/focus/states >= 3:1 against --owe-bg (WCAG 1.4.3 / 1.4.11).
|
|
6
|
+
*
|
|
7
|
+
* Tokens added in 1.0 (existing names keep working):
|
|
8
|
+
* --owe-radius card radius (24px) --owe-radius-sm inner controls (10px)
|
|
9
|
+
* --owe-radius-md menus (12px) --owe-radius-lg dialogs (16px)
|
|
10
|
+
* --owe-btn-size toolbar button (40px) --owe-pad-x/-y editing-area padding
|
|
11
|
+
* --owe-chrome toolbar / status bar surface
|
|
12
|
+
* --owe-field-bg inputs and source view --owe-popup-border menu / dialog edge
|
|
13
|
+
* --owe-body paragraph text --owe-text-2 secondary text (quotes)
|
|
14
|
+
* --owe-icon toolbar icon color --owe-caret caret color
|
|
15
|
+
* --owe-primary(-hover) solid fills: pressed buttons, primary buttons (white text, 4.8:1)
|
|
16
|
+
* --owe-gradient bottom edge of the card --owe-backdrop dialog backdrop
|
|
17
|
+
* --owe-accent is the TEXT/link/focus blue; --owe-primary is the FILL blue.
|
|
18
|
+
* Set --owe-font (e.g. "Almarai", system-ui) to change the typeface; no web fonts are bundled.
|
|
19
|
+
*/
|
|
20
|
+
@layer owe {
|
|
21
|
+
.owe,
|
|
22
|
+
.owe-content-root {
|
|
23
|
+
--owe-font: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans Arabic", "Noto Sans", Tahoma, sans-serif;
|
|
24
|
+
--owe-font-mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
|
|
25
|
+
--owe-font-size: 1rem;
|
|
26
|
+
--owe-line-height: 1.65;
|
|
27
|
+
--owe-radius: 24px;
|
|
28
|
+
--owe-radius-sm: 10px;
|
|
29
|
+
--owe-radius-md: 12px;
|
|
30
|
+
--owe-radius-lg: 16px;
|
|
31
|
+
--owe-space: 8px;
|
|
32
|
+
--owe-target: 32px; /* >= 24px (WCAG 2.5.8); 32px is comfortable */
|
|
33
|
+
--owe-btn-size: 40px;
|
|
34
|
+
--owe-pad-x: clamp(16px, 4vw, 32px);
|
|
35
|
+
--owe-pad-y: 28px;
|
|
36
|
+
--owe-gradient: linear-gradient(90deg, #0066ff, #9644ff, #af47c0);
|
|
37
|
+
|
|
38
|
+
--owe-bg: #ffffff;
|
|
39
|
+
--owe-chrome: #fafbfc;
|
|
40
|
+
--owe-surface: #f4f6f8;
|
|
41
|
+
--owe-surface-2: #f1f3f5;
|
|
42
|
+
--owe-field-bg: #ffffff;
|
|
43
|
+
--owe-text: #101112;
|
|
44
|
+
--owe-body: #1b1f24;
|
|
45
|
+
--owe-text-2: #3d434a;
|
|
46
|
+
--owe-muted: #54595f; /* 7:1 on white */
|
|
47
|
+
--owe-icon: #3a3f45;
|
|
48
|
+
--owe-border: #8a929a; /* 3.2:1 on white */
|
|
49
|
+
--owe-border-subtle: #e3e6ea;
|
|
50
|
+
--owe-popup-border: #d5d9de;
|
|
51
|
+
--owe-accent: #0057d9; /* 6.2:1 on white */
|
|
52
|
+
--owe-on-accent: #ffffff;
|
|
53
|
+
--owe-primary: #0066ff;
|
|
54
|
+
--owe-primary-hover: #0057d9;
|
|
55
|
+
--owe-accent-soft: #d3e3fd;
|
|
56
|
+
--owe-focus: #0066ff;
|
|
57
|
+
--owe-caret: #0066ff;
|
|
58
|
+
--owe-danger: #b3261e;
|
|
59
|
+
--owe-warning: #8a5300;
|
|
60
|
+
--owe-success: #146c2e;
|
|
61
|
+
--owe-selection: rgb(0 102 255 / 0.22);
|
|
62
|
+
--owe-code-bg: #f4f6f8;
|
|
63
|
+
--owe-mark: #fff2a8;
|
|
64
|
+
--owe-shadow: 0 16px 40px rgb(16 17 18 / 0.16), 0 2px 8px rgb(16 17 18 / 0.08);
|
|
65
|
+
--owe-backdrop: rgb(16 17 18 / 0.45);
|
|
66
|
+
--owe-z-popover: 1000;
|
|
67
|
+
|
|
68
|
+
color-scheme: light;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@media (prefers-color-scheme: dark) {
|
|
72
|
+
.owe:not([data-theme="light"]),
|
|
73
|
+
.owe-content-root:not([data-theme="light"]) {
|
|
74
|
+
--owe-bg: #17191b;
|
|
75
|
+
--owe-chrome: #131517;
|
|
76
|
+
--owe-surface: #1e2124;
|
|
77
|
+
--owe-surface-2: #25282b;
|
|
78
|
+
--owe-field-bg: #101214;
|
|
79
|
+
--owe-text: #ffffff;
|
|
80
|
+
--owe-body: #dcdedf;
|
|
81
|
+
--owe-text-2: #c3c7cb;
|
|
82
|
+
--owe-muted: #a1a7ad;
|
|
83
|
+
--owe-icon: #d2d4d5;
|
|
84
|
+
--owe-border: #6b7279;
|
|
85
|
+
--owe-border-subtle: #25282b;
|
|
86
|
+
--owe-popup-border: #33383d;
|
|
87
|
+
--owe-accent: #4d94ff;
|
|
88
|
+
--owe-on-accent: #ffffff;
|
|
89
|
+
--owe-primary: #0066ff;
|
|
90
|
+
--owe-primary-hover: #0057d9;
|
|
91
|
+
--owe-accent-soft: #14315e;
|
|
92
|
+
--owe-focus: #4d94ff;
|
|
93
|
+
--owe-caret: #5b9dff;
|
|
94
|
+
--owe-danger: #f2b8b5;
|
|
95
|
+
--owe-warning: #f6c66b;
|
|
96
|
+
--owe-success: #8bd6a0;
|
|
97
|
+
--owe-selection: rgb(0 102 255 / 0.55);
|
|
98
|
+
--owe-code-bg: #0f1112;
|
|
99
|
+
--owe-mark: #4a35b8;
|
|
100
|
+
--owe-shadow: 0 16px 40px rgb(0 0 0 / 0.55), 0 2px 8px rgb(0 0 0 / 0.4);
|
|
101
|
+
--owe-backdrop: rgb(4 5 16 / 0.65);
|
|
102
|
+
color-scheme: dark;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.owe[data-theme="dark"],
|
|
107
|
+
.owe-content-root[data-theme="dark"] {
|
|
108
|
+
--owe-bg: #17191b;
|
|
109
|
+
--owe-chrome: #131517;
|
|
110
|
+
--owe-surface: #1e2124;
|
|
111
|
+
--owe-surface-2: #25282b;
|
|
112
|
+
--owe-field-bg: #101214;
|
|
113
|
+
--owe-text: #ffffff;
|
|
114
|
+
--owe-body: #dcdedf;
|
|
115
|
+
--owe-text-2: #c3c7cb;
|
|
116
|
+
--owe-muted: #a1a7ad;
|
|
117
|
+
--owe-icon: #d2d4d5;
|
|
118
|
+
--owe-border: #6b7279;
|
|
119
|
+
--owe-border-subtle: #25282b;
|
|
120
|
+
--owe-popup-border: #33383d;
|
|
121
|
+
--owe-accent: #4d94ff;
|
|
122
|
+
--owe-on-accent: #ffffff;
|
|
123
|
+
--owe-primary: #0066ff;
|
|
124
|
+
--owe-primary-hover: #0057d9;
|
|
125
|
+
--owe-accent-soft: #14315e;
|
|
126
|
+
--owe-focus: #4d94ff;
|
|
127
|
+
--owe-caret: #5b9dff;
|
|
128
|
+
--owe-danger: #f2b8b5;
|
|
129
|
+
--owe-warning: #f6c66b;
|
|
130
|
+
--owe-success: #8bd6a0;
|
|
131
|
+
--owe-selection: rgb(0 102 255 / 0.55);
|
|
132
|
+
--owe-code-bg: #0f1112;
|
|
133
|
+
--owe-mark: #4a35b8;
|
|
134
|
+
--owe-shadow: 0 16px 40px rgb(0 0 0 / 0.55), 0 2px 8px rgb(0 0 0 / 0.4);
|
|
135
|
+
--owe-backdrop: rgb(4 5 16 / 0.65);
|
|
136
|
+
color-scheme: dark;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@media (prefers-contrast: more) {
|
|
140
|
+
.owe {
|
|
141
|
+
--owe-border: currentColor;
|
|
142
|
+
--owe-border-subtle: var(--owe-border);
|
|
143
|
+
--owe-popup-border: var(--owe-border);
|
|
144
|
+
--owe-muted: var(--owe-text);
|
|
145
|
+
--owe-text-2: var(--owe-text);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* Content styles — shared by the editing area (.owe-content) and published pages
|
|
152
|
+
* (wrap saved HTML in <div class="owe-content-root">), so what authors see is what readers get.
|
|
153
|
+
*/
|
|
154
|
+
@layer owe {
|
|
155
|
+
:is(.owe-content, .owe-content-root) {
|
|
156
|
+
font-family: var(--owe-font);
|
|
157
|
+
font-size: var(--owe-font-size);
|
|
158
|
+
line-height: var(--owe-line-height);
|
|
159
|
+
color: var(--owe-body);
|
|
160
|
+
overflow-wrap: break-word;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
:is(.owe-content, .owe-content-root) > :first-child {
|
|
164
|
+
margin-block-start: 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
:is(.owe-content, .owe-content-root) :is(p, ul, ol, blockquote, pre, table, figure, hr) {
|
|
168
|
+
margin-block: 0 0.85em;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
:is(.owe-content, .owe-content-root) :is(h1, h2, h3, h4, h5, h6) {
|
|
172
|
+
line-height: 1.2;
|
|
173
|
+
margin-block: 1.3em 0.5em;
|
|
174
|
+
font-weight: 700;
|
|
175
|
+
letter-spacing: -0.02em;
|
|
176
|
+
color: var(--owe-text);
|
|
177
|
+
}
|
|
178
|
+
/* Negative tracking breaks Arabic joining: reset for RTL / Arabic-script content. */
|
|
179
|
+
:is(.owe-content, .owe-content-root) :is(h1, h2, h3, h4, h5, h6):is([dir="rtl"], :lang(ar), :lang(fa), :lang(ur), :lang(he)),
|
|
180
|
+
:is([dir="rtl"], :lang(ar), :lang(fa), :lang(ur), :lang(he)) :is(.owe-content, .owe-content-root) :is(h1, h2, h3, h4, h5, h6),
|
|
181
|
+
:is(.owe-content, .owe-content-root):is([dir="rtl"], :lang(ar), :lang(fa), :lang(ur), :lang(he)) :is(h1, h2, h3, h4, h5, h6) {
|
|
182
|
+
letter-spacing: 0;
|
|
183
|
+
}
|
|
184
|
+
:is(.owe-content, .owe-content-root) h1 { font-size: 2.1em; }
|
|
185
|
+
:is(.owe-content, .owe-content-root) h2 { font-size: 1.5em; }
|
|
186
|
+
:is(.owe-content, .owe-content-root) h3 { font-size: 1.25em; }
|
|
187
|
+
:is(.owe-content, .owe-content-root) h4 { font-size: 1.1em; }
|
|
188
|
+
:is(.owe-content, .owe-content-root) h5 { font-size: 1em; }
|
|
189
|
+
:is(.owe-content, .owe-content-root) h6 { font-size: 0.9em; }
|
|
190
|
+
|
|
191
|
+
:is(.owe-content, .owe-content-root) a {
|
|
192
|
+
color: var(--owe-accent);
|
|
193
|
+
text-decoration: underline; /* never colour alone (WCAG 1.4.1) */
|
|
194
|
+
text-underline-offset: 0.15em;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
:is(.owe-content, .owe-content-root) :is(ul, ol) {
|
|
198
|
+
padding-inline-start: 1.6em;
|
|
199
|
+
}
|
|
200
|
+
:is(.owe-content, .owe-content-root) li > :is(p, ul, ol) {
|
|
201
|
+
margin-block: 0.15em;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
:is(.owe-content, .owe-content-root) ul[data-type="taskList"] {
|
|
205
|
+
list-style: none;
|
|
206
|
+
padding-inline-start: 0.25em;
|
|
207
|
+
}
|
|
208
|
+
:is(.owe-content, .owe-content-root) li[data-type="taskItem"] {
|
|
209
|
+
display: flex;
|
|
210
|
+
align-items: flex-start;
|
|
211
|
+
gap: 0.5em;
|
|
212
|
+
}
|
|
213
|
+
:is(.owe-content, .owe-content-root) li[data-type="taskItem"] > input,
|
|
214
|
+
.owe-task-control > input {
|
|
215
|
+
inline-size: 1.1em;
|
|
216
|
+
block-size: 1.1em;
|
|
217
|
+
margin-block-start: 0.3em;
|
|
218
|
+
accent-color: var(--owe-primary);
|
|
219
|
+
flex: none;
|
|
220
|
+
}
|
|
221
|
+
:is(.owe-content, .owe-content-root) li[data-type="taskItem"] > div {
|
|
222
|
+
flex: 1;
|
|
223
|
+
min-inline-size: 0;
|
|
224
|
+
}
|
|
225
|
+
:is(.owe-content, .owe-content-root) li[data-checked="true"] > div {
|
|
226
|
+
text-decoration: line-through;
|
|
227
|
+
color: var(--owe-muted);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
:is(.owe-content, .owe-content-root) blockquote {
|
|
231
|
+
margin-inline: 0;
|
|
232
|
+
padding-inline-start: 1em;
|
|
233
|
+
border-inline-start: 3px solid var(--owe-accent);
|
|
234
|
+
border-start-start-radius: 2px;
|
|
235
|
+
border-end-start-radius: 2px;
|
|
236
|
+
color: var(--owe-text-2);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
:is(.owe-content, .owe-content-root) code {
|
|
240
|
+
font-family: var(--owe-font-mono);
|
|
241
|
+
font-size: 0.9em;
|
|
242
|
+
background: var(--owe-code-bg);
|
|
243
|
+
border: 1px solid var(--owe-border-subtle);
|
|
244
|
+
border-radius: 6px;
|
|
245
|
+
padding: 0.1em 0.35em;
|
|
246
|
+
}
|
|
247
|
+
:is(.owe-content, .owe-content-root) pre {
|
|
248
|
+
font-family: var(--owe-font-mono);
|
|
249
|
+
background: var(--owe-code-bg);
|
|
250
|
+
border: 1px solid var(--owe-border-subtle);
|
|
251
|
+
border-radius: var(--owe-radius-md);
|
|
252
|
+
padding: 1em 1.25em;
|
|
253
|
+
overflow-x: auto;
|
|
254
|
+
direction: ltr; /* code is left-to-right even in RTL documents */
|
|
255
|
+
text-align: start;
|
|
256
|
+
tab-size: 2;
|
|
257
|
+
}
|
|
258
|
+
:is(.owe-content, .owe-content-root) pre code {
|
|
259
|
+
background: none;
|
|
260
|
+
border: 0;
|
|
261
|
+
padding: 0;
|
|
262
|
+
font-size: 0.9em;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
:is(.owe-content, .owe-content-root) hr {
|
|
266
|
+
border: none;
|
|
267
|
+
border-block-start: 2px solid var(--owe-border-subtle);
|
|
268
|
+
margin-block: 1.5em;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
:is(.owe-content, .owe-content-root) img {
|
|
272
|
+
max-inline-size: 100%;
|
|
273
|
+
block-size: auto;
|
|
274
|
+
display: block;
|
|
275
|
+
}
|
|
276
|
+
:is(.owe-content, .owe-content-root) figure {
|
|
277
|
+
margin-inline: 0;
|
|
278
|
+
}
|
|
279
|
+
:is(.owe-content, .owe-content-root) figcaption {
|
|
280
|
+
color: var(--owe-muted);
|
|
281
|
+
font-size: 0.9em;
|
|
282
|
+
margin-block-start: 0.4em;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
:is(.owe-content, .owe-content-root) table {
|
|
286
|
+
border-collapse: collapse;
|
|
287
|
+
inline-size: 100%;
|
|
288
|
+
table-layout: fixed;
|
|
289
|
+
}
|
|
290
|
+
:is(.owe-content, .owe-content-root) caption {
|
|
291
|
+
caption-side: top;
|
|
292
|
+
text-align: start;
|
|
293
|
+
font-weight: 600;
|
|
294
|
+
padding-block-end: 0.4em;
|
|
295
|
+
}
|
|
296
|
+
:is(.owe-content, .owe-content-root) :is(td, th) {
|
|
297
|
+
border: 1px solid var(--owe-border-subtle);
|
|
298
|
+
padding: 0.55em 0.8em;
|
|
299
|
+
vertical-align: top;
|
|
300
|
+
text-align: start;
|
|
301
|
+
min-inline-size: 3em;
|
|
302
|
+
}
|
|
303
|
+
:is(.owe-content, .owe-content-root) th {
|
|
304
|
+
background: var(--owe-surface);
|
|
305
|
+
color: var(--owe-text);
|
|
306
|
+
font-weight: 700;
|
|
307
|
+
}
|
|
308
|
+
:is(.owe-content, .owe-content-root) :is(td, th) > :last-child {
|
|
309
|
+
margin-block-end: 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
:is(.owe-content, .owe-content-root) mark {
|
|
313
|
+
background-color: var(--owe-mark);
|
|
314
|
+
color: inherit;
|
|
315
|
+
border-radius: 0.2em;
|
|
316
|
+
padding-inline: 0.15em;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Alignment classes (used by the editor always, and by output when TextAlign output: "class"). */
|
|
320
|
+
.owe-align-center { text-align: center; }
|
|
321
|
+
.owe-align-end { text-align: end; }
|
|
322
|
+
.owe-align-justify { text-align: justify; }
|
|
323
|
+
}
|
package/content.min.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! open-wysiwyg-editor | MIT License */
|
|
2
|
+
/*! open-wysiwyg-editor | MIT License */ @layer owe{.owe,.owe-content-root{--owe-font:system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans Arabic","Noto Sans",Tahoma,sans-serif;--owe-font-mono:ui-monospace,"JetBrains Mono","SFMono-Regular",Menlo,Consolas,"Liberation Mono",monospace;--owe-font-size:1rem;--owe-line-height:1.65;--owe-radius:24px;--owe-radius-sm:10px;--owe-radius-md:12px;--owe-radius-lg:16px;--owe-space:8px;--owe-target:32px;--owe-btn-size:40px;--owe-pad-x:clamp(16px,4vw,32px);--owe-pad-y:28px;--owe-gradient:linear-gradient(90deg,#0066ff,#9644ff,#af47c0);--owe-bg:#ffffff;--owe-chrome:#fafbfc;--owe-surface:#f4f6f8;--owe-surface-2:#f1f3f5;--owe-field-bg:#ffffff;--owe-text:#101112;--owe-body:#1b1f24;--owe-text-2:#3d434a;--owe-muted:#54595f;--owe-icon:#3a3f45;--owe-border:#8a929a;--owe-border-subtle:#e3e6ea;--owe-popup-border:#d5d9de;--owe-accent:#0057d9;--owe-on-accent:#ffffff;--owe-primary:#0066ff;--owe-primary-hover:#0057d9;--owe-accent-soft:#d3e3fd;--owe-focus:#0066ff;--owe-caret:#0066ff;--owe-danger:#b3261e;--owe-warning:#8a5300;--owe-success:#146c2e;--owe-selection:rgb(0 102 255 / 0.22);--owe-code-bg:#f4f6f8;--owe-mark:#fff2a8;--owe-shadow:0 16px 40px rgb(16 17 18 / 0.16),0 2px 8px rgb(16 17 18 / 0.08);--owe-backdrop:rgb(16 17 18 / 0.45);--owe-z-popover:1000;color-scheme:light}@media (prefers-color-scheme:dark){.owe:not([data-theme="light"]),.owe-content-root:not([data-theme="light"]){--owe-bg:#17191b;--owe-chrome:#131517;--owe-surface:#1e2124;--owe-surface-2:#25282b;--owe-field-bg:#101214;--owe-text:#ffffff;--owe-body:#dcdedf;--owe-text-2:#c3c7cb;--owe-muted:#a1a7ad;--owe-icon:#d2d4d5;--owe-border:#6b7279;--owe-border-subtle:#25282b;--owe-popup-border:#33383d;--owe-accent:#4d94ff;--owe-on-accent:#ffffff;--owe-primary:#0066ff;--owe-primary-hover:#0057d9;--owe-accent-soft:#14315e;--owe-focus:#4d94ff;--owe-caret:#5b9dff;--owe-danger:#f2b8b5;--owe-warning:#f6c66b;--owe-success:#8bd6a0;--owe-selection:rgb(0 102 255 / 0.55);--owe-code-bg:#0f1112;--owe-mark:#4a35b8;--owe-shadow:0 16px 40px rgb(0 0 0 / 0.55),0 2px 8px rgb(0 0 0 / 0.4);--owe-backdrop:rgb(4 5 16 / 0.65);color-scheme:dark}}.owe[data-theme="dark"],.owe-content-root[data-theme="dark"]{--owe-bg:#17191b;--owe-chrome:#131517;--owe-surface:#1e2124;--owe-surface-2:#25282b;--owe-field-bg:#101214;--owe-text:#ffffff;--owe-body:#dcdedf;--owe-text-2:#c3c7cb;--owe-muted:#a1a7ad;--owe-icon:#d2d4d5;--owe-border:#6b7279;--owe-border-subtle:#25282b;--owe-popup-border:#33383d;--owe-accent:#4d94ff;--owe-on-accent:#ffffff;--owe-primary:#0066ff;--owe-primary-hover:#0057d9;--owe-accent-soft:#14315e;--owe-focus:#4d94ff;--owe-caret:#5b9dff;--owe-danger:#f2b8b5;--owe-warning:#f6c66b;--owe-success:#8bd6a0;--owe-selection:rgb(0 102 255 / 0.55);--owe-code-bg:#0f1112;--owe-mark:#4a35b8;--owe-shadow:0 16px 40px rgb(0 0 0 / 0.55),0 2px 8px rgb(0 0 0 / 0.4);--owe-backdrop:rgb(4 5 16 / 0.65);color-scheme:dark}@media (prefers-contrast:more){.owe{--owe-border:currentColor;--owe-border-subtle:var(--owe-border);--owe-popup-border:var(--owe-border);--owe-muted:var(--owe-text);--owe-text-2:var(--owe-text)}}}@layer owe{:is(.owe-content,.owe-content-root){font-family:var(--owe-font);font-size:var(--owe-font-size);line-height:var(--owe-line-height);color:var(--owe-body);overflow-wrap:break-word}:is(.owe-content,.owe-content-root)>:first-child{margin-block-start:0}:is(.owe-content,.owe-content-root) :is(p,ul,ol,blockquote,pre,table,figure,hr){margin-block:0 0.85em}:is(.owe-content,.owe-content-root) :is(h1,h2,h3,h4,h5,h6){line-height:1.2;margin-block:1.3em 0.5em;font-weight:700;letter-spacing:-0.02em;color:var(--owe-text)}:is(.owe-content,.owe-content-root) :is(h1,h2,h3,h4,h5,h6):is([dir="rtl"],:lang(ar),:lang(fa),:lang(ur),:lang(he)),:is([dir="rtl"],:lang(ar),:lang(fa),:lang(ur),:lang(he)) :is(.owe-content,.owe-content-root) :is(h1,h2,h3,h4,h5,h6),:is(.owe-content,.owe-content-root):is([dir="rtl"],:lang(ar),:lang(fa),:lang(ur),:lang(he)) :is(h1,h2,h3,h4,h5,h6){letter-spacing:0}:is(.owe-content,.owe-content-root) h1{font-size:2.1em}:is(.owe-content,.owe-content-root) h2{font-size:1.5em}:is(.owe-content,.owe-content-root) h3{font-size:1.25em}:is(.owe-content,.owe-content-root) h4{font-size:1.1em}:is(.owe-content,.owe-content-root) h5{font-size:1em}:is(.owe-content,.owe-content-root) h6{font-size:0.9em}:is(.owe-content,.owe-content-root) a{color:var(--owe-accent);text-decoration:underline;text-underline-offset:0.15em}:is(.owe-content,.owe-content-root) :is(ul,ol){padding-inline-start:1.6em}:is(.owe-content,.owe-content-root) li>:is(p,ul,ol){margin-block:0.15em}:is(.owe-content,.owe-content-root) ul[data-type="taskList"]{list-style:none;padding-inline-start:0.25em}:is(.owe-content,.owe-content-root) li[data-type="taskItem"]{display:flex;align-items:flex-start;gap:0.5em}:is(.owe-content,.owe-content-root) li[data-type="taskItem"]>input,.owe-task-control>input{inline-size:1.1em;block-size:1.1em;margin-block-start:0.3em;accent-color:var(--owe-primary);flex:none}:is(.owe-content,.owe-content-root) li[data-type="taskItem"]>div{flex:1;min-inline-size:0}:is(.owe-content,.owe-content-root) li[data-checked="true"]>div{text-decoration:line-through;color:var(--owe-muted)}:is(.owe-content,.owe-content-root) blockquote{margin-inline:0;padding-inline-start:1em;border-inline-start:3px solid var(--owe-accent);border-start-start-radius:2px;border-end-start-radius:2px;color:var(--owe-text-2)}:is(.owe-content,.owe-content-root) code{font-family:var(--owe-font-mono);font-size:0.9em;background:var(--owe-code-bg);border:1px solid var(--owe-border-subtle);border-radius:6px;padding:0.1em 0.35em}:is(.owe-content,.owe-content-root) pre{font-family:var(--owe-font-mono);background:var(--owe-code-bg);border:1px solid var(--owe-border-subtle);border-radius:var(--owe-radius-md);padding:1em 1.25em;overflow-x:auto;direction:ltr;text-align:start;tab-size:2}:is(.owe-content,.owe-content-root) pre code{background:none;border:0;padding:0;font-size:0.9em}:is(.owe-content,.owe-content-root) hr{border:none;border-block-start:2px solid var(--owe-border-subtle);margin-block:1.5em}:is(.owe-content,.owe-content-root) img{max-inline-size:100%;block-size:auto;display:block}:is(.owe-content,.owe-content-root) figure{margin-inline:0}:is(.owe-content,.owe-content-root) figcaption{color:var(--owe-muted);font-size:0.9em;margin-block-start:0.4em}:is(.owe-content,.owe-content-root) table{border-collapse:collapse;inline-size:100%;table-layout:fixed}:is(.owe-content,.owe-content-root) caption{caption-side:top;text-align:start;font-weight:600;padding-block-end:0.4em}:is(.owe-content,.owe-content-root) :is(td,th){border:1px solid var(--owe-border-subtle);padding:0.55em 0.8em;vertical-align:top;text-align:start;min-inline-size:3em}:is(.owe-content,.owe-content-root) th{background:var(--owe-surface);color:var(--owe-text);font-weight:700}:is(.owe-content,.owe-content-root) :is(td,th)>:last-child{margin-block-end:0}:is(.owe-content,.owe-content-root) mark{background-color:var(--owe-mark);color:inherit;border-radius:0.2em;padding-inline:0.15em}.owe-align-center{text-align:center}.owe-align-end{text-align:end}.owe-align-justify{text-align:justify}}
|