feat(ui): adopt Tailwind v4 design system and archive change
- Add tailwindcss v4 + @tailwindcss/vite to mall, shop-admin, admin - Add @vmall/shared/theme.css tokens with html[data-accent] presets - Add @vmall/ui kit (VBtn/VBadge/VField/VInput/VCard/VPanel/VTable/VPage, VAccentSwatch, useAccent) as a Nuxt module - Convert all three apps to kit + utilities; delete ui.css/mall.css and every <style scoped>; consoles get accent presets, mall locked to red - Fix VCard boolean prop default (padding) and PDP/store stale useAsyncData keys on param navigation - Archive adopt-tailwind-design-system; new frontend-ui capability spec
This commit is contained in:
@@ -7,6 +7,6 @@
|
||||
".": "./src/index.ts",
|
||||
"./locales": "./src/locales/index.ts",
|
||||
"./types": "./src/types.ts",
|
||||
"./ui.css": "./src/ui.css"
|
||||
"./theme.css": "./src/theme.css"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Kit components live outside each app's Vite root; scan them for utilities. */
|
||||
@source "../../ui";
|
||||
|
||||
/*
|
||||
* Accent presets: utilities reference var(--primary*) at runtime via
|
||||
* `@theme inline`, so html[data-accent] swaps the palette without a rebuild.
|
||||
*/
|
||||
@theme inline {
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-hover: var(--primary-hover);
|
||||
--color-primary-soft: var(--primary-soft);
|
||||
}
|
||||
|
||||
@theme {
|
||||
--color-bg: #f5f5f5;
|
||||
--color-surface: #ffffff;
|
||||
--color-border: #e2e5ea;
|
||||
--color-text: #1c2330;
|
||||
--color-muted: #66707f;
|
||||
--color-danger: #d64545;
|
||||
--color-success: #1f9d63;
|
||||
--color-warning: #c77d0a;
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
|
||||
/* Spec-locked mall sizes: max-w-mall, h-hero */
|
||||
--container-mall: 1200px;
|
||||
--spacing-hero: 450px;
|
||||
}
|
||||
|
||||
:root,
|
||||
html[data-accent="red"] {
|
||||
--primary: #ca151e;
|
||||
--primary-hover: #a81219;
|
||||
--primary-soft: #fdecec;
|
||||
}
|
||||
|
||||
html[data-accent="blue"] {
|
||||
--primary: #2f6fed;
|
||||
--primary-hover: #2058c8;
|
||||
--primary-soft: #e7effd;
|
||||
}
|
||||
|
||||
html[data-accent="teal"] {
|
||||
--primary: #0f8a72;
|
||||
--primary-hover: #0b6b59;
|
||||
--primary-soft: #e0f4ef;
|
||||
}
|
||||
|
||||
html[data-accent="violet"] {
|
||||
--primary: #7c3aed;
|
||||
--primary-hover: #6425c9;
|
||||
--primary-soft: #f0e9fd;
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
:root {
|
||||
--bg: #f6f7f9;
|
||||
--surface: #ffffff;
|
||||
--border: #e2e5ea;
|
||||
--text: #1c2330;
|
||||
--muted: #66707f;
|
||||
--primary: #2f6fed;
|
||||
--primary-dark: #2058c8;
|
||||
--danger: #d64545;
|
||||
--success: #1f9d63;
|
||||
--warning: #c77d0a;
|
||||
--radius: 8px;
|
||||
--shadow: 0 1px 3px rgba(16, 24, 40, 0.08);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Inter", -apple-system, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
a { color: var(--primary); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
.container { max-width: 1120px; margin: 0 auto; padding: 0 16px; }
|
||||
|
||||
.topnav {
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
.topnav-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
height: 56px;
|
||||
}
|
||||
.topnav .brand { font-weight: 700; font-size: 17px; color: var(--text); }
|
||||
.topnav nav { display: flex; gap: 14px; flex: 1; }
|
||||
.topnav nav a { color: var(--muted); font-weight: 500; }
|
||||
.topnav nav a.router-link-active { color: var(--primary); }
|
||||
.topnav .spacer { flex: 1; }
|
||||
|
||||
select, input, textarea {
|
||||
font: inherit;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
width: 100%;
|
||||
}
|
||||
select { width: auto; }
|
||||
input:focus, textarea:focus, select:focus { outline: 2px solid rgba(47, 111, 237, 0.3); border-color: var(--primary); }
|
||||
label { display: block; font-weight: 500; margin-bottom: 4px; color: var(--text); }
|
||||
.field { margin-bottom: 14px; }
|
||||
.field-row { display: flex; gap: 12px; }
|
||||
.field-row .field { flex: 1; }
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 14px;
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
.btn:hover { background: #f0f2f5; }
|
||||
.btn.primary { background: var(--primary); border-color: var(--primary); color: #fff; }
|
||||
.btn.primary:hover { background: var(--primary-dark); }
|
||||
.btn.danger { color: var(--danger); border-color: var(--danger); }
|
||||
.btn.danger:hover { background: #fdf0f0; }
|
||||
.btn.sm { padding: 4px 10px; font-size: 13px; }
|
||||
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 20px;
|
||||
}
|
||||
.card + .card { margin-top: 16px; }
|
||||
|
||||
.page { padding: 24px 0 48px; }
|
||||
.page-title { font-size: 20px; font-weight: 700; margin: 0 0 16px; }
|
||||
.page-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
|
||||
.page-head .page-title { margin: 0; }
|
||||
|
||||
table.table { width: 100%; border-collapse: collapse; background: var(--surface); }
|
||||
.table th, .table td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); }
|
||||
.table th { color: var(--muted); font-weight: 600; font-size: 13px; }
|
||||
.table tr:last-child td { border-bottom: none; }
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
background: #eef1f5;
|
||||
color: var(--muted);
|
||||
}
|
||||
.badge.green { background: #e4f6ec; color: var(--success); }
|
||||
.badge.blue { background: #e7effd; color: var(--primary); }
|
||||
.badge.orange { background: #fdf1e0; color: var(--warning); }
|
||||
.badge.red { background: #fbe6e6; color: var(--danger); }
|
||||
|
||||
.grid { display: grid; gap: 16px; }
|
||||
.grid.products { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
|
||||
.product-card { overflow: hidden; padding: 0; display: flex; flex-direction: column; }
|
||||
.product-card img { width: 100%; aspect-ratio: 1; object-fit: cover; background: #eef1f5; }
|
||||
.product-card .body { padding: 12px; }
|
||||
.product-card .name { font-weight: 600; margin-bottom: 4px; }
|
||||
.product-card .price { color: var(--primary); font-weight: 700; }
|
||||
|
||||
.muted { color: var(--muted); }
|
||||
.row { display: flex; align-items: center; gap: 12px; }
|
||||
.between { justify-content: space-between; }
|
||||
.mt { margin-top: 16px; }
|
||||
.mb { margin-bottom: 16px; }
|
||||
.error-text { color: var(--danger); margin: 8px 0; }
|
||||
|
||||
.form-narrow { max-width: 420px; margin: 40px auto; }
|
||||
|
||||
.auth-layout { display: flex; min-height: 100vh; }
|
||||
.auth-layout aside {
|
||||
width: 220px;
|
||||
background: var(--surface);
|
||||
border-right: 1px solid var(--border);
|
||||
padding: 16px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.auth-layout aside .brand { font-weight: 700; font-size: 16px; padding: 8px 10px 16px; }
|
||||
.auth-layout aside a {
|
||||
display: block;
|
||||
padding: 8px 10px;
|
||||
border-radius: var(--radius);
|
||||
color: var(--muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
.auth-layout aside a:hover { background: #f0f2f5; text-decoration: none; }
|
||||
.auth-layout aside a.router-link-active { background: #e7effd; color: var(--primary); }
|
||||
.auth-layout main { flex: 1; padding: 24px; overflow: auto; }
|
||||
.auth-layout .main-head { display: flex; justify-content: flex-end; gap: 10px; margin-bottom: 12px; }
|
||||
Reference in New Issue
Block a user