Implement hybrid CSS architecture to fix white-on-white modal issue on sites with CSS resets

- Migrate from inline CSS to external insertr.css with cascade layer architecture
- Add CSS CDN serving capability (ServeInsertrCSS handler and /insertr.css route)
- Implement hybrid approach: @layer insertr for modern browsers + html body selectors for legacy browsers
- Remove scattered inline CSS from JavaScript modules for better maintainability
- Solve form element spacing conflicts with aggressive site CSS resets like '* {margin:0; padding:0}'
- Enable proper CSS caching and separation of concerns
This commit is contained in:
2025-09-17 14:39:34 +02:00
parent cd202ebb1d
commit 1bf597266e
11 changed files with 785 additions and 694 deletions

View File

@@ -364,19 +364,20 @@ func (i *Injector) InjectEditorScript(doc *html.Node) {
return
}
// Create script element that loads insertr.js from our server with site configuration
scriptHTML := fmt.Sprintf(`<script src="http://localhost:8080/insertr.js" data-insertr-injected="true" data-site-id="%s" data-api-endpoint="http://localhost:8080/api/content" data-mock-auth="true" data-debug="true"></script>`, i.siteID)
// Create CSS and script elements that load from our server with site configuration
insertrHTML := fmt.Sprintf(`<link rel="stylesheet" href="http://localhost:8080/insertr.css" data-insertr-injected="true">
<script src="http://localhost:8080/insertr.js" data-insertr-injected="true" data-site-id="%s" data-api-endpoint="http://localhost:8080/api/content" data-mock-auth="true" data-debug="true"></script>`, i.siteID)
// Parse and inject the script
scriptDoc, err := html.Parse(strings.NewReader(scriptHTML))
// Parse and inject the CSS and script elements
insertrDoc, err := html.Parse(strings.NewReader(insertrHTML))
if err != nil {
log.Printf("Error parsing editor script HTML: %v", err)
return
}
// Extract and inject all script elements
if err := i.injectAllScriptElements(scriptDoc, headNode); err != nil {
log.Printf("Error injecting script elements: %v", err)
// Extract and inject all CSS and script elements
if err := i.injectAllHeadElements(insertrDoc, headNode); err != nil {
log.Printf("Error injecting CSS and script elements: %v", err)
return
}
@@ -415,6 +416,38 @@ func (i *Injector) findAllScriptElements(node *html.Node) []*html.Node {
return scripts
}
// injectAllHeadElements finds and injects all head elements (link, script) from parsed HTML
func (i *Injector) injectAllHeadElements(doc *html.Node, targetNode *html.Node) error {
elements := i.findAllHeadElements(doc)
for _, element := range elements {
// Remove from original parent
if element.Parent != nil {
element.Parent.RemoveChild(element)
}
// Add to target node
targetNode.AppendChild(element)
}
return nil
}
// findAllHeadElements recursively finds all link and script elements
func (i *Injector) findAllHeadElements(node *html.Node) []*html.Node {
var elements []*html.Node
if node.Type == html.ElementNode && (node.Data == "script" || node.Data == "link") {
elements = append(elements, node)
}
for child := node.FirstChild; child != nil; child = child.NextSibling {
childElements := i.findAllHeadElements(child)
elements = append(elements, childElements...)
}
return elements
}
// hasInsertrGate checks if document has .insertr-gate elements
func (i *Injector) hasInsertrGate(node *html.Node) bool {
if node.Type == html.ElementNode {