diff --git a/web/canvas.ts b/web/canvas.ts index a23c7e2..61e7653 100644 --- a/web/canvas.ts +++ b/web/canvas.ts @@ -94,6 +94,38 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void { /** Everything to undo, in the order it was done. */ const undo: (() => void)[] = []; + /* + A stylesheet, for the one thing inline styles cannot express: `:hover`. + + The component tools sat permanently at the top-right of whatever they belonged to, on top of the + text. On a paragraph that fills its column there is nowhere for them to go that is not on top of + something — so they are hidden until the component is hovered, which is when they are wanted and + the only time they are. + + The outline goes the same way. An editor should show the page, with the handles arriving under the + cursor; an editor that outlines everything all the time is showing you the editor. + */ + const sheet = document.createElement('style'); + sheet.setAttribute(MARK, 'stylesheet'); + sheet.textContent = ` + [data-fofos-component] { outline: 1px dotted transparent; transition: outline-color .12s; } + [data-fofos-component]:hover { outline-color: color-mix(in srgb, ${ACCENT} 55%, transparent); } + [${MARK}="component-tools"] { opacity: 0; transition: opacity .12s; } + [data-fofos-component]:hover > [${MARK}="component-tools"] { opacity: 1; } + [${MARK}="component-tools"]:focus-within { opacity: 1; } + + /* + The section's Delete, same treatment. Its NAME stays visible — that is what tells you which band + you are looking at, and it sits above the section rather than on it — but a destructive verb does + not need to be on screen at all times. + */ + [${MARK}="section-delete"] { opacity: 0; transition: opacity .12s; } + [data-section-id]:hover [${MARK}="section-delete"] { opacity: 1; } + [${MARK}="section-delete"]:focus { opacity: 1; } + `; + document.head.append(sheet); + undo.push(() => sheet.remove()); + /* The site's empty-page placeholder, hidden while the builder draws its own controls in that space. @@ -166,6 +198,7 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void { style(shape, ['opacity:0.7']); const remove = document.createElement('button'); + remove.setAttribute(MARK, 'section-delete'); remove.type = 'button'; remove.textContent = 'Delete'; style(remove, [ @@ -217,27 +250,31 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void { if (!node) continue; const wasPosition = node.style.position; - const wasOutline = node.style.outline; node.style.position = 'relative'; - node.style.outline = `1px dotted color-mix(in srgb, ${ACCENT} 60%, transparent)`; + // The outline and the tools' visibility are the stylesheet's, so they can depend on :hover. + node.setAttribute('data-fofos-component', ''); undo.push(() => { node.style.position = wasPosition; - node.style.outline = wasOutline; + node.removeAttribute('data-fofos-component'); }); const tools = document.createElement('div'); tools.setAttribute(MARK, 'component-tools'); style(tools, [ 'position:absolute', - 'top:0', + // Above its own top-right corner rather than inside it, so even while visible it sits in the + // gap between components instead of over the first line of this one. + 'top:-1.25rem', 'right:0', 'display:flex', 'gap:0.5rem', - 'padding:0.125rem 0.25rem', + 'padding:0 0.25rem', 'font-size:0.625rem', 'font-family:ui-sans-serif, system-ui, sans-serif', `color:${ACCENT}`, 'background:var(--site-bg, #fff)', + // Above the page, below the section bar, and never a target when invisible. + 'z-index:5', ]); for (const [label, run] of [