refactor: consolidate CSS to use .themed class with native nesting

- Add .themed class as shared selector for both shop and preview
- Move visual/behavioral styles from .preview-frame to .themed
- Keep .preview-frame only for CSS variable switching (editor live preview)
- Update CSSGenerator to target .themed instead of .shop-root
- Refactor CSS files to use native CSS nesting syntax
- Update tests to reflect new class structure

This improves maintainability by:
- Eliminating duplicate selectors (.shop-root + .preview-frame)
- Using modern CSS nesting (94%+ browser support)
- Clear separation: .preview-frame = vars, .themed = styles

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-17 21:43:26 +00:00
parent 7491c34723
commit 75206474a1
8 changed files with 838 additions and 673 deletions

View File

@@ -1,10 +1,13 @@
defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do
@moduledoc """
Tests that verify CSS selectors work correctly for both the theme editor
preview (.preview-frame) and the shop pages (.shop-root).
Tests that verify CSS works correctly for both the theme editor
preview and the shop pages using the shared .themed class.
These tests ensure that the theme-layer2-attributes.css file has correct
selectors for both contexts, and that CSS custom properties are resolved.
Architecture:
- Both shop pages and preview use .themed class for shared styles
- Theme editor uses .preview-frame[data-*] selectors for live switching
- Shop pages get theme values via inline CSS from CSSGenerator
- Component styles use .themed for shared styling
"""
use SimpleshopThemeWeb.ConnCase, async: false
@@ -20,23 +23,23 @@ defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do
end
describe "CSS selector consistency" do
test "shop home page has .shop-root with data attributes", %{conn: conn} do
test "shop home page has .themed with data attributes", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/")
# Verify shop-root element exists with theme data attributes
assert html =~ ~r/<div[^>]*class="shop-root/
# Verify themed element exists with theme data attributes
assert html =~ ~r/<div[^>]*class="themed/
assert html =~ ~r/data-mood="/
assert html =~ ~r/data-typography="/
assert html =~ ~r/data-shape="/
assert html =~ ~r/data-density="/
end
test "theme editor has .preview-frame with data attributes", %{conn: conn, user: user} do
test "theme editor has .themed with data attributes", %{conn: conn, user: user} do
conn = log_in_user(conn, user)
{:ok, _view, html} = live(conn, ~p"/admin/theme")
# Verify preview-frame element exists with theme data attributes
assert html =~ ~r/<div[^>]*class="preview-frame/
# Verify themed element exists in preview-frame with theme data attributes
assert html =~ ~r/<div[^>]*class="themed/
assert html =~ ~r/data-mood="/
assert html =~ ~r/data-typography="/
assert html =~ ~r/data-shape="/
@@ -85,55 +88,60 @@ defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do
end
describe "CSS file structure" do
test "theme-layer2-attributes.css has both .preview-frame and .shop-root selectors" do
test "theme-layer2-attributes.css has .preview-frame variant selectors for editor" do
css_path = Path.join([File.cwd!(), "assets", "css", "theme-layer2-attributes.css"])
css_content = File.read!(css_path)
# Check that mood selectors exist for both
assert css_content =~ ".preview-frame[data-mood=\"dark\"]"
assert css_content =~ ".shop-root[data-mood=\"dark\"]"
assert css_content =~ ".preview-frame[data-mood=\"warm\"]"
assert css_content =~ ".shop-root[data-mood=\"warm\"]"
# Check typography selectors
assert css_content =~ ".preview-frame[data-typography=\"modern\"]"
assert css_content =~ ".shop-root[data-typography=\"modern\"]"
# Check shape selectors
assert css_content =~ ".preview-frame[data-shape=\"sharp\"]"
assert css_content =~ ".shop-root[data-shape=\"sharp\"]"
# Check descendant selectors (important for specificity)
assert css_content =~ ".preview-frame .product-grid"
assert css_content =~ ".shop-root .product-grid"
assert css_content =~ ".preview-frame .product-card"
assert css_content =~ ".shop-root .product-card"
# Variant selectors are editor-only (.preview-frame) using CSS nesting
# The file uses &[data-*] syntax inside .preview-frame { }
assert css_content =~ ".preview-frame"
assert css_content =~ "&[data-mood=\"dark\"]"
assert css_content =~ "&[data-mood=\"warm\"]"
assert css_content =~ "&[data-typography=\"modern\"]"
assert css_content =~ "&[data-shape=\"sharp\"]"
end
test "default selectors include both .preview-frame and .shop-root" do
test "theme-layer2-attributes.css has shared .themed component styles" do
css_path = Path.join([File.cwd!(), "assets", "css", "theme-layer2-attributes.css"])
css_content = File.read!(css_path)
# The default (neutral) mood should apply to both
# This regex checks for the pattern where both selectors are grouped
assert Regex.match?(
~r/\.preview-frame,\s*\n\.shop-root\s*\{[^}]*--t-surface-base/,
css_content
)
# Component styles use .themed for shared styling (both shop and preview)
assert css_content =~ ".themed"
# Uses CSS nesting syntax
assert css_content =~ "& .product-card"
assert css_content =~ "& .filter-pill"
end
end
describe "generated CSS cache" do
test "generated CSS includes theme variables" do
# Apply a preset
{:ok, settings} = Settings.apply_preset(:bold)
test "generated CSS includes ALL theme token categories" do
# Apply a preset with specific values
{:ok, settings} = Settings.apply_preset(:night)
# Generate CSS
css = SimpleshopTheme.Theme.CSSGenerator.generate(settings)
# Check that key variables are present
# Mood tokens (surface, text, border colors)
assert css =~ "--t-surface-base:"
assert css =~ "--t-text-primary:"
assert css =~ "--t-border-default:"
# Typography tokens
assert css =~ "--t-font-heading:"
assert css =~ "--t-font-body:"
assert css =~ "--t-heading-weight:"
# Shape tokens (border radii)
assert css =~ "--t-radius-sm:"
assert css =~ "--t-radius-button:"
assert css =~ "--t-radius-card:"
# Density tokens
assert css =~ "--t-density:"
assert css =~ "--space-md:"
assert css =~ "--space-lg:"
# Slider-controlled values
assert css =~ "--t-accent-h:"
assert css =~ "--t-accent-s:"
assert css =~ "--t-accent-l:"
@@ -141,6 +149,23 @@ defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do
assert css =~ "--t-heading-weight-override:"
end
test "generated CSS uses correct values for dark mood" do
{:ok, settings} = Settings.apply_preset(:night)
css = SimpleshopTheme.Theme.CSSGenerator.generate(settings)
# Dark mood should have dark surface colors
assert css =~ "--t-surface-base: #0a0a0a"
assert css =~ "--t-text-primary: #fafafa"
end
test "generated CSS uses correct values for warm mood" do
{:ok, settings} = Settings.apply_preset(:gallery)
css = SimpleshopTheme.Theme.CSSGenerator.generate(settings)
# Warm mood should have warm surface colors
assert css =~ "--t-surface-base: #fdf8f3"
end
test "CSS cache is warmed on startup and invalidated on settings change" do
# Ensure cache has content
SimpleshopTheme.Theme.CSSCache.warm()

View File

@@ -59,8 +59,8 @@ defmodule SimpleshopThemeWeb.ThemeLiveTest do
test "displays generated CSS in preview", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/admin/theme")
# CSS generator outputs accent colors and layout variables
assert html =~ ".preview-frame, .shop-root"
# CSS generator outputs accent colors and layout variables for shop pages
assert html =~ ".themed {"
assert html =~ "--t-accent-h:"
end