replace DaisyUI components with admin CSS, remove DaisyUI plugin (Phase 6)

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>
This commit is contained in:
jamey 2026-02-17 23:05:01 +00:00
parent b25e04d1b2
commit af0b0c217f
15 changed files with 751 additions and 1189 deletions

View File

@ -0,0 +1,610 @@
/* Admin component styles replaces DaisyUI components.
Uses DaisyUI theme variables (--color-*) which stay until Phase 7. */
/* ── Layout ── */
.admin-layout {
display: grid;
grid-template-columns: 0 1fr;
height: 100%;
}
.admin-layout-toggle {
position: absolute;
opacity: 0;
pointer-events: none;
}
.admin-layout-content {
display: flex;
flex-direction: column;
min-height: 100vh;
min-height: 100dvh;
grid-column: 2;
}
.admin-sidebar-wrapper {
grid-column: 1;
grid-row: 1;
z-index: 40;
display: none;
}
/* When checkbox is checked, show sidebar overlay on mobile */
.admin-layout-toggle:checked ~ .admin-sidebar-wrapper {
display: block;
position: fixed;
inset: 0;
z-index: 40;
}
.admin-sidebar-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.3);
cursor: pointer;
}
.admin-sidebar {
position: relative;
z-index: 1;
width: 16rem;
min-height: 100%;
display: flex;
flex-direction: column;
background-color: var(--color-base-200);
}
@media (min-width: 64em) {
.admin-layout {
grid-template-columns: 16rem 1fr;
}
.admin-sidebar-wrapper {
display: block;
position: relative;
}
.admin-sidebar-overlay {
display: none;
}
.admin-topbar {
display: none;
}
}
/* ── Top bar (mobile only) ── */
.admin-topbar {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
background-color: var(--color-base-100);
border-bottom: 1px solid var(--color-base-200);
}
.admin-topbar-title {
flex: 1;
font-size: 1.125rem;
font-weight: 600;
}
/* ── Sidebar nav ── */
.admin-nav {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.125rem;
& li a,
& li button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
text-decoration: none;
color: inherit;
font-size: 0.875rem;
transition: background-color 0.1s;
cursor: pointer;
background: none;
border: none;
width: 100%;
text-align: left;
&:hover {
background-color: var(--color-base-300);
}
&.active {
background-color: var(--color-base-300);
font-weight: 600;
}
}
}
/* ── Buttons ── */
.admin-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
line-height: 1.25rem;
border-radius: 0.375rem;
border: 1px solid transparent;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
transition: background-color 0.1s, opacity 0.1s;
background-color: var(--color-primary);
color: var(--color-primary-content);
&:hover { opacity: 0.85; }
&:disabled { opacity: 0.5; cursor: not-allowed; }
}
.admin-btn-primary {
background-color: var(--color-primary);
color: var(--color-primary-content);
}
.admin-btn-soft {
background-color: color-mix(in oklch, var(--color-primary) 12%, transparent);
color: var(--color-primary);
&:hover { background-color: color-mix(in oklch, var(--color-primary) 20%, transparent); opacity: 1; }
}
.admin-btn-ghost {
background: none;
color: inherit;
&:hover { background-color: var(--color-base-200); opacity: 1; }
}
.admin-btn-outline {
background: none;
color: inherit;
border-color: var(--color-base-300);
&:hover { background-color: var(--color-base-200); opacity: 1; }
}
.admin-btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
.admin-btn-icon {
padding: 0.5rem;
aspect-ratio: 1;
border-radius: 0.375rem;
}
.admin-btn-icon-round {
padding: 0.25rem;
aspect-ratio: 1;
border-radius: 9999px;
}
/* ── Cards ── */
.admin-card {
background-color: var(--color-base-100);
border: 1px solid var(--color-base-200);
border-radius: 0.5rem;
overflow: hidden;
}
.admin-card-body {
padding: 1rem 1.25rem;
}
.admin-card-title {
font-weight: 600;
margin-bottom: 0.75rem;
}
.admin-card-actions {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
padding-top: 0.75rem;
border-top: 1px solid var(--color-base-200);
margin-top: 0.75rem;
}
/* ── Tables ── */
.admin-table {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
& th {
text-align: left;
padding: 0.5rem 0.75rem;
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: color-mix(in oklch, var(--color-base-content) 60%, transparent);
border-bottom: 1px solid var(--color-base-200);
}
& td {
padding: 0.5rem 0.75rem;
border-bottom: 1px solid color-mix(in oklch, var(--color-base-200) 50%, transparent);
vertical-align: middle;
}
& tbody tr:last-child td {
border-bottom: none;
}
}
.admin-table-zebra tbody tr:nth-child(even) {
background-color: color-mix(in oklch, var(--color-base-200) 30%, transparent);
}
/* ── Forms ── */
.admin-fieldset {
margin-bottom: 0.5rem;
}
.admin-label {
display: block;
font-size: 0.875rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
.admin-input,
.admin-select,
.admin-textarea {
display: block;
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
line-height: 1.5;
color: var(--color-base-content);
background-color: var(--color-base-100);
border: 1px solid var(--color-base-300);
border-radius: 0.375rem;
transition: border-color 0.15s;
&:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 1px color-mix(in oklch, var(--color-primary) 20%, transparent);
}
}
.admin-select {
appearance: auto;
}
.admin-input-error {
border-color: var(--color-error);
&:focus {
border-color: var(--color-error);
box-shadow: 0 0 0 1px color-mix(in oklch, var(--color-error) 20%, transparent);
}
}
.admin-checkbox {
width: 1rem;
height: 1rem;
accent-color: var(--color-primary);
vertical-align: middle;
cursor: pointer;
}
.admin-checkbox-sm {
width: 0.875rem;
height: 0.875rem;
}
.admin-select-sm,
.admin-input-sm {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
/* ── Toggle ── */
.admin-toggle {
appearance: none;
width: 2.5rem;
height: 1.25rem;
background-color: var(--color-base-300);
border-radius: 9999px;
position: relative;
cursor: pointer;
transition: background-color 0.2s;
vertical-align: middle;
flex-shrink: 0;
&::after {
content: "";
position: absolute;
top: 0.125rem;
left: 0.125rem;
width: 1rem;
height: 1rem;
background: white;
border-radius: 9999px;
transition: transform 0.2s;
}
&:checked {
background-color: var(--color-primary);
}
&:checked::after {
transform: translateX(1.25rem);
}
}
.admin-toggle-sm {
width: 2rem;
height: 1rem;
&::after {
width: 0.75rem;
height: 0.75rem;
}
&:checked::after {
transform: translateX(1rem);
}
}
/* ── Feedback ── */
.admin-toast {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 50;
}
.admin-alert {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
}
.admin-alert-info {
background-color: color-mix(in oklch, var(--color-info) 10%, transparent);
color: var(--color-info);
border: 1px solid color-mix(in oklch, var(--color-info) 25%, transparent);
}
.admin-alert-error {
background-color: color-mix(in oklch, var(--color-error) 10%, transparent);
color: var(--color-error);
border: 1px solid color-mix(in oklch, var(--color-error) 25%, transparent);
}
/* ── Modal ── */
.admin-modal {
border: none;
border-radius: 0.75rem;
padding: 0;
max-width: 32rem;
width: calc(100% - 2rem);
background-color: var(--color-base-100);
color: var(--color-base-content);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
&::backdrop {
background: rgba(0, 0, 0, 0.4);
}
}
.admin-modal-box {
padding: 1.5rem;
position: relative;
}
.admin-modal-close {
position: absolute;
right: 0.5rem;
top: 0.5rem;
}
.admin-modal-actions {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
margin-top: 1.5rem;
}
/* ── Badge ── */
.admin-badge {
display: inline-flex;
align-items: center;
padding: 0.125rem 0.5rem;
font-size: 0.75rem;
font-weight: 500;
border-radius: 9999px;
background-color: var(--color-base-200);
color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
}
.admin-badge-sm {
padding: 0 0.375rem;
font-size: 0.625rem;
}
/* ── Dropdown ── */
.admin-dropdown {
position: relative;
display: inline-block;
& .admin-dropdown-content {
display: none;
position: absolute;
right: 0;
top: 100%;
margin-top: 0.25rem;
min-width: 12rem;
background-color: var(--color-base-100);
border: 1px solid var(--color-base-200);
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
z-index: 50;
padding: 0.25rem;
}
&:focus-within .admin-dropdown-content,
&[open] .admin-dropdown-content {
display: block;
}
}
/* ── Spinner ── */
@keyframes admin-spin {
to { transform: rotate(360deg); }
}
.admin-spinner {
display: inline-block;
width: 1.25rem;
height: 1.25rem;
border: 2px solid var(--color-base-300);
border-top-color: var(--color-primary);
border-radius: 9999px;
animation: admin-spin 0.6s linear infinite;
}
/* ── Divider ── */
.admin-divider {
display: flex;
align-items: center;
gap: 1rem;
color: color-mix(in oklch, var(--color-base-content) 40%, transparent);
font-size: 0.75rem;
&::before,
&::after {
content: "";
flex: 1;
height: 1px;
background-color: var(--color-base-300);
}
}
/* ── List ── */
.admin-list {
list-style: none;
padding: 0;
margin: 0;
}
.admin-list-row {
display: flex;
gap: 1rem;
padding: 0.75rem 0;
border-bottom: 1px solid var(--color-base-200);
&:last-child { border-bottom: none; }
}
.admin-list-grow {
flex: 1;
}
/* ── Form error text ── */
.admin-error {
color: var(--color-error);
}
/* ── Link ── */
.admin-link {
color: var(--color-primary);
text-decoration: underline;
text-underline-offset: 0.125em;
&:hover {
opacity: 0.8;
}
}
/* ── Range ── */
.admin-range {
appearance: none;
width: 100%;
height: 0.375rem;
background-color: var(--color-base-300);
border-radius: 9999px;
cursor: pointer;
vertical-align: middle;
&::-webkit-slider-thumb {
appearance: none;
width: 1rem;
height: 1rem;
background: var(--color-primary);
border-radius: 9999px;
border: 2px solid var(--color-base-100);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
&::-moz-range-thumb {
width: 1rem;
height: 1rem;
background: var(--color-primary);
border-radius: 9999px;
border: 2px solid var(--color-base-100);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
}
/* ── Alert outline ── */
.admin-alert-outline {
background: none;
color: color-mix(in oklch, var(--color-base-content) 70%, transparent);
border: 1px solid var(--color-base-300);
}
/* ── Swap (visibility toggle) ── */
.admin-swap {
position: relative;
display: inline-flex;
& .admin-swap-on,
& .admin-swap-off {
display: none;
}
& .admin-swap-off { display: inline-flex; }
&.active .admin-swap-on { display: inline-flex; }
&.active .admin-swap-off { display: none; }
}

View File

@ -10,13 +10,6 @@
The heroicons installation itself is managed by your mix.exs */
@plugin "../vendor/heroicons";
/* daisyUI Tailwind Plugin. You can update this file by fetching the latest version with:
curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui.js
Make sure to look at the daisyUI changelog: https://daisyui.com/docs/changelog/ */
@plugin "../vendor/daisyui" {
themes: false;
}
/* daisyUI theme plugin. You can update this file by fetching the latest version with:
curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui-theme.js
We ship with two themes, a light one inspired on Phoenix colors and a dark one inspired
@ -111,7 +104,8 @@
/* Theme CSS - Layer 3: Semantic aliases */
@import "./theme-semantic.css";
/* This file is for your main application CSS */
/* Admin component styles (replaces DaisyUI components) */
@import "./admin/components.css";
/* Cart drawer open state styles */
.cart-drawer.open {

1031
assets/vendor/daisyui.js vendored

File diff suppressed because one or more lines are too long

View File

@ -8,16 +8,7 @@ defmodule SimpleshopThemeWeb.CoreComponents do
with doc strings and declarative assigns. You may customize and style
them in any way you want, based on your application growth and needs.
The foundation for styling is Tailwind CSS, a utility-first CSS framework,
augmented with daisyUI, a Tailwind CSS plugin that provides UI components
and themes. Here are useful references:
* [daisyUI](https://daisyui.com/docs/intro/) - a good place to get
started and see the available components.
* [Tailwind CSS](https://tailwindcss.com) - the foundational framework
we build on. You will use it for layout, sizing, flexbox, grid, and
spacing.
Styled with custom admin CSS (`assets/css/admin/components.css`).
* [Heroicons](https://heroicons.com) - see `icon/1` for usage.
@ -56,13 +47,13 @@ defmodule SimpleshopThemeWeb.CoreComponents do
id={@id}
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
role="alert"
class="toast toast-top toast-end z-50"
class="admin-toast"
{@rest}
>
<div class={[
"alert w-80 sm:w-96 max-w-80 sm:max-w-96 text-wrap",
@kind == :info && "alert-info",
@kind == :error && "alert-error"
"admin-alert w-80 sm:w-96 max-w-80 sm:max-w-96 text-wrap",
@kind == :info && "admin-alert-info",
@kind == :error && "admin-alert-error"
]}>
<.icon :if={@kind == :info} name="hero-information-circle" class="size-5 shrink-0" />
<.icon :if={@kind == :error} name="hero-exclamation-circle" class="size-5 shrink-0" />
@ -94,11 +85,11 @@ defmodule SimpleshopThemeWeb.CoreComponents do
slot :inner_block, required: true
def button(%{rest: rest} = assigns) do
variants = %{"primary" => "btn-primary", nil => "btn-primary btn-soft"}
variants = %{"primary" => "admin-btn-primary", nil => "admin-btn-primary admin-btn-soft"}
assigns =
assign_new(assigns, :class, fn ->
["btn", Map.fetch!(variants, assigns[:variant])]
["admin-btn", Map.fetch!(variants, assigns[:variant])]
end)
if rest[:href] || rest[:navigate] || rest[:patch] do
@ -185,17 +176,17 @@ defmodule SimpleshopThemeWeb.CoreComponents do
end)
~H"""
<div class="fieldset mb-2">
<div class="admin-fieldset">
<label>
<input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} />
<span class="label">
<span class="admin-label">
<input
type="checkbox"
id={@id}
name={@name}
value="true"
checked={@checked}
class={@class || "checkbox checkbox-sm"}
class={@class || "admin-checkbox admin-checkbox-sm"}
{@rest}
/>{@label}
</span>
@ -207,13 +198,13 @@ defmodule SimpleshopThemeWeb.CoreComponents do
def input(%{type: "select"} = assigns) do
~H"""
<div class="fieldset mb-2">
<div class="admin-fieldset">
<label>
<span :if={@label} class="label mb-1">{@label}</span>
<span :if={@label} class="admin-label">{@label}</span>
<select
id={@id}
name={@name}
class={[@class || "w-full select", @errors != [] && (@error_class || "select-error")]}
class={[@class || "admin-select", @errors != [] && (@error_class || "admin-input-error")]}
multiple={@multiple}
{@rest}
>
@ -228,15 +219,15 @@ defmodule SimpleshopThemeWeb.CoreComponents do
def input(%{type: "textarea"} = assigns) do
~H"""
<div class="fieldset mb-2">
<div class="admin-fieldset">
<label>
<span :if={@label} class="label mb-1">{@label}</span>
<span :if={@label} class="admin-label">{@label}</span>
<textarea
id={@id}
name={@name}
class={[
@class || "w-full textarea",
@errors != [] && (@error_class || "textarea-error")
@class || "admin-textarea",
@errors != [] && (@error_class || "admin-input-error")
]}
{@rest}
>{Phoenix.HTML.Form.normalize_value("textarea", @value)}</textarea>
@ -249,17 +240,17 @@ defmodule SimpleshopThemeWeb.CoreComponents do
# All other inputs text, datetime-local, url, password, etc. are handled here...
def input(assigns) do
~H"""
<div class="fieldset mb-2">
<div class="admin-fieldset">
<label>
<span :if={@label} class="label mb-1">{@label}</span>
<span :if={@label} class="admin-label">{@label}</span>
<input
type={@type}
name={@name}
id={@id}
value={Phoenix.HTML.Form.normalize_value(@type, @value)}
class={[
@class || "w-full input",
@errors != [] && (@error_class || "input-error")
@class || "admin-input",
@errors != [] && (@error_class || "admin-input-error")
]}
{@rest}
/>
@ -272,7 +263,7 @@ defmodule SimpleshopThemeWeb.CoreComponents do
# Helper used by inputs to generate form errors
defp error(assigns) do
~H"""
<p class="mt-1.5 flex gap-2 items-center text-sm text-error">
<p class="admin-error mt-1.5 flex gap-2 items-center text-sm">
<.icon name="hero-exclamation-circle" class="size-5" />
{render_slot(@inner_block)}
</p>
@ -334,7 +325,7 @@ defmodule SimpleshopThemeWeb.CoreComponents do
end
~H"""
<table class="table table-zebra">
<table class="admin-table admin-table-zebra">
<thead>
<tr>
<th :for={col <- @col}>{col[:label]}</th>
@ -381,9 +372,9 @@ defmodule SimpleshopThemeWeb.CoreComponents do
def list(assigns) do
~H"""
<ul class="list">
<li :for={item <- @item} class="list-row">
<div class="list-col-grow">
<ul class="admin-list">
<li :for={item <- @item} class="admin-list-row">
<div class="admin-list-grow">
<div class="font-bold">{item.title}</div>
<div>{render_slot(item)}</div>
</div>
@ -497,14 +488,14 @@ defmodule SimpleshopThemeWeb.CoreComponents do
~H"""
<dialog
id={@id}
class="modal"
class="admin-modal"
phx-mounted={@show && show_modal(@id)}
phx-remove={hide_modal(@id)}
>
<div class="modal-box max-w-lg">
<form method="dialog">
<div class="admin-modal-box">
<form method="dialog" class="admin-modal-close">
<button
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
class="admin-btn admin-btn-ghost admin-btn-icon-round admin-btn-sm"
phx-click={@on_cancel}
aria-label={gettext("close")}
>
@ -512,13 +503,10 @@ defmodule SimpleshopThemeWeb.CoreComponents do
</button>
</form>
{render_slot(@inner_block)}
<div :if={@actions != []} class="modal-action">
<div :if={@actions != []} class="admin-modal-actions">
{render_slot(@actions)}
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button phx-click={@on_cancel}>close</button>
</form>
</dialog>
"""
end

View File

@ -1,23 +1,21 @@
<div class="drawer lg:drawer-open h-full">
<input id="admin-drawer" type="checkbox" class="drawer-toggle" />
<div class="admin-layout h-full">
<input id="admin-drawer" type="checkbox" class="admin-layout-toggle" />
<%!-- main content area --%>
<div class="drawer-content flex flex-col min-h-screen">
<div class="admin-layout-content">
<%!-- mobile header --%>
<header class="navbar bg-base-100 border-b border-base-200 lg:hidden">
<div class="flex-none">
<label for="admin-drawer" class="btn btn-square btn-ghost" aria-label="Open navigation">
<.icon name="hero-bars-3" class="size-5" />
</label>
</div>
<div class="flex-1">
<span class="text-lg font-semibold">SimpleShop</span>
</div>
<div class="flex-none">
<.link href={~p"/"} class="btn btn-ghost btn-sm">
<.icon name="hero-arrow-top-right-on-square-mini" class="size-4" /> Shop
</.link>
</div>
<header class="admin-topbar">
<label
for="admin-drawer"
class="admin-btn admin-btn-ghost admin-btn-icon"
aria-label="Open navigation"
>
<.icon name="hero-bars-3" class="size-5" />
</label>
<span class="admin-topbar-title">SimpleShop</span>
<.link href={~p"/"} class="admin-btn admin-btn-ghost admin-btn-sm">
<.icon name="hero-arrow-top-right-on-square-mini" class="size-4" /> Shop
</.link>
</header>
<%!-- page content --%>
@ -29,9 +27,9 @@
</div>
<%!-- sidebar --%>
<div class="drawer-side z-40">
<label for="admin-drawer" class="drawer-overlay" aria-label="Close navigation"></label>
<aside class="bg-base-200 w-64 min-h-full flex flex-col">
<div class="admin-sidebar-wrapper">
<label for="admin-drawer" class="admin-sidebar-overlay" aria-label="Close navigation"></label>
<aside class="admin-sidebar">
<%!-- sidebar header --%>
<div class="p-4 border-b border-base-300">
<.link navigate={~p"/admin"} class="text-lg font-bold tracking-tight">
@ -44,7 +42,7 @@
<%!-- nav links --%>
<nav class="flex-1 p-2" aria-label="Admin navigation">
<ul class="menu gap-0.5">
<ul class="admin-nav">
<li>
<.link
navigate={~p"/admin"}
@ -90,7 +88,7 @@
<%!-- sidebar footer --%>
<div class="p-2 border-t border-base-300">
<ul class="menu gap-0.5">
<ul class="admin-nav">
<li>
<.link href={~p"/"}>
<.icon name="hero-arrow-top-right-on-square" class="size-5" /> View shop

View File

@ -43,9 +43,9 @@ defmodule SimpleshopThemeWeb.Admin.OrderShow do
<div class="grid gap-6 mt-6 lg:grid-cols-2">
<%!-- order info --%>
<div class="card bg-base-100 shadow-sm border border-base-200">
<div class="card-body">
<h3 class="card-title text-base">Order details</h3>
<div class="admin-card">
<div class="admin-card-body">
<h3 class="admin-card-title">Order details</h3>
<.list>
<:item title="Date">{format_date(@order.inserted_at)}</:item>
<:item title="Customer">{@order.customer_email || ""}</:item>
@ -61,9 +61,9 @@ defmodule SimpleshopThemeWeb.Admin.OrderShow do
</div>
<%!-- shipping address --%>
<div class="card bg-base-100 shadow-sm border border-base-200">
<div class="card-body">
<h3 class="card-title text-base">Shipping address</h3>
<div class="admin-card">
<div class="admin-card-body">
<h3 class="admin-card-title">Shipping address</h3>
<%= if @order.shipping_address != %{} do %>
<.list>
<:item :if={@order.shipping_address["name"]} title="Name">
@ -96,10 +96,10 @@ defmodule SimpleshopThemeWeb.Admin.OrderShow do
</div>
<%!-- fulfilment --%>
<div class="card bg-base-100 shadow-sm border border-base-200 mt-6">
<div class="card-body">
<div class="admin-card mt-6">
<div class="admin-card-body">
<div class="flex items-center justify-between">
<h3 class="card-title text-base">Fulfilment</h3>
<h3 class="admin-card-title">Fulfilment</h3>
<.fulfilment_badge status={@order.fulfilment_status} />
</div>
<.list>
@ -114,7 +114,7 @@ defmodule SimpleshopThemeWeb.Admin.OrderShow do
</:item>
<:item :if={@order.tracking_number} title="Tracking">
<%= if @order.tracking_url do %>
<a href={@order.tracking_url} target="_blank" class="link link-primary">
<a href={@order.tracking_url} target="_blank" class="admin-link">
{@order.tracking_number}
</a>
<% else %>
@ -135,7 +135,7 @@ defmodule SimpleshopThemeWeb.Admin.OrderShow do
<button
:if={can_submit?(@order)}
phx-click="submit_to_provider"
class="btn btn-primary btn-sm"
class="admin-btn admin-btn-primary admin-btn-sm"
>
<.icon name="hero-paper-airplane-mini" class="size-4" />
{if @order.fulfilment_status == "failed",
@ -145,7 +145,7 @@ defmodule SimpleshopThemeWeb.Admin.OrderShow do
<button
:if={can_refresh?(@order)}
phx-click="refresh_status"
class="btn btn-ghost btn-sm"
class="admin-btn admin-btn-ghost admin-btn-sm"
>
<.icon name="hero-arrow-path-mini" class="size-4" /> Refresh status
</button>
@ -154,10 +154,10 @@ defmodule SimpleshopThemeWeb.Admin.OrderShow do
</div>
<%!-- line items --%>
<div class="card bg-base-100 shadow-sm border border-base-200 mt-6">
<div class="card-body">
<h3 class="card-title text-base">Items</h3>
<table class="table table-zebra">
<div class="admin-card mt-6">
<div class="admin-card-body">
<h3 class="admin-card-title">Items</h3>
<table class="admin-table admin-table-zebra">
<thead>
<tr>
<th>Product</th>

View File

@ -109,13 +109,13 @@ defmodule SimpleshopThemeWeb.Admin.Orders do
phx-click="filter"
phx-value-status={@status}
class={[
"btn btn-sm",
@active && "btn-primary",
!@active && "btn-ghost"
"admin-btn admin-btn-sm",
@active && "admin-btn-primary",
!@active && "admin-btn-ghost"
]}
>
{@label}
<span :if={@count > 0} class="badge badge-sm ml-1">{@count}</span>
<span :if={@count > 0} class="admin-badge admin-badge-sm ml-1">{@count}</span>
</button>
"""
end

View File

@ -106,12 +106,15 @@ defmodule SimpleshopThemeWeb.Admin.ProductShow do
href={provider_edit_url(@product)}
target="_blank"
rel="noopener"
class="btn btn-ghost btn-sm"
class="admin-btn admin-btn-ghost admin-btn-sm"
>
Edit on {provider_label(@product)}
<.icon name="hero-arrow-top-right-on-square-mini" class="size-4" />
</.link>
<.link navigate={~p"/products/#{@product.slug}"} class="btn btn-ghost btn-sm">
<.link
navigate={~p"/products/#{@product.slug}"}
class="admin-btn admin-btn-ghost admin-btn-sm"
>
View on shop <.icon name="hero-arrow-top-right-on-square-mini" class="size-4" />
</.link>
</:actions>
@ -136,9 +139,9 @@ defmodule SimpleshopThemeWeb.Admin.ProductShow do
<p :if={@product.images == []} class="text-base-content/40 text-sm">No images</p>
</div>
<div class="card bg-base-100 shadow-sm border border-base-200">
<div class="card-body">
<h3 class="card-title text-base">Details</h3>
<div class="admin-card">
<div class="admin-card-body">
<h3 class="admin-card-title">Details</h3>
<.list>
<:item :if={@product.provider_connection} title="Provider">
{provider_label(@product)} via {@product.provider_connection.name}
@ -161,19 +164,19 @@ defmodule SimpleshopThemeWeb.Admin.ProductShow do
<%!-- storefront controls --%>
<div class="card bg-base-100 shadow-sm border border-base-200 mt-6">
<div class="card-body">
<h3 class="card-title text-base">Storefront controls</h3>
<div class="admin-card-body">
<h3 class="admin-card-title">Storefront controls</h3>
<.form
for={@form}
phx-submit="save_storefront"
phx-change="validate_storefront"
class="flex flex-wrap gap-4 items-end"
>
<label class="form-control w-auto">
<span class="label-text text-xs mb-0.5">Visibility</span>
<label class="w-auto">
<span class="text-xs mb-0.5">Visibility</span>
<select
name="product[visible]"
class="select select-bordered select-sm"
class="admin-select admin-select-sm"
aria-label="Visibility"
>
<option
@ -190,25 +193,25 @@ defmodule SimpleshopThemeWeb.Admin.ProductShow do
</option>
</select>
</label>
<label class="form-control w-auto flex-1 min-w-48">
<span class="label-text text-xs mb-0.5">Category</span>
<label class="w-auto flex-1 min-w-48">
<span class="text-xs mb-0.5">Category</span>
<input
type="text"
name="product[category]"
value={@form[:category].value}
class="input input-bordered input-sm"
class="admin-input admin-input-sm"
placeholder="e.g. Apparel"
/>
</label>
<.button type="submit" class="btn-sm btn-primary">Save</.button>
<.button type="submit" class="admin-btn-sm admin-btn-primary">Save</.button>
</.form>
</div>
</div>
<%!-- variants --%>
<div class="card bg-base-100 shadow-sm border border-base-200 mt-6">
<div class="card-body">
<h3 class="card-title text-base">Variants ({length(@product.variants)})</h3>
<div class="admin-card-body">
<h3 class="admin-card-title">Variants ({length(@product.variants)})</h3>
<.table id="variants" rows={@product.variants}>
<:col :let={variant} label="Options">{ProductVariant.options_title(variant)}</:col>
<:col :let={variant} label="SKU">{variant.sku || ""}</:col>
@ -242,8 +245,8 @@ defmodule SimpleshopThemeWeb.Admin.ProductShow do
:if={@product.provider_connection}
class="card bg-base-100 shadow-sm border border-base-200 mt-6"
>
<div class="card-body">
<h3 class="card-title text-base">Provider data</h3>
<div class="admin-card-body">
<h3 class="admin-card-title">Provider data</h3>
<.list>
<:item title="Provider">
{provider_label(@product)} via {@product.provider_connection.name}
@ -253,7 +256,7 @@ defmodule SimpleshopThemeWeb.Admin.ProductShow do
<:item title="Sync status">{@product.provider_connection.sync_status}</:item>
</.list>
<div class="mt-4">
<button phx-click="resync" class="btn btn-outline btn-sm">
<button phx-click="resync" class="admin-btn admin-btn-outline admin-btn-sm">
<.icon name="hero-arrow-path" class="size-4" /> Re-sync
</button>
</div>

View File

@ -166,7 +166,7 @@ defmodule SimpleshopThemeWeb.Admin.Products do
aria-pressed={to_string(product.visible)}
aria-label={"Toggle visibility for #{product.title}"}
class={[
"swap swap-rotate btn btn-ghost btn-xs",
"admin-btn admin-btn-ghost admin-btn-sm",
product.visible && "text-green-600",
!product.visible && "text-base-content/30"
]}
@ -181,7 +181,7 @@ defmodule SimpleshopThemeWeb.Admin.Products do
<.icon name="hero-cube" class="size-12 mx-auto mb-4" />
<p class="text-lg font-medium">No products yet</p>
<p class="text-sm mt-1">
<.link navigate={~p"/admin/providers"} class="link link-primary">
<.link navigate={~p"/admin/providers"} class="admin-link">
Connect a provider
</.link>
to sync your products.
@ -196,9 +196,9 @@ defmodule SimpleshopThemeWeb.Admin.Products do
defp filter_select(assigns) do
~H"""
<label class="form-control w-auto">
<span class="label-text text-xs mb-0.5">{@label}</span>
<select name={@name} class="select select-bordered select-sm" aria-label={@label}>
<label class="w-auto">
<span class="text-xs mb-0.5">{@label}</span>
<select name={@name} class="admin-select admin-select-sm" aria-label={@label}>
<option :for={{label, value} <- @options} value={value} selected={value == @value}>
{label}
</option>

View File

@ -22,7 +22,7 @@
href="https://printify.com/app/auth/login"
target="_blank"
rel="noopener"
class="link"
class="admin-link"
>
Log in to Printify
</a>
@ -30,7 +30,7 @@
href="https://printify.com/app/auth/register"
target="_blank"
rel="noopener"
class="link"
class="admin-link"
>create a free account</a>)
</li>
<li>Click <strong>Account</strong> (top right)</li>
@ -52,7 +52,7 @@
href="https://www.printful.com/auth/login"
target="_blank"
rel="noopener"
class="link"
class="admin-link"
>
Log in to Printful
</a>
@ -60,7 +60,7 @@
href="https://www.printful.com/auth/signup"
target="_blank"
rel="noopener"
class="link"
class="admin-link"
>create a free account</a>)
</li>
<li>Go to <strong>Settings</strong> &rarr; <strong>API access</strong></li>
@ -90,7 +90,7 @@
<div class="flex items-center gap-3 mb-6">
<button
type="button"
class="btn btn-outline btn-sm"
class="admin-btn admin-btn-outline admin-btn-sm"
phx-click="test_connection"
disabled={@testing}
>
@ -127,7 +127,7 @@
do: "Connect to #{provider_label(@provider_type)}",
else: "Save changes"}
</.button>
<.link navigate={~p"/admin/providers"} class="btn btn-ghost">
<.link navigate={~p"/admin/providers"} class="admin-btn admin-btn-ghost">
Cancel
</.link>
</div>

View File

@ -1,11 +1,11 @@
<.header>
Providers
<:actions>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-primary">
<div class="admin-dropdown">
<div tabindex="0" role="button" class="admin-btn admin-btn-primary">
<.icon name="hero-plus" class="size-4 mr-1" /> Connect provider
</div>
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box shadow-lg w-52 z-10">
<ul tabindex="0" class="admin-dropdown-content">
<li>
<.link navigate={~p"/admin/providers/new?type=printify"}>Printify</.link>
</li>
@ -29,7 +29,7 @@
<.button navigate={~p"/admin/providers/new?type=printify"}>
Connect Printify
</.button>
<.button navigate={~p"/admin/providers/new?type=printful"} class="btn-outline">
<.button navigate={~p"/admin/providers/new?type=printful"} class="admin-btn-outline">
Connect Printful
</.button>
</div>
@ -38,9 +38,9 @@
<div
:for={{dom_id, connection} <- @streams.connections}
id={dom_id}
class="card bg-base-100 shadow-sm border border-base-200"
class="admin-card"
>
<div class="card-body">
<div class="admin-card-body">
<div class="flex items-start justify-between gap-4">
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
@ -58,7 +58,7 @@
<div class="flex items-center gap-2">
<.link
navigate={~p"/admin/providers/#{connection.id}/edit"}
class="btn btn-ghost btn-sm"
class="admin-btn admin-btn-ghost admin-btn-sm"
>
Settings
</.link>
@ -66,19 +66,19 @@
phx-click="delete"
phx-value-id={connection.id}
data-confirm={"Disconnect from #{String.capitalize(connection.provider_type)}? Your synced products will remain in your shop."}
class="btn btn-ghost btn-sm text-error"
class="admin-btn admin-btn-ghost admin-btn-sm text-error"
>
Disconnect
</button>
</div>
</div>
<div class="card-actions justify-end mt-4 pt-4 border-t border-base-200">
<div class="admin-card-actions">
<button
phx-click="sync"
phx-value-id={connection.id}
disabled={connection.sync_status == "syncing"}
class="btn btn-outline btn-sm"
class="admin-btn admin-btn-outline admin-btn-sm"
>
<.icon
name="hero-arrow-path"

View File

@ -219,7 +219,7 @@
max="120"
value={@theme_settings.logo_size}
name="logo_size"
class="range range-xs range-primary w-full"
class="admin-range w-full"
/>
</form>
@ -235,7 +235,7 @@
phx-value-setting_value={
if @theme_settings.logo_recolor, do: "false", else: "true"
}
class="toggle toggle-sm toggle-primary"
class="admin-toggle admin-toggle-sm"
/>
<span class="text-sm text-base-content/70">Recolour logo</span>
</label>
@ -277,7 +277,7 @@
phx-value-setting_value={
if @theme_settings.header_background_enabled, do: "false", else: "true"
}
class="toggle toggle-sm toggle-primary"
class="admin-toggle admin-toggle-sm"
/>
<span class="text-sm text-base-content/80">Header background image</span>
</label>
@ -328,7 +328,7 @@
max="200"
value={@theme_settings.header_zoom}
name="header_zoom"
class="range range-xs range-primary w-full"
class="admin-range w-full"
/>
</form>
<form phx-change="update_setting" phx-value-field="header_position_x">
@ -346,7 +346,7 @@
max="100"
value={@theme_settings.header_position_x}
name="header_position_x"
class="range range-xs range-primary w-full"
class="admin-range w-full"
/>
</form>
<form phx-change="update_setting" phx-value-field="header_position_y">
@ -364,7 +364,7 @@
max="100"
value={@theme_settings.header_position_y}
name="header_position_y"
class="range range-xs range-primary w-full"
class="admin-range w-full"
/>
</form>
</div>
@ -771,7 +771,7 @@
checked={@theme_settings.announcement_bar}
phx-click="toggle_setting"
phx-value-field="announcement_bar"
class="checkbox checkbox-sm"
class="admin-checkbox admin-checkbox-sm"
/>
<span class="text-sm text-base-content">Announcement bar</span>
</label>
@ -784,7 +784,7 @@
checked={@theme_settings.sticky_header}
phx-click="toggle_setting"
phx-value-field="sticky_header"
class="checkbox checkbox-sm"
class="admin-checkbox admin-checkbox-sm"
/>
<span class="text-sm text-base-content">Sticky header</span>
</label>
@ -988,7 +988,7 @@
checked={@theme_settings.hover_image}
phx-click="toggle_setting"
phx-value-field="hover_image"
class="checkbox checkbox-sm"
class="admin-checkbox admin-checkbox-sm"
/>
<span class="text-sm text-base-content">Second image on hover</span>
</label>
@ -1001,7 +1001,7 @@
checked={@theme_settings.show_prices}
phx-click="toggle_setting"
phx-value-field="show_prices"
class="checkbox checkbox-sm"
class="admin-checkbox admin-checkbox-sm"
/>
<span class="text-sm text-base-content">Show prices</span>
</label>
@ -1031,7 +1031,7 @@
checked={@theme_settings.pdp_trust_badges}
phx-click="toggle_setting"
phx-value-field="pdp_trust_badges"
class="checkbox checkbox-sm"
class="admin-checkbox admin-checkbox-sm"
/>
<span class="text-sm text-base-content">Trust badges</span>
</label>
@ -1044,7 +1044,7 @@
checked={@theme_settings.pdp_reviews}
phx-click="toggle_setting"
phx-value-field="pdp_reviews"
class="checkbox checkbox-sm"
class="admin-checkbox admin-checkbox-sm"
/>
<span class="text-sm text-base-content">Reviews section</span>
</label>
@ -1057,7 +1057,7 @@
checked={@theme_settings.pdp_related_products}
phx-click="toggle_setting"
phx-value-field="pdp_related_products"
class="checkbox checkbox-sm"
class="admin-checkbox admin-checkbox-sm"
/>
<span class="text-sm text-base-content">Related products</span>
</label>

View File

@ -26,11 +26,11 @@ defmodule SimpleshopThemeWeb.Auth.Confirmation do
name={@form[:remember_me].name}
value="true"
phx-disable-with="Confirming..."
class="btn btn-primary w-full"
class="admin-btn-primary w-full"
>
Confirm and stay logged in
</.button>
<.button phx-disable-with="Confirming..." class="btn btn-primary btn-soft w-full mt-2">
<.button phx-disable-with="Confirming..." class="admin-btn-soft w-full mt-2">
Confirm and log in only this time
</.button>
</.form>
@ -46,7 +46,7 @@ defmodule SimpleshopThemeWeb.Auth.Confirmation do
>
<input type="hidden" name={@form[:token].name} value={@form[:token].value} />
<%= if @current_scope do %>
<.button phx-disable-with="Logging in..." class="btn btn-primary w-full">
<.button phx-disable-with="Logging in..." class="admin-btn-primary w-full">
Log in
</.button>
<% else %>
@ -54,17 +54,17 @@ defmodule SimpleshopThemeWeb.Auth.Confirmation do
name={@form[:remember_me].name}
value="true"
phx-disable-with="Logging in..."
class="btn btn-primary w-full"
class="admin-btn-primary w-full"
>
Keep me logged in on this device
</.button>
<.button phx-disable-with="Logging in..." class="btn btn-primary btn-soft w-full mt-2">
<.button phx-disable-with="Logging in..." class="admin-btn-soft w-full mt-2">
Log me in only this time
</.button>
<% end %>
</.form>
<p :if={!@user.confirmed_at} class="alert alert-outline mt-8">
<p :if={!@user.confirmed_at} class="admin-alert admin-alert-outline mt-8">
Tip: If you prefer passwords, you can enable them in the user settings.
</p>
</div>

View File

@ -25,7 +25,7 @@ defmodule SimpleshopThemeWeb.Auth.Login do
</.header>
</div>
<div :if={local_mail_adapter?()} class="alert alert-info">
<div :if={local_mail_adapter?()} class="admin-alert admin-alert-info">
<.icon name="hero-information-circle" class="size-6 shrink-0" />
<div>
<p>You are running the local mail adapter.</p>
@ -51,12 +51,12 @@ defmodule SimpleshopThemeWeb.Auth.Login do
required
phx-mounted={JS.focus()}
/>
<.button class="btn btn-primary w-full">
<.button class="admin-btn-primary w-full">
Log in with email <span aria-hidden="true"></span>
</.button>
</.form>
<div class="divider">or</div>
<div class="admin-divider">or</div>
<.form
:let={f}
@ -80,10 +80,10 @@ defmodule SimpleshopThemeWeb.Auth.Login do
label="Password"
autocomplete="current-password"
/>
<.button class="btn btn-primary w-full" name={@form[:remember_me].name} value="true">
<.button class="admin-btn-primary w-full" name={@form[:remember_me].name} value="true">
Log in and stay logged in <span aria-hidden="true"></span>
</.button>
<.button class="btn btn-primary btn-soft w-full mt-2">
<.button class="admin-btn-soft w-full mt-2">
Log in only this time
</.button>
</.form>

View File

@ -32,7 +32,7 @@ defmodule SimpleshopThemeWeb.Auth.Registration do
phx-mounted={JS.focus()}
/>
<.button phx-disable-with="Creating account..." class="btn btn-primary w-full">
<.button phx-disable-with="Creating account..." class="admin-btn-primary w-full">
Create an account
</.button>
</.form>