From 476ec9667abc7bbb483b770765fed6a99b5d5a2e Mon Sep 17 00:00:00 2001 From: Jamey Greenwood Date: Wed, 31 Dec 2025 00:24:53 +0000 Subject: [PATCH] fix: add data attributes and Google Fonts to enable theme visual changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Google Fonts link to root layout for typography presets - Add data-mood, data-typography, data-shape, data-density attributes to preview-frame - Create theme-layer2-attributes.css with attribute-based CSS from demo - Move theme CSS files from priv/static/css to assets/css for proper compilation - Update CSS import order (primitives → layer2 → semantic) - Add 'css' to static_paths to serve theme CSS files This fixes the issue where theme controls updated the database but didn't visually affect the preview. The demo's attribute-based CSS system is now properly integrated with the Phoenix LiveView implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- assets/css/app.css | 7 +- assets/css/theme-layer2-attributes.css | 157 ++++++++++++++++ .../css/theme-primitives.css | 0 .../static => assets}/css/theme-semantic.css | 0 lib/simpleshop_theme_web.ex | 2 +- .../components/layouts/root.html.heex | 3 + .../live/theme_live/index.ex | 70 ++++++++ .../live/theme_live/index.html.heex | 168 ++++++++++++++---- .../live/theme_live_test.exs | 97 ++++++++++ 9 files changed, 470 insertions(+), 34 deletions(-) create mode 100644 assets/css/theme-layer2-attributes.css rename {priv/static => assets}/css/theme-primitives.css (100%) rename {priv/static => assets}/css/theme-semantic.css (100%) diff --git a/assets/css/app.css b/assets/css/app.css index 80cefc4..d0ca8e7 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -103,9 +103,12 @@ [data-phx-session], [data-phx-teleported-src] { display: contents } /* Theme CSS - Layer 1: Primitives (fixed CSS variables) */ -@import url("/css/theme-primitives.css"); +@import "./theme-primitives.css"; + +/* Theme CSS - Layer 2: Attribute-based theme tokens */ +@import "./theme-layer2-attributes.css"; /* Theme CSS - Layer 3: Semantic aliases */ -@import url("/css/theme-semantic.css"); +@import "./theme-semantic.css"; /* This file is for your main application CSS */ diff --git a/assets/css/theme-layer2-attributes.css b/assets/css/theme-layer2-attributes.css new file mode 100644 index 0000000..f711f00 --- /dev/null +++ b/assets/css/theme-layer2-attributes.css @@ -0,0 +1,157 @@ +/* ======================================== + LAYER 2: THEME TOKENS (Attribute-based) + ======================================== */ + +/* Mood - Default (Neutral) */ +.preview-frame { + --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; +} + +.preview-frame[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; +} + +.preview-frame[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; +} + +.preview-frame[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 - Default (Clean/Inter) */ +.preview-frame { + --t-font-heading: var(--p-font-inter); + --t-font-body: var(--p-font-inter); + --t-heading-weight: 600; + --t-heading-tracking: -0.025em; +} + +.preview-frame[data-typography="editorial"] { + --t-font-heading: var(--p-font-fraunces); + --t-font-body: var(--p-font-source); + --t-heading-weight: 600; + --t-heading-tracking: -0.02em; +} + +.preview-frame[data-typography="modern"] { + --t-font-heading: var(--p-font-space); + --t-font-body: var(--p-font-space); + --t-heading-weight: 500; + --t-heading-tracking: -0.03em; +} + +.preview-frame[data-typography="classic"] { + --t-font-heading: var(--p-font-baskerville); + --t-font-body: var(--p-font-source); + --t-heading-weight: 400; + --t-heading-tracking: 0; +} + +.preview-frame[data-typography="friendly"] { + --t-font-heading: var(--p-font-nunito); + --t-font-body: var(--p-font-nunito); + --t-heading-weight: 700; + --t-heading-tracking: -0.01em; +} + +.preview-frame[data-typography="minimal"] { + --t-font-heading: var(--p-font-outfit); + --t-font-body: var(--p-font-outfit); + --t-heading-weight: 300; + --t-heading-tracking: 0; +} + +.preview-frame[data-typography="impulse"] { + --t-font-heading: var(--p-font-avenir); + --t-font-body: var(--p-font-avenir); + --t-heading-weight: 300; + --t-heading-tracking: 0.02em; +} + +/* Shape - Default (Soft) */ +.preview-frame { + --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); +} + +.preview-frame[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; +} + +.preview-frame[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); +} + +.preview-frame[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 - Default (Balanced) */ +.preview-frame { + --t-density: 1; +} + +.preview-frame[data-density="spacious"] { + --t-density: 1.25; +} + +.preview-frame[data-density="compact"] { + --t-density: 0.85; +} diff --git a/priv/static/css/theme-primitives.css b/assets/css/theme-primitives.css similarity index 100% rename from priv/static/css/theme-primitives.css rename to assets/css/theme-primitives.css diff --git a/priv/static/css/theme-semantic.css b/assets/css/theme-semantic.css similarity index 100% rename from priv/static/css/theme-semantic.css rename to assets/css/theme-semantic.css diff --git a/lib/simpleshop_theme_web.ex b/lib/simpleshop_theme_web.ex index ce56225..8389413 100644 --- a/lib/simpleshop_theme_web.ex +++ b/lib/simpleshop_theme_web.ex @@ -17,7 +17,7 @@ defmodule SimpleshopThemeWeb do those modules here. """ - def static_paths, do: ~w(assets fonts images favicon.ico robots.txt) + def static_paths, do: ~w(assets css fonts images favicon.ico robots.txt) def router do quote do diff --git a/lib/simpleshop_theme_web/components/layouts/root.html.heex b/lib/simpleshop_theme_web/components/layouts/root.html.heex index 5f32636..6811c47 100644 --- a/lib/simpleshop_theme_web/components/layouts/root.html.heex +++ b/lib/simpleshop_theme_web/components/layouts/root.html.heex @@ -8,6 +8,9 @@ {assigns[:page_title]} + + +