diff --git a/web/canvas.ts b/web/canvas.ts index 61e7653..a9d074c 100644 --- a/web/canvas.ts +++ b/web/canvas.ts @@ -108,20 +108,50 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void { const sheet = document.createElement('style'); sheet.setAttribute(MARK, 'stylesheet'); sheet.textContent = ` + /* + EVERYTHING belonging to a section appears when that section is hovered. + + Its outline, its name, its shape, its Delete, the outlines of its columns and the button that adds + to them. A page with four bands was four labelled boxes at rest, and the page underneath was the + one thing you could not see — which is what an editor is for. + + Opacity rather than display, so nothing moves when it appears: the controls hold their space + whether or not they are visible, and the page does not reflow under the cursor. + */ + [data-section-id] { + outline: 1px solid transparent; + outline-offset: -1px; + transition: outline-color .12s; + } + [data-section-id]:hover { outline-color: ${ACCENT}; } + + [${MARK}="section-bar"] { opacity: 0; transition: opacity .12s; } + [data-section-id]:hover [${MARK}="section-bar"] { opacity: 1; } + /* Keyboard reaches them too: focus inside a band is the same intent as hovering it. */ + [data-section-id]:focus-within [${MARK}="section-bar"] { opacity: 1; } + + [data-column] { + outline: 1px dashed transparent; + outline-offset: -5px; + transition: outline-color .12s; + } + [data-section-id]:hover [data-column] { + outline-color: color-mix(in srgb, ${ACCENT} 45%, transparent); + } + + [${MARK}="add-component"] { opacity: 0; transition: opacity .12s; } + [data-section-id]:hover [${MARK}="add-component"] { opacity: 1; } + [${MARK}="add-component"]:focus { opacity: 1; } + + /* + A COMPONENT's own handles need their own hover, not the section's. A band with three paragraphs + would otherwise show three sets of Edit and Delete at once, and none of them would say which. + */ [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()); @@ -152,24 +182,17 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void { if (!element) continue; const previousPosition = element.style.position; - const previousOutline = element.style.outline; - const previousOffset = element.style.outlineOffset; const previousMargin = element.style.margin; // `relative` so the label and the controls can be placed against this band rather than the page. element.style.position = 'relative'; - element.style.outline = `1px solid ${ACCENT}`; - // INSET. At `4px` the outline sat outside the element, so a band that fills the content column drew - // its edges past the measure — the guides said the column ended and the skeleton crossed them. - // Editor chrome that overflows the thing it is describing is chrome that lies about it. - element.style.outlineOffset = '-1px'; - // Room for the outline and the label above it, so two bands in a row do not touch. + // Room for the outline and the label above it, so two bands in a row do not touch. Kept inline + // because it is layout and must hold whether or not anything is hovered — the outline itself is in + // the stylesheet, INSET, so a band filling the column never draws past the measure. element.style.margin = '2.5rem 0'; undo.push(() => { element.style.position = previousPosition; - element.style.outline = previousOutline; - element.style.outlineOffset = previousOffset; element.style.margin = previousMargin; }); @@ -198,7 +221,6 @@ 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, [ @@ -228,15 +250,6 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void { the wrong one. */ element.querySelectorAll('[data-column]').forEach((columnElement, columnIndex) => { - const was = columnElement.style.outline; - const wasOffset = columnElement.style.outlineOffset; - columnElement.style.outline = `1px dashed color-mix(in srgb, ${ACCENT} 45%, transparent)`; - columnElement.style.outlineOffset = '-5px'; - undo.push(() => { - columnElement.style.outline = was; - columnElement.style.outlineOffset = wasOffset; - }); - /* Every component in this column gets a handle.