windcraft-editor 0.0.0-stage → 0.1.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.
Files changed (60) hide show
  1. package/README.md +49 -2
  2. package/core/board-layout.js +71 -0
  3. package/core/class-groups.js +210 -0
  4. package/core/design-spec.js +303 -0
  5. package/core/exporters.js +156 -0
  6. package/core/pages.js +39 -0
  7. package/core/prop-tailwind.js +158 -0
  8. package/core/split-sections.js +99 -0
  9. package/core/theme.js +87 -0
  10. package/design/agent.js +2118 -0
  11. package/design/classes-section.js +103 -0
  12. package/design/code-editor.js +293 -0
  13. package/design/code-view.js +158 -0
  14. package/design/color-picker.js +107 -0
  15. package/design/config.js +5 -0
  16. package/design/controls.js +132 -0
  17. package/design/css-props.js +111 -0
  18. package/design/design-tab.js +120 -0
  19. package/design/device-frame.js +40 -0
  20. package/design/editor/add-menu.js +151 -0
  21. package/design/editor/api.js +47 -0
  22. package/design/editor/base.js +55 -0
  23. package/design/editor/code-panel.js +206 -0
  24. package/design/editor/color-ui.js +135 -0
  25. package/design/editor/designs.js +606 -0
  26. package/design/editor/docs.js +121 -0
  27. package/design/editor/frame-handles.js +223 -0
  28. package/design/editor/frame-messages.js +195 -0
  29. package/design/editor/inspector.js +461 -0
  30. package/design/editor/layers.js +61 -0
  31. package/design/editor/pages-panel.js +161 -0
  32. package/design/editor/panel-resize.js +47 -0
  33. package/design/editor/plane.js +132 -0
  34. package/design/editor/present.js +52 -0
  35. package/design/editor/toolbar.js +267 -0
  36. package/design/editor.js +47 -0
  37. package/design/figma-sections.js +440 -0
  38. package/design/float-toolbar.js +163 -0
  39. package/design/group-controls.js +52 -0
  40. package/design/host.js +22 -0
  41. package/design/panel-model.js +130 -0
  42. package/design/panel-state.js +40 -0
  43. package/design/panel.js +212 -0
  44. package/design/presenter.js +312 -0
  45. package/design/prop-sections.js +18 -0
  46. package/design/selection-overlay.js +93 -0
  47. package/design/store.js +29 -0
  48. package/design/style-sections.js +315 -0
  49. package/design/tailwind-classes.js +65 -0
  50. package/embed-frame.js +43 -0
  51. package/embed.html +15 -0
  52. package/embed.js +60 -0
  53. package/index.js +62 -0
  54. package/package.json +50 -5
  55. package/playground/component-source.js +124 -0
  56. package/styles.css +402 -0
  57. package/template.js +247 -0
  58. package/vendor/codemirror.js +24 -0
  59. package/vendor/preact-htm.js +1 -0
  60. package/vendor/prettier.js +171 -0
