feat: migrate visual fixtures to storybook
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
<script setup>
|
||||
import { RouterView } from 'vue-router';
|
||||
import { useTheme } from '@/composables/useTheme.js';
|
||||
|
||||
useTheme();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
@use 'sass:map';
|
||||
|
||||
:root {
|
||||
--color-white: #ffffff;
|
||||
--color-black: #0b0d10;
|
||||
--color-cobalt-500: #1769ff;
|
||||
--color-cobalt-400: #4d8cff;
|
||||
--color-cobalt-700: #0e45b5;
|
||||
--color-gray-050: #f8fafc;
|
||||
--color-gray-100: #f1f4f8;
|
||||
--color-gray-200: #e3e8ef;
|
||||
--color-gray-300: #c7d0dc;
|
||||
--color-gray-500: #667384;
|
||||
--color-gray-700: #344052;
|
||||
--color-gray-900: #161b22;
|
||||
--color-success-500: #12a150;
|
||||
--color-warning-500: #d88a00;
|
||||
--color-danger-500: #e5484d;
|
||||
--color-info-500: #1683c4;
|
||||
|
||||
--font-ui: 'Manrope', ui-sans-serif, system-ui, sans-serif;
|
||||
--font-display: 'Space Grotesk', 'Manrope', ui-sans-serif, sans-serif;
|
||||
--weight-regular: 400;
|
||||
--weight-medium: 500;
|
||||
--weight-semibold: 600;
|
||||
--weight-bold: 700;
|
||||
--text-xs: 0.75rem;
|
||||
--text-sm: 0.875rem;
|
||||
--text-md: 1rem;
|
||||
--text-lg: 1.25rem;
|
||||
--text-xl: 1.5rem;
|
||||
--text-2xl: 2rem;
|
||||
--text-display: clamp(2.5rem, 8vw, 5.5rem);
|
||||
--leading-tight: 1.05;
|
||||
--leading-normal: 1.5;
|
||||
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
--space-16: 64px;
|
||||
--space-24: 96px;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 6px;
|
||||
--radius-lg: 8px;
|
||||
--radius-xl: 12px;
|
||||
--radius-2xl: 16px;
|
||||
--radius-full: 999px;
|
||||
--border-thin: 1px;
|
||||
--border-strong: 2px;
|
||||
--shadow-sm: 0 2px 8px rgb(11 13 16 / 8%);
|
||||
--shadow-md: 0 8px 24px rgb(11 13 16 / 12%);
|
||||
--duration-fast: 80ms;
|
||||
--duration-standard: 180ms;
|
||||
--duration-slow: 350ms;
|
||||
--breakpoint-sm: 640px;
|
||||
--breakpoint-md: 768px;
|
||||
--breakpoint-lg: 1024px;
|
||||
--breakpoint-xl: 1280px;
|
||||
--breakpoint-2xl: 1536px;
|
||||
}
|
||||
|
||||
:root,
|
||||
[data-theme='light'] {
|
||||
color-scheme: light;
|
||||
--color-background: var(--color-gray-050);
|
||||
--color-surface: var(--color-white);
|
||||
--color-surface-raised: var(--color-white);
|
||||
--color-surface-muted: var(--color-gray-100);
|
||||
--color-text: var(--color-gray-900);
|
||||
--color-text-muted: var(--color-gray-500);
|
||||
--color-text-subtle: var(--color-gray-700);
|
||||
--color-border: var(--color-gray-200);
|
||||
--color-border-strong: var(--color-gray-300);
|
||||
--color-interactive: var(--color-cobalt-500);
|
||||
--color-interactive-hover: var(--color-cobalt-700);
|
||||
--color-on-interactive: var(--color-white);
|
||||
--color-focus: var(--color-cobalt-500);
|
||||
--color-stage: var(--color-gray-100);
|
||||
--glow-focus: 0 0 0 4px rgb(23 105 255 / 22%);
|
||||
--glow-selection: 0 0 0 3px rgb(23 105 255 / 18%);
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
color-scheme: dark;
|
||||
--color-background: #0b0d10;
|
||||
--color-surface: #11161d;
|
||||
--color-surface-raised: #18202b;
|
||||
--color-surface-muted: #202a37;
|
||||
--color-text: #f5f7fa;
|
||||
--color-text-muted: #a7b2c2;
|
||||
--color-text-subtle: #d1d8e2;
|
||||
--color-border: #2b3745;
|
||||
--color-border-strong: #425064;
|
||||
--color-interactive: var(--color-cobalt-400);
|
||||
--color-interactive-hover: #83adff;
|
||||
--color-on-interactive: #07142f;
|
||||
--color-focus: var(--color-cobalt-400);
|
||||
--color-stage: #202630;
|
||||
--glow-focus: 0 0 0 4px rgb(77 140 255 / 28%);
|
||||
--glow-selection: 0 0 0 3px rgb(77 140 255 / 24%);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:root { --duration-fast: 0ms; --duration-standard: 0ms; --duration-slow: 0ms; }
|
||||
}
|
||||
@@ -1,53 +1,97 @@
|
||||
:root {
|
||||
color: #22231f;
|
||||
background: #f3efe6;
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
@use './global-tokens';
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap');
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; min-width: 320px; }
|
||||
button, input { font: inherit; }
|
||||
html { background: var(--color-background); }
|
||||
body { min-width: 320px; margin: 0; background: var(--color-background); color: var(--color-text); font-family: var(--font-ui); font-synthesis: none; line-height: var(--leading-normal); text-rendering: optimizeLegibility; transition: background var(--duration-standard) ease, color var(--duration-standard) ease; }
|
||||
button, input, select, textarea { font: inherit; }
|
||||
button, a, input, select, textarea { &:focus-visible { outline: var(--border-strong) solid var(--color-focus); outline-offset: 3px; box-shadow: var(--glow-focus); } }
|
||||
button { cursor: pointer; }
|
||||
h1, h2, h3, h4 { margin: 0; font-family: var(--font-display); line-height: var(--leading-tight); }
|
||||
|
||||
.catalog-page {
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(circle at 85% 12%, #d9e7df 0, transparent 30%), #f3efe6;
|
||||
}
|
||||
|
||||
.catalog-header, .catalog-results { width: min(1120px, calc(100% - 40px)); margin: 0 auto; }
|
||||
.catalog-header { padding: 88px 0 72px; }
|
||||
.catalog-kicker, .listing-card__status { color: #a3482a; font: 700 0.75rem/1.2 Arial, sans-serif; letter-spacing: 0.14em; text-transform: uppercase; }
|
||||
h1 { max-width: 680px; margin: 12px 0 18px; font-size: clamp(3.2rem, 9vw, 7rem); line-height: 0.92; font-weight: 500; }
|
||||
.catalog-intro { max-width: 480px; color: #61625a; font: 1.1rem/1.5 Arial, sans-serif; }
|
||||
.search-field { display: block; max-width: 720px; margin-top: 48px; color: #61625a; font: 700 0.8rem/1 Arial, sans-serif; text-transform: uppercase; letter-spacing: 0.08em; }
|
||||
.search-field input { display: block; width: 100%; margin-top: 12px; padding: 18px 20px; border: 1px solid #9ca69b; border-radius: 2px; background: #fffdf8; color: #22231f; font-size: 1.05rem; letter-spacing: 0; text-transform: none; outline-offset: 4px; }
|
||||
.catalog-results { padding-bottom: 88px; }
|
||||
.results-heading { display: flex; align-items: baseline; justify-content: space-between; border-bottom: 1px solid #c9c8bd; padding-bottom: 14px; }
|
||||
h2 { margin: 0; font-size: 1.8rem; font-weight: 500; }
|
||||
.results-heading span { color: #77786f; font: 0.85rem Arial, sans-serif; }
|
||||
.listing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 24px; }
|
||||
.detail-page { width: min(920px, calc(100% - 40px)); min-height: 100vh; margin: 0 auto; padding: 48px 0 88px; }
|
||||
.detail-back { color: #386052; font: 700 0.85rem Arial, sans-serif; }
|
||||
.detail-content { max-width: 680px; padding-top: 96px; }
|
||||
.catalog-page, .detail-page, .showcase-page { min-height: 100vh; background: var(--color-background); }
|
||||
.catalog-header, .catalog-results { width: min(1120px, calc(100% - var(--space-8))); margin: 0 auto; }
|
||||
.catalog-header { padding: var(--space-24) 0 var(--space-16); }
|
||||
.catalog-kicker, .listing-card__status { color: var(--color-interactive); font-size: var(--text-xs); font-weight: var(--weight-bold); letter-spacing: 0.12em; text-transform: uppercase; }
|
||||
h1 { max-width: 760px; margin: var(--space-3) 0 var(--space-4); font-size: var(--text-display); }
|
||||
.catalog-intro { max-width: 560px; color: var(--color-text-muted); font-size: var(--text-lg); }
|
||||
.search-field { display: block; max-width: 720px; margin-top: var(--space-12); color: var(--color-text-subtle); font-size: var(--text-sm); font-weight: var(--weight-bold); }
|
||||
.search-field input { display: block; width: 100%; margin-top: var(--space-2); border: var(--border-thin) solid var(--color-border-strong); border-radius: var(--radius-lg); padding: var(--space-4) var(--space-6); background: var(--color-surface); color: var(--color-text); }
|
||||
.catalog-results { padding-bottom: var(--space-24); }
|
||||
.results-heading { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); border-bottom: var(--border-thin) solid var(--color-border); padding-bottom: var(--space-3); }
|
||||
.results-heading span { color: var(--color-text-muted); font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
|
||||
.listing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); margin-top: var(--space-6); }
|
||||
.detail-page { width: min(920px, calc(100% - var(--space-8))); margin: 0 auto; padding: var(--space-12) 0 var(--space-24); }
|
||||
.detail-back { color: var(--color-interactive); font-weight: var(--weight-bold); text-decoration: none; }
|
||||
.detail-content { max-width: 680px; padding-top: var(--space-16); }
|
||||
.detail-content h1 { font-size: clamp(3rem, 8vw, 6rem); }
|
||||
.detail-price { font: 700 1.25rem Arial, sans-serif; }
|
||||
.detail-notes { max-width: 520px; color: #61625a; font: 1.1rem/1.5 Arial, sans-serif; }
|
||||
.product-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 32px; max-width: 560px; margin-top: 40px; border-top: 1px solid #c9c8bd; padding-top: 22px; }
|
||||
.product-facts div { display: grid; gap: 6px; }
|
||||
.product-facts dt { color: #77786f; font: 700 0.72rem Arial, sans-serif; letter-spacing: 0.1em; text-transform: uppercase; }
|
||||
.product-facts dd { margin: 0; font: 1rem Arial, sans-serif; }
|
||||
.catalog-state { padding: 48px 0; color: #61625a; font: 1rem/1.5 Arial, sans-serif; }
|
||||
.catalog-state--error { color: #a3482a; }
|
||||
.pagination { display: flex; align-items: center; justify-content: center; gap: 18px; margin-top: 28px; color: #61625a; font: 0.85rem Arial, sans-serif; }
|
||||
.pagination button { border: 1px solid #9ca69b; padding: 10px 14px; background: #fffdf8; color: #386052; cursor: pointer; }
|
||||
.pagination button:hover:not(:disabled) { border-color: #386052; }
|
||||
.pagination button:focus-visible { outline: 3px solid #a3482a; outline-offset: 3px; }
|
||||
.detail-price { color: var(--color-text); font-size: var(--text-xl); font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }
|
||||
.detail-notes { max-width: 560px; color: var(--color-text-muted); font-size: var(--text-lg); }
|
||||
.product-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6) var(--space-8); max-width: 560px; margin-top: var(--space-12); border-top: var(--border-thin) solid var(--color-border); padding-top: var(--space-6); }
|
||||
.product-facts div { display: grid; gap: var(--space-2); }
|
||||
.product-facts dt { color: var(--color-text-muted); font-size: var(--text-xs); font-weight: var(--weight-bold); letter-spacing: 0.1em; text-transform: uppercase; }
|
||||
.product-facts dd { margin: 0; }
|
||||
.catalog-state { padding: var(--space-12) 0; color: var(--color-text-muted); }
|
||||
.catalog-state--error { color: var(--color-danger-500); }
|
||||
.pagination { display: flex; align-items: center; justify-content: center; gap: var(--space-6); margin-top: var(--space-8); color: var(--color-text-muted); font-size: var(--text-sm); }
|
||||
.pagination button { min-width: 44px; min-height: 44px; border: var(--border-thin) solid var(--color-border-strong); border-radius: var(--radius-md); padding: var(--space-2) var(--space-4); background: var(--color-surface); color: var(--color-interactive); }
|
||||
.pagination button:disabled { cursor: not-allowed; opacity: 0.45; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.catalog-header { padding-top: 56px; }
|
||||
.listing-grid { grid-template-columns: 1fr; }
|
||||
h1 { font-size: 4rem; }
|
||||
.showcase-page { padding: var(--space-12) max(var(--space-4), calc((100vw - 1200px) / 2)); }
|
||||
.showcase-header { display: flex; align-items: end; justify-content: space-between; gap: var(--space-8); margin-bottom: var(--space-12); }
|
||||
.showcase-header h1 { max-width: 720px; }
|
||||
.showcase-header p { max-width: 600px; color: var(--color-text-muted); }
|
||||
.showcase-section { margin-top: var(--space-16); }
|
||||
.showcase-section h2 { margin-bottom: var(--space-6); font-size: var(--text-2xl); }
|
||||
.showcase-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
|
||||
.showcase-panel { border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-xl); padding: var(--space-6); background: var(--color-surface); box-shadow: var(--shadow-sm); }
|
||||
.showcase-panel h3 { margin-bottom: var(--space-4); font-size: var(--text-lg); }
|
||||
.theme-controls, .control-row, .status-row, .swatch-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
|
||||
.theme-controls { justify-content: end; }
|
||||
.theme-controls label, .fixture-label { color: var(--color-text-muted); font-size: var(--text-xs); font-weight: var(--weight-bold); }
|
||||
.theme-controls select, .fixture-input { min-height: 44px; border: var(--border-thin) solid var(--color-border-strong); border-radius: var(--radius-md); padding: var(--space-2) var(--space-3); background: var(--color-surface); color: var(--color-text); }
|
||||
.button { min-height: 44px; border: var(--border-thin) solid transparent; border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); font-weight: var(--weight-bold); }
|
||||
.button--primary { background: var(--color-interactive); color: var(--color-on-interactive); }
|
||||
.button--secondary { border-color: var(--color-border-strong); background: var(--color-surface); color: var(--color-text); }
|
||||
.button--danger { background: var(--color-danger-500); color: var(--color-white); }
|
||||
.button:disabled { cursor: not-allowed; opacity: 0.45; }
|
||||
.swatch { display: grid; gap: var(--space-2); color: var(--color-text-muted); font-size: var(--text-xs); }
|
||||
.swatch__chip { width: 72px; height: 44px; border-radius: var(--radius-md); }
|
||||
.swatch__chip--cobalt { background: var(--color-cobalt-500); }
|
||||
.swatch__chip--success { background: var(--color-success-500); }
|
||||
.swatch__chip--warning { background: var(--color-warning-500); }
|
||||
.swatch__chip--danger { background: var(--color-danger-500); }
|
||||
.swatch__chip--info { background: var(--color-info-500); }
|
||||
.type-sample { display: grid; gap: var(--space-3); }
|
||||
.type-sample__display { font-family: var(--font-display); font-size: var(--text-2xl); }
|
||||
.type-sample__ui { font-weight: var(--weight-semibold); }
|
||||
.type-sample__numeric { font-size: var(--text-xl); font-variant-numeric: tabular-nums; }
|
||||
.status { display: inline-flex; align-items: center; gap: var(--space-2); border-radius: var(--radius-full); padding: var(--space-2) var(--space-3); font-size: var(--text-xs); font-weight: var(--weight-bold); }
|
||||
.status::before { width: 8px; height: 8px; border-radius: 50%; background: currentColor; content: ''; }
|
||||
.status--available { background: color-mix(in srgb, var(--color-success-500) 14%, transparent); color: var(--color-success-500); }
|
||||
.status--pending { background: color-mix(in srgb, var(--color-warning-500) 16%, transparent); color: var(--color-warning-500); }
|
||||
.status--sold { background: color-mix(in srgb, var(--color-danger-500) 14%, transparent); color: var(--color-danger-500); }
|
||||
.condition { border: var(--border-thin) solid var(--color-border-strong); border-radius: var(--radius-md); padding: var(--space-2) var(--space-3); color: var(--color-text-subtle); font-size: var(--text-sm); }
|
||||
.condition--selected { border-color: var(--color-interactive); box-shadow: var(--glow-selection); }
|
||||
.fixture-stage { display: grid; place-items: center; min-height: 180px; border-radius: var(--radius-lg); background: var(--color-stage); color: var(--color-text-muted); text-align: center; }
|
||||
.fixture-stage--image { min-height: 240px; background: linear-gradient(135deg, var(--color-stage), var(--color-surface-muted)); }
|
||||
.fixture-stage__glyph { font-family: var(--font-display); font-size: 3rem; font-weight: var(--weight-bold); }
|
||||
.fixture-card { display: grid; gap: var(--space-4); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-xl); padding: var(--space-4); background: var(--color-surface-raised); }
|
||||
.fixture-card h3 { font-size: var(--text-lg); }
|
||||
.fixture-card__meta { color: var(--color-text-muted); font-size: var(--text-sm); }
|
||||
.fixture-card__price { font-size: var(--text-lg); font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }
|
||||
.fixture-card__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
||||
.state-stack { display: grid; gap: var(--space-3); }
|
||||
.state { border-radius: var(--radius-lg); padding: var(--space-4); }
|
||||
.state--empty { border: var(--border-thin) dashed var(--color-border-strong); color: var(--color-text-muted); }
|
||||
.state--loading { background: var(--color-surface-muted); color: var(--color-text-muted); }
|
||||
.state--error { background: color-mix(in srgb, var(--color-danger-500) 10%, transparent); color: var(--color-danger-500); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.catalog-header { padding-top: var(--space-16); }
|
||||
.listing-grid, .showcase-grid { grid-template-columns: 1fr; }
|
||||
.showcase-header { display: grid; align-items: start; }
|
||||
.theme-controls { justify-content: start; }
|
||||
.product-facts { grid-template-columns: 1fr; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
.listing-card {
|
||||
display: grid;
|
||||
grid-template-columns: 60px 1fr;
|
||||
gap: 18px;
|
||||
gap: var(--space-4);
|
||||
min-height: 180px;
|
||||
padding: 20px;
|
||||
border: 1px solid #d6d3c7;
|
||||
background: #fffdf8;
|
||||
color: #22231f;
|
||||
border: var(--border-thin) solid var(--color-border);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: var(--space-6);
|
||||
background: var(--color-surface-raised);
|
||||
color: var(--color-text);
|
||||
box-shadow: var(--shadow-sm);
|
||||
text-decoration: none;
|
||||
transition: border-color 160ms ease, transform 160ms ease;
|
||||
transition: border-color var(--duration-standard) ease, transform var(--duration-standard) ease;
|
||||
}
|
||||
|
||||
.listing-card:hover { border-color: #386052; transform: translateY(-2px); }
|
||||
.listing-card:focus-visible { outline: 3px solid #a3482a; outline-offset: 4px; }
|
||||
.listing-card__mark { display: grid; place-items: center; aspect-ratio: 1; align-self: start; background: #d9e7df; color: #386052; font: 700 0.85rem Arial, sans-serif; }
|
||||
.listing-card__status { display: block; margin-top: 2px; color: #386052; font: 700 0.75rem/1.2 Arial, sans-serif; letter-spacing: 0.14em; text-transform: uppercase; }
|
||||
.listing-card__name { display: block; margin: 8px 0 16px; font-size: 1.25rem; }
|
||||
.listing-card__price { display: block; font: 700 1rem Arial, sans-serif; }
|
||||
.listing-card:hover { border-color: var(--color-interactive); transform: translateY(-2px); }
|
||||
.listing-card__mark { display: grid; place-items: center; aspect-ratio: 1; align-self: start; border-radius: var(--radius-md); background: var(--color-stage); color: var(--color-interactive); font: var(--weight-bold) var(--text-sm) var(--font-display); }
|
||||
.listing-card__status { display: block; margin-top: 2px; }
|
||||
.listing-card__name { display: block; margin: var(--space-2) 0 var(--space-4); font-family: var(--font-display); font-size: var(--text-lg); }
|
||||
.listing-card__price { display: block; font-size: var(--text-md); font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }
|
||||
@@ -9,4 +9,26 @@ export const available = {
|
||||
status: 'AVAILABLE',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const pending = {
|
||||
args: {
|
||||
listing: {
|
||||
id: 'listing-2',
|
||||
productName: 'Pikachu World Collection',
|
||||
askingPriceMinor: 8200,
|
||||
status: 'PENDING',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const sold = {
|
||||
args: {
|
||||
listing: {
|
||||
id: 'listing-3',
|
||||
productName: 'Gengar holo promo',
|
||||
askingPriceMinor: 4500,
|
||||
status: 'SOLD',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import DesignSystemShowcase from './DesignSystemShowcase.vue';
|
||||
|
||||
export default {
|
||||
title: 'Organisms/Design System Showcase',
|
||||
component: DesignSystemShowcase,
|
||||
};
|
||||
|
||||
export const AllFixtures = {};
|
||||
@@ -0,0 +1,136 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const selectedCondition = ref('Near Mint');
|
||||
const listings = [
|
||||
{ id: 'fixture-charizard', productName: 'Charizard ex #199/165', askingPriceMinor: 11500, status: 'AVAILABLE' },
|
||||
{ id: 'fixture-pikachu', productName: 'Pikachu World Collection', askingPriceMinor: 8200, status: 'PENDING' },
|
||||
{ id: 'fixture-gengar', productName: 'Gengar holo promo', askingPriceMinor: 4500, status: 'SOLD' },
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="showcase-page">
|
||||
<header class="showcase-header">
|
||||
<div>
|
||||
<p class="catalog-kicker">Design system fixture</p>
|
||||
<h1>Collectible data, made precise.</h1>
|
||||
<p>Representative visual fixtures for the catalog, detail views, controls, states, and themes. This component uses mock data only.</p>
|
||||
</div>
|
||||
<p class="fixture-label">Use the Storybook theme toolbar to inspect light and dark themes.</p>
|
||||
</header>
|
||||
|
||||
<section class="showcase-section" aria-labelledby="type-title">
|
||||
<h2 id="type-title">Typography and tokens</h2>
|
||||
<div class="showcase-grid">
|
||||
<article class="showcase-panel type-sample">
|
||||
<h3>Type scale</h3>
|
||||
<p class="type-sample__display">Space Grotesk display</p>
|
||||
<p class="type-sample__ui">Manrope interface and metadata</p>
|
||||
<p class="type-sample__numeric">$1,150.00 · 12 copies · 199/165</p>
|
||||
</article>
|
||||
<article class="showcase-panel">
|
||||
<h3>Primitive palette</h3>
|
||||
<div class="swatch-row">
|
||||
<span class="swatch"><span class="swatch__chip swatch__chip--cobalt" />Cobalt</span>
|
||||
<span class="swatch"><span class="swatch__chip swatch__chip--success" />Success</span>
|
||||
<span class="swatch"><span class="swatch__chip swatch__chip--warning" />Warning</span>
|
||||
<span class="swatch"><span class="swatch__chip swatch__chip--danger" />Danger</span>
|
||||
<span class="swatch"><span class="swatch__chip swatch__chip--info" />Info</span>
|
||||
</div>
|
||||
</article>
|
||||
<article class="showcase-panel">
|
||||
<h3>Controls</h3>
|
||||
<div class="control-row">
|
||||
<button class="button button--primary" type="button">Primary</button>
|
||||
<button class="button button--secondary" type="button">Secondary</button>
|
||||
<button class="button button--danger" type="button">Remove</button>
|
||||
<button class="button button--secondary" type="button" disabled>Disabled</button>
|
||||
</div>
|
||||
<label class="fixture-label" for="fixture-input">Persistent label</label>
|
||||
<input id="fixture-input" class="fixture-input" value="Charizard ex" />
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="showcase-section" aria-labelledby="state-title">
|
||||
<h2 id="state-title">Statuses, conditions, and feedback</h2>
|
||||
<div class="showcase-grid">
|
||||
<article class="showcase-panel">
|
||||
<h3>Status treatments</h3>
|
||||
<div class="status-row">
|
||||
<span class="status status--available">Available</span>
|
||||
<span class="status status--pending">Pending</span>
|
||||
<span class="status status--sold">Sold</span>
|
||||
</div>
|
||||
</article>
|
||||
<article class="showcase-panel">
|
||||
<h3>Condition selection</h3>
|
||||
<div class="control-row">
|
||||
<button v-for="condition in ['Near Mint', 'Lightly Played', 'Damaged']" :key="condition" class="condition" :class="{ 'condition--selected': selectedCondition === condition }" type="button" @click="selectedCondition = condition">
|
||||
{{ condition }}
|
||||
</button>
|
||||
</div>
|
||||
<p class="fixture-label">Selected: {{ selectedCondition }}</p>
|
||||
</article>
|
||||
<article class="showcase-panel state-stack">
|
||||
<h3>Feedback states</h3>
|
||||
<p class="state state--empty">No relevant matches found.</p>
|
||||
<p class="state state--loading" role="status">Loading collectible...</p>
|
||||
<p class="state state--error" role="alert">Unable to load marketplace values.</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="showcase-section" aria-labelledby="card-title">
|
||||
<h2 id="card-title">Collectible cards</h2>
|
||||
<div class="showcase-grid">
|
||||
<article v-for="listing in listings" :key="listing.id" class="fixture-card">
|
||||
<div class="fixture-stage" aria-label="Neutral collectible image stage"><span class="fixture-stage__glyph">PK</span></div>
|
||||
<div>
|
||||
<p class="fixture-card__meta">Trading card · Scarlet & Violet 151</p>
|
||||
<h3>{{ listing.productName }}</h3>
|
||||
<p class="fixture-card__meta">Condition: {{ listing.status === 'SOLD' ? 'Lightly Played' : 'Near Mint' }}</p>
|
||||
</div>
|
||||
<span :class="['status', `status--${listing.status.toLowerCase()}`]">{{ listing.status }}</span>
|
||||
<p v-if="listing.status !== 'SOLD'" class="fixture-card__price">${{ (listing.askingPriceMinor / 100).toFixed(2) }}</p>
|
||||
<p v-else class="fixture-card__meta">Sold history retained</p>
|
||||
<div class="fixture-card__actions">
|
||||
<button class="button button--primary" type="button">View collectible</button>
|
||||
<button class="button button--secondary" type="button">Compare</button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="showcase-section" aria-labelledby="detail-title">
|
||||
<h2 id="detail-title">Detail and gallery treatment</h2>
|
||||
<div class="showcase-grid">
|
||||
<article class="showcase-panel">
|
||||
<h3>Collectible detail</h3>
|
||||
<div class="fixture-stage fixture-stage--image" aria-label="Neutral image gallery stage"><span class="fixture-stage__glyph">01</span></div>
|
||||
<p class="catalog-kicker">Identity</p>
|
||||
<h3>Charizard ex #199/165</h3>
|
||||
<p class="fixture-card__meta">Scarlet & Violet 151 · Trading card · Near Mint</p>
|
||||
</article>
|
||||
<article class="showcase-panel">
|
||||
<h3>Listing detail</h3>
|
||||
<p class="catalog-kicker">Available</p>
|
||||
<h3>Asking price</h3>
|
||||
<p class="fixture-card__price">$115.00</p>
|
||||
<dl class="product-facts">
|
||||
<div><dt>Condition</dt><dd>Near Mint</dd></div>
|
||||
<div><dt>Market context</dt><dd>$120 reference</dd></div>
|
||||
<div><dt>Updated</dt><dd>Today</dd></div>
|
||||
<div><dt>Seller note</dt><dd>Clean front and back</dd></div>
|
||||
</dl>
|
||||
</article>
|
||||
<article class="showcase-panel">
|
||||
<h3>Image treatment</h3>
|
||||
<div class="fixture-stage fixture-stage--image" aria-label="Museum-style contained collectible image stage"><span class="fixture-stage__glyph">GALLERY</span></div>
|
||||
<p class="fixture-card__meta">Contained, neutral, aspect-ratio preserving, and free of cobalt image backgrounds.</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
const preference = ref('system');
|
||||
|
||||
function applyTheme() {
|
||||
const resolved = preference.value === 'system'
|
||||
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
|
||||
: preference.value;
|
||||
document.documentElement.dataset.theme = resolved;
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
function setTheme(nextTheme) {
|
||||
preference.value = nextTheme;
|
||||
localStorage.setItem('pokemon-inventory-theme', nextTheme);
|
||||
applyTheme();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
preference.value = localStorage.getItem('pokemon-inventory-theme') ?? 'system';
|
||||
applyTheme();
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', applyTheme);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', applyTheme);
|
||||
});
|
||||
|
||||
return { preference, setTheme };
|
||||
}
|
||||
Reference in New Issue
Block a user