From 99f1688eaee7c0cca577130a84e86b33c38f6749 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Thu, 21 Aug 2025 13:13:59 +0100 Subject: [PATCH 1/7] Make caret and placeholder use highlighted text's base colour or preferably color CSS of code-input element --- code-input.css | 20 ++++++++++++++------ code-input.js | 15 ++++++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/code-input.css b/code-input.css index 1795174..835af1d 100644 --- a/code-input.css +++ b/code-input.css @@ -20,7 +20,13 @@ code-input { top: 0; left: 0; - color: black; + /* CSS variables rather than inline styles used for values synced from JavaScript + to keep low precedence and thus overridability + The variables may change and are for internal use. */ + --code-input_highlight-text-color: black; /* Set by JS to be base text color of pre code element */ + color: var(--code-input_highlight-text-color, black); + --code-input_default-caret-color: black; /* Set by JS to be same as color property */ + caret-color: var(--code-input_default-caret-color, black); /* Set by JS to be equal to color property */ background-color: white; /* Normal inline styles */ @@ -36,7 +42,6 @@ code-input { text-align: start; line-height: 1.5; /* Inherited to child elements */ tab-size: 2; - caret-color: darkgrey; white-space: pre; padding: 0!important; /* Use --padding to set the code-input element's padding */ display: grid; @@ -118,12 +123,13 @@ code-input pre { /* Make textarea almost completely transparent, except for caret and placeholder */ code-input textarea:not([data-code-input-fallback]) { - color: transparent; background: transparent; - caret-color: inherit!important; /* Or choose your favourite color */ + color: transparent; + caret-color: inherit; /* Or choose your favourite color */ } -code-input textarea::placeholder { - color: lightgrey; +code-input textarea:not([data-code-input-fallback]):placeholder-shown { + /* Show placeholder */ + color: inherit; } /* Can be scrolled */ @@ -252,6 +258,8 @@ code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) texta code-input:has(textarea[data-code-input-fallback]) { padding: 0!important; /* Padding now in the textarea */ box-sizing: content-box; + + caret-color: revert; /* JS not setting the colour since no highlighting */ } code-input textarea[data-code-input-fallback] { overflow: auto; diff --git a/code-input.js b/code-input.js index 42b5ae2..6ff64c4 100644 --- a/code-input.js +++ b/code-input.js @@ -506,6 +506,18 @@ var codeInput = { this.needsHighlight = false; } + // Synchronise colors + if(this.textareaElement) { + let color; + if(this.templateObject.preElementStyled) { + color = getComputedStyle(this.preElement).color; + } else { + color = getComputedStyle(this.codeElement).color; + } + this.style.setProperty("--code-input_highlight-text-color", color); + } + this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); + window.requestAnimationFrame(this.animateFrame.bind(this)); } @@ -536,11 +548,9 @@ var codeInput = { syncSize() { // Synchronise the size of the pre/code and textarea elements if(this.templateObject.preElementStyled) { - this.style.backgroundColor = getComputedStyle(this.preElement).backgroundColor; this.textareaElement.style.height = getComputedStyle(this.preElement).height; this.textareaElement.style.width = getComputedStyle(this.preElement).width; } else { - this.style.backgroundColor = getComputedStyle(this.codeElement).backgroundColor; this.textareaElement.style.height = getComputedStyle(this.codeElement).height; this.textareaElement.style.width = getComputedStyle(this.codeElement).width; } @@ -765,7 +775,6 @@ var codeInput = { // Update with fallback textarea's state so can keep editing // if loaded slowly if(fallbackSelectionStart !== undefined) { - console.log("sel", fallbackSelectionStart, fallbackSelectionEnd, fallbackSelectionDirection, "scr", fallbackScrollTop, fallbackScrollLeft, "foc", fallbackFocused); textarea.setSelectionRange(fallbackSelectionStart, fallbackSelectionEnd, fallbackSelectionDirection); textarea.scrollTo(fallbackScrollTop, fallbackScrollLeft); } From 542806d29f62670c83b3501ee4dc294cb891d0f3 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Mon, 25 Aug 2025 20:53:41 +0100 Subject: [PATCH 2/7] Move from polling to transition listeners approach for better performance (styles-removed bug remains) --- code-input.css | 15 ++++++++++++++- code-input.js | 49 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/code-input.css b/code-input.css index 835af1d..3c55084 100644 --- a/code-input.css +++ b/code-input.css @@ -24,7 +24,7 @@ code-input { to keep low precedence and thus overridability The variables may change and are for internal use. */ --code-input_highlight-text-color: black; /* Set by JS to be base text color of pre code element */ - color: var(--code-input_highlight-text-color, black); + color: var(--code-input_highlight-text-color, black); /* Variable separate so can be overridden by CSS */ --code-input_default-caret-color: black; /* Set by JS to be same as color property */ caret-color: var(--code-input_default-caret-color, black); /* Set by JS to be equal to color property */ background-color: white; @@ -73,6 +73,12 @@ code-input textarea, code-input:not(.code-input_pre-element-styled) pre code, co code-input:not(.code-input_pre-element-styled) pre code, code-input.code-input_pre-element-styled pre { height: max-content; width: max-content; + + + /* Allow colour change to reflect properly; + transition-behavior: allow-discrete could be used but this is better supported and + works with the color property. */ + transition: color 0.001s; } code-input:not(.code-input_pre-element-styled) pre, code-input.code-input_pre-element-styled pre code { @@ -169,6 +175,13 @@ code-input .code-input_dialog-container { /* Dialog boxes' text is based on text-direction */ text-align: inherit; + + + /* Allow colour change to reflect properly; + transition-behavior: allow-discrete could be used but this is better supported and + works with the color property. */ + color: inherit; + transition: color 0.001s; } [dir=rtl] code-input .code-input_dialog-container, code-input[dir=rtl] .code-input_dialog-container { diff --git a/code-input.js b/code-input.js index 6ff64c4..d5e4069 100644 --- a/code-input.js +++ b/code-input.js @@ -506,18 +506,6 @@ var codeInput = { this.needsHighlight = false; } - // Synchronise colors - if(this.textareaElement) { - let color; - if(this.templateObject.preElementStyled) { - color = getComputedStyle(this.preElement).color; - } else { - color = getComputedStyle(this.codeElement).color; - } - this.style.setProperty("--code-input_highlight-text-color", color); - } - this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); - window.requestAnimationFrame(this.animateFrame.bind(this)); } @@ -749,7 +737,6 @@ var codeInput = { this.codeElement = code; pre.append(code); this.append(pre); - if (this.templateObject.isCode) { if (lang != undefined && lang != "") { code.classList.add("language-" + lang.toLowerCase()); @@ -788,7 +775,41 @@ var codeInput = { // The only element that could be resized is this code-input element. this.syncSize(); }); - resizeObserver.observe(this.textareaElement); + resizeObserver.observe(this); + + // Synchronise colors + if(this.templateObject.preElementStyled) { + this.preElement.addEventListener("transitionend", (evt) => { + if(evt.propertyName == "color") { + // So previous variable value does not affect new value: + // (required to deal with color being no longer specified in CSS) + this.style.removeProperty("--code-input_highlight-text-color"); + + console.log("pre", evt, getComputedStyle(this.preElement).color); + this.style.setProperty("--code-input_highlight-text-color", getComputedStyle(this.preElement).color); + } + }); + } else { + this.codeElement.addEventListener("transitionend", (evt) => { + if(evt.propertyName == "color") { + // So previous variable value does not affect new value: + // (required to deal with color being no longer specified in CSS) + this.style.removeProperty("--code-input_highlight-text-color"); + + console.log("code", evt, getComputedStyle(this.codeElement).color); + this.style.setProperty("--code-input_highlight-text-color", getComputedStyle(this.codeElement).color); + } + }); + } + // Not on this element so CSS transition property which must be set for + // listener to work does not conflict with library-user transition property + this.dialogContainerElement.addEventListener("transitionend", (evt) => { + if(evt.propertyName == "color") { + console.log("ci", evt, getComputedStyle(this).color); + + this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); + } + }); this.classList.add("code-input_loaded"); } From 2c0aef738e4112e8f33e97a9c19142169e6b5a21 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sun, 31 Aug 2025 16:59:14 +0100 Subject: [PATCH 3/7] Complete working colour sync --- code-input.css | 19 +++++---- code-input.js | 104 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 77 insertions(+), 46 deletions(-) diff --git a/code-input.css b/code-input.css index 3c55084..c178c10 100644 --- a/code-input.css +++ b/code-input.css @@ -22,10 +22,11 @@ code-input { /* CSS variables rather than inline styles used for values synced from JavaScript to keep low precedence and thus overridability - The variables may change and are for internal use. */ - --code-input_highlight-text-color: black; /* Set by JS to be base text color of pre code element */ - color: var(--code-input_highlight-text-color, black); /* Variable separate so can be overridden by CSS */ - --code-input_default-caret-color: black; /* Set by JS to be same as color property */ + The variable names may change and are for internal use. */ + /* --code-input_highlight-text-color: Set by JS to be base text color of pre code element */ + --code-input_no-override-color: inherit; /* Set by JS for very short time to get whether color has been overriden */ + color: var(--code-input_no-override-color, inherit); + /* --code-input_default-caret-color: Set by JS to be same as color property */ caret-color: var(--code-input_default-caret-color, black); /* Set by JS to be equal to color property */ background-color: white; @@ -135,7 +136,7 @@ code-input textarea:not([data-code-input-fallback]) { } code-input textarea:not([data-code-input-fallback]):placeholder-shown { /* Show placeholder */ - color: inherit; + color: var(--code-input_highlight-text-color, inherit); } /* Can be scrolled */ @@ -176,10 +177,8 @@ code-input .code-input_dialog-container { /* Dialog boxes' text is based on text-direction */ text-align: inherit; - /* Allow colour change to reflect properly; - transition-behavior: allow-discrete could be used but this is better supported and - works with the color property. */ + * transition-behavior: allow-discrete could be used but this is * better supported and works with the color property. */ color: inherit; transition: color 0.001s; } @@ -254,7 +253,7 @@ code-input:not(.code-input_loaded)::after { border-top: 1px solid currentColor; outline-top: 0; background-color: inherit; - color: inherit; + color: var(--code-input_highlight-text-color, inherit); margin: 0; padding: 0; @@ -277,7 +276,7 @@ code-input:has(textarea[data-code-input-fallback]) { code-input textarea[data-code-input-fallback] { overflow: auto; background-color: inherit; - color: inherit; + color: var(--code-input_highlight-text-color, inherit); /* Don't overlap with message */ min-height: calc(100% - var(--padding-top, 16px) - 2em - var(--padding-bottom, 16px)); diff --git a/code-input.js b/code-input.js index d5e4069..d63ebc3 100644 --- a/code-input.js +++ b/code-input.js @@ -530,20 +530,67 @@ var codeInput = { this.pluginEvt("afterHighlight"); } + getStyledHighlightingElement() { + if(this.templateObject.preElementStyled) { + return this.preElement; + } else { + return this.codeElement; + } + } + /** * Set the size of the textarea element to the size of the pre/code element. */ syncSize() { // Synchronise the size of the pre/code and textarea elements - if(this.templateObject.preElementStyled) { - this.textareaElement.style.height = getComputedStyle(this.preElement).height; - this.textareaElement.style.width = getComputedStyle(this.preElement).width; - } else { - this.textareaElement.style.height = getComputedStyle(this.codeElement).height; - this.textareaElement.style.width = getComputedStyle(this.codeElement).width; + this.textareaElement.style.height = getComputedStyle(this.getStyledHighlightingElement()).height; + this.textareaElement.style.width = getComputedStyle(this.getStyledHighlightingElement()).width; + } + + /** + * If the color attribute has been defined on the + * code-input element by external code, return true. + * Otherwise, make the aspects the color affects + * (placeholder and caret colour) be the base colour + * of the highlighted text, for best contrast, and + * return false. + */ + isColorOverridenSyncIfNot() { + this.style.setProperty("--code-input_no-override-color", "rgb(0, 0, 0)"); + if(getComputedStyle(this).color == "rgb(0, 0, 0)") { + // May not be overriden + this.style.setProperty("--code-input_no-override-color", "rgb(255, 255, 255)"); + if(getComputedStyle(this).color == "rgb(255, 255, 255)") { + // Definitely not overriden + this.style.removeProperty("--code-input_no-override-color"); + + this.style.setProperty("--code-input_highlight-text-color", getComputedStyle(this.getStyledHighlightingElement()).color); + this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this.getStyledHighlightingElement()).color); + return false; + } + } + this.style.removeProperty("--code-input_no-override-color"); + + return true; + } + + /** + * Update the aspects the color affects + * (placeholder and caret colour) to the correct + * colour: either that defined on the code-input + * element, or if none is defined externally the + * base colour of the highlighted text. + */ + syncColorCompletely() { + // color of code-input element + if(this.isColorOverridenSyncIfNot()) { + // color overriden + this.style.removeProperty("--code-input_highlight-text-color"); + this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); } } + /** * Show some instructions to the user only if they are using keyboard navigation - for example, a prompt on how to navigate with the keyboard if Tab is repurposed. * @param {string} instructions The instructions to display only if keyboard navigation is being used. If it's blank, no instructions will be shown. @@ -778,38 +825,23 @@ var codeInput = { resizeObserver.observe(this); // Synchronise colors - if(this.templateObject.preElementStyled) { - this.preElement.addEventListener("transitionend", (evt) => { - if(evt.propertyName == "color") { - // So previous variable value does not affect new value: - // (required to deal with color being no longer specified in CSS) - this.style.removeProperty("--code-input_highlight-text-color"); - - console.log("pre", evt, getComputedStyle(this.preElement).color); - this.style.setProperty("--code-input_highlight-text-color", getComputedStyle(this.preElement).color); - } - }); - } else { - this.codeElement.addEventListener("transitionend", (evt) => { - if(evt.propertyName == "color") { - // So previous variable value does not affect new value: - // (required to deal with color being no longer specified in CSS) - this.style.removeProperty("--code-input_highlight-text-color"); - - console.log("code", evt, getComputedStyle(this.codeElement).color); - this.style.setProperty("--code-input_highlight-text-color", getComputedStyle(this.codeElement).color); - } - }); - } - // Not on this element so CSS transition property which must be set for - // listener to work does not conflict with library-user transition property - this.dialogContainerElement.addEventListener("transitionend", (evt) => { + const preColorChangeCallback = (evt) => { if(evt.propertyName == "color") { - console.log("ci", evt, getComputedStyle(this).color); - - this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); + this.isColorOverridenSyncIfNot(); } - }); + }; + this.preElement.addEventListener("transitionend", preColorChangeCallback); + this.preElement.addEventListener("-webkit-transitionend", preColorChangeCallback); + const thisColorChangeCallback = (evt) => { + if(evt.propertyName == "color") { + this.syncColorCompletely(); + } + }; + // Not on this element so CSS transition property which must be set for + this.dialogContainerElement.addEventListener("transitionend", thisColorChangeCallback); + this.dialogContainerElement.addEventListener("-webkit-transitionend", thisColorChangeCallback); + + this.syncColorCompletely(); this.classList.add("code-input_loaded"); } From b76ea47266c22079e79497986d809abb524ece9d Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Wed, 3 Sep 2025 11:46:09 +0100 Subject: [PATCH 4/7] Make color/caret-color work when transition set on code-input element, and transitionend event be called as expected --- code-input.css | 6 +++--- code-input.js | 21 ++++++++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/code-input.css b/code-input.css index c178c10..7d4b966 100644 --- a/code-input.css +++ b/code-input.css @@ -24,10 +24,10 @@ code-input { to keep low precedence and thus overridability The variable names may change and are for internal use. */ /* --code-input_highlight-text-color: Set by JS to be base text color of pre code element */ - --code-input_no-override-color: inherit; /* Set by JS for very short time to get whether color has been overriden */ - color: var(--code-input_no-override-color, inherit); + --code-input_no-override-color: revert; /* Set by JS for very short time to get whether color has been overriden */ + color: var(--code-input_no-override-color, revert); /* --code-input_default-caret-color: Set by JS to be same as color property */ - caret-color: var(--code-input_default-caret-color, black); /* Set by JS to be equal to color property */ + caret-color: var(--code-input_default-caret-color, black); /* Set by JS to be equal to color property - currentColor won't work because it's lazily evaluated so gives transparent for the textarea */ background-color: white; /* Normal inline styles */ diff --git a/code-input.js b/code-input.js index d63ebc3..aa8c92e 100644 --- a/code-input.js +++ b/code-input.js @@ -556,6 +556,8 @@ var codeInput = { * return false. */ isColorOverridenSyncIfNot() { + const oldTransition = this.style.transition; + this.style.transition = "unset"; this.style.setProperty("--code-input_no-override-color", "rgb(0, 0, 0)"); if(getComputedStyle(this).color == "rgb(0, 0, 0)") { // May not be overriden @@ -563,13 +565,17 @@ var codeInput = { if(getComputedStyle(this).color == "rgb(255, 255, 255)") { // Definitely not overriden this.style.removeProperty("--code-input_no-override-color"); + this.style.transition = oldTransition; - this.style.setProperty("--code-input_highlight-text-color", getComputedStyle(this.getStyledHighlightingElement()).color); - this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this.getStyledHighlightingElement()).color); + const highlightedTextColor = getComputedStyle(this.getStyledHighlightingElement()).color; + + this.style.setProperty("--code-input_highlight-text-color", highlightedTextColor); + this.style.setProperty("--code-input_default-caret-color", highlightedTextColor); return false; } } this.style.removeProperty("--code-input_no-override-color"); + this.style.transition = oldTransition; return true; } @@ -836,11 +842,20 @@ var codeInput = { if(evt.propertyName == "color") { this.syncColorCompletely(); } + if(evt.target == this.dialogContainerElement) { + evt.stopPropagation(); + // Prevent bubbling because code-input + // transitionend is separate + } }; - // Not on this element so CSS transition property which must be set for + // Not on this element so CSS transition property does not override publicly-visible one this.dialogContainerElement.addEventListener("transitionend", thisColorChangeCallback); this.dialogContainerElement.addEventListener("-webkit-transitionend", thisColorChangeCallback); + // For when this code-input element has an externally-defined, different-duration transition + this.addEventListener("transitionend", thisColorChangeCallback); + this.addEventListener("-webkit-transitionend", thisColorChangeCallback); + this.syncColorCompletely(); this.classList.add("code-input_loaded"); From 28f869a45827419f4c2236ac24ba96dda33703fd Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sat, 20 Sep 2025 14:59:42 +0100 Subject: [PATCH 5/7] Prevent no-override-color becoming visible when transition set --- code-input.css | 8 ++++---- code-input.js | 36 +++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/code-input.css b/code-input.css index 7d4b966..76bf603 100644 --- a/code-input.css +++ b/code-input.css @@ -24,10 +24,10 @@ code-input { to keep low precedence and thus overridability The variable names may change and are for internal use. */ /* --code-input_highlight-text-color: Set by JS to be base text color of pre code element */ - --code-input_no-override-color: revert; /* Set by JS for very short time to get whether color has been overriden */ - color: var(--code-input_no-override-color, revert); - /* --code-input_default-caret-color: Set by JS to be same as color property */ - caret-color: var(--code-input_default-caret-color, black); /* Set by JS to be equal to color property - currentColor won't work because it's lazily evaluated so gives transparent for the textarea */ + /* --code-input_no-override-color: Set by JS for very short time to get whether color has been overriden */ + color: var(--code-input_no-override-color, black); + /* --code-input_default-caret-color: Set by JS to be same as color property - currentColor won't work because it's lazily evaluated so gives transparent for the textarea */ + caret-color: var(--code-input_default-caret-color, black); background-color: white; /* Normal inline styles */ diff --git a/code-input.js b/code-input.js index aa8c92e..0cc4a68 100644 --- a/code-input.js +++ b/code-input.js @@ -558,24 +558,26 @@ var codeInput = { isColorOverridenSyncIfNot() { const oldTransition = this.style.transition; this.style.transition = "unset"; - this.style.setProperty("--code-input_no-override-color", "rgb(0, 0, 0)"); - if(getComputedStyle(this).color == "rgb(0, 0, 0)") { - // May not be overriden - this.style.setProperty("--code-input_no-override-color", "rgb(255, 255, 255)"); - if(getComputedStyle(this).color == "rgb(255, 255, 255)") { - // Definitely not overriden - this.style.removeProperty("--code-input_no-override-color"); - this.style.transition = oldTransition; - - const highlightedTextColor = getComputedStyle(this.getStyledHighlightingElement()).color; - - this.style.setProperty("--code-input_highlight-text-color", highlightedTextColor); - this.style.setProperty("--code-input_default-caret-color", highlightedTextColor); - return false; + window.requestAnimationFrame(() => { + this.style.setProperty("--code-input_no-override-color", "rgb(0, 0, 0)"); + if(getComputedStyle(this).color == "rgb(0, 0, 0)") { + // May not be overriden + this.style.setProperty("--code-input_no-override-color", "rgb(255, 255, 255)"); + if(getComputedStyle(this).color == "rgb(255, 255, 255)") { + // Definitely not overriden + this.style.removeProperty("--code-input_no-override-color"); + this.style.transition = oldTransition; + + const highlightedTextColor = getComputedStyle(this.getStyledHighlightingElement()).color; + + this.style.setProperty("--code-input_highlight-text-color", highlightedTextColor); + this.style.setProperty("--code-input_default-caret-color", highlightedTextColor); + return false; + } } - } - this.style.removeProperty("--code-input_no-override-color"); - this.style.transition = oldTransition; + this.style.removeProperty("--code-input_no-override-color"); + this.style.transition = oldTransition; + }); return true; } From 6e50d69a04b5d256e1285fae211793dca8e741a6 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sat, 20 Sep 2025 17:08:55 +0100 Subject: [PATCH 6/7] Add tests for synchronise color; Make synchronise color not use the style attribute for long-term storage and document use of class attribute --- code-input.css | 6 +++--- code-input.js | 41 ++++++++++++++++++++++++++++-------- docs/interface/css/_index.md | 2 ++ tests/hljs.html | 4 ++-- tests/prism.html | 2 +- tests/tester.js | 28 ++++++++++++++++++++++++ 6 files changed, 68 insertions(+), 15 deletions(-) diff --git a/code-input.css b/code-input.css index 76bf603..78d8396 100644 --- a/code-input.css +++ b/code-input.css @@ -27,7 +27,7 @@ code-input { /* --code-input_no-override-color: Set by JS for very short time to get whether color has been overriden */ color: var(--code-input_no-override-color, black); /* --code-input_default-caret-color: Set by JS to be same as color property - currentColor won't work because it's lazily evaluated so gives transparent for the textarea */ - caret-color: var(--code-input_default-caret-color, black); + caret-color: var(--code-input_default-caret-color, inherit); background-color: white; /* Normal inline styles */ @@ -132,7 +132,7 @@ code-input pre { code-input textarea:not([data-code-input-fallback]) { background: transparent; color: transparent; - caret-color: inherit; /* Or choose your favourite color */ + caret-color: inherit; } code-input textarea:not([data-code-input-fallback]):placeholder-shown { /* Show placeholder */ @@ -253,7 +253,7 @@ code-input:not(.code-input_loaded)::after { border-top: 1px solid currentColor; outline-top: 0; background-color: inherit; - color: var(--code-input_highlight-text-color, inherit); + color: inherit; margin: 0; padding: 0; diff --git a/code-input.js b/code-input.js index 0cc4a68..ff973b5 100644 --- a/code-input.js +++ b/code-input.js @@ -152,6 +152,8 @@ var codeInput = { } }, + stylesheetI: 0, // Increments to give different classes to each code-input element so they can have custom styles synchronised internally without affecting the inline style + /** * Please see `codeInput.templates.prism` or `codeInput.templates.hljs`. * Templates are used in `` elements and once registered with @@ -445,6 +447,16 @@ var codeInput = { */ dialogContainerElement = null; + /** + * Like style attribute, but with a specificity of 1 + * element, 1 class. Present so styles can be set on only + * this element while giving other code freedom of use of + * the style attribute. + * + * For internal use only. + */ + internalStyle = null; + /** * Form-Associated Custom Element Callbacks * https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example @@ -559,23 +571,23 @@ var codeInput = { const oldTransition = this.style.transition; this.style.transition = "unset"; window.requestAnimationFrame(() => { - this.style.setProperty("--code-input_no-override-color", "rgb(0, 0, 0)"); + this.internalStyle.setProperty("--code-input_no-override-color", "rgb(0, 0, 0)"); if(getComputedStyle(this).color == "rgb(0, 0, 0)") { // May not be overriden - this.style.setProperty("--code-input_no-override-color", "rgb(255, 255, 255)"); + this.internalStyle.setProperty("--code-input_no-override-color", "rgb(255, 255, 255)"); if(getComputedStyle(this).color == "rgb(255, 255, 255)") { // Definitely not overriden - this.style.removeProperty("--code-input_no-override-color"); + this.internalStyle.removeProperty("--code-input_no-override-color"); this.style.transition = oldTransition; const highlightedTextColor = getComputedStyle(this.getStyledHighlightingElement()).color; - this.style.setProperty("--code-input_highlight-text-color", highlightedTextColor); - this.style.setProperty("--code-input_default-caret-color", highlightedTextColor); + this.internalStyle.setProperty("--code-input_highlight-text-color", highlightedTextColor); + this.internalStyle.setProperty("--code-input_default-caret-color", highlightedTextColor); return false; } } - this.style.removeProperty("--code-input_no-override-color"); + this.internalStyle.removeProperty("--code-input_no-override-color"); this.style.transition = oldTransition; }); @@ -593,8 +605,8 @@ var codeInput = { // color of code-input element if(this.isColorOverridenSyncIfNot()) { // color overriden - this.style.removeProperty("--code-input_highlight-text-color"); - this.style.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); + this.internalStyle.removeProperty("--code-input_highlight-text-color"); + this.internalStyle.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); } } @@ -832,6 +844,15 @@ var codeInput = { }); resizeObserver.observe(this); + + // Add internal style as non-externally-overridable alternative to style attribute for e.g. syncing color + this.classList.add("code-input_styles_" + codeInput.stylesheetI); + const stylesheet = document.createElement("style"); + stylesheet.innerHTML = "code-input.code-input_styles_" + codeInput.stylesheetI + " {}"; + this.appendChild(stylesheet); + this.internalStyle = stylesheet.sheet.cssRules[0].style; + codeInput.stylesheetI++; + // Synchronise colors const preColorChangeCallback = (evt) => { if(evt.propertyName == "color") { @@ -993,7 +1014,9 @@ var codeInput = { code.classList.add("language-" + newValue); } - if (mainTextarea.placeholder == oldValue) mainTextarea.placeholder = newValue; + if (mainTextarea.placeholder == oldValue || oldValue == null && mainTextarea.placeholder == "") { + mainTextarea.placeholder = newValue; + } this.scheduleHighlight(); diff --git a/docs/interface/css/_index.md b/docs/interface/css/_index.md index 34ab4a7..61d8681 100644 --- a/docs/interface/css/_index.md +++ b/docs/interface/css/_index.md @@ -10,3 +10,5 @@ title = 'Styling `code-input` elements with CSS' * The CSS variable `--padding` should be used rather than the property `padding` (e.g. `...`), or `--padding-left`, `--padding-right`, `--padding-top` and `--padding-bottom` instead of the CSS properties of the same names. For technical reasons, the value must have a unit (i.e. `0px`, not `0`). * Background colours set on `code-input` elements will not work with highlighters that set background colours themselves - use `(code-input's selector) pre[class*="language-"]` for Prism.js or `.hljs` for highlight.js to target the highlighted element with higher specificity than the highlighter's theme. You may also set the `background-color` of the code-input element for its appearance when its template is unregistered / there is no JavaScript. * For now, elements on top of `code-input` elements should have a CSS `z-index` at least 3 greater than the `code-input` element. + +Please do **not** use `className` in JavaScript referring to code-input elements, because the code-input library needs to add its own classes to code-input elements for easier progressive enhancement. You can, however, use `classList` and `style` as much as you want - it will make your code cleaner anyway. diff --git a/tests/hljs.html b/tests/hljs.html index 28f2bc2..25bf95a 100644 --- a/tests/hljs.html +++ b/tests/hljs.html @@ -6,7 +6,7 @@ code-input Tester - + @@ -42,7 +42,7 @@

Test for Prism.js

Test Results (Click to Open)
- diff --git a/tests/prism.html b/tests/prism.html index 50e4570..bfc6906 100644 --- a/tests/prism.html +++ b/tests/prism.html @@ -5,7 +5,7 @@ code-input Tester - + diff --git a/tests/tester.js b/tests/tester.js index 80b54b3..b33f2e7 100644 --- a/tests/tester.js +++ b/tests/tester.js @@ -326,6 +326,34 @@ console.log("I've got another line!", 2 < 3, "should be true."); // A third line with <html> tags `); // Extra newline so line numbers visible if enabled. + // Delete all code + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; + backspace(textarea); + codeInputElement.setAttribute("language", "JavaScript"); // for placeholder + + await waitAsync(100); // Wait for rendered value to update + testAssertion("Core", "Light theme Caret/Placeholder Color Correct", confirm("Are the caret and placeholder near-black? (OK=Yes)"), "user-judged"); + + if(isHLJS) { + document.getElementById("theme-stylesheet").href = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css"; + } else { + document.getElementById("theme-stylesheet").href = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css"; + } + await waitAsync(200); // Wait for colours to update + testAssertion("Core", "Dark theme Caret/Placeholder Color Correct", confirm("Are the caret and placeholder near-white? (OK=Yes)"), "user-judged"); + + codeInputElement.style.color = "red"; + await waitAsync(200); // Wait for colours to update + testAssertion("Core", "Overriden color Caret/Placeholder Color Correct", confirm("Are the caret and placeholder (for Firefox) or just caret (for Chromium/WebKit, for consistency with textareas) red? (OK=Yes)"), "user-judged"); + + codeInputElement.style.removeProperty("color"); + codeInputElement.style.caretColor = "red"; + await waitAsync(200); // Wait for colours to update + testAssertion("Core", "Overriden caret-color Caret/Placeholder Color Correct", confirm("Is the caret red and placeholder near-white? (OK=Yes)"), "user-judged"); + + codeInputElement.style.removeProperty("caret-color"); + /*--- Tests for plugins ---*/ // AutoCloseBrackets testAddingText("AutoCloseBrackets", textarea, function(textarea) { From af1099f1d6073ff259938b75ee27e35bbcd574d0 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sat, 20 Sep 2025 17:19:16 +0100 Subject: [PATCH 7/7] Document caret/placeholder theme behaviour --- docs/interface/css/_index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/interface/css/_index.md b/docs/interface/css/_index.md index 61d8681..2a1c60f 100644 --- a/docs/interface/css/_index.md +++ b/docs/interface/css/_index.md @@ -10,5 +10,6 @@ title = 'Styling `code-input` elements with CSS' * The CSS variable `--padding` should be used rather than the property `padding` (e.g. `...`), or `--padding-left`, `--padding-right`, `--padding-top` and `--padding-bottom` instead of the CSS properties of the same names. For technical reasons, the value must have a unit (i.e. `0px`, not `0`). * Background colours set on `code-input` elements will not work with highlighters that set background colours themselves - use `(code-input's selector) pre[class*="language-"]` for Prism.js or `.hljs` for highlight.js to target the highlighted element with higher specificity than the highlighter's theme. You may also set the `background-color` of the code-input element for its appearance when its template is unregistered / there is no JavaScript. * For now, elements on top of `code-input` elements should have a CSS `z-index` at least 3 greater than the `code-input` element. +* The caret and placeholder colour by default follow and give good contrast with the highlighted theme. Setting a CSS rule (with a specificity higher than one element and one class, for good backwards compatibility) for `color` and/or `caret-color` properties on the code-input element will override this behaviour. Please do **not** use `className` in JavaScript referring to code-input elements, because the code-input library needs to add its own classes to code-input elements for easier progressive enhancement. You can, however, use `classList` and `style` as much as you want - it will make your code cleaner anyway.