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:
@@ -731,3 +731,30 @@ func (h *ContentHandler) ServeInsertrJS(w http.ResponseWriter, r *http.Request)
|
||||
// Copy file contents to response
|
||||
io.Copy(w, file)
|
||||
}
|
||||
|
||||
// ServeInsertrCSS handles GET /insertr.css - serves the insertr CSS stylesheet
|
||||
func (h *ContentHandler) ServeInsertrCSS(w http.ResponseWriter, r *http.Request) {
|
||||
// Path to the built insertr.css file
|
||||
cssPath := "lib/dist/insertr.css"
|
||||
|
||||
// Check if file exists
|
||||
if _, err := os.Stat(cssPath); os.IsNotExist(err) {
|
||||
http.Error(w, "insertr.css not found - run 'just build-lib' to build the library", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Open and serve the file
|
||||
file, err := os.Open(cssPath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to open insertr.css: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Set appropriate headers
|
||||
w.Header().Set("Content-Type", "text/css")
|
||||
w.Header().Set("Cache-Control", "no-cache") // For development
|
||||
|
||||
// Copy file contents to response
|
||||
io.Copy(w, file)
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ func ContentTypeMiddleware(next http.Handler) http.Handler {
|
||||
if r.URL.Path != "/" &&
|
||||
!strings.HasPrefix(r.URL.Path, "/sites/") &&
|
||||
!strings.HasPrefix(r.URL.Path, "/insertr.js") &&
|
||||
!strings.HasPrefix(r.URL.Path, "/insertr.css") &&
|
||||
(r.Method == "GET" || r.Method == "POST" || r.Method == "PUT") {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user