single-element mode with gradients
Following the disscussion in https://github.com/WebCoder49/code-input/issues/190, I am opening this issue to discuss requirements and implementation.
Current status
Demo: https://codepen.io/eyaler/pen/NPGOaJb
Features:
- supports tabs
- supports wrapping (container width only)
- demo integration with highlight.js (without reparsing)
- works on Firefox/Chromium/Safari on Windows/Android/macOS/iOS
- initial performance tests show negligible overhead on top of highlight.js
- no issues found with positions and transforms in Chromium
Current limitations:
- strictly single font monospace (but we could try to solve this later at risk of performance and robustness)
- no bold/italics/underline nor background highlighting (perhaps the latter can be dealt with using background-blend?)
- wrapping strictly at container width (cannot rely on browser wrapping, but we could use our own logic or library to wrap semantically)
Open questions:
- background-clip: text vs. mix-blend + additional div (i have a strong preference for the former)
- for the clip option - background color is controlled by the element behind. do we leave that to the user or do something?
- need more fuzzing tests to make sure it is robust and performant
- need to design a more generic connector to the parsing engine, and add a prism.js option, maybe also tree-sitter?
- is the wrapping behavior acceptible? should we factor it out or does this simple approach make sense only here? how is this option controlled?
- how to integrate with code-input, so that the html uses a regular textarea with a class?
- allowing for typed chars to appear instantly before highlighting finishes
- background-image: url(svg) as an alternative to gradients, opens many possibilities
Benefits over mirror element:
- Typed characters and caret are immediately visible in the current highlighted colour
- Less DOM tree is created
Original element-behind approach still must be an option because of the limitations listed above.
@paulrouget Your extra input here would be great.
* Typed characters and caret are immediately visible in the current highlighted colour
For the current implementation using background-clip: text, as opposed to mix-blend, the above doesn't hold:
- background-color is transparent to reduce halo (due to small differences in the clipping between background-color and background-image), and the gradients are only behind the existing text. but we could think about extending all lines + some extra lines below, to some given width using the default or the last color. I can look into this if it's important.
- the caret does not get color with background-clip: text. btw - why do you want this? it's harder to find the caret.
@eyaler approach is great. I really love this system.
I haven't looked seriously at the code. Just want to mention that maybe measureCell could be faster with using ch and lh units instead of getComputedStyle and clientWidth? I don't know if it's fully possible (especially the wrap part).
@paulrouget thanks!
ch does not account for letter-spacing. if letter-spacing is zero then ch works, but you still need to get the computed tab size in these units.
regarding lh, in firefox and safari we need to offset background y-positions by the scrolling which is given in px, so you would need to put in the css calc(${row}lh - ${scrollY}px) per line. it simplifies the code a bit at expense of the css. not sure what is better for performance.
On an unrelated thought, we could also consider background-image: url(svg) instead of gradients. this can support more styling options, and essentially allows a continous spectrum of possibilities from a full dom-heavy svg text mirror to using a single big svg repeating background pattern or complex color filter matrix, and perhaps there is a nicer sweet spot in between.
- the caret does not get color with background-clip: text. btw - why do you want this? it's harder to find the caret.
I was thinking it was a potential (small) aesthetic benefit but I agree it could be worse for contrast. To be honest I'm unsure of the great benefits of this approach but get that having fewer elements may cause fewer problems down the line and since you two are interested in it would be willing to include it as an option.