- sidebar nav grouped under Shop/Content/Settings section headers with
subtle uppercase labels (#105)
- custom page settings now show inline in a collapsible panel within the
editor instead of navigating away to a separate page (#107)
- admin editor preview loads real products and categories from the DB,
falling back to PreviewData only on fresh installs (#108)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- rename "Providers" to "Print providers" in sidebar (#110)
- add LiveView navigation guard to EditorKeyboard hook — intercepts
link clicks in capture phase when editor has unsaved changes (#103)
- add description field to all 26 block types, shown as subtitle in
block picker; filter searches descriptions too (#104)
- add visible column headers (Label / Path) and proper sr-only labels
with for attributes on nav editor inputs (#106)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Legal pages (privacy, delivery, terms) now auto-populate content from
shop settings on mount, show auto-generated vs customised badges, and
have a regenerate button. Theme editor gains alt text fields for logo,
header, and icon images. Image picker in page builder now has an upload
button and alt text warning badges. Clearing unused image references
shows an orphan info flash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New block types: spacer, divider, button/CTA, video embed (YouTube,
Vimeo with privacy-enhanced embeds, fallback for unknown URLs).
Page templates (blank, content, landing) shown when creating custom
pages. Duplicate page action on admin index with slug deduplication.
Fix block picker on shop edit sidebar being cut off on mobile by
accounting for bottom nav and making the grid scrollable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Chrome's font boosting algorithm was applying different text size
multipliers across pages, making the announcement bar font size
inconsistent between home and collection views on mobile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
History stacks (@history/@future) on both admin editor and live sidebar,
capped at 50 entries. All mutations routed through apply_mutation for
consistent history tracking. EditorKeyboard JS hook combines DirtyGuard
with Ctrl+Z/Ctrl+Shift+Z. Settings panel fade-in animation. 10 new tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded header, footer and mobile nav with settings-driven
loops. Nav items stored as JSON via Settings, loaded in ThemeHook with
sensible defaults. New admin navigation editor at /admin/navigation
for add/remove/reorder/save/reset. Mobile bottom nav also driven from
header nav items with icon mapping by slug.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New settings form for creating and editing custom page metadata
(title, slug, meta description, published, nav settings). Pages
index shows custom pages section with draft badges and delete.
Editor shows settings button for custom pages, hides reset to
defaults. 20 new tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The error-main flex container was missing flex-direction: column,
which went unnoticed until the page builder added a second block
(featured_products) to the error page defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire up image field in block settings with a modal picker that
browses the media library. Fix picker thumbnails collapsing to
14px by replacing overflow:hidden with overflow:clip on grid
items (hidden sets min-height:0 in grid context). Polish media
library mobile sheet with scrim overlay and tighter spacing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admins can now edit pages directly on the live shop by clicking the
pencil icon in the header. A sidebar slides in with block management
controls (add, remove, reorder, edit settings, save, reset, done).
Key features:
- PageEditorHook on_mount with handle_params/event/info hooks
- BlockEditor pure functions extracted from admin editor
- Shared BlockEditorComponents with event_prefix namespacing
- Collapsible sidebar: X closes it, header pencil reopens it
- Backdrop overlay dismisses sidebar on tap
- Conditional admin.css loading for logged-in users
- content_body block now portable (textarea setting + rich text fallback)
13 integration tests, 26 unit tests, 1370 total passing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces typed settings schema with SettingsField struct, replaces
the read-only JSON textarea with a full repeater UI for info_card items.
Supports add, remove, reorder and inline editing of repeater items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stage 6 of the page builder: admin UI at /admin/pages for managing
page layouts. Page list shows all 14 pages grouped by category.
Editor supports reorder (up/down), add, remove, duplicate, save,
and reset to defaults. DirtyGuard JS hook warns on unsaved changes.
ARIA live regions announce block operations for screen readers.
Also: regenerate admin icons (81 rules via mix task with @layer
wrapping), add gen_smtp dep for SMTP email adapter, add :key to
page renderer block loop for correct LiveView diffing.
1309 tests, 0 failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Home, Content (about/delivery/privacy/terms), Contact, and ErrorHTML
now render through the generic PageRenderer instead of hardcoded
templates. Block wrapper divs enable CSS grid targeting. Featured
products block supports layout/card_variant/columns settings for
different page contexts. Contact page uses CSS grid on data-block-type
attributes for two-column layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Variant options (colour, size) are now URL params handled via
handle_params instead of phx-click events. Swatches and size buttons
render as patch links in shop mode, so changing variants works as
plain navigation without JS. Quantity is now a number input that
submits with the form. Unavailable variants render as disabled spans.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every key shop flow now works via plain HTML forms when JS is
unavailable. LiveView progressively enhances when JS connects.
- PDP: form wraps variant/qty/add-to-cart with action="/cart/add"
- Cart page: qty +/- and remove use form POST fallbacks
- Cart/search header icons are now links with phx-click enhancement
- Collection sort form has GET action + noscript submit button
- New /search page with form-based search for no-JS users
- CartController gains add/remove/update_item POST actions
- CartHook gains update_quantity_form and remove_item_form handlers
- Fix flaky analytics tests caused by event table pollution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The stretched-link::after overlay (z-index: 0) was blocked by
product-card-image-wrap (z-index: 1), so only the title text was
actually clickable. Wrapping the image area in a <.link> component
directly fixes this — taps/clicks bubble up to the link naturally,
and touch-scroll on the image carousel still works on mobile.
Also corrects the mode check: ThemeHook sets mode: :shop on shop pages,
not :live, so the condition is now mode != :preview (consistent with
how the title link already worked).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.themed-button-outline:where(a) { display: inline-block } (line 2147)
was beating .social-link-card-item { display: flex } (line 1796) —
same layer, same specificity, later wins. Used the parent selector
.social-link-card-list .social-link-card-item to give display: flex
higher specificity (2 classes vs 1) so it wins.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Follows the same fix as inputs — the --t-text-small override was making
inline form buttons smaller than other buttons on the page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
card-inline-form was overriding input font-size to --t-text-small, making
inline inputs noticeably smaller than contact form inputs. Removed the
override so all inputs inherit the same base size from the themed container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
themed-input was inheriting font size from its container rather than
being set explicitly, so contact form inputs appeared larger than
the compact inline inputs in cards like "Track your order".
Added font-size: var(--t-text-base) to the contact form context rule
so both inputs are intentionally sized rather than relying on inheritance.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
themed-input had no padding in the base style, so inputs were rendering
with browser-default 1px/2px padding. Added 0.5rem 0.75rem as the base.
Also fixed the contact form CSS which was targeting .shop-input (a class
that doesn't exist on rendered elements) instead of .themed-input.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Capitalise lead sentence regardless of shop_name value
- Add stripe.com/privacy URL when mentioning Stripe in payment section
- Remove mention of logout from session cookie description
- Make third-party sharing text provider-agnostic (no longer names Printify etc.)
- Add :updated_at block to privacy, delivery, and terms pages showing when
content last changed — auto-tracked via content hash, so the date advances
automatically whenever relevant settings change
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Magic link flow on contact page: customer enters email, gets a
time-limited signed link, clicks through to /orders showing all their
paid orders and full detail pages with thumbnails and product links.
- OrderLookupController generates/verifies Phoenix.Token signed links
- Contact LiveView handles lookup_orders + reset_tracking events
- Orders and OrderDetail LiveViews gated by session email
- Order detail shows thumbnails, links to products still available
- .themed-button gets base padding/font-weight so all usages are consistent
- order-summary-card sticky scoped to .cart-grid (was leaking to orders list)
- 27 new tests (1095 total)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each stat card now shows the percentage change vs the equivalent
previous period (e.g. 30d compares last 30 days vs 30 days before).
Handles zero-baseline with "new" label and caps extreme deltas at
>999%. Seed data extended to 2 years for meaningful 12m comparisons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add visitors_by_hour query for hourly breakdown on "today" period
- replace SVG-only chart with HTML/CSS grid layout (bars + labels)
- Y-axis scale with nice rounded max, midpoint, and zero
- X-axis date labels (formatted as "Feb 18") spaced evenly
- adaptive bar gaps (1px for sparse data, 0 for 365-day dense view)
- labels use real HTML text so they're readable on mobile
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Card radio component for picking email providers (SMTP, SendGrid, Mailjet, etc.)
with instant client-side switching via JS hook. Adapter configs are pre-rendered
and toggled without a server round-trip. Secrets are preserved when re-saving
with blank password fields. Includes from address field, test email sending,
and disconnect flow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Setup wizard no longer requires email delivery. Admin account is
auto-confirmed and auto-logged-in via token redirect. Adds setup
secret gate for prod (logged on boot), SMTP env var config in
runtime.exs, email_configured? helper, and admin warning banner
when email isn't set up. Includes plan files for this task and
the follow-up email settings UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 7: add LoadTheme to base :browser pipeline so auth and setup
pages get theme settings. Update root.html.heex with .themed wrapper,
font preloads, layer declaration, and generated CSS injection.
Remove old data-theme JS toggle script.
Phase 8: upgrade admin/reset.css to a proper @layer reset matching
the shop reset structure. Remove dead theme toggle CSS rules.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 4: admin components and utilities now reference --t-* theme
tokens directly. Status colour tokens added to theme-semantic.css.
Bridge file (admin/themes.css) deleted.
Phase 5: removed duplicated .preview-frame CSS block (~160 lines).
Admin components and icons wrapped in @layer admin. Layer order
updated in admin_root to include admin layer.
Phase 6: added prefers-reduced-motion support (zeroes all durations
and disables animations). Migrated physical properties to logical
equivalents (text-align start/end, margin-inline, padding-inline,
inset-inline-end) across shop and admin CSS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 3: Rewrite admin/themes.css as a bridge mapping DaisyUI --color-*
variables to --t-* theme tokens. Admin components still reference
--color-base-100, --color-primary etc but these now resolve through the
bridge to the merchant's theme colours. Status colours (error, success,
warning, info) stay hardcoded.
Add .themed wrapper with data-mood to admin_root.html.heex. Remove the
old data-theme JS toggle script — dark mode now comes from the theme
system mood setting. Admin inherits theme colours, typography, and shape
from the merchant's chosen theme.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1: Replace hex_to_hsl with hex_to_oklch in CSSGenerator, output
--t-accent-l/c/h instead of --t-accent-h/s/l. All 46 HSL accent
references across theme-semantic.css, theme-layer2-attributes.css, and
shop/components.css replaced with oklch/color-mix equivalents. Dead
style*= attribute selectors for button variants replaced with proper
class-based selectors. Added color-scheme: light/dark to mood output.
Phase 2: Add LoadTheme plug to admin pipeline, extend AdminLayoutHook
with theme_settings and generated_css assigns, add font preloads and
generated CSS injection to admin_root.html.heex. No visual changes to
admin yet — .themed wrapper added in next phase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
phase 1 (no admin): show only the email form
phase 2 (admin created, not logged in): "check your inbox" gate with
"wrong email? start over" link that deletes the unconfirmed user
phase 3 (logged in via magic link): show provider + stripe steps
removes the confusing redirect to /users/log-in after account creation.
users now stay on /setup throughout the entire setup process.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
add system font stack to admin reset CSS so setup/admin pages render
sans-serif on all devices instead of falling through to browser default.
pass path_resolver to CSSGenerator.generate on cache miss paths so
font URLs resolve to digested paths in production.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace non-existent --color-base-content-60 variable (with hard-coded
black fallback) with color-mix(in oklch, var(--color-base-content) 60%,
transparent) which adapts to the current theme.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- new /setup page with three-section onboarding (account, provider, payments)
- dashboard launch checklist with progress bar, go-live, dismiss
- provider registry on Provider module (single source of truth for metadata)
- payments registry for Stripe
- setup context made provider-agnostic (provider_connected, theme_customised, etc.)
- admin provider pages now fully registry-driven (no hardcoded provider names)
- auth flow: fresh installs redirect to /setup, signed_in_path respects setup state
- removed old /admin/setup wizard
- 840 tests, 0 failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Coming soon page was unstyled after Tailwind removal — replaced
utility classes with proper component CSS. Also removed dead h-full
classes from shop_root layout, adding height: 100% to the shop reset.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Providers was a hidden page with no nav link. Also adds
LiveDashboard and ErrorTracker to the sidebar footer, and
styles the provider dropdown menu items.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the setup stepper out of the dashboard into its own LiveView.
Dashboard now redirects to setup when site isn't live, and shows
stats-only view once live. Also cleans up button component variant
handling, fixes alert CSS, and removes stale demo.html.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
replace Tailwind CLI with esbuild for admin CSS bundling. admin now uses
hand-written utility classes (admin/utilities.css), static heroicon CSS
generated by mix generate_admin_icons, plain CSS colour themes extracted
from DaisyUI plugin config, and minimal resets. rename app.css to admin.css
for clarity alongside shop.css. delete vendor/daisyui-theme.js and
vendor/heroicons.js. no Tailwind dependency remains in the project.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add admin/components.css with custom admin-* component classes replacing
all DaisyUI component usage across admin LiveViews, auth pages, layout,
and core_components. Delete daisyui.js vendor file (246KB). Theme plugin
stays for color variables until Phase 7.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace all Tailwind utilities in content.ex and collection.ex with
semantic CSS classes (content body, contact form, cards, reviews, etc.)
- Delete app-shop.css (Tailwind shop entry point)
- Remove shop Tailwind config from config.exs, dev.exs, mix.exs
- Remove shop Tailwind stylesheet link from shop_root.html.heex
- Add collection filter bar, empty state, and select dropdown styles
- Fix filter pill sizing (use theme font vars instead of hardcoded rem)
- Fix active pill contrast (tinted accent background + dark accent text)
- Fix --t-text-on-accent fallback for pill legibility
- Add padding/font-size to .themed-select
Shop pages now use zero Tailwind. Admin Tailwind remains for Phase 6.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove ~140 Tailwind utility classes from product.ex and cart.ex, replacing
with semantic CSS classes in components.css. Delete helper functions that
generated Tailwind class strings (card_classes, image_container_classes,
content_padding_class, title_classes, hero_cta_classes, grid_classes).
Use data-* attributes for variant styling, grid columns, and sticky
positioning. Update theme-layer2 selectors for renamed classes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Absorb ~100 Tailwind utility classes from layout.ex and all page
templates into semantic CSS rules in components.css. Uses theme font-size
vars (--t-text-small, --t-text-caption) instead of rem to respect the
theme's em-based scaling system.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>