mnfst 0.5.230 → 0.5.232

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.
@@ -2,136 +2,139 @@
2
2
 
3
3
  @layer base, components, utilities;
4
4
 
5
- /* Self-contained: every rule the plugin needs lives here, so this stylesheet works
6
- on its own. Authors override without specificity battles — rules are :where()
7
- wrapped and keyed off data-edit-* attributes, including the injected affordances
8
- (handles, overlay, live region, toolbar, menu). No class names to collide with. */
5
+ /* Rules are :where()-wrapped and keyed off data-edit-* attributes, so authors
6
+ override without specificity battles. Visuals are on for every area;
7
+ x-edit.bare (data-edit-area="bare") keeps the behaviour and draws none of them. */
9
8
 
10
9
  @layer utilities {
11
10
 
12
- /* ---- Editing UI scope ----
13
- ALL chrome — cursors, movable/text outlines, size-handle hairlines, drop
14
- targets, the floating toolbar — lives under ONE scope: [data-edit-ui], on
15
- the region itself or any ancestor (e.g. <body data-edit-ui> for a whole
16
- editor view). The plugin sets it on `.authoring` regions automatically;
17
- everything else opts in with the attribute. A region outside the scope is
18
- just as editable — it simply carries no visual cues. */
11
+ /* ---- Pieces ---- */
12
+ :where([data-edit-piece]) {
13
+ cursor: grab;
14
+ touch-action: none;
15
+ transition: opacity 0.12s ease, outline-color 0.12s ease;
16
+ }
19
17
 
20
- /* ---- Editable area (armed) ---- */
21
- :where([data-edit-armed]) {
22
- position: relative;
18
+ :where([data-edit-piece]):where(:hover):where(:not([data-edit-area="bare"], [data-edit-area="bare"] *, [data-edit-dragging], [data-edit-ghost], :has([data-edit-dragging]) *)) {
19
+ outline: 1px solid color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 45%, transparent);
20
+ outline-offset: 3px;
23
21
  }
24
22
 
25
- /* No default region boundary or label: a page being edited should look
26
- like the page, not a diagram of its regions. The hooks remain for a real
27
- editor product to draw its own — [data-edit-authoring] marks the region
28
- and data-edit-label carries "key · regime". */
23
+ :where([data-edit-piece]):focus-visible:where(:not([data-edit-area="bare"], [data-edit-area="bare"] *)) {
24
+ outline: 2px solid var(--edit-accent, var(--color-brand-content));
25
+ outline-offset: 3px;
26
+ }
29
27
 
30
- /* ---- Reorder (pointer + keyboard; touch-action:none lets touch-drag beat scroll) ---- */
31
- :where([data-edit-sortable]) {
28
+ /* Grip: drags a text piece; sits at its inline-start edge */
29
+ :where([data-edit-grip]) {
30
+ position: fixed;
31
+ z-index: 1001;
32
+ width: var(--edit-grip-size, 0.75rem);
33
+ height: calc(var(--edit-grip-size, 0.75rem) * 1.5);
34
+ translate: calc(-100% - var(--edit-grip-offset, 0.25rem)) 0;
35
+ border-radius: 3px;
36
+ color: var(--edit-accent, var(--color-brand-content));
37
+ background: radial-gradient(circle, currentColor 1px, transparent 1.5px) center / 6px 6px;
38
+ opacity: 0.6;
32
39
  cursor: grab;
33
40
  touch-action: none;
34
- transition: opacity 0.12s ease, outline-color 0.12s ease;
41
+ user-select: none;
42
+
43
+ &:hover {
44
+ opacity: 1;
45
+ }
35
46
  }
36
47
 
37
- /* No injected grip: the row itself is the handle (grab cursor), while text
38
- inside stays a text surface (text cursor, below). Rows that are pure text
39
- still reorder by keyboard. */
48
+ :where([data-edit-grip]:dir(rtl)) {
49
+ translate: var(--edit-grip-offset, 0.25rem) 0;
50
+ }
40
51
 
41
- :where([data-edit-sortable], [data-edit-movable]):focus-visible {
42
- outline: 2px solid var(--edit-accent, var(--color-brand-content));
43
- outline-offset: 3px;
52
+ :where([data-edit-grip][hidden]) {
53
+ display: none;
44
54
  }
45
55
 
46
- /* Lifted out of the flow and following the pointer. */
47
- :where([data-edit-sortable][data-edit-dragging]) {
56
+ /* Lifted, following the pointer */
57
+ :where([data-edit-dragging=""]) {
48
58
  z-index: 1000;
49
59
  cursor: grabbing;
60
+ }
61
+
62
+ :where([data-edit-dragging=""]):where(:not([data-edit-area="bare"], [data-edit-area="bare"] *)) {
50
63
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
51
64
  }
52
65
 
53
- /* The stand-in holding the slot the drop would land in — a translucent copy of
54
- the element by default, because the clearest preview of where it lands is the
55
- thing itself. Restyle it freely: hide its contents for an empty outlined slot,
56
- give it a background, animate it. */
66
+ /* Grabbed from the keyboard */
67
+ :where([data-edit-dragging="keyboard"]):where(:not([data-edit-area="bare"], [data-edit-area="bare"] *)) {
68
+ outline: 2px dashed var(--edit-accent, var(--color-brand-content));
69
+ outline-offset: 3px;
70
+ }
71
+
72
+ /* The stand-in holding the slot the drop would land in */
57
73
  :where([data-edit-ghost]) {
58
74
  opacity: var(--edit-ghost-opacity, 0.4);
59
75
  pointer-events: none;
60
76
  }
61
77
 
62
- /* Valid cross-region destinations during a drag: a dashed invitation, and
63
- enough height that an EMPTY region can be dropped onto at all. */
64
- :where([data-edit-drop-target]) {
78
+ :where([data-edit-area="bare"] [data-edit-ghost]) {
79
+ opacity: 0;
80
+ }
81
+
82
+ /* The container the drop would land in */
83
+ :where(:has(> [data-edit-ghost])):where(:not([data-edit-area="bare"], [data-edit-area="bare"] *)) {
84
+ outline: 1px dashed color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 55%, transparent);
85
+ outline-offset: 4px;
86
+ }
87
+
88
+ /* Other areas a drag can land in; an empty one still has a surface */
89
+ :where([data-edit-drop-target]):where(:not([data-edit-area="bare"])) {
90
+ min-height: 2.5rem;
65
91
  outline: 1px dashed color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 55%, transparent);
66
92
  outline-offset: 4px;
93
+ }
94
+
95
+ /* While dragging: empty containers can be dropped into, nothing else reacts */
96
+ :where([data-edit-area]:not([data-edit-area="bare"]):has([data-edit-dragging=""])) :where(:empty:not(img, input, br, hr, svg, video, canvas, iframe, textarea, [data-edit-ghost], [data-edit-handle], [data-edit-area="bare"], [data-edit-area="bare"] *)) {
67
97
  min-height: 2.5rem;
68
98
  }
69
99
 
70
- /* Nothing else should react while a drag is in progress */
71
- :where([data-edit-dragging-in]) {
100
+ :where([data-edit-area]:has([data-edit-dragging=""])) {
72
101
  cursor: grabbing;
73
102
  user-select: none;
74
103
 
75
- & :where([data-edit-sortable]:not([data-edit-dragging])) {
104
+ & :where([data-edit-piece]:not([data-edit-dragging])) {
76
105
  cursor: grabbing;
77
106
  }
78
107
  }
79
108
 
80
- :where([data-edit-grabbed]) {
81
- outline: 2px dashed var(--edit-accent, var(--color-brand-content));
82
- outline-offset: 3px;
83
- }
84
-
85
- /* ---- Move (positioned children) ---- */
86
- :where([data-edit-movable]) {
87
- cursor: move;
88
- touch-action: none;
89
- }
90
-
91
- :where([data-edit-ui] [data-edit-movable], [data-edit-ui][data-edit-movable]):hover {
92
- outline: 1px solid var(--edit-accent, var(--color-brand-content));
93
- outline-offset: 2px;
94
- }
95
-
96
- /* ---- Inline text ----
97
- One weight everywhere: hover hints and the focused caret both use the same
98
- 1px line, so focusing doesn't make the page jump a weight class. */
99
- :where([data-edit-ui]) :where([data-edit-armed]) [contenteditable="true"],
100
- :where([data-edit-ui])[data-edit-armed] [contenteditable="true"] {
109
+ /* ---- Inline text ---- */
110
+ :where([data-edit-area]:not([data-edit-area="bare"])) :where([contenteditable="true"], [data-edit-text]):where(:not([data-edit-area="bare"] *)),
111
+ :where([data-edit-area]:is([contenteditable="true"], [data-edit-text]):not([data-edit-area="bare"])) {
101
112
  cursor: text;
102
113
  outline: 1px solid transparent;
103
114
  outline-offset: 2px;
104
115
  border-radius: 3px;
105
116
  transition: outline-color 0.12s ease;
106
- }
107
117
 
108
- :where([data-edit-ui]) :where([data-edit-armed]) [contenteditable="true"]:hover,
109
- :where([data-edit-ui])[data-edit-armed] [contenteditable="true"]:hover {
110
- outline-color: color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 45%, transparent);
111
- }
118
+ &:hover {
119
+ outline-color: color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 45%, transparent);
120
+ }
112
121
 
113
- :where([data-edit-ui]) :where([data-edit-armed]) [contenteditable="true"]:focus,
114
- :where([data-edit-ui])[data-edit-armed] [contenteditable="true"]:focus {
115
- outline-color: var(--edit-accent, var(--color-brand-content));
122
+ &:focus {
123
+ outline-color: var(--edit-accent, var(--color-brand-content));
124
+ }
116
125
  }
117
126
 
118
- /* ---- Size handles ----
119
- Injected as siblings of the element's own markup, so its native :hover and
120
- :focus still fire and its body stays interactive. */
121
- :where([data-edit-sizable]) {
127
+ /* ---- Size handles ---- */
128
+ :where(:has(> [data-edit-handle])) {
122
129
  position: relative;
123
130
  }
124
131
 
125
- /* Handles reveal on approach rather than sitting on the element permanently.
126
- The hairline is chrome, so it follows the [data-edit-ui] scope like every
127
- other affordance; outside the scope the handles still work (hit areas and
128
- resize cursors stay), they just draw nothing. */
129
- :where([data-edit-sizable]) :where([data-edit-handle])::before {
132
+ :where([data-edit-handle])::before {
130
133
  opacity: 0;
131
134
  transition: opacity 0.12s ease, background-color 0.15s ease;
132
135
  }
133
136
 
134
- :where([data-edit-ui] [data-edit-sizable], [data-edit-ui][data-edit-sizable]):is(:hover, :focus-within) :where([data-edit-handle])::before {
137
+ :where(:has(> [data-edit-handle])):is(:hover, :focus-within):where(:not([data-edit-area="bare"], [data-edit-area="bare"] *)) > :where([data-edit-handle])::before {
135
138
  opacity: 1;
136
139
  }
137
140
 
@@ -139,9 +142,9 @@
139
142
  position: absolute;
140
143
  z-index: 100;
141
144
  background: transparent;
145
+ touch-action: none;
142
146
  --h: var(--edit-size-handle, 1rem);
143
147
 
144
- /* Hairline, revealed on hover */
145
148
  &::before {
146
149
  content: '';
147
150
  position: absolute;
@@ -161,11 +164,10 @@
161
164
  }
162
165
  }
163
166
 
164
- :where([data-edit-handle]):focus-visible {
167
+ :where([data-edit-handle]):focus-visible:where(:not([data-edit-area="bare"], [data-edit-area="bare"] *)) {
165
168
  outline: 2px solid var(--edit-accent, var(--color-brand-content));
166
169
  }
167
170
 
168
- /* Edges span their axis; corners are square. A corner value contains a hyphen. */
169
171
  :where([data-edit-handle="top"], [data-edit-handle="bottom"]) {
170
172
  left: 0;
171
173
  width: 100%;
@@ -193,8 +195,6 @@
193
195
  height: var(--h);
194
196
  }
195
197
 
196
- /* Inset — each handle straddles its edge; a corner matches one rule per axis.
197
- inset-inline-* carries the logical start/end values, so they flip for RTL. */
198
198
  :where([data-edit-handle^="top"]) {
199
199
  top: calc(var(--h) * -0.5);
200
200
  }
@@ -219,7 +219,6 @@
219
219
  inset-inline-end: calc(var(--h) * -0.5);
220
220
  }
221
221
 
222
- /* Corner cursors; the logical pair flips with direction. */
223
222
  :where([data-edit-handle="top-left"], [data-edit-handle="bottom-right"], [data-edit-handle="top-start"], [data-edit-handle="bottom-end"]) {
224
223
  cursor: nwse-resize;
225
224
  }
@@ -236,12 +235,12 @@
236
235
  cursor: nwse-resize;
237
236
  }
238
237
 
239
- /* Collapsed past --edit-size-collapse-* — authors restyle freely */
240
- :where([data-edit-collapsed]) {
241
- opacity: 0.45;
238
+ /* Bare: handles keep their hit areas and cursors, draw nothing */
239
+ :where([data-edit-area="bare"] [data-edit-handle])::before {
240
+ content: none;
242
241
  }
243
242
 
244
- /* Full-viewport capture layer during a size or move drag */
243
+ /* Pointer capture layer during a resize */
245
244
  :where([data-edit-overlay]) {
246
245
  position: fixed;
247
246
  inset: 0;
@@ -249,7 +248,7 @@
249
248
  background: transparent;
250
249
  }
251
250
 
252
- /* Screen-reader announcements (keyboard grab / move / drop) */
251
+ /* Screen-reader announcements */
253
252
  :where([data-edit-live]) {
254
253
  position: absolute;
255
254
  width: 1px;
@@ -264,65 +263,9 @@
264
263
 
265
264
  @media (prefers-reduced-motion: reduce) {
266
265
 
267
- :where([data-edit-armed]) > [draggable="true"],
266
+ :where([data-edit-piece]),
268
267
  :where([data-edit-handle])::before {
269
268
  transition: none;
270
269
  }
271
270
  }
272
271
  }
273
-
274
- /* ---- Authoring chrome (toolbar + class/scope menu) ---- */
275
- @layer components {
276
-
277
- /* Presence is script's business — it knows which route is on screen. Whether it
278
- is seen is not, so the toolbar is styled and revealed here. */
279
- :where([data-edit-toolbar]) {
280
- display: none;
281
- position: fixed;
282
- inset-block-end: 1rem;
283
- inset-inline-end: 1rem;
284
- z-index: 9999;
285
- gap: 0.5rem;
286
- align-items: center;
287
-
288
- & button[disabled] {
289
- opacity: 0.35;
290
- pointer-events: none;
291
- }
292
- }
293
-
294
- /* --edit-toolbar: none (any scope up to :root) suppresses it — e.g. a site
295
- that demos .authoring regions but has its own publish flow. */
296
- :root[data-edit-active] :where([data-edit-toolbar]) {
297
- display: var(--edit-toolbar, flex);
298
- }
299
-
300
- /* A popover: the top layer sits above every stacking context, so no handle,
301
- overlay or z-index anywhere on the page can paint over it. */
302
- :where([data-edit-menu]) {
303
- position: fixed;
304
- margin: 0;
305
- width: 16rem;
306
- padding: 0.6rem;
307
- display: flex;
308
- flex-direction: column;
309
- gap: 0.4rem;
310
- background: var(--color-surface-1);
311
- border: 1px solid var(--color-line);
312
- border-radius: var(--radius);
313
- box-shadow: 0 8px 30px rgb(0 0 0 / 0.2);
314
-
315
- & input {
316
- width: 100%;
317
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
318
- font-size: 0.75rem;
319
- }
320
-
321
- & small {
322
- font-size: 0.7rem;
323
- text-transform: uppercase;
324
- letter-spacing: 0.05em;
325
- color: var(--color-content-subtle);
326
- }
327
- }
328
- }