Consolidate skills + add patterns source file

Major changes:
- Consolidate 8 design skills into single frontend-design skill with 7 reference files
- Add source/patterns.md as single source of truth for patterns/antipatterns
- Patterns are merged into skill during build, served via API for website
- Website now dynamically renders both "What TO Do" and "What NOT to Do" sections
- Update build system to handle directory-based skills with references
- Add /api/patterns endpoint to server
- Refactor website with new Antidote section layout

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2025-12-15 14:08:30 -08:00
co-authored by Claude Opus 4.5
parent 99f1091f20
commit 2181137d04
69 changed files with 6658 additions and 4751 deletions
+103 -168
View File
@@ -1,195 +1,130 @@
/**
* Problem Section - Showcases the "AI slop" aesthetic
* Deliberately uses generic design patterns to illustrate what we're solving
* Problem Section - The Clarity Lens
* Interactive before/after comparison
*/
.problem-section {
padding: var(--spacing-2xl) 0;
border-top: 1px solid var(--color-mist);
/* Additional lens styles that need complex CSS */
.lens-container {
cursor: crosshair;
user-select: none;
-webkit-user-select: none;
}
.problem-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-2xl);
align-items: center;
@media (max-width: 968px) {
grid-template-columns: 1fr;
gap: var(--spacing-xl);
}
}
.problem-content {
max-width: 480px;
}
.problem-content .section-title {
margin-bottom: var(--spacing-md);
}
.problem-lead {
font-size: 1.25rem;
line-height: 1.6;
color: var(--color-charcoal);
}
/* AI Slop Demo - Deliberately generic design */
.problem-showcase {
display: flex;
justify-content: center;
}
.ai-slop-demo {
position: relative;
max-width: 380px;
width: 100%;
}
.slop-card {
/* Deliberately generic card styling */
background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 50%, #ddd6fe 100%);
border-radius: 16px;
padding: 24px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
font-family: 'Inter', system-ui, sans-serif;
}
.slop-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
}
.slop-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
}
.slop-text {
flex: 1;
}
.slop-title {
font-size: 16px;
font-weight: 600;
color: #1f2937;
margin-bottom: 2px;
}
.slop-subtitle {
font-size: 13px;
color: #6b7280;
}
.slop-body {
font-size: 14px;
line-height: 1.6;
color: #4b5563;
margin-bottom: 20px;
}
.slop-button {
width: 100%;
padding: 12px 24px;
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
color: white;
border: none;
border-radius: 8px;
font-family: 'Inter', system-ui, sans-serif;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.slop-button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
}
/* Labels pointing out the generic patterns */
.slop-labels {
/* Subtle grid background for the lens container */
.lens-container::before {
content: '';
position: absolute;
inset: 0;
background-image:
linear-gradient(var(--color-mist) 1px, transparent 1px),
linear-gradient(90deg, var(--color-mist) 1px, transparent 1px);
background-size: 20px 20px;
opacity: 0.5;
pointer-events: none;
}
.slop-label {
/* Lens glow effect */
.lens-circle::before {
content: '';
position: absolute;
font-size: 0.6875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-accent);
background: var(--color-paper);
padding: 4px 8px;
border: 1px solid var(--color-accent);
border-radius: 2px;
white-space: nowrap;
opacity: 0;
animation: labelFadeIn 0.5s ease forwards;
inset: -20px;
border-radius: 50%;
background: radial-gradient(
circle,
var(--color-accent-dim) 0%,
transparent 70%
);
opacity: 0.5;
pointer-events: none;
}
.slop-label[data-point="font"] {
top: 24px;
right: -20px;
transform: translateX(100%);
animation-delay: 0.2s;
}
.slop-label[data-point="gradient"] {
top: 50%;
right: -20px;
transform: translateX(100%) translateY(-50%);
animation-delay: 0.4s;
}
.slop-label[data-point="layout"] {
bottom: 60px;
left: -20px;
transform: translateX(-100%);
animation-delay: 0.6s;
}
.slop-label[data-point="copy"] {
bottom: 24px;
left: -20px;
transform: translateX(-100%);
animation-delay: 0.8s;
}
@keyframes labelFadeIn {
from {
opacity: 0;
transform: translateX(calc(var(--tx, 100%) - 10px));
/* Pulsing animation for the lens when idle */
@keyframes lensGlow {
0%, 100% {
box-shadow:
0 0 0 4px var(--color-accent-dim),
0 20px 60px rgba(0,0,0,0.15);
}
to {
opacity: 1;
transform: translateX(var(--tx, 100%));
50% {
box-shadow:
0 0 0 8px var(--color-accent-dim),
0 20px 80px rgba(0,0,0,0.2);
}
}
.slop-label[data-point="layout"],
.slop-label[data-point="copy"] {
--tx: -100%;
.lens-container:not(:hover) .lens-circle {
animation: lensGlow 3s ease-in-out infinite;
}
.slop-label[data-point="gradient"] {
--tx: 100%;
transform: translateX(100%) translateY(-50%);
/* Smooth transitions when hovering */
.lens-container:hover .lens-circle {
transition: left 0.05s linear, top 0.05s linear;
}
@media (max-width: 640px) {
.slop-labels {
.lens-container:hover .lens-after {
transition: clip-path 0.05s linear;
}
/* Mobile: tap to reveal */
@media (hover: none) {
.lens-label {
display: none;
}
.ai-slop-demo {
max-width: 100%;
.lens-circle::after {
content: 'Tap & Drag';
position: absolute;
bottom: -36px;
left: 50%;
transform: translateX(-50%);
font-size: 0.625rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--color-accent);
background: var(--color-paper);
padding: 4px 12px;
border-radius: 4px;
white-space: nowrap;
}
}
/* Hover state for impeccable card inside lens */
.lens-after .impeccable-card {
box-shadow: 0 10px 40px rgba(0,0,0,0.08);
}
/* Entry animation for the lens */
@keyframes lensEntry {
from {
opacity: 0;
transform: translate(-50%, -50%) scale(0.8);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
.lens-circle {
animation: lensEntry 0.6s var(--ease-out) 0.3s backwards;
}
/* Labels hover states */
.lens-label-item {
transition: color var(--duration-fast) var(--ease-out);
cursor: default;
}
.lens-label-item:hover {
color: var(--color-text);
}
.lens-label-item[data-point="after"]:hover .lens-label-dot--accent {
transform: scale(1.3);
}
.lens-label-dot {
transition: transform var(--duration-fast) var(--ease-spring);
}