package html import "strings" // Responsive wraps multiple Layout variants for breakpoint-aware rendering. // Each variant is rendered inside a container with data-variant for CSS targeting. type Responsive struct { variants []responsiveVariant } type responsiveVariant struct { name string layout *Layout } // NewResponsive creates a new multi-variant responsive compositor. func NewResponsive() *Responsive { return &Responsive{} } // escapeCSSString escapes a string for safe use inside a double-quoted CSS // attribute selector. func escapeCSSString(s string) string { var b strings.Builder for _, r := range s { switch r { case '\\', '"': b.WriteByte('\\') b.WriteRune(r) case '\n': b.WriteString(`\a `) case '\r': b.WriteString(`\d `) case '\f': b.WriteString(`\c `) default: b.WriteRune(r) } } return b.String() } // VariantSelector returns a CSS attribute selector for a named responsive // variant. func VariantSelector(name string) string { return `[data-variant="` + escapeCSSString(name) + `"]` } // ScopeVariant prefixes a selector so it only matches elements inside the // named responsive variant. func ScopeVariant(name, selector string) string { scope := VariantSelector(name) if selector == "" { return scope } return scope + " " + selector } // Variant adds a named layout variant (e.g., "desktop", "tablet", "mobile"). // Variants render in insertion order. func (r *Responsive) Variant(name string, layout *Layout) *Responsive { r.variants = append(r.variants, responsiveVariant{name: name, layout: layout}) return r } // Render produces HTML with each variant in a data-variant container. func (r *Responsive) Render(ctx *Context) string { var b strings.Builder for _, v := range r.variants { b.WriteString(`