fix(storefront): premium UX polish for home, catalog, search

- product-card: normalize hardcoded hex colors to design tokens
  (--text-primary, --border-color, --primary-color, --bg-tertiary,
  --bg-secondary); stock bar and stock badge now use semantic
  --success-color/--warning-color/--error-color instead of
  near-duplicate literal hex; add-to-cart hover uses --primary-hover
  and --transition-* tokens; card hover shadow uses --shadow-lg
- product-card: add aria-pressed to favorite/compare toggle buttons
  so their selected state isn't color-only
- filters-panel: add aria-pressed to color/size/rating filter chips;
  add a visible checkmark glyph on selected color swatches plus a
  focus-style selection ring, so selection isn't conveyed by border
  color alone
- layout-switcher: add aria-pressed to the active layout button
- catalog-container: add aria-current to the mobile sort-sheet and
  grid-sheet option buttons; active sort option gets a checkmark
  and bold weight instead of color-only highlighting
- category-grid: normalize hardcoded border/background/text colors
  to tokens; align focus ring with the color-mix pattern used
  elsewhere in catalog
- search-results, sorting-control: normalize skeleton/select colors
  to tokens; sort <select> gets a hover border state
- home: convert loading-grid/empty-state spacing to --space-* tokens

Build verified green via `npm run build`.

Out of scope / skipped:
- pages/category and pages/search retain their existing hand-rolled
  skeleton markup (not app-skeleton) - replacing it is a composition
  change, not covered by this visual-polish pass
- product-card rating-stars color and legacy pages/category,
  pages/search hex literals left as-is where no exact token match
  exists, to avoid an unintended visual shift

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-23 11:34:05 +04:00
parent 2c244ad366
commit 9ea8c98faf
11 changed files with 74 additions and 42 deletions

View File

@@ -12,10 +12,10 @@
@if (showFavoriteControl() || showCompareControl() || showShareAction || showQuickViewPlaceholder) {
<div class="product-actions-overlay">
@if (showFavoriteControl()) {
<button type="button" class="product-action-btn product-action-favorite" [attr.aria-label]="'catalog.favorite' | translate" [class.active]="isFavorite" (click)="onFavoritePlaceholder($event)"></button>
<button type="button" class="product-action-btn product-action-favorite" [attr.aria-label]="'catalog.favorite' | translate" [attr.aria-pressed]="isFavorite" [class.active]="isFavorite" (click)="onFavoritePlaceholder($event)"></button>
}
@if (showCompareControl()) {
<button type="button" class="product-action-btn" [attr.aria-label]="'catalog.compare' | translate" [class.active]="isCompared" (click)="onComparePlaceholder($event)"></button>
<button type="button" class="product-action-btn" [attr.aria-label]="'catalog.compare' | translate" [attr.aria-pressed]="isCompared" [class.active]="isCompared" (click)="onComparePlaceholder($event)"></button>
}
@if (showShareAction) {
<button type="button" class="product-action-btn" [attr.aria-label]="'catalog.share' | translate" (click)="onShare($event)"></button>

View File

@@ -5,11 +5,11 @@
display: flex;
flex-direction: column;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
transition: transform var(--transition-normal), box-shadow var(--transition-normal);
&:hover {
transform: translateY(-2px);
box-shadow: 0 10px 26px rgba(15, 23, 42, 0.12);
box-shadow: var(--shadow-lg);
}
}
@@ -26,14 +26,14 @@
width: 100%;
aspect-ratio: 1 / 1;
min-height: 220px;
border: 1px solid #d3dad9;
border: 1px solid var(--border-color);
border-radius: 13px 13px 0 0;
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.15);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background: #f0f0f0;
background: var(--bg-tertiary);
img {
width: 100%;
@@ -72,7 +72,7 @@
align-items: center;
padding: 2px 8px;
border-radius: var(--radius-full, 999px);
background: rgba(22, 163, 74, 0.9);
background: color-mix(in srgb, var(--success-color) 90%, black);
color: #fff;
font-size: var(--font-size-xs, 0.75rem);
font-weight: var(--font-weight-bold, 700);
@@ -80,7 +80,7 @@
}
.stock-badge.out {
background: rgba(239, 68, 68, 0.9);
background: color-mix(in srgb, var(--error-color) 90%, black);
}
.product-actions-overlay {
@@ -98,7 +98,7 @@
border: 1px solid rgba(30, 60, 56, 0.15);
border-radius: 50%;
background: rgba(255, 255, 255, 0.95);
color: #1e3c38;
color: var(--text-primary);
font-size: var(--font-size-lg, 1rem);
font-weight: var(--font-weight-bold, 700);
display: inline-flex;
@@ -110,12 +110,12 @@
.product-action-btn:hover {
transform: translateY(-1px);
border-color: #497671;
border-color: var(--primary-color);
}
.product-action-btn.active {
border-color: #497671;
color: #497671;
border-color: var(--primary-color);
color: var(--primary-color);
}
.product-action-btn:focus-visible,
@@ -158,7 +158,7 @@
min-height: 24px;
padding: 3px 8px;
border-radius: var(--radius-full, 999px);
background: #497671;
background: var(--primary-color);
color: #fff;
font-size: var(--font-size-xs, 0.75rem);
font-weight: var(--font-weight-bold, 700);
@@ -167,12 +167,12 @@
.product-details {
width: 100%;
border: 1px solid #d3dad9;
border: 1px solid var(--border-color);
border-top: none;
border-radius: 0 0 13px 13px;
padding: 16px;
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.15);
background: #f5f3f9;
background: var(--bg-secondary);
flex: 1;
display: flex;
flex-direction: column;
@@ -182,7 +182,7 @@
.product-name {
font-weight: var(--font-weight-bold, 700);
font-size: clamp(14px, 1.4vw, 18px);
color: #1e3c38;
color: var(--text-primary);
margin: 0;
line-height: 1.3;
display: -webkit-box;
@@ -196,7 +196,7 @@
.product-description {
margin: 0;
color: #697777;
color: var(--text-secondary);
font-size: var(--font-size-base, 0.875rem);
line-height: 1.45;
display: -webkit-box;
@@ -211,7 +211,7 @@
align-items: center;
gap: 6px;
font-size: var(--font-size-sm, 0.8125rem);
color: #697777;
color: var(--text-secondary);
.rating-stars {
color: #b45309;
@@ -234,7 +234,7 @@
.discounted-price,
.current-price {
color: #1e3c38;
color: var(--text-primary);
font-size: var(--font-size-xl, 1.125rem);
font-weight: 800;
}
@@ -252,18 +252,18 @@
.bar-segment {
flex: 1;
border-radius: var(--radius-full, 999px);
background: #d3dad9;
background: var(--border-color);
&.filled.high {
background: #16a34a;
background: var(--success-color);
}
&.filled.medium {
background: #f59e0b;
background: var(--warning-color);
}
&.filled.low {
background: #ef4444;
background: var(--error-color);
}
}
@@ -273,14 +273,14 @@
min-height: 42px;
border: 0;
border-radius: var(--radius-lg, 13px);
background: #497671;
background: var(--primary-color);
color: #fff;
font-weight: var(--font-weight-bold, 700);
cursor: pointer;
transition: background 0.2s ease, transform 0.15s ease;
transition: background var(--transition-normal), transform var(--transition-fast);
&:hover {
background: #3d635f;
background: var(--primary-hover);
transform: translateY(-1px);
}