more progress

This commit is contained in:
Paul Bakaus
2025-12-14 13:55:29 -08:00
parent 5b6aafc85e
commit 99f1091f20
39 changed files with 1648 additions and 2166 deletions
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"css": {
"parser": {
"tailwindDirectives": true
}
}
}
+44
View File
@@ -0,0 +1,44 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "vibe-design-plugins",
"dependencies": {
"tailwindcss": "^4.1.18",
},
"devDependencies": {
"bun-plugin-tailwind": "^0.1.2",
},
},
},
"packages": {
"@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2Ie4jDGvNGuPSD+pyyBKL8dJmX+bZfDNYEalwgROImVtwB1XYAatJK20dMaRlPA7jOhjvS9Io+4IZAJu7Js0AA=="],
"@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-4/BJojT8hk5g6Gecjn5yI7y96/+9Mtzsvdp9+2dcy9sTMdlV7jBvDzswqyJPZyQqw0F3HV3Vu9XuMubZwKd9lA=="],
"@oven/bun-darwin-x64-baseline": ["@oven/bun-darwin-x64-baseline@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZYxzIOCDqylTMsnWYERjKMMuK2b4an4qbloBmUZTwLHmVzos00yrhtpitZhJBgH6yB/l4Q5eoJ2W98UKtFFeiQ=="],
"@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-8DUIlanftMdFxLGq2FxwKwfrp8O4ZofF/8Oc6lxCyEFmg2hixbHhL04+fPfJIi5D4hZloynxZdwTeDbGv/Kc4A=="],
"@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-6UtmM4wXgRKz+gnLZEfddfsuBSVQpJr09K12e5pbdnLzeWgXYlBT5FG8S7SVn1t6cbgBMnigEsFjWwfTuMNoCw=="],
"@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-03iSDMqdrmIFAsvsRptq+A7EGNjkg20dNzPnqxAlXHk5rc1PeIRWIP0eIn0i3nI6mmdj33mimf9AGr0+d0lKMg=="],
"@oven/bun-linux-x64-baseline": ["@oven/bun-linux-x64-baseline@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-ZMGPbFPqmG/VYJv61D+Y1V7T23jPK57vYl7yYLakmkTRjG6vcJ0Akhb2qR1iW94rHvfEBjeuVDAZBp8Qp9oyWA=="],
"@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-xUXPuJHndGhk4K3Cx1FgTyTgDZOn+ki3eWvdXYqKdfi0EaNA9KpUq+/vUtpJbZRjzpHs9L+OJcdDILq5H0LX4g=="],
"@oven/bun-linux-x64-musl-baseline": ["@oven/bun-linux-x64-musl-baseline@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-qsGSSlNsxiX8lAayK2uYCfMLtqu776F0nn7qoyzg9Ti7mElM3woNh7RtGClTwQ6qsp5/UvgqT9g4pLaDHmqJFg=="],
"@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nswsuN6+HZPim6x4tFpDFpMa/qpTKfywbGvCkzxwrbJO9MtpuW/54NA1nFbHhpV14OLU0xuxyBj2PK4FHq4MlA=="],
"@oven/bun-windows-x64-baseline": ["@oven/bun-windows-x64-baseline@1.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-ZQiSDFfSUdOrPTiL2GvkxlC/kMED4fsJwdZnwJK6S9ylXnk9xY/9ZXfe1615SFLQl2LsVRzJAtjQLeM0BifIKQ=="],
"bun": ["bun@1.3.4", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.3.4", "@oven/bun-darwin-x64": "1.3.4", "@oven/bun-darwin-x64-baseline": "1.3.4", "@oven/bun-linux-aarch64": "1.3.4", "@oven/bun-linux-aarch64-musl": "1.3.4", "@oven/bun-linux-x64": "1.3.4", "@oven/bun-linux-x64-baseline": "1.3.4", "@oven/bun-linux-x64-musl": "1.3.4", "@oven/bun-linux-x64-musl-baseline": "1.3.4", "@oven/bun-windows-x64": "1.3.4", "@oven/bun-windows-x64-baseline": "1.3.4" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-xV6KgD5ImquuKsoghzbWmYzeCXmmSgN6yJGz444hri2W+NGKNRFUNrEhy9+/rRXbvNA2qF0K0jAwqFNy1/GhBg=="],
"bun-plugin-tailwind": ["bun-plugin-tailwind@0.1.2", "", { "peerDependencies": { "bun": ">=1.0.0" } }, "sha512-41jNC1tZRSK3s1o7pTNrLuQG8kL/0vR/JgiTmZAJ1eHwe0w5j6HFPKeqEk0WAD13jfrUC7+ULuewFBBCoADPpg=="],
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
}
}
+3
View File
@@ -0,0 +1,3 @@
# Configure Tailwind CSS plugin for Bun's static server and bundler
[serve.static]
plugins = ["bun-plugin-tailwind"]
+7 -2
View File
@@ -21,6 +21,11 @@
"ux"
],
"author": "Paul Bakaus",
"license": "SEE LICENSE FILE"
"license": "SEE LICENSE FILE",
"devDependencies": {
"bun-plugin-tailwind": "^0.1.2"
},
"dependencies": {
"tailwindcss": "^4.1.18"
}
}
-81
View File
@@ -1,81 +0,0 @@
/* Animations & Keyframes */
/* Background Atmosphere */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(circle at 20% 30%, var(--c-accent-dim) 0%, transparent 50%),
radial-gradient(circle at 80% 70%, var(--c-accent-dim) 0%, transparent 50%);
opacity: 0;
z-index: -1;
animation: fadeInBg var(--duration-slowest) var(--ease-out) 0.3s forwards;
pointer-events: none;
}
/* Keyframe Definitions */
@keyframes fadeInBg {
to {
opacity: 1;
}
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-40px);
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(40px);
}
}
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes expandLine {
from {
width: 0;
}
}
@keyframes drawCircle {
from {
opacity: 0;
scale: 0;
}
to {
opacity: 1;
scale: 1;
}
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
/* Animation Utilities */
[data-animate] {
opacity: 0;
transform: translateY(20px);
&.animated {
animation: fadeInUp var(--duration-slow) var(--ease-out) both;
}
}
-69
View File
@@ -1,69 +0,0 @@
/* Button Components */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.875rem 2rem;
font-family: var(--font-body);
font-size: 0.9375rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
border: none;
cursor: pointer;
transition: all var(--duration-base) var(--ease-out);
position: relative;
overflow: hidden;
text-decoration: none;
}
/* Primary Button */
.btn-primary {
background: var(--c-accent);
color: var(--c-paper);
width: 100%;
position: relative;
z-index: 1;
&::before {
content: '';
position: absolute;
inset: 0;
background: var(--c-ink);
transform: translateY(100%);
transition: transform var(--duration-base) var(--ease-out);
z-index: -1;
}
&:hover::before {
transform: translateY(0);
}
}
/* Secondary Button */
.btn-secondary {
background: transparent;
color: var(--c-ink);
border: 1px solid var(--c-ink);
&:hover {
background: var(--c-ink);
color: var(--c-paper);
}
}
/* Small Button Variant */
.btn-small {
padding: 0.5rem 1rem;
font-size: 0.8125rem;
border: 1px solid var(--c-mist);
background: transparent;
color: var(--c-ink);
&:hover {
border-color: var(--c-accent);
color: var(--c-accent);
}
}
-112
View File
@@ -1,112 +0,0 @@
/* Card Components */
/* Download Bundles Section */
.download-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: var(--s-md);
@media (max-width: 640px) {
grid-template-columns: 1fr;
}
}
.download-card {
opacity: 0;
transform: translateY(20px);
&.animated {
animation: fadeInUp var(--duration-slow) var(--ease-out) both;
}
}
.card-inner {
height: 100%;
padding: var(--s-lg);
background: var(--c-paper);
border: 1px solid var(--c-mist);
display: flex;
flex-direction: column;
gap: var(--s-md);
transition: all var(--duration-base) var(--ease-out);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: var(--c-accent);
transform: translateX(-100%);
transition: transform 0.4s var(--ease-out);
}
&:hover {
border-color: var(--c-accent);
transform: translateY(-4px);
&::before {
transform: translateX(0);
}
}
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--s-md);
h3 {
font-size: 1.375rem;
font-weight: 500;
margin: 0;
}
}
.badge {
font-size: 0.625rem;
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 0.25em 0.625em;
border: 1px solid var(--c-accent);
color: var(--c-accent);
border-radius: 2px;
font-weight: 600;
white-space: nowrap;
}
.card-inner p {
color: var(--c-ash);
font-size: 0.9375rem;
line-height: 1.6;
flex-grow: 1;
margin: 0;
}
/* Contribute Section */
.contribute {
padding: var(--s-2xl) 0;
text-align: center;
}
.contribute-content {
max-width: 480px;
margin: 0 auto;
}
.contribute h2 {
font-size: 2rem;
font-weight: 600;
margin-bottom: var(--s-sm);
}
.contribute p {
font-size: 1.125rem;
color: var(--c-ash);
margin-bottom: var(--s-lg);
line-height: 1.6;
}
-65
View File
@@ -1,65 +0,0 @@
/* Footer & Contribute Section */
.contribute {
padding: var(--s-2xl) 0;
border-top: 1px solid var(--c-mist);
text-align: center;
}
.contribute-content {
max-width: var(--content-width);
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: var(--s-md);
h2 {
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 600;
}
p {
font-size: 1.125rem;
color: var(--c-ash);
max-width: 42ch;
}
}
footer {
border-top: 1px solid var(--c-mist);
padding: var(--s-2xl) var(--s-lg);
background: var(--c-paper);
}
.footer-content {
max-width: var(--max-width);
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.875rem;
color: var(--c-ash);
a {
color: var(--c-ink);
text-decoration: underline;
text-underline-offset: 0.2em;
}
@media (max-width: 768px) {
flex-direction: column;
gap: var(--s-sm);
text-align: center;
}
}
/* Print Styles */
@media print {
body::before,
.hero-decoration,
.btn {
display: none;
}
}
+72 -72
View File
@@ -10,11 +10,11 @@
}
.skill-nav-item {
padding: var(--s-sm) var(--s-md);
padding: var(--spacing-sm) var(--spacing-md);
background: transparent;
border: none;
border-left: 2px solid transparent;
color: var(--c-ash);
color: var(--color-ash);
font-family: var(--font-body);
font-size: 0.9375rem;
font-weight: 400;
@@ -26,14 +26,14 @@
}
.skill-nav-item:hover {
color: var(--c-text);
background: color-mix(in oklch, var(--c-paper) 85%, var(--c-accent));
color: var(--color-text);
background: color-mix(in oklch, var(--color-paper) 85%, var(--color-accent));
}
.skill-nav-item.active {
color: var(--c-accent);
border-left-color: var(--c-accent);
background: color-mix(in oklch, var(--c-accent) 5%, var(--c-paper));
color: var(--color-accent);
border-left-color: var(--color-accent);
background: color-mix(in oklch, var(--color-accent) 5%, var(--color-paper));
font-weight: 500;
}
@@ -41,17 +41,17 @@
.skills-nav {
flex-direction: row;
flex-wrap: wrap;
gap: var(--s-xs);
gap: var(--spacing-xs);
}
.skill-nav-item {
border-left: none;
border-bottom: 2px solid transparent;
padding: var(--s-xs) var(--s-md);
padding: var(--spacing-xs) var(--spacing-md);
}
.skill-nav-item.active {
border-bottom-color: var(--c-accent);
border-bottom-color: var(--color-accent);
}
}
@@ -59,7 +59,7 @@
.skills-showcase {
display: grid;
grid-template-columns: 1.2fr 1fr;
gap: var(--s-lg);
gap: var(--spacing-lg);
align-items: start;
@media (max-width: 1100px) {
@@ -86,7 +86,7 @@
}
.skill-header {
margin-bottom: var(--s-md);
margin-bottom: var(--spacing-md);
}
.skill-header h3 {
@@ -94,17 +94,17 @@
font-size: 1.75rem;
font-weight: 300;
font-style: italic;
margin: 0 0 var(--s-xs) 0;
color: var(--c-text);
margin: 0 0 var(--spacing-xs) 0;
color: var(--color-text);
letter-spacing: -0.02em;
line-height: 1.1;
}
.skill-header .meta {
display: flex;
gap: var(--s-sm);
gap: var(--spacing-sm);
align-items: center;
color: var(--c-ash);
color: var(--color-ash);
font-size: 0.8125rem;
font-weight: 400;
letter-spacing: 0.03em;
@@ -112,7 +112,7 @@
}
.skill-header .meta a {
color: var(--c-accent);
color: var(--color-accent);
text-decoration: none;
transition: opacity 0.2s ease;
}
@@ -124,22 +124,22 @@
.skill-description {
font-size: 0.9375rem;
line-height: 1.6;
margin-bottom: var(--s-md);
color: var(--c-charcoal);
margin-bottom: var(--spacing-md);
color: var(--color-charcoal);
max-width: 50ch;
}
/* Skill Sections (Focus Areas, Relationships) */
.skill-section {
margin-bottom: var(--s-lg);
margin-bottom: var(--spacing-lg);
}
.skill-section h4 {
font-family: var(--font-body);
font-size: 0.75rem;
font-weight: 600;
margin: 0 0 var(--s-sm) 0;
color: var(--c-ash);
margin: 0 0 var(--spacing-sm) 0;
color: var(--color-ash);
text-transform: uppercase;
letter-spacing: 0.1em;
}
@@ -148,26 +148,26 @@
.focus-areas-compact {
display: flex;
flex-wrap: wrap;
gap: var(--s-xs);
gap: var(--spacing-xs);
}
.focus-area-tag {
display: inline-flex;
align-items: center;
gap: var(--s-xs);
padding: var(--s-xs) var(--s-sm);
background: var(--c-paper);
gap: var(--spacing-xs);
padding: var(--spacing-xs) var(--spacing-sm);
background: var(--color-paper);
border-radius: 3px;
font-size: 0.8125rem;
}
.focus-area-tag strong {
color: var(--c-text);
color: var(--color-text);
font-weight: 500;
}
.focus-area-tag span {
color: var(--c-ash);
color: var(--color-ash);
font-size: 0.75rem;
}
@@ -175,15 +175,15 @@
.relationship-tags {
display: flex;
flex-wrap: wrap;
gap: var(--s-xs);
gap: var(--spacing-xs);
}
.relationship-tag {
display: inline-block;
padding: var(--s-xs) var(--s-sm);
background: var(--c-paper);
color: var(--c-text);
border: 1px solid color-mix(in oklch, var(--c-accent) 30%, transparent);
padding: var(--spacing-xs) var(--spacing-sm);
background: var(--color-paper);
color: var(--color-text);
border: 1px solid color-mix(in oklch, var(--color-accent) 30%, transparent);
font-size: 0.8125rem;
font-weight: 400;
border-radius: 2px;
@@ -192,36 +192,36 @@
}
.relationship-tag:hover {
border-color: var(--c-accent);
color: var(--c-accent);
background: color-mix(in oklch, var(--c-accent) 5%, var(--c-paper));
border-color: var(--color-accent);
color: var(--color-accent);
background: color-mix(in oklch, var(--color-accent) 5%, var(--color-paper));
}
/* Skill Downloads (Icons) */
.skill-downloads {
display: flex;
align-items: center;
gap: var(--s-sm);
padding-top: var(--s-md);
margin-top: var(--s-md);
border-top: 1px solid var(--c-mist);
gap: var(--spacing-sm);
padding-top: var(--spacing-md);
margin-top: var(--spacing-md);
border-top: 1px solid var(--color-mist);
}
.downloads-label {
font-size: 0.75rem;
color: var(--c-ash);
margin-right: var(--s-xs);
color: var(--color-ash);
margin-right: var(--spacing-xs);
}
.download-icon {
display: flex;
align-items: center;
gap: var(--s-xs);
gap: var(--spacing-xs);
padding: 6px 10px;
border-radius: 4px;
background: transparent;
border: 1px solid var(--c-mist);
color: var(--c-ash);
border: 1px solid var(--color-mist);
color: var(--color-ash);
text-decoration: none;
font-size: 0.75rem;
transition: all 0.15s ease;
@@ -234,16 +234,16 @@
}
.download-icon:hover {
color: var(--c-accent);
border-color: var(--c-accent);
background: color-mix(in oklch, var(--c-accent) 5%, transparent);
color: var(--color-accent);
border-color: var(--color-accent);
background: color-mix(in oklch, var(--color-accent) 5%, transparent);
}
/* Command Detail Panel - mirrors skills showcase */
.command-detail-panel {
display: grid;
grid-template-columns: 1.2fr 1fr;
gap: var(--s-lg);
gap: var(--spacing-lg);
align-items: start;
@media (max-width: 1100px) {
@@ -263,15 +263,15 @@
font-family: 'Space Grotesk', monospace;
font-size: 2rem;
font-weight: 500;
margin: 0 0 var(--s-xs) 0;
color: var(--c-text);
margin: 0 0 var(--spacing-xs) 0;
color: var(--color-text);
}
.command-header .meta {
display: flex;
gap: var(--s-sm);
gap: var(--spacing-sm);
align-items: center;
color: var(--c-ash);
color: var(--color-ash);
font-size: 0.8125rem;
text-transform: uppercase;
letter-spacing: 0.03em;
@@ -280,24 +280,24 @@
.command-description {
font-size: 1rem;
line-height: 1.7;
color: var(--c-charcoal);
margin-bottom: var(--s-lg);
color: var(--color-charcoal);
margin-bottom: var(--spacing-lg);
}
/* Process Steps - Compact */
.process-steps-compact {
display: flex;
flex-wrap: wrap;
gap: var(--s-sm);
gap: var(--spacing-sm);
counter-reset: step;
}
.process-step-compact {
display: flex;
align-items: center;
gap: var(--s-xs);
padding: var(--s-xs) var(--s-sm);
background: var(--c-paper);
gap: var(--spacing-xs);
padding: var(--spacing-xs) var(--spacing-sm);
background: var(--color-paper);
border-radius: 3px;
font-size: 0.8125rem;
}
@@ -310,8 +310,8 @@
justify-content: center;
width: 20px;
height: 20px;
background: var(--c-accent);
color: var(--c-paper);
background: var(--color-accent);
color: var(--color-paper);
border-radius: 50%;
font-size: 0.6875rem;
font-weight: 600;
@@ -331,10 +331,10 @@
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 2px 5px;
background: var(--c-mist);
color: var(--c-ash);
background: var(--color-mist);
color: var(--color-ash);
border-radius: 2px;
margin-left: var(--s-xs);
margin-left: var(--spacing-xs);
vertical-align: middle;
}
@@ -344,13 +344,13 @@
align-items: center;
justify-content: center;
text-align: center;
padding: var(--s-2xl);
padding: var(--spacing-2xl);
min-height: 300px;
}
.coming-soon-icon {
color: var(--c-mist);
margin-bottom: var(--s-lg);
color: var(--color-mist);
margin-bottom: var(--spacing-lg);
}
.coming-soon-showcase h3 {
@@ -358,20 +358,20 @@
font-size: 1.75rem;
font-weight: 300;
font-style: italic;
margin: 0 0 var(--s-sm) 0;
color: var(--c-text);
margin: 0 0 var(--spacing-sm) 0;
color: var(--color-text);
}
.coming-soon-text {
font-size: 1rem;
color: var(--c-accent);
color: var(--color-accent);
font-weight: 500;
margin-bottom: var(--s-md);
margin-bottom: var(--spacing-md);
}
.coming-soon-showcase .skill-description {
max-width: 50ch;
color: var(--c-ash);
color: var(--color-ash);
font-size: 0.9375rem;
line-height: 1.6;
}
-119
View File
@@ -1,119 +0,0 @@
/* Hero Section */
.hero {
min-height: 85vh;
display: flex;
align-items: center;
position: relative;
padding: var(--s-2xl) var(--s-lg);
overflow: hidden;
}
.hero-grid {
max-width: var(--max-width);
margin: 0 auto;
display: grid;
grid-template-columns: 1fr auto;
gap: var(--s-2xl);
align-items: center;
width: 100%;
position: relative;
}
.hero-content {
animation: slideInLeft var(--duration-slower) var(--ease-out) 0.1s both;
}
/* Title Styling */
.title {
font-size: clamp(3rem, 8vw, 7rem);
font-weight: 300;
line-height: 0.9;
margin-bottom: var(--s-lg);
display: flex;
flex-direction: column;
gap: -0.1em;
}
.title-main {
font-weight: 400;
letter-spacing: -0.03em;
}
.title-sub {
font-weight: 300;
font-style: italic;
color: var(--c-accent);
letter-spacing: -0.02em;
}
/* Tagline & Providers */
.tagline {
font-size: clamp(1.125rem, 2vw, 1.5rem);
line-height: 1.4;
color: var(--c-charcoal);
margin-bottom: var(--s-md);
max-width: 32ch;
}
.providers {
font-size: 0.875rem;
color: var(--c-ash);
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 500;
}
/* Decorative Elements */
.hero-decoration {
width: 240px;
height: 240px;
position: relative;
animation: slideInRight var(--duration-slower) var(--ease-out) 0.3s both;
}
.deco-line {
position: absolute;
width: 160px;
height: 2px;
background: var(--c-accent);
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-25deg);
animation: expandLine 1s var(--ease-out) 0.6s both;
}
.deco-circle {
position: absolute;
width: 120px;
height: 120px;
border: 2px solid var(--c-accent);
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation: drawCircle 1s var(--ease-out) 0.8s both;
opacity: 0.3;
}
/* Hero Responsive */
@media (max-width: 768px) {
.hero {
min-height: 70vh;
padding: var(--s-xl) var(--s-md);
}
.hero-grid {
grid-template-columns: 1fr;
gap: var(--s-xl);
}
.hero-decoration {
display: none;
}
.title {
font-size: clamp(2.5rem, 12vw, 4rem);
}
}
-64
View File
@@ -1,64 +0,0 @@
/* Intro Section */
.intro {
padding: var(--s-2xl) 0;
}
.intro-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: var(--s-2xl);
align-items: start;
@media (max-width: 768px) {
grid-template-columns: 1fr;
gap: var(--s-xl);
}
}
.intro-content h2 {
font-size: clamp(1.75rem, 3vw, 2.5rem);
font-weight: 400;
line-height: 1.3;
color: var(--c-charcoal);
}
/* Meta Statistics */
.intro-meta {
display: flex;
flex-direction: column;
gap: var(--s-lg);
padding-top: var(--s-md);
@media (max-width: 768px) {
flex-direction: row;
justify-content: space-between;
}
}
.meta-item {
display: flex;
flex-direction: column;
gap: var(--s-xs);
}
.meta-number {
font-family: var(--font-display);
font-size: 3rem;
font-weight: 600;
line-height: 1;
color: var(--c-accent);
@media (max-width: 480px) {
font-size: 2.5rem;
}
}
.meta-label {
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-ash);
font-weight: 500;
}
-160
View File
@@ -1,160 +0,0 @@
/* Layout Components */
.container {
max-width: var(--max-width);
margin: 0 auto;
padding: 0 var(--s-lg);
@media (max-width: 640px) {
padding: 0 var(--s-md);
}
}
/* Section Styling */
section {
border-top: 1px solid var(--c-mist);
}
.section-title {
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 600;
margin-bottom: 0;
padding-bottom: var(--s-sm);
position: relative;
display: block;
&::after {
content: '';
position: absolute;
bottom: -0.25em;
left: 0;
width: 80px;
height: 2px;
background: var(--c-accent);
}
@media (max-width: 968px) {
margin-bottom: var(--s-lg);
}
}
.section-subtitle {
font-size: 1.125rem;
color: var(--c-ash);
margin-top: var(--s-md);
max-width: 50ch;
}
/* Section Headers */
.section-header-sticky {
margin-bottom: var(--s-lg);
.section-title {
margin-bottom: var(--s-sm);
}
.section-subtitle {
margin-top: var(--s-sm);
}
}
/* Skills Section Layout */
.skills-section {
padding: var(--s-xl) 0;
}
.skills-layout {
display: grid;
grid-template-columns: 180px 1fr;
gap: var(--s-lg);
align-items: start;
@media (max-width: 968px) {
grid-template-columns: 1fr;
gap: var(--s-md);
}
}
.skills-nav {
display: flex;
flex-direction: column;
gap: 2px;
@media (max-width: 968px) {
flex-direction: row;
flex-wrap: wrap;
gap: var(--s-xs);
}
}
.skills-showcase {
/* Contain the showcase to prevent excessive scrolling */
}
/* Commands Section Layout - mirrors skills layout */
.commands-section {
padding: var(--s-xl) 0;
}
.commands-layout {
display: grid;
grid-template-columns: 180px 1fr;
gap: var(--s-lg);
align-items: start;
@media (max-width: 968px) {
grid-template-columns: 1fr;
gap: var(--s-md);
}
}
.commands-nav {
display: flex;
flex-direction: column;
gap: var(--s-sm);
@media (max-width: 968px) {
flex-direction: row;
flex-wrap: wrap;
gap: var(--s-xs);
}
}
.commands-showcase {
/* Contains demo + info */
}
/* Downloads Section */
.downloads-section {
padding: var(--s-2xl) 0;
}
.downloads-header {
margin-bottom: var(--s-xl);
max-width: 600px;
}
.downloads-header .section-title {
margin-bottom: var(--s-md);
}
.downloads-lead {
font-size: 1.125rem;
line-height: 1.6;
color: var(--c-ash);
}
/* Grid Utilities */
.item-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: var(--s-md);
}
/* Loading State */
.loading {
padding: var(--s-xl);
text-align: center;
color: var(--c-ash);
font-style: italic;
}
+370 -22
View File
@@ -1,29 +1,377 @@
/*
* impeccable.style — Modular CSS Architecture
* impeccable.style — Tailwind CSS v4 + Custom Components
*
* Uses Bun's CSS bundler for:
* - CSS nesting (no preprocessor needed)
* - OKLCH colors with automatic fallbacks
* - Automatic bundling via @import
*
* Structure:
* - tokens.css: Design system (colors, spacing, typography)
* - reset.css: Modern baseline
* - animations.css: Keyframes & utilities
* - layout.css: Grid & section primitives
* - hero/intro/buttons/cards/footer.css: Component styles
* Architecture:
* - Tailwind v4 with custom theme tokens
* - Custom components for complex UI patterns
* - Minimal CSS for what Tailwind can't express
*/
@import "./tokens.css";
@import "./reset.css";
@import "./animations.css";
@import "./layout.css";
@import "./hero.css";
@import "./buttons.css";
@import "./cards.css";
@import "./gallery.css";
@import "tailwindcss";
/* ============================================
TAILWIND THEME - Static Design Tokens
============================================ */
@theme {
/* Typography */
--font-display: 'Cormorant Garamond', Georgia, serif;
--font-body: 'Instrument Sans', system-ui, sans-serif;
--font-mono: 'Space Grotesk', monospace;
/* Spacing Scale */
--spacing-xs: 8px;
--spacing-sm: 16px;
--spacing-md: 24px;
--spacing-lg: 48px;
--spacing-xl: 80px;
--spacing-2xl: 128px;
--spacing-3xl: 192px;
/* Width */
--width-max: 1280px;
--width-content: 840px;
/* Animation */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--duration-fast: 0.2s;
--duration-base: 0.3s;
--duration-slow: 0.6s;
--duration-slower: 0.8s;
--duration-slowest: 1.2s;
}
/* ============================================
COLOR TOKENS - Light/Dark Mode via CSS
============================================ */
:root {
/* Colors - Light Mode */
--color-ink: oklch(10% 0 0);
--color-text: oklch(10% 0 0);
--color-paper: oklch(98% 0 0);
--color-charcoal: oklch(25% 0 0);
--color-ash: oklch(55% 0 0);
--color-mist: oklch(92% 0 0);
--color-bg: oklch(96% 0.005 350);
--color-accent: oklch(60% 0.25 350);
--color-accent-hover: oklch(52% 0.25 350);
--color-accent-dim: oklch(60% 0.25 350 / 0.15);
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
:root {
--color-ink: oklch(98% 0 0);
--color-text: oklch(98% 0 0);
--color-paper: oklch(10% 0 0);
--color-charcoal: oklch(92% 0 0);
--color-ash: oklch(65% 0 0);
--color-mist: oklch(20% 0 0);
--color-bg: oklch(12% 0.005 350);
--color-accent: oklch(68% 0.23 350);
--color-accent-hover: oklch(76% 0.23 350);
}
}
/* ============================================
BASE STYLES
============================================ */
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
font-family: var(--font-body);
color: var(--color-ink);
background: var(--color-paper);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
font-weight: 600;
line-height: 1.1;
letter-spacing: -0.02em;
}
a {
color: var(--color-accent);
text-decoration: none;
transition: opacity var(--duration-fast) var(--ease-out);
}
a:hover {
opacity: 0.7;
}
/* ============================================
SECTION TITLE - With accent underline
============================================ */
.section-title {
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 600;
position: relative;
display: block;
padding-bottom: var(--spacing-sm);
}
.section-title::after {
content: '';
position: absolute;
bottom: -0.25em;
left: 0;
width: 80px;
height: 2px;
background: var(--color-accent);
}
/* ============================================
BACKGROUND ATMOSPHERE
============================================ */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(circle at 20% 30%, var(--color-accent-dim) 0%, transparent 50%),
radial-gradient(circle at 80% 70%, var(--color-accent-dim) 0%, transparent 50%);
opacity: 0;
z-index: -1;
animation: fadeInBg var(--duration-slowest) var(--ease-out) 0.3s forwards;
pointer-events: none;
}
/* ============================================
HERO DECORATIONS
============================================ */
.deco-line {
position: absolute;
width: 160px;
height: 2px;
background: var(--color-accent);
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-25deg);
animation: expandLine 1s var(--ease-out) 0.6s both;
}
.deco-circle {
position: absolute;
width: 120px;
height: 120px;
border: 2px solid var(--color-accent);
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation: drawCircle 1s var(--ease-out) 0.8s both;
opacity: 0.3;
}
/* ============================================
BUTTONS
============================================ */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.875rem 2rem;
font-family: var(--font-body);
font-size: 0.9375rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
border: none;
cursor: pointer;
transition: all var(--duration-base) var(--ease-out);
position: relative;
overflow: hidden;
text-decoration: none;
}
.btn-primary {
background: var(--color-accent);
color: var(--color-paper);
width: 100%;
position: relative;
z-index: 1;
}
.btn-primary::before {
content: '';
position: absolute;
inset: 0;
background: var(--color-ink);
transform: translateY(100%);
transition: transform var(--duration-base) var(--ease-out);
z-index: -1;
}
.btn-primary:hover::before {
transform: translateY(0);
}
.btn-secondary {
background: transparent;
color: var(--color-ink);
border: 1px solid var(--color-ink);
}
.btn-secondary:hover {
background: var(--color-ink);
color: var(--color-paper);
}
/* ============================================
CARD COMPONENTS
============================================ */
.card-inner {
height: 100%;
padding: var(--spacing-lg);
background: var(--color-paper);
border: 1px solid var(--color-mist);
display: flex;
flex-direction: column;
gap: var(--spacing-md);
transition: all var(--duration-base) var(--ease-out);
position: relative;
overflow: hidden;
}
.card-inner::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: var(--color-accent);
transform: translateX(-100%);
transition: transform 0.4s var(--ease-out);
}
.card-inner:hover {
border-color: var(--color-accent);
transform: translateY(-4px);
}
.card-inner:hover::before {
transform: translateX(0);
}
.badge {
font-size: 0.625rem;
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 0.25em 0.625em;
border: 1px solid var(--color-accent);
color: var(--color-accent);
border-radius: 2px;
font-weight: 600;
white-space: nowrap;
}
/* ============================================
SOLUTION CARDS
============================================ */
.solution-card {
padding: var(--spacing-lg);
background: var(--color-paper);
border-left: 3px solid var(--color-accent);
transition: all var(--duration-base) var(--ease-out);
}
.solution-card:hover {
background: color-mix(in oklch, var(--color-paper) 90%, var(--color-accent));
transform: translateX(4px);
}
.solution-card code {
font-family: var(--font-mono);
font-size: 0.875em;
background: color-mix(in oklch, var(--color-accent) 15%, transparent);
color: var(--color-accent);
padding: 0.125em 0.375em;
border-radius: 3px;
}
/* ============================================
ANIMATIONS
============================================ */
@keyframes fadeInBg {
to { opacity: 1; }
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-40px);
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(40px);
}
}
@keyframes fadeInUp {
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes expandLine {
from { width: 0; }
}
@keyframes drawCircle {
from {
opacity: 0;
scale: 0;
}
to {
opacity: 1;
scale: 1;
}
}
[data-animate] {
opacity: 0;
transform: translateY(20px);
}
[data-animate].animated {
animation: fadeInUp var(--duration-slow) var(--ease-out) both;
}
/* Hero animations */
.hero-content {
animation: slideInLeft var(--duration-slower) var(--ease-out) 0.1s both;
}
.hero-decoration {
animation: slideInRight var(--duration-slower) var(--ease-out) 0.3s both;
}
/* ============================================
IMPORTS - Complex components that need custom CSS
============================================ */
@import "./problem-section.css";
@import "./solution-section.css";
@import "./skill-demos.css";
@import "./gallery.css";
@import "./workflow.css";
@import "./footer.css";
+9 -9
View File
@@ -4,19 +4,19 @@
*/
.problem-section {
padding: var(--s-2xl) 0;
border-top: 1px solid var(--c-mist);
padding: var(--spacing-2xl) 0;
border-top: 1px solid var(--color-mist);
}
.problem-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--s-2xl);
gap: var(--spacing-2xl);
align-items: center;
@media (max-width: 968px) {
grid-template-columns: 1fr;
gap: var(--s-xl);
gap: var(--spacing-xl);
}
}
@@ -25,13 +25,13 @@
}
.problem-content .section-title {
margin-bottom: var(--s-md);
margin-bottom: var(--spacing-md);
}
.problem-lead {
font-size: 1.25rem;
line-height: 1.6;
color: var(--c-charcoal);
color: var(--color-charcoal);
}
/* AI Slop Demo - Deliberately generic design */
@@ -124,10 +124,10 @@
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-accent);
background: var(--c-paper);
color: var(--color-accent);
background: var(--color-paper);
padding: 4px 8px;
border: 1px solid var(--c-accent);
border: 1px solid var(--color-accent);
border-radius: 2px;
white-space: nowrap;
opacity: 0;
-71
View File
@@ -1,71 +0,0 @@
/* Modern CSS Reset */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
font-family: var(--font-body);
font-weight: 400;
font-size: 1rem;
line-height: 1.6;
color: var(--c-ink);
background: var(--c-paper);
overflow-x: hidden;
min-height: 100vh;
}
/* Typography Defaults */
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--font-display);
font-weight: 600;
line-height: 1.1;
letter-spacing: -0.02em;
}
p {
max-width: 65ch;
}
a {
color: var(--c-accent);
text-decoration: none;
transition: opacity var(--duration-fast) var(--ease-out);
&:hover {
opacity: 0.7;
}
}
/* Better image defaults */
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
}
/* Remove default button styles */
button {
font: inherit;
cursor: pointer;
}
+175 -175
View File
@@ -14,30 +14,30 @@
.demo-tabs {
display: flex;
gap: 2px;
margin-bottom: var(--s-sm);
margin-bottom: var(--spacing-sm);
justify-content: center;
}
.demo-tab {
padding: var(--s-xs) var(--s-md);
padding: var(--spacing-xs) var(--spacing-md);
background: transparent;
border: none;
border-bottom: 2px solid transparent;
font-family: var(--font-body);
font-size: 0.8125rem;
font-weight: 400;
color: var(--c-ash);
color: var(--color-ash);
cursor: pointer;
transition: all 0.15s ease;
}
.demo-tab:hover {
color: var(--c-text);
color: var(--color-text);
}
.demo-tab.active {
color: var(--c-accent);
border-bottom-color: var(--c-accent);
color: var(--color-accent);
border-bottom-color: var(--color-accent);
font-weight: 500;
}
@@ -58,8 +58,8 @@
============================================ */
.demo-container {
background: var(--c-paper);
border: 1px solid var(--c-mist);
background: var(--color-paper);
border: 1px solid var(--color-mist);
border-radius: 6px;
overflow: hidden;
}
@@ -68,16 +68,16 @@
display: flex;
align-items: center;
justify-content: flex-end;
padding: var(--s-xs) var(--s-md);
background: var(--c-bg);
border-bottom: 1px solid var(--c-mist);
padding: var(--spacing-xs) var(--spacing-md);
background: var(--color-bg);
border-bottom: 1px solid var(--color-mist);
min-height: 40px;
}
.demo-toggle {
display: flex;
align-items: center;
gap: var(--s-sm);
gap: var(--spacing-sm);
}
.demo-toggle-label {
@@ -85,19 +85,19 @@
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--c-ash);
color: var(--color-ash);
transition: color 0.15s ease;
}
.demo-toggle-label.active {
color: var(--c-accent);
color: var(--color-accent);
}
.demo-toggle-switch {
position: relative;
width: 36px;
height: 20px;
background: var(--c-mist);
background: var(--color-mist);
border-radius: 10px;
cursor: pointer;
transition: background 0.15s ease;
@@ -110,14 +110,14 @@
left: 2px;
width: 16px;
height: 16px;
background: var(--c-paper);
background: var(--color-paper);
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
transition: transform 0.15s ease;
}
.demo-toggle-switch.active {
background: var(--c-accent);
background: var(--color-accent);
}
.demo-toggle-switch.active::after {
@@ -125,20 +125,20 @@
}
.demo-viewport {
padding: var(--s-lg);
padding: var(--spacing-lg);
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
background: var(--c-bg);
background: var(--color-bg);
}
.demo-caption {
padding: var(--s-xs) var(--s-md);
padding: var(--spacing-xs) var(--spacing-md);
font-size: 0.75rem;
color: var(--c-ash);
border-top: 1px solid var(--c-mist);
background: var(--c-bg);
color: var(--color-ash);
border-top: 1px solid var(--color-mist);
background: var(--color-bg);
text-align: center;
}
@@ -149,39 +149,39 @@
.uxw-demo {
width: 100%;
max-width: 320px;
padding: var(--s-lg);
background: var(--c-paper);
border: 1px solid var(--c-mist);
padding: var(--spacing-lg);
background: var(--color-paper);
border: 1px solid var(--color-mist);
border-radius: 6px;
text-align: center;
}
/* Error Messages */
.uxw-error-icon { font-size: 2rem; margin-bottom: var(--s-sm); }
.uxw-error-title { font-weight: 600; color: #c00; margin-bottom: var(--s-xs); }
.uxw-error-text { font-size: 0.875rem; color: var(--c-ash); }
.uxw-error-action { margin-top: var(--s-sm); font-size: 0.875rem; color: var(--c-accent); cursor: pointer; text-decoration: underline; }
.uxw-error-icon { font-size: 2rem; margin-bottom: var(--spacing-sm); }
.uxw-error-title { font-weight: 600; color: #c00; margin-bottom: var(--spacing-xs); }
.uxw-error-text { font-size: 0.875rem; color: var(--color-ash); }
.uxw-error-action { margin-top: var(--spacing-sm); font-size: 0.875rem; color: var(--color-accent); cursor: pointer; text-decoration: underline; }
.uxw-error-after .uxw-error-icon { color: var(--c-accent); }
.uxw-error-after .uxw-error-title { color: var(--c-text); }
.uxw-error-after .uxw-error-text { color: var(--c-charcoal); }
.uxw-error-after .uxw-error-icon { color: var(--color-accent); }
.uxw-error-after .uxw-error-title { color: var(--color-text); }
.uxw-error-after .uxw-error-text { color: var(--color-charcoal); }
/* Button Labels */
.uxw-button-context { font-size: 0.875rem; color: var(--c-charcoal); margin-bottom: var(--s-md); font-weight: 500; }
.uxw-button-row { display: flex; gap: var(--s-sm); justify-content: center; }
.uxw-btn { padding: var(--s-xs) var(--s-md); border-radius: 4px; font-size: 0.875rem; font-weight: 500; cursor: pointer; border: none; }
.uxw-btn-primary { background: var(--c-text); color: var(--c-paper); }
.uxw-btn-secondary { background: transparent; color: var(--c-ash); border: 1px solid var(--c-mist); }
.uxw-button-context { font-size: 0.875rem; color: var(--color-charcoal); margin-bottom: var(--spacing-md); font-weight: 500; }
.uxw-button-row { display: flex; gap: var(--spacing-sm); justify-content: center; }
.uxw-btn { padding: var(--spacing-xs) var(--spacing-md); border-radius: 4px; font-size: 0.875rem; font-weight: 500; cursor: pointer; border: none; }
.uxw-btn-primary { background: var(--color-text); color: var(--color-paper); }
.uxw-btn-secondary { background: transparent; color: var(--color-ash); border: 1px solid var(--color-mist); }
.uxw-btn-danger { background: #c00; color: white; }
/* Empty States */
.uxw-empty-icon { font-size: 2.5rem; margin-bottom: var(--s-sm); opacity: 0.4; }
.uxw-empty-title { font-weight: 500; color: var(--c-ash); }
.uxw-empty-text { font-size: 0.875rem; color: var(--c-charcoal); margin-top: var(--s-xs); }
.uxw-empty-action { margin-top: var(--s-md); }
.uxw-empty-icon { font-size: 2.5rem; margin-bottom: var(--spacing-sm); opacity: 0.4; }
.uxw-empty-title { font-weight: 500; color: var(--color-ash); }
.uxw-empty-text { font-size: 0.875rem; color: var(--color-charcoal); margin-top: var(--spacing-xs); }
.uxw-empty-action { margin-top: var(--spacing-md); }
.uxw-empty-after .uxw-empty-icon { opacity: 1; }
.uxw-empty-after .uxw-empty-title { color: var(--c-text); }
.uxw-empty-after .uxw-empty-title { color: var(--color-text); }
/* ============================================
SPATIAL DESIGN DEMOS
@@ -190,9 +190,9 @@
.spatial-demo {
width: 100%;
max-width: 340px;
padding: var(--s-md);
background: var(--c-paper);
border: 1px solid var(--c-mist);
padding: var(--spacing-md);
background: var(--color-paper);
border: 1px solid var(--color-mist);
border-radius: 6px;
}
@@ -206,16 +206,16 @@
.spatial-grid-after {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--s-sm);
gap: var(--spacing-sm);
}
.spatial-card-item {
padding: var(--s-sm);
background: var(--c-bg);
border: 1px solid var(--c-mist);
padding: var(--spacing-sm);
background: var(--color-bg);
border: 1px solid var(--color-mist);
border-radius: 4px;
font-size: 0.8125rem;
color: var(--c-charcoal);
color: var(--color-charcoal);
text-align: center;
}
@@ -229,8 +229,8 @@
.spatial-hierarchy-before .spatial-h-cta,
.spatial-hierarchy-before .spatial-h-link {
font-size: 0.9375rem;
margin-bottom: var(--s-xs);
color: var(--c-charcoal);
margin-bottom: var(--spacing-xs);
color: var(--color-charcoal);
}
.spatial-hierarchy-after .spatial-h-title {
@@ -238,52 +238,52 @@
font-size: 1.75rem;
font-weight: 300;
font-style: italic;
margin-bottom: var(--s-xs);
color: var(--c-text);
margin-bottom: var(--spacing-xs);
color: var(--color-text);
}
.spatial-hierarchy-after .spatial-h-subtitle {
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--c-ash);
margin-bottom: var(--s-md);
color: var(--color-ash);
margin-bottom: var(--spacing-md);
}
.spatial-hierarchy-after .spatial-h-cta {
display: inline-block;
padding: var(--s-sm) var(--s-lg);
background: var(--c-text);
color: var(--c-paper);
padding: var(--spacing-sm) var(--spacing-lg);
background: var(--color-text);
color: var(--color-paper);
font-size: 0.875rem;
font-weight: 500;
border-radius: 4px;
margin-bottom: var(--s-sm);
margin-bottom: var(--spacing-sm);
}
.spatial-hierarchy-after .spatial-h-link {
font-size: 0.75rem;
color: var(--c-ash);
color: var(--color-ash);
}
/* Whitespace */
.spatial-whitespace-before {
padding: var(--s-xs) !important;
padding: var(--spacing-xs) !important;
}
.spatial-whitespace-before .spatial-ws-title { font-size: 1rem; font-weight: 600; margin-bottom: 2px; }
.spatial-whitespace-before .spatial-ws-price { font-size: 0.875rem; color: var(--c-ash); margin-bottom: 4px; }
.spatial-whitespace-before .spatial-ws-features { font-size: 0.75rem; color: var(--c-ash); margin-bottom: 6px; }
.spatial-whitespace-before .spatial-ws-btn { width: 100%; padding: 6px; font-size: 0.75rem; background: var(--c-text); color: var(--c-paper); border: none; border-radius: 3px; cursor: pointer; }
.spatial-whitespace-before .spatial-ws-price { font-size: 0.875rem; color: var(--color-ash); margin-bottom: 4px; }
.spatial-whitespace-before .spatial-ws-features { font-size: 0.75rem; color: var(--color-ash); margin-bottom: 6px; }
.spatial-whitespace-before .spatial-ws-btn { width: 100%; padding: 6px; font-size: 0.75rem; background: var(--color-text); color: var(--color-paper); border: none; border-radius: 3px; cursor: pointer; }
.spatial-whitespace-after {
padding: var(--s-lg) !important;
padding: var(--spacing-lg) !important;
}
.spatial-whitespace-after .spatial-ws-title { font-family: var(--font-display); font-size: 1.5rem; font-weight: 400; margin-bottom: var(--s-sm); }
.spatial-whitespace-after .spatial-ws-price { font-size: 1.25rem; font-weight: 600; color: var(--c-text); margin-bottom: var(--s-sm); }
.spatial-whitespace-after .spatial-ws-features { font-size: 0.8125rem; color: var(--c-ash); margin-bottom: var(--s-lg); line-height: 1.6; }
.spatial-whitespace-after .spatial-ws-btn { width: 100%; padding: var(--s-sm); font-size: 0.875rem; background: var(--c-text); color: var(--c-paper); border: none; border-radius: 4px; cursor: pointer; font-weight: 500; }
.spatial-whitespace-after .spatial-ws-title { font-family: var(--font-display); font-size: 1.5rem; font-weight: 400; margin-bottom: var(--spacing-sm); }
.spatial-whitespace-after .spatial-ws-price { font-size: 1.25rem; font-weight: 600; color: var(--color-text); margin-bottom: var(--spacing-sm); }
.spatial-whitespace-after .spatial-ws-features { font-size: 0.8125rem; color: var(--color-ash); margin-bottom: var(--spacing-lg); line-height: 1.6; }
.spatial-whitespace-after .spatial-ws-btn { width: 100%; padding: var(--spacing-sm); font-size: 0.875rem; background: var(--color-text); color: var(--color-paper); border: none; border-radius: 4px; cursor: pointer; font-weight: 500; }
/* ============================================
MOTION DESIGN DEMOS
@@ -293,7 +293,7 @@
display: flex;
flex-direction: column;
align-items: center;
gap: var(--s-sm);
gap: var(--spacing-sm);
width: 100%;
max-width: 280px;
}
@@ -306,19 +306,19 @@
.motion-list-item {
display: flex;
align-items: center;
gap: var(--s-sm);
padding: var(--s-sm) var(--s-md);
background: var(--c-bg);
border: 1px solid var(--c-mist);
gap: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-md);
background: var(--color-bg);
border: 1px solid var(--color-mist);
border-radius: 4px;
font-size: 0.875rem;
color: var(--c-charcoal);
color: var(--color-charcoal);
}
.motion-dot {
width: 8px;
height: 8px;
background: var(--c-accent);
background: var(--color-accent);
border-radius: 50%;
}
@@ -348,13 +348,13 @@
}
.motion-btn-before {
background: var(--c-charcoal);
color: var(--c-paper);
background: var(--color-charcoal);
color: var(--color-paper);
}
.motion-btn-after {
background: var(--c-text);
color: var(--c-paper);
background: var(--color-text);
color: var(--color-paper);
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s ease;
}
@@ -369,24 +369,24 @@
/* State Transitions */
.motion-card {
padding: var(--s-md);
background: var(--c-bg);
border: 1px solid var(--c-mist);
padding: var(--spacing-md);
background: var(--color-bg);
border: 1px solid var(--color-mist);
border-radius: 6px;
text-align: center;
min-width: 140px;
}
.motion-card-icon { font-size: 1.5rem; margin-bottom: var(--s-xs); }
.motion-card-text { font-size: 0.8125rem; color: var(--c-charcoal); }
.motion-card-icon { font-size: 1.5rem; margin-bottom: var(--spacing-xs); }
.motion-card-text { font-size: 0.8125rem; color: var(--color-charcoal); }
.motion-card-after {
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.demo-viewport[data-state="after"] .motion-card-after {
background: color-mix(in oklch, var(--c-accent) 10%, var(--c-paper));
border-color: var(--c-accent);
background: color-mix(in oklch, var(--color-accent) 10%, var(--color-paper));
border-color: var(--color-accent);
}
.demo-viewport[data-state="after"] .motion-card-after .motion-card-icon {
@@ -415,13 +415,13 @@
.typo-pairing-before .typo-heading {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: var(--s-xs);
margin-bottom: var(--spacing-xs);
}
.typo-pairing-before .typo-body {
font-size: 0.9375rem;
line-height: 1.5;
color: var(--c-ash);
color: var(--color-ash);
}
.typo-pairing-after .typo-heading {
@@ -430,21 +430,21 @@
font-weight: 300;
font-style: italic;
letter-spacing: -0.02em;
margin-bottom: var(--s-sm);
color: var(--c-text);
margin-bottom: var(--spacing-sm);
color: var(--color-text);
}
.typo-pairing-after .typo-body {
font-family: var(--font-body);
font-size: 0.9375rem;
line-height: 1.7;
color: var(--c-charcoal);
color: var(--color-charcoal);
}
/* Hierarchy */
.typo-hierarchy-before .typo-h1 { font-size: 1.125rem; font-weight: 600; margin-bottom: 4px; }
.typo-hierarchy-before .typo-meta { font-size: 0.9375rem; color: var(--c-ash); margin-bottom: var(--s-xs); }
.typo-hierarchy-before .typo-p { font-size: 0.875rem; line-height: 1.5; color: var(--c-charcoal); }
.typo-hierarchy-before .typo-meta { font-size: 0.9375rem; color: var(--color-ash); margin-bottom: var(--spacing-xs); }
.typo-hierarchy-before .typo-p { font-size: 0.875rem; line-height: 1.5; color: var(--color-charcoal); }
.typo-hierarchy-after .typo-h1 {
font-family: var(--font-display);
@@ -459,14 +459,14 @@
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--c-accent);
margin-bottom: var(--s-md);
color: var(--color-accent);
margin-bottom: var(--spacing-md);
}
.typo-hierarchy-after .typo-p {
font-size: 0.9375rem;
line-height: 1.7;
color: var(--c-ash);
color: var(--color-ash);
}
/* ============================================
@@ -476,33 +476,33 @@
.int-demo {
display: flex;
flex-direction: column;
gap: var(--s-md);
gap: var(--spacing-md);
width: 100%;
max-width: 280px;
}
/* Button States */
.int-states-demo {
gap: var(--s-lg);
gap: var(--spacing-lg);
}
.int-state-row {
display: flex;
align-items: center;
gap: var(--s-md);
gap: var(--spacing-md);
}
.int-state-label {
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-ash);
color: var(--color-ash);
width: 40px;
}
.int-btn {
flex: 1;
padding: var(--s-sm) var(--s-md);
padding: var(--spacing-sm) var(--spacing-md);
font-size: 0.875rem;
font-weight: 500;
border-radius: 4px;
@@ -510,26 +510,26 @@
}
.int-btn-poor {
background: var(--c-charcoal);
color: var(--c-paper);
background: var(--color-charcoal);
color: var(--color-paper);
border: none;
}
.int-btn-good {
background: var(--c-text);
color: var(--c-paper);
background: var(--color-text);
color: var(--color-paper);
border: 2px solid transparent;
transition: all 0.15s ease;
}
.int-btn-good:hover {
background: var(--c-charcoal);
background: var(--color-charcoal);
}
.int-btn-good:focus {
outline: none;
border-color: var(--c-accent);
box-shadow: 0 0 0 3px color-mix(in oklch, var(--c-accent) 25%, transparent);
border-color: var(--color-accent);
box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-accent) 25%, transparent);
}
.int-btn-good:active {
@@ -538,18 +538,18 @@
/* Affordances */
.int-aff-item {
padding: var(--s-sm) var(--s-md);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: 4px;
font-size: 0.875rem;
cursor: pointer;
}
.int-aff-poor {
color: var(--c-charcoal);
color: var(--color-charcoal);
}
.int-aff-good {
color: var(--c-accent);
color: var(--color-accent);
text-decoration: underline;
text-underline-offset: 2px;
}
@@ -559,16 +559,16 @@
}
.int-affordance-after .int-aff-item {
background: var(--c-bg);
border: 1px solid var(--c-mist);
color: var(--c-accent);
background: var(--color-bg);
border: 1px solid var(--color-mist);
color: var(--color-accent);
text-decoration: underline;
text-underline-offset: 2px;
transition: background 0.15s ease;
}
.int-affordance-after .int-aff-item:hover {
background: color-mix(in oklch, var(--c-accent) 5%, var(--c-paper));
background: color-mix(in oklch, var(--color-accent) 5%, var(--color-paper));
}
.int-affordance-after .int-aff-item::after {
@@ -580,7 +580,7 @@
.int-feedback-after {
display: flex;
align-items: center;
gap: var(--s-md);
gap: var(--spacing-md);
flex-direction: row;
}
@@ -601,13 +601,13 @@
}
.int-fb-silent {
background: var(--c-mist);
color: var(--c-ash);
background: var(--color-mist);
color: var(--color-ash);
}
.int-fb-active {
background: var(--c-charcoal);
color: var(--c-paper);
background: var(--color-charcoal);
color: var(--color-paper);
transition: all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@@ -620,7 +620,7 @@
}
.int-fb-active.liked {
background: var(--c-accent);
background: var(--color-accent);
animation: heartPop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@@ -630,7 +630,7 @@
.int-fb-label {
font-size: 0.875rem;
color: var(--c-charcoal);
color: var(--color-charcoal);
}
/* ============================================
@@ -647,10 +647,10 @@
.color-palette-after {
display: flex;
flex-wrap: wrap;
gap: var(--s-xs);
padding: var(--s-md);
background: var(--c-paper);
border: 1px solid var(--c-mist);
gap: var(--spacing-xs);
padding: var(--spacing-md);
background: var(--color-paper);
border: 1px solid var(--color-mist);
border-radius: 6px;
}
@@ -663,10 +663,10 @@
.color-card {
width: 100%;
margin-top: var(--s-sm);
padding: var(--s-sm);
background: var(--c-paper);
border: 1px solid var(--c-mist);
margin-top: var(--spacing-sm);
padding: var(--spacing-sm);
background: var(--color-paper);
border: 1px solid var(--color-mist);
border-radius: 4px;
display: flex;
flex-direction: column;
@@ -687,18 +687,18 @@
.color-palette-before .card-btn { background: #ffe66d; color: #333; }
/* After - Harmonious palette */
.color-palette-after .swatch-1 { background: var(--c-text); }
.color-palette-after .swatch-2 { background: var(--c-charcoal); }
.color-palette-after .swatch-3 { background: var(--c-ash); }
.color-palette-after .swatch-4 { background: var(--c-mist); }
.color-palette-after .swatch-5 { background: var(--c-accent); }
.color-palette-after .card-title { color: var(--c-text); }
.color-palette-after .card-subtitle { color: var(--c-ash); }
.color-palette-after .card-btn { background: var(--c-accent); color: var(--c-paper); }
.color-palette-after .swatch-1 { background: var(--color-text); }
.color-palette-after .swatch-2 { background: var(--color-charcoal); }
.color-palette-after .swatch-3 { background: var(--color-ash); }
.color-palette-after .swatch-4 { background: var(--color-mist); }
.color-palette-after .swatch-5 { background: var(--color-accent); }
.color-palette-after .card-title { color: var(--color-text); }
.color-palette-after .card-subtitle { color: var(--color-ash); }
.color-palette-after .card-btn { background: var(--color-accent); color: var(--color-paper); }
/* Strategic Accent */
.color-accent-card {
padding: var(--s-md);
padding: var(--spacing-md);
border-radius: 6px;
}
@@ -708,27 +708,27 @@
}
.color-accent-before .color-accent-title { font-weight: 600; color: #333; margin-bottom: 4px; }
.color-accent-before .color-accent-text { font-size: 0.8125rem; color: #666; margin-bottom: var(--s-sm); }
.color-accent-before .color-accent-btn { width: 100%; padding: var(--s-xs); background: #333; color: white; border: none; border-radius: 4px; font-size: 0.8125rem; cursor: pointer; }
.color-accent-before .color-accent-text { font-size: 0.8125rem; color: #666; margin-bottom: var(--spacing-sm); }
.color-accent-before .color-accent-btn { width: 100%; padding: var(--spacing-xs); background: #333; color: white; border: none; border-radius: 4px; font-size: 0.8125rem; cursor: pointer; }
.color-accent-after .color-accent-card {
background: color-mix(in oklch, var(--c-accent) 8%, var(--c-paper));
border: 1px solid color-mix(in oklch, var(--c-accent) 20%, var(--c-paper));
background: color-mix(in oklch, var(--color-accent) 8%, var(--color-paper));
border: 1px solid color-mix(in oklch, var(--color-accent) 20%, var(--color-paper));
}
.color-accent-after .color-accent-title { font-weight: 600; color: var(--c-text); margin-bottom: 4px; }
.color-accent-after .color-accent-text { font-size: 0.8125rem; color: var(--c-ash); margin-bottom: var(--s-sm); }
.color-accent-after .color-accent-btn { width: 100%; padding: var(--s-xs); background: var(--c-accent); color: var(--c-paper); border: none; border-radius: 4px; font-size: 0.8125rem; font-weight: 500; cursor: pointer; }
.color-accent-after .color-accent-title { font-weight: 600; color: var(--color-text); margin-bottom: 4px; }
.color-accent-after .color-accent-text { font-size: 0.8125rem; color: var(--color-ash); margin-bottom: var(--spacing-sm); }
.color-accent-after .color-accent-btn { width: 100%; padding: var(--spacing-xs); background: var(--color-accent); color: var(--color-paper); border: none; border-radius: 4px; font-size: 0.8125rem; font-weight: 500; cursor: pointer; }
/* Contrast Ratios */
.color-contrast-static {
display: flex;
flex-direction: column;
gap: var(--s-sm);
gap: var(--spacing-sm);
}
.contrast-example {
padding: var(--s-md);
padding: var(--spacing-md);
border-radius: 6px;
text-align: center;
}
@@ -739,8 +739,8 @@
}
.contrast-pass {
background: var(--c-charcoal);
color: var(--c-paper);
background: var(--color-charcoal);
color: var(--color-paper);
}
.contrast-badge {
@@ -755,7 +755,7 @@
}
.contrast-fail .contrast-badge { background: #ddd; }
.contrast-pass .contrast-badge { background: var(--c-accent); color: var(--c-paper); }
.contrast-pass .contrast-badge { background: var(--color-accent); color: var(--color-paper); }
.contrast-text { font-size: 1rem; font-weight: 500; margin-bottom: 2px; }
.contrast-ratio { font-size: 0.6875rem; opacity: 0.7; }
@@ -773,20 +773,20 @@
.resp-touch-demo {
display: flex;
flex-direction: column;
gap: var(--s-lg);
gap: var(--spacing-lg);
}
.resp-touch-row {
display: flex;
align-items: center;
gap: var(--s-md);
gap: var(--spacing-md);
}
.resp-label {
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-ash);
color: var(--color-ash);
width: 70px;
}
@@ -806,36 +806,36 @@
width: 24px;
height: 24px;
font-size: 0.75rem;
background: var(--c-mist);
color: var(--c-ash);
background: var(--color-mist);
color: var(--color-ash);
}
.resp-touch-good button {
width: 44px;
height: 44px;
font-size: 1rem;
background: var(--c-text);
color: var(--c-paper);
background: var(--color-text);
color: var(--color-paper);
}
/* Fluid Layout */
.resp-fluid-demo {
padding: var(--s-md);
background: var(--c-bg);
border: 1px solid var(--c-mist);
padding: var(--spacing-md);
background: var(--color-bg);
border: 1px solid var(--color-mist);
border-radius: 6px;
}
.resp-fluid-container {
display: flex;
flex-direction: column;
gap: var(--s-md);
gap: var(--spacing-md);
}
.resp-fluid-fixed,
.resp-fluid-adaptive {
font-size: 0.75rem;
color: var(--c-ash);
color: var(--color-ash);
}
.resp-fluid-fixed span,
@@ -846,18 +846,18 @@
.resp-fluid-bar {
height: 24px;
background: var(--c-mist);
background: var(--color-mist);
border-radius: 4px;
}
.resp-fluid-adaptive .resp-fluid-bar {
background: var(--c-accent);
background: var(--color-accent);
}
/* Adaptive Content */
.resp-adapt-demo {
display: flex;
gap: var(--s-sm);
gap: var(--spacing-sm);
align-items: flex-end;
}
@@ -869,14 +869,14 @@
display: block;
margin-top: 4px;
font-size: 0.625rem;
color: var(--c-ash);
color: var(--color-ash);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.resp-device-screen {
background: var(--c-paper);
border: 2px solid var(--c-mist);
background: var(--color-paper);
border: 2px solid var(--color-mist);
border-radius: 4px;
padding: 4px;
display: flex;
@@ -889,7 +889,7 @@
.resp-device-desktop .resp-device-screen { width: 120px; height: 70px; }
.resp-block {
background: var(--c-mist);
background: var(--color-mist);
border-radius: 2px;
}
@@ -899,8 +899,8 @@
flex: 1;
}
.resp-header { height: 16px; background: var(--c-charcoal); }
.resp-sidebar { width: 30%; background: var(--c-charcoal); }
.resp-header { height: 16px; background: var(--color-charcoal); }
.resp-sidebar { width: 30%; background: var(--color-charcoal); }
.resp-content { flex: 1; }
/* ============================================
-96
View File
@@ -1,96 +0,0 @@
/**
* Solution Section - Skills + Commands explanation
*/
.solution-section {
padding: var(--s-2xl) 0;
border-top: 1px solid var(--c-mist);
}
.solution-grid {
display: grid;
grid-template-columns: 1fr;
gap: var(--s-xl);
}
.solution-content {
max-width: 680px;
}
.solution-content .section-title {
margin-bottom: var(--s-md);
}
.solution-lead {
font-size: 1.375rem;
line-height: 1.6;
color: var(--c-charcoal);
}
.solution-lead strong {
color: var(--c-text);
font-weight: 600;
}
/* Solution Cards - Side by side */
.solution-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--s-lg);
@media (max-width: 640px) {
grid-template-columns: 1fr;
}
}
.solution-card {
padding: var(--s-lg);
background: var(--c-paper);
border-left: 3px solid var(--c-accent);
transition: all var(--duration-base) var(--ease-out);
}
.solution-card:hover {
background: color-mix(in oklch, var(--c-paper) 90%, var(--c-accent));
transform: translateX(4px);
}
.solution-icon {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: var(--s-md);
color: var(--c-accent);
}
.solution-card h3 {
font-family: var(--font-display);
font-size: 1.75rem;
font-weight: 400;
margin: 0 0 var(--s-sm) 0;
color: var(--c-text);
}
.solution-card p {
font-size: 1rem;
line-height: 1.6;
color: var(--c-ash);
margin: 0;
}
.solution-card code {
font-family: 'Space Grotesk', monospace;
font-size: 0.875em;
background: color-mix(in oklch, var(--c-accent) 15%, transparent);
color: var(--c-accent);
padding: 0.125em 0.375em;
border-radius: 3px;
}
.solution-card em {
font-style: italic;
color: var(--c-charcoal);
}
-66
View File
@@ -1,66 +0,0 @@
/* Design Tokens — Single Source of Truth */
:root {
/* Typography Scale */
--font-display: 'Cormorant Garamond', Georgia, serif;
--font-body: 'Instrument Sans', system-ui, sans-serif;
/* Base Colors — Refined Monochrome + Electric Accent */
--c-ink: oklch(10% 0 0);
--c-paper: oklch(98% 0 0);
--c-charcoal: oklch(25% 0 0);
--c-ash: oklch(55% 0 0);
--c-mist: oklch(92% 0 0);
/* Semantic aliases for common use */
--c-text: var(--c-ink);
--c-bg: oklch(96% 0.005 350); /* Very subtle pink tint */
/* Accent — Vibrant Electric Pink using OKLCH for perceptual uniformity */
--c-accent: oklch(60% 0.25 350);
--c-accent-hover: oklch(from var(--c-accent) calc(l - 8%) c h);
--c-accent-dim: color-mix(in oklch, var(--c-accent), transparent 85%);
/* Spacing Scale — 8px base unit */
--s-unit: 8px;
--s-xs: calc(var(--s-unit) * 1); /* 8px */
--s-sm: calc(var(--s-unit) * 2); /* 16px */
--s-md: calc(var(--s-unit) * 3); /* 24px */
--s-lg: calc(var(--s-unit) * 6); /* 48px */
--s-xl: calc(var(--s-unit) * 10); /* 80px */
--s-2xl: calc(var(--s-unit) * 16); /* 128px */
--s-3xl: calc(var(--s-unit) * 24); /* 192px */
/* Layout Constraints */
--max-width: 1280px;
--content-width: 840px;
/* Animation Curves */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--duration-fast: 0.2s;
--duration-base: 0.3s;
--duration-slow: 0.6s;
--duration-slower: 0.8s;
--duration-slowest: 1.2s;
/* Shadows */
--shadow-sm: 0 1px 2px color-mix(in oklch, var(--c-ink), transparent 90%);
--shadow-md: 0 4px 8px color-mix(in oklch, var(--c-ink), transparent 85%);
}
/* Dark Mode — Automatic via prefers-color-scheme */
@media (prefers-color-scheme: dark) {
:root {
--c-ink: oklch(98% 0 0);
--c-paper: oklch(10% 0 0);
--c-charcoal: oklch(92% 0 0);
--c-ash: oklch(65% 0 0);
--c-mist: oklch(20% 0 0);
--c-accent: oklch(68% 0.23 350);
--c-accent-hover: oklch(from var(--c-accent) calc(l + 8%) c h);
--c-text: var(--c-ink);
--c-bg: oklch(12% 0.005 350);
}
}
+63 -63
View File
@@ -14,7 +14,7 @@
display: flex;
flex-direction: column;
gap: 2px;
margin-bottom: var(--s-sm);
margin-bottom: var(--spacing-sm);
&:last-child {
margin-bottom: 0;
@@ -26,10 +26,10 @@
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-ash);
padding: var(--s-xs) var(--s-md);
color: var(--color-ash);
padding: var(--spacing-xs) var(--spacing-md);
margin-bottom: 2px;
margin-top: var(--s-xs);
margin-top: var(--spacing-xs);
&:first-child {
margin-top: 0;
@@ -37,11 +37,11 @@
}
.command-nav-item {
padding: var(--s-xs) var(--s-md);
padding: var(--spacing-xs) var(--spacing-md);
background: transparent;
border: none;
border-left: 2px solid transparent;
color: var(--c-ash);
color: var(--color-ash);
font-family: 'Space Grotesk', monospace;
font-size: 0.875rem;
font-weight: 400;
@@ -52,14 +52,14 @@
}
.command-nav-item:hover {
color: var(--c-text);
background: color-mix(in oklch, var(--c-paper) 85%, var(--c-accent));
color: var(--color-text);
background: color-mix(in oklch, var(--color-paper) 85%, var(--color-accent));
}
.command-nav-item.active {
color: var(--c-accent);
border-left-color: var(--c-accent);
background: color-mix(in oklch, var(--c-accent) 5%, var(--c-paper));
color: var(--color-accent);
border-left-color: var(--color-accent);
background: color-mix(in oklch, var(--color-accent) 5%, var(--color-paper));
font-weight: 500;
}
@@ -67,30 +67,30 @@
.commands-nav {
flex-direction: row;
flex-wrap: wrap;
gap: var(--s-xs);
gap: var(--spacing-xs);
}
.command-nav-group {
flex-direction: row;
flex-wrap: wrap;
gap: var(--s-xs);
margin-bottom: var(--s-sm);
gap: var(--spacing-xs);
margin-bottom: var(--spacing-sm);
}
.command-nav-label {
width: 100%;
padding: var(--s-xs) 0;
padding: var(--spacing-xs) 0;
margin-top: 0;
}
.command-nav-item {
border-left: none;
border-bottom: 2px solid transparent;
padding: var(--s-xs) var(--s-sm);
padding: var(--spacing-xs) var(--spacing-sm);
}
.command-nav-item.active {
border-bottom-color: var(--c-accent);
border-bottom-color: var(--color-accent);
}
}
@@ -98,7 +98,7 @@
.workflow-row {
display: flex;
align-items: center;
gap: var(--s-md);
gap: var(--spacing-md);
flex-wrap: wrap;
}
@@ -107,20 +107,20 @@
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--c-ash);
color: var(--color-ash);
width: 120px;
flex-shrink: 0;
@media (max-width: 768px) {
width: 100%;
margin-bottom: var(--s-xs);
margin-bottom: var(--spacing-xs);
}
}
.workflow-row-content {
display: flex;
align-items: center;
gap: var(--s-sm);
gap: var(--spacing-sm);
flex-wrap: wrap;
}
@@ -130,35 +130,35 @@
align-items: center;
padding: 4px 10px;
background: transparent;
border: 1px solid var(--c-mist);
border: 1px solid var(--color-mist);
border-radius: 3px;
font-family: 'Space Grotesk', monospace;
font-size: 0.8125rem;
font-weight: 500;
color: var(--c-charcoal);
color: var(--color-charcoal);
cursor: pointer;
transition: all var(--duration-fast) var(--ease-out);
}
.workflow-node:hover {
border-color: var(--c-accent);
color: var(--c-accent);
background: color-mix(in oklch, var(--c-accent) 5%, transparent);
border-color: var(--color-accent);
color: var(--color-accent);
background: color-mix(in oklch, var(--color-accent) 5%, transparent);
}
.workflow-node.active {
background: var(--c-accent);
border-color: var(--c-accent);
color: var(--c-paper);
background: var(--color-accent);
border-color: var(--color-accent);
color: var(--color-paper);
}
/* Workflow Arrow */
.workflow-arrow {
display: flex;
align-items: center;
color: var(--c-ash);
color: var(--color-ash);
font-size: 1.25rem;
padding: 0 var(--s-xs);
padding: 0 var(--spacing-xs);
}
.workflow-arrow-bidirectional::before {
@@ -178,17 +178,17 @@
.workflow-group {
display: flex;
align-items: center;
gap: var(--s-sm);
padding: var(--s-sm);
background: color-mix(in oklch, var(--c-paper) 50%, transparent);
border: 1px dashed var(--c-mist);
gap: var(--spacing-sm);
padding: var(--spacing-sm);
background: color-mix(in oklch, var(--color-paper) 50%, transparent);
border: 1px dashed var(--color-mist);
border-radius: 4px;
}
.workflow-group-label {
font-size: 0.6875rem;
font-weight: 500;
color: var(--c-ash);
color: var(--color-ash);
text-transform: uppercase;
letter-spacing: 0.05em;
writing-mode: vertical-lr;
@@ -198,7 +198,7 @@
writing-mode: horizontal-tb;
transform: none;
width: 100%;
margin-bottom: var(--s-xs);
margin-bottom: var(--spacing-xs);
}
}
@@ -206,20 +206,20 @@
.workflow-production {
display: grid;
grid-template-columns: auto 1fr;
gap: var(--s-md);
gap: var(--spacing-md);
align-items: start;
}
.workflow-production-stages {
display: flex;
flex-direction: column;
gap: var(--s-sm);
gap: var(--spacing-sm);
}
.workflow-stage {
display: flex;
align-items: center;
gap: var(--s-sm);
gap: var(--spacing-sm);
}
.workflow-stage-label {
@@ -227,21 +227,21 @@
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-ash);
color: var(--color-ash);
width: 80px;
}
.workflow-stage-nodes {
display: flex;
gap: var(--s-xs);
gap: var(--spacing-xs);
flex-wrap: wrap;
}
/* Aesthetic Adjustments Section */
.workflow-aesthetics {
border-top: 1px solid var(--c-mist);
padding-top: var(--s-lg);
margin-top: var(--s-sm);
border-top: 1px solid var(--color-mist);
padding-top: var(--spacing-lg);
margin-top: var(--spacing-sm);
}
.workflow-aesthetics-title {
@@ -249,26 +249,26 @@
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--c-ash);
margin-bottom: var(--s-md);
color: var(--color-ash);
margin-bottom: var(--spacing-md);
}
.workflow-pairs {
display: flex;
flex-wrap: wrap;
gap: var(--s-lg);
gap: var(--spacing-lg);
}
.workflow-pair {
display: flex;
align-items: center;
gap: var(--s-sm);
gap: var(--spacing-sm);
}
/* Responsive */
@media (max-width: 768px) {
.workflow-container {
padding: var(--s-md);
padding: var(--spacing-md);
}
.workflow-production {
@@ -282,15 +282,15 @@
.workflow-stage-label {
width: auto;
margin-bottom: var(--s-xs);
margin-bottom: var(--spacing-xs);
}
}
/* Command Card in Grid */
.command-card {
padding: var(--s-sm) var(--s-md);
background: var(--c-paper);
border: 1px solid var(--c-mist);
padding: var(--spacing-sm) var(--spacing-md);
background: var(--color-paper);
border: 1px solid var(--color-mist);
border-radius: 3px;
cursor: pointer;
transition: all var(--duration-fast) var(--ease-out);
@@ -298,27 +298,27 @@
}
.command-card:hover {
border-color: var(--c-accent);
border-color: var(--color-accent);
transform: translateY(-2px);
box-shadow: 0 4px 12px color-mix(in oklch, var(--c-accent) 10%, transparent);
box-shadow: 0 4px 12px color-mix(in oklch, var(--color-accent) 10%, transparent);
}
.command-card.active {
border-color: var(--c-accent);
background: color-mix(in oklch, var(--c-accent) 5%, var(--c-paper));
border-color: var(--color-accent);
background: color-mix(in oklch, var(--color-accent) 5%, var(--color-paper));
}
.command-card-name {
font-family: 'Space Grotesk', monospace;
font-size: 0.9375rem;
font-weight: 500;
color: var(--c-text);
color: var(--color-text);
margin-bottom: 2px;
}
.command-card-desc {
font-size: 0.75rem;
color: var(--c-ash);
color: var(--color-ash);
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
@@ -334,9 +334,9 @@
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 2px 6px;
background: var(--c-mist);
color: var(--c-ash);
background: var(--color-mist);
color: var(--color-ash);
border-radius: 2px;
margin-top: var(--s-xs);
margin-top: var(--spacing-xs);
}
+69 -68
View File
@@ -7,37 +7,38 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400&family=Instrument+Sans:wght@400;500;600;700&family=Inter:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="tailwindcss" />
<link rel="stylesheet" href="./css/main.css">
</head>
<body>
<body class="min-h-screen overflow-x-hidden leading-relaxed">
<!-- 1. HERO - Hook with the problem statement -->
<header class="hero">
<div class="hero-grid">
<header class="min-h-[85vh] flex items-center relative py-32 px-12 overflow-hidden max-md:min-h-[70vh] max-md:py-20 max-md:px-6">
<div class="max-w-max mx-auto grid grid-cols-[1fr_auto] gap-32 items-center w-full relative max-md:grid-cols-1 max-md:gap-20">
<div class="hero-content">
<h1 class="title">
<span class="title-main">Impeccable style</span>
<span class="title-sub">made easy</span>
<h1 class="font-display text-[clamp(3rem,8vw,7rem)] font-light leading-[0.9] mb-12 flex flex-col max-md:text-[clamp(2.5rem,12vw,4rem)]">
<span class="font-normal tracking-tight">Impeccable style</span>
<span class="font-light italic text-accent tracking-tight">made easy</span>
</h1>
<p class="tagline">Your LLM can design beautiful interfaces.<br>It just needs to know what to ask for.</p>
<p class="providers">Works with Cursor · Claude Code · Gemini CLI · Codex CLI</p>
<p class="text-[clamp(1.125rem,2vw,1.5rem)] leading-snug text-charcoal mb-6 max-w-[32ch]">Your LLM can design beautiful interfaces.<br>It just needs to know what to ask for.</p>
<p class="text-sm text-ash uppercase tracking-widest font-medium">Works with Cursor · Claude Code · Gemini CLI · Codex CLI</p>
</div>
<div class="hero-decoration" aria-hidden="true">
<div class="hero-decoration w-60 h-60 relative max-md:hidden" aria-hidden="true">
<div class="deco-line"></div>
<div class="deco-circle"></div>
</div>
</div>
</header>
<main class="container">
<main class="max-w-max mx-auto px-12 max-sm:px-6">
<!-- 2. THE PROBLEM - Show the "AI slop" aesthetic -->
<section class="problem-section">
<div class="problem-grid">
<div class="problem-content">
<h2 class="section-title">The Problem</h2>
<p class="problem-lead">LLMs are biased toward safe, generic design choices. Without guidance, you get the same predictable aesthetic every time.</p>
<section class="problem-section py-32 border-t border-mist">
<div class="problem-grid grid grid-cols-2 gap-32 items-center max-lg:grid-cols-1 max-lg:gap-20">
<div class="max-w-md">
<h2 class="section-title mb-6">The Problem</h2>
<p class="text-xl leading-relaxed text-charcoal">LLMs are biased toward safe, generic design choices. Without guidance, you get the same predictable aesthetic every time.</p>
</div>
<div class="problem-showcase">
<div class="ai-slop-demo">
<div class="problem-showcase flex justify-center">
<div class="ai-slop-demo relative max-w-sm w-full">
<div class="slop-card">
<div class="slop-header">
<div class="slop-avatar"></div>
@@ -61,118 +62,118 @@
</section>
<!-- 3. THE SOLUTION - Brief explanation -->
<section class="solution-section">
<div class="solution-grid">
<div class="solution-content">
<h2 class="section-title">The Solution</h2>
<p class="solution-lead">Teach your AI assistant the language of design. <strong>Skills</strong> provide deep design knowledge. <strong>Commands</strong> give you precise control over the creative process.</p>
<section class="py-32 border-t border-mist">
<div class="grid gap-20">
<div class="max-w-2xl">
<h2 class="section-title mb-6">The Solution</h2>
<p class="text-[1.375rem] leading-relaxed text-charcoal">Teach your AI assistant the language of design. <strong class="text-ink font-semibold">Skills</strong> provide deep design knowledge. <strong class="text-ink font-semibold">Commands</strong> give you precise control over the creative process.</p>
</div>
<div class="solution-cards">
<div class="grid grid-cols-2 gap-12 max-sm:grid-cols-1">
<div class="solution-card">
<div class="solution-icon">
<div class="w-12 h-12 flex items-center justify-center mb-6 text-accent">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"/>
</svg>
</div>
<h3>8 Skills</h3>
<p>Deep knowledge in typography, color, motion, spatial design, and more. The AI learns <em>how</em> to think about design.</p>
<h3 class="font-display text-3xl font-normal mb-4">8 Skills</h3>
<p class="text-base leading-relaxed text-ash">Deep knowledge in typography, color, motion, spatial design, and more. The AI learns <em class="italic text-charcoal">how</em> to think about design.</p>
</div>
<div class="solution-card">
<div class="solution-icon">
<div class="w-12 h-12 flex items-center justify-center mb-6 text-accent">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z"/>
</svg>
</div>
<h3>15 Commands</h3>
<p>Precise actions like <code>/bolder</code>, <code>/animate</code>, <code>/audit</code>. Steer the AI through iterative design refinement.</p>
<h3 class="font-display text-3xl font-normal mb-4">15 Commands</h3>
<p class="text-base leading-relaxed text-ash">Precise actions like <code>/bolder</code>, <code>/animate</code>, <code>/audit</code>. Steer the AI through iterative design refinement.</p>
</div>
</div>
</div>
</section>
<!-- 4. SKILLS SHOWCASE - Interactive demos -->
<section class="skills-section" id="skills-section">
<div class="section-header-sticky">
<h2 class="section-title">Skills</h2>
<p class="section-subtitle">Design knowledge that transforms AI output</p>
<section class="skills-section py-20 border-t border-mist" id="skills-section">
<div class="mb-12">
<h2 class="section-title mb-4">Skills</h2>
<p class="text-lg text-ash mt-6 max-w-[50ch]">Design knowledge that transforms AI output</p>
</div>
<div class="skills-layout">
<nav class="skills-nav" id="skills-nav">
<div class="loading">Loading...</div>
<div class="skills-layout grid grid-cols-[180px_1fr] gap-12 items-start max-lg:grid-cols-1 max-lg:gap-6">
<nav class="skills-nav flex flex-col gap-0.5 max-lg:flex-row max-lg:flex-wrap max-lg:gap-2" id="skills-nav">
<div class="py-20 text-center text-ash italic">Loading...</div>
</nav>
<div class="skills-showcase" id="skills-detail">
<div class="loading">Select a skill to see it in action</div>
<div class="py-20 text-center text-ash italic">Select a skill to see it in action</div>
</div>
</div>
</section>
<!-- 5. COMMANDS - Sidebar nav + details (mirrors skills layout) -->
<section class="commands-section" id="commands-section">
<div class="section-header-sticky">
<h2 class="section-title">Commands</h2>
<p class="section-subtitle">Precise control over the design process</p>
<section class="commands-section py-20 border-t border-mist" id="commands-section">
<div class="mb-12">
<h2 class="section-title mb-4">Commands</h2>
<p class="text-lg text-ash mt-6 max-w-[50ch]">Precise control over the design process</p>
</div>
<div class="commands-layout">
<div class="commands-layout grid grid-cols-[180px_1fr] gap-12 items-start max-lg:grid-cols-1 max-lg:gap-6">
<!-- Commands nav (left sidebar) -->
<nav class="commands-nav" id="workflow-diagram">
<nav class="commands-nav flex flex-col gap-4 max-lg:flex-row max-lg:flex-wrap max-lg:gap-2" id="workflow-diagram">
<!-- Will be rendered by JS -->
</nav>
<!-- Command detail (right side) -->
<div class="commands-showcase" id="commands-detail">
<div class="loading">Select a command to view details</div>
<div class="py-20 text-center text-ash italic">Select a command to view details</div>
</div>
</div>
</section>
<!-- 6. DOWNLOAD BUNDLES - After demonstrating value -->
<section class="downloads-section" id="downloads">
<div class="downloads-header">
<h2 class="section-title">Get Started</h2>
<p class="downloads-lead">Download the complete bundle for your preferred AI tool. Extract into your project root and start creating.</p>
<section class="py-32 border-t border-mist" id="downloads">
<div class="mb-20 max-w-xl">
<h2 class="section-title mb-6">Get Started</h2>
<p class="text-lg leading-relaxed text-ash">Download the complete bundle for your preferred AI tool. Extract into your project root and start creating.</p>
</div>
<div class="download-grid">
<div class="grid grid-cols-[repeat(auto-fit,minmax(260px,1fr))] gap-6 max-sm:grid-cols-1">
<article class="download-card" data-animate>
<div class="card-inner">
<div class="card-header">
<h3>Cursor</h3>
<div class="flex justify-between items-start gap-6">
<h3 class="font-display text-xl font-medium">Cursor</h3>
<span class="badge">Rules + Commands</span>
</div>
<p>Commands and rules for Cursor IDE. Works via appending, no frontmatter required.</p>
<p class="text-ash text-[0.9375rem] leading-relaxed flex-grow">Commands and rules for Cursor IDE. Works via appending, no frontmatter required.</p>
<button class="btn btn-primary" data-bundle="cursor">Download</button>
</div>
</article>
<article class="download-card" data-animate>
<div class="card-inner">
<div class="card-header">
<h3>Claude Code</h3>
<div class="flex justify-between items-start gap-6">
<h3 class="font-display text-xl font-medium">Claude Code</h3>
<span class="badge">Full Featured</span>
</div>
<p>Complete YAML frontmatter with argument support. Follows Anthropic Skills specification.</p>
<p class="text-ash text-[0.9375rem] leading-relaxed flex-grow">Complete YAML frontmatter with argument support. Follows Anthropic Skills specification.</p>
<button class="btn btn-primary" data-bundle="claude-code">Download</button>
</div>
</article>
<article class="download-card" data-animate>
<div class="card-inner">
<div class="card-header">
<h3>Gemini CLI</h3>
<div class="flex justify-between items-start gap-6">
<h3 class="font-display text-xl font-medium">Gemini CLI</h3>
<span class="badge">TOML + Modular</span>
</div>
<p>TOML-formatted commands with modular skill imports via GEMINI.md.</p>
<p class="text-ash text-[0.9375rem] leading-relaxed flex-grow">TOML-formatted commands with modular skill imports via GEMINI.md.</p>
<button class="btn btn-primary" data-bundle="gemini">Download</button>
</div>
</article>
<article class="download-card" data-animate>
<div class="card-inner">
<div class="card-header">
<h3>Codex CLI</h3>
<div class="flex justify-between items-start gap-6">
<h3 class="font-display text-xl font-medium">Codex CLI</h3>
<span class="badge">Prompts + Routing</span>
</div>
<p>Custom prompt format with skill routing through AGENTS.md architecture.</p>
<p class="text-ash text-[0.9375rem] leading-relaxed flex-grow">Custom prompt format with skill routing through AGENTS.md architecture.</p>
<button class="btn btn-primary" data-bundle="codex">Download</button>
</div>
</article>
@@ -180,19 +181,19 @@
</section>
<!-- 7. OPEN SOURCE -->
<section class="contribute">
<div class="contribute-content">
<h2>Open Source</h2>
<p>View the source, contribute skills and commands, or report issues on GitHub.</p>
<section class="py-32 border-t border-mist text-center">
<div class="max-w-md mx-auto flex flex-col items-center gap-6">
<h2 class="font-display text-[clamp(2rem,4vw,3rem)] font-semibold">Open Source</h2>
<p class="text-lg text-ash max-w-[42ch]">View the source, contribute skills and commands, or report issues on GitHub.</p>
<a href="https://github.com/pbakaus/vibe-design-plugins" class="btn btn-secondary">View Repository</a>
</div>
</section>
</main>
<footer>
<div class="footer-content">
<footer class="border-t border-mist py-32 px-12 bg-paper">
<div class="max-w-max mx-auto flex justify-between items-center text-sm text-ash max-md:flex-col max-md:gap-4 max-md:text-center">
<p>impeccable.style</p>
<p><a href="https://github.com/pbakaus/vibe-design-plugins">GitHub</a></p>
<p><a href="https://github.com/pbakaus/vibe-design-plugins" class="text-ink underline underline-offset-2">GitHub</a></p>
</div>
</footer>
-110
View File
@@ -1,110 +0,0 @@
// ============================================
// COMMAND DEMOS - Before/after demonstrations for commands
// ============================================
export function renderCommandDemo(commandId) {
const demos = {
'bolder': renderBolderDemo,
'animate': renderAnimateDemo,
'normalize': renderNormalizeDemo
};
const renderFn = demos[commandId];
if (renderFn) return renderFn();
return `
<div class="demo-container">
<div class="demo-header">
<span class="demo-title">Command Preview</span>
</div>
<div class="demo-viewport">
<div style="text-align: center; color: var(--c-ash); font-style: italic;">
Visual demo for /${commandId} coming soon
</div>
</div>
</div>
`;
}
function renderBolderDemo() {
return `
<div class="demo-container">
<div class="demo-header">
<span class="demo-title">Before / After</span>
<div class="demo-toggle">
<span class="demo-toggle-label active">Before</span>
<div class="demo-toggle-switch" data-demo="command-bolder"></div>
<span class="demo-toggle-label">After</span>
</div>
</div>
<div class="demo-viewport" data-state="before" id="command-bolder-viewport">
<div style="text-align: center; padding: var(--s-md); max-width: 280px;">
<div style="font-size: 1.125rem; font-weight: 500; margin-bottom: 8px; color: var(--c-charcoal);">Introducing Our Product</div>
<div style="font-size: 0.875rem; color: var(--c-ash); margin-bottom: 16px;">A solution for modern teams</div>
<button style="padding: 8px 16px; background: var(--c-mist); color: var(--c-charcoal); border: none; border-radius: 4px; font-size: 0.875rem;">Learn More</button>
</div>
</div>
<div class="demo-caption">Timid design → Bold, confident design</div>
</div>
`;
}
function renderAnimateDemo() {
return `
<div class="demo-container">
<div class="demo-header">
<span class="demo-title">Before / After</span>
<div class="demo-toggle">
<span class="demo-toggle-label active">Before</span>
<div class="demo-toggle-switch" data-demo="command-animate"></div>
<span class="demo-toggle-label">After</span>
</div>
</div>
<div class="demo-viewport demo-viewport-dark" data-state="before" id="command-animate-viewport">
<div class="motion-demo-page" style="opacity: 1;">
<div class="motion-demo-page-hero" style="opacity: 1; transform: none;">Hero</div>
<div class="motion-demo-page-content">
<div class="motion-demo-page-heading" style="opacity: 1; transform: none;"></div>
<div class="motion-demo-page-text" style="opacity: 1; transform: none;"></div>
<div class="motion-demo-page-text" style="opacity: 1; transform: none;"></div>
<div class="motion-demo-page-text" style="opacity: 1; transform: none;"></div>
</div>
</div>
</div>
<div class="demo-caption">Static page → Choreographed page load</div>
</div>
`;
}
function renderNormalizeDemo() {
return `
<div class="demo-container">
<div class="demo-header">
<span class="demo-title">Before / After</span>
<div class="demo-toggle">
<span class="demo-toggle-label active">Before</span>
<div class="demo-toggle-switch" data-demo="command-normalize"></div>
<span class="demo-toggle-label">After</span>
</div>
</div>
<div class="demo-viewport" data-state="before" id="command-normalize-viewport">
<div style="display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 260px;">
<div style="padding: 12px 16px; background: #f0f0f0; border-radius: 6px;">
<div style="font-size: 14px; font-weight: 600; margin-bottom: 4px;">Card One</div>
<div style="font-size: 13px; color: #888;">Some description text here</div>
</div>
<div style="padding: 18px 12px; background: #e8e8e8; border-radius: 12px;">
<div style="font-size: 16px; font-weight: 500; margin-bottom: 8px;">Card Two</div>
<div style="font-size: 14px; color: #666;">Different spacing and styles</div>
</div>
<div style="padding: 10px 20px; background: #f5f5f5; border-radius: 4px;">
<div style="font-size: 15px; font-weight: 700; margin-bottom: 2px;">Card Three</div>
<div style="font-size: 12px; color: #999;">Yet another variation</div>
</div>
</div>
</div>
<div class="demo-caption">Inconsistent styles → Systematic design tokens</div>
</div>
`;
}
+23 -8
View File
@@ -2,13 +2,13 @@
// COMMANDS - Rendering, workflow, and category logic
// ============================================
import { renderCommandDemo } from "./command-demos.js";
import {
commandCategories,
commandProcessSteps,
commandRelationships,
readyCommands,
} from "./data.js";
import { renderCommandDemo } from "./demo-renderer.js";
import { setupCommandDemoToggles } from "./demo-toggles.js";
let currentCommand = null;
@@ -25,28 +25,43 @@ export function renderWorkflowDiagram(allCommands, onSelectCommand) {
// Group commands by category for the sidebar
const groups = {
production: ['audit', 'normalize', 'adapt', 'clarify', 'harden', 'optimize', 'polish'],
aesthetic: ['bolder', 'quieter', 'animate', 'delight', 'colorize', 'simplify'],
system: ['extract', 'onboard']
production: [
"audit",
"normalize",
"adapt",
"clarify",
"harden",
"optimize",
"polish",
],
aesthetic: [
"bolder",
"quieter",
"animate",
"delight",
"colorize",
"simplify",
],
system: ["extract", "onboard"],
};
const renderNavItem = (id) => {
const ready = isCommandReady(id);
return `<button class="command-nav-item ${!ready ? 'coming-soon' : ''}" data-command="${id}">/${id}${!ready ? '<span class="coming-soon-badge">Soon</span>' : ''}</button>`;
return `<button class="command-nav-item ${!ready ? "coming-soon" : ""}" data-command="${id}">/${id}${!ready ? '<span class="coming-soon-badge">Soon</span>' : ""}</button>`;
};
container.innerHTML = `
<div class="command-nav-group">
<span class="command-nav-label">Production</span>
${groups.production.map(renderNavItem).join('')}
${groups.production.map(renderNavItem).join("")}
</div>
<div class="command-nav-group">
<span class="command-nav-label">Aesthetic</span>
${groups.aesthetic.map(renderNavItem).join('')}
${groups.aesthetic.map(renderNavItem).join("")}
</div>
<div class="command-nav-group">
<span class="command-nav-label">System</span>
${groups.system.map(renderNavItem).join('')}
${groups.system.map(renderNavItem).join("")}
</div>
`;
+129
View File
@@ -0,0 +1,129 @@
// ============================================
// DEMO RENDERER - Generic rendering for command and skill demos
// ============================================
import { getCommandDemo } from './demos/commands/index.js';
import { getSkillDemo } from './demos/skills/index.js';
/**
* Render a command demo
*/
export function renderCommandDemo(commandId) {
const demo = getCommandDemo(commandId);
if (!demo) {
return `
<div class="demo-container">
<div class="demo-viewport">
<div style="text-align: center; color: var(--color-ash); font-style: italic; padding: var(--spacing-lg);">
Visual demo for /${commandId} coming soon
</div>
</div>
</div>
`;
}
const viewportClass = demo.viewportClass ? ` ${demo.viewportClass}` : '';
return `
<div class="demo-container">
<div class="demo-header">
<div class="demo-toggle">
<span class="demo-toggle-label active">Before</span>
<div class="demo-toggle-switch" data-demo="command-${demo.id}"></div>
<span class="demo-toggle-label">After</span>
</div>
</div>
<div class="demo-viewport${viewportClass}" data-state="before" id="command-${demo.id}-viewport">
${demo.before}
</div>
<div class="demo-caption">${demo.caption}</div>
</div>
`;
}
/**
* Render a skill demo (with tabs if multiple demos)
*/
export function renderSkillDemo(skillId) {
const skill = getSkillDemo(skillId);
if (!skill || !skill.tabs || skill.tabs.length === 0) {
return `
<div class="demo-container">
<div class="demo-viewport">
<div style="text-align: center; color: var(--color-ash); padding: var(--spacing-xl);">
<p>Demo for ${skillId.replace(/-/g, ' ')} coming soon</p>
</div>
</div>
</div>
`;
}
const showTabs = skill.tabs.length > 1;
const tabs = showTabs ? skill.tabs.map((tab, i) => `
<button class="demo-tab ${i === 0 ? 'active' : ''}" data-demo-tab="${tab.id}" data-skill="${skillId}">
${tab.label}
</button>
`).join('') : '';
const panels = skill.tabs.map((tab, i) => `
<div class="demo-panel ${i === 0 ? 'active' : ''}" data-demo-panel="${tab.id}">
${renderSkillTabDemo(skillId, tab)}
</div>
`).join('');
return `
<div class="demo-tabbed-container">
${showTabs ? `<div class="demo-tabs">${tabs}</div>` : ''}
<div class="demo-panels">
${panels}
</div>
</div>
`;
}
/**
* Render a single skill tab demo
*/
function renderSkillTabDemo(skillId, tab) {
const hasToggle = tab.hasToggle !== false;
const demoId = `${skillId}-${tab.id}`;
return `
<div class="demo-container">
<div class="demo-header">
${hasToggle ? `
<div class="demo-toggle">
<span class="demo-toggle-label active">Before</span>
<div class="demo-toggle-switch" data-demo="${demoId}"></div>
<span class="demo-toggle-label">After</span>
</div>
` : ''}
</div>
<div class="demo-viewport" data-state="before" id="${demoId}-viewport">
${tab.before}
</div>
<div class="demo-caption">${tab.caption}</div>
</div>
`;
}
/**
* Setup demo tab switching
*/
export function setupDemoTabs() {
document.querySelectorAll('.demo-tab').forEach(tab => {
tab.addEventListener('click', () => {
const tabId = tab.dataset.demoTab;
const container = tab.closest('.demo-tabbed-container');
container.querySelectorAll('.demo-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
container.querySelectorAll('.demo-panel').forEach(p => p.classList.remove('active'));
container.querySelector(`[data-demo-panel="${tabId}"]`)?.classList.add('active');
});
});
}
+78 -344
View File
@@ -2,8 +2,18 @@
// DEMO TOGGLES - Handle before/after toggle interactions
// ============================================
import { getCommandDemo } from './demos/commands/index.js';
import { getSkillDemo } from './demos/skills/index.js';
/**
* Setup toggle handlers for skill demos
*/
export function setupDemoToggles() {
document.querySelectorAll('.demo-toggle-switch').forEach(toggle => {
// Skip if already has handler
if (toggle.dataset.initialized) return;
toggle.dataset.initialized = 'true';
toggle.addEventListener('click', () => {
const demoId = toggle.dataset.demo;
const isActive = toggle.classList.toggle('active');
@@ -22,8 +32,15 @@ export function setupDemoToggles() {
setupInteractiveButtons();
}
/**
* Setup toggle handlers for command demos
*/
export function setupCommandDemoToggles(allCommands, selectCommand) {
document.querySelectorAll('.command-demo-area .demo-toggle-switch').forEach(toggle => {
// Skip if already has handler
if (toggle.dataset.initialized) return;
toggle.dataset.initialized = 'true';
toggle.addEventListener('click', () => {
const demoId = toggle.dataset.demo;
const isActive = toggle.classList.toggle('active');
@@ -45,376 +62,93 @@ export function setupCommandDemoToggles(allCommands, selectCommand) {
});
}
/**
* Setup interactive demo buttons (like the "like" button)
*/
function setupInteractiveButtons() {
// Like button feedback demo
document.querySelectorAll('.int-fb-active[data-action="like"]').forEach(btn => {
if (btn.dataset.initialized) return;
btn.dataset.initialized = 'true';
btn.addEventListener('click', () => {
btn.classList.toggle('liked');
const label = btn.nextElementSibling;
if (label) {
label.textContent = btn.classList.contains('liked') ? 'Liked!' : 'Click me!';
label.textContent = btn.classList.contains('liked') ? 'Liked!' : 'Click to try!';
}
});
});
}
/**
* Handle skill demo toggle
*/
function handleDemoToggle(demoId, isAfter) {
const viewport = document.getElementById(`${demoId}-viewport`);
if (!viewport) return;
viewport.dataset.state = isAfter ? 'after' : 'before';
// Handle specific demo transformations
switch (demoId) {
// UX Writing
case 'uxwriting-error':
toggleUXError(viewport, isAfter);
break;
case 'uxwriting-buttons':
toggleUXButtons(viewport, isAfter);
break;
case 'uxwriting-empty':
toggleUXEmpty(viewport, isAfter);
break;
// Spatial Design
case 'spatial-grid':
toggleSpatialGrid(viewport, isAfter);
break;
case 'spatial-hierarchy':
toggleSpatialHierarchy(viewport, isAfter);
break;
case 'spatial-whitespace':
toggleSpatialWhitespace(viewport, isAfter);
break;
// Motion Design
case 'motion-stagger':
toggleMotionStagger(viewport, isAfter);
break;
case 'motion-micro':
toggleMotionMicro(viewport, isAfter);
break;
case 'motion-transition':
toggleMotionTransition(viewport, isAfter);
break;
// Typography
case 'typography-pairing':
toggleTypoPairing(viewport, isAfter);
break;
case 'typography-hierarchy':
toggleTypoHierarchy(viewport, isAfter);
break;
// Interaction Design
case 'interaction-states':
// No content change needed - CSS handles it
break;
case 'interaction-affordance':
toggleAffordance(viewport, isAfter);
break;
case 'interaction-feedback':
toggleFeedback(viewport, isAfter);
break;
// Color & Contrast
case 'color-palette':
toggleColorPalette(viewport, isAfter);
break;
case 'color-accent':
toggleColorAccent(viewport, isAfter);
break;
// Parse skill ID and tab ID from demoId (e.g., "ux-writing-errors")
const parts = demoId.split('-');
const tabId = parts.pop();
const skillId = parts.join('-');
const skill = getSkillDemo(skillId);
if (!skill) return;
const tab = skill.tabs.find(t => t.id === tabId);
if (!tab) return;
// Check for custom toggle handler
if (tab.onToggle) {
tab.onToggle(viewport, isAfter);
return;
}
}
// ========== UX WRITING ==========
function toggleUXError(viewport, isAfter) {
const demo = viewport.querySelector('.uxw-demo');
if (!demo) return;
if (isAfter) {
demo.className = 'uxw-demo uxw-error-after';
demo.innerHTML = `
<div class="uxw-error-icon">🔐</div>
<div class="uxw-error-title">You don't have access</div>
<div class="uxw-error-text">Your session may have expired. Sign in again to continue.</div>
<div class="uxw-error-action">Sign in →</div>
`;
} else {
demo.className = 'uxw-demo uxw-error-before';
demo.innerHTML = `
<div class="uxw-error-icon">⚠</div>
<div class="uxw-error-title">Error 403</div>
<div class="uxw-error-text">Access denied. Authentication failure occurred.</div>
`;
// Check for CSS class toggle
if (tab.beforeClass && tab.afterClass) {
const demo = viewport.firstElementChild;
if (demo) {
demo.className = isAfter ? tab.afterClass : tab.beforeClass;
}
return;
}
}
function toggleUXButtons(viewport, isAfter) {
const demo = viewport.querySelector('.uxw-demo');
if (!demo) return;
if (isAfter) {
demo.innerHTML = `
<div class="uxw-button-context">Delete account permanently?</div>
<div class="uxw-button-row">
<button class="uxw-btn uxw-btn-danger">Delete My Account</button>
<button class="uxw-btn uxw-btn-secondary">Keep Account</button>
</div>
`;
} else {
demo.innerHTML = `
<div class="uxw-button-context">Delete account permanently?</div>
<div class="uxw-button-row">
<button class="uxw-btn uxw-btn-primary">Submit</button>
<button class="uxw-btn uxw-btn-secondary">Cancel</button>
</div>
`;
}
}
function toggleUXEmpty(viewport, isAfter) {
const demo = viewport.querySelector('.uxw-demo');
if (!demo) return;
if (isAfter) {
demo.className = 'uxw-demo uxw-empty-after';
demo.innerHTML = `
<div class="uxw-empty-icon">📝</div>
<div class="uxw-empty-title">No projects yet</div>
<div class="uxw-empty-text">Create your first project to get started.</div>
<div class="uxw-empty-action"><button class="uxw-btn uxw-btn-primary">Create Project</button></div>
`;
} else {
demo.className = 'uxw-demo uxw-empty-before';
demo.innerHTML = `
<div class="uxw-empty-icon">📁</div>
<div class="uxw-empty-title">No items</div>
`;
}
}
// ========== SPATIAL DESIGN ==========
function toggleSpatialGrid(viewport, isAfter) {
const demo = viewport.querySelector('.spatial-demo');
if (!demo) return;
demo.className = isAfter ? 'spatial-demo spatial-grid-after' : 'spatial-demo spatial-grid-before';
// Reset inline styles on children
demo.querySelectorAll('.spatial-card-item').forEach((item, i) => {
item.style.width = isAfter ? '' : ['45%', '30%', '55%', '25%'][i];
});
}
function toggleSpatialHierarchy(viewport, isAfter) {
const demo = viewport.querySelector('.spatial-demo');
if (!demo) return;
demo.className = isAfter ? 'spatial-demo spatial-hierarchy-after' : 'spatial-demo spatial-hierarchy-before';
}
function toggleSpatialWhitespace(viewport, isAfter) {
const demo = viewport.querySelector('.spatial-demo');
if (!demo) return;
demo.className = isAfter ? 'spatial-demo spatial-whitespace-after' : 'spatial-demo spatial-whitespace-before';
}
// ========== MOTION DESIGN ==========
function toggleMotionStagger(viewport, isAfter) {
if (isAfter) {
// Trigger re-animation by cloning elements
const items = viewport.querySelectorAll('.motion-list-item');
items.forEach(item => {
const clone = item.cloneNode(true);
item.parentNode.replaceChild(clone, item);
});
}
}
function toggleMotionMicro(viewport, isAfter) {
const btn = viewport.querySelector('.motion-btn');
if (!btn) return;
btn.className = isAfter ? 'motion-btn motion-btn-after' : 'motion-btn motion-btn-before';
}
function toggleMotionTransition(viewport, isAfter) {
const card = viewport.querySelector('.motion-card');
if (!card) return;
card.className = isAfter ? 'motion-card motion-card-after' : 'motion-card motion-card-before';
if (isAfter) {
card.querySelector('.motion-card-icon').textContent = '✓';
card.querySelector('.motion-card-text').textContent = 'Order Confirmed';
} else {
card.querySelector('.motion-card-icon').textContent = '📦';
card.querySelector('.motion-card-text').textContent = 'Order Placed';
}
}
// ========== TYPOGRAPHY ==========
function toggleTypoPairing(viewport, isAfter) {
const demo = viewport.querySelector('.typo-demo');
if (!demo) return;
demo.className = isAfter ? 'typo-demo typo-pairing-after' : 'typo-demo typo-pairing-before';
}
function toggleTypoHierarchy(viewport, isAfter) {
const demo = viewport.querySelector('.typo-demo');
if (!demo) return;
demo.className = isAfter ? 'typo-demo typo-hierarchy-after' : 'typo-demo typo-hierarchy-before';
}
// ========== INTERACTION DESIGN ==========
function toggleAffordance(viewport, isAfter) {
const demo = viewport.querySelector('.int-demo');
if (!demo) return;
demo.className = isAfter ? 'int-demo int-affordance-after' : 'int-demo int-affordance-before';
}
function toggleFeedback(viewport, isAfter) {
const demo = viewport.querySelector('.int-demo');
if (!demo) return;
if (isAfter) {
demo.className = 'int-demo int-feedback-after';
demo.innerHTML = `
<button class="int-fb-btn int-fb-active" data-action="like">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</button>
<span class="int-fb-label">Click to try!</span>
`;
// Re-setup interactive button
setupInteractiveButtons();
} else {
demo.className = 'int-demo int-feedback-before';
demo.innerHTML = `
<button class="int-fb-btn int-fb-silent">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</button>
<span class="int-fb-label">Click — nothing happens</span>
`;
}
}
// ========== COLOR & CONTRAST ==========
function toggleColorPalette(viewport, isAfter) {
const demo = viewport.querySelector('.color-demo');
if (!demo) return;
demo.className = isAfter ? 'color-demo color-palette-after' : 'color-demo color-palette-before';
}
function toggleColorAccent(viewport, isAfter) {
const demo = viewport.querySelector('.color-demo');
if (!demo) return;
demo.className = isAfter ? 'color-demo color-accent-after' : 'color-demo color-accent-before';
}
// ========== COMMAND DEMOS ==========
function handleCommandDemoToggle(demoId, isAfter) {
switch (demoId) {
case 'command-bolder':
handleBolderDemo(isAfter);
break;
case 'command-animate':
handleAnimateDemo(isAfter);
break;
case 'command-normalize':
handleNormalizeDemo(isAfter);
break;
}
}
function handleBolderDemo(isAfter) {
const viewport = document.getElementById('command-bolder-viewport');
if (!viewport) return;
if (isAfter) {
viewport.innerHTML = `
<div style="text-align: center; padding: var(--s-lg); max-width: 320px;">
<div style="font-family: var(--font-display); font-size: 2.5rem; font-weight: 300; font-style: italic; margin-bottom: 12px; color: var(--c-text); line-height: 1;">Introducing Our Product</div>
<div style="font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.15em; color: var(--c-accent); margin-bottom: 24px;">A solution for modern teams</div>
<button style="padding: 14px 32px; background: var(--c-text); color: var(--c-paper); border: none; font-size: 0.9375rem; font-weight: 500; letter-spacing: 0.02em;">Learn More</button>
</div>
`;
} else {
viewport.innerHTML = `
<div style="text-align: center; padding: var(--s-md); max-width: 280px;">
<div style="font-size: 1.125rem; font-weight: 500; margin-bottom: 8px; color: var(--c-charcoal);">Introducing Our Product</div>
<div style="font-size: 0.875rem; color: var(--c-ash); margin-bottom: 16px;">A solution for modern teams</div>
<button style="padding: 8px 16px; background: var(--c-mist); color: var(--c-charcoal); border: none; border-radius: 4px; font-size: 0.875rem;">Learn More</button>
</div>
`;
}
}
function handleAnimateDemo(isAfter) {
const viewport = document.getElementById('command-animate-viewport');
if (viewport) {
viewport.dataset.state = isAfter ? 'after' : 'before';
if (isAfter) {
const page = viewport.querySelector('.motion-demo-page');
if (page) {
const clone = page.cloneNode(true);
page.parentNode.replaceChild(clone, page);
}
// HTML swap
if (tab.after && tab.before) {
viewport.innerHTML = isAfter ? tab.after : tab.before;
// Run after-render callback if exists
if (isAfter && tab.onAfterRender) {
tab.onAfterRender();
}
}
}
function handleNormalizeDemo(isAfter) {
const viewport = document.getElementById('command-normalize-viewport');
/**
* Handle command demo toggle
*/
function handleCommandDemoToggle(demoId, isAfter) {
// Extract command ID from demoId (e.g., "command-normalize" -> "normalize")
const commandId = demoId.replace('command-', '');
const demo = getCommandDemo(commandId);
if (!demo) return;
const viewport = document.getElementById(`${demoId}-viewport`);
if (!viewport) return;
if (isAfter) {
viewport.innerHTML = `
<div style="display: flex; flex-direction: column; gap: var(--s-sm); width: 100%; max-width: 260px;">
<div style="padding: var(--s-md); background: var(--c-bg); border-radius: 4px; border-left: 2px solid var(--c-accent);">
<div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--c-text);">Card One</div>
<div style="font-size: 0.8125rem; color: var(--c-ash);">Consistent description text</div>
</div>
<div style="padding: var(--s-md); background: var(--c-bg); border-radius: 4px; border-left: 2px solid var(--c-accent);">
<div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--c-text);">Card Two</div>
<div style="font-size: 0.8125rem; color: var(--c-ash);">Same spacing and styles</div>
</div>
<div style="padding: var(--s-md); background: var(--c-bg); border-radius: 4px; border-left: 2px solid var(--c-accent);">
<div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--c-text);">Card Three</div>
<div style="font-size: 0.8125rem; color: var(--c-ash);">Unified design system</div>
</div>
</div>
`;
} else {
viewport.innerHTML = `
<div style="display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 260px;">
<div style="padding: 12px 16px; background: #f0f0f0; border-radius: 6px;">
<div style="font-size: 14px; font-weight: 600; margin-bottom: 4px;">Card One</div>
<div style="font-size: 13px; color: #888;">Some description text here</div>
</div>
<div style="padding: 18px 12px; background: #e8e8e8; border-radius: 12px;">
<div style="font-size: 16px; font-weight: 500; margin-bottom: 8px;">Card Two</div>
<div style="font-size: 14px; color: #666;">Different spacing and styles</div>
</div>
<div style="padding: 10px 20px; background: #f5f5f5; border-radius: 4px;">
<div style="font-size: 15px; font-weight: 700; margin-bottom: 2px;">Card Three</div>
<div style="font-size: 12px; color: #999;">Yet another variation</div>
</div>
</div>
`;
viewport.dataset.state = isAfter ? 'after' : 'before';
// Check for custom toggle handler
if (demo.onToggle) {
demo.onToggle(viewport, isAfter);
return;
}
// HTML swap
if (demo.after && demo.before) {
viewport.innerHTML = isAfter ? demo.after : demo.before;
}
}
+36
View File
@@ -0,0 +1,36 @@
// Animate command demo - shows static page becoming choreographed
export default {
id: 'animate',
caption: 'Static page → Choreographed page load',
// This demo uses CSS animations triggered by data-state attribute
// The before/after states are the same HTML, animation is CSS-driven
before: `
<div class="motion-demo-page" style="opacity: 1;">
<div class="motion-demo-page-hero" style="opacity: 1; transform: none;">Hero</div>
<div class="motion-demo-page-content">
<div class="motion-demo-page-heading" style="opacity: 1; transform: none;"></div>
<div class="motion-demo-page-text" style="opacity: 1; transform: none;"></div>
<div class="motion-demo-page-text" style="opacity: 1; transform: none;"></div>
<div class="motion-demo-page-text" style="opacity: 1; transform: none;"></div>
</div>
</div>
`,
after: null, // Uses CSS animation, no HTML change needed
// Custom toggle handler for animation re-triggering
onToggle: (viewport, isAfter) => {
viewport.dataset.state = isAfter ? 'after' : 'before';
if (isAfter) {
// Re-trigger animation by cloning the element
const page = viewport.querySelector('.motion-demo-page');
if (page) {
const clone = page.cloneNode(true);
page.parentNode.replaceChild(clone, page);
}
}
},
viewportClass: 'demo-viewport-dark'
};
+21
View File
@@ -0,0 +1,21 @@
// Bolder command demo - shows timid design becoming bold and confident
export default {
id: 'bolder',
caption: 'Timid design → Bold, confident design',
before: `
<div style="text-align: center; padding: var(--spacing-md); max-width: 280px;">
<div style="font-size: 1.125rem; font-weight: 500; margin-bottom: 8px; color: var(--color-charcoal);">Introducing Our Product</div>
<div style="font-size: 0.875rem; color: var(--color-ash); margin-bottom: 16px;">A solution for modern teams</div>
<button style="padding: 8px 16px; background: var(--color-mist); color: var(--color-charcoal); border: none; border-radius: 4px; font-size: 0.875rem;">Learn More</button>
</div>
`,
after: `
<div style="text-align: center; padding: var(--spacing-lg); max-width: 320px;">
<div style="font-family: var(--font-display); font-size: 2.5rem; font-weight: 300; font-style: italic; margin-bottom: 12px; color: var(--color-ink); line-height: 1;">Introducing Our Product</div>
<div style="font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.15em; color: var(--color-accent); margin-bottom: 24px;">A solution for modern teams</div>
<button style="padding: 14px 32px; background: var(--color-ink); color: var(--color-paper); border: none; font-size: 0.9375rem; font-weight: 500; letter-spacing: 0.02em;">Learn More</button>
</div>
`
};
+15
View File
@@ -0,0 +1,15 @@
// Command demos registry
import animate from "./animate.js";
import bolder from "./bolder.js";
import normalize from "./normalize.js";
export const commandDemos = {
normalize,
bolder,
animate,
};
export function getCommandDemo(commandId) {
return commandDemos[commandId] || null;
}
+39
View File
@@ -0,0 +1,39 @@
// Normalize command demo - shows inconsistent styles becoming systematic
export default {
id: 'normalize',
caption: 'Inconsistent styles → Systematic design tokens',
before: `
<div style="display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 260px;">
<div style="padding: 12px 16px; background: #f0f0f0; border-radius: 6px;">
<div style="font-size: 14px; font-weight: 600; margin-bottom: 4px;">Card One</div>
<div style="font-size: 13px; color: #888;">Some description text here</div>
</div>
<div style="padding: 18px 12px; background: #e8e8e8; border-radius: 12px;">
<div style="font-size: 16px; font-weight: 500; margin-bottom: 8px;">Card Two</div>
<div style="font-size: 14px; color: #666;">Different spacing and styles</div>
</div>
<div style="padding: 10px 20px; background: #f5f5f5; border-radius: 4px;">
<div style="font-size: 15px; font-weight: 700; margin-bottom: 2px;">Card Three</div>
<div style="font-size: 12px; color: #999;">Yet another variation</div>
</div>
</div>
`,
after: `
<div style="display: flex; flex-direction: column; gap: var(--spacing-sm); width: 100%; max-width: 260px;">
<div style="padding: var(--spacing-md); background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
<div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--color-ink);">Card One</div>
<div style="font-size: 0.8125rem; color: var(--color-ash);">Consistent description text</div>
</div>
<div style="padding: var(--spacing-md); background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
<div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--color-ink);">Card Two</div>
<div style="font-size: 0.8125rem; color: var(--color-ash);">Same spacing and styles</div>
</div>
<div style="padding: var(--spacing-md); background: var(--color-paper); border: 1px solid var(--color-mist); border-radius: 6px;">
<div style="font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; color: var(--color-ink);">Card Three</div>
<div style="font-size: 0.8125rem; color: var(--color-ash);">Unified design system</div>
</div>
</div>
`
};
+3
View File
@@ -0,0 +1,3 @@
// Main demos registry - exports all demos
export { commandDemos, getCommandDemo } from "./commands/index.js";
export { getSkillDemo, skillDemos } from "./skills/index.js";
@@ -0,0 +1,65 @@
// Color and Contrast skill demos
export default {
id: 'color-and-contrast',
tabs: [
{
id: 'palette',
label: 'Color Harmony',
caption: 'Clashing colors vs harmonious palette',
beforeClass: 'color-demo color-palette-before',
afterClass: 'color-demo color-palette-after',
before: `
<div class="color-demo color-palette-before">
<div class="color-swatch swatch-1"></div>
<div class="color-swatch swatch-2"></div>
<div class="color-swatch swatch-3"></div>
<div class="color-swatch swatch-4"></div>
<div class="color-swatch swatch-5"></div>
<div class="color-card">
<span class="card-title">Title</span>
<span class="card-subtitle">Subtitle</span>
<button class="card-btn">Action</button>
</div>
</div>
`,
after: null // Uses CSS class toggle
},
{
id: 'accent',
label: 'Strategic Accent',
caption: 'Monochrome monotony vs strategic accent',
beforeClass: 'color-demo color-accent-before',
afterClass: 'color-demo color-accent-after',
before: `
<div class="color-demo color-accent-before">
<div class="color-accent-card">
<div class="color-accent-title">Premium Plan</div>
<div class="color-accent-text">Unlock all features and get priority support.</div>
<button class="color-accent-btn">Upgrade Now</button>
</div>
</div>
`,
after: null // Uses CSS class toggle
},
{
id: 'contrast',
label: 'Contrast Ratios',
caption: 'Accessibility failures vs WCAG compliance',
hasToggle: false, // Static comparison
before: `
<div class="color-demo color-contrast-static">
<div class="contrast-example contrast-fail">
<span class="contrast-badge">Fails WCAG</span>
<div class="contrast-text">Hard to Read</div>
<div class="contrast-ratio">2.5:1</div>
</div>
<div class="contrast-example contrast-pass">
<span class="contrast-badge">Passes AAA</span>
<div class="contrast-text">Easy to Read</div>
<div class="contrast-ratio">12.6:1</div>
</div>
</div>
`
}
]
};
+23
View File
@@ -0,0 +1,23 @@
// Skill demos registry
import colorAndContrast from "./color-and-contrast.js";
import interactionDesign from "./interaction-design.js";
import motionDesign from "./motion-design.js";
import responsiveDesign from "./responsive-design.js";
import spatialDesign from "./spatial-design.js";
import typography from "./typography.js";
import uxWriting from "./ux-writing.js";
export const skillDemos = {
"ux-writing": uxWriting,
"spatial-design": spatialDesign,
"motion-design": motionDesign,
typography: typography,
"interaction-design": interactionDesign,
"color-and-contrast": colorAndContrast,
"responsive-design": responsiveDesign,
};
export function getSkillDemo(skillId) {
return skillDemos[skillId] || null;
}
@@ -0,0 +1,76 @@
// Interaction Design skill demos
export default {
id: 'interaction-design',
tabs: [
{
id: 'states',
label: 'Button States',
caption: 'Missing states vs complete interaction feedback',
// This demo shows both states side-by-side, no toggle needed
hasToggle: false,
before: `
<div class="int-demo int-states-demo">
<div class="int-state-row">
<span class="int-state-label">Poor</span>
<button class="int-btn int-btn-poor">Click Me</button>
</div>
<div class="int-state-row">
<span class="int-state-label">Good</span>
<button class="int-btn int-btn-good">Click Me</button>
</div>
</div>
`
},
{
id: 'affordance',
label: 'Affordances',
caption: 'Unclear actions vs obvious clickability',
beforeClass: 'int-demo int-affordance-before',
afterClass: 'int-demo int-affordance-after',
before: `
<div class="int-demo int-affordance-before">
<div class="int-aff-item int-aff-poor">
<span>Learn more</span>
</div>
<div class="int-aff-item int-aff-poor">
<span>Settings</span>
</div>
</div>
`,
after: null // Uses CSS class toggle
},
{
id: 'feedback',
label: 'Feedback',
caption: 'Silent actions vs immediate confirmation',
before: `
<div class="int-demo int-feedback-before">
<button class="int-fb-btn int-fb-silent">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</button>
<span class="int-fb-label">Click — nothing happens</span>
</div>
`,
after: `
<div class="int-demo int-feedback-after">
<button class="int-fb-btn int-fb-active" data-action="like">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</button>
<span class="int-fb-label">Click to try!</span>
</div>
`,
onAfterRender: () => {
// Setup interactive like button
document.querySelectorAll('.int-fb-active[data-action="like"]').forEach(btn => {
btn.addEventListener('click', () => {
btn.classList.toggle('liked');
const label = btn.nextElementSibling;
if (label) {
label.textContent = btn.classList.contains('liked') ? 'Liked!' : 'Click to try!';
}
});
});
}
}
]
};
+66
View File
@@ -0,0 +1,66 @@
// Motion Design skill demos
export default {
id: 'motion-design',
tabs: [
{
id: 'stagger',
label: 'Staggered Reveal',
caption: 'Instant appearance vs orchestrated reveal',
before: `
<div class="motion-demo motion-stagger-demo">
<div class="motion-list-item"><span class="motion-dot"></span>Dashboard</div>
<div class="motion-list-item"><span class="motion-dot"></span>Analytics</div>
<div class="motion-list-item"><span class="motion-dot"></span>Settings</div>
<div class="motion-list-item"><span class="motion-dot"></span>Profile</div>
</div>
`,
after: null, // CSS animation triggered by data-state
onToggle: (viewport, isAfter) => {
if (isAfter) {
// Re-trigger animation by cloning elements
const items = viewport.querySelectorAll('.motion-list-item');
items.forEach(item => {
const clone = item.cloneNode(true);
item.parentNode.replaceChild(clone, item);
});
}
}
},
{
id: 'micro',
label: 'Micro-interactions',
caption: 'Static button vs responsive feedback',
before: `
<div class="motion-demo motion-micro-demo">
<button class="motion-btn motion-btn-before">Add to Cart</button>
</div>
`,
after: `
<div class="motion-demo motion-micro-demo">
<button class="motion-btn motion-btn-after">Add to Cart</button>
</div>
`
},
{
id: 'transition',
label: 'State Changes',
caption: 'Jarring change vs smooth transition',
before: `
<div class="motion-demo motion-transition-demo">
<div class="motion-card motion-card-before">
<div class="motion-card-icon">📦</div>
<div class="motion-card-text">Order Placed</div>
</div>
</div>
`,
after: `
<div class="motion-demo motion-transition-demo">
<div class="motion-card motion-card-after">
<div class="motion-card-icon">✓</div>
<div class="motion-card-text">Order Confirmed</div>
</div>
</div>
`
}
]
};
@@ -0,0 +1,88 @@
// Responsive Design skill demos
export default {
id: 'responsive-design',
tabs: [
{
id: 'touch',
label: 'Touch Targets',
caption: 'Tiny targets vs accessible touch areas',
hasToggle: false, // Static comparison
before: `
<div class="resp-demo resp-touch-demo">
<div class="resp-touch-row">
<span class="resp-label">Too Small</span>
<div class="resp-touch-targets resp-touch-bad">
<button>×</button>
<button></button>
<button>+</button>
</div>
</div>
<div class="resp-touch-row">
<span class="resp-label">Accessible</span>
<div class="resp-touch-targets resp-touch-good">
<button>×</button>
<button></button>
<button>+</button>
</div>
</div>
</div>
`
},
{
id: 'fluid',
label: 'Fluid Layout',
caption: 'Fixed breakage vs fluid adaptation',
hasToggle: false, // Static comparison
before: `
<div class="resp-demo resp-fluid-demo">
<div class="resp-fluid-container">
<div class="resp-fluid-fixed">
<span>Fixed 400px</span>
<div class="resp-fluid-bar" style="width: 400px; max-width: 100%;"></div>
</div>
<div class="resp-fluid-adaptive">
<span>Fluid 80%</span>
<div class="resp-fluid-bar" style="width: 80%;"></div>
</div>
</div>
</div>
`
},
{
id: 'adapt',
label: 'Adaptive Content',
caption: 'Same layout vs optimized for context',
hasToggle: false, // Static comparison
before: `
<div class="resp-demo resp-adapt-demo">
<div class="resp-device resp-device-mobile">
<div class="resp-device-screen">
<div class="resp-block resp-header"></div>
<div class="resp-block resp-content"></div>
</div>
<span>Mobile</span>
</div>
<div class="resp-device resp-device-tablet">
<div class="resp-device-screen">
<div class="resp-block resp-header"></div>
<div class="resp-block-row">
<div class="resp-block resp-content"></div>
<div class="resp-block resp-content"></div>
</div>
</div>
<span>Tablet</span>
</div>
<div class="resp-device resp-device-desktop">
<div class="resp-device-screen">
<div class="resp-block-row">
<div class="resp-block resp-sidebar"></div>
<div class="resp-block resp-content"></div>
</div>
</div>
<span>Desktop</span>
</div>
</div>
`
}
]
};
+59
View File
@@ -0,0 +1,59 @@
// Spatial Design skill demos
export default {
id: 'spatial-design',
tabs: [
{
id: 'grid',
label: 'Grid Systems',
caption: 'Chaotic placement vs intentional grid alignment',
before: `
<div class="spatial-demo spatial-grid-before">
<div class="spatial-card-item" style="width: 45%;">Card One</div>
<div class="spatial-card-item" style="width: 30%;">Card Two</div>
<div class="spatial-card-item" style="width: 55%;">Card Three</div>
<div class="spatial-card-item" style="width: 25%;">Card Four</div>
</div>
`,
after: `
<div class="spatial-demo spatial-grid-after">
<div class="spatial-card-item">Card One</div>
<div class="spatial-card-item">Card Two</div>
<div class="spatial-card-item">Card Three</div>
<div class="spatial-card-item">Card Four</div>
</div>
`
},
{
id: 'hierarchy',
label: 'Visual Weight',
caption: 'Equal weight vs clear visual priority',
beforeClass: 'spatial-demo spatial-hierarchy-before',
afterClass: 'spatial-demo spatial-hierarchy-after',
before: `
<div class="spatial-demo spatial-hierarchy-before">
<div class="spatial-h-title">Welcome Back</div>
<div class="spatial-h-subtitle">Dashboard</div>
<div class="spatial-h-cta">View Reports</div>
<div class="spatial-h-link">Settings</div>
</div>
`,
after: null // Uses CSS class toggle
},
{
id: 'whitespace',
label: 'Breathing Room',
caption: 'Cramped elements vs comfortable spacing',
beforeClass: 'spatial-demo spatial-whitespace-before',
afterClass: 'spatial-demo spatial-whitespace-after',
before: `
<div class="spatial-demo spatial-whitespace-before">
<div class="spatial-ws-title">Premium Plan</div>
<div class="spatial-ws-price">$29/mo</div>
<div class="spatial-ws-features">Unlimited projects • Priority support • Advanced analytics</div>
<button class="spatial-ws-btn">Upgrade Now</button>
</div>
`,
after: null // Uses CSS class toggle
}
]
};
+35
View File
@@ -0,0 +1,35 @@
// Typography skill demos
export default {
id: 'typography',
tabs: [
{
id: 'pairing',
label: 'Font Pairing',
caption: 'Generic system fonts vs distinctive pairing',
beforeClass: 'typo-demo typo-pairing-before',
afterClass: 'typo-demo typo-pairing-after',
before: `
<div class="typo-demo typo-pairing-before">
<div class="typo-heading">Welcome to the Future</div>
<div class="typo-body">Experience innovation like never before with our cutting-edge platform designed for modern teams.</div>
</div>
`,
after: null // Uses CSS class toggle
},
{
id: 'hierarchy',
label: 'Scale & Hierarchy',
caption: 'Flat sizing vs dramatic scale contrast',
beforeClass: 'typo-demo typo-hierarchy-before',
afterClass: 'typo-demo typo-hierarchy-after',
before: `
<div class="typo-demo typo-hierarchy-before">
<div class="typo-h1">Article Title</div>
<div class="typo-meta">Published January 2025</div>
<div class="typo-p">This is the body text of the article containing the main content and ideas.</div>
</div>
`,
after: null // Uses CSS class toggle
}
]
};
+68
View File
@@ -0,0 +1,68 @@
// UX Writing skill demos
export default {
id: 'ux-writing',
tabs: [
{
id: 'errors',
label: 'Error Messages',
caption: 'Technical jargon vs human, actionable guidance',
before: `
<div class="uxw-demo uxw-error-before">
<div class="uxw-error-icon">⚠</div>
<div class="uxw-error-title">Error 403</div>
<div class="uxw-error-text">Access denied. Authentication failure occurred.</div>
</div>
`,
after: `
<div class="uxw-demo uxw-error-after">
<div class="uxw-error-icon">🔐</div>
<div class="uxw-error-title">You don't have access</div>
<div class="uxw-error-text">Your session may have expired. Sign in again to continue.</div>
<div class="uxw-error-action">Sign in →</div>
</div>
`
},
{
id: 'buttons',
label: 'Button Labels',
caption: 'Vague labels vs clear, specific actions',
before: `
<div class="uxw-demo uxw-buttons-before">
<div class="uxw-button-context">Delete account permanently?</div>
<div class="uxw-button-row">
<button class="uxw-btn uxw-btn-primary">Submit</button>
<button class="uxw-btn uxw-btn-secondary">Cancel</button>
</div>
</div>
`,
after: `
<div class="uxw-demo uxw-buttons-after">
<div class="uxw-button-context">Delete account permanently?</div>
<div class="uxw-button-row">
<button class="uxw-btn uxw-btn-danger">Delete My Account</button>
<button class="uxw-btn uxw-btn-secondary">Keep Account</button>
</div>
</div>
`
},
{
id: 'empty',
label: 'Empty States',
caption: 'Blank nothing vs helpful, encouraging guidance',
before: `
<div class="uxw-demo uxw-empty-before">
<div class="uxw-empty-icon">📁</div>
<div class="uxw-empty-title">No items</div>
</div>
`,
after: `
<div class="uxw-demo uxw-empty-after">
<div class="uxw-empty-icon">📝</div>
<div class="uxw-empty-title">No projects yet</div>
<div class="uxw-empty-text">Create your first project to get started.</div>
<div class="uxw-empty-action"><button class="uxw-btn uxw-btn-primary">Create Project</button></div>
</div>
`
}
]
};
-386
View File
@@ -1,386 +0,0 @@
// ============================================
// SKILL DEMOS - Interactive before/after demonstrations
// Each skill has multiple demos selectable via tabs
// ============================================
// Demo definitions for each skill
const skillDemoDefinitions = {
'ux-writing': [
{ id: 'errors', label: 'Error Messages', demo: 'uxwriting-error' },
{ id: 'buttons', label: 'Button Labels', demo: 'uxwriting-buttons' },
{ id: 'empty', label: 'Empty States', demo: 'uxwriting-empty' }
],
'spatial-design': [
{ id: 'grid', label: 'Grid Systems', demo: 'spatial-grid' },
{ id: 'hierarchy', label: 'Visual Weight', demo: 'spatial-hierarchy' },
{ id: 'whitespace', label: 'Breathing Room', demo: 'spatial-whitespace' }
],
'motion-design': [
{ id: 'stagger', label: 'Staggered Reveal', demo: 'motion-stagger' },
{ id: 'micro', label: 'Micro-interactions', demo: 'motion-micro' },
{ id: 'transition', label: 'State Changes', demo: 'motion-transition' }
],
'typography': [
{ id: 'pairing', label: 'Font Pairing', demo: 'typography-pairing' },
{ id: 'hierarchy', label: 'Scale & Hierarchy', demo: 'typography-hierarchy' }
],
'interaction-design': [
{ id: 'states', label: 'Button States', demo: 'interaction-states' },
{ id: 'affordance', label: 'Affordances', demo: 'interaction-affordance' },
{ id: 'feedback', label: 'Feedback', demo: 'interaction-feedback' }
],
'color-and-contrast': [
{ id: 'palette', label: 'Color Harmony', demo: 'color-palette' },
{ id: 'accent', label: 'Strategic Accent', demo: 'color-accent' },
{ id: 'contrast', label: 'Contrast Ratios', demo: 'color-contrast' }
],
'responsive-design': [
{ id: 'touch', label: 'Touch Targets', demo: 'responsive-touch' },
{ id: 'fluid', label: 'Fluid Layout', demo: 'responsive-fluid' },
{ id: 'adapt', label: 'Adaptive Content', demo: 'responsive-adapt' }
]
};
export function renderSkillDemo(skillId) {
const demos = skillDemoDefinitions[skillId];
if (!demos || demos.length === 0) {
return renderGenericDemo(skillId);
}
const showTabs = demos.length > 1;
const tabs = showTabs ? demos.map((d, i) => `
<button class="demo-tab ${i === 0 ? 'active' : ''}" data-demo-tab="${d.id}" data-skill="${skillId}">
${d.label}
</button>
`).join('') : '';
const demoContents = demos.map((d, i) => `
<div class="demo-panel ${i === 0 ? 'active' : ''}" data-demo-panel="${d.id}">
${renderDemoContent(skillId, d)}
</div>
`).join('');
return `
<div class="demo-tabbed-container">
${showTabs ? `<div class="demo-tabs">${tabs}</div>` : ''}
<div class="demo-panels">
${demoContents}
</div>
</div>
`;
}
function renderDemoContent(_skillId, demoConfig) {
const demoId = demoConfig.demo;
const noToggle = ['color-contrast', 'responsive-touch', 'responsive-fluid', 'responsive-adapt'];
const hasToggle = !noToggle.includes(demoId);
return `
<div class="demo-container">
<div class="demo-header">
${hasToggle ? `
<div class="demo-toggle">
<span class="demo-toggle-label active">Before</span>
<div class="demo-toggle-switch" data-demo="${demoId}"></div>
<span class="demo-toggle-label">After</span>
</div>
` : ''}
</div>
<div class="demo-viewport" data-state="before" id="${demoId}-viewport">
${getDemoInitialContent(demoId)}
</div>
<div class="demo-caption">${getDemoCaption(demoId)}</div>
</div>
`;
}
function getDemoCaption(demoId) {
const captions = {
// UX Writing
'uxwriting-error': 'Technical jargon vs human, actionable guidance',
'uxwriting-buttons': 'Vague labels vs clear, specific actions',
'uxwriting-empty': 'Blank nothing vs helpful, encouraging guidance',
// Spatial Design
'spatial-grid': 'Chaotic placement vs intentional grid alignment',
'spatial-hierarchy': 'Equal weight vs clear visual priority',
'spatial-whitespace': 'Cramped elements vs comfortable spacing',
// Motion Design
'motion-stagger': 'Instant appearance vs orchestrated reveal',
'motion-micro': 'Static button vs responsive feedback',
'motion-transition': 'Jarring change vs smooth transition',
// Typography
'typography-pairing': 'Generic system fonts vs distinctive pairing',
'typography-hierarchy': 'Flat sizing vs dramatic scale contrast',
// Interaction Design
'interaction-states': 'Missing states vs complete interaction feedback',
'interaction-affordance': 'Unclear actions vs obvious clickability',
'interaction-feedback': 'Silent actions vs immediate confirmation',
// Color & Contrast
'color-palette': 'Clashing colors vs harmonious palette',
'color-accent': 'Monochrome monotony vs strategic accent',
'color-contrast': 'Accessibility failures vs WCAG compliance',
// Responsive Design
'responsive-touch': 'Tiny targets vs accessible touch areas',
'responsive-fluid': 'Fixed breakage vs fluid adaptation',
'responsive-adapt': 'Same layout vs optimized for context'
};
return captions[demoId] || '';
}
function getDemoInitialContent(demoId) {
const contents = {
// ========== UX WRITING ==========
'uxwriting-error': `
<div class="uxw-demo uxw-error-before">
<div class="uxw-error-icon">⚠</div>
<div class="uxw-error-title">Error 403</div>
<div class="uxw-error-text">Access denied. Authentication failure occurred.</div>
</div>
`,
'uxwriting-buttons': `
<div class="uxw-demo uxw-buttons-before">
<div class="uxw-button-row">
<button class="uxw-btn uxw-btn-primary">Submit</button>
<button class="uxw-btn uxw-btn-secondary">Cancel</button>
</div>
<div class="uxw-button-context">Delete account permanently?</div>
</div>
`,
'uxwriting-empty': `
<div class="uxw-demo uxw-empty-before">
<div class="uxw-empty-icon">📁</div>
<div class="uxw-empty-title">No items</div>
</div>
`,
// ========== SPATIAL DESIGN ==========
'spatial-grid': `
<div class="spatial-demo spatial-grid-before">
<div class="spatial-card-item" style="width: 45%;">Card One</div>
<div class="spatial-card-item" style="width: 30%;">Card Two</div>
<div class="spatial-card-item" style="width: 55%;">Card Three</div>
<div class="spatial-card-item" style="width: 25%;">Card Four</div>
</div>
`,
'spatial-hierarchy': `
<div class="spatial-demo spatial-hierarchy-before">
<div class="spatial-h-title">Welcome Back</div>
<div class="spatial-h-subtitle">Dashboard</div>
<div class="spatial-h-cta">View Reports</div>
<div class="spatial-h-link">Settings</div>
</div>
`,
'spatial-whitespace': `
<div class="spatial-demo spatial-whitespace-before">
<div class="spatial-ws-title">Premium Plan</div>
<div class="spatial-ws-price">$29/mo</div>
<div class="spatial-ws-features">Unlimited projects • Priority support • Advanced analytics</div>
<button class="spatial-ws-btn">Upgrade Now</button>
</div>
`,
// ========== MOTION DESIGN ==========
'motion-stagger': `
<div class="motion-demo motion-stagger-demo">
<div class="motion-list-item"><span class="motion-dot"></span>Dashboard</div>
<div class="motion-list-item"><span class="motion-dot"></span>Analytics</div>
<div class="motion-list-item"><span class="motion-dot"></span>Settings</div>
<div class="motion-list-item"><span class="motion-dot"></span>Profile</div>
</div>
`,
'motion-micro': `
<div class="motion-demo motion-micro-demo">
<button class="motion-btn motion-btn-before">Add to Cart</button>
</div>
`,
'motion-transition': `
<div class="motion-demo motion-transition-demo">
<div class="motion-card motion-card-before">
<div class="motion-card-icon">📦</div>
<div class="motion-card-text">Order Placed</div>
</div>
</div>
`,
// ========== TYPOGRAPHY ==========
'typography-pairing': `
<div class="typo-demo typo-pairing-before">
<div class="typo-heading">Welcome to the Future</div>
<div class="typo-body">Experience innovation like never before with our cutting-edge platform designed for modern teams.</div>
</div>
`,
'typography-hierarchy': `
<div class="typo-demo typo-hierarchy-before">
<div class="typo-h1">Article Title</div>
<div class="typo-meta">Published January 2025</div>
<div class="typo-p">This is the body text of the article containing the main content and ideas.</div>
</div>
`,
// ========== INTERACTION DESIGN ==========
'interaction-states': `
<div class="int-demo int-states-demo">
<div class="int-state-row">
<span class="int-state-label">Poor</span>
<button class="int-btn int-btn-poor">Click Me</button>
</div>
<div class="int-state-row">
<span class="int-state-label">Good</span>
<button class="int-btn int-btn-good">Click Me</button>
</div>
</div>
`,
'interaction-affordance': `
<div class="int-demo int-affordance-before">
<div class="int-aff-item int-aff-poor">
<span>Learn more</span>
</div>
<div class="int-aff-item int-aff-poor">
<span>Settings</span>
</div>
</div>
`,
'interaction-feedback': `
<div class="int-demo int-feedback-before">
<button class="int-fb-btn int-fb-silent">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</button>
<span class="int-fb-label">Click — nothing happens</span>
</div>
`,
// ========== COLOR & CONTRAST ==========
'color-palette': `
<div class="color-demo color-palette-before">
<div class="color-swatch swatch-1"></div>
<div class="color-swatch swatch-2"></div>
<div class="color-swatch swatch-3"></div>
<div class="color-swatch swatch-4"></div>
<div class="color-swatch swatch-5"></div>
<div class="color-card">
<span class="card-title">Title</span>
<span class="card-subtitle">Subtitle</span>
<button class="card-btn">Action</button>
</div>
</div>
`,
'color-accent': `
<div class="color-demo color-accent-before">
<div class="color-accent-card">
<div class="color-accent-title">Premium Plan</div>
<div class="color-accent-text">Unlock all features and get priority support.</div>
<button class="color-accent-btn">Upgrade Now</button>
</div>
</div>
`,
'color-contrast': `
<div class="color-demo color-contrast-static">
<div class="contrast-example contrast-fail">
<span class="contrast-badge">Fails WCAG</span>
<div class="contrast-text">Hard to Read</div>
<div class="contrast-ratio">2.5:1</div>
</div>
<div class="contrast-example contrast-pass">
<span class="contrast-badge">Passes AAA</span>
<div class="contrast-text">Easy to Read</div>
<div class="contrast-ratio">12.6:1</div>
</div>
</div>
`,
// ========== RESPONSIVE DESIGN ==========
'responsive-touch': `
<div class="resp-demo resp-touch-demo">
<div class="resp-touch-row">
<span class="resp-label">Too Small</span>
<div class="resp-touch-targets resp-touch-bad">
<button>×</button>
<button></button>
<button>+</button>
</div>
</div>
<div class="resp-touch-row">
<span class="resp-label">Accessible</span>
<div class="resp-touch-targets resp-touch-good">
<button>×</button>
<button></button>
<button>+</button>
</div>
</div>
</div>
`,
'responsive-fluid': `
<div class="resp-demo resp-fluid-demo">
<div class="resp-fluid-container">
<div class="resp-fluid-fixed">
<span>Fixed 400px</span>
<div class="resp-fluid-bar" style="width: 400px; max-width: 100%;"></div>
</div>
<div class="resp-fluid-adaptive">
<span>Fluid 80%</span>
<div class="resp-fluid-bar" style="width: 80%;"></div>
</div>
</div>
</div>
`,
'responsive-adapt': `
<div class="resp-demo resp-adapt-demo">
<div class="resp-device resp-device-mobile">
<div class="resp-device-screen">
<div class="resp-block resp-header"></div>
<div class="resp-block resp-content"></div>
</div>
<span>Mobile</span>
</div>
<div class="resp-device resp-device-tablet">
<div class="resp-device-screen">
<div class="resp-block resp-header"></div>
<div class="resp-block-row">
<div class="resp-block resp-content"></div>
<div class="resp-block resp-content"></div>
</div>
</div>
<span>Tablet</span>
</div>
<div class="resp-device resp-device-desktop">
<div class="resp-device-screen">
<div class="resp-block-row">
<div class="resp-block resp-sidebar"></div>
<div class="resp-block resp-content"></div>
</div>
</div>
<span>Desktop</span>
</div>
</div>
`
};
return contents[demoId] || '<div style="padding: 2rem; text-align: center; color: #999;">Demo coming soon</div>';
}
function renderGenericDemo(skillId) {
return `
<div class="demo-container">
<div class="demo-viewport">
<div style="text-align: center; color: var(--c-ash); padding: var(--s-xl);">
<p>Demo for ${skillId.replace(/-/g, ' ')} coming soon</p>
</div>
</div>
</div>
`;
}
export function setupDemoTabs() {
document.querySelectorAll('.demo-tab').forEach(tab => {
tab.addEventListener('click', () => {
const tabId = tab.dataset.demoTab;
const container = tab.closest('.demo-tabbed-container');
container.querySelectorAll('.demo-tab').forEach(t => { t.classList.remove('active'); });
tab.classList.add('active');
container.querySelectorAll('.demo-panel').forEach(p => { p.classList.remove('active'); });
container.querySelector(`[data-demo-panel="${tabId}"]`)?.classList.add('active');
});
});
}
+4 -4
View File
@@ -2,9 +2,9 @@
// SKILLS - Rendering and selection logic
// ============================================
import { skillFocusAreas, readySkills } from "./data.js";
import { readySkills, skillFocusAreas } from "./data.js";
import { renderSkillDemo, setupDemoTabs } from "./demo-renderer.js";
import { setupDemoToggles } from "./demo-toggles.js";
import { renderSkillDemo, setupDemoTabs } from "./skill-demos.js";
let currentSkill = null;
@@ -39,9 +39,9 @@ export function renderSkillsNav(skills, onSelect) {
nav.innerHTML = filteredSkills
.map(
(skill) => `
<button class="skill-nav-item ${!isSkillReady(skill.id) ? 'coming-soon' : ''}" data-id="${skill.id}">
<button class="skill-nav-item ${!isSkillReady(skill.id) ? "coming-soon" : ""}" data-id="${skill.id}">
${formatSkillName(skill.id)}
${!isSkillReady(skill.id) ? '<span class="coming-soon-badge">Soon</span>' : ''}
${!isSkillReady(skill.id) ? '<span class="coming-soon-badge">Soon</span>' : ""}
</button>
`,
)