fix: add data attributes and Google Fonts to enable theme visual changes
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,76 @@ defmodule SimpleshopThemeWeb.ThemeLive.Index do
|
||||
{:noreply, assign(socket, :preview_page, page_atom)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("update_setting", %{"field" => field, "setting_value" => value}, socket) do
|
||||
field_atom = String.to_existing_atom(field)
|
||||
attrs = %{field_atom => value}
|
||||
|
||||
case Settings.update_theme_settings(attrs) do
|
||||
{:ok, theme_settings} ->
|
||||
generated_css = CSSGenerator.generate(theme_settings)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:theme_settings, theme_settings)
|
||||
|> assign(:generated_css, generated_css)
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, _} ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("update_setting", %{"field" => field} = params, socket) do
|
||||
# For phx-change events from select/input elements, the value comes from the name attribute
|
||||
value = params[field] || params["#{field}_text"] || params["value"]
|
||||
|
||||
if value do
|
||||
field_atom = String.to_existing_atom(field)
|
||||
attrs = %{field_atom => value}
|
||||
|
||||
case Settings.update_theme_settings(attrs) do
|
||||
{:ok, theme_settings} ->
|
||||
generated_css = CSSGenerator.generate(theme_settings)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:theme_settings, theme_settings)
|
||||
|> assign(:generated_css, generated_css)
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, _} ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
else
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("update_color", %{"field" => field, "value" => value}, socket) do
|
||||
field_atom = String.to_existing_atom(field)
|
||||
attrs = %{field_atom => value}
|
||||
|
||||
case Settings.update_theme_settings(attrs) do
|
||||
{:ok, theme_settings} ->
|
||||
generated_css = CSSGenerator.generate(theme_settings)
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:theme_settings, theme_settings)
|
||||
|> assign(:generated_css, generated_css)
|
||||
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, _} ->
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("save_theme", _params, socket) do
|
||||
socket = put_flash(socket, :info, "Theme saved successfully")
|
||||
|
||||
@@ -35,38 +35,137 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Settings Display -->
|
||||
<!-- Customization Controls -->
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- Mood -->
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold mb-4">Current Settings</h2>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex justify-between">
|
||||
<span class="font-medium">Mood:</span>
|
||||
<span class="capitalize"><%= @theme_settings.mood %></span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="font-medium">Typography:</span>
|
||||
<span class="capitalize"><%= @theme_settings.typography %></span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="font-medium">Shape:</span>
|
||||
<span class="capitalize"><%= @theme_settings.shape %></span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="font-medium">Density:</span>
|
||||
<span class="capitalize"><%= @theme_settings.density %></span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="font-medium">Accent Color:</span>
|
||||
<span>
|
||||
<span
|
||||
class="inline-block w-4 h-4 rounded-sm border border-base-300"
|
||||
style={"background-color: #{@theme_settings.accent_color}"}
|
||||
>
|
||||
</span>
|
||||
<%= @theme_settings.accent_color %>
|
||||
</span>
|
||||
</div>
|
||||
<h3 class="font-semibold mb-3">Mood</h3>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<%= for mood <- ["neutral", "warm", "cool", "dark"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
phx-value-field="mood"
|
||||
phx-value-setting_value={mood}
|
||||
class={"btn btn-sm #{if @theme_settings.mood == mood, do: "btn-primary", else: "btn-outline"} capitalize"}
|
||||
>
|
||||
<%= mood %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Typography -->
|
||||
<div>
|
||||
<h3 class="font-semibold mb-3">Typography</h3>
|
||||
<form phx-change="update_setting" phx-value-field="typography">
|
||||
<select
|
||||
name="typography"
|
||||
class="select select-bordered select-sm w-full capitalize"
|
||||
>
|
||||
<%= for typo <- ["clean", "editorial", "modern", "classic", "friendly", "minimal", "impulse"] do %>
|
||||
<option value={typo} selected={@theme_settings.typography == typo}>
|
||||
<%= typo %>
|
||||
</option>
|
||||
<% end %>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Shape -->
|
||||
<div>
|
||||
<h3 class="font-semibold mb-3">Shape</h3>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<%= for shape <- ["sharp", "soft", "round", "pill"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
phx-value-field="shape"
|
||||
phx-value-setting_value={shape}
|
||||
class={"btn btn-sm #{if @theme_settings.shape == shape, do: "btn-primary", else: "btn-outline"} capitalize"}
|
||||
>
|
||||
<%= shape %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Density -->
|
||||
<div>
|
||||
<h3 class="font-semibold mb-3">Density</h3>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<%= for density <- ["spacious", "balanced", "compact"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
phx-value-field="density"
|
||||
phx-value-setting_value={density}
|
||||
class={"btn btn-sm #{if @theme_settings.density == density, do: "btn-primary", else: "btn-outline"} capitalize text-xs"}
|
||||
>
|
||||
<%= density %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grid Columns -->
|
||||
<div>
|
||||
<h3 class="font-semibold mb-3">Grid Columns</h3>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<%= for cols <- ["2", "3", "4"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
phx-value-field="grid_columns"
|
||||
phx-value-setting_value={cols}
|
||||
class={"btn btn-sm #{if @theme_settings.grid_columns == cols, do: "btn-primary", else: "btn-outline"}"}
|
||||
>
|
||||
<%= cols %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colors -->
|
||||
<div>
|
||||
<h3 class="font-semibold mb-3">Accent Color</h3>
|
||||
<div class="flex gap-2 items-center">
|
||||
<input
|
||||
type="color"
|
||||
phx-change="update_color"
|
||||
phx-value-field="accent_color"
|
||||
value={@theme_settings.accent_color}
|
||||
class="w-12 h-10 rounded cursor-pointer"
|
||||
phx-debounce="300"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
phx-change="update_color"
|
||||
phx-value-field="accent_color"
|
||||
value={@theme_settings.accent_color}
|
||||
class="input input-bordered input-sm flex-1 font-mono text-xs"
|
||||
placeholder="#000000"
|
||||
phx-debounce="blur"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Header Layout -->
|
||||
<div>
|
||||
<h3 class="font-semibold mb-3">Header Layout</h3>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<%= for layout <- ["standard", "centered", "minimal"] do %>
|
||||
<button
|
||||
type="button"
|
||||
phx-click="update_setting"
|
||||
phx-value-field="header_layout"
|
||||
phx-value-setting_value={layout}
|
||||
class={"btn btn-sm #{if @theme_settings.header_layout == layout, do: "btn-primary", else: "btn-outline"} capitalize text-xs"}
|
||||
>
|
||||
<%= layout %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,7 +202,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Preview Frame -->
|
||||
<div class="preview-frame bg-white overflow-auto" style="min-height: 600px; max-height: calc(100vh - 200px);">
|
||||
<div class="preview-frame bg-white overflow-auto"
|
||||
data-mood={@theme_settings.mood}
|
||||
data-typography={@theme_settings.typography}
|
||||
data-shape={@theme_settings.shape}
|
||||
data-density={@theme_settings.density}
|
||||
data-grid={@theme_settings.grid_columns}
|
||||
data-header={@theme_settings.header_layout}
|
||||
style="min-height: 600px; max-height: calc(100vh - 200px);">
|
||||
<style>
|
||||
<%= Phoenix.HTML.raw(@generated_css) %>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user