diff --git a/assets/css/app-shop.css b/assets/css/app-shop.css new file mode 100644 index 0000000..1b91dae --- /dev/null +++ b/assets/css/app-shop.css @@ -0,0 +1,58 @@ +/* Shop CSS - Tailwind without daisyUI + This is the CSS bundle for public shop pages (localhost:4001). + It excludes daisyUI which is only needed for admin pages. + See app.css for the full admin version. */ + +@import "tailwindcss" source(none); +@source "../css"; +@source "../js"; +/* Only scan shop-specific files, not admin pages */ +@source "../../lib/simpleshop_theme_web/live/shop_live"; +@source "../../lib/simpleshop_theme_web/components/shop_components.ex"; +@source "../../lib/simpleshop_theme_web/components/page_templates"; +@source "../../lib/simpleshop_theme_web/components/layouts/shop.html.heex"; +@source "../../lib/simpleshop_theme_web/components/layouts/shop_root.html.heex"; + +/* Heroicons plugin */ +@plugin "../vendor/heroicons"; + +/* NO daisyUI - shop pages use the custom .themed system instead */ + +/* Add variants based on LiveView classes */ +@custom-variant phx-click-loading (.phx-click-loading&, .phx-click-loading &); +@custom-variant phx-submit-loading (.phx-submit-loading&, .phx-submit-loading &); +@custom-variant phx-change-loading (.phx-change-loading&, .phx-change-loading &); + +/* Make LiveView wrapper divs transparent for layout */ +[data-phx-session], [data-phx-teleported-src] { display: contents } + +/* Theme CSS - Layer 1: Primitives (fixed CSS variables) */ +@import "./theme-primitives.css"; + +/* Theme CSS - Layer 2: Shared styles only (.themed selectors) + Note: .preview-frame rules are still included but unused on shop pages. + This is acceptable as it's only ~5KB and splitting adds complexity. */ +@import "./theme-layer2-attributes.css"; + +/* Theme CSS - Layer 3: Semantic aliases */ +@import "./theme-semantic.css"; + +/* Cart drawer open state styles */ +.cart-drawer.open { + right: 0 !important; +} + +.cart-drawer-overlay.open { + opacity: 1 !important; + visibility: visible !important; +} + +/* Product gallery thumbnail styles */ +.pdp-thumbnail { + border: 2px solid var(--t-border-default); + transition: border-color 0.15s ease; +} + +.pdp-thumbnail-active { + border-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); +} diff --git a/assets/css/app.css b/assets/css/app.css index b0cffbf..ab400ce 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -102,9 +102,6 @@ /* Make LiveView wrapper divs transparent for layout */ [data-phx-session], [data-phx-teleported-src] { display: contents } -/* Self-hosted fonts - all font-face declarations */ -@import "./fonts.css"; - /* Theme CSS - Layer 1: Primitives (fixed CSS variables) */ @import "./theme-primitives.css"; @@ -135,3 +132,167 @@ .pdp-thumbnail-active { border-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); } + +/* ============================================= + THEME EDITOR ONLY: .preview-frame CSS variable switching + These rules enable live theme switching in the editor. + Shop pages get CSS variables inline from CSSGenerator. + ============================================= */ + +.preview-frame { + /* Mood - Default (Neutral) */ + --t-surface-base: #ffffff; + --t-surface-raised: #ffffff; + --t-surface-sunken: #f5f5f5; + --t-surface-overlay: rgba(255, 255, 255, 0.95); + --t-text-primary: #171717; + --t-text-secondary: #525252; + --t-text-tertiary: #a3a3a3; + --t-text-inverse: #ffffff; + --t-border-default: #e5e5e5; + --t-border-subtle: #f0f0f0; + + /* Typography - Default (Clean) */ + --t-font-heading: var(--p-font-manrope); + --t-font-body: var(--p-font-inter); + --t-heading-weight: 600; + --t-heading-tracking: -0.02em; + + /* Shape - Default (Soft) */ + --t-radius-sm: var(--p-radius-sm); + --t-radius-md: var(--p-radius-md); + --t-radius-lg: var(--p-radius-lg); + --t-radius-button: var(--p-radius-md); + --t-radius-card: var(--p-radius-lg); + --t-radius-input: var(--p-radius-md); + --t-radius-image: var(--p-radius-md); + + /* Density - Default (Balanced) */ + --t-density: 1; + --space-xs: calc(var(--p-space-2) * var(--t-density)); + --space-sm: calc(var(--p-space-3) * var(--t-density)); + --space-md: calc(var(--p-space-4) * var(--t-density)); + --space-lg: calc(var(--p-space-6) * var(--t-density)); + --space-xl: calc(var(--p-space-8) * var(--t-density)); + --space-2xl: calc(var(--p-space-12) * var(--t-density)); + + /* Mood Variants */ + &[data-mood="warm"] { + --t-surface-base: #fdf8f3; + --t-surface-raised: #fffcf8; + --t-surface-sunken: #f5ebe0; + --t-text-primary: #1c1917; + --t-text-secondary: #57534e; + --t-text-tertiary: #a8a29e; + --t-border-default: #e7e0d8; + --t-border-subtle: #f0ebe4; + } + + &[data-mood="cool"] { + --t-surface-base: #f4f7fb; + --t-surface-raised: #f8fafc; + --t-surface-sunken: #e8eff7; + --t-text-primary: #0f172a; + --t-text-secondary: #475569; + --t-text-tertiary: #94a3b8; + --t-border-default: #d4dce8; + --t-border-subtle: #e8eff5; + } + + &[data-mood="dark"] { + --t-surface-base: #0a0a0a; + --t-surface-raised: #171717; + --t-surface-sunken: #000000; + --t-surface-overlay: rgba(23, 23, 23, 0.95); + --t-text-primary: #fafafa; + --t-text-secondary: #a3a3a3; + --t-text-tertiary: #737373; + --t-text-inverse: #171717; + --t-border-default: #262626; + --t-border-subtle: #1c1c1c; + --p-shadow-strength: 0.25; + } + + /* Typography Variants */ + &[data-typography="editorial"] { + --t-font-heading: var(--p-font-playfair); + --t-font-body: var(--p-font-raleway); + --t-heading-weight: 500; + --t-heading-tracking: -0.01em; + } + + &[data-typography="modern"] { + --t-font-heading: var(--p-font-space); + --t-font-body: var(--p-font-inter); + --t-heading-weight: 500; + --t-heading-tracking: -0.03em; + } + + &[data-typography="classic"] { + --t-font-heading: var(--p-font-cormorant); + --t-font-body: var(--p-font-source-serif); + --t-heading-weight: 500; + --t-heading-tracking: 0; + } + + &[data-typography="friendly"] { + --t-font-heading: var(--p-font-fraunces); + --t-font-body: var(--p-font-work-sans); + --t-heading-weight: 600; + --t-heading-tracking: -0.01em; + } + + &[data-typography="minimal"] { + --t-font-heading: var(--p-font-dm-sans); + --t-font-body: var(--p-font-source-serif); + --t-heading-weight: 500; + --t-heading-tracking: 0; + } + + &[data-typography="impulse"] { + --t-font-heading: var(--p-font-raleway); + --t-font-body: var(--p-font-inter); + --t-heading-weight: 300; + --t-heading-tracking: 0.02em; + } + + /* Shape Variants */ + &[data-shape="sharp"] { + --t-radius-sm: 0; + --t-radius-md: 0; + --t-radius-lg: 0; + --t-radius-button: 0; + --t-radius-card: 0; + --t-radius-input: 0; + --t-radius-image: 0; + } + + &[data-shape="round"] { + --t-radius-sm: var(--p-radius-md); + --t-radius-md: var(--p-radius-lg); + --t-radius-lg: var(--p-radius-xl); + --t-radius-button: var(--p-radius-lg); + --t-radius-card: var(--p-radius-xl); + --t-radius-input: var(--p-radius-lg); + --t-radius-image: var(--p-radius-lg); + } + + &[data-shape="pill"] { + --t-radius-sm: var(--p-radius-full); + --t-radius-md: var(--p-radius-full); + --t-radius-lg: var(--p-radius-xl); + --t-radius-button: var(--p-radius-full); + --t-radius-card: var(--p-radius-xl); + --t-radius-input: var(--p-radius-full); + --t-radius-image: var(--p-radius-lg); + } + + /* Density Variants */ + &[data-density="spacious"] { + --t-density: 1.25; + } + + &[data-density="compact"] { + --t-density: 0.85; + } +} diff --git a/assets/css/fonts.css b/assets/css/fonts.css deleted file mode 100644 index ad0d959..0000000 --- a/assets/css/fonts.css +++ /dev/null @@ -1,269 +0,0 @@ -/* Self-hosted Google Fonts - * All fonts loaded locally for privacy and performance. - * Browsers only download fonts actually used on the page. - */ - -/* Inter - Clean, Modern, Impulse presets (body) */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url('/fonts/inter-v20-latin-300.woff2') format('woff2'); -} -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/inter-v20-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/inter-v20-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/inter-v20-latin-600.woff2') format('woff2'); -} -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('/fonts/inter-v20-latin-700.woff2') format('woff2'); -} - -/* Manrope - Clean preset (heading) */ -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/manrope-v20-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/manrope-v20-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/manrope-v20-latin-600.woff2') format('woff2'); -} -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('/fonts/manrope-v20-latin-700.woff2') format('woff2'); -} - -/* Work Sans - Friendly preset (body) */ -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url('/fonts/work-sans-v24-latin-300.woff2') format('woff2'); -} -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/work-sans-v24-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/work-sans-v24-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Work Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/work-sans-v24-latin-600.woff2') format('woff2'); -} - -/* DM Sans - Minimal preset (heading) */ -@font-face { - font-family: 'DM Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/dm-sans-v17-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'DM Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/dm-sans-v17-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'DM Sans'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/dm-sans-v17-latin-600.woff2') format('woff2'); -} -@font-face { - font-family: 'DM Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('/fonts/dm-sans-v17-latin-700.woff2') format('woff2'); -} - -/* Raleway - Editorial, Impulse presets (body/heading) */ -@font-face { - font-family: 'Raleway'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url('/fonts/raleway-v37-latin-300.woff2') format('woff2'); -} -@font-face { - font-family: 'Raleway'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/raleway-v37-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Raleway'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/raleway-v37-latin-500.woff2') format('woff2'); -} - -/* Space Grotesk - Modern preset (heading) */ -@font-face { - font-family: 'Space Grotesk'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/space-grotesk-v22-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Space Grotesk'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/space-grotesk-v22-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Space Grotesk'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/space-grotesk-v22-latin-600.woff2') format('woff2'); -} - -/* Playfair Display - Editorial preset (heading) */ -@font-face { - font-family: 'Playfair Display'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/playfair-display-v40-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Playfair Display'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/playfair-display-v40-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Playfair Display'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('/fonts/playfair-display-v40-latin-700.woff2') format('woff2'); -} - -/* Cormorant Garamond - Classic preset (heading) */ -@font-face { - font-family: 'Cormorant Garamond'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/cormorant-garamond-v21-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Cormorant Garamond'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/cormorant-garamond-v21-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Cormorant Garamond'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/cormorant-garamond-v21-latin-600.woff2') format('woff2'); -} - -/* Source Serif 4 - Classic, Minimal presets (body) */ -@font-face { - font-family: 'Source Serif 4'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/source-serif-4-v14-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Source Serif 4'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/source-serif-4-v14-latin-600.woff2') format('woff2'); -} - -/* Fraunces - Friendly preset (heading) */ -@font-face { - font-family: 'Fraunces'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/fraunces-v38-latin-regular.woff2') format('woff2'); -} -@font-face { - font-family: 'Fraunces'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/fonts/fraunces-v38-latin-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Fraunces'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/fonts/fraunces-v38-latin-600.woff2') format('woff2'); -} -@font-face { - font-family: 'Fraunces'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('/fonts/fraunces-v38-latin-700.woff2') format('woff2'); -} diff --git a/assets/css/theme-layer2-attributes.css b/assets/css/theme-layer2-attributes.css index fd9e732..da67fe2 100644 --- a/assets/css/theme-layer2-attributes.css +++ b/assets/css/theme-layer2-attributes.css @@ -1,183 +1,12 @@ /* ======================================== - LAYER 2: THEME TOKENS (Attribute-based) + LAYER 2: THEME TOKENS (Shared Styles) ======================================== - ARCHITECTURE: - - .themed is the shared class for all themed content (shop + preview) - - .preview-frame uses data-attribute selectors for CSS variable switching (editor only) - - Shop pages get CSS variable values inline from CSSGenerator - - All visual/behavioral styles use .themed + This file contains .themed styles used by both shop and theme editor. + The .preview-frame CSS variable switching rules are in app.css (admin only). + Shop pages get CSS variables inline from CSSGenerator. ======================================== */ -/* ============================================= - CSS VARIABLE SWITCHING (Editor-only) - These set CSS variables based on data attributes. - Shop pages get these values inline from CSSGenerator. - ============================================= */ - -.preview-frame { - /* Mood - Default (Neutral) */ - --t-surface-base: #ffffff; - --t-surface-raised: #ffffff; - --t-surface-sunken: #f5f5f5; - --t-surface-overlay: rgba(255, 255, 255, 0.95); - --t-text-primary: #171717; - --t-text-secondary: #525252; - --t-text-tertiary: #a3a3a3; - --t-text-inverse: #ffffff; - --t-border-default: #e5e5e5; - --t-border-subtle: #f0f0f0; - - /* Typography - Default (Clean) */ - --t-font-heading: var(--p-font-manrope); - --t-font-body: var(--p-font-inter); - --t-heading-weight: 600; - --t-heading-tracking: -0.02em; - - /* Shape - Default (Soft) */ - --t-radius-sm: var(--p-radius-sm); - --t-radius-md: var(--p-radius-md); - --t-radius-lg: var(--p-radius-lg); - --t-radius-button: var(--p-radius-md); - --t-radius-card: var(--p-radius-lg); - --t-radius-input: var(--p-radius-md); - --t-radius-image: var(--p-radius-md); - - /* Density - Default (Balanced) */ - --t-density: 1; - --space-xs: calc(var(--p-space-2) * var(--t-density)); - --space-sm: calc(var(--p-space-3) * var(--t-density)); - --space-md: calc(var(--p-space-4) * var(--t-density)); - --space-lg: calc(var(--p-space-6) * var(--t-density)); - --space-xl: calc(var(--p-space-8) * var(--t-density)); - --space-2xl: calc(var(--p-space-12) * var(--t-density)); - - /* Mood Variants */ - &[data-mood="warm"] { - --t-surface-base: #fdf8f3; - --t-surface-raised: #fffcf8; - --t-surface-sunken: #f5ebe0; - --t-text-primary: #1c1917; - --t-text-secondary: #57534e; - --t-text-tertiary: #a8a29e; - --t-border-default: #e7e0d8; - --t-border-subtle: #f0ebe4; - } - - &[data-mood="cool"] { - --t-surface-base: #f4f7fb; - --t-surface-raised: #f8fafc; - --t-surface-sunken: #e8eff7; - --t-text-primary: #0f172a; - --t-text-secondary: #475569; - --t-text-tertiary: #94a3b8; - --t-border-default: #d4dce8; - --t-border-subtle: #e8eff5; - } - - &[data-mood="dark"] { - --t-surface-base: #0a0a0a; - --t-surface-raised: #171717; - --t-surface-sunken: #000000; - --t-surface-overlay: rgba(23, 23, 23, 0.95); - --t-text-primary: #fafafa; - --t-text-secondary: #a3a3a3; - --t-text-tertiary: #737373; - --t-text-inverse: #171717; - --t-border-default: #262626; - --t-border-subtle: #1c1c1c; - --p-shadow-strength: 0.25; - } - - /* Typography Variants */ - &[data-typography="editorial"] { - --t-font-heading: var(--p-font-playfair); - --t-font-body: var(--p-font-raleway); - --t-heading-weight: 500; - --t-heading-tracking: -0.01em; - } - - &[data-typography="modern"] { - --t-font-heading: var(--p-font-space); - --t-font-body: var(--p-font-inter); - --t-heading-weight: 500; - --t-heading-tracking: -0.03em; - } - - &[data-typography="classic"] { - --t-font-heading: var(--p-font-cormorant); - --t-font-body: var(--p-font-source-serif); - --t-heading-weight: 500; - --t-heading-tracking: 0; - } - - &[data-typography="friendly"] { - --t-font-heading: var(--p-font-fraunces); - --t-font-body: var(--p-font-work-sans); - --t-heading-weight: 600; - --t-heading-tracking: -0.01em; - } - - &[data-typography="minimal"] { - --t-font-heading: var(--p-font-dm-sans); - --t-font-body: var(--p-font-source-serif); - --t-heading-weight: 500; - --t-heading-tracking: 0; - } - - &[data-typography="impulse"] { - --t-font-heading: var(--p-font-raleway); - --t-font-body: var(--p-font-inter); - --t-heading-weight: 300; - --t-heading-tracking: 0.02em; - } - - /* Shape Variants */ - &[data-shape="sharp"] { - --t-radius-sm: 0; - --t-radius-md: 0; - --t-radius-lg: 0; - --t-radius-button: 0; - --t-radius-card: 0; - --t-radius-input: 0; - --t-radius-image: 0; - } - - &[data-shape="round"] { - --t-radius-sm: var(--p-radius-md); - --t-radius-md: var(--p-radius-lg); - --t-radius-lg: var(--p-radius-xl); - --t-radius-button: var(--p-radius-lg); - --t-radius-card: var(--p-radius-xl); - --t-radius-input: var(--p-radius-lg); - --t-radius-image: var(--p-radius-lg); - } - - &[data-shape="pill"] { - --t-radius-sm: var(--p-radius-full); - --t-radius-md: var(--p-radius-full); - --t-radius-lg: var(--p-radius-xl); - --t-radius-button: var(--p-radius-full); - --t-radius-card: var(--p-radius-xl); - --t-radius-input: var(--p-radius-full); - --t-radius-image: var(--p-radius-lg); - } - - /* Density Variants */ - &[data-density="spacious"] { - --t-density: 1.25; - } - - &[data-density="compact"] { - --t-density: 0.85; - } -} - -/* ============================================= - SHARED STYLES (Both Editor and Shop) - All visual/behavioral styles use .themed - ============================================= */ - .themed { /* Font size scale */ font-size: calc(16px * var(--t-font-size-scale, 1)); diff --git a/config/config.exs b/config/config.exs index 00eb410..72f8eef 100644 --- a/config/config.exs +++ b/config/config.exs @@ -63,6 +63,13 @@ config :tailwind, --output=priv/static/assets/css/app.css ), cd: Path.expand("..", __DIR__) + ], + simpleshop_theme_shop: [ + args: ~w( + --input=assets/css/app-shop.css + --output=priv/static/assets/css/app-shop.css + ), + cd: Path.expand("..", __DIR__) ] # Configures Elixir's Logger diff --git a/config/dev.exs b/config/dev.exs index 5f7f679..341d2b6 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -23,7 +23,8 @@ config :simpleshop_theme, SimpleshopThemeWeb.Endpoint, secret_key_base: "Jk04sYT/pzfZ0cywS+i0vCURPoQYgqAGa72uS8bv2gydLyusWFc08kJyEnQP4zgT", watchers: [ esbuild: {Esbuild, :install_and_run, [:simpleshop_theme, ~w(--sourcemap=inline --watch)]}, - tailwind: {Tailwind, :install_and_run, [:simpleshop_theme, ~w(--watch)]} + tailwind: {Tailwind, :install_and_run, [:simpleshop_theme, ~w(--watch)]}, + tailwind_shop: {Tailwind, :install_and_run, [:simpleshop_theme_shop, ~w(--watch)]} ] # ## SSL Support diff --git a/lib/simpleshop_theme_web/components/layouts/shop_root.html.heex b/lib/simpleshop_theme_web/components/layouts/shop_root.html.heex index 9b8ecd4..d74eb4e 100644 --- a/lib/simpleshop_theme_web/components/layouts/shop_root.html.heex +++ b/lib/simpleshop_theme_web/components/layouts/shop_root.html.heex @@ -13,7 +13,7 @@ ) do %> <% end %> - + diff --git a/lib/simpleshop_theme_web/live/theme_live/index.html.heex b/lib/simpleshop_theme_web/live/theme_live/index.html.heex index 72b0902..1217650 100644 --- a/lib/simpleshop_theme_web/live/theme_live/index.html.heex +++ b/lib/simpleshop_theme_web/live/theme_live/index.html.heex @@ -938,6 +938,11 @@ data-shadow={@theme_settings.card_shadow} data-button-style={@theme_settings.button_style}> diff --git a/mix.exs b/mix.exs index 677030d..07a82ba 100644 --- a/mix.exs +++ b/mix.exs @@ -86,9 +86,15 @@ defmodule SimpleshopTheme.MixProject do "ecto.reset": ["ecto.drop", "ecto.setup"], test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"], "assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"], - "assets.build": ["compile", "tailwind simpleshop_theme", "esbuild simpleshop_theme"], + "assets.build": [ + "compile", + "tailwind simpleshop_theme", + "tailwind simpleshop_theme_shop", + "esbuild simpleshop_theme" + ], "assets.deploy": [ "tailwind simpleshop_theme --minify", + "tailwind simpleshop_theme_shop --minify", "esbuild simpleshop_theme --minify", "phx.digest" ], diff --git a/test/simpleshop_theme_web/live/theme_css_consistency_test.exs b/test/simpleshop_theme_web/live/theme_css_consistency_test.exs index 72976a5..6054816 100644 --- a/test/simpleshop_theme_web/live/theme_css_consistency_test.exs +++ b/test/simpleshop_theme_web/live/theme_css_consistency_test.exs @@ -5,9 +5,9 @@ defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do 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 + - Theme editor uses .preview-frame[data-*] selectors for live switching (in app.css) + - Shop pages get theme values via inline CSS from CSSGenerator (app-shop.css) + - Component styles use .themed for shared styling (theme-layer2-attributes.css) """ use SimpleshopThemeWeb.ConnCase, async: false @@ -88,12 +88,13 @@ defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do end describe "CSS file structure" 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"]) + test "app.css has .preview-frame variant selectors for theme editor" do + css_path = Path.join([File.cwd!(), "assets", "css", "app.css"]) css_content = File.read!(css_path) # Variant selectors are editor-only (.preview-frame) using CSS nesting # The file uses &[data-*] syntax inside .preview-frame { } + # These rules are in app.css (admin only), not app-shop.css assert css_content =~ ".preview-frame" assert css_content =~ "&[data-mood=\"dark\"]" assert css_content =~ "&[data-mood=\"warm\"]"