` container for CSS or JavaScript targeting.
+**Responsive variants** -- `Responsive` wraps multiple `Layout` instances with named breakpoints (e.g. `"desktop"`, `"mobile"`). Each variant renders inside a `
` container for CSS or JavaScript targeting. `VariantSelector(name)` returns a ready-made attribute selector for styling these containers from CSS.
**Grammar pipeline** -- Server-side only. `Imprint()` renders a node tree to HTML, strips tags, tokenises the plain text via `go-i18n/reversal`, and returns a `GrammarImprint` for semantic analysis. `CompareVariants()` computes pairwise similarity scores across responsive variants.
diff --git a/responsive.go b/responsive.go
index 1f0dd06..6d85b6f 100644
--- a/responsive.go
+++ b/responsive.go
@@ -1,5 +1,10 @@
package html
+import (
+ "strconv"
+ "strings"
+)
+
// Compile-time interface check.
var _ Node = (*Responsive)(nil)
@@ -56,3 +61,41 @@ func (r *Responsive) Render(ctx *Context) string {
}
return b.String()
}
+
+// VariantSelector returns a CSS attribute selector for a responsive variant.
+// Usage example: selector := VariantSelector("desktop")
+func VariantSelector(name string) string {
+ return `[data-variant="` + escapeCSSString(name) + `"]`
+}
+
+func escapeCSSString(s string) string {
+ if s == "" {
+ return ""
+ }
+
+ 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 `)
+ case '\t':
+ b.WriteString(`\9 `)
+ default:
+ if r < 0x20 || r == 0x7f {
+ b.WriteByte('\\')
+ b.WriteString(strings.ToUpper(strconv.FormatInt(int64(r), 16)))
+ b.WriteByte(' ')
+ continue
+ }
+ b.WriteRune(r)
+ }
+ }
+ return b.String()
+}
diff --git a/responsive_test.go b/responsive_test.go
index 58f67c9..9438c55 100644
--- a/responsive_test.go
+++ b/responsive_test.go
@@ -110,3 +110,19 @@ func TestResponsive_Render_NilContext_Good(t *testing.T) {
t.Fatalf("responsive.Render(nil) = %q, want %q", got, want)
}
}
+
+func TestVariantSelector_Good(t *testing.T) {
+ got := VariantSelector("desktop")
+ want := `[data-variant="desktop"]`
+ if got != want {
+ t.Fatalf("VariantSelector(%q) = %q, want %q", "desktop", got, want)
+ }
+}
+
+func TestVariantSelector_Escapes_Good(t *testing.T) {
+ got := VariantSelector("desk\"top\\wide")
+ want := `[data-variant="desk\"top\\wide"]`
+ if got != want {
+ t.Fatalf("VariantSelector escaping = %q, want %q", got, want)
+ }
+}