feat: add shared templates and static assets infrastructure

Add shared template infrastructure and static assets:
- Shared Jinja2 templates for reusable components
- Favicon for branding
- Local Tailwind CSS fallback
- Shop CSS styles directory

This provides the foundation for consistent UI components across
admin, vendor, and shop frontends with CDN fallback support.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-22 15:53:23 +01:00
parent 7879e2f70a
commit cadf771138
5 changed files with 317 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
{# app/templates/shared/cdn-fallback.html #}
{# CDN with Local Fallback Pattern #}
{# This partial handles loading CDN resources with automatic fallback to local copies #}
{# Tailwind CSS with fallback #}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
onerror="this.onerror=null; this.href='{{ url_for('static', path='shared/css/tailwind.min.css') }}';">
{# Alpine.js with fallback - must be loaded at the end of body #}
{# Usage: Include this partial at the bottom of your template, before page-specific scripts #}
<script>
// Alpine.js CDN with fallback
(function() {
var script = document.createElement('script');
script.defer = true;
script.src = 'https://cdn.jsdelivr.net/npm/alpinejs@3.13.3/dist/cdn.min.js';
script.onerror = function() {
console.warn('Alpine.js CDN failed, loading local copy...');
var fallbackScript = document.createElement('script');
fallbackScript.defer = true;
fallbackScript.src = '{{ url_for("static", path="shared/js/vendor/alpine.min.js") }}';
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);
})();
</script>

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

1
static/shared/css/tailwind.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

283
static/shop/css/shop.css Normal file
View File

@@ -0,0 +1,283 @@
/* Shop Frontend Styles */
/* Uses CSS variables injected by theme system */
/* Base Utilities */
.hover\:text-primary:hover {
color: var(--color-primary) !important;
}
.text-primary {
color: var(--color-primary);
}
.bg-primary {
background-color: var(--color-primary);
}
.bg-accent {
background-color: var(--color-accent);
}
.border-primary {
border-color: var(--color-primary);
}
/* Button Styles */
.btn-primary {
background-color: var(--color-primary);
color: white;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.btn-secondary {
background-color: var(--color-secondary);
color: white;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
}
.btn-secondary:hover {
opacity: 0.9;
}
.btn-outline {
border: 2px solid var(--color-primary);
color: var(--color-primary);
background-color: transparent;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
}
.btn-outline:hover {
background-color: var(--color-primary);
color: white;
}
/* Product Card Styles */
.product-card {
background: white;
border-radius: 0.5rem;
overflow: hidden;
transition: all 0.3s;
border: 1px solid var(--color-border);
}
.product-card:hover {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
transform: translateY(-4px);
}
.product-card.dark {
background: #1f2937;
border-color: #374151;
}
.product-card-image {
width: 100%;
height: 250px;
object-fit: cover;
}
.product-card-title {
font-family: var(--font-heading);
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.5rem;
}
.product-card-price {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-primary);
}
/* Category Badge */
.category-badge {
display: inline-block;
padding: 0.25rem 0.75rem;
background-color: var(--color-secondary);
color: white;
font-size: 0.75rem;
font-weight: 500;
border-radius: 9999px;
}
/* Toast Notifications */
.toast {
animation: slideInRight 0.3s ease-out;
}
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Loading Spinner */
.spinner {
border: 3px solid rgba(0, 0, 0, 0.1);
border-left-color: var(--color-primary);
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Search Overlay */
.search-overlay {
backdrop-filter: blur(4px);
}
/* Mobile Menu Slide */
.mobile-menu {
transform: translateX(-100%);
transition: transform 0.3s ease-out;
}
.mobile-menu.open {
transform: translateX(0);
}
/* Cart Badge */
.cart-badge {
min-width: 1.25rem;
height: 1.25rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
}
/* Smooth Transitions */
* {
transition-property: background-color, border-color, color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
/* Typography with Theme Fonts */
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-heading);
}
body {
font-family: var(--font-body);
color: var(--color-text);
background-color: var(--color-background);
}
/* Dark Mode Adjustments */
.dark body {
background-color: #111827;
color: #f9fafb;
}
.dark .product-card {
background-color: #1f2937;
border-color: #374151;
}
.dark .product-card-title {
color: #f9fafb;
}
/* Hero Section */
.hero-section {
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
color: white;
padding: 4rem 2rem;
text-align: center;
}
/* Feature Cards */
.feature-card {
padding: 2rem;
border-radius: 0.5rem;
border: 1px solid var(--color-border);
transition: all 0.3s;
}
.feature-card:hover {
border-color: var(--color-primary);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Breadcrumbs */
.breadcrumb {
display: flex;
gap: 0.5rem;
align-items: center;
font-size: 0.875rem;
color: #6b7280;
}
.breadcrumb a:hover {
color: var(--color-primary);
}
/* Price Display */
.price-old {
text-decoration: line-through;
color: #9ca3af;
font-size: 0.875rem;
}
.price-new {
color: var(--color-accent);
font-weight: 700;
font-size: 1.5rem;
}
.price-discount {
background-color: var(--color-accent);
color: white;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 600;
}
/* Rating Stars */
.rating-stars {
display: flex;
gap: 0.125rem;
color: #fbbf24;
}
/* Responsive Grid */
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
@media (max-width: 640px) {
.product-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
}
}