From afdbc42ff872ad05de0c5b279dac7859ea36253f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Tue, 28 Jul 2026 01:55:43 +0300 Subject: [PATCH 1/7] Split host-UI CSS into overlay.css and toolbar.css; parameterise overlay id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rdfa-editor.css no longer contains the annotation overlay or editor toolbar styles — host UIs that provide their own design system (e.g. Bootstrap) should omit overlay.css and toolbar.css and style #rdfa-editor-overlay / #edit-toolbar themselves. - overlay.css: extracted #rdfa-editor-overlay, .rdfa-editor-ui .btn-*, typeahead styles (formerly lines 600–885 of rdfa-editor.css) - toolbar.css: extracted #edit-toolbar layout and button styles (formerly lines 156–283 of rdfa-editor.css) - src/overlay.xsl: add $local:overlay-id variable (default 'rdfa-editor-overlay'); use it in local:render-overlay, local:show-overlay, local:hide-overlay and the ixsl:onkeydown match pattern so host UIs can shadow the id without forking the templates Co-Authored-By: Claude Sonnet 4.6 --- overlay.css | 290 ++++++++++++++++++++++++++++++++++++++++ rdfa-editor.css | 341 ------------------------------------------------ src/overlay.xsl | 10 +- toolbar.css | 57 ++++++++ 4 files changed, 353 insertions(+), 345 deletions(-) create mode 100644 overlay.css create mode 100644 toolbar.css diff --git a/overlay.css b/overlay.css new file mode 100644 index 0000000..a1a0a1f --- /dev/null +++ b/overlay.css @@ -0,0 +1,290 @@ +/* RDFa Editor annotation overlay styles. + Host UIs that provide their own form styling (e.g. Bootstrap) + should omit this file and style #rdfa-editor-overlay themselves. */ + +/* Modern Overlay Styling */ + #rdfa-editor-overlay { + background: linear-gradient(to bottom, #f8f9fa, #ffffff); + border: 2px solid #2196f3; + border-radius: 8px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); + font-family: "Roboto", sans-serif; + min-width: 450px; + max-width: 500px; + z-index: 10000; + } + +.rdfa-editor-ui .overlay-header { + background: linear-gradient(135deg, #2196f3, #1976d2); + color: white; + padding: 16px 20px; + border-radius: 6px 6px 0 0; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + +.rdfa-editor-ui .overlay-header h3 { + margin: 0 0 8px 0; + font-size: 18px; + font-weight: 500; + } + +#rdfa-editor-overlay form { + padding: 20px; + } + +/* Statement (S / P / O) framing */ + .rdfa-editor-ui .statement { + display: grid; + grid-template-columns: 26px 1fr; + gap: 10px 12px; + align-items: center; + background: #e3f2fd; + border-radius: 6px; + padding: 14px 16px; + margin-bottom: 16px; + } + +.rdfa-editor-ui .stmt-role { + font-family: 'Courier New', monospace; + font-weight: bold; + font-size: 12px; + color: white; + background: #2196f3; + border-radius: 50%; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + } + +#stmt-subject { + font-family: 'Courier New', monospace; + font-size: 12px; + color: #1565c0; + word-break: break-all; + } + +/* Advanced fields disclosure */ + #rdfa-editor-overlay details { + border: 1px solid #e0e0e0; + border-radius: 6px; + padding: 8px 16px; + margin-bottom: 16px; + background: white; + } + +#rdfa-editor-overlay summary { + cursor: pointer; + font-size: 13px; + font-weight: 500; + color: #757575; + padding: 4px 0; + } + +#rdfa-editor-overlay details fieldset { + margin-top: 12px; + } + +/* Fieldsets */ + #rdfa-editor-overlay fieldset { + border: 1px solid #e0e0e0; + border-radius: 6px; + padding: 12px 16px; + margin-bottom: 16px; + background: white; + } + +#rdfa-editor-overlay label { + display: block; + font-weight: 500; + color: #424242; + margin-bottom: 8px; + font-size: 14px; + } + +#rdfa-editor-overlay input[type="text"], + #rdfa-editor-overlay select { + width: 100%; + padding: 10px 12px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; + font-family: "Roboto", sans-serif; + transition: border-color 0.2s; + box-sizing: border-box; + } + +#rdfa-editor-overlay input[type="text"]:focus, + #rdfa-editor-overlay select:focus { + outline: none; + border-color: #2196f3; + box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1); + } + +#rdfa-editor-overlay input[type="text"]:disabled { + background-color: #f5f5f5; + color: #999; + } + +.rdfa-editor-ui .helper-text { + display: block; + font-size: 12px; + color: #757575; + margin-top: 6px; + font-style: italic; + } + +/* Action Buttons */ + .rdfa-editor-ui .action-buttons { + display: flex; + gap: 12px; + margin-top: 20px; + justify-content: flex-end; + } + +.rdfa-editor-ui .btn-primary, + .rdfa-editor-ui .btn-secondary, + .rdfa-editor-ui .btn-danger { + padding: 10px 24px; + border: none; + border-radius: 4px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + font-family: "Roboto", sans-serif; + } + +.rdfa-editor-ui .btn-primary { + background: #4caf50; + color: white; + } + +.rdfa-editor-ui .btn-primary:hover { + background: #45a049; + box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3); + } + +.rdfa-editor-ui .btn-secondary { + background: #f5f5f5; + color: #424242; + border: 1px solid #e0e0e0; + } + +.rdfa-editor-ui .btn-secondary:hover { + background: #e0e0e0; + } + +.rdfa-editor-ui .btn-danger { + background: #f44336; + color: white; + margin-right: auto; + } + +.rdfa-editor-ui .btn-danger:hover { + background: #d32f2f; + box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3); + } + +#rdfa-editor-overlay input[name="custom-datatype"] { + margin-top: 8px; + } + +/* Typeahead lookups (property / type) */ + .rdfa-editor-ui .typeahead-field { + position: relative; + display: block; + } + +.rdfa-editor-ui .typeahead-menu { + position: absolute; + top: 100%; + left: 0; + right: 0; + z-index: 10002; + margin: 2px 0 0 0; + padding: 4px; + list-style: none; + background: white; + border: 1px solid #e0e0e0; + border-radius: 6px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); + max-height: 260px; + overflow-y: auto; + } + +.rdfa-editor-ui .typeahead-option { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 10px; + padding: 7px 10px; + border-radius: 4px; + font-size: 14px; + color: #212121; + cursor: pointer; + } + +/* children never intercept the mousedown, so the
  • is always the event target */ + .rdfa-editor-ui .typeahead-option * { + pointer-events: none; + } + +.rdfa-editor-ui .typeahead-option:hover, + .rdfa-editor-ui .typeahead-option[aria-selected="true"] { + background: #e3f2fd; + color: #0d47a1; + } + +.rdfa-editor-ui .typeahead-option-label strong { + font-weight: 700; + } + +.rdfa-editor-ui .typeahead-option-uri { + flex: 0 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 11px; + color: #9e9e9e; + } + +.rdfa-editor-ui .typeahead-value { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 10px 12px; + border: 1px solid #ccc; + border-radius: 4px; + background: white; + font-size: 14px; + font-family: "Roboto", sans-serif; + text-align: left; + cursor: pointer; + } + +.rdfa-editor-ui .typeahead-value:hover { + border-color: #2196f3; + } + +.rdfa-editor-ui .typeahead-label { + flex: 1 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + +.rdfa-editor-ui .typeahead-clear { + flex: 0 0 auto; + color: #9e9e9e; + font-size: 16px; + line-height: 1; + padding: 0 2px; + } + +.rdfa-editor-ui .typeahead-clear:hover { + color: #f44336; + } diff --git a/rdfa-editor.css b/rdfa-editor.css index 60aa7c4..3bd7b8e 100644 --- a/rdfa-editor.css +++ b/rdfa-editor.css @@ -153,11 +153,6 @@ color: #757575; } -#edit-toolbar button:disabled { - opacity: 0.4; - cursor: default; - } - .rdfa-editor-content [contenteditable="true"]:focus { outline: 2px solid rgba(33, 150, 243, 0.35); outline-offset: 2px; @@ -233,55 +228,6 @@ box-shadow: inset 0 0 0 2px #2196f3; } -/* Editor toolbar (injected into the navbar) */ - #edit-toolbar { - order: -1; - margin-right: auto; - display: flex; - gap: 6px; - align-items: center; - } - -#edit-toolbar select, - #edit-toolbar button { - padding: 6px 10px; - border: 1px solid #e0e0e0; - border-radius: 4px; - background: white; - font-size: 13px; - font-family: "Roboto", sans-serif; - cursor: pointer; - } - -#edit-toolbar button:hover { - background: #f5f5f5; - } - -/* Caret-contextual toggle state (aria-pressed toggle buttons) */ - #edit-toolbar button[aria-pressed="true"] { - background: #e3f0ff; - border-color: #6aa3e0; - color: #1a5fb4; - } - -/* Functional groups: thin dividers between clusters, no labels */ - #edit-toolbar .tb-group { - display: flex; - gap: 6px; - align-items: center; - } - -#edit-toolbar .tb-group + .tb-group { - border-left: 1px solid #e0e0e0; - padding-left: 6px; - } - -/* Table-ops cluster switches on when the caret is in a table */ - #edit-toolbar .tb-group.table-ops.active { - background: #e3f0ff; - border-radius: 4px; - } - /* Link / figure dialogs */ .edit-dialog { background: white; @@ -597,293 +543,6 @@ margin-top: 8px; } -/* Modern Overlay Styling */ - #overlay { - background: linear-gradient(to bottom, #f8f9fa, #ffffff); - border: 2px solid #2196f3; - border-radius: 8px; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); - font-family: "Roboto", sans-serif; - min-width: 450px; - max-width: 500px; - z-index: 10000; - } - -.rdfa-editor-ui .overlay-header { - background: linear-gradient(135deg, #2196f3, #1976d2); - color: white; - padding: 16px 20px; - border-radius: 6px 6px 0 0; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - -.rdfa-editor-ui .overlay-header h3 { - margin: 0 0 8px 0; - font-size: 18px; - font-weight: 500; - } - -#overlay form { - padding: 20px; - } - -/* Statement (S / P / O) framing */ - .rdfa-editor-ui .statement { - display: grid; - grid-template-columns: 26px 1fr; - gap: 10px 12px; - align-items: center; - background: #e3f2fd; - border-radius: 6px; - padding: 14px 16px; - margin-bottom: 16px; - } - -.rdfa-editor-ui .stmt-role { - font-family: 'Courier New', monospace; - font-weight: bold; - font-size: 12px; - color: white; - background: #2196f3; - border-radius: 50%; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - } - -#stmt-subject { - font-family: 'Courier New', monospace; - font-size: 12px; - color: #1565c0; - word-break: break-all; - } - -/* Advanced fields disclosure */ - #overlay details { - border: 1px solid #e0e0e0; - border-radius: 6px; - padding: 8px 16px; - margin-bottom: 16px; - background: white; - } - -#overlay summary { - cursor: pointer; - font-size: 13px; - font-weight: 500; - color: #757575; - padding: 4px 0; - } - -#overlay details fieldset { - margin-top: 12px; - } - -/* Fieldsets */ - #overlay fieldset { - border: 1px solid #e0e0e0; - border-radius: 6px; - padding: 12px 16px; - margin-bottom: 16px; - background: white; - } - -#overlay label { - display: block; - font-weight: 500; - color: #424242; - margin-bottom: 8px; - font-size: 14px; - } - -#overlay input[type="text"], - #overlay select { - width: 100%; - padding: 10px 12px; - border: 1px solid #ccc; - border-radius: 4px; - font-size: 14px; - font-family: "Roboto", sans-serif; - transition: border-color 0.2s; - box-sizing: border-box; - } - -#overlay input[type="text"]:focus, - #overlay select:focus { - outline: none; - border-color: #2196f3; - box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1); - } - -#overlay input[type="text"]:disabled { - background-color: #f5f5f5; - color: #999; - } - -.rdfa-editor-ui .helper-text { - display: block; - font-size: 12px; - color: #757575; - margin-top: 6px; - font-style: italic; - } - -/* Action Buttons */ - .rdfa-editor-ui .action-buttons { - display: flex; - gap: 12px; - margin-top: 20px; - justify-content: flex-end; - } - -.rdfa-editor-ui .btn-primary, - .rdfa-editor-ui .btn-secondary, - .rdfa-editor-ui .btn-danger { - padding: 10px 24px; - border: none; - border-radius: 4px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - font-family: "Roboto", sans-serif; - } - -.rdfa-editor-ui .btn-primary { - background: #4caf50; - color: white; - } - -.rdfa-editor-ui .btn-primary:hover { - background: #45a049; - box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3); - } - -.rdfa-editor-ui .btn-secondary { - background: #f5f5f5; - color: #424242; - border: 1px solid #e0e0e0; - } - -.rdfa-editor-ui .btn-secondary:hover { - background: #e0e0e0; - } - -.rdfa-editor-ui .btn-danger { - background: #f44336; - color: white; - margin-right: auto; - } - -.rdfa-editor-ui .btn-danger:hover { - background: #d32f2f; - box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3); - } - -#overlay input[name="custom-datatype"] { - margin-top: 8px; - } - -/* Typeahead lookups (property / type) */ - .rdfa-editor-ui .typeahead-field { - position: relative; - display: block; - } - -.rdfa-editor-ui .typeahead-menu { - position: absolute; - top: 100%; - left: 0; - right: 0; - z-index: 10002; - margin: 2px 0 0 0; - padding: 4px; - list-style: none; - background: white; - border: 1px solid #e0e0e0; - border-radius: 6px; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); - max-height: 260px; - overflow-y: auto; - } - -.rdfa-editor-ui .typeahead-option { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 10px; - padding: 7px 10px; - border-radius: 4px; - font-size: 14px; - color: #212121; - cursor: pointer; - } - -/* children never intercept the mousedown, so the
  • is always the event target */ - .rdfa-editor-ui .typeahead-option * { - pointer-events: none; - } - -.rdfa-editor-ui .typeahead-option:hover, - .rdfa-editor-ui .typeahead-option[aria-selected="true"] { - background: #e3f2fd; - color: #0d47a1; - } - -.rdfa-editor-ui .typeahead-option-label strong { - font-weight: 700; - } - -.rdfa-editor-ui .typeahead-option-uri { - flex: 0 1 auto; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 11px; - color: #9e9e9e; - } - -.rdfa-editor-ui .typeahead-value { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - padding: 10px 12px; - border: 1px solid #ccc; - border-radius: 4px; - background: white; - font-size: 14px; - font-family: "Roboto", sans-serif; - text-align: left; - cursor: pointer; - } - -.rdfa-editor-ui .typeahead-value:hover { - border-color: #2196f3; - } - -.rdfa-editor-ui .typeahead-label { - flex: 1 1 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - -.rdfa-editor-ui .typeahead-clear { - flex: 0 0 auto; - color: #9e9e9e; - font-size: 16px; - line-height: 1; - padding: 0 2px; - } - -.rdfa-editor-ui .typeahead-clear:hover { - color: #f44336; - } - /* Modal Dialog */ #output-modal { position: fixed; diff --git a/src/overlay.xsl b/src/overlay.xsl index ec4226c..7917da0 100644 --- a/src/overlay.xsl +++ b/src/overlay.xsl @@ -11,6 +11,8 @@ version="3.0"> + + - + @@ -310,7 +312,7 @@ version="3.0"> - + diff --git a/toolbar.css b/toolbar.css new file mode 100644 index 0000000..6ce5805 --- /dev/null +++ b/toolbar.css @@ -0,0 +1,57 @@ +/* RDFa Editor toolbar styles (injected into the host navbar). + Host UIs that provide their own button styling (e.g. Bootstrap) + should omit this file and style #edit-toolbar themselves. */ + +#edit-toolbar button:disabled { + opacity: 0.4; + cursor: default; + } + +/* Editor toolbar (injected into the navbar) */ + #edit-toolbar { + order: -1; + margin-right: auto; + display: flex; + gap: 6px; + align-items: center; + } + +#edit-toolbar select, + #edit-toolbar button { + padding: 6px 10px; + border: 1px solid #e0e0e0; + border-radius: 4px; + background: white; + font-size: 13px; + font-family: "Roboto", sans-serif; + cursor: pointer; + } + +#edit-toolbar button:hover { + background: #f5f5f5; + } + +/* Caret-contextual toggle state (aria-pressed toggle buttons) */ + #edit-toolbar button[aria-pressed="true"] { + background: #e3f0ff; + border-color: #6aa3e0; + color: #1a5fb4; + } + +/* Functional groups: thin dividers between clusters, no labels */ + #edit-toolbar .tb-group { + display: flex; + gap: 6px; + align-items: center; + } + +#edit-toolbar .tb-group + .tb-group { + border-left: 1px solid #e0e0e0; + padding-left: 6px; + } + +/* Table-ops cluster switches on when the caret is in a table */ + #edit-toolbar .tb-group.table-ops.active { + background: #e3f0ff; + border-radius: 4px; + } From 918443ff438bb3736c747f7fbef3a7cc7f9bf660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Tue, 28 Jul 2026 11:03:35 +0300 Subject: [PATCH 2/7] =?UTF-8?q?Update=20browser=20tests:=20#overlay=20?= =?UTF-8?q?=E2=86=92=20#rdfa-editor-overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows the rename of the overlay element id introduced in the previous commit (parameterised via $local:overlay-id). Co-Authored-By: Claude Sonnet 4.6 --- tests/browser/datatype.mjs | 28 ++++++++++++++-------------- tests/browser/editor.mjs | 10 +++++----- tests/browser/features.mjs | 8 ++++---- tests/browser/inspector.mjs | 2 +- tests/browser/tables.mjs | 2 +- tests/browser/typeahead-helper.mjs | 6 +++--- tests/browser/typeahead.mjs | 14 +++++++------- 7 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/browser/datatype.mjs b/tests/browser/datatype.mjs index e94df3b..76d181d 100644 --- a/tests/browser/datatype.mjs +++ b/tests/browser/datatype.mjs @@ -16,7 +16,7 @@ page.on('dialog', d => d.accept()); const assert = (name, cond) => { results[name] = cond; if (!cond) errors.push('ASSERT FAILED: ' + name); }; await page.goto(BASE + '/tests/fixture.html'); -await page.waitForSelector('#overlay', { state: 'attached', timeout: 15000 }) +await page.waitForSelector('#rdfa-editor-overlay', { state: 'attached', timeout: 15000 }) .catch(() => errors.push('overlay never rendered')); await page.waitForSelector('#content > * > [data-role=chrome]', { state: 'attached', timeout: 5000 }) .catch(() => errors.push('chrome never injected')); @@ -65,9 +65,9 @@ const spanAttrs = (property) => page.evaluate(p => { await selectText('Q4 2024'); await rightClickSelection(); await typeIri(page, 'property', 'http://purl.org/dc/terms/date'); -await page.selectOption('#overlay select[name=datatype]', XSD + 'date'); -await page.fill('#overlay input[name=value]', '2024-10-01'); -await page.click('#overlay button.spo-action'); +await page.selectOption('#rdfa-editor-overlay select[name=datatype]', XSD + 'date'); +await page.fill('#rdfa-editor-overlay input[name=value]', '2024-10-01'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(150); const dateSpan = await spanAttrs('http://purl.org/dc/terms/date'); @@ -87,8 +87,8 @@ await page.click('#output-modal .modal-close'); await selectText('information'); await rightClickSelection(); await pickTerm(page, 'property', 'http://purl.org/dc/terms/description', 'description'); -await page.fill('#overlay input[name=lang]', 'fr'); -await page.click('#overlay button.spo-action'); +await page.fill('#rdfa-editor-overlay input[name=lang]', 'fr'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(150); const langSpan = await spanAttrs('http://purl.org/dc/terms/description'); @@ -105,13 +105,13 @@ await page.click('#output-modal .modal-close'); // ---- 3. mutual exclusion: a chosen datatype disables the language input -------- await selectText('graduated'); await rightClickSelection(); -await page.selectOption('#overlay select[name=datatype]', XSD + 'integer'); +await page.selectOption('#rdfa-editor-overlay select[name=datatype]', XSD + 'integer'); assert('mutex.langDisabledWithDatatype', - await page.evaluate(() => document.querySelector('#overlay input[name=lang]').disabled === true)); -await page.selectOption('#overlay select[name=datatype]', ''); + await page.evaluate(() => document.querySelector('#rdfa-editor-overlay input[name=lang]').disabled === true)); +await page.selectOption('#rdfa-editor-overlay select[name=datatype]', ''); assert('mutex.langEnabledWithoutDatatype', - await page.evaluate(() => document.querySelector('#overlay input[name=lang]').disabled === false)); -await page.click('#overlay button.cancel-action'); + await page.evaluate(() => document.querySelector('#rdfa-editor-overlay input[name=lang]').disabled === false)); +await page.click('#rdfa-editor-overlay button.cancel-action'); await page.waitForTimeout(100); // ---- 4. edit round-trip: prefill, then clear the datatype ---------------------- @@ -125,10 +125,10 @@ await rightClickSelection(); assert('edit.detailsAutoOpened', await page.evaluate(() => document.getElementById('advanced-fields').open === true)); assert('edit.datatypePrefilled', - await page.evaluate(dt => document.querySelector('#overlay select[name=datatype]').value === dt, XSD + 'date')); + await page.evaluate(dt => document.querySelector('#rdfa-editor-overlay select[name=datatype]').value === dt, XSD + 'date')); // clear the datatype -> plain literal, keeping @content -await page.selectOption('#overlay select[name=datatype]', ''); -await page.click('#overlay button.spo-action'); +await page.selectOption('#rdfa-editor-overlay select[name=datatype]', ''); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(150); const cleared = await spanAttrs('http://purl.org/dc/terms/date'); assert('edit.datatypeClearedOnRemove', cleared !== null && cleared.datatype === null); diff --git a/tests/browser/editor.mjs b/tests/browser/editor.mjs index b657c2f..25cf2c6 100644 --- a/tests/browser/editor.mjs +++ b/tests/browser/editor.mjs @@ -315,7 +315,7 @@ await page.evaluate(() => { await page.locator('#content > p', { hasText: 'official website' }).click({ button: 'right', position: { x: 25, y: 8 } }); await page.waitForTimeout(300); await pickTerm(page, 'property', 'http://purl.org/dc/terms/description', 'description'); -await page.click('#overlay button.spo-action'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.click('#parse-rdf'); await page.waitForTimeout(300); const rdf = await page.evaluate(() => document.getElementById('output-content').textContent); @@ -340,16 +340,16 @@ results.blockAnnotation = { getComputedStyle(document.getElementById('overlay')).display !== 'none'), // edit pre-fill renders the committed typeahead button carrying the IRI propertyPrefilled: await page.evaluate(() => - document.querySelector('#overlay .typeahead-field[data-field=property] input[type=hidden]')?.value + document.querySelector('#rdfa-editor-overlay .typeahead-field[data-field=property] input[type=hidden]')?.value === 'http://purl.org/dc/terms/title'), // the value field must not leak the block's chrome (⠿) glyph valueClean: await page.evaluate(() => - document.querySelector('#overlay input[name=value]').value === 'Demo document'), + document.querySelector('#rdfa-editor-overlay input[name=value]').value === 'Demo document'), removeShown: await page.evaluate(() => - getComputedStyle(document.querySelector('#overlay button.remove-action')).display !== 'none'), + getComputedStyle(document.querySelector('#rdfa-editor-overlay button.remove-action')).display !== 'none'), }; // Remove strips the RDFa attributes but keeps the heading (does not unwrap it) -await page.click('#overlay button.remove-action'); +await page.click('#rdfa-editor-overlay button.remove-action'); await page.waitForTimeout(200); results.blockAnnotation.headingKept = await page.evaluate(() => !!document.querySelector('#content > h1') && diff --git a/tests/browser/features.mjs b/tests/browser/features.mjs index fe177db..4a53224 100644 --- a/tests/browser/features.mjs +++ b/tests/browser/features.mjs @@ -114,7 +114,7 @@ await page.evaluate(() => { await page.locator('#content > p', { hasText: 'official website' }).click({ button: 'right', position: { x: 25, y: 8 } }); await page.waitForTimeout(300); await pickTerm(page, 'property', 'http://purl.org/dc/terms/description', 'description'); -await page.click('#overlay button.spo-action'); +await page.click('#rdfa-editor-overlay button.spo-action'); const spanCount = await page.evaluate(() => document.querySelectorAll('#content span[property="http://purl.org/dc/terms/description"]').length); await caretInText('#content > p:first-of-type'); await page.keyboard.press(undoKey); @@ -254,9 +254,9 @@ await page.waitForTimeout(300); // its input is unchanged. This exercises the same surfacing pipeline (badge/squiggle/ // modal/canonical/undo). Open the advanced disclosure to reach the datatype control. await page.evaluate(() => { document.getElementById('advanced-fields').open = true; }); -await page.selectOption('#overlay select[name=datatype]', 'urn:rdfa-editor:custom'); -await page.fill('#overlay input[name=custom-datatype]', 'nmae'); -await page.click('#overlay button.spo-action'); +await page.selectOption('#rdfa-editor-overlay select[name=datatype]', 'urn:rdfa-editor:custom'); +await page.fill('#rdfa-editor-overlay input[name=custom-datatype]', 'nmae'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(200); results.lint.badgeShown = await page.evaluate(() => getComputedStyle(document.getElementById('lint-badge')).display !== 'none' diff --git a/tests/browser/inspector.mjs b/tests/browser/inspector.mjs index 6dd7ea2..26eba4d 100644 --- a/tests/browser/inspector.mjs +++ b/tests/browser/inspector.mjs @@ -13,7 +13,7 @@ page.on('dialog', d => d.accept()); const assert = (name, cond) => { results[name] = cond; if (!cond) errors.push('ASSERT FAILED: ' + name); }; await page.goto(BASE + '/tests/fixture.html'); -await page.waitForSelector('#overlay', { state: 'attached', timeout: 15000 }) +await page.waitForSelector('#rdfa-editor-overlay', { state: 'attached', timeout: 15000 }) .catch(() => errors.push('overlay never rendered')); await page.waitForSelector('#content > * > [data-role=chrome]', { state: 'attached', timeout: 5000 }) .catch(() => errors.push('chrome never injected')); diff --git a/tests/browser/tables.mjs b/tests/browser/tables.mjs index 824df35..ea0bce2 100644 --- a/tests/browser/tables.mjs +++ b/tests/browser/tables.mjs @@ -286,7 +286,7 @@ await page.locator('#content table > tbody > tr:first-child > td').first() .click({ button: 'right', position: { x: 8, y: 8 } }); await page.waitForTimeout(300); await pickTerm(page, 'property', 'http://purl.org/dc/terms/description', 'description'); -await page.click('#overlay button.spo-action'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(120); assert('cellAnnotation', await page.evaluate(() => !!document.querySelector('#content table td span[property="http://purl.org/dc/terms/description"]'))); diff --git a/tests/browser/typeahead-helper.mjs b/tests/browser/typeahead-helper.mjs index b2a6be0..6124988 100644 --- a/tests/browser/typeahead-helper.mjs +++ b/tests/browser/typeahead-helper.mjs @@ -1,10 +1,10 @@ // Shared drivers for the property/type typeahead fields in the annotation overlay. -// A field is #overlay .typeahead-field[data-field=property|typeof]; it is either in +// A field is #rdfa-editor-overlay .typeahead-field[data-field=property|typeof]; it is either in // the typing state (input.typeahead-input + ul.typeahead-menu) or committed // (button.typeahead-value). These helpers normalise to the typing state first. export async function openTypeahead(page, field) { - const wrap = `#overlay .typeahead-field[data-field=${field}]`; + const wrap = `#rdfa-editor-overlay .typeahead-field[data-field=${field}]`; if (await page.locator(`${wrap} .typeahead-value`).count()) await page.click(`${wrap} .typeahead-value`); // committed -> re-open for editing return wrap; @@ -33,5 +33,5 @@ export async function typeIri(page, field, iri) { // the committed IRI a field currently holds (hidden input inside the button), or '' export async function committedIri(page, field) { return page.evaluate(f => document.querySelector( - `#overlay .typeahead-field[data-field=${f}] input[type=hidden]`)?.value ?? '', field); + `#rdfa-editor-overlay .typeahead-field[data-field=${f}] input[type=hidden]`)?.value ?? '', field); } diff --git a/tests/browser/typeahead.mjs b/tests/browser/typeahead.mjs index 2609cd7..833aab5 100644 --- a/tests/browser/typeahead.mjs +++ b/tests/browser/typeahead.mjs @@ -14,15 +14,15 @@ page.on('dialog', d => d.accept()); const assert = (name, cond) => { results[name] = cond; if (!cond) errors.push('ASSERT FAILED: ' + name); }; await page.goto(BASE + '/tests/fixture.html'); -await page.waitForSelector('#overlay', { state: 'attached', timeout: 15000 }) +await page.waitForSelector('#rdfa-editor-overlay', { state: 'attached', timeout: 15000 }) .catch(() => errors.push('overlay never rendered')); await page.waitForSelector('#content > * > [data-role=chrome]', { state: 'attached', timeout: 5000 }) .catch(() => errors.push('chrome never injected')); const FOAF = 'http://xmlns.com/foaf/0.1/'; const TITLE = 'http://purl.org/dc/terms/title'; -const pWrap = '#overlay .typeahead-field[data-field=property]'; -const tWrap = '#overlay .typeahead-field[data-field=typeof]'; +const pWrap = '#rdfa-editor-overlay .typeahead-field[data-field=property]'; +const tWrap = '#rdfa-editor-overlay .typeahead-field[data-field=typeof]'; const pIn = `${pWrap} input.typeahead-input`; const menuOpen = (w) => page.evaluate(s => getComputedStyle(document.querySelector(s + ' .typeahead-menu')).display !== 'none', w); @@ -61,7 +61,7 @@ const rightClickSelection = async () => { }; const annotate = async (substr) => { await selectText(substr); await rightClickSelection(); }; -const cancel = async () => { await page.click('#overlay button.cancel-action'); await page.waitForTimeout(80); }; +const cancel = async () => { await page.click('#rdfa-editor-overlay button.cancel-action'); await page.waitForTimeout(80); }; // select an existing annotation (the h1 that carries @property=dct:title) and open it const openTitleEdit = async () => { @@ -111,7 +111,7 @@ await cancel(); // ---- 4. a full IRI typed directly is accepted as a free entry ------------------- await annotate('company'); await typeIri(page, 'property', 'https://schema.org/birthDate'); -await page.click('#overlay button.spo-action'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(120); assert('freeIri.written', await page.evaluate(() => !!document.querySelector('#content span[property="https://schema.org/birthDate"]'))); @@ -121,7 +121,7 @@ await annotate('graduated'); await page.fill(pIn, 'notaterm'); await page.waitForTimeout(80); assert('nonIri.notCommitted', await committedIri(page, 'property') === ''); -await page.click('#overlay button.spo-action'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(120); assert('nonIri.noProperty', await page.evaluate(() => !document.querySelector('#content [property="notaterm"]'))); @@ -149,7 +149,7 @@ await cancel(); await openTitleEdit(); await page.click(`${pWrap} .typeahead-value`); await page.waitForTimeout(80); -await page.click('#overlay button.spo-action'); +await page.click('#rdfa-editor-overlay button.spo-action'); await page.waitForTimeout(120); assert('stale.untouchedPreserves', await page.evaluate(t => document.querySelector('#content > h1')?.getAttribute('property') === t, TITLE)); From 2d44823e985e85e25325035764b7726711905784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Tue, 28 Jul 2026 11:53:20 +0300 Subject: [PATCH 3/7] Extract .edit-dialog styles to dialogs.css (host-UI split) Move toolbar insertion dialog styles (link, table, figure) into a separate dialogs.css alongside overlay.css and toolbar.css. Host UIs that provide their own form styling should omit this file and style .edit-dialog themselves. Co-Authored-By: Claude Sonnet 4.6 --- dialogs.css | 43 +++++++++++++++++++++++++++++++++++++++++++ rdfa-editor.css | 39 --------------------------------------- 2 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 dialogs.css diff --git a/dialogs.css b/dialogs.css new file mode 100644 index 0000000..9be8ecc --- /dev/null +++ b/dialogs.css @@ -0,0 +1,43 @@ +/* RDFa Editor toolbar dialog styles (insert link, table, figure). + Host UIs that provide their own form styling (e.g. Bootstrap) + should omit this file and style .edit-dialog themselves. */ + +/* Link / figure dialogs */ +.edit-dialog { + background: white; + border: 2px solid #2196f3; + border-radius: 8px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); + padding: 16px; + min-width: 320px; + z-index: 10000; +} + +.edit-dialog label { + display: block; + font-size: 13px; + font-weight: 500; + color: #424242; + margin: 8px 0 4px 0; +} + +.edit-dialog input[type="text"] { + width: 100%; + padding: 8px 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; + box-sizing: border-box; +} + +.edit-dialog .rdfa-editor-ui .action-buttons { + margin-top: 14px; +} + +.edit-dialog .rdfa-editor-ui .checkbox-label { + display: inline-flex; + gap: 6px; + align-items: center; + font-weight: 400; + margin-top: 8px; +} diff --git a/rdfa-editor.css b/rdfa-editor.css index 3bd7b8e..bbae229 100644 --- a/rdfa-editor.css +++ b/rdfa-editor.css @@ -228,37 +228,6 @@ box-shadow: inset 0 0 0 2px #2196f3; } -/* Link / figure dialogs */ - .edit-dialog { - background: white; - border: 2px solid #2196f3; - border-radius: 8px; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); - padding: 16px; - min-width: 320px; - z-index: 10000; - } - -.edit-dialog label { - display: block; - font-size: 13px; - font-weight: 500; - color: #424242; - margin: 8px 0 4px 0; - } - -.edit-dialog input[type="text"] { - width: 100%; - padding: 8px 10px; - border: 1px solid #ccc; - border-radius: 4px; - font-size: 14px; - box-sizing: border-box; - } - -.edit-dialog .rdfa-editor-ui .action-buttons { - margin-top: 14px; - } /* Slash menu (/ to insert a block) */ #slash-menu { @@ -535,14 +504,6 @@ text-decoration-skip-ink: none; } -.edit-dialog .rdfa-editor-ui .checkbox-label { - display: inline-flex; - gap: 6px; - align-items: center; - font-weight: 400; - margin-top: 8px; - } - /* Modal Dialog */ #output-modal { position: fixed; From 78983c89ca1c4541f2bf77a364385269a68f48c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Tue, 28 Jul 2026 11:54:41 +0300 Subject: [PATCH 4/7] Fix remaining getElementById('overlay') references in browser tests The earlier selector sweep only caught #overlay CSS selectors; three files still used getElementById('overlay') in JS evaluate() callbacks, causing getComputedStyle() to receive null and crash the test runner. Co-Authored-By: Claude Sonnet 4.6 --- tests/browser/editor.mjs | 2 +- tests/browser/hardening.mjs | 4 ++-- tests/browser/multiinstance.mjs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/browser/editor.mjs b/tests/browser/editor.mjs index 25cf2c6..9a0ee2c 100644 --- a/tests/browser/editor.mjs +++ b/tests/browser/editor.mjs @@ -337,7 +337,7 @@ await page.locator('#content > h1').click({ button: 'right', position: { x: 60, await page.waitForTimeout(300); results.blockAnnotation = { editorOpened: await page.evaluate(() => - getComputedStyle(document.getElementById('overlay')).display !== 'none'), + getComputedStyle(document.getElementById('rdfa-editor-overlay')).display !== 'none'), // edit pre-fill renders the committed typeahead button carrying the IRI propertyPrefilled: await page.evaluate(() => document.querySelector('#rdfa-editor-overlay .typeahead-field[data-field=property] input[type=hidden]')?.value diff --git a/tests/browser/hardening.mjs b/tests/browser/hardening.mjs index 6ed14ba..53d77f1 100644 --- a/tests/browser/hardening.mjs +++ b/tests/browser/hardening.mjs @@ -114,7 +114,7 @@ await page.locator('#content > p', { hasText: 'official website' }).click({ butt await page.waitForTimeout(200); await page.keyboard.press('Escape'); results.escape.overlayClosed = await page.evaluate(() => - getComputedStyle(document.getElementById('overlay')).display === 'none'); + getComputedStyle(document.getElementById('rdfa-editor-overlay')).display === 'none'); // Alt+ArrowDown moves the block; undo restores const orderBefore = await page.evaluate(() => [...document.getElementById('content').children].map(e => e.tagName).join(',')); @@ -131,7 +131,7 @@ results.altMove = { results.aria = await page.evaluate(() => ({ toolbar: document.getElementById('edit-toolbar').getAttribute('role') === 'toolbar', buttonsLabelled: [...document.querySelectorAll('#edit-toolbar button')].every(b => b.getAttribute('aria-label')), - overlayDialog: document.getElementById('overlay').getAttribute('role') === 'dialog', + overlayDialog: document.getElementById('rdfa-editor-overlay').getAttribute('role') === 'dialog', dialogsModal: ['link-dialog', 'figure-dialog', 'find-dialog'].every(id => document.getElementById(id).getAttribute('aria-modal') === 'true'), tocNav: document.getElementById('toc-drawer').getAttribute('role') === 'navigation', diff --git a/tests/browser/multiinstance.mjs b/tests/browser/multiinstance.mjs index 6700666..a494d1b 100644 --- a/tests/browser/multiinstance.mjs +++ b/tests/browser/multiinstance.mjs @@ -123,7 +123,7 @@ await page.locator('#content > p', { hasText: 'official website' }).first() .click({ button: 'right', position: { x: 30, y: 8 } }); await page.waitForTimeout(200); results.selectionHint = await page.evaluate(() => ({ - overlayShown: getComputedStyle(document.getElementById('overlay')).display !== 'none', + overlayShown: getComputedStyle(document.getElementById('rdfa-editor-overlay')).display !== 'none', hintBoxes: document.querySelectorAll('.rdfa-editor-selection-hint').length > 0, })); await page.keyboard.press('Escape'); From b084df8d560f4100e5270477c089fdc21573e71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Tue, 28 Jul 2026 12:07:31 +0300 Subject: [PATCH 5/7] Load overlay.css, toolbar.css and dialogs.css in demo index.html These files were split out of rdfa-editor.css; the demo page must load all three so the annotation overlay, editor toolbar and insertion dialogs are styled correctly for non-Bootstrap users. Co-Authored-By: Claude Sonnet 4.6 --- index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.html b/index.html index 54e1bfc..6dac44f 100644 --- a/index.html +++ b/index.html @@ -29,6 +29,9 @@ } + + +