Add WCAG AA compliant accent color variants and update default accent to meet 4.5:1 contrast ratio requirements. - Add --t-accent-text (darker for text on light backgrounds) - Add --t-accent-button (darker for button backgrounds with white text) - Change default accent from #3b82f6 to #2563eb (better contrast) - Update presets and tests for new default These changes ensure accent colors meet accessibility standards while maintaining visual consistency with the brand palette. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
227 lines
5.0 KiB
Elixir
227 lines
5.0 KiB
Elixir
defmodule SimpleshopTheme.Theme.Presets do
|
|
@moduledoc """
|
|
Defines the 8 curated theme presets for SimpleShop.
|
|
"""
|
|
|
|
@presets %{
|
|
gallery: %{
|
|
mood: "warm",
|
|
typography: "editorial",
|
|
shape: "soft",
|
|
density: "spacious",
|
|
grid_columns: "3",
|
|
header_layout: "centered",
|
|
accent_color: "#e85d04",
|
|
layout_width: "wide",
|
|
card_shadow: "sm",
|
|
announcement_bar: true
|
|
},
|
|
studio: %{
|
|
mood: "neutral",
|
|
typography: "clean",
|
|
shape: "soft",
|
|
density: "balanced",
|
|
grid_columns: "4",
|
|
header_layout: "standard",
|
|
accent_color: "#2563eb",
|
|
layout_width: "wide",
|
|
card_shadow: "sm",
|
|
announcement_bar: true
|
|
},
|
|
boutique: %{
|
|
mood: "warm",
|
|
typography: "classic",
|
|
shape: "soft",
|
|
density: "balanced",
|
|
grid_columns: "3",
|
|
header_layout: "left",
|
|
accent_color: "#b45309",
|
|
layout_width: "contained",
|
|
card_shadow: "md",
|
|
announcement_bar: true
|
|
},
|
|
bold: %{
|
|
mood: "neutral",
|
|
typography: "modern",
|
|
shape: "sharp",
|
|
density: "compact",
|
|
grid_columns: "4",
|
|
header_layout: "standard",
|
|
accent_color: "#dc2626",
|
|
layout_width: "full",
|
|
card_shadow: "none",
|
|
announcement_bar: true
|
|
},
|
|
playful: %{
|
|
mood: "neutral",
|
|
typography: "friendly",
|
|
shape: "pill",
|
|
density: "balanced",
|
|
grid_columns: "4",
|
|
header_layout: "standard",
|
|
accent_color: "#8b5cf6",
|
|
layout_width: "wide",
|
|
card_shadow: "md",
|
|
announcement_bar: true
|
|
},
|
|
minimal: %{
|
|
mood: "neutral",
|
|
typography: "impulse",
|
|
shape: "sharp",
|
|
density: "spacious",
|
|
grid_columns: "2",
|
|
header_layout: "standard",
|
|
accent_color: "#171717",
|
|
layout_width: "full",
|
|
card_shadow: "none",
|
|
announcement_bar: false
|
|
},
|
|
night: %{
|
|
mood: "dark",
|
|
typography: "modern",
|
|
shape: "soft",
|
|
density: "balanced",
|
|
grid_columns: "4",
|
|
header_layout: "standard",
|
|
accent_color: "#f97316",
|
|
layout_width: "wide",
|
|
card_shadow: "lg",
|
|
announcement_bar: true
|
|
},
|
|
classic: %{
|
|
mood: "warm",
|
|
typography: "classic",
|
|
shape: "soft",
|
|
density: "spacious",
|
|
grid_columns: "3",
|
|
header_layout: "standard",
|
|
accent_color: "#166534",
|
|
layout_width: "contained",
|
|
card_shadow: "sm",
|
|
announcement_bar: true
|
|
}
|
|
}
|
|
|
|
@descriptions %{
|
|
gallery: "Editorial serif headlines",
|
|
studio: "Clean modern sans-serif",
|
|
boutique: "Elegant classic serif",
|
|
bold: "Tech-forward geometric",
|
|
playful: "Quirky variable font",
|
|
minimal: "Light refined pairing",
|
|
night: "Dark tech aesthetic",
|
|
classic: "Traditional luxury serif"
|
|
}
|
|
|
|
# Core keys used to match presets (excludes branding-specific settings)
|
|
@core_keys ~w(mood typography shape density grid_columns header_layout accent_color layout_width card_shadow announcement_bar)a
|
|
|
|
@doc """
|
|
Returns all available presets.
|
|
|
|
## Examples
|
|
|
|
iex> all()
|
|
%{gallery: %{...}, studio: %{...}, ...}
|
|
|
|
"""
|
|
def all, do: @presets
|
|
|
|
@doc """
|
|
Gets a preset by name.
|
|
|
|
## Examples
|
|
|
|
iex> get(:gallery)
|
|
%{mood: "warm", typography: "editorial", ...}
|
|
|
|
iex> get(:nonexistent)
|
|
nil
|
|
|
|
"""
|
|
def get(preset_name) when is_atom(preset_name) do
|
|
Map.get(@presets, preset_name)
|
|
end
|
|
|
|
@doc """
|
|
Lists all preset names.
|
|
|
|
## Examples
|
|
|
|
iex> list_names()
|
|
[:gallery, :studio, :boutique, :bold, :playful, :minimal, :night, :classic]
|
|
|
|
"""
|
|
def list_names do
|
|
Map.keys(@presets)
|
|
end
|
|
|
|
@doc """
|
|
Gets the description for a preset.
|
|
|
|
## Examples
|
|
|
|
iex> get_description(:gallery)
|
|
"Elegant & editorial"
|
|
|
|
"""
|
|
def get_description(preset_name) when is_atom(preset_name) do
|
|
Map.get(@descriptions, preset_name, "")
|
|
end
|
|
|
|
@doc """
|
|
Returns all presets with their descriptions.
|
|
|
|
## Examples
|
|
|
|
iex> all_with_descriptions()
|
|
[{:bold, "High contrast, strong"}, ...]
|
|
|
|
"""
|
|
def all_with_descriptions do
|
|
@presets
|
|
|> Map.keys()
|
|
|> Enum.sort()
|
|
|> Enum.map(fn name -> {name, Map.get(@descriptions, name, "")} end)
|
|
end
|
|
|
|
@doc """
|
|
Detects which preset matches the current theme settings, if any.
|
|
Only compares core theme keys, ignoring branding-specific settings.
|
|
|
|
## Examples
|
|
|
|
iex> detect_preset(%ThemeSettings{mood: "warm", typography: "editorial", ...})
|
|
:gallery
|
|
|
|
iex> detect_preset(%ThemeSettings{...customized...})
|
|
nil
|
|
|
|
"""
|
|
def detect_preset(theme_settings) do
|
|
current_core = extract_core_values(theme_settings)
|
|
|
|
Enum.find_value(@presets, fn {name, preset} ->
|
|
preset_core = Map.take(preset, @core_keys)
|
|
|
|
if maps_match?(current_core, preset_core) do
|
|
name
|
|
else
|
|
nil
|
|
end
|
|
end)
|
|
end
|
|
|
|
defp extract_core_values(theme_settings) do
|
|
theme_settings
|
|
|> Map.from_struct()
|
|
|> Map.take(@core_keys)
|
|
end
|
|
|
|
defp maps_match?(map1, map2) do
|
|
Enum.all?(@core_keys, fn key ->
|
|
Map.get(map1, key) == Map.get(map2, key)
|
|
end)
|
|
end
|
|
end
|