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:
@@ -10,10 +10,16 @@ defmodule SimpleshopTheme.Theme.CSSGeneratorTest do
|
||||
css = CSSGenerator.generate(settings)
|
||||
|
||||
assert is_binary(css)
|
||||
assert css =~ ".preview-frame, .shop-root"
|
||||
# CSS targets .themed (used by both shop and preview)
|
||||
assert css =~ ".themed {"
|
||||
assert css =~ "--t-accent-h:"
|
||||
assert css =~ "--t-accent-s:"
|
||||
assert css =~ "--t-accent-l:"
|
||||
# Should include all theme token categories
|
||||
assert css =~ "--t-surface-base:"
|
||||
assert css =~ "--t-font-heading:"
|
||||
assert css =~ "--t-radius-sm:"
|
||||
assert css =~ "--t-density:"
|
||||
end
|
||||
|
||||
test "converts hex colors to HSL" do
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user