remove Tailwind + DaisyUI theme + heroicons plugin, admin fully custom CSS (Phase 7)

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>
This commit is contained in:
jamey 2026-02-18 01:15:28 +00:00
parent af0b0c217f
commit 82583822f5
21 changed files with 1614 additions and 319 deletions

View File

@ -1,130 +1,22 @@
/* See the Tailwind configuration guide for advanced usage
https://tailwindcss.com/docs/configuration */
/* Admin CSS — bundled by esbuild */
@import "tailwindcss" source(none);
@source "../css";
@source "../js";
@source "../../lib/simpleshop_theme_web";
/* Reset and colour themes */
@import "./admin/reset.css";
@import "./admin/themes.css";
/* A Tailwind plugin that makes "hero-#{ICON}" classes available.
The heroicons installation itself is managed by your mix.exs */
@plugin "../vendor/heroicons";
/* 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
on Elixir colors. Build your own at: https://daisyui.com/theme-generator/ */
@plugin "../vendor/daisyui-theme" {
name: "dark";
default: false;
prefersdark: true;
color-scheme: "dark";
--color-base-100: oklch(30.33% 0.016 252.42);
--color-base-200: oklch(25.26% 0.014 253.1);
--color-base-300: oklch(20.15% 0.012 254.09);
--color-base-content: oklch(97.807% 0.029 256.847);
--color-primary: oklch(58% 0.233 277.117);
--color-primary-content: oklch(96% 0.018 272.314);
--color-secondary: oklch(58% 0.233 277.117);
--color-secondary-content: oklch(96% 0.018 272.314);
--color-accent: oklch(60% 0.25 292.717);
--color-accent-content: oklch(96% 0.016 293.756);
--color-neutral: oklch(37% 0.044 257.287);
--color-neutral-content: oklch(98% 0.003 247.858);
--color-info: oklch(58% 0.158 241.966);
--color-info-content: oklch(97% 0.013 236.62);
--color-success: oklch(60% 0.118 184.704);
--color-success-content: oklch(98% 0.014 180.72);
--color-warning: oklch(66% 0.179 58.318);
--color-warning-content: oklch(98% 0.022 95.277);
--color-error: oklch(58% 0.253 17.585);
--color-error-content: oklch(96% 0.015 12.422);
--radius-selector: 0.25rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.21875rem;
--size-field: 0.21875rem;
--border: 1.5px;
--depth: 1;
--noise: 0;
}
@plugin "../vendor/daisyui-theme" {
name: "light";
default: true;
prefersdark: false;
color-scheme: "light";
--color-base-100: oklch(98% 0 0);
--color-base-200: oklch(96% 0.001 286.375);
--color-base-300: oklch(92% 0.004 286.32);
--color-base-content: oklch(21% 0.006 285.885);
--color-primary: oklch(70% 0.213 47.604);
--color-primary-content: oklch(98% 0.016 73.684);
--color-secondary: oklch(55% 0.027 264.364);
--color-secondary-content: oklch(98% 0.002 247.839);
--color-accent: oklch(0% 0 0);
--color-accent-content: oklch(100% 0 0);
--color-neutral: oklch(44% 0.017 285.786);
--color-neutral-content: oklch(98% 0 0);
--color-info: oklch(62% 0.214 259.815);
--color-info-content: oklch(97% 0.014 254.604);
--color-success: oklch(70% 0.14 182.503);
--color-success-content: oklch(98% 0.014 180.72);
--color-warning: oklch(66% 0.179 58.318);
--color-warning-content: oklch(98% 0.022 95.277);
--color-error: oklch(58% 0.253 17.585);
--color-error-content: oklch(96% 0.015 12.422);
--radius-selector: 0.25rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.21875rem;
--size-field: 0.21875rem;
--border: 1.5px;
--depth: 1;
--noise: 0;
}
/* Add variants based on LiveView classes */
@custom-variant phx-click-loading (.phx-click-loading&, .phx-click-loading &);
@custom-variant phx-submit-loading (.phx-submit-loading&, .phx-submit-loading &);
@custom-variant phx-change-loading (.phx-change-loading&, .phx-change-loading &);
/* Use the data attribute for dark mode */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
/* Make LiveView wrapper divs transparent for layout */
[data-phx-session], [data-phx-teleported-src] { display: contents }
/* Theme CSS - Layer 1: Primitives (fixed CSS variables) */
/* Theme CSS layers (used by theme editor preview) */
@import "./theme-primitives.css";
/* Theme CSS - Layer 2: Attribute-based theme tokens */
@import "./theme-layer2-attributes.css";
/* Theme CSS - Layer 3: Semantic aliases */
@import "./theme-semantic.css";
/* Admin component styles (replaces DaisyUI components) */
/* Admin components, icons, and utilities */
@import "./admin/components.css";
@import "./admin/icons.css";
@import "./admin/utilities.css";
/* Cart drawer open state styles */
.cart-drawer.open {
right: 0 !important;
}
.cart-drawer-overlay.open {
opacity: 1 !important;
visibility: visible !important;
}
/* Product gallery thumbnail styles */
.pdp-thumbnail {
border: 2px solid var(--t-border-default);
transition: border-color 0.15s ease;
}
.pdp-thumbnail-active {
border-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
/* LiveView loading state variants */
.phx-click-loading, .phx-submit-loading, .phx-change-loading {
/* Available for component styling */
}
/* =============================================

View File

@ -608,3 +608,12 @@
&.active .admin-swap-on { display: inline-flex; }
&.active .admin-swap-off { display: none; }
}
/* ── Theme toggle ── */
.theme-toggle-indicator {
transition: left 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
[data-theme="light"] .theme-toggle-indicator { left: 33.333333%; }
[data-theme="dark"] .theme-toggle-indicator { left: 66.666667%; }

625
assets/css/admin/icons.css Normal file
View File

@ -0,0 +1,625 @@
/* Generated by mix generate_admin_icons — do not edit by hand */
.hero-arrow-left-mini {
--hero-arrow-left-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M17%2010a.75.75%200%200%201-.75.75H5.612l4.158%203.96a.75.75%200%201%201-1.04%201.08l-5.5-5.25a.75.75%200%200%201%200-1.08l5.5-5.25a.75.75%200%201%201%201.04%201.08L5.612%209.25H16.25A.75.75%200%200%201%2017%2010Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-arrow-left-mini);
mask: var(--hero-arrow-left-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-arrow-path {
--hero-arrow-path: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M16.023%209.348h4.992v-.001M2.985%2019.644v-4.992m0%200h4.992m-4.993%200%203.181%203.183a8.25%208.25%200%200%200%2013.803-3.7M4.031%209.865a8.25%208.25%200%200%201%2013.803-3.7l3.181%203.182m0-4.991v4.99"/></svg>');
-webkit-mask: var(--hero-arrow-path);
mask: var(--hero-arrow-path);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-arrow-path-mini {
--hero-arrow-path-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M15.312%2011.424a5.5%205.5%200%200%201-9.201%202.466l-.312-.311h2.433a.75.75%200%200%200%200-1.5H3.989a.75.75%200%200%200-.75.75v4.242a.75.75%200%200%200%201.5%200v-2.43l.31.31a7%207%200%200%200%2011.712-3.138.75.75%200%200%200-1.449-.39Zm1.23-3.723a.75.75%200%200%200%20.219-.53V2.929a.75.75%200%200%200-1.5%200V5.36l-.31-.31A7%207%200%200%200%203.239%208.188a.75.75%200%201%200%201.448.389A5.5%205.5%200%200%201%2013.89%206.11l.311.31h-2.432a.75.75%200%200%200%200%201.5h4.243a.75.75%200%200%200%20.53-.219Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-arrow-path-mini);
mask: var(--hero-arrow-path-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-arrow-right-start-on-rectangle {
--hero-arrow-right-start-on-rectangle: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M15.75%209V5.25A2.25%202.25%200%200%200%2013.5%203h-6a2.25%202.25%200%200%200-2.25%202.25v13.5A2.25%202.25%200%200%200%207.5%2021h6a2.25%202.25%200%200%200%202.25-2.25V15m3%200%203-3m0%200-3-3m3%203H9"/></svg>');
-webkit-mask: var(--hero-arrow-right-start-on-rectangle);
mask: var(--hero-arrow-right-start-on-rectangle);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-arrow-top-right-on-square {
--hero-arrow-top-right-on-square: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M13.5%206H5.25A2.25%202.25%200%200%200%203%208.25v10.5A2.25%202.25%200%200%200%205.25%2021h10.5A2.25%202.25%200%200%200%2018%2018.75V10.5m-10.5%206L21%203m0%200h-5.25M21%203v5.25"/></svg>');
-webkit-mask: var(--hero-arrow-top-right-on-square);
mask: var(--hero-arrow-top-right-on-square);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-arrow-top-right-on-square-mini {
--hero-arrow-top-right-on-square-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M4.25%205.5a.75.75%200%200%200-.75.75v8.5c0%20.414.336.75.75.75h8.5a.75.75%200%200%200%20.75-.75v-4a.75.75%200%200%201%201.5%200v4A2.25%202.25%200%200%201%2012.75%2017h-8.5A2.25%202.25%200%200%201%202%2014.75v-8.5A2.25%202.25%200%200%201%204.25%204h5a.75.75%200%200%201%200%201.5h-5Z"%20clip-rule="evenodd"/>%20%20<path%20fill-rule="evenodd"%20d="M6.194%2012.753a.75.75%200%200%200%201.06.053L16.5%204.44v2.81a.75.75%200%200%200%201.5%200v-4.5a.75.75%200%200%200-.75-.75h-4.5a.75.75%200%200%200%200%201.5h2.553l-9.056%208.194a.75.75%200%200%200-.053%201.06Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-arrow-top-right-on-square-mini);
mask: var(--hero-arrow-top-right-on-square-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-arrow-uturn-left-mini {
--hero-arrow-uturn-left-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M7.793%202.232a.75.75%200%200%201-.025%201.06L3.622%207.25h10.003a5.375%205.375%200%200%201%200%2010.75H10.75a.75.75%200%200%201%200-1.5h2.875a3.875%203.875%200%200%200%200-7.75H3.622l4.146%203.957a.75.75%200%200%201-1.036%201.085l-5.5-5.25a.75.75%200%200%201%200-1.085l5.5-5.25a.75.75%200%200%201%201.06.025Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-arrow-uturn-left-mini);
mask: var(--hero-arrow-uturn-left-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-banknotes {
--hero-banknotes: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M2.25%2018.75a60.07%2060.07%200%200%201%2015.797%202.101c.727.198%201.453-.342%201.453-1.096V18.75M3.75%204.5v.75A.75.75%200%200%201%203%206h-.75m0%200v-.375c0-.621.504-1.125%201.125-1.125H20.25M2.25%206v9m18-10.5v.75c0%20.414.336.75.75.75h.75m-1.5-1.5h.375c.621%200%201.125.504%201.125%201.125v9.75c0%20.621-.504%201.125-1.125%201.125h-.375m1.5-1.5H21a.75.75%200%200%200-.75.75v.75m0%200H3.75m0%200h-.375a1.125%201.125%200%200%201-1.125-1.125V15m1.5%201.5v-.75A.75.75%200%200%200%203%2015h-.75M15%2010.5a3%203%200%201%201-6%200%203%203%200%200%201%206%200Zm3%200h.008v.008H18V10.5Zm-12%200h.008v.008H6V10.5Z"/></svg>');
-webkit-mask: var(--hero-banknotes);
mask: var(--hero-banknotes);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-bars-3 {
--hero-bars-3: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M3.75%206.75h16.5M3.75%2012h16.5m-16.5%205.25h16.5"/></svg>');
-webkit-mask: var(--hero-bars-3);
mask: var(--hero-bars-3);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-bug-ant {
--hero-bug-ant: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M12%2012.75c1.148%200%202.278.08%203.383.237%201.037.146%201.866.966%201.866%202.013%200%203.728-2.35%206.75-5.25%206.75S6.75%2018.728%206.75%2015c0-1.046.83-1.867%201.866-2.013A24.204%2024.204%200%200%201%2012%2012.75Zm0%200c2.883%200%205.647.508%208.207%201.44a23.91%2023.91%200%200%201-1.152%206.06M12%2012.75c-2.883%200-5.647.508-8.208%201.44.125%202.104.52%204.136%201.153%206.06M12%2012.75a2.25%202.25%200%200%200%202.248-2.354M12%2012.75a2.25%202.25%200%200%201-2.248-2.354M12%208.25c.995%200%201.971-.08%202.922-.236.403-.066.74-.358.795-.762a3.778%203.778%200%200%200-.399-2.25M12%208.25c-.995%200-1.97-.08-2.922-.236-.402-.066-.74-.358-.795-.762a3.734%203.734%200%200%201%20.4-2.253M12%208.25a2.25%202.25%200%200%200-2.248%202.146M12%208.25a2.25%202.25%200%200%201%202.248%202.146M8.683%205a6.032%206.032%200%200%201-1.155-1.002c.07-.63.27-1.222.574-1.747m.581%202.749A3.75%203.75%200%200%201%2015.318%205m0%200c.427-.283.815-.62%201.155-.999a4.471%204.471%200%200%200-.575-1.752M4.921%206a24.048%2024.048%200%200%200-.392%203.314c1.668.546%203.416.914%205.223%201.082M19.08%206c.205%201.08.337%202.187.392%203.314a23.882%2023.882%200%200%201-5.223%201.082"/></svg>');
-webkit-mask: var(--hero-bug-ant);
mask: var(--hero-bug-ant);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-chart-bar {
--hero-chart-bar: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M3%2013.125C3%2012.504%203.504%2012%204.125%2012h2.25c.621%200%201.125.504%201.125%201.125v6.75C7.5%2020.496%206.996%2021%206.375%2021h-2.25A1.125%201.125%200%200%201%203%2019.875v-6.75ZM9.75%208.625c0-.621.504-1.125%201.125-1.125h2.25c.621%200%201.125.504%201.125%201.125v11.25c0%20.621-.504%201.125-1.125%201.125h-2.25a1.125%201.125%200%200%201-1.125-1.125V8.625ZM16.5%204.125c0-.621.504-1.125%201.125-1.125h2.25C20.496%203%2021%203.504%2021%204.125v15.75c0%20.621-.504%201.125-1.125%201.125h-2.25a1.125%201.125%200%200%201-1.125-1.125V4.125Z"/></svg>');
-webkit-mask: var(--hero-chart-bar);
mask: var(--hero-chart-bar);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-check-badge {
--hero-check-badge: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M9%2012.75%2011.25%2015%2015%209.75M21%2012c0%201.268-.63%202.39-1.593%203.068a3.745%203.745%200%200%201-1.043%203.296%203.745%203.745%200%200%201-3.296%201.043A3.745%203.745%200%200%201%2012%2021c-1.268%200-2.39-.63-3.068-1.593a3.746%203.746%200%200%201-3.296-1.043%203.745%203.745%200%200%201-1.043-3.296A3.745%203.745%200%200%201%203%2012c0-1.268.63-2.39%201.593-3.068a3.745%203.745%200%200%201%201.043-3.296%203.746%203.746%200%200%201%203.296-1.043A3.746%203.746%200%200%201%2012%203c1.268%200%202.39.63%203.068%201.593a3.746%203.746%200%200%201%203.296%201.043%203.746%203.746%200%200%201%201.043%203.296A3.745%203.745%200%200%201%2021%2012Z"/></svg>');
-webkit-mask: var(--hero-check-badge);
mask: var(--hero-check-badge);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-check-circle {
--hero-check-circle: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M9%2012.75%2011.25%2015%2015%209.75M21%2012a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Z"/></svg>');
-webkit-mask: var(--hero-check-circle);
mask: var(--hero-check-circle);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-check-circle-mini {
--hero-check-circle-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M10%2018a8%208%200%201%200%200-16%208%208%200%200%200%200%2016Zm3.857-9.809a.75.75%200%200%200-1.214-.882l-3.483%204.79-1.88-1.88a.75.75%200%201%200-1.06%201.061l2.5%202.5a.75.75%200%200%200%201.137-.089l4-5.5Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-check-circle-mini);
mask: var(--hero-check-circle-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-check-mini {
--hero-check-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M16.704%204.153a.75.75%200%200%201%20.143%201.052l-8%2010.5a.75.75%200%200%201-1.127.075l-4.5-4.5a.75.75%200%200%201%201.06-1.06l3.894%203.893%207.48-9.817a.75.75%200%200%201%201.05-.143Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-check-mini);
mask: var(--hero-check-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-chevron-down-mini {
--hero-chevron-down-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M5.22%208.22a.75.75%200%200%201%201.06%200L10%2011.94l3.72-3.72a.75.75%200%201%201%201.06%201.06l-4.25%204.25a.75.75%200%200%201-1.06%200L5.22%209.28a.75.75%200%200%201%200-1.06Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-chevron-down-mini);
mask: var(--hero-chevron-down-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-chevron-right-mini {
--hero-chevron-right-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M8.22%205.22a.75.75%200%200%201%201.06%200l4.25%204.25a.75.75%200%200%201%200%201.06l-4.25%204.25a.75.75%200%200%201-1.06-1.06L11.94%2010%208.22%206.28a.75.75%200%200%201%200-1.06Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-chevron-right-mini);
mask: var(--hero-chevron-right-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-chevron-up-mini {
--hero-chevron-up-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M9.47%206.47a.75.75%200%200%201%201.06%200l4.25%204.25a.75.75%200%201%201-1.06%201.06L10%208.06l-3.72%203.72a.75.75%200%200%201-1.06-1.06l4.25-4.25Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-chevron-up-mini);
mask: var(--hero-chevron-up-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-clock {
--hero-clock: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M12%206v6h4.5m4.5%200a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Z"/></svg>');
-webkit-mask: var(--hero-clock);
mask: var(--hero-clock);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-clock-mini {
--hero-clock-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M10%2018a8%208%200%201%200%200-16%208%208%200%200%200%200%2016Zm.75-13a.75.75%200%200%200-1.5%200v5c0%20.414.336.75.75.75h4a.75.75%200%200%200%200-1.5h-3.25V5Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-clock-mini);
mask: var(--hero-clock-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-cog-6-tooth {
--hero-cog-6-tooth: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M9.594%203.94c.09-.542.56-.94%201.11-.94h2.593c.55%200%201.02.398%201.11.94l.213%201.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257%201.075.124l1.217-.456a1.125%201.125%200%200%201%201.37.49l1.296%202.247a1.125%201.125%200%200%201-.26%201.431l-1.003.827c-.293.241-.438.613-.43.992a7.723%207.723%200%200%201%200%20.255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26%201.43l-1.298%202.247a1.125%201.125%200%200%201-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47%206.47%200%200%201-.22.128c-.331.183-.581.495-.644.869l-.213%201.281c-.09.543-.56.94-1.11.94h-2.594c-.55%200-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52%206.52%200%200%201-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125%201.125%200%200%201-1.369-.49l-1.297-2.247a1.125%201.125%200%200%201%20.26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932%206.932%200%200%201%200-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125%201.125%200%200%201-.26-1.43l1.297-2.247a1.125%201.125%200%200%201%201.37-.491l1.216.456c.356.133.751.072%201.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"/>%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M15%2012a3%203%200%201%201-6%200%203%203%200%200%201%206%200Z"/></svg>');
-webkit-mask: var(--hero-cog-6-tooth);
mask: var(--hero-cog-6-tooth);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-cog-6-tooth-mini {
--hero-cog-6-tooth-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M7.84%201.804A1%201%200%200%201%208.82%201h2.36a1%201%200%200%201%20.98.804l.331%201.652a6.993%206.993%200%200%201%201.929%201.115l1.598-.54a1%201%200%200%201%201.186.447l1.18%202.044a1%201%200%200%201-.205%201.251l-1.267%201.113a7.047%207.047%200%200%201%200%202.228l1.267%201.113a1%201%200%200%201%20.206%201.25l-1.18%202.045a1%201%200%200%201-1.187.447l-1.598-.54a6.993%206.993%200%200%201-1.929%201.115l-.33%201.652a1%201%200%200%201-.98.804H8.82a1%201%200%200%201-.98-.804l-.331-1.652a6.993%206.993%200%200%201-1.929-1.115l-1.598.54a1%201%200%200%201-1.186-.447l-1.18-2.044a1%201%200%200%201%20.205-1.251l1.267-1.114a7.05%207.05%200%200%201%200-2.227L1.821%207.773a1%201%200%200%201-.206-1.25l1.18-2.045a1%201%200%200%201%201.187-.447l1.598.54A6.992%206.992%200%200%201%207.51%203.456l.33-1.652ZM10%2013a3%203%200%201%200%200-6%203%203%200%200%200%200%206Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-cog-6-tooth-mini);
mask: var(--hero-cog-6-tooth-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-computer-desktop-micro {
--hero-computer-desktop-micro: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2016%2016"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M2%204.25A2.25%202.25%200%200%201%204.25%202h7.5A2.25%202.25%200%200%201%2014%204.25v5.5A2.25%202.25%200%200%201%2011.75%2012h-1.312c.1.128.21.248.328.36a.75.75%200%200%201%20.234.545v.345a.75.75%200%200%201-.75.75h-4.5a.75.75%200%200%201-.75-.75v-.345a.75.75%200%200%201%20.234-.545c.118-.111.228-.232.328-.36H4.25A2.25%202.25%200%200%201%202%209.75v-5.5Zm2.25-.75a.75.75%200%200%200-.75.75v4.5c0%20.414.336.75.75.75h7.5a.75.75%200%200%200%20.75-.75v-4.5a.75.75%200%200%200-.75-.75h-7.5Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-computer-desktop-micro);
mask: var(--hero-computer-desktop-micro);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1rem;
height: 1rem;
}
.hero-cube {
--hero-cube: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="m21%207.5-9-5.25L3%207.5m18%200-9%205.25m9-5.25v9l-9%205.25M3%207.5l9%205.25M3%207.5v9l9%205.25m0-9v9"/></svg>');
-webkit-mask: var(--hero-cube);
mask: var(--hero-cube);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-exclamation-circle {
--hero-exclamation-circle: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M12%209v3.75m9-.75a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Zm-9%203.75h.008v.008H12v-.008Z"/></svg>');
-webkit-mask: var(--hero-exclamation-circle);
mask: var(--hero-exclamation-circle);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-exclamation-triangle {
--hero-exclamation-triangle: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M12%209v3.75m-9.303%203.376c-.866%201.5.217%203.374%201.948%203.374h14.71c1.73%200%202.813-1.874%201.948-3.374L13.949%203.378c-.866-1.5-3.032-1.5-3.898%200L2.697%2016.126ZM12%2015.75h.007v.008H12v-.008Z"/></svg>');
-webkit-mask: var(--hero-exclamation-triangle);
mask: var(--hero-exclamation-triangle);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-exclamation-triangle-mini {
--hero-exclamation-triangle-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M8.485%202.495c.673-1.167%202.357-1.167%203.03%200l6.28%2010.875c.673%201.167-.17%202.625-1.516%202.625H3.72c-1.347%200-2.189-1.458-1.515-2.625L8.485%202.495ZM10%205a.75.75%200%200%201%20.75.75v3.5a.75.75%200%200%201-1.5%200v-3.5A.75.75%200%200%201%2010%205Zm0%209a1%201%200%201%200%200-2%201%201%200%200%200%200%202Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-exclamation-triangle-mini);
mask: var(--hero-exclamation-triangle-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-eye {
--hero-eye: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M2.036%2012.322a1.012%201.012%200%200%201%200-.639C3.423%207.51%207.36%204.5%2012%204.5c4.638%200%208.573%203.007%209.963%207.178.07.207.07.431%200%20.639C20.577%2016.49%2016.64%2019.5%2012%2019.5c-4.638%200-8.573-3.007-9.963-7.178Z"/>%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M15%2012a3%203%200%201%201-6%200%203%203%200%200%201%206%200Z"/></svg>');
-webkit-mask: var(--hero-eye);
mask: var(--hero-eye);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-eye-mini {
--hero-eye-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20d="M10%2012.5a2.5%202.5%200%201%200%200-5%202.5%202.5%200%200%200%200%205Z"/>%20%20<path%20fill-rule="evenodd"%20d="M.664%2010.59a1.651%201.651%200%200%201%200-1.186A10.004%2010.004%200%200%201%2010%203c4.257%200%207.893%202.66%209.336%206.41.147.381.146.804%200%201.186A10.004%2010.004%200%200%201%2010%2017c-4.257%200-7.893-2.66-9.336-6.41ZM14%2010a4%204%200%201%201-8%200%204%204%200%200%201%208%200Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-eye-mini);
mask: var(--hero-eye-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-eye-slash {
--hero-eye-slash: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M3.98%208.223A10.477%2010.477%200%200%200%201.934%2012C3.226%2016.338%207.244%2019.5%2012%2019.5c.993%200%201.953-.138%202.863-.395M6.228%206.228A10.451%2010.451%200%200%201%2012%204.5c4.756%200%208.773%203.162%2010.065%207.498a10.522%2010.522%200%200%201-4.293%205.774M6.228%206.228%203%203m3.228%203.228%203.65%203.65m7.894%207.894L21%2021m-3.228-3.228-3.65-3.65m0%200a3%203%200%201%200-4.243-4.243m4.242%204.242L9.88%209.88"/></svg>');
-webkit-mask: var(--hero-eye-slash);
mask: var(--hero-eye-slash);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-eye-slash-mini {
--hero-eye-slash-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M3.28%202.22a.75.75%200%200%200-1.06%201.06l14.5%2014.5a.75.75%200%201%200%201.06-1.06l-1.745-1.745a10.029%2010.029%200%200%200%203.3-4.38%201.651%201.651%200%200%200%200-1.185A10.004%2010.004%200%200%200%209.999%203a9.956%209.956%200%200%200-4.744%201.194L3.28%202.22ZM7.752%206.69l1.092%201.092a2.5%202.5%200%200%201%203.374%203.373l1.091%201.092a4%204%200%200%200-5.557-5.557Z"%20clip-rule="evenodd"/>%20%20<path%20d="m10.748%2013.93%202.523%202.523a9.987%209.987%200%200%201-3.27.547c-4.258%200-7.894-2.66-9.337-6.41a1.651%201.651%200%200%201%200-1.186A10.007%2010.007%200%200%201%202.839%206.02L6.07%209.252a4%204%200%200%200%204.678%204.678Z"/></svg>');
-webkit-mask: var(--hero-eye-slash-mini);
mask: var(--hero-eye-slash-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-home {
--hero-home: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="m2.25%2012%208.954-8.955c.44-.439%201.152-.439%201.591%200L21.75%2012M4.5%209.75v10.125c0%20.621.504%201.125%201.125%201.125H9.75v-4.875c0-.621.504-1.125%201.125-1.125h2.25c.621%200%201.125.504%201.125%201.125V21h4.125c.621%200%201.125-.504%201.125-1.125V9.75M8.25%2021h8.25"/></svg>');
-webkit-mask: var(--hero-home);
mask: var(--hero-home);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-inbox {
--hero-inbox: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M2.25%2013.5h3.86a2.25%202.25%200%200%201%202.012%201.244l.256.512a2.25%202.25%200%200%200%202.013%201.244h3.218a2.25%202.25%200%200%200%202.013-1.244l.256-.512a2.25%202.25%200%200%201%202.013-1.244h3.859m-19.5.338V18a2.25%202.25%200%200%200%202.25%202.25h15A2.25%202.25%200%200%200%2021.75%2018v-4.162c0-.224-.034-.447-.1-.661L19.24%205.338a2.25%202.25%200%200%200-2.15-1.588H6.911a2.25%202.25%200%200%200-2.15%201.588L2.35%2013.177a2.25%202.25%200%200%200-.1.661Z"/></svg>');
-webkit-mask: var(--hero-inbox);
mask: var(--hero-inbox);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-information-circle {
--hero-information-circle: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="m11.25%2011.25.041-.02a.75.75%200%200%201%201.063.852l-.708%202.836a.75.75%200%200%200%201.063.853l.041-.021M21%2012a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Zm-9-3.75h.008v.008H12V8.25Z"/></svg>');
-webkit-mask: var(--hero-information-circle);
mask: var(--hero-information-circle);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-minus-circle-mini {
--hero-minus-circle-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M10%2018a8%208%200%201%200%200-16%208%208%200%200%200%200%2016ZM6.75%209.25a.75.75%200%200%200%200%201.5h6.5a.75.75%200%200%200%200-1.5h-6.5Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-minus-circle-mini);
mask: var(--hero-minus-circle-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-moon-micro {
--hero-moon-micro: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2016%2016"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20d="M14.438%2010.148c.19-.425-.321-.787-.748-.601A5.5%205.5%200%200%201%206.453%202.31c.186-.427-.176-.938-.6-.748a6.501%206.501%200%201%200%208.585%208.586Z"/></svg>');
-webkit-mask: var(--hero-moon-micro);
mask: var(--hero-moon-micro);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1rem;
height: 1rem;
}
.hero-no-symbol-mini {
--hero-no-symbol-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="m5.965%204.904%209.131%209.131a6.5%206.5%200%200%200-9.131-9.131Zm8.07%2010.192L4.904%205.965a6.5%206.5%200%200%200%209.131%209.131ZM4.343%204.343a8%208%200%201%201%2011.314%2011.314A8%208%200%200%201%204.343%204.343Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-no-symbol-mini);
mask: var(--hero-no-symbol-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-paint-brush {
--hero-paint-brush: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M9.53%2016.122a3%203%200%200%200-5.78%201.128%202.25%202.25%200%200%201-2.4%202.245%204.5%204.5%200%200%200%208.4-2.245c0-.399-.078-.78-.22-1.128Zm0%200a15.998%2015.998%200%200%200%203.388-1.62m-5.043-.025a15.994%2015.994%200%200%201%201.622-3.395m3.42%203.42a15.995%2015.995%200%200%200%204.764-4.648l3.876-5.814a1.151%201.151%200%200%200-1.597-1.597L14.146%206.32a15.996%2015.996%200%200%200-4.649%204.763m3.42%203.42a6.776%206.776%200%200%200-3.42-3.42"/></svg>');
-webkit-mask: var(--hero-paint-brush);
mask: var(--hero-paint-brush);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-paint-brush-mini {
--hero-paint-brush-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20d="M15.993%201.385a1.87%201.87%200%200%201%202.623%202.622l-4.03%205.27a12.749%2012.749%200%200%201-4.237%203.562%204.508%204.508%200%200%200-3.188-3.188%2012.75%2012.75%200%200%201%203.562-4.236l5.27-4.03ZM6%2011a3%203%200%200%200-3%203%20.5.5%200%200%201-.72.45.75.75%200%200%200-1.035.931A4.001%204.001%200%200%200%209%2014.004V14a3.01%203.01%200%200%200-1.66-2.685A2.99%202.99%200%200%200%206%2011Z"/></svg>');
-webkit-mask: var(--hero-paint-brush-mini);
mask: var(--hero-paint-brush-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-paper-airplane-mini {
--hero-paper-airplane-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20d="M3.105%202.288a.75.75%200%200%200-.826.95l1.414%204.926A1.5%201.5%200%200%200%205.135%209.25h6.115a.75.75%200%200%201%200%201.5H5.135a1.5%201.5%200%200%200-1.442%201.086l-1.414%204.926a.75.75%200%200%200%20.826.95%2028.897%2028.897%200%200%200%2015.293-7.155.75.75%200%200%200%200-1.114A28.897%2028.897%200%200%200%203.105%202.288Z"/></svg>');
-webkit-mask: var(--hero-paper-airplane-mini);
mask: var(--hero-paper-airplane-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-photo {
--hero-photo: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="m2.25%2015.75%205.159-5.159a2.25%202.25%200%200%201%203.182%200l5.159%205.159m-1.5-1.5%201.409-1.409a2.25%202.25%200%200%201%203.182%200l2.909%202.909m-18%203.75h16.5a1.5%201.5%200%200%200%201.5-1.5V6a1.5%201.5%200%200%200-1.5-1.5H3.75A1.5%201.5%200%200%200%202.25%206v12a1.5%201.5%200%200%200%201.5%201.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375%200a.375.375%200%201%201-.75%200%20.375.375%200%200%201%20.75%200Z"/></svg>');
-webkit-mask: var(--hero-photo);
mask: var(--hero-photo);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-plus {
--hero-plus: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M12%204.5v15m7.5-7.5h-15"/></svg>');
-webkit-mask: var(--hero-plus);
mask: var(--hero-plus);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-plus-mini {
--hero-plus-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20d="M10.75%204.75a.75.75%200%200%200-1.5%200v4.5h-4.5a.75.75%200%200%200%200%201.5h4.5v4.5a.75.75%200%200%200%201.5%200v-4.5h4.5a.75.75%200%200%200%200-1.5h-4.5v-4.5Z"/></svg>');
-webkit-mask: var(--hero-plus-mini);
mask: var(--hero-plus-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-question-mark-circle-mini {
--hero-question-mark-circle-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M18%2010a8%208%200%201%201-16%200%208%208%200%200%201%2016%200ZM8.94%206.94a.75.75%200%201%201-1.061-1.061%203%203%200%201%201%202.871%205.026v.345a.75.75%200%200%201-1.5%200v-.5c0-.72.57-1.172%201.081-1.287A1.5%201.5%200%201%200%208.94%206.94ZM10%2015a1%201%200%201%200%200-2%201%201%200%200%200%200%202Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-question-mark-circle-mini);
mask: var(--hero-question-mark-circle-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-rocket-launch {
--hero-rocket-launch: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M15.59%2014.37a6%206%200%200%201-5.84%207.38v-4.8m5.84-2.58a14.98%2014.98%200%200%200%206.16-12.12A14.98%2014.98%200%200%200%209.631%208.41m5.96%205.96a14.926%2014.926%200%200%201-5.841%202.58m-.119-8.54a6%206%200%200%200-7.381%205.84h4.8m2.581-5.84a14.927%2014.927%200%200%200-2.58%205.84m2.699%202.7c-.103.021-.207.041-.311.06a15.09%2015.09%200%200%201-2.448-2.448%2014.9%2014.9%200%200%201%20.06-.312m-2.24%202.39a4.493%204.493%200%200%200-1.757%204.306%204.493%204.493%200%200%200%204.306-1.758M16.5%209a1.5%201.5%200%201%201-3%200%201.5%201.5%200%200%201%203%200Z"/></svg>');
-webkit-mask: var(--hero-rocket-launch);
mask: var(--hero-rocket-launch);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-shopping-bag {
--hero-shopping-bag: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M15.75%2010.5V6a3.75%203.75%200%201%200-7.5%200v4.5m11.356-1.993%201.263%2012c.07.665-.45%201.243-1.119%201.243H4.25a1.125%201.125%200%200%201-1.12-1.243l1.264-12A1.125%201.125%200%200%201%205.513%207.5h12.974c.576%200%201.059.435%201.119%201.007ZM8.625%2010.5a.375.375%200%201%201-.75%200%20.375.375%200%200%201%20.75%200Zm7.5%200a.375.375%200%201%201-.75%200%20.375.375%200%200%201%20.75%200Z"/></svg>');
-webkit-mask: var(--hero-shopping-bag);
mask: var(--hero-shopping-bag);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-signal {
--hero-signal: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M9.348%2014.652a3.75%203.75%200%200%201%200-5.304m5.304%200a3.75%203.75%200%200%201%200%205.304m-7.425%202.121a6.75%206.75%200%200%201%200-9.546m9.546%200a6.75%206.75%200%200%201%200%209.546M5.106%2018.894c-3.808-3.807-3.808-9.98%200-13.788m13.788%200c3.808%203.807%203.808%209.98%200%2013.788M12%2012h.008v.008H12V12Zm.375%200a.375.375%200%201%201-.75%200%20.375.375%200%200%201%20.75%200Z"/></svg>');
-webkit-mask: var(--hero-signal);
mask: var(--hero-signal);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-sun-micro {
--hero-sun-micro: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2016%2016"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20d="M8%201a.75.75%200%200%201%20.75.75v1.5a.75.75%200%200%201-1.5%200v-1.5A.75.75%200%200%201%208%201ZM10.5%208a2.5%202.5%200%201%201-5%200%202.5%202.5%200%200%201%205%200ZM12.95%204.11a.75.75%200%201%200-1.06-1.06l-1.062%201.06a.75.75%200%200%200%201.061%201.062l1.06-1.061ZM15%208a.75.75%200%200%201-.75.75h-1.5a.75.75%200%200%201%200-1.5h1.5A.75.75%200%200%201%2015%208ZM11.89%2012.95a.75.75%200%200%200%201.06-1.06l-1.06-1.062a.75.75%200%200%200-1.062%201.061l1.061%201.06ZM8%2012a.75.75%200%200%201%20.75.75v1.5a.75.75%200%200%201-1.5%200v-1.5A.75.75%200%200%201%208%2012ZM5.172%2011.89a.75.75%200%200%200-1.061-1.062L3.05%2011.89a.75.75%200%201%200%201.06%201.06l1.06-1.06ZM4%208a.75.75%200%200%201-.75.75h-1.5a.75.75%200%200%201%200-1.5h1.5A.75.75%200%200%201%204%208ZM4.11%205.172A.75.75%200%200%200%205.173%204.11L4.11%203.05a.75.75%200%201%200-1.06%201.06l1.06%201.06Z"/></svg>');
-webkit-mask: var(--hero-sun-micro);
mask: var(--hero-sun-micro);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1rem;
height: 1rem;
}
.hero-truck-mini {
--hero-truck-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20d="M6.5%203c-1.051%200-2.093.04-3.125.117A1.49%201.49%200%200%200%202%204.607V10.5h9V4.606c0-.771-.59-1.43-1.375-1.489A41.568%2041.568%200%200%200%206.5%203ZM2%2012v2.5A1.5%201.5%200%200%200%203.5%2016h.041a3%203%200%200%201%205.918%200h.791a.75.75%200%200%200%20.75-.75V12H2Z"/>%20%20<path%20d="M6.5%2018a1.5%201.5%200%201%200%200-3%201.5%201.5%200%200%200%200%203ZM13.25%205a.75.75%200%200%200-.75.75v8.514a3.001%203.001%200%200%201%204.893%201.44c.37-.275.61-.719.595-1.227a24.905%2024.905%200%200%200-1.784-8.549A1.486%201.486%200%200%200%2014.823%205H13.25ZM14.5%2018a1.5%201.5%200%201%200%200-3%201.5%201.5%200%200%200%200%203Z"/></svg>');
-webkit-mask: var(--hero-truck-mini);
mask: var(--hero-truck-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-x-circle {
--hero-x-circle: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="m9.75%209.75%204.5%204.5m0-4.5-4.5%204.5M21%2012a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Z"/></svg>');
-webkit-mask: var(--hero-x-circle);
mask: var(--hero-x-circle);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}
.hero-x-circle-mini {
--hero-x-circle-mini: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%2020%2020"%20fill="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20fill-rule="evenodd"%20d="M10%2018a8%208%200%201%200%200-16%208%208%200%200%200%200%2016ZM8.28%207.22a.75.75%200%200%200-1.06%201.06L8.94%2010l-1.72%201.72a.75.75%200%201%200%201.06%201.06L10%2011.06l1.72%201.72a.75.75%200%201%200%201.06-1.06L11.06%2010l1.72-1.72a.75.75%200%200%200-1.06-1.06L10%208.94%208.28%207.22Z"%20clip-rule="evenodd"/></svg>');
-webkit-mask: var(--hero-x-circle-mini);
mask: var(--hero-x-circle-mini);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
}
.hero-x-mark {
--hero-x-mark: url('data:image/svg+xml;utf8,<svg%20xmlns="http://www.w3.org/2000/svg"%20fill="none"%20viewBox="0%200%2024%2024"%20stroke-width="1.5"%20stroke="currentColor"%20aria-hidden="true"%20data-slot="icon">%20%20<path%20stroke-linecap="round"%20stroke-linejoin="round"%20d="M6%2018%2018%206M6%206l12%2012"/></svg>');
-webkit-mask: var(--hero-x-mark);
mask: var(--hero-x-mark);
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: 1.5rem;
height: 1.5rem;
}

View File

@ -0,0 +1,19 @@
/* Minimal resets for admin pages */
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 0;
}
/* Make LiveView wrapper divs transparent for layout */
[data-phx-session], [data-phx-teleported-src] {
display: contents;
}
/* Phoenix LiveView loading states */
.phx-no-feedback.phx-no-feedback {
/* Suppress validation styles until form is interacted with */
}

116
assets/css/admin/themes.css Normal file
View File

@ -0,0 +1,116 @@
/* Admin colour themes extracted from DaisyUI theme plugin config.
Light is the default; dark activates via OS preference or data-theme attribute. */
:root {
color-scheme: light;
/* Base */
--color-base-100: oklch(98% 0 0);
--color-base-200: oklch(96% 0.001 286.375);
--color-base-300: oklch(92% 0.004 286.32);
--color-base-content: oklch(21% 0.006 285.885);
/* Brand */
--color-primary: oklch(70% 0.213 47.604);
--color-primary-content: oklch(98% 0.016 73.684);
--color-secondary: oklch(55% 0.027 264.364);
--color-secondary-content: oklch(98% 0.002 247.839);
--color-accent: oklch(0% 0 0);
--color-accent-content: oklch(100% 0 0);
--color-neutral: oklch(44% 0.017 285.786);
--color-neutral-content: oklch(98% 0 0);
/* Feedback */
--color-info: oklch(62% 0.214 259.815);
--color-info-content: oklch(97% 0.014 254.604);
--color-success: oklch(70% 0.14 182.503);
--color-success-content: oklch(98% 0.014 180.72);
--color-warning: oklch(66% 0.179 58.318);
--color-warning-content: oklch(98% 0.022 95.277);
--color-error: oklch(58% 0.253 17.585);
--color-error-content: oklch(96% 0.015 12.422);
/* Shape */
--radius-selector: 0.25rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.21875rem;
--size-field: 0.21875rem;
--border: 1.5px;
--depth: 1;
--noise: 0;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--color-base-100: oklch(30.33% 0.016 252.42);
--color-base-200: oklch(25.26% 0.014 253.1);
--color-base-300: oklch(20.15% 0.012 254.09);
--color-base-content: oklch(97.807% 0.029 256.847);
--color-primary: oklch(58% 0.233 277.117);
--color-primary-content: oklch(96% 0.018 272.314);
--color-secondary: oklch(58% 0.233 277.117);
--color-secondary-content: oklch(96% 0.018 272.314);
--color-accent: oklch(60% 0.25 292.717);
--color-accent-content: oklch(96% 0.016 293.756);
--color-neutral: oklch(37% 0.044 257.287);
--color-neutral-content: oklch(98% 0.003 247.858);
--color-info: oklch(58% 0.158 241.966);
--color-info-content: oklch(97% 0.013 236.62);
--color-success: oklch(60% 0.118 184.704);
--color-success-content: oklch(98% 0.014 180.72);
--color-warning: oklch(66% 0.179 58.318);
--color-warning-content: oklch(98% 0.022 95.277);
--color-error: oklch(58% 0.253 17.585);
--color-error-content: oklch(96% 0.015 12.422);
--radius-selector: 0.25rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.21875rem;
--size-field: 0.21875rem;
--border: 1.5px;
--depth: 1;
--noise: 0;
}
}
[data-theme="dark"] {
color-scheme: dark;
--color-base-100: oklch(30.33% 0.016 252.42);
--color-base-200: oklch(25.26% 0.014 253.1);
--color-base-300: oklch(20.15% 0.012 254.09);
--color-base-content: oklch(97.807% 0.029 256.847);
--color-primary: oklch(58% 0.233 277.117);
--color-primary-content: oklch(96% 0.018 272.314);
--color-secondary: oklch(58% 0.233 277.117);
--color-secondary-content: oklch(96% 0.018 272.314);
--color-accent: oklch(60% 0.25 292.717);
--color-accent-content: oklch(96% 0.016 293.756);
--color-neutral: oklch(37% 0.044 257.287);
--color-neutral-content: oklch(98% 0.003 247.858);
--color-info: oklch(58% 0.158 241.966);
--color-info-content: oklch(97% 0.013 236.62);
--color-success: oklch(60% 0.118 184.704);
--color-success-content: oklch(98% 0.014 180.72);
--color-warning: oklch(66% 0.179 58.318);
--color-warning-content: oklch(98% 0.022 95.277);
--color-error: oklch(58% 0.253 17.585);
--color-error-content: oklch(96% 0.015 12.422);
--radius-selector: 0.25rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.21875rem;
--size-field: 0.21875rem;
--border: 1.5px;
--depth: 1;
--noise: 0;
}

View File

@ -0,0 +1,684 @@
/* Admin utility classes replaces Tailwind utilities for admin/auth pages.
Only classes actually used in templates are defined here. */
/* ========================================
Shadow/Ring foundation
======================================== */
:where(html) {
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-ring-color: currentColor;
--tw-ring-inset: ;
}
/* ========================================
Display
======================================== */
.hidden { display: none; }
.block { display: block; }
.inline { display: inline; }
.inline-flex { display: inline-flex; }
.flex { display: flex; }
.grid { display: grid; }
/* ========================================
Position
======================================== */
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.inset-0 { inset: 0; }
.inset-y-0 { top: 0; bottom: 0; }
.left-0 { left: 0; }
.right-0 { right: 0; }
.left-\[40rem\] { left: 40rem; }
.-right-1\.5 { right: -0.375rem; }
.-top-1\.5 { top: -0.375rem; }
.z-0 { z-index: 0; }
/* ========================================
Flexbox
======================================== */
.flex-1 { flex: 1 1 0%; }
.flex-none { flex: none; }
.flex-col { flex-direction: column; }
.flex-row { flex-direction: row; }
.flex-wrap { flex-wrap: wrap; }
.flex-shrink-0, .shrink-0 { flex-shrink: 0; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.self-start { align-self: flex-start; }
/* ========================================
Grid
======================================== */
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* ========================================
Gap
======================================== */
.gap-1 { gap: 0.25rem; }
.gap-1\.5 { gap: 0.375rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-6 { gap: 1.5rem; }
.gap-\[14px\] { gap: 14px; }
.gap-x-4 { column-gap: 1rem; }
.gap-x-6 { column-gap: 1.5rem; }
.gap-y-1 { row-gap: 0.25rem; }
.gap-y-4 { row-gap: 1rem; }
/* ========================================
Padding
======================================== */
.p-0 { padding: 0; }
.p-1 { padding: 0.25rem; }
.p-2 { padding: 0.5rem; }
.p-3 { padding: 0.75rem; }
.p-4 { padding: 1rem; }
.p-6 { padding: 1.5rem; }
.p-8 { padding: 2rem; }
.px-1\.5 { padding-inline: 0.375rem; }
.px-2 { padding-inline: 0.5rem; }
.px-3 { padding-inline: 0.75rem; }
.px-4 { padding-inline: 1rem; }
.px-6 { padding-inline: 1.5rem; }
.px-\[14px\] { padding-inline: 14px; }
.py-0\.5 { padding-block: 0.125rem; }
.py-1 { padding-block: 0.25rem; }
.py-1\.5 { padding-block: 0.375rem; }
.py-2 { padding-block: 0.5rem; }
.py-2\.5 { padding-block: 0.625rem; }
.py-3 { padding-block: 0.75rem; }
.py-4 { padding-block: 1rem; }
.py-10 { padding-block: 2.5rem; }
.py-12 { padding-block: 3rem; }
.py-\[5px\] { padding-block: 5px; }
.py-\[10px\] { padding-block: 10px; }
.pb-2 { padding-bottom: 0.5rem; }
.pb-6 { padding-bottom: 1.5rem; }
.pb-8 { padding-bottom: 2rem; }
.pb-10 { padding-bottom: 2.5rem; }
.pl-10 { padding-left: 2.5rem; }
.pt-3 { padding-top: 0.75rem; }
.pt-4 { padding-top: 1rem; }
.pt-6 { padding-top: 1.5rem; }
/* ========================================
Margin
======================================== */
.mt-0\.5 { margin-top: 0.125rem; }
.mt-1 { margin-top: 0.25rem; }
.mt-1\.5 { margin-top: 0.375rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; }
.mt-10 { margin-top: 2.5rem; }
.-mt-1 { margin-top: -0.25rem; }
.mb-0\.5 { margin-bottom: 0.125rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.ml-1 { margin-left: 0.25rem; }
.ml-3 { margin-left: 0.75rem; }
.mr-1 { margin-right: 0.25rem; }
.-mr-2 { margin-right: -0.5rem; }
.mx-auto { margin-inline: auto; }
.-mx-2 { margin-inline: -0.5rem; }
.-my-0\.5 { margin-block: -0.125rem; }
/* ========================================
Space between (lobotomised owl)
======================================== */
.space-y-1 > :not(:first-child) { margin-top: 0.25rem; }
.space-y-4 > :not(:first-child) { margin-top: 1rem; }
.space-y-6 > :not(:first-child) { margin-top: 1.5rem; }
/* ========================================
Sizing
======================================== */
.w-0 { width: 0; }
.w-3 { width: 0.75rem; }
.w-4 { width: 1rem; }
.w-5 { width: 1.25rem; }
.w-6 { width: 1.5rem; }
.w-9 { width: 2.25rem; }
.w-10 { width: 2.5rem; }
.w-12 { width: 3rem; }
.w-16 { width: 4rem; }
.w-28 { width: 7rem; }
.w-1\/3 { width: 33.333333%; }
.w-auto { width: auto; }
.w-fit { width: fit-content; }
.w-80 { width: 20rem; }
.w-full { width: 100%; }
.w-\[14px\] { width: 14px; }
.w-\[18px\] { width: 18px; }
.h-1\.5 { height: 0.375rem; }
.h-3 { height: 0.75rem; }
.h-4 { height: 1rem; }
.h-5 { height: 1.25rem; }
.h-6 { height: 1.5rem; }
.h-9 { height: 2.25rem; }
.h-10 { height: 2.5rem; }
.h-12 { height: 3rem; }
.h-full { height: 100%; }
.h-\[14px\] { height: 14px; }
.h-\[18px\] { height: 18px; }
.h-\[60px\] { height: 60px; }
.size-3 { width: 0.75rem; height: 0.75rem; }
.size-4 { width: 1rem; height: 1rem; }
.size-5 { width: 1.25rem; height: 1.25rem; }
.size-6 { width: 1.5rem; height: 1.5rem; }
.size-10 { width: 2.5rem; height: 2.5rem; }
.size-12 { width: 3rem; height: 3rem; }
.size-16 { width: 4rem; height: 4rem; }
.min-h-0 { min-height: 0; }
.min-h-screen { min-height: 100vh; }
.min-w-0 { min-width: 0; }
.min-w-48 { min-width: 12rem; }
.max-h-full { max-height: 100%; }
.max-w-80 { max-width: 20rem; }
.max-w-sm { max-width: 24rem; }
.max-w-md { max-width: 28rem; }
.max-w-lg { max-width: 32rem; }
.max-w-xl { max-width: 36rem; }
.max-w-2xl { max-width: 42rem; }
.max-w-5xl { max-width: 64rem; }
.max-w-full { max-width: 100%; }
.max-w-\[1200px\] { max-width: 1200px; }
.aspect-square { aspect-ratio: 1 / 1; }
/* ========================================
Typography
======================================== */
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-base { font-size: 1rem; line-height: 1.5rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.text-2xl { font-size: 1.5rem; line-height: 2rem; }
.text-\[2rem\] { font-size: 2rem; }
.font-normal { font-weight: 400; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.font-mono {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-balance { text-wrap: balance; }
.text-wrap { text-wrap: wrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.break-all { word-break: break-all; }
.uppercase { text-transform: uppercase; }
.capitalize { text-transform: capitalize; }
.underline { text-decoration-line: underline; }
.leading-6 { line-height: 1.5rem; }
.leading-7 { line-height: 1.75rem; }
.leading-8 { line-height: 2rem; }
.leading-10 { line-height: 2.5rem; }
.leading-none { line-height: 1; }
.leading-relaxed { line-height: 1.625; }
.tracking-tight { letter-spacing: -0.025em; }
.tracking-tighter { letter-spacing: -0.05em; }
.tracking-wider { letter-spacing: 0.05em; }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
/* ========================================
Colours DaisyUI semantic
======================================== */
.bg-base-100 { background-color: var(--color-base-100); }
.bg-base-200 { background-color: var(--color-base-200); }
.bg-base-300 { background-color: var(--color-base-300); }
.bg-base-content { background-color: var(--color-base-content); }
.bg-primary { background-color: var(--color-primary); }
.bg-white { background-color: #fff; }
.text-base-100 { color: var(--color-base-100); }
.text-base-content { color: var(--color-base-content); }
.text-base-content\/30 { color: color-mix(in oklch, var(--color-base-content) 30%, transparent); }
.text-base-content\/40 { color: color-mix(in oklch, var(--color-base-content) 40%, transparent); }
.text-base-content\/50 { color: color-mix(in oklch, var(--color-base-content) 50%, transparent); }
.text-base-content\/60 { color: color-mix(in oklch, var(--color-base-content) 60%, transparent); }
.text-base-content\/70 { color: color-mix(in oklch, var(--color-base-content) 70%, transparent); }
.text-base-content\/80 { color: color-mix(in oklch, var(--color-base-content) 80%, transparent); }
.text-error { color: var(--color-error); }
.text-success { color: var(--color-success); }
.text-white { color: #fff; }
.text-brand { color: var(--color-primary); }
.fill-base-content\/40 { fill: color-mix(in oklch, var(--color-base-content) 40%, transparent); }
/* ========================================
Colours status palette
======================================== */
.bg-green-50 { background-color: #f0fdf4; }
.bg-green-500 { background-color: #22c55e; }
.bg-green-600 { background-color: #16a34a; }
.text-green-600 { color: #16a34a; }
.text-green-700 { color: #15803d; }
.text-green-900 { color: #14532d; }
.border-green-200 { border-color: #bbf7d0; }
.bg-red-50 { background-color: #fef2f2; }
.text-red-600 { color: #dc2626; }
.text-red-700 { color: #b91c1c; }
.bg-amber-50 { background-color: #fffbeb; }
.bg-amber-100 { background-color: #fef3c7; }
.text-amber-600 { color: #d97706; }
.text-amber-700 { color: #b45309; }
.text-amber-800 { color: #92400e; }
.text-amber-900 { color: #78350f; }
.bg-blue-50 { background-color: #eff6ff; }
.text-blue-700 { color: #1d4ed8; }
.bg-purple-50 { background-color: #faf5ff; }
.text-purple-700 { color: #7e22ce; }
/* Arbitrary background colours (macOS traffic lights in theme editor) */
.bg-\[\#ff5f57\] { background-color: #ff5f57; }
.bg-\[\#ffbd2e\] { background-color: #ffbd2e; }
.bg-\[\#28c940\] { background-color: #28c940; }
/* ========================================
Borders
======================================== */
.border { border: 1px solid; }
.border-0 { border-width: 0; }
.border-1 { border-width: 1px; }
.border-2 { border-width: 2px; }
.border-b { border-bottom: 1px solid; }
.border-t { border-top: 1px solid; }
.border-t-0 { border-top-width: 0; }
.border-dashed { border-style: dashed; }
.border-base-200 { border-color: var(--color-base-200); }
.border-base-300 { border-color: var(--color-base-300); }
.border-base-content\/20 { border-color: color-mix(in oklch, var(--color-base-content) 20%, transparent); }
.border-b-base-content\/30 { border-bottom-color: color-mix(in oklch, var(--color-base-content) 30%, transparent); }
/* Arbitrary border colours (macOS traffic lights) */
.border-\[\#e14640\] { border-color: #e14640; }
.border-\[\#dfa123\] { border-color: #dfa123; }
.border-\[\#1aab29\] { border-color: #1aab29; }
/* ========================================
Border radius
======================================== */
.rounded { border-radius: 0.25rem; }
.rounded-md { border-radius: 0.375rem; }
.rounded-lg { border-radius: 0.5rem; }
.rounded-xl { border-radius: 0.75rem; }
.rounded-full { border-radius: 9999px; }
.rounded-b-lg {
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
}
.rounded-t-\[10px\] {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.rounded-box { border-radius: var(--radius-box, 0.5rem); }
/* ========================================
Ring (outline via box-shadow)
======================================== */
.ring-1 {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 1px var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.ring-inset { --tw-ring-inset: inset; }
.ring-base-300 { --tw-ring-color: var(--color-base-300); }
.ring-green-600\/20 { --tw-ring-color: rgb(22 163 74 / 0.2); }
.ring-red-600\/20 { --tw-ring-color: rgb(220 38 38 / 0.2); }
.ring-amber-600\/10 { --tw-ring-color: rgb(217 119 6 / 0.1); }
.ring-blue-600\/20 { --tw-ring-color: rgb(37 99 235 / 0.2); }
.ring-purple-600\/20 { --tw-ring-color: rgb(147 51 234 / 0.2); }
/* ========================================
Shadow
======================================== */
.shadow-xs {
--tw-shadow: 0 1px rgb(0 0 0 / 0.05);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-sm {
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
/* ========================================
Overflow
======================================== */
.overflow-auto { overflow: auto; }
.overflow-hidden { overflow: hidden; }
.overflow-x-auto { overflow-x: auto; }
/* ========================================
Object fit
======================================== */
.object-cover { object-fit: cover; }
.object-contain { object-fit: contain; }
/* ========================================
Opacity
======================================== */
.opacity-40 { opacity: 0.4; }
.opacity-75 { opacity: 0.75; }
/* ========================================
Cursor
======================================== */
.cursor-pointer { cursor: pointer; }
/* ========================================
Gradient
======================================== */
.bg-gradient-to-b {
--tw-gradient-from: transparent;
--tw-gradient-to: transparent;
background-image: linear-gradient(to bottom, var(--tw-gradient-from), var(--tw-gradient-to));
}
.from-base-300 { --tw-gradient-from: var(--color-base-300); }
.to-base-300\/80 { --tw-gradient-to: color-mix(in oklch, var(--color-base-300) 80%, transparent); }
/* ========================================
Filter
======================================== */
.brightness-200 { filter: brightness(2); }
/* ========================================
Transitions & animation
======================================== */
.transition {
transition-property: color, background-color, border-color, text-decoration-color,
fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-colors {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-transform {
transition-property: transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-\[left\] {
transition-property: left;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
@keyframes spin { to { transform: rotate(360deg); } }
.animate-spin { animation: spin 1s linear infinite; }
@media (prefers-reduced-motion: no-preference) {
.motion-safe\:animate-spin { animation: spin 1s linear infinite; }
}
/* ========================================
Hover states
======================================== */
.hover\:bg-base-200:hover { background-color: var(--color-base-200); }
.hover\:bg-base-200\/50:hover { background-color: color-mix(in oklch, var(--color-base-200) 50%, transparent); }
.hover\:bg-base-300:hover { background-color: var(--color-base-300); }
.hover\:bg-base-content\/80:hover { background-color: color-mix(in oklch, var(--color-base-content) 80%, transparent); }
.hover\:bg-green-500:hover { background-color: #22c55e; }
.hover\:border-base-300:hover { border-color: var(--color-base-300); }
.hover\:border-base-content\/40:hover { border-color: color-mix(in oklch, var(--color-base-content) 40%, transparent); }
.hover\:opacity-100:hover { opacity: 1; }
.hover\:text-base-content:hover { color: var(--color-base-content); }
.hover\:text-base-content\/70:hover { color: color-mix(in oklch, var(--color-base-content) 70%, transparent); }
.hover\:text-base-content\/80:hover { color: color-mix(in oklch, var(--color-base-content) 80%, transparent); }
.hover\:text-red-800:hover { color: #991b1b; }
.hover\:underline:hover { text-decoration-line: underline; }
.hover\:cursor-pointer:hover { cursor: pointer; }
/* ========================================
Focus states
======================================== */
.focus\:border-primary:focus { border-color: var(--color-primary); }
.focus\:outline-none:focus { outline: 2px solid transparent; outline-offset: 2px; }
.focus\:ring-1:focus {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 1px var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.focus\:ring-primary\/20:focus { --tw-ring-color: color-mix(in oklch, var(--color-primary) 20%, transparent); }
/* ========================================
Disabled states
======================================== */
.disabled\:cursor-not-allowed:disabled { cursor: not-allowed; }
.disabled\:opacity-50:disabled { opacity: 0.5; }
/* ========================================
Group states
======================================== */
.group { /* marker class — no styles */ }
.group:hover .group-hover\:bg-base-300 { background-color: var(--color-base-300); }
.group:hover .group-hover\:fill-base-content { fill: var(--color-base-content); }
.group:hover .group-hover\:opacity-70 { opacity: 0.7; }
.group:hover .group-hover\:text-base-content { color: var(--color-base-content); }
.group[open] .group-open\:rotate-180 { transform: rotate(180deg); }
/* ========================================
Pseudo-class selectors
======================================== */
.only\:block:only-child { display: block; }
.last\:pb-0:last-child { padding-bottom: 0; }
.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker { display: none; }
/* ========================================
Responsive: sm (min-width: 640px)
======================================== */
@media (min-width: 640px) {
.sm\:flex-col { flex-direction: column; }
.sm\:flex-row { flex-direction: row; }
.sm\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sm\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.sm\:p-6 { padding: 1.5rem; }
.sm\:px-6 { padding-inline: 1.5rem; }
.sm\:py-6 { padding-block: 1.5rem; }
.sm\:py-28 { padding-block: 7rem; }
.sm\:w-auto { width: auto; }
.sm\:w-96 { width: 24rem; }
.sm\:max-w-96 { max-width: 24rem; }
/* JS transition scale (used in show/hide) */
.sm\:translate-y-0 { translate: 0 0; }
.sm\:scale-95 { scale: 0.95; }
.sm\:scale-100 { scale: 1; }
.sm\:group-hover\:scale-105 { /* defined below with group:hover */ }
}
.group:hover .sm\:group-hover\:scale-105 {
@media (min-width: 640px) { scale: 1.05; }
}
/* ========================================
Responsive: lg (min-width: 1024px)
======================================== */
@media (min-width: 1024px) {
.lg\:block { display: block; }
.lg\:col-span-2 { grid-column: span 2 / span 2; }
.lg\:flex-row { flex-direction: row; }
.lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.lg\:h-screen { height: 100vh; }
.lg\:mx-0 { margin-inline: 0; }
.lg\:p-8 { padding: 2rem; }
.lg\:px-8 { padding-inline: 2rem; }
}
/* ========================================
Responsive: xl (min-width: 1280px)
======================================== */
@media (min-width: 1280px) {
.xl\:left-\[50rem\] { left: 50rem; }
.xl\:px-28 { padding-inline: 7rem; }
.xl\:py-32 { padding-block: 8rem; }
}
/* ========================================
JS transition helpers (used by core_components show/hide)
======================================== */
.translate-y-4 { translate: 0 1rem; }
.translate-y-0 { translate: 0 0; }
.opacity-0 { opacity: 0; }
.opacity-100 { opacity: 1; }
.scale-95 { scale: 0.95; }
.scale-100 { scale: 1; }
/* Duration classes used by JS.transition */
.duration-200 { transition-duration: 200ms; }
.duration-300 { transition-duration: 300ms; }
.ease-in { transition-timing-function: cubic-bezier(0.4, 0, 1, 1); }
.ease-out { transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }
/* ========================================
DaisyUI component stubs (used in Phoenix home page and theme toggle)
======================================== */
.card {
border-radius: var(--radius-box, 0.5rem);
overflow: hidden;
}
.badge {
display: inline-flex;
align-items: center;
border-radius: var(--radius-selector, 0.25rem);
padding-inline: 0.5rem;
font-size: 0.875rem;
line-height: 1.25rem;
border: 1px solid currentColor;
width: fit-content;
}
.badge-sm { font-size: 0.75rem; line-height: 1rem; padding-inline: 0.375rem; }
.badge-warning {
border-color: var(--color-warning);
color: var(--color-warning-content);
background-color: var(--color-warning);
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 600;
border-radius: var(--radius-field, 0.25rem);
border: 1px solid transparent;
cursor: pointer;
transition: background-color 150ms, border-color 150ms;
}
.btn-primary {
background-color: var(--color-primary);
color: var(--color-primary-content);
}
.btn-primary:hover {
filter: brightness(0.9);
}
/* ========================================
Prose (minimal typography block for instructions)
======================================== */
.prose {
line-height: 1.75;
}
.prose p { margin-top: 1em; margin-bottom: 1em; }
.prose :first-child { margin-top: 0; }
.prose :last-child { margin-bottom: 0; }
.prose a { color: var(--color-primary); text-decoration: underline; }
.prose strong { font-weight: 600; }
.prose-sm { font-size: 0.875rem; line-height: 1.5; }
/* ========================================
List styles
======================================== */
.list-decimal { list-style-type: decimal; }
.list-inside { list-style-position: inside; }
.list-none { list-style-type: none; }

File diff suppressed because one or more lines are too long

View File

@ -1,43 +0,0 @@
const plugin = require("tailwindcss/plugin")
const fs = require("fs")
const path = require("path")
module.exports = plugin(function({matchComponents, theme}) {
let iconsDir = path.join(__dirname, "../../deps/heroicons/optimized")
let values = {}
let icons = [
["", "/24/outline"],
["-solid", "/24/solid"],
["-mini", "/20/solid"],
["-micro", "/16/solid"]
]
icons.forEach(([suffix, dir]) => {
fs.readdirSync(path.join(iconsDir, dir)).forEach(file => {
let name = path.basename(file, ".svg") + suffix
values[name] = {name, fullPath: path.join(iconsDir, dir, file)}
})
})
matchComponents({
"hero": ({name, fullPath}) => {
let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "")
content = encodeURIComponent(content)
let size = theme("spacing.6")
if (name.endsWith("-mini")) {
size = theme("spacing.5")
} else if (name.endsWith("-micro")) {
size = theme("spacing.4")
}
return {
[`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
"-webkit-mask": `var(--hero-${name})`,
"mask": `var(--hero-${name})`,
"mask-repeat": "no-repeat",
"background-color": "currentColor",
"vertical-align": "middle",
"display": "inline-block",
"width": size,
"height": size
}
}
}, {values})
})

View File

@ -57,17 +57,10 @@ config :esbuild,
simpleshop_theme_shop_css: [
args: ~w(css/shop.css --bundle --outdir=../priv/static/assets/css),
cd: Path.expand("../assets", __DIR__)
]
# Configure tailwind (the version is required)
config :tailwind,
version: "4.1.7",
simpleshop_theme: [
args: ~w(
--input=assets/css/app.css
--output=priv/static/assets/css/app.css
),
cd: Path.expand("..", __DIR__)
],
simpleshop_theme_admin_css: [
args: ~w(css/admin.css --bundle --outdir=../priv/static/assets/css),
cd: Path.expand("../assets", __DIR__)
]
# Configures Elixir's Logger

View File

@ -27,7 +27,8 @@ config :simpleshop_theme, SimpleshopThemeWeb.Endpoint,
esbuild: {Esbuild, :install_and_run, [:simpleshop_theme, ~w(--sourcemap=inline --watch)]},
esbuild_shop_css:
{Esbuild, :install_and_run, [:simpleshop_theme_shop_css, ~w(--sourcemap=inline --watch)]},
tailwind: {Tailwind, :install_and_run, [:simpleshop_theme, ~w(--watch)]}
esbuild_admin_css:
{Esbuild, :install_and_run, [:simpleshop_theme_admin_css, ~w(--sourcemap=inline --watch)]}
]
# ## SSL Support

View File

@ -0,0 +1,126 @@
defmodule Mix.Tasks.GenerateAdminIcons do
@moduledoc """
Generates static CSS for heroicon classes used in admin templates.
Scans `lib/` for `hero-*` class references, reads the matching SVGs from
`deps/heroicons/optimized/`, and writes `assets/css/admin/icons.css` with
CSS mask-based icon classes that match the old Tailwind plugin output.
## Usage
mix generate_admin_icons
"""
@shortdoc "Generate admin/icons.css from heroicons SVGs"
use Mix.Task
@icons_dir "deps/heroicons/optimized"
@output_path "assets/css/admin/icons.css"
# Maps suffix to subdirectory and default size
@variants %{
"" => {"24/outline", "1.5rem"},
"-solid" => {"24/solid", "1.5rem"},
"-mini" => {"20/solid", "1.25rem"},
"-micro" => {"16/solid", "1rem"}
}
@impl Mix.Task
def run(_args) do
icons = scan_used_icons()
Mix.shell().info("Found #{length(icons)} icon references in lib/")
css_rules =
icons
|> Enum.sort()
|> Enum.map(&generate_rule/1)
|> Enum.reject(&is_nil/1)
missing = length(icons) - length(css_rules)
if missing > 0, do: Mix.shell().info("Skipped #{missing} (SVG not found or not an icon)")
content = """
/* Generated by mix generate_admin_icons do not edit by hand */
#{Enum.join(css_rules, "\n\n")}
"""
File.mkdir_p!(Path.dirname(@output_path))
File.write!(@output_path, content)
Mix.shell().info("Wrote #{length(css_rules)} icon rules to #{@output_path}")
end
defp scan_used_icons do
Path.wildcard("lib/**/*.{ex,heex}")
|> Enum.flat_map(fn path ->
path
|> File.read!()
|> then(&Regex.scan(~r/hero-[a-z][a-z0-9-]+/, &1))
|> List.flatten()
end)
|> Enum.uniq()
|> Enum.reject(&shop_class?/1)
end
# Filter out CSS class names from shop components that start with "hero-"
# but aren't actual heroicon references
defp shop_class?(name) do
name in [
"hero-section",
"hero-description",
"hero-cta",
"hero-cta-group",
"hero-pre-title",
"hero-error",
"hero-image",
"hero-section--page"
]
end
defp generate_rule(class_name) do
{suffix, {dir, size}} = detect_variant(class_name)
base = String.trim_leading(class_name, "hero-")
svg_name = if suffix == "", do: base, else: String.trim_trailing(base, suffix)
svg_path = Path.join([@icons_dir, dir, "#{svg_name}.svg"])
if File.exists?(svg_path) do
content =
svg_path
|> File.read!()
|> String.replace(~r/\r?\n|\r/, "")
|> URI.encode(&uri_allowed?/1)
"""
.#{class_name} {
--hero-#{class_name |> String.trim_leading("hero-")}: url('data:image/svg+xml;utf8,#{content}');
-webkit-mask: var(--hero-#{class_name |> String.trim_leading("hero-")});
mask: var(--hero-#{class_name |> String.trim_leading("hero-")});
mask-repeat: no-repeat;
background-color: currentColor;
vertical-align: middle;
display: inline-block;
width: #{size};
height: #{size};
}\
"""
else
Mix.shell().info(" warning: SVG not found for #{class_name} (expected #{svg_path})")
nil
end
end
defp detect_variant(class_name) do
cond do
String.ends_with?(class_name, "-micro") -> {"-micro", @variants["-micro"]}
String.ends_with?(class_name, "-mini") -> {"-mini", @variants["-mini"]}
String.ends_with?(class_name, "-solid") -> {"-solid", @variants["-solid"]}
true -> {"", @variants[""]}
end
end
# Characters that don't need percent-encoding in data URIs
defp uri_allowed?(char) do
URI.char_unreserved?(char) or char in [?<, ?>, ?/, ?=, ?", ?', ?:, ?;, ?(, ?), ?!, ?@, ?,, ?.]
end
end

View File

@ -133,10 +133,9 @@ defmodule Mix.Tasks.Lighthouse do
defp build_prod_assets! do
Mix.shell().info("Building production assets...")
Mix.Task.run("tailwind", ["simpleshop_theme", "--minify"])
Mix.Task.run("tailwind", ["simpleshop_theme_shop", "--minify"])
Mix.Task.run("esbuild", ["simpleshop_theme", "--minify"])
Mix.Task.run("esbuild", ["simpleshop_theme_shop_css", "--minify"])
Mix.Task.run("esbuild", ["simpleshop_theme_admin_css", "--minify"])
Mix.Task.run("phx.digest")
Mix.shell().info(" Assets built and digested.")

View File

@ -158,9 +158,9 @@ defmodule Mix.Tasks.Screenshots do
defp build_prod_assets! do
Mix.shell().info("Building production assets...")
Mix.Task.run("tailwind", ["simpleshop_theme", "--minify"])
Mix.Task.run("esbuild", ["simpleshop_theme", "--minify"])
Mix.Task.run("esbuild", ["simpleshop_theme_shop_css", "--minify"])
Mix.Task.run("esbuild", ["simpleshop_theme_admin_css", "--minify"])
Mix.Task.run("phx.digest")
Mix.shell().info(" Assets built and digested.")

View File

@ -393,8 +393,8 @@ defmodule SimpleshopThemeWeb.CoreComponents do
You can customize the size and colors of the icons by setting
width, height, and background color classes.
Icons are extracted from the `deps/heroicons` directory and bundled within
your compiled app.css by the plugin in `assets/vendor/heroicons.js`.
Icons are extracted from the `deps/heroicons` directory and bundled into
`admin/icons.css` by `mix generate_admin_icons`.
## Examples

View File

@ -98,14 +98,14 @@ defmodule SimpleshopThemeWeb.Layouts do
end
@doc """
Provides dark vs light theme toggle based on themes defined in app.css.
Provides dark vs light theme toggle based on themes defined in admin.css.
See <head> in root.html.heex which applies the theme before page load.
"""
def theme_toggle(assigns) do
~H"""
<div class="card relative flex flex-row items-center border-2 border-base-300 bg-base-300 rounded-full">
<div class="absolute w-1/3 h-full rounded-full border-1 border-base-200 bg-base-100 brightness-200 left-0 [[data-theme=light]_&]:left-1/3 [[data-theme=dark]_&]:left-2/3 transition-[left]" />
<div class="theme-toggle-indicator absolute w-1/3 h-full rounded-full border-1 border-base-200 bg-base-100 brightness-200 left-0" />
<button
class="flex p-2 cursor-pointer w-1/3"

View File

@ -11,7 +11,7 @@
<style>
@layer properties, reset, primitives, tokens, theme, base, components, layout, utilities, overrides;
</style>
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<link phx-track-static rel="stylesheet" href={~p"/assets/css/admin.css"} />
<link phx-track-static rel="stylesheet" href={~p"/assets/css/shop.css"} />
<script defer phx-track-static src={~p"/assets/js/app.js"}>
</script>

View File

@ -7,7 +7,7 @@
<.live_title default="SimpleshopTheme" suffix=" · Phoenix Framework">
{assigns[:page_title]}
</.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<link phx-track-static rel="stylesheet" href={~p"/assets/css/admin.css"} />
<script defer phx-track-static src={~p"/assets/js/app.js"}>
</script>
<script>

View File

@ -66,7 +66,7 @@ defmodule SimpleshopThemeWeb.ErrorHTML do
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{@error_code} - {@error_title}</title>
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<link phx-track-static rel="stylesheet" href={~p"/assets/css/admin.css"} />
<style id="theme-css">
<%= Phoenix.HTML.raw(@generated_css) %>
</style>

View File

@ -54,7 +54,6 @@ defmodule SimpleshopTheme.MixProject do
{:lazy_html, ">= 0.1.0", only: :test},
{:phoenix_live_dashboard, "~> 0.8.3"},
{:esbuild, "~> 0.10", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.3", runtime: Mix.env() == :dev},
{:heroicons,
github: "tailwindlabs/heroicons",
tag: "v2.2.0",
@ -95,17 +94,17 @@ defmodule SimpleshopTheme.MixProject do
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
"assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
"assets.setup": ["esbuild.install --if-missing"],
"assets.build": [
"compile",
"tailwind simpleshop_theme",
"esbuild simpleshop_theme",
"esbuild simpleshop_theme_shop_css"
"esbuild simpleshop_theme_shop_css",
"esbuild simpleshop_theme_admin_css"
],
"assets.deploy": [
"tailwind simpleshop_theme --minify",
"esbuild simpleshop_theme --minify",
"esbuild simpleshop_theme_shop_css --minify",
"esbuild simpleshop_theme_admin_css --minify",
"phx.digest"
],
precommit: ["compile --warning-as-errors", "deps.unlock --unused", "format", "test"],

View File

@ -66,7 +66,6 @@
"stripity_stripe": {:hex, :stripity_stripe, "3.2.0", "07c27f5f2ac87006945b5c997b99d1210e009e380ea78d339d025b11c9c745f5", [:mix], [{:hackney, "~> 1.18", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}, {:uri_query, "~> 0.2.0", [hex: :uri_query, repo: "hexpm", optional: false]}], "hexpm", "f797936a9e9538370bae7dc73d73eafd7e44ecdc95b71c88492c43f6df094cb0"},
"sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"},
"swoosh": {:hex, :swoosh, "1.20.0", "b04134c2b302da74c3a95ca4ddde191e4854d2847d6687783fecb023a9647598", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "13e610f709bae54851d68afb6862882aa646e5c974bf49e3bf5edd84a73cf213"},
"tailwind": {:hex, :tailwind, "0.4.1", "e7bcc222fe96a1e55f948e76d13dd84a1a7653fb051d2a167135db3b4b08d3e9", [:mix], [], "hexpm", "6249d4f9819052911120dbdbe9e532e6bd64ea23476056adb7f730aa25c220d1"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"},
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},

View File

@ -5,8 +5,8 @@ defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do
Architecture:
- Both shop pages and preview use .themed class for shared styles
- Theme editor uses .preview-frame[data-*] selectors for live switching (in app.css)
- Shop pages get theme values via inline CSS from CSSGenerator (app-shop.css)
- Theme editor uses .preview-frame[data-*] selectors for live switching (in admin.css)
- Shop pages get theme values via inline CSS from CSSGenerator (shop.css)
- Component styles use .themed for shared styling (theme-layer2-attributes.css)
"""
@ -90,13 +90,13 @@ defmodule SimpleshopThemeWeb.ThemeCSSConsistencyTest do
end
describe "CSS file structure" do
test "app.css has .preview-frame variant selectors for theme editor" do
css_path = Path.join([File.cwd!(), "assets", "css", "app.css"])
test "admin.css has .preview-frame variant selectors for theme editor" do
css_path = Path.join([File.cwd!(), "assets", "css", "admin.css"])
css_content = File.read!(css_path)
# Variant selectors are editor-only (.preview-frame) using CSS nesting
# The file uses &[data-*] syntax inside .preview-frame { }
# These rules are in app.css (admin only), not app-shop.css
# These rules are in admin.css (admin only), not shop.css
assert css_content =~ ".preview-frame"
assert css_content =~ "&[data-mood=\"dark\"]"
assert css_content =~ "&[data-mood=\"warm\"]"