package/styles.css ADDED
@@ -0,0 +1,402 @@
1
+ /* The editor's own styles (toolbars, panels, the Properties panel, selection handles). Loaded by mountEditor(). */
2
+ html, body { height: 100%; }
3
+ body { margin: 0; overflow: hidden; background: #e5e5e5; color: #333; font: 12px/1.35 Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; -webkit-font-smoothing: antialiased; }
4
+ button { cursor: pointer; background: none; border: 0; padding: 0; font: inherit; color: inherit; }
5
+ /* dots keep their size at every zoom; only their spacing changes (see applyView) */
6
+ #canvas { background-color: #e5e5e5; background-image: radial-gradient(circle, #b4b4b4 0.95px, transparent 1.25px); background-size: 24px 24px; }
7
+ /* pixel grid: one line per design pixel, shown from 400% zoom; lines stay 1 screen pixel thick */
8
+ #pgrid { background-image: linear-gradient(to right, rgba(120, 138, 156, .38) calc(1px * var(--iz, 1)), transparent 0), linear-gradient(to bottom, rgba(120, 138, 156, .38) calc(1px * var(--iz, 1)), transparent 0); background-size: 1px 1px; }
9
+ /* layers */
10
+ #layers { font-size: 11px; }
11
+ .layer-row { height: 24px; display: flex; align-items: center; border-radius: 4px; }
12
+ .layer-row:hover { background: #f5f5f5; }
13
+ .layer-row.is-selected { background: #ededed; color: #111; }
14
+ /* the eye and the lock at the end of a row: faint until the row is pointed at, solid while hidden or locked */
15
+ .layer-acts { margin-left: auto; display: flex; flex: none; align-items: center; gap: 1px; }
16
+ .layer-act { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; border-radius: 4px; color: #737373; opacity: 0; }
17
+ .layer-row:hover .layer-act, .layer-act.on { opacity: 1; }
18
+ .layer-act:hover { background: #e0e0e0; color: #111; }
19
+ .layer-row.layer-hidden > :not(.layer-acts) { opacity: .45; }
20
+ /* the current page stays pinned at the top of the left panel while it scrolls; display:contents lets the row stick against the whole panel, not just the Pages list */
21
+ #pagesList { display: contents; }
22
+ #pagesList .layer-row { margin: 0 4px; }
23
+ /* pinned, top to bottom: the Pages title with its + button, the current page, then the Layers title */
24
+ #leftBody { --pages-head: 31px; --page-row: 24px; }
25
+ #pagesHead { position: sticky; top: 0; z-index: 6; background: #fff; height: var(--pages-head); box-sizing: border-box; }
26
+ #pagesList .layer-row.is-selected { position: sticky; top: var(--pages-head); z-index: 5; box-shadow: 0 0 0 4px #fff; }
27
+ #layersHead { position: sticky; top: calc(var(--pages-head) + var(--page-row)); z-index: 4; background: #fff; }
28
+ [data-path].layer-row { position: relative; }
29
+ .layer-row.is-dragging { opacity: .4; }
30
+ /* where a dragged layer will land: a blue line with a dot (before / after) at the target's indent, or the whole row outlined (inside) */
31
+ .layer-row.drop-before::after, .layer-row.drop-after::after { content: ""; position: absolute; left: var(--ind, 0); right: 2px; height: 2px; border-radius: 1px; background: #111; pointer-events: none; }
32
+ .layer-row.drop-before::after { top: -1px; }
33
+ .layer-row.drop-after::after { bottom: -1px; }
34
+ .layer-row.drop-before::before, .layer-row.drop-after::before { content: ""; position: absolute; left: calc(var(--ind, 0px) - 3px); width: 8px; height: 8px; border-radius: 50%; border: 2px solid #111; background: #fff; box-sizing: border-box; pointer-events: none; z-index: 1; }
35
+ .layer-row.drop-before::before { top: -4px; }
36
+ .layer-row.drop-after::before { bottom: -4px; }
37
+ .layer-row.drop-inside { background: rgba(0,0,0,.07); box-shadow: inset 0 0 0 1.5px #111; }
38
+ .seg button.is-on { background: #111; color: #fff; }
39
+ .field { height: 28px; width: 100%; border: 1px solid #d4d4d4; border-radius: 6px; background: #fff; padding: 0 8px; font-size: 12px; }
40
+ .field:focus { outline: none; border-color: #111; }
41
+ textarea.field { height: auto; padding: 6px 8px; font-family: ui-monospace, monospace; }
42
+
43
+ /* project board: frames live on one infinite plane */
44
+ .fwrap { position: absolute; }
45
+ .flabel { position: absolute; left: 0; bottom: 100%; margin-bottom: 6px; white-space: nowrap; font-size: 12px; color: #555; transform-origin: 0 100%; transform: scale(var(--iz, 1)); pointer-events: none; }
46
+ .flabel span { color: #999; margin-left: 6px; }
47
+ .flabel .fgroup { color: #999; font-style: normal; }
48
+ .group-head { padding: 8px 8px 2px; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: #999; }
49
+ .fwrap.sel .flabel { color: #111; }
50
+ .frame { position: relative; overflow: hidden; background: #fff; border-radius: 2px; box-shadow: 0 1px 3px rgba(0,0,0,.18); cursor: pointer; width: 100%; height: 100%; }
51
+ .fwrap:hover .frame { outline: calc(1px * var(--iz, 1)) solid #c4c4c4; } /* a page you could open: a light hint, not a selection */
52
+ .fwrap.sel .frame { outline: calc(2px * var(--iz, 1)) solid #111; }
53
+ /* the editor: one frame is live, the rest are previews you can click or drag */
54
+ .flabel.drag { pointer-events: auto; cursor: grab; padding: 2px 4px 2px 0; }
55
+ .flabel.drag:active { cursor: grabbing; }
56
+ .fwrap.active .frame { cursor: default; outline: none !important; overflow: visible; }
57
+ .fwrap.active.bodyhover .frame { outline: calc(1px * var(--iz, 1)) solid #c4c4c4 !important; } /* the open frame's empty area under the pointer */
58
+ .fwrap.active.sel .flabel { color: #111; font-weight: 600; }
59
+ .frename { font: inherit; color: #222; background: #fff; border: 1px solid #111; border-radius: 4px; padding: 0 4px; min-width: 140px; outline: none; -webkit-user-select: text; user-select: text; }
60
+ .gshield { position: absolute; inset: 0; z-index: 2; }
61
+ /* the code box and the highlight behind it share every metric, so a character is exactly where its highlight is */
62
+ /* code boxes: a coloured copy (pre.ck) behind a see-through textarea with the same font and spacing */
63
+ .code-layer { margin: 0; padding: 12px; font: 12px/20px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; white-space: pre; tab-size: 4; overflow: auto; letter-spacing: 0; }
64
+ .code-input { color: transparent; caret-color: #111; }
65
+ .code-input::selection { background: rgba(10,132,224,.22); }
66
+ .ck { color: #24292f; }
67
+ .ck .ck-tag { color: #116329; }
68
+ .ck .ck-attr { color: #8250df; }
69
+ .ck .ck-str { color: #0a3069; }
70
+ .ck .ck-com, .ck .ck-doc { color: #6e7781; font-style: italic; }
71
+ .ck .sel { background: rgba(10,132,224,.07); }
72
+ .ck .selt { background: rgba(10,132,224,.16); }
73
+ .code-gutter { margin: 0; padding: 12px 8px 12px 10px; font: 12px/20px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: #b0b0b0; text-align: right; background: #fafafa; border-right: 1px solid #eee; overflow: hidden; user-select: none; min-width: 40px; }
74
+ #codeHl { overflow: hidden; }
75
+ /* the Code tab's element code: wraps to the panel's width and grows with its content, up to a limit */
76
+ .elcode { position: relative; max-height: 320px; overflow: auto; border: 1px solid #e5e5e5; border-radius: 6px; background: #fff; }
77
+ .elcode .code-layer { padding: 8px; font-size: 11px; line-height: 17px; white-space: pre-wrap; overflow-wrap: anywhere; overflow: visible; }
78
+ /* one scroll only: the box scrolls, the inner block is as tall as the code, and the textarea fills that block */
79
+ .elcode .elin { position: relative; }
80
+ .elcode pre { position: relative; min-height: 34px; }
81
+ .elcode textarea.code-layer { position: absolute; inset: 0; width: 100%; height: 100%; resize: none; border: 0; background: transparent; color: transparent; caret-color: #111; outline: none; overflow: hidden; }
82
+ .elcode:focus-within { border-color: #0a84e0; }
83
+ .elhead { display: flex; align-items: center; justify-content: space-between; }
84
+ .elhead button { font-size: 10.5px; color: #555; padding: 1px 6px; border-radius: 4px; }
85
+ .elhead button:hover { background: #f0f0f0; }
86
+ [data-elnote] { margin-top: 4px; }
87
+ [data-elnote].bad { color: #b45309; }
88
+ .dirty-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: #111; margin-left: 6px; vertical-align: middle; }
89
+ .fwrap.dragging { z-index: 50; cursor: grabbing; }
90
+ /* the open frame's content is the live iframe above all the frames: raising its white box would cover it (a blank page while dragging) */
91
+ .fwrap.active.dragging { z-index: auto; }
92
+ .fwrap.dragging .frame { box-shadow: 0 8px 30px rgba(0,0,0,.25); cursor: grabbing; }
93
+
94
+ #canvas, #ftb, #sel { -webkit-user-select: none; user-select: none; }
95
+ #ftb input { -webkit-user-select: text; user-select: text; }
96
+ /* floating controls */
97
+ .pill { background: #fff; border: 1px solid #e5e5e5; border-radius: 12px; box-shadow: 0 4px 16px rgba(0,0,0,.10); }
98
+ .pop { background: #fff; border: 1px solid #e5e5e5; border-radius: 12px; box-shadow: 0 8px 28px rgba(0,0,0,.16); }
99
+ #pillL.tight #designsName { display: none; }
100
+ /* Left panel: Pages and Layers are two sections; the header only keeps Back, the toggle and a menu */
101
+ .sec2 { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px 3px; font-size: 11px; font-weight: 600; color: #8c8c8c; }
102
+ #navMoreBtn { display: none; }
103
+ #leftProject { display: none; }
104
+ #leftHead #leftProject { display: flex; flex: 1 1 auto; width: auto; min-width: 0; margin: 0 2px; }
105
+ #leftHead #navGroup { width: 100%; }
106
+ #leftHead #navGroup > span:nth-of-type(3) { display: none; } /* the second divider (span 1 is the hidden back label) */
107
+ #leftHead #navGroup > .relative { display: flex; align-items: center; margin-left: auto; flex: 0 0 auto; }
108
+ #leftHead #designsBtn { display: none; }
109
+ .dtl { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 28px; padding: 3px 10px; border-radius: 6px; font-size: 12px; color: #222; text-align: left; }
110
+ .dtl:hover { background: #f5f5f5; }
111
+ .dtl .sub2 { margin-left: auto; color: #8c8c8c; font-size: 11px; display: inline-flex; align-items: center; gap: 4px; }
112
+ .dtl .mut { color: #8c8c8c; }
113
+ #leftHead #designsMenu { left: auto; right: 0; width: 224px; }
114
+ #leftHead #designs, #leftHead #newDesignBtn { display: none; } /* the Pages section already lists them */
115
+ .tbtn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 8px; color: #404040; }
116
+ .tbtn:hover { background: #f0f0f0; }
117
+ .tbtn.on { background: #ededed; color: #111; }
118
+ .ptab { padding: 4px 10px; border-radius: 6px; font-size: 12px; font-weight: 500; color: #777; }
119
+ .ptab.is-on { background: #f0f0f0; color: #000; }
120
+ #ftb .tbtn { width: 28px; height: 28px; }
121
+ #ftb select, #ftb input[type=number] { height: 28px; border: 0; background: transparent; border-radius: 8px; font-size: 13px; color: #222; }
122
+ #ftb select:hover, #ftb input[type=number]:hover { background: #f0f0f0; }
123
+ #ftb input[type=number] { width: 52px; text-align: center; -moz-appearance: textfield; }
124
+ #ftb input[type=number]::-webkit-inner-spin-button, #ftb input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
125
+ #ftb .sep { width: 1px; height: 20px; background: #e5e5e5; margin: 0 2px; }
126
+ #ftb .swatch { position: relative; width: 24px; height: 24px; border-radius: 7px; border: 1px solid rgba(0,0,0,.2); overflow: hidden; margin: 0 2px; }
127
+ #ftb .swatch input { position: absolute; inset: -6px; width: 40px; height: 40px; opacity: 0; cursor: pointer; }
128
+ /* the Border / Radius / Spacing panels that open from the bar: sized to sit with its 28px buttons */
129
+ #ftb { position: absolute; }
130
+ #ftb .ftpop { position: absolute; left: 0; bottom: calc(100% + 6px); width: 224px; padding: 10px; background: #fff; border: 1px solid #ececec; border-radius: 14px; box-shadow: 0 8px 28px rgba(0,0,0,.16); display: flex; flex-direction: column; gap: 6px; z-index: 50; }
131
+ #ftb.popdown .ftpop { bottom: auto; top: calc(100% + 6px); }
132
+ #ftb .fttl { font-size: 12px; color: #8c8c8c; padding: 0 2px; }
133
+ #ftb .frow { display: flex; align-items: center; gap: 6px; }
134
+ #ftb .fgrid { display: grid; grid-template-columns: 1fr 1fr 26px; align-items: center; gap: 6px; }
135
+ #ftb .ffield { display: flex; align-items: center; gap: 5px; flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid #e6e6e6; border-radius: 8px; background: #fff; color: #9a9a9a; font-size: 12px; }
136
+ /* no browser or Tailwind focus ring on any of the bar's fields: a focused field just gets a blue border (like Claude Design) and shows its unit */
137
+ #ftb input:focus, #ftb select:focus, #ftb input:focus-visible, #ftb select:focus-visible { outline: none !important; box-shadow: none !important; --tw-ring-shadow: 0 0 #0000 !important; }
138
+ #ftb .ffield:focus-within { border-color: #111; }
139
+ #ftb .ffield:focus-within .fu { display: inline; }
140
+ #ftb input[type=number]:focus, #ftb select:focus { background: #f0f0f0; }
141
+ #ftb .ffield input[type=number]:focus { background: transparent; }
142
+ #ftb .ffield .fl { flex: none; font-size: 12px; display: inline-flex; align-items: center; }
143
+ #ftb .ffield .fl svg { width: 14px; height: 14px; }
144
+ #ftb .ffield .fu { display: none; } /* like Claude Design: just the number, no unit */
145
+ #ftb .ffield input[type=number] { flex: 1; width: 100%; min-width: 0; height: 100%; padding: 0; text-align: left; font-size: 13px; color: #111; background: transparent; }
146
+ #ftb .ffield input[type=number]:hover, #ftb .ffield input[type=number]:focus { background: transparent; outline: none; box-shadow: none; border: 0; } /* the field itself shows the focus ring */
147
+ #ftb .fseg { display: flex; gap: 2px; padding: 2px; background: #f0f0f0; border-radius: 8px; }
148
+ #ftb .fseg button { flex: 1; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 6px; color: #555; }
149
+ #ftb .fseg button svg { width: 14px; height: 14px; }
150
+ #ftb .fseg button:hover { background: rgba(255,255,255,.6); }
151
+ #ftb .fseg button.on { background: #fff; color: #111; box-shadow: 0 1px 3px rgba(0,0,0,.15); }
152
+ #ftb .swatch.big { width: 26px; height: 26px; border-radius: 8px; margin: 0; flex: none; }
153
+ #ftb .fhex { flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid #e6e6e6; border-radius: 8px; font-size: 13px; color: #111; text-transform: uppercase; background: #fff; }
154
+ #ftb .fhex:focus { outline: none; border-color: #111; }
155
+ #ftb .sidebtn { flex: none; width: 26px; height: 26px; border-radius: 8px; color: #444; }
156
+ #ftb .sidebtn svg { width: 14px; height: 14px; }
157
+ #ftb .sidebtn.on { background: #ededed; color: #111; }
158
+ #ftb .lbl2 { font-size: 11px; color: #8c8c8c; padding: 0 2px 0 6px; }
159
+ /* the selected element: drawn above the frame so its edge never clips it; sizes use --iz to stay constant on screen */
160
+ /* the selection inverts whatever is under it (white lines, difference blend), so it reads on light and dark designs alike */
161
+ #lines { mix-blend-mode: difference; } /* only the lines invert; the handles are always black, with a thin white rim so they show on dark designs */
162
+ #lines .sout { position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
163
+ #sel .sbox { position: absolute; box-sizing: border-box; pointer-events: none; transform-origin: 50% 50%; }
164
+ #sel .sh { position: absolute; pointer-events: auto; }
165
+ /* rings are drawn with box-shadow, not border: browsers round a thin border up to a full pixel, which at high zoom turns the handle solid blue */
166
+ #sel .sc { width: 7px; height: 7px; background: #111; box-shadow: 0 0 0 .75px rgba(255,255,255,.8); }
167
+ #sel .sc::after, #sel .sd::after, #fsel .fc::after { content: ""; position: absolute; inset: -5px; } /* the square is small, its grab area is not */
168
+ #sel .sd { width: 8px; height: 8px; border-radius: 50%; background: #111; box-shadow: 0 0 0 .75px rgba(255,255,255,.8); }
169
+ #slbl .slabel.pt { transform: none; }
170
+ #slbl .slabel { position: absolute; transform: translateX(-50%); background: #111; color: #fff; font: 500 11px/1 Inter, system-ui, sans-serif; padding: 4px 6px; border-radius: 4px; white-space: nowrap; pointer-events: none; }
171
+ /* the selected frame: handles scale with the zoom so they keep their size */
172
+ #fsel .fh { position: absolute; pointer-events: auto; }
173
+ #fsel .fbox { position: absolute; box-sizing: border-box; pointer-events: none; }
174
+ #fsel .fc { width: 7px; height: 7px; background: #111; box-shadow: 0 0 0 .75px rgba(255,255,255,.8); }
175
+
176
+ /* inspector */
177
+ #inspector { font-size: 11px; }
178
+ .itabs { display: flex; gap: 2px; padding: 6px 8px; border-bottom: 1px solid #e5e5e5; position: sticky; top: 0; background: #fff; z-index: 5; }
179
+ .itabs button { padding: 5px 9px; border-radius: 5px; font-size: 11px; font-weight: 500; color: #777; }
180
+ .itabs button.on { background: #f0f0f0; color: #000; }
181
+ /* Screen and State: where edits go (design-tab.js VariantBar) */
182
+ .vbar2 { display: flex; flex-direction: column; gap: 5px; padding: 8px 10px 10px; border-bottom: 1px solid #e5e5e5; }
183
+ .vrow { display: flex; align-items: center; gap: 6px; }
184
+ .vl { width: 38px; flex: none; font-size: 10px; color: #8c8c8c; }
185
+ .vchips { display: flex; flex: 1; min-width: 0; gap: 2px; background: #f5f5f5; border-radius: 5px; padding: 2px; }
186
+ .vchip { position: relative; flex: 1; min-width: 0; height: 20px; padding: 0 3px; border-radius: 3px; font-size: 10.5px; color: #555; white-space: nowrap; }
187
+ .vchip:hover { color: #111; }
188
+ .vchip.on { background: #fff; color: #111; box-shadow: 0 0 0 1px #e0e0e0; }
189
+ .vchip i { position: absolute; top: 2px; right: 1px; width: 4px; height: 4px; border-radius: 50%; background: #38bdf8; }
190
+ .vrow:first-child .vchip.on:not(:first-child), .vrow .vchip.on:not(:first-child) { background: #e0f2fe; color: #0369a1; box-shadow: 0 0 0 1px #7dd3fc; }
191
+ .vrow.vtop { align-items: flex-start; }
192
+ .vrow.vtop .vl { padding-top: 5px; }
193
+ .vstates { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
194
+ .vchips.vextra { background: none; padding: 0; flex-wrap: wrap; gap: 3px; }
195
+ .vchips.vextra .vchip { flex: none; padding: 0 7px; background: #f5f5f5; }
196
+ .vchips.vextra .vchip.on { background: #e0f2fe; color: #0369a1; box-shadow: 0 0 0 1px #7dd3fc; }
197
+ .vadd { position: relative; flex: none; }
198
+ .vchip.vplus { width: 22px; flex: none; font-size: 13px; line-height: 1; color: #555; }
199
+ .vpop { position: absolute; right: 0; top: 24px; z-index: 30; width: 218px; max-height: 340px; overflow: auto; padding: 6px 8px 8px; background: #fff; border: 1px solid #e5e5e5; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.12); }
200
+ .vpg { margin: 6px 0 3px; font-size: 9.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: #8c8c8c; }
201
+ .vpg:first-child { margin-top: 0; }
202
+ .vpl { display: flex; flex-wrap: wrap; gap: 3px; }
203
+ .vpi { position: relative; height: 20px; padding: 0 7px; border-radius: 4px; background: #f5f5f5; font-size: 10.5px; color: #333; }
204
+ .vpi:hover { background: #ebebeb; }
205
+ .vpi.on { background: #e0f2fe; color: #0369a1; }
206
+ .vpi i { position: absolute; top: 2px; right: 2px; width: 4px; height: 4px; border-radius: 50%; background: #38bdf8; }
207
+ .vnote.on { margin: 2px 0 0; padding: 6px 8px; border-radius: 6px; background: #e0f2fe; color: #075985; font-size: 10.5px; line-height: 1.45; }
208
+ .vnote.on code { font-weight: 600; }
209
+ .vnote.on button { margin-left: 2px; font-weight: 600; text-decoration: underline; color: inherit; }
210
+ .vbar { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 8px 8px 4px; }
211
+ /* the editor's colour picker (color-picker.js), opened beside the selection */
212
+ .cpick { position: fixed; z-index: 60; display: none; width: 232px; padding: 10px; background: #fff; border: 1px solid #e5e5e5; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,.14); }
213
+ .cp-sv { position: relative; height: 150px; border-radius: 6px; cursor: crosshair; touch-action: none; overflow: hidden; }
214
+ .cp-white, .cp-black { position: absolute; inset: 0; }
215
+ .cp-white { background: linear-gradient(to right, #fff, rgba(255,255,255,0)); }
216
+ .cp-black { background: linear-gradient(to top, #000, rgba(0,0,0,0)); }
217
+ .cp-dot { position: absolute; width: 14px; height: 14px; border: 2px solid #fff; border-radius: 50%; box-shadow: 0 0 0 1px rgba(0,0,0,.35); transform: translate(-50%, -50%); pointer-events: none; }
218
+ .cp-hue { position: relative; height: 12px; margin: 10px 0; border-radius: 6px; cursor: pointer; touch-action: none; background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); }
219
+ .cp-knob { position: absolute; top: 50%; width: 14px; height: 14px; border: 2px solid #fff; border-radius: 50%; box-shadow: 0 0 0 1px rgba(0,0,0,.35); transform: translate(-50%, -50%); pointer-events: none; }
220
+ .cp-row { display: flex; align-items: center; gap: 6px; }
221
+ .cp-prev { width: 26px; height: 26px; flex: none; border-radius: 6px; border: 1px solid rgba(0,0,0,.1); }
222
+ .cp-hex { flex: 1; min-width: 0; height: 26px; padding: 0 8px; border: 1px solid #e0e0e0; border-radius: 6px; font: 12px ui-monospace, Menlo, monospace; color: #222; }
223
+ .cp-hex:focus { outline: none; border-color: #111; }
224
+ .cp-drop { width: 26px; height: 26px; flex: none; display: flex; align-items: center; justify-content: center; border: 1px solid #e0e0e0; border-radius: 6px; color: #444; }
225
+ .cp-drop svg { width: 14px; height: 14px; }
226
+ .cp-drop:hover { background: #f5f5f5; }
227
+ .hintnote { margin: 0 0 6px; padding: 6px 8px; border-radius: 6px; background: #fff7ed; color: #9a3412; font-size: 10.5px; line-height: 1.5; }
228
+ .hintnote button { margin-left: 2px; font-weight: 600; text-decoration: underline; color: inherit; }
229
+ .vnote { padding: 0 10px 6px; color: #111; font-size: 10.5px; margin: 0; }
230
+ .vnote code { background: #e5f4ff; padding: 0 4px; border-radius: 3px; }
231
+ .crumbs { padding: 6px 10px; display: flex; flex-wrap: wrap; gap: 1px; align-items: center; color: #8c8c8c; }
232
+ .crumbs button { padding: 0 3px; border-radius: 3px; }
233
+ .crumbs button:hover { background: #f0f0f0; }
234
+ .crumbs .cur { color: #000; font-weight: 600; }
235
+ .sl { color: #ccc; }
236
+ .sec { border-bottom: 1px solid #e5e5e5; }
237
+ .sech { display: flex; align-items: center; gap: 6px; width: 100%; padding: 9px 10px; font-weight: 600; font-size: 11px; text-align: left; }
238
+ .sech:hover { background: #fafafa; }
239
+ .sech .car { width: 10px; color: #8c8c8c; font-size: 9px; }
240
+ .dot { width: 6px; height: 6px; border-radius: 50%; background: #111; margin-left: auto; }
241
+ .secb { padding: 2px 10px 12px; display: flex; flex-direction: column; gap: 6px; }
242
+ .secb.nopad { padding-top: 10px; }
243
+ .sub { margin: 6px 0 0; color: #8c8c8c; font-weight: 600; font-size: 10px; text-transform: uppercase; letter-spacing: .04em; }
244
+ .sub.first { margin-top: 0; }
245
+ .row { display: flex; align-items: center; gap: 8px; min-width: 0; }
246
+ .row.compact .lbl { width: 14px; }
247
+ .lbl { width: 62px; flex: none; color: #8c8c8c; }
248
+ .row .fi { flex: 1; }
249
+ .fi { height: 24px; min-width: 0; width: 100%; border: 1px solid transparent; background: #f5f5f5; border-radius: 4px; padding: 0 6px; font-size: 11px; color: #222; font-family: inherit; }
250
+ .fi:hover { border-color: #e0e0e0; }
251
+ .fi:focus { outline: none; border-color: #111; background: #fff; }
252
+ textarea.fi.area { height: auto; padding: 6px; line-height: 1.4; resize: vertical; }
253
+ .mono { font-family: ui-monospace, Menlo, monospace; }
254
+ .cell { display: flex; align-items: center; background: #f5f5f5; border-radius: 4px; border: 1px solid transparent; min-width: 0; }
255
+ .cell:hover { border-color: #e0e0e0; }
256
+ .cell:focus-within { border-color: #111; background: #fff; }
257
+ .cell .fi { background: transparent; border: 0; padding-left: 2px; }
258
+ .clbl { width: 46px; flex: none; color: #8c8c8c; padding-left: 6px; font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: clip; }
259
+ .four .clbl { width: 20px; }
260
+ .two { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
261
+ .four { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
262
+ .seg2 { display: flex; flex: 1; background: #f5f5f5; border-radius: 4px; padding: 2px; gap: 2px; min-width: 0; }
263
+ .seg2 button { flex: 1; height: 20px; border-radius: 3px; font-size: 10.5px; color: #555; white-space: nowrap; }
264
+ .seg2 button:hover { color: #000; }
265
+ .seg2 button.on { background: #fff; color: #000; box-shadow: 0 0 0 1px #e0e0e0; }
266
+ .crow { display: flex; align-items: center; gap: 6px; }
267
+ .crow .grow { flex: 1; }
268
+ .crow .op { width: 38px; flex: none; text-align: right; }
269
+ .crow .lbl { width: 62px; }
270
+ .pct { color: #8c8c8c; }
271
+ .sw { width: 22px; height: 22px; border-radius: 4px; border: 1px solid rgba(0,0,0,.15); flex: none; background-color: #fff; background-image: linear-gradient(135deg, transparent 47%, #e5484d 47%, #e5484d 53%, transparent 53%); }
272
+ .sw[class*="bg-"], .sw.swatch-t, .sw[style] { background-image: none; }
273
+ .swatch-t { background: repeating-conic-gradient(#ddd 0 25%, #fff 0 50%) 0 0 / 8px 8px; }
274
+ .pal { padding: 6px; border: 1px solid #e5e5e5; border-radius: 6px; background: #fff; display: flex; flex-direction: column; gap: 2px; }
275
+ .palrow { display: grid; grid-template-columns: repeat(11, 1fr); gap: 2px; align-items: center; }
276
+ .sw2 { aspect-ratio: 1; border-radius: 2px; border: 1px solid rgba(0,0,0,.08); }
277
+ .sw2.on { outline: 2px solid #111; outline-offset: 1px; }
278
+ .sw2.custom { position: relative; overflow: hidden; background: conic-gradient(red, yellow, lime, cyan, blue, magenta, red); }
279
+ .sw2.custom input { opacity: 0; position: absolute; inset: 0; width: 100%; height: 100%; cursor: pointer; }
280
+ .clear { grid-column: span 5; font-size: 10px; color: #666; height: 18px; border-radius: 3px; background: #f5f5f5; }
281
+ /* the element bar under the crumbs: its tag, then move / duplicate / delete as icons */
282
+ .ebar { display: flex; align-items: center; gap: 6px; padding: 0 10px 8px; border-bottom: 1px solid #e5e5e5; }
283
+ .ebar .etag { flex: 1; min-width: 0; }
284
+ .etagv { display: flex; align-items: center; color: #555; }
285
+ .ib.danger { color: #d92d20; }
286
+ .ib.danger:hover { background: #fee2e2; }
287
+ /* the Code tab's class box: chips to edit or remove, and text at the end to add */
288
+ .cbox { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-height: 30px; padding: 4px; background: #f5f5f5; border: 1px solid transparent; border-radius: 5px; cursor: text; }
289
+ .cbox:focus-within { border-color: #111; background: #fff; }
290
+ .cbox .chip { cursor: pointer; background: #fff; box-shadow: 0 0 0 1px #e5e5e5; }
291
+ .cbox:focus-within .chip { background: #f5f5f5; box-shadow: none; }
292
+ .cbox .chip.var { background: #e5f4ff; box-shadow: 0 0 0 1px #bae0fd; }
293
+ .cbedit { font: 10.5px ui-monospace, Menlo, monospace; height: 18px; padding: 0 4px; border: 1px solid #111; border-radius: 4px; outline: none; background: #fff; }
294
+ .cbin { flex: 1; min-width: 90px; height: 20px; border: 0; background: transparent; outline: none; font: 10.5px ui-monospace, Menlo, monospace; color: #111; padding: 0 2px; }
295
+ .chips { display: flex; flex-wrap: wrap; gap: 4px; }
296
+ .chip { display: inline-flex; align-items: center; gap: 3px; background: #f0f0f0; border-radius: 4px; padding: 1px 3px 1px 6px; font: 10.5px ui-monospace, Menlo, monospace; }
297
+ .chip.var { background: #e5f4ff; color: #0a6cc4; }
298
+ .chip button { color: #999; padding: 0 2px; }
299
+ .chip button:hover { color: #d92d20; }
300
+ .kv { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
301
+ .muted { color: #999; font-weight: 400; font-size: 10.5px; margin: 0; }
302
+ .empty { padding: 18px 14px; color: #8c8c8c; }
303
+ .primary { height: 28px; border-radius: 5px; background: #111; color: #fff; font-weight: 600; }
304
+ .primary:hover { background: #0a84e0; }
305
+ .fi.is-set { border-left: 2px solid #111; }
306
+ /* where a property's value comes from: a Tailwind class, CSS (style attribute or the design's stylesheet), or a class on a parent */
307
+ .cinp { width: 24px; height: 24px; flex: none; padding: 0; border: 1px solid #e0e0e0; border-radius: 4px; background: none; cursor: pointer; }
308
+ .hidden { display: none; }
309
+ /* dropdowns: a clear chevron instead of the browser's tiny arrow */
310
+ select.fi, .vbar select { -webkit-appearance: none; appearance: none; padding-right: 20px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%23737373' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 5px center; background-size: 12px; }
311
+ /* a value the element's own styling does not give (a default, or inherited) is faint until it is set */
312
+ .row.unset .fi, .row.unset .seg2, .row.unset .lbl { opacity: .5; }
313
+ .row.unset:hover .fi, .row.unset .fi:focus { opacity: 1; }
314
+
315
+ /* ── Position and Layout, laid out as Figma's (figma-sections.js) ── */
316
+ .fsec { border-bottom: 1px solid #e5e5e5; padding: 10px 10px 12px; display: flex; flex-direction: column; gap: 6px; }
317
+ .fsech { display: flex; align-items: center; justify-content: space-between; height: 20px; font-weight: 600; font-size: 11px; color: #111; }
318
+ .fsx { display: flex; gap: 2px; }
319
+ .fsub { margin: 2px 0 -2px; font-size: 10px; color: #8c8c8c; }
320
+ .fsubn { color: #b4b4b4; }
321
+ .frow { display: flex; align-items: center; gap: 6px; min-width: 0; }
322
+ .frow > .nf { flex: 1; }
323
+ .frow.four { display: grid; grid-template-columns: 1fr 1fr 24px; }
324
+ .frow.four > .ib { grid-column: 3; grid-row: 1; }
325
+ .frow.two2 { display: grid; grid-template-columns: 1fr 1fr; }
326
+ .nf { display: flex; align-items: center; height: 26px; min-width: 0; background: #f5f5f5; border: 1px solid transparent; border-radius: 5px; }
327
+ .nf:hover { border-color: #e0e0e0; }
328
+ .nf:focus-within { border-color: #111; background: #fff; }
329
+ .nfl { flex: none; min-width: 20px; padding: 0 2px 0 7px; color: #8c8c8c; font-size: 10.5px; white-space: nowrap; }
330
+ .nfi { flex: 1; min-width: 0; width: 100%; height: 100%; border: 0; background: transparent; padding: 0 4px 0 2px; font: inherit; font-size: 11px; color: #111; outline: none; }
331
+ .nfu { padding-right: 6px; color: #8c8c8c; }
332
+ .nf .nfi { min-width: 30px; }
333
+ .nfs:not(.wide) { max-width: 46px; padding: 0 13px 0 4px; font-size: 10px; background-position: right 2px center; background-size: 10px; text-overflow: clip; }
334
+ .nfs { flex: none; height: 100%; border: 0; border-left: 1px solid #ebebeb; background-color: transparent; font-size: 10.5px; color: #555; padding: 0 16px 0 6px; -webkit-appearance: none; appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%23737373' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 3px center; background-size: 11px; outline: none; }
335
+ .nf.unset .nfi, .nf.unset .nfl { opacity: .5; }
336
+ .nf.unset:hover .nfi, .nf.unset:focus-within .nfi, .nf.unset:focus-within .nfl { opacity: 1; }
337
+ .nf.off { background: #fafafa; }
338
+ .nf.off .nfi { color: #a3a3a3; }
339
+ .ibrow { display: flex; gap: 6px; }
340
+ .ibg { display: flex; background: #f5f5f5; border-radius: 5px; padding: 1px; flex: none; }
341
+ .ibrow .ibg, .ibg.wide { flex: 1; }
342
+ .ibg .ib { flex: 1; }
343
+ .ib { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; height: 24px; border-radius: 4px; color: #333; }
344
+ .ib:hover { background: #ebebeb; }
345
+ .ib.on { background: #fff; color: #111; box-shadow: 0 0 0 1px #e0e0e0; }
346
+ .ib.faint { color: #c4c4c4; cursor: default; }
347
+ .ib.faint:hover { background: transparent; }
348
+ .lgrid { display: grid; grid-template-columns: 76px 1fr; gap: 6px; }
349
+ .lcol { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
350
+ .abox { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); background: #f5f5f5; border-radius: 5px; height: 58px; padding: 3px; }
351
+ .ad { display: flex; align-items: center; justify-content: center; border-radius: 3px; }
352
+ .ad i { width: 4px; height: 4px; border-radius: 50%; background: #c4c4c4; }
353
+ .ad:hover i { background: #737373; }
354
+ .ad.on i { width: 10px; height: 4px; border-radius: 1.5px; background: #111; }
355
+ .fcheck { display: flex; align-items: center; gap: 6px; font-size: 11px; color: #333; margin-top: 2px; }
356
+ .fcheck.unset { color: #8c8c8c; }
357
+ .fbtns { display: flex; gap: 4px; flex-wrap: wrap; }
358
+ .fbtns button { flex: 1; height: 24px; padding: 0 6px; border-radius: 4px; border: 1px solid #e5e5e5; font-size: 10.5px; background: #fff; white-space: nowrap; }
359
+ .fbtns button:hover { background: #f5f5f5; }
360
+ /* the style sections (style-sections.js): a colour swatch in a field, a dropdown filling a field, an add button with a menu */
361
+ .cin { flex: none; width: 16px; height: 16px; margin-left: 5px; padding: 0; border: 1px solid rgba(0,0,0,.15); border-radius: 3px; background: none; cursor: pointer; }
362
+ .cin::-webkit-color-swatch-wrapper { padding: 0; }
363
+ .cin::-webkit-color-swatch { border: 0; border-radius: 2px; }
364
+ .nfs.wide { flex: 1; min-width: 0; border-left: 0; padding-left: 7px; font-size: 11px; color: #111; background-position: right 6px center; }
365
+ .nf.unset .nfs.wide { opacity: .5; }
366
+ .addsel { position: relative; overflow: hidden; cursor: pointer; }
367
+ .addsel select { position: absolute; inset: 0; opacity: 0; cursor: pointer; font-size: 12px; }
368
+ .pagehead { display: flex; flex-direction: column; gap: 2px; margin: 8px 10px 0; padding: 7px 9px; border-radius: 6px; background: #eef4ff; color: #1e3a8a; font-size: 10.5px; line-height: 1.4; }
369
+ .pagehead b { font-size: 11px; }
370
+ .fsec .row { gap: 6px; }
371
+ /* where a set value comes from: a blue edge for a Tailwind class, amber for CSS */
372
+ .nf { position: relative; }
373
+ /* a field with suggestions (Tailwind colours, sizes...) shows them as you type, without Chrome's ▼ that looks like a dropdown */
374
+ #panel input[list]::-webkit-calendar-picker-indicator { display: none !important; }
375
+ .nf.src-tw { box-shadow: inset 2px 0 0 #38bdf8; }
376
+ .nf.src-css { box-shadow: inset 2px 0 0 #f59e0b; }
377
+ .nf.disp { flex: none; width: 74px; }
378
+ .ib.txt { font-size: 10.5px; padding: 0 6px; color: #555; }
379
+ .ib.txt.on { color: #111; }
380
+ .fsec .crow .lbl { width: 40px; }
381
+ .fsec .row .lbl { width: 56px; }
382
+
383
+ .legend { margin: 6px 10px 2px; font-size: 10.5px; line-height: 1.6; color: #8c8c8c; }
384
+ .legend .edge { display: inline-block; width: 10px; height: 12px; margin: 0 3px -2px 0; border-radius: 2px; background: #f5f5f5; }
385
+ .legend .edge.tw { box-shadow: inset 2px 0 0 #38bdf8; }
386
+ .legend .edge.css { margin-left: 8px; box-shadow: inset 2px 0 0 #f59e0b; }
387
+
388
+ /* mountEditor({ ui }): what an embedded editor leaves out */
389
+ .wc-no-panels #leftPanel, .wc-no-panels #panel, .wc-no-panels #codePanel, .wc-no-panels #leftBtn, .wc-no-panels #panelBtn, .wc-no-panels #codeBtn { display: none !important; }
390
+ .wc-no-nav #navGroup, .wc-no-nav #navSep { display: none !important; }
391
+ .wc-no-add #addBtn { display: none !important; }
392
+
393
+ /* side panels: the edge that resizes them (editor/panel-resize.js) */
394
+ .rz { position: absolute; top: 0; bottom: 0; width: 7px; z-index: 30; cursor: col-resize; touch-action: none; }
395
+ .rz.l { right: -4px; }
396
+ .rz.r { left: -4px; }
397
+ .rz::after { content: ""; position: absolute; top: 0; bottom: 0; left: 3px; width: 1px; background: #0090f1; opacity: 0; transition: opacity .12s; }
398
+ .rz:hover::after, .rz.on::after { opacity: .7; width: 2px; left: 2.5px; }
399
+ /* the arrow tool's pointer over the canvas: the same bold arrow as inside the design (see ARROW in agent.js) */
400
+ #canvas { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M21%2011%202%202l9%2019%202-8z%22%20fill%3D%22%23111%22%20stroke%3D%22%23fff%22%20stroke-width%3D%221.6%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E") 2 2, default; }
401
+ /* with Interact selected, the pointer over the canvas is Interact's own (see INTERACT_CURSOR in docs.js) */
402
+ #canvas[data-tool="interact"] { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cg%20stroke%3D%22%23fff%22%20stroke-width%3D%223.6%22%3E%3Cpath%20d%3D%22M9%209l11%204.5-4.6%201.4L14%2019.5z%22%2F%3E%3Cpath%20d%3D%22M6.5%203.5%207%206M3.5%206.5%206%207M4.5%2010.5l2-1.5M10.5%204.5%209%206.5%22%2F%3E%3C%2Fg%3E%3Cg%20stroke%3D%22%23111%22%20stroke-width%3D%221.6%22%3E%3Cpath%20d%3D%22M9%209l11%204.5-4.6%201.4L14%2019.5z%22%2F%3E%3Cpath%20d%3D%22M6.5%203.5%207%206M3.5%206.5%206%207M4.5%2010.5l2-1.5M10.5%204.5%209%206.5%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") 9 9, default; }