diff --git a/.claude/skills/design-system/SKILL.md b/.claude/skills/design-system/SKILL.md new file mode 100644 index 0000000..cfbcba0 --- /dev/null +++ b/.claude/skills/design-system/SKILL.md @@ -0,0 +1,244 @@ +--- +name: ckm:design-system +description: Token architecture, component specifications, and slide generation. Three-layer tokens (primitive→semantic→component), CSS variables, spacing/typography scales, component specs, strategic slide creation. Use for design tokens, systematic design, brand-compliant presentations. +argument-hint: "[component or token]" +license: MIT +metadata: + author: claudekit + version: "1.0.0" +--- + +# Design System + +Token architecture, component specifications, systematic design, slide generation. + +## When to Use + +- Design token creation +- Component state definitions +- CSS variable systems +- Spacing/typography scales +- Design-to-code handoff +- Tailwind theme configuration +- **Slide/presentation generation** + +## Token Architecture + +Load: `references/token-architecture.md` + +### Three-Layer Structure + +``` +Primitive (raw values) + ↓ +Semantic (purpose aliases) + ↓ +Component (component-specific) +``` + +**Example:** +```css +/* Primitive */ +--color-blue-600: #2563EB; + +/* Semantic */ +--color-primary: var(--color-blue-600); + +/* Component */ +--button-bg: var(--color-primary); +``` + +## Quick Start + +**Generate tokens:** +```bash +node scripts/generate-tokens.cjs --config tokens.json -o tokens.css +``` + +**Validate usage:** +```bash +node scripts/validate-tokens.cjs --dir src/ +``` + +## References + +| Topic | File | +|-------|------| +| Token Architecture | `references/token-architecture.md` | +| Primitive Tokens | `references/primitive-tokens.md` | +| Semantic Tokens | `references/semantic-tokens.md` | +| Component Tokens | `references/component-tokens.md` | +| Component Specs | `references/component-specs.md` | +| States & Variants | `references/states-and-variants.md` | +| Tailwind Integration | `references/tailwind-integration.md` | + +## Component Spec Pattern + +| Property | Default | Hover | Active | Disabled | +|----------|---------|-------|--------|----------| +| Background | primary | primary-dark | primary-darker | muted | +| Text | white | white | white | muted-fg | +| Border | none | none | none | muted-border | +| Shadow | sm | md | none | none | + +## Scripts + +| Script | Purpose | +|--------|---------| +| `generate-tokens.cjs` | Generate CSS from JSON token config | +| `validate-tokens.cjs` | Check for hardcoded values in code | +| `search-slides.py` | BM25 search + contextual recommendations | +| `slide-token-validator.py` | Validate slide HTML for token compliance | +| `fetch-background.py` | Fetch images from Pexels/Unsplash | + +## Templates + +| Template | Purpose | +|----------|---------| +| `design-tokens-starter.json` | Starter JSON with three-layer structure | + +## Integration + +**With brand:** Extract primitives from brand colors/typography +**With ui-styling:** Component tokens → Tailwind config + +**Skill Dependencies:** brand, ui-styling +**Primary Agents:** ui-ux-designer, frontend-developer + +## Slide System + +Brand-compliant presentations using design tokens + Chart.js + contextual decision system. + +### Source of Truth + +| File | Purpose | +|------|---------| +| `docs/brand-guidelines.md` | Brand identity, voice, colors | +| `assets/design-tokens.json` | Token definitions (primitive→semantic→component) | +| `assets/design-tokens.css` | CSS variables (import in slides) | +| `assets/css/slide-animations.css` | CSS animation library | + +### Slide Search (BM25) + +```bash +# Basic search (auto-detect domain) +python scripts/search-slides.py "investor pitch" + +# Domain-specific search +python scripts/search-slides.py "problem agitation" -d copy +python scripts/search-slides.py "revenue growth" -d chart + +# Contextual search (Premium System) +python scripts/search-slides.py "problem slide" --context --position 2 --total 9 +python scripts/search-slides.py "cta" --context --position 9 --prev-emotion frustration +``` + +### Decision System CSVs + +| File | Purpose | +|------|---------| +| `data/slide-strategies.csv` | 15 deck structures + emotion arcs + sparkline beats | +| `data/slide-layouts.csv` | 25 layouts + component variants + animations | +| `data/slide-layout-logic.csv` | Goal → Layout + break_pattern flag | +| `data/slide-typography.csv` | Content type → Typography scale | +| `data/slide-color-logic.csv` | Emotion → Color treatment | +| `data/slide-backgrounds.csv` | Slide type → Image category (Pexels/Unsplash) | +| `data/slide-copy.csv` | 25 copywriting formulas (PAS, AIDA, FAB) | +| `data/slide-charts.csv` | 25 chart types with Chart.js config | + +### Contextual Decision Flow + +``` +1. Parse goal/context + ↓ +2. Search slide-strategies.csv → Get strategy + emotion beats + ↓ +3. For each slide: + a. Query slide-layout-logic.csv → layout + break_pattern + b. Query slide-typography.csv → type scale + c. Query slide-color-logic.csv → color treatment + d. Query slide-backgrounds.csv → image if needed + e. Apply animation class from slide-animations.css + ↓ +4. Generate HTML with design tokens + ↓ +5. Validate with slide-token-validator.py +``` + +### Pattern Breaking (Duarte Sparkline) + +Premium decks alternate between emotions for engagement: +``` +"What Is" (frustration) ↔ "What Could Be" (hope) +``` + +System calculates pattern breaks at 1/3 and 2/3 positions. + +### Slide Requirements + +**ALL slides MUST:** +1. Import `assets/design-tokens.css` - single source of truth +2. Use CSS variables: `var(--color-primary)`, `var(--slide-bg)`, etc. +3. Use Chart.js for charts (NOT CSS-only bars) +4. Include navigation (keyboard arrows, click, progress bar) +5. Center align content +6. Focus on persuasion/conversion + +### Chart.js Integration + +```html + + + + +``` + +### Token Compliance + +```css +/* CORRECT - uses token */ +background: var(--slide-bg); +color: var(--color-primary); +font-family: var(--typography-font-heading); + +/* WRONG - hardcoded */ +background: #0D0D0D; +color: #FF6B6B; +font-family: 'Space Grotesk'; +``` + +### Reference Implementation + +Working example with all features: +``` +assets/designs/slides/claudekit-pitch-251223.html +``` + +### Command + +```bash +/slides:create "10-slide investor pitch for ClaudeKit Marketing" +``` + +## Best Practices + +1. Never use raw hex in components - always reference tokens +2. Semantic layer enables theme switching (light/dark) +3. Component tokens enable per-component customization +4. Use HSL format for opacity control +5. Document every token's purpose +6. **Slides must import design-tokens.css and use var() exclusively** diff --git a/.claude/skills/design-system/data/slide-backgrounds.csv b/.claude/skills/design-system/data/slide-backgrounds.csv new file mode 100644 index 0000000..1150484 --- /dev/null +++ b/.claude/skills/design-system/data/slide-backgrounds.csv @@ -0,0 +1,11 @@ +slide_type,image_category,overlay_style,text_placement,image_sources,search_keywords +hero,abstract-tech,gradient-dark,center,pexels:unsplash,technology abstract gradient dark +vision,future-workspace,gradient-brand,left,pexels:unsplash,futuristic office modern workspace +team,professional-people,gradient-dark,bottom,pexels:unsplash,business team professional diverse +testimonial,office-environment,blur-dark,center,pexels:unsplash,modern office workspace bright +cta,celebration-success,gradient-brand,center,pexels:unsplash,success celebration achievement +problem,frustration-pain,desaturate-dark,center,pexels:unsplash,stress frustration problem dark +solution,breakthrough-moment,gradient-accent,right,pexels:unsplash,breakthrough success innovation light +hook,attention-grabbing,gradient-dark,center,pexels:unsplash,dramatic abstract attention bold +social,community-connection,blur-dark,center,pexels:unsplash,community collaboration connection +demo,product-showcase,gradient-dark,left,pexels:unsplash,technology product showcase clean diff --git a/.claude/skills/design-system/data/slide-charts.csv b/.claude/skills/design-system/data/slide-charts.csv new file mode 100644 index 0000000..373648e --- /dev/null +++ b/.claude/skills/design-system/data/slide-charts.csv @@ -0,0 +1,26 @@ +id,chart_type,keywords,best_for,data_type,when_to_use,when_to_avoid,max_categories,slide_context,css_implementation,accessibility_notes,sources +1,Bar Chart Vertical,"bar, vertical, comparison, categories, ranking",Comparing values across categories,Categorical discrete,"Comparing 3-12 categories, showing ranking, highlighting differences",Continuous time data trends,12,Traction metrics feature comparison,"Chart.js or CSS flexbox with height percentage bars",Always label axes include values,Atlassian Data Charts +2,Bar Chart Horizontal,"horizontal bar, ranking, long labels, categories",Categories with long names ranking,Categorical discrete,"Long category names, 5+ items, reading left-to-right natural",Few categories time series,15,Team performance competitor analysis,"CSS flexbox with width percentage bars",Natural reading direction for labels,Datylon Blog +3,Line Chart,"line, trend, time series, growth, change over time",Showing trends over continuous time,Time series continuous,"Time-based data, showing growth trajectory, 10+ data points",Categorical comparisons,50+ points,Revenue growth MRR user growth,"Chart.js line or SVG path element",Include data point markers for screen readers,Tableau Best Practices +4,Area Chart,"area, cumulative, volume, trend, filled line",Showing volume or magnitude over time,Time series cumulative,"Emphasizing magnitude, showing cumulative totals, comparing totals",Precise value comparison,3-4 series,Revenue breakdown market share over time,"Chart.js area or SVG path with fill",Use patterns not just colors for series,Data Visualization Guide +5,Pie Chart,"pie, composition, percentage, parts, whole",Showing parts of a whole,Proportional percentage,"2-5 slices, showing simple composition, adds to 100%",More than 6 categories precise comparison,6 max,Market share budget allocation simple splits,"CSS conic-gradient or Chart.js pie",Never use 3D always include percentages,FusionCharts Blog +6,Donut Chart,"donut, composition, percentage, center metric",Parts of whole with center highlight,Proportional percentage,"Like pie but need center space for key metric, 2-5 segments",Many categories,6 max,Composition with key stat in center,"CSS conic-gradient with inner circle",Same as pie include legend,Modern alternative to pie +7,Stacked Bar,"stacked, composition, comparison, breakdown",Comparing composition across categories,Categorical + proportional,"Showing composition AND comparison, segment contribution",Too many segments precise values,5 segments,Revenue by segment across quarters,"Chart.js stacked bar or CSS nested divs",Order segments consistently use legend,Atlassian Data Charts +8,Grouped Bar,"grouped, clustered, side by side, multi-series",Comparing multiple metrics per category,Multi-series categorical,"Direct comparison of 2-3 metrics per category",Too many groups (>4) or categories (>8),4 groups,Feature comparison pricing tiers,"Chart.js grouped bar CSS grid bars",Color code consistently across groups,Data Visualization Best Practices +9,100% Stacked Bar,"100%, proportion, normalized, percentage",Comparing proportions across categories,Proportional comparative,"Comparing percentage breakdown across categories, not absolute values",Showing absolute values,5 segments,Market share comparison percentage breakdown,"CSS flexbox 100% width segments",Clearly indicate percentage scale,Proportional analysis +10,Funnel Chart,"funnel, conversion, stages, drop-off, pipeline",Showing conversion or drop-off through stages,Sequential stage-based,"Sales funnel, conversion rates, sequential process with decreasing values",Non-sequential data equal stages,6-8 stages,User conversion sales pipeline,"CSS trapezoid shapes or SVG",Label each stage with count and percentage,Marketing/Sales standard +11,Gauge Chart,"gauge, progress, goal, target, kpi",Showing progress toward a goal,Single metric vs target,"Single KPI progress, goal completion, health scores",Multiple metrics,1 metric,Goal progress health score,"CSS conic-gradient or arc SVG",Include numeric value not just visual,Dashboard widgets +12,Sparkline,"sparkline, mini, inline, trend, compact",Showing trend in minimal space,Time series inline,"Inline metrics, table cells, compact trend indication",Detailed analysis,N/A,Metric cards with trend indicator,SVG path or canvas inline,Supplement with text for accessibility,Edward Tufte +13,Scatter Plot,"scatter, correlation, relationship, distribution",Showing relationship between two variables,Bivariate continuous,"Correlation analysis, pattern detection, outlier identification",Categorical data simple comparisons,100+ points,Correlation analysis segmentation,Canvas or SVG circles positioned,Include trend line if meaningful,Statistical visualization +14,Bubble Chart,"bubble, three variables, scatter, size",Showing three variables simultaneously,Trivariate continuous,"Three-variable comparison, population/size matters",Simple comparisons,30-50 bubbles,Market analysis with size dimension,"SVG circles with varying radius",Legend for size scale essential,Data Visualization Guide +15,Heatmap,"heatmap, matrix, intensity, correlation, grid",Showing intensity across two dimensions,Matrix intensity,"Large data matrices, time-day patterns, correlation matrices",Few data points,Unlimited grid,Usage patterns correlation matrices,CSS grid with background-color intensity,Use colorblind-safe gradients,Datylon Blog +16,Waterfall Chart,"waterfall, bridge, contribution, breakdown",Showing how values add to a total,Cumulative contribution,"Financial analysis, showing positive/negative contributions",Non-additive data,10-15 items,Revenue bridge profit breakdown,"CSS positioned bars with connectors",Clear positive/negative color coding,Financial reporting standard +17,Treemap,"treemap, hierarchy, nested, proportion",Showing hierarchical proportional data,Hierarchical proportional,"Nested categories, space-efficient proportions, 2 levels max",Simple comparisons few items,50+ items,Budget breakdown category analysis,"CSS grid with calculated areas",Include text labels on larger segments,Ben Shneiderman +18,Radar Chart,"radar, spider, multi-metric, profile",Comparing multiple metrics for single item,Multi-metric profile,"Comparing 5-8 metrics for one or two items, skill profiles",More than 3 items to compare,8 axes max,Feature profile skill assessment,SVG polygon on axes,Ensure scale is clear and consistent,Profile comparison +19,Bullet Chart,"bullet, target, actual, performance",Showing actual vs target with ranges,KPI with target,"Progress against target with qualitative ranges",Simple goal tracking,1-3 per slide,KPI performance with targets,"CSS layered bars with markers",Clearly label target and actual,Stephen Few +20,Timeline,"timeline, chronology, history, milestones",Showing events over time,Event-based temporal,"History roadmap milestones, showing progression",Quantitative comparison,10-15 events,Company history product roadmap,"CSS flexbox with positioned markers",Ensure logical reading order,Chronological visualization +21,Sankey Diagram,"sankey, flow, distribution, connections",Showing flow or distribution between nodes,Flow distribution,"Showing how values flow from source to destination",Simple distributions,15-20 nodes,User flow budget flow,D3.js or dedicated library,Alternative text description essential,Complex flow visualization +22,KPI Card,"kpi, metric, number, stat, scorecard",Highlighting single important metric,Single metric,"Dashboard hero metrics, emphasizing one key number",Showing trends or comparisons,1 number,Main KPI highlight,"Large font-size centered number",Include trend context if relevant,Dashboard design +23,Progress Bar,"progress, completion, percentage, bar",Showing completion percentage,Single percentage,"Simple progress indication, goal completion",Multiple goals comparison,1 per context,Project completion goal progress,"CSS width with percentage gradient",Include numeric percentage,UI/UX standard +24,Comparison Table,"table, comparison, matrix, features",Detailed feature or value comparison,Multi-attribute categorical,"Detailed comparison, many attributes, exact values matter",Visual impact storytelling,10-15 rows,Feature matrix pricing comparison,"HTML table with CSS styling",Proper table headers and scope,Information design +25,Icon Array,"icon array, pictogram, proportion, visual",Showing proportions with visual metaphor,Proportional visual,"Making statistics tangible (e.g. 1 in 10 people), visual impact",Precise values large numbers,100 icons,Statistics visualization impact slides,"CSS grid or flexbox with icons",Describe proportion in text,ISOTYPE Otto Neurath diff --git a/.claude/skills/design-system/data/slide-color-logic.csv b/.claude/skills/design-system/data/slide-color-logic.csv new file mode 100644 index 0000000..d9481d6 --- /dev/null +++ b/.claude/skills/design-system/data/slide-color-logic.csv @@ -0,0 +1,14 @@ +emotion,background,text_color,accent_usage,use_full_bleed,gradient,card_style +frustration,dark-surface,foreground,minimal,false,none,subtle-border +hope,accent-bleed,dark,none,true,none,none +fear,dark-background,primary,stats-only,false,none,glow-primary +relief,surface,foreground,icons,false,none,accent-bar +trust,surface-elevated,foreground,metrics,false,none,subtle-border +urgency,gradient,white,cta-button,true,primary,none +curiosity,dark-glow,gradient-text,badge,false,glow,glow-secondary +confidence,surface,foreground,chart-accent,false,none,none +warmth,dark-surface,foreground,avatar-ring,false,none,none +evaluation,surface-elevated,foreground,highlight,false,none,comparison +narrative,dark-background,foreground-secondary,timeline-dots,false,none,none +clarity,surface,foreground,icons,false,none,feature-card +interest,dark-glow,foreground,demo-highlight,false,glow,none diff --git a/.claude/skills/design-system/data/slide-copy.csv b/.claude/skills/design-system/data/slide-copy.csv new file mode 100644 index 0000000..ed448c0 --- /dev/null +++ b/.claude/skills/design-system/data/slide-copy.csv @@ -0,0 +1,26 @@ +id,formula_name,keywords,components,use_case,example_template,emotion_trigger,slide_type,source +1,AIDA,"aida, attention, interest, desire, action",Attention→Interest→Desire→Action,Lead-gen CTAs general persuasion,"{Attention hook} → {Interesting detail} → {Desirable outcome} → {Action step}",Curiosity→Engagement→Want→Urgency,CTA slides,Classic copywriting 1898 +2,PAS,"pas, problem, agitation, solution, dan kennedy",Problem→Agitate→Solution,Sales pages problem slides most reliable,"You're struggling with {problem}. It's costing you {agitation}. {Solution} fixes this.",Frustration→Fear→Relief,Problem slides,Dan Kennedy +3,4Ps,"4ps, promise, picture, proof, push, ray edwards",Promise→Picture→Proof→Push,Home pages lead-gen,"{Promise benefit} → {Picture future state} → {Proof it works} → {Push to act}",Hope→Vision→Trust→Action,Solution slides,Ray Edwards +4,Before-After-Bridge,"bab, before, after, bridge, transformation",Before→After→Bridge,Transformation case studies,"Before: {old state}. After: {new state}. Bridge: {how to get there}",Pain→Pleasure→Path,Before/after slides,Copywriting classic +5,QUEST,"quest, qualify, understand, educate, stimulate, transition",Qualify→Understand→Educate→Stimulate→Transition,Matching solution to prospect,"{Qualify audience} → {Show understanding} → {Educate on solution} → {Stimulate desire} → {Transition to CTA}",Recognition→Empathy→Learning→Excitement,Educational slides,Michel Fortin +6,Star-Story-Solution,"star, story, solution, narrative",Star→Story→Solution,Personality brands info products,"{Introduce character} → {Tell their struggle} → {Reveal their solution}",Connection→Empathy→Hope,Case study slides,CopyHackers +7,Feature-Advantage-Benefit,"fab, feature, advantage, benefit",Feature→Advantage→Benefit,Feature explanations product slides,"{Feature name}: {What it does} → {Why that matters} → {How it helps you}",Curiosity→Understanding→Desire,Feature slides,Sales training classic +8,What If,"what if, imagination, possibility, hook",What if + Possibility,Opening hooks vision slides,"What if you could {desirable outcome} without {common obstacle}?",Wonder→Possibility,Title problem slides,Headline formula +9,How To,"how to, tutorial, guide, instruction",How to + Specific outcome,Educational actionable content,"How to {achieve specific result} in {timeframe or steps}",Curiosity→Empowerment,Educational slides,Headline formula +10,Number List,"number, list, reasons, ways, tips",Number + Topic + Promise,Scannable benefit lists,"{Number} {Ways/Reasons/Tips} to {achieve outcome}",Curiosity→Completeness,Feature summary slides,Content marketing +11,Question Hook,"question, hook, curiosity, engagement",Question that implies answer,Opening engagement slides,"{Question that reader answers yes to}? Here's how.",Recognition→Curiosity,Opening slides,Rhetorical technique +12,Proof Stack,"proof, evidence, credibility, stats",Stat→Source→Implication,Building credibility trust,"{Impressive stat} (Source: {credible source}). This means {implication for audience}.",Trust→Validation,Traction proof slides,Social proof theory +13,Future Pacing,"future, vision, imagine, picture this",Imagine + Future state,Vision and aspiration slides,"Imagine: {desirable future scenario}. That's what {solution} delivers.",Aspiration→Desire,Solution CTA slides,NLP technique +14,Social Proof,"social proof, testimonial, customers, trust",Who + Result + Quote,Credibility through others,"{Customer name} increased {metric} by {amount}. '{Quote about experience}'",Trust→FOMO,Testimonial slides,Robert Cialdini +15,Scarcity Urgency,"scarcity, urgency, limited, deadline, fomo",Limited + Deadline + Consequence,Driving action urgency,"Only {quantity} available. Offer ends {date}. {Consequence of missing out}.",Fear of loss→Action,CTA closing slides,Cialdini influence +16,Cost of Inaction,"cost, inaction, consequence, loss",Current cost + Future cost + Comparison,Motivating change,"Every {timeframe} without {solution} costs you {quantified loss}. That's {larger number} per year.",Loss aversion→Urgency,Problem agitation slides,Loss aversion psychology +17,Simple Benefit,"benefit, value, outcome, result",You get + Specific benefit,Clear value communication,"{Solution}: You get {specific tangible benefit}.",Clarity→Desire,Any slide,Direct response +18,Objection Preempt,"objection, concern, but, however, faq",Objection + Response + Proof,"Handling concerns proactively","You might think {objection}. Actually, {counter with proof}.",Doubt→Resolution,FAQ objection slides,Sales training +19,Comparison Frame,"comparison, versus, than, better, alternative",Us vs Them + Specific difference,Competitive positioning,"{Competitor approach}: {limitation}. {Our approach}: {advantage}.",Evaluation→Preference,Comparison slides,Positioning strategy +20,Pain-Claim-Gain,"pcg, pain, claim, gain",Pain point→Bold claim→Specific gain,Concise value proposition,"{Pain point}? {Bold claim about solution}. Result: {specific gain}.",Frustration→Hope→Excitement,Problem/solution slides,Copywriting framework +21,One Thing,"one thing, single, focus, key",The one thing + Why it matters,Focus and clarity,"The #1 thing {audience} needs to {outcome} is {one thing}.",Focus→Clarity,Key message slides,Gary Keller concept +22,Riddle Open,"riddle, mystery, puzzle, question",Mystery + Reveal + Implication,Engagement through curiosity,"{Intriguing mystery or paradox}. The answer: {reveal}. For you: {implication}.",Mystery→Insight,Opening slides,Storytelling technique +23,Hero Journey,"hero, journey, transformation, story",Ordinary→Call→Challenge→Triumph,Narrative structure,"{Character in ordinary world} → {Discovers challenge} → {Overcomes with solution} → {Achieves transformation}",Identification→Tension→Triumph,Full deck structure,Joseph Campbell +24,Value Stack,"value, stack, bundle, worth",Component + Value → Total value,Justifying price/investment,"{Item 1} (Worth ${X}) + {Item 2} (Worth ${Y}) + ... = Total value ${Z}. Your investment: ${actual price}.",Value perception,Pricing offer slides,Info product marketing +25,Power Statement,"power, statement, bold, declaration",Bold declaration + Supporting fact,Authority and confidence,"{Bold declaration}. {Supporting evidence or fact}.",Confidence→Trust,Key message slides,Thought leadership diff --git a/.claude/skills/design-system/data/slide-layout-logic.csv b/.claude/skills/design-system/data/slide-layout-logic.csv new file mode 100644 index 0000000..a673bec --- /dev/null +++ b/.claude/skills/design-system/data/slide-layout-logic.csv @@ -0,0 +1,16 @@ +goal,emotion,layout_pattern,direction,visual_weight,break_pattern,use_bg_image +hook,curiosity,split-hero,visual-right,70-visual,false,true +problem,frustration,card-grid,centered,balanced,false,false +agitation,fear,full-bleed-stat,centered,100-text,true,false +solution,relief,split-feature,visual-left,50-50,false,true +proof,trust,metric-grid,centered,numbers-dominant,false,false +social,connection,quote-hero,centered,80-text,true,true +comparison,evaluation,split-compare,side-by-side,balanced,false,false +traction,confidence,chart-insight,chart-left,60-chart,false,false +cta,urgency,gradient-cta,centered,100-text,true,true +team,warmth,team-grid,centered,balanced,false,true +pricing,evaluation,pricing-cards,centered,balanced,false,false +demo,interest,split-demo,visual-left,60-visual,false,false +vision,hope,full-bleed-hero,centered,100-visual,true,true +timeline,narrative,timeline-flow,horizontal,balanced,false,false +features,clarity,feature-grid,centered,balanced,false,false diff --git a/.claude/skills/design-system/data/slide-layouts.csv b/.claude/skills/design-system/data/slide-layouts.csv new file mode 100644 index 0000000..708be11 --- /dev/null +++ b/.claude/skills/design-system/data/slide-layouts.csv @@ -0,0 +1,26 @@ +id,layout_name,keywords,use_case,content_zones,visual_weight,cta_placement,recommended_for,avoid_for,css_structure,card_variant,metric_style,quote_style,grid_columns,visual_treatment,animation_class +1,Title Slide,"title, cover, opening, intro, hero",Opening slide first impression,"Center: Logo + Title + Tagline, Bottom: Date/Presenter",Visual-heavy minimal text,None or subtle,All presentations,Never skip,"display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center",none,none,none,1,gradient-glow,animate-fade-up +2,Problem Statement,"problem, pain, challenge, issue",Establish the problem being solved,"Left: Problem headline, Right: Pain point bullets or icon grid",50/50 text visual balance,None,Pitch decks sales,Internal updates,"display:grid; grid-template-columns:1fr 1fr; gap:48px; align-items:center",icon-left,none,none,2,subtle-border,animate-stagger +3,Solution Overview,"solution, answer, approach, how",Introduce your solution,"Top: Solution headline, Center: Solution visual/diagram, Bottom: 3 key points",Visual-dominant,Subtle learn more,After problem slide,Without context,"display:flex; flex-direction:column; gap:32px",accent-bar,none,none,3,icon-top,animate-scale +4,Feature Grid,"features, grid, cards, capabilities, 3-column",Showcase multiple features,"Top: Section title, Grid: 3-6 feature cards with icon+title+description",Balanced grid,Bottom CTA optional,Product demos SaaS,Storytelling slides,"display:grid; grid-template-columns:repeat(3,1fr); gap:24px",accent-bar,none,none,3,icon-top,animate-stagger +5,Metrics Dashboard,"metrics, kpis, numbers, stats, data",Display key performance data,"Top: Context headline, Center: 3-4 large metric cards, Bottom: Trend context",Numbers-dominant,None,Traction slides QBRs,Early-stage no data,"display:grid; grid-template-columns:repeat(4,1fr); gap:16px",metric-card,gradient-number,none,4,none,animate-stagger-scale +6,Comparison Table,"comparison, vs, versus, table, matrix",Compare options or competitors,"Top: Comparison title, Center: Feature comparison table, Bottom: Conclusion",Table-heavy,Highlight winner row,Competitive analysis,Storytelling,"display:flex; flex-direction:column; table width:100%",comparison,none,none,2,highlight-winner,animate-fade-up +7,Timeline Flow,"timeline, roadmap, journey, steps, process",Show progression over time,"Top: Timeline title, Center: Horizontal timeline with milestones, Bottom: Current status",Visual timeline,End milestone CTA,Roadmaps history,Dense data,"display:flex; flex-direction:column; timeline:flex with arrows",none,none,none,1,timeline-dots,animate-stagger +8,Team Grid,"team, people, founders, leadership",Introduce team members,"Top: Team title, Grid: Photo + Name + Title + Brief bio cards",Photo-heavy,None or careers link,Investor decks about,Technical content,"display:grid; grid-template-columns:repeat(4,1fr); gap:24px",avatar-card,none,none,4,avatar-ring,animate-stagger +9,Quote Testimonial,"quote, testimonial, social proof, customer",Feature customer endorsement,"Center: Large quote text, Bottom: Photo + Name + Title + Company logo",Quote-dominant minimal UI,None,Sales case studies,Without real quotes,"display:flex; flex-direction:column; justify-content:center; font-size:large; font-style:italic",none,none,large-italic,1,author-avatar,animate-fade-up +10,Two Column Split,"split, two-column, side-by-side, comparison",Present two related concepts,"Left column: Content A, Right column: Content B",50/50 balanced,Either column bottom,Comparisons before/after,Single concept,display:grid; grid-template-columns:1fr 1fr; gap:48px,none,none,none,2,offset-image,animate-fade-up +11,Big Number Hero,"big number, stat, impact, headline metric",Emphasize one powerful metric,"Center: Massive number, Below: Context label and trend",Number-dominant,None,Impact slides traction,Multiple metrics,"display:flex; flex-direction:column; justify-content:center; align-items:center; font-size:120px",none,oversized,none,1,centered,animate-count +12,Product Screenshot,"screenshot, product, demo, ui, interface",Show product in action,"Top: Feature headline, Center: Product screenshot with annotations, Bottom: Key callouts",Screenshot-dominant,Try it CTA,Product demos,Abstract concepts,"display:flex; flex-direction:column; img max-height:60vh",none,none,none,1,screenshot-shadow,animate-scale +13,Pricing Cards,"pricing, plans, tiers, packages",Present pricing options,"Top: Pricing headline, Center: 2-4 pricing cards side by side, Bottom: FAQ or guarantee",Cards balanced,Each card has CTA,Sales pricing pages,Free products,"display:grid; grid-template-columns:repeat(3,1fr); gap:24px; .popular:scale(1.05)",pricing-card,none,none,3,popular-highlight,animate-stagger +14,CTA Closing,"cta, closing, call to action, next steps, final",Drive action end presentation,"Center: Bold headline + Value reminder, Center: Primary CTA button, Below: Secondary option",CTA-dominant,Primary center,All presentations,Middle slides,"display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center",none,none,none,1,gradient-bg,animate-pulse +15,Agenda Overview,"agenda, outline, contents, structure",Preview presentation structure,"Top: Agenda title, Center: Numbered list or visual timeline of sections",Text-light scannable,None,Long presentations,Short 3-5 slides,"display:flex; flex-direction:column; ol list-style-type:decimal",none,none,none,1,numbered-list,animate-stagger +16,Before After,"before, after, transformation, results, comparison",Show transformation impact,"Left: Before state (muted), Right: After state (vibrant), Center: Arrow or transition",50/50 high contrast,After column CTA,Case studies results,No transformation data,"display:grid; grid-template-columns:1fr 1fr; .before:opacity(0.7)",comparison,none,none,2,contrast-pair,animate-scale +17,Icon Grid Stats,"icons, stats, grid, key points, summary",Summarize key points visually,"Grid: 4-6 icon + stat + label combinations",Icons-dominant,None,Summary slides,Detailed explanations,"display:grid; grid-template-columns:repeat(3,1fr); gap:32px; text-align:center",icon-stat,sparkline,none,3,icon-top,animate-stagger +18,Full Bleed Image,"image, photo, visual, background, hero",Create visual impact,"Full background image, Overlay: Text with contrast, Corner: Logo",Image-dominant,Overlay CTA optional,Emotional moments,Data-heavy,background-size:cover; color:white; text-shadow for contrast,none,none,none,1,bg-overlay,animate-ken-burns +19,Video Embed,"video, demo, embed, multimedia",Show video content,"Top: Context headline, Center: Video player (16:9), Bottom: Key points if needed",Video-dominant,After video CTA,Demos testimonials,Reading-focused,"aspect-ratio:16/9; video controls",none,none,none,1,video-frame,animate-scale +20,Funnel Diagram,"funnel, conversion, stages, pipeline",Show conversion or process flow,"Top: Funnel title, Center: Funnel visualization with stage labels and metrics",Diagram-dominant,None,Sales marketing funnels,Non-sequential data,SVG or CSS trapezoid shapes,none,funnel-numbers,none,1,funnel-gradient,animate-chart +21,Quote Plus Stats,"quote, stats, hybrid, testimonial, metrics",Combine social proof with data,"Left: Customer quote with photo, Right: 3 supporting metrics",Balanced quote/data,None,Sales enablement,Without both elements,"display:grid; grid-template-columns:1.5fr 1fr; gap:48px",metric-card,gradient-number,side-quote,2,author-avatar,animate-stagger +22,Section Divider,"section, divider, break, transition",Transition between sections,"Center: Section number + Section title, Minimal design",Typography-only,None,Long presentations,Every slide,"display:flex; justify-content:center; align-items:center; font-size:48px",none,none,none,1,section-number,animate-fade-up +23,Logo Grid,"logos, clients, partners, trust, social proof",Display client or partner logos,"Top: Trust headline, Grid: 8-16 logos evenly spaced",Logos-only,None,Credibility slides,Few logos <6,"display:grid; grid-template-columns:repeat(4,1fr); gap:32px; filter:grayscale(1)",none,none,none,4,logo-grayscale,animate-stagger +24,Chart Focus,"chart, graph, data, visualization, analytics",Present data visualization,"Top: Chart title and context, Center: Single large chart, Bottom: Key insight",Chart-dominant,None,Data-driven slides,Poor data quality,"chart max-height:65vh; annotation for key point",none,sparkline,none,1,chart-left,animate-chart +25,Q&A Slide,"qa, questions, discussion, interactive",Invite audience questions,"Center: Q&A or Questions? text, Below: Contact info or submission method",Minimal text,None,End of presentations,Skip if no time,"display:flex; justify-content:center; align-items:center; font-size:64px",none,none,none,1,centered,animate-fade-up diff --git a/.claude/skills/design-system/data/slide-strategies.csv b/.claude/skills/design-system/data/slide-strategies.csv new file mode 100644 index 0000000..1aa2760 --- /dev/null +++ b/.claude/skills/design-system/data/slide-strategies.csv @@ -0,0 +1,16 @@ +id,strategy_name,keywords,slide_count,structure,goal,audience,tone,narrative_arc,key_metrics,sources,emotion_arc,sparkline_beats +1,YC Seed Deck,"yc, seed, startup, investor, funding, vc, venture","10-12","1.Title 2.Problem 3.Solution 4.Traction 5.Market 6.Product 7.Business Model 8.Team 9.Financials 10.Ask",Raise seed funding from VCs,Seed investors hunting asymmetric upside,Clear concise focused narrative,Problem→Solution→Evidence→Ask,MRR ARR growth rate user count,Y Combinator Library,"curiosity→frustration→hope→confidence→trust→urgency","hook|what-is|what-could-be|proof|proof|what-could-be|proof|trust|what-could-be|action" +2,Guy Kawasaki 10/20/30,"kawasaki, pitch, investor, 10 slides, venture","10","1.Title 2.Problem/Opportunity 3.Value Proposition 4.Underlying Magic 5.Business Model 6.Go-to-Market 7.Competition 8.Team 9.Projections 10.Status/Timeline/Ask",Pitch to investors in 20 min,VCs angel investors,Confident not arrogant,Hook→Magic→Proof→Ask,5yr projections milestones,Guy Kawasaki Blog,"curiosity→frustration→hope→confidence→trust→urgency","hook|what-is|what-could-be|what-could-be|proof|proof|evaluation|trust|proof|action" +3,Series A Deck,"series a, growth, scale, investor, traction","12-15","1.Title 2.Mission 3.Problem 4.Solution 5.Traction/Metrics 6.Product Demo 7.Market Size 8.Business Model 9.Competition 10.Team 11.Go-to-Market 12.Financials 13.Use of Funds 14.Ask",Raise Series A funding,Growth-stage VCs,Data-driven confident,Traction→Scale→Vision,Revenue growth LTV CAC cohorts,YC Library,"curiosity→hope→frustration→relief→confidence→trust→urgency","hook|what-could-be|what-is|what-could-be|proof|proof|proof|proof|evaluation|trust|proof|proof|what-could-be|action" +4,Product Demo,"demo, product, walkthrough, features, saas","5-8","1.Hook/Problem 2.Solution Overview 3.Live Demo/Screenshots 4.Key Features 5.Benefits 6.Pricing 7.CTA",Demonstrate product value,Prospects users,Enthusiastic helpful,Problem→See it work→Value,Conversion engagement time-saved,Product-led growth best practices,"curiosity→frustration→hope→confidence→urgency","hook|what-is|what-could-be|what-could-be|what-could-be|evaluation|action" +5,Sales Pitch,"sales, pitch, prospect, close, deal","7-10","1.Personalized Hook 2.Their Problem 3.Cost of Inaction 4.Your Solution 5.Proof/Case Studies 6.Differentiators 7.Pricing/ROI 8.Objection Handling 9.CTA 10.Next Steps",Close deal win customer,Qualified prospects,Consultative trustworthy,Pain→Agitate→Solve→Prove,ROI case study metrics,Sandler Sales Training,"connection→frustration→fear→hope→trust→confidence→urgency","hook|what-is|what-is|what-could-be|proof|what-could-be|evaluation|trust|action|action" +6,Nancy Duarte Sparkline,"duarte, sparkline, story, transformation, resonate","Varies","Alternate: What Is→What Could Be→What Is→What Could Be→New Bliss",Transform audience perspective,Any audience needing persuasion,Inspiring visionary,Tension→Release→Tension→Release→Resolution,Audience transformation,Nancy Duarte Resonate,"frustration→hope→frustration→hope→relief","what-is|what-could-be|what-is|what-could-be|new-bliss" +7,Problem-Solution-Benefit,"psb, simple, clear, benefit, value","3-5","1.Problem Statement 2.Solution Introduction 3.Key Benefits 4.Proof 5.CTA",Quick persuasion simple message,Time-pressed audience,Direct clear,Problem→Solution→Outcome,Core value metrics,Marketing fundamentals,"frustration→hope→confidence→urgency","what-is|what-could-be|what-could-be|proof|action" +8,Quarterly Business Review,"qbr, business review, internal, stakeholder","10-15","1.Executive Summary 2.Goals vs Results 3.Key Metrics 4.Wins 5.Challenges 6.Learnings 7.Customer Insights 8.Competitive Update 9.Next Quarter Goals 10.Resource Needs",Update stakeholders on progress,Internal leadership,Professional factual,Review→Analyze→Plan,KPIs OKRs progress %,Internal communications,"clarity→trust→confidence→evaluation→hope","summary|proof|proof|celebration|what-is|insight|trust|evaluation|what-could-be|action" +9,Team All-Hands,"all-hands, company, internal, culture, update","8-12","1.Opening/Energy 2.Company Wins 3.Metrics Dashboard 4.Team Spotlights 5.Product Updates 6.Customer Stories 7.Challenges/Learnings 8.Roadmap Preview 9.Q&A 10.Closing Motivation",Align and motivate team,All employees,Transparent inspiring,Celebrate→Update→Align→Energize,Company-wide KPIs,Internal communications,"warmth→confidence→trust→connection→hope→urgency","hook|celebration|proof|connection|what-could-be|trust|what-is|what-could-be|interaction|action" +10,Conference Talk,"conference, talk, keynote, public speaking, thought leadership","15-25","1.Hook/Story 2.Credibility 3.Big Idea 4.Point 1 + Evidence 5.Point 2 + Evidence 6.Point 3 + Evidence 7.Synthesis 8.Call to Action 9.Q&A Prep",Establish thought leadership,Conference attendees,Expert engaging,Story→Teach→Inspire,Audience engagement social shares,TED Talk guidelines,"curiosity→trust→hope→confidence→confidence→confidence→clarity→urgency","hook|trust|what-could-be|proof|proof|proof|synthesis|action|interaction" +11,Workshop Training,"workshop, training, education, how-to, tutorial","20-40","1.Welcome/Objectives 2.Agenda 3.Concept 1 4.Exercise 1 5.Concept 2 6.Exercise 2 7.Concept 3 8.Exercise 3 9.Synthesis 10.Resources 11.Q&A",Teach practical skills,Learners trainees,Patient instructive,Learn→Practice→Apply→Reflect,Skill acquisition completion,Adult learning principles,"warmth→clarity→confidence→confidence→confidence→confidence→clarity→hope","welcome|structure|teaching|practice|teaching|practice|teaching|practice|synthesis|resources|interaction" +12,Case Study Presentation,"case study, success story, customer, results","8-12","1.Customer Introduction 2.Their Challenge 3.Why They Chose Us 4.Implementation 5.Solution Details 6.Results/Metrics 7.Customer Quote 8.Lessons Learned 9.Applicability 10.CTA",Prove value through example,Prospects similar to case,Authentic factual,Challenge→Journey→Transformation,Before/after metrics ROI,Marketing case study best practices,"connection→frustration→trust→hope→confidence→celebration→trust→clarity→urgency","connection|what-is|trust|what-could-be|what-could-be|proof|trust|insight|what-could-be|action" +13,Competitive Analysis,"competitive, analysis, comparison, market","6-10","1.Market Landscape 2.Competitor Overview 3.Feature Comparison Matrix 4.Pricing Comparison 5.Strengths/Weaknesses 6.Our Differentiation 7.Market Positioning 8.Strategic Recommendations",Inform strategic decisions,Internal leadership,Analytical objective,Landscape→Analysis→Strategy,Market share feature gaps,Competitive intelligence,"clarity→evaluation→evaluation→evaluation→clarity→hope→confidence→urgency","overview|evaluation|comparison|comparison|analysis|what-could-be|proof|action" +14,Board Meeting Deck,"board, governance, investor update, quarterly","15-20","1.Agenda 2.Executive Summary 3.Financial Overview 4.Key Metrics 5.Product Update 6.Sales/Marketing 7.Operations 8.Team/Hiring 9.Risks/Challenges 10.Strategic Initiatives 11.Upcoming Milestones 12.Ask/Discussion",Update board on company status,Board members,Professional detailed,Report→Analyze→Discuss→Decide,All key business metrics,Board governance best practices,"clarity→confidence→trust→trust→confidence→confidence→trust→connection→evaluation→hope→confidence→urgency","structure|summary|proof|proof|proof|proof|proof|trust|what-is|what-could-be|proof|action" +15,Webinar Presentation,"webinar, online, education, lead gen","20-30","1.Welcome/Housekeeping 2.Presenter Intro 3.Agenda 4.Hook/Problem 5.Teaching Content 6.Case Study 7.Product Introduction 8.Demo 9.Offer/CTA 10.Q&A 11.Resources",Generate leads educate prospects,Webinar registrants,Educational helpful,Teach→Demonstrate→Offer,Registrations attendance conversion,Webinar marketing best practices,"warmth→trust→clarity→curiosity→confidence→trust→hope→confidence→urgency→connection→clarity","welcome|trust|structure|hook|teaching|trust|what-could-be|proof|action|interaction|resources" diff --git a/.claude/skills/design-system/data/slide-typography.csv b/.claude/skills/design-system/data/slide-typography.csv new file mode 100644 index 0000000..204243c --- /dev/null +++ b/.claude/skills/design-system/data/slide-typography.csv @@ -0,0 +1,15 @@ +content_type,primary_size,secondary_size,accent_size,weight_contrast,letter_spacing,line_height +hero-statement,120px,32px,14px,700-400,tight,1.0 +metric-callout,96px,18px,12px,700-500,normal,1.1 +feature-grid,28px,16px,12px,600-400,normal,1.4 +quote-block,36px,18px,14px,400-italic,loose,1.5 +data-insight,48px,20px,14px,700-400,normal,1.2 +cta-action,64px,24px,16px,700-500,tight,1.1 +title-only,80px,24px,14px,700-400,tight,1.0 +subtitle-heavy,56px,28px,16px,600-400,normal,1.2 +body-focus,24px,18px,14px,500-400,normal,1.6 +comparison,32px,16px,12px,600-400,normal,1.3 +timeline,28px,16px,12px,500-400,normal,1.4 +pricing,48px,20px,14px,700-500,normal,1.2 +team,24px,16px,14px,600-400,normal,1.4 +testimonial,32px,20px,14px,400-italic,loose,1.5 diff --git a/.claude/skills/design-system/references/component-specs.md b/.claude/skills/design-system/references/component-specs.md new file mode 100644 index 0000000..cc7821b --- /dev/null +++ b/.claude/skills/design-system/references/component-specs.md @@ -0,0 +1,236 @@ +# Component Specifications + +Detailed specs for core components with states and variants. + +## Button + +### Variants + +| Variant | Background | Text | Border | Use Case | +|---------|------------|------|--------|----------| +| default | primary | white | none | Primary actions | +| secondary | gray-100 | gray-900 | none | Secondary actions | +| outline | transparent | foreground | border | Tertiary actions | +| ghost | transparent | foreground | none | Subtle actions | +| link | transparent | primary | none | Navigation | +| destructive | red-600 | white | none | Dangerous actions | + +### Sizes + +| Size | Height | Padding X | Padding Y | Font Size | Icon Size | +|------|--------|-----------|-----------|-----------|-----------| +| sm | 32px | 12px | 6px | 14px | 16px | +| default | 40px | 16px | 8px | 14px | 18px | +| lg | 48px | 24px | 12px | 16px | 20px | +| icon | 40px | 0 | 0 | - | 18px | + +### States + +| State | Background | Text | Opacity | Cursor | +|-------|------------|------|---------|--------| +| default | token | token | 1 | pointer | +| hover | darker | token | 1 | pointer | +| active | darkest | token | 1 | pointer | +| focus | token | token | 1 | pointer | +| disabled | muted | muted-fg | 0.5 | not-allowed | +| loading | token | token | 0.7 | wait | + +### Anatomy + +``` +┌─────────────────────────────────────┐ +│ [icon] Label Text [icon] │ +└─────────────────────────────────────┘ + ↑ ↑ + leading icon trailing icon +``` + +--- + +## Input + +### Variants + +| Variant | Description | +|---------|-------------| +| default | Standard text input | +| textarea | Multi-line text | +| select | Dropdown selection | +| checkbox | Boolean toggle | +| radio | Single selection | +| switch | Toggle switch | + +### Sizes + +| Size | Height | Padding | Font Size | +|------|--------|---------|-----------| +| sm | 32px | 8px 12px | 14px | +| default | 40px | 8px 12px | 14px | +| lg | 48px | 12px 16px | 16px | + +### States + +| State | Border | Background | Ring | +|-------|--------|------------|------| +| default | gray-300 | white | none | +| hover | gray-400 | white | none | +| focus | primary | white | primary/20% | +| error | red-500 | white | red/20% | +| disabled | gray-200 | gray-100 | none | + +### Anatomy + +``` +Label (optional) +┌─────────────────────────────────────┐ +│ [icon] Placeholder/Value [action] │ +└─────────────────────────────────────┘ +Helper text or error message +``` + +--- + +## Card + +### Variants + +| Variant | Shadow | Border | Use Case | +|---------|--------|--------|----------| +| default | sm | 1px | Standard card | +| elevated | lg | none | Prominent content | +| outline | none | 1px | Subtle container | +| interactive | sm→md | 1px | Clickable card | + +### Anatomy + +``` +┌─────────────────────────────────────┐ +│ Card Header │ +│ Title │ +│ Description │ +├─────────────────────────────────────┤ +│ Card Content │ +│ Main content area │ +│ │ +├─────────────────────────────────────┤ +│ Card Footer │ +│ Actions │ +└─────────────────────────────────────┘ +``` + +### Spacing + +| Area | Padding | +|------|---------| +| header | 24px 24px 0 | +| content | 24px | +| footer | 0 24px 24px | +| gap | 16px | + +--- + +## Badge + +### Variants + +| Variant | Background | Text | +|---------|------------|------| +| default | primary | white | +| secondary | gray-100 | gray-900 | +| outline | transparent | foreground | +| destructive | red-600 | white | +| success | green-600 | white | +| warning | yellow-500 | gray-900 | + +### Sizes + +| Size | Padding | Font Size | Height | +|------|---------|-----------|--------| +| sm | 4px 8px | 11px | 20px | +| default | 4px 10px | 12px | 24px | +| lg | 6px 12px | 14px | 28px | + +--- + +## Alert + +### Variants + +| Variant | Icon | Background | Border | +|---------|------|------------|--------| +| default | info | gray-50 | gray-200 | +| destructive | alert | red-50 | red-200 | +| success | check | green-50 | green-200 | +| warning | warning | yellow-50 | yellow-200 | + +### Anatomy + +``` +┌─────────────────────────────────────┐ +│ [icon] Title [×]│ +│ Description text │ +└─────────────────────────────────────┘ +``` + +--- + +## Dialog + +### Sizes + +| Size | Max Width | Use Case | +|------|-----------|----------| +| sm | 384px | Simple confirmations | +| default | 512px | Standard dialogs | +| lg | 640px | Complex forms | +| xl | 768px | Data-heavy dialogs | +| full | 100% - 32px | Full-screen on mobile | + +### Anatomy + +``` +┌───────────────────────────────────────┐ +│ Dialog Header [×]│ +│ Title │ +│ Description │ +├───────────────────────────────────────┤ +│ Dialog Content │ +│ Scrollable if needed │ +│ │ +├───────────────────────────────────────┤ +│ Dialog Footer │ +│ [Cancel] [Confirm]│ +└───────────────────────────────────────┘ +``` + +--- + +## Table + +### Row States + +| State | Background | Use Case | +|-------|------------|----------| +| default | white | Normal row | +| hover | gray-50 | Mouse over | +| selected | primary/10% | Selected row | +| striped | gray-50/white | Alternating | + +### Cell Alignment + +| Content Type | Alignment | +|--------------|-----------| +| Text | Left | +| Numbers | Right | +| Status/Badge | Center | +| Actions | Right | + +### Spacing + +| Element | Value | +|---------|-------| +| cell padding | 12px 16px | +| header padding | 12px 16px | +| row height (compact) | 40px | +| row height (default) | 48px | +| row height (comfortable) | 56px | diff --git a/.claude/skills/design-system/references/component-tokens.md b/.claude/skills/design-system/references/component-tokens.md new file mode 100644 index 0000000..912ab9d --- /dev/null +++ b/.claude/skills/design-system/references/component-tokens.md @@ -0,0 +1,214 @@ +# Component Tokens + +Component-specific tokens referencing semantic layer. + +## Button Tokens + +```css +:root { + /* Default (Primary) */ + --button-bg: var(--color-primary); + --button-fg: var(--color-primary-foreground); + --button-hover-bg: var(--color-primary-hover); + --button-active-bg: var(--color-primary-active); + + /* Secondary */ + --button-secondary-bg: var(--color-secondary); + --button-secondary-fg: var(--color-secondary-foreground); + --button-secondary-hover-bg: var(--color-secondary-hover); + + /* Outline */ + --button-outline-border: var(--color-border); + --button-outline-fg: var(--color-foreground); + --button-outline-hover-bg: var(--color-accent); + + /* Ghost */ + --button-ghost-fg: var(--color-foreground); + --button-ghost-hover-bg: var(--color-accent); + + /* Destructive */ + --button-destructive-bg: var(--color-destructive); + --button-destructive-fg: var(--color-destructive-foreground); + --button-destructive-hover-bg: var(--color-destructive-hover); + + /* Sizing */ + --button-padding-x: var(--space-4); + --button-padding-y: var(--space-2); + --button-padding-x-sm: var(--space-3); + --button-padding-y-sm: var(--space-1-5); + --button-padding-x-lg: var(--space-6); + --button-padding-y-lg: var(--space-3); + + /* Shape */ + --button-radius: var(--radius-md); + --button-font-size: var(--font-size-sm); + --button-font-weight: var(--font-weight-medium); +} +``` + +## Input Tokens + +```css +:root { + /* Background & Border */ + --input-bg: var(--color-background); + --input-border: var(--color-input); + --input-fg: var(--color-foreground); + + /* Placeholder */ + --input-placeholder: var(--color-muted-foreground); + + /* Focus */ + --input-focus-border: var(--color-ring); + --input-focus-ring: var(--color-ring); + + /* Error */ + --input-error-border: var(--color-error); + --input-error-fg: var(--color-error); + + /* Disabled */ + --input-disabled-bg: var(--color-muted); + --input-disabled-fg: var(--color-muted-foreground); + + /* Sizing */ + --input-padding-x: var(--space-3); + --input-padding-y: var(--space-2); + --input-radius: var(--radius-md); + --input-font-size: var(--font-size-sm); +} +``` + +## Card Tokens + +```css +:root { + /* Background & Border */ + --card-bg: var(--color-card); + --card-fg: var(--color-card-foreground); + --card-border: var(--color-border); + + /* Shadow */ + --card-shadow: var(--shadow-default); + --card-shadow-hover: var(--shadow-md); + + /* Spacing */ + --card-padding: var(--space-6); + --card-padding-sm: var(--space-4); + --card-gap: var(--space-4); + + /* Shape */ + --card-radius: var(--radius-lg); +} +``` + +## Badge Tokens + +```css +:root { + /* Default */ + --badge-bg: var(--color-primary); + --badge-fg: var(--color-primary-foreground); + + /* Secondary */ + --badge-secondary-bg: var(--color-secondary); + --badge-secondary-fg: var(--color-secondary-foreground); + + /* Outline */ + --badge-outline-border: var(--color-border); + --badge-outline-fg: var(--color-foreground); + + /* Destructive */ + --badge-destructive-bg: var(--color-destructive); + --badge-destructive-fg: var(--color-destructive-foreground); + + /* Sizing */ + --badge-padding-x: var(--space-2-5); + --badge-padding-y: var(--space-0-5); + --badge-radius: var(--radius-full); + --badge-font-size: var(--font-size-xs); +} +``` + +## Alert Tokens + +```css +:root { + /* Default */ + --alert-bg: var(--color-background); + --alert-fg: var(--color-foreground); + --alert-border: var(--color-border); + + /* Destructive */ + --alert-destructive-bg: var(--color-destructive); + --alert-destructive-fg: var(--color-destructive-foreground); + + /* Spacing */ + --alert-padding: var(--space-4); + --alert-radius: var(--radius-lg); +} +``` + +## Dialog/Modal Tokens + +```css +:root { + /* Overlay */ + --dialog-overlay-bg: rgb(0 0 0 / 0.5); + + /* Content */ + --dialog-bg: var(--color-background); + --dialog-fg: var(--color-foreground); + --dialog-border: var(--color-border); + --dialog-shadow: var(--shadow-lg); + + /* Spacing */ + --dialog-padding: var(--space-6); + --dialog-radius: var(--radius-lg); + --dialog-max-width: 32rem; +} +``` + +## Table Tokens + +```css +:root { + /* Header */ + --table-header-bg: var(--color-muted); + --table-header-fg: var(--color-muted-foreground); + + /* Body */ + --table-row-bg: var(--color-background); + --table-row-hover-bg: var(--color-muted); + --table-row-fg: var(--color-foreground); + + /* Border */ + --table-border: var(--color-border); + + /* Spacing */ + --table-cell-padding-x: var(--space-4); + --table-cell-padding-y: var(--space-3); +} +``` + +## Usage Example + +```css +.button { + background: var(--button-bg); + color: var(--button-fg); + padding: var(--button-padding-y) var(--button-padding-x); + border-radius: var(--button-radius); + font-size: var(--button-font-size); + font-weight: var(--button-font-weight); + transition: background var(--duration-fast); +} + +.button:hover { + background: var(--button-hover-bg); +} + +.button.secondary { + background: var(--button-secondary-bg); + color: var(--button-secondary-fg); +} +``` diff --git a/.claude/skills/design-system/references/primitive-tokens.md b/.claude/skills/design-system/references/primitive-tokens.md new file mode 100644 index 0000000..eb251a2 --- /dev/null +++ b/.claude/skills/design-system/references/primitive-tokens.md @@ -0,0 +1,203 @@ +# Primitive Tokens + +Raw design values - foundation of the design system. + +## Color Scales + +### Gray Scale + +```css +:root { + --color-gray-50: #F9FAFB; + --color-gray-100: #F3F4F6; + --color-gray-200: #E5E7EB; + --color-gray-300: #D1D5DB; + --color-gray-400: #9CA3AF; + --color-gray-500: #6B7280; + --color-gray-600: #4B5563; + --color-gray-700: #374151; + --color-gray-800: #1F2937; + --color-gray-900: #111827; + --color-gray-950: #030712; +} +``` + +### Primary Colors (Blue) + +```css +:root { + --color-blue-50: #EFF6FF; + --color-blue-100: #DBEAFE; + --color-blue-200: #BFDBFE; + --color-blue-300: #93C5FD; + --color-blue-400: #60A5FA; + --color-blue-500: #3B82F6; + --color-blue-600: #2563EB; + --color-blue-700: #1D4ED8; + --color-blue-800: #1E40AF; + --color-blue-900: #1E3A8A; +} +``` + +### Status Colors + +```css +:root { + /* Success - Green */ + --color-green-500: #22C55E; + --color-green-600: #16A34A; + + /* Warning - Yellow */ + --color-yellow-500: #EAB308; + --color-yellow-600: #CA8A04; + + /* Error - Red */ + --color-red-500: #EF4444; + --color-red-600: #DC2626; + + /* Info - Blue */ + --color-info: var(--color-blue-500); +} +``` + +## Spacing Scale + +4px base unit system. + +```css +:root { + --space-0: 0; + --space-px: 1px; + --space-0-5: 0.125rem; /* 2px */ + --space-1: 0.25rem; /* 4px */ + --space-1-5: 0.375rem; /* 6px */ + --space-2: 0.5rem; /* 8px */ + --space-2-5: 0.625rem; /* 10px */ + --space-3: 0.75rem; /* 12px */ + --space-3-5: 0.875rem; /* 14px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-7: 1.75rem; /* 28px */ + --space-8: 2rem; /* 32px */ + --space-9: 2.25rem; /* 36px */ + --space-10: 2.5rem; /* 40px */ + --space-12: 3rem; /* 48px */ + --space-14: 3.5rem; /* 56px */ + --space-16: 4rem; /* 64px */ + --space-20: 5rem; /* 80px */ + --space-24: 6rem; /* 96px */ +} +``` + +## Typography Scale + +```css +:root { + /* Font Sizes */ + --font-size-xs: 0.75rem; /* 12px */ + --font-size-sm: 0.875rem; /* 14px */ + --font-size-base: 1rem; /* 16px */ + --font-size-lg: 1.125rem; /* 18px */ + --font-size-xl: 1.25rem; /* 20px */ + --font-size-2xl: 1.5rem; /* 24px */ + --font-size-3xl: 1.875rem; /* 30px */ + --font-size-4xl: 2.25rem; /* 36px */ + --font-size-5xl: 3rem; /* 48px */ + + /* Line Heights */ + --leading-none: 1; + --leading-tight: 1.25; + --leading-snug: 1.375; + --leading-normal: 1.5; + --leading-relaxed: 1.625; + --leading-loose: 2; + + /* Font Weights */ + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + /* Letter Spacing */ + --tracking-tighter: -0.05em; + --tracking-tight: -0.025em; + --tracking-normal: 0; + --tracking-wide: 0.025em; + --tracking-wider: 0.05em; +} +``` + +## Border Radius + +```css +:root { + --radius-none: 0; + --radius-sm: 0.125rem; /* 2px */ + --radius-default: 0.25rem; /* 4px */ + --radius-md: 0.375rem; /* 6px */ + --radius-lg: 0.5rem; /* 8px */ + --radius-xl: 0.75rem; /* 12px */ + --radius-2xl: 1rem; /* 16px */ + --radius-3xl: 1.5rem; /* 24px */ + --radius-full: 9999px; +} +``` + +## Shadows + +```css +:root { + --shadow-none: none; + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-default: 0 1px 3px 0 rgb(0 0 0 / 0.1), + 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), + 0 2px 4px -2px rgb(0 0 0 / 0.1); + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), + 0 4px 6px -4px rgb(0 0 0 / 0.1); + --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), + 0 8px 10px -6px rgb(0 0 0 / 0.1); + --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); + --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); +} +``` + +## Motion / Duration + +```css +:root { + --duration-75: 75ms; + --duration-100: 100ms; + --duration-150: 150ms; + --duration-200: 200ms; + --duration-300: 300ms; + --duration-500: 500ms; + --duration-700: 700ms; + --duration-1000: 1000ms; + + /* Semantic durations */ + --duration-fast: var(--duration-150); + --duration-normal: var(--duration-200); + --duration-slow: var(--duration-300); +} +``` + +## Z-Index Scale + +```css +:root { + --z-auto: auto; + --z-0: 0; + --z-10: 10; + --z-20: 20; + --z-30: 30; + --z-40: 40; + --z-50: 50; + --z-dropdown: 1000; + --z-sticky: 1100; + --z-modal: 1200; + --z-popover: 1300; + --z-tooltip: 1400; +} +``` diff --git a/.claude/skills/design-system/references/semantic-tokens.md b/.claude/skills/design-system/references/semantic-tokens.md new file mode 100644 index 0000000..b441010 --- /dev/null +++ b/.claude/skills/design-system/references/semantic-tokens.md @@ -0,0 +1,215 @@ +# Semantic Tokens + +Purpose-based aliases referencing primitive tokens. + +## Color Semantics + +### Background & Foreground + +```css +:root { + /* Page background */ + --color-background: var(--color-gray-50); + --color-foreground: var(--color-gray-900); + + /* Card/surface background */ + --color-card: white; + --color-card-foreground: var(--color-gray-900); + + /* Popover/dropdown */ + --color-popover: white; + --color-popover-foreground: var(--color-gray-900); +} +``` + +### Primary + +```css +:root { + --color-primary: var(--color-blue-600); + --color-primary-hover: var(--color-blue-700); + --color-primary-active: var(--color-blue-800); + --color-primary-foreground: white; +} +``` + +### Secondary + +```css +:root { + --color-secondary: var(--color-gray-100); + --color-secondary-hover: var(--color-gray-200); + --color-secondary-foreground: var(--color-gray-900); +} +``` + +### Muted + +```css +:root { + --color-muted: var(--color-gray-100); + --color-muted-foreground: var(--color-gray-500); +} +``` + +### Accent + +```css +:root { + --color-accent: var(--color-gray-100); + --color-accent-foreground: var(--color-gray-900); +} +``` + +### Destructive + +```css +:root { + --color-destructive: var(--color-red-600); + --color-destructive-hover: var(--color-red-700); + --color-destructive-foreground: white; +} +``` + +### Status Colors + +```css +:root { + --color-success: var(--color-green-600); + --color-success-foreground: white; + + --color-warning: var(--color-yellow-500); + --color-warning-foreground: var(--color-gray-900); + + --color-error: var(--color-red-600); + --color-error-foreground: white; + + --color-info: var(--color-blue-500); + --color-info-foreground: white; +} +``` + +### Border & Ring + +```css +:root { + --color-border: var(--color-gray-200); + --color-input: var(--color-gray-200); + --color-ring: var(--color-blue-500); +} +``` + +## Spacing Semantics + +```css +:root { + /* Component internal spacing */ + --spacing-component-xs: var(--space-1); + --spacing-component-sm: var(--space-2); + --spacing-component: var(--space-3); + --spacing-component-lg: var(--space-4); + + /* Section spacing */ + --spacing-section-sm: var(--space-8); + --spacing-section: var(--space-12); + --spacing-section-lg: var(--space-16); + + /* Page margins */ + --spacing-page-x: var(--space-4); + --spacing-page-y: var(--space-6); +} +``` + +## Typography Semantics + +```css +:root { + /* Headings */ + --font-heading: var(--font-size-2xl); + --font-heading-lg: var(--font-size-3xl); + --font-heading-xl: var(--font-size-4xl); + + /* Body */ + --font-body: var(--font-size-base); + --font-body-sm: var(--font-size-sm); + --font-body-lg: var(--font-size-lg); + + /* Labels & Captions */ + --font-label: var(--font-size-sm); + --font-caption: var(--font-size-xs); +} +``` + +## Interactive States + +```css +:root { + /* Focus ring */ + --ring-width: 2px; + --ring-offset: 2px; + --ring-color: var(--color-ring); + + /* Opacity for disabled */ + --opacity-disabled: 0.5; + + /* Transitions */ + --transition-colors: color, background-color, border-color; + --transition-transform: transform; + --transition-all: all; +} +``` + +## Dark Mode Overrides + +```css +.dark { + --color-background: var(--color-gray-950); + --color-foreground: var(--color-gray-50); + + --color-card: var(--color-gray-900); + --color-card-foreground: var(--color-gray-50); + + --color-popover: var(--color-gray-900); + --color-popover-foreground: var(--color-gray-50); + + --color-muted: var(--color-gray-800); + --color-muted-foreground: var(--color-gray-400); + + --color-secondary: var(--color-gray-800); + --color-secondary-foreground: var(--color-gray-50); + + --color-accent: var(--color-gray-800); + --color-accent-foreground: var(--color-gray-50); + + --color-border: var(--color-gray-800); + --color-input: var(--color-gray-800); +} +``` + +## Usage Patterns + +### Applying Semantic Tokens + +```css +/* Good - uses semantic tokens */ +.card { + background: var(--color-card); + color: var(--color-card-foreground); + border: 1px solid var(--color-border); +} + +/* Bad - uses primitive tokens directly */ +.card { + background: var(--color-gray-50); + color: var(--color-gray-900); +} +``` + +### Theme Switching + +Semantic tokens enable instant theme switching: + +```js +// Toggle dark mode +document.documentElement.classList.toggle('dark'); +``` diff --git a/.claude/skills/design-system/references/states-and-variants.md b/.claude/skills/design-system/references/states-and-variants.md new file mode 100644 index 0000000..64b808a --- /dev/null +++ b/.claude/skills/design-system/references/states-and-variants.md @@ -0,0 +1,241 @@ +# States and Variants + +Component state definitions and variant patterns. + +## Interactive States + +### State Definitions + +| State | Trigger | Visual Change | +|-------|---------|---------------| +| default | None | Base appearance | +| hover | Mouse over | Slight color shift | +| focus | Tab/click | Focus ring | +| active | Mouse down | Darkest color | +| disabled | disabled attr | Reduced opacity | +| loading | Async action | Spinner + opacity | + +### State Priority + +When multiple states apply, priority (highest to lowest): + +1. disabled +2. loading +3. active +4. focus +5. hover +6. default + +### State Transitions + +```css +/* Standard transition for interactive elements */ +.interactive { + transition-property: color, background-color, border-color, box-shadow; + transition-duration: var(--duration-fast); + transition-timing-function: ease-in-out; +} +``` + +| Transition | Duration | Easing | +|------------|----------|--------| +| Color changes | 150ms | ease-in-out | +| Background | 150ms | ease-in-out | +| Transform | 200ms | ease-out | +| Opacity | 150ms | ease | +| Shadow | 200ms | ease-out | + +## Focus States + +### Focus Ring Spec + +```css +/* Standard focus ring */ +.focusable:focus-visible { + outline: none; + box-shadow: 0 0 0 var(--ring-offset) var(--color-background), + 0 0 0 calc(var(--ring-offset) + var(--ring-width)) var(--ring-color); +} +``` + +| Property | Value | +|----------|-------| +| Ring width | 2px | +| Ring offset | 2px | +| Ring color | primary (blue-500) | +| Offset color | background | + +### Focus Within + +```css +/* Container focus when child is focused */ +.container:focus-within { + border-color: var(--color-ring); +} +``` + +## Disabled States + +### Visual Treatment + +```css +.disabled { + opacity: var(--opacity-disabled); /* 0.5 */ + pointer-events: none; + cursor: not-allowed; +} +``` + +| Property | Disabled Value | +|----------|----------------| +| Opacity | 50% | +| Pointer events | none | +| Cursor | not-allowed | +| Background | muted | +| Color | muted-foreground | + +### Accessibility + +- Use `aria-disabled="true"` for semantic disabled +- Use `disabled` attribute for form elements +- Maintain sufficient contrast (3:1 minimum) + +## Loading States + +### Spinner Placement + +| Component | Spinner Position | +|-----------|------------------| +| Button | Replace icon or center | +| Input | Trailing position | +| Card | Center overlay | +| Page | Center of viewport | + +### Loading Treatment + +```css +.loading { + position: relative; + pointer-events: none; +} + +.loading::after { + content: ''; + /* spinner styles */ +} + +.loading > * { + opacity: 0.7; +} +``` + +## Error States + +### Visual Indicators + +```css +.error { + border-color: var(--color-error); + color: var(--color-error); +} + +.error:focus-visible { + box-shadow: 0 0 0 2px var(--color-background), + 0 0 0 4px var(--color-error); +} +``` + +| Element | Error Treatment | +|---------|-----------------| +| Input border | red-500 | +| Input focus ring | red/20% | +| Helper text | red-600 | +| Icon | red-500 | + +### Error Messages + +- Position below input +- Use error color +- Include icon for accessibility +- Clear on valid input + +## Variant Patterns + +### Color Variants + +```css +/* Pattern for color variants */ +.component { + --component-bg: var(--color-primary); + --component-fg: var(--color-primary-foreground); + background: var(--component-bg); + color: var(--component-fg); +} + +.component.secondary { + --component-bg: var(--color-secondary); + --component-fg: var(--color-secondary-foreground); +} + +.component.destructive { + --component-bg: var(--color-destructive); + --component-fg: var(--color-destructive-foreground); +} +``` + +### Size Variants + +```css +/* Pattern for size variants */ +.component { + --component-height: 40px; + --component-padding: var(--space-4); + --component-font: var(--font-size-sm); +} + +.component.sm { + --component-height: 32px; + --component-padding: var(--space-3); + --component-font: var(--font-size-xs); +} + +.component.lg { + --component-height: 48px; + --component-padding: var(--space-6); + --component-font: var(--font-size-base); +} +``` + +## Accessibility Requirements + +### Color Contrast + +| Element | Minimum Ratio | +|---------|---------------| +| Normal text | 4.5:1 | +| Large text (18px+) | 3:1 | +| UI components | 3:1 | +| Focus indicator | 3:1 | + +### State Indicators + +- Never rely on color alone +- Use icons, text, or patterns +- Ensure focus is visible +- Provide loading announcements + +### ARIA States + +```html + + + + + + + + +Error message +``` diff --git a/.claude/skills/design-system/references/tailwind-integration.md b/.claude/skills/design-system/references/tailwind-integration.md new file mode 100644 index 0000000..c632788 --- /dev/null +++ b/.claude/skills/design-system/references/tailwind-integration.md @@ -0,0 +1,251 @@ +# Tailwind Integration + +Map design system tokens to Tailwind CSS configuration. + +## CSS Variables Setup + +### Base Layer + +```css +/* globals.css */ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + /* Primitives */ + --color-blue-600: 37 99 235; /* HSL: 217 91% 60% */ + + /* Semantic */ + --background: 0 0% 100%; + --foreground: 222 47% 11%; + --primary: 217 91% 60%; + --primary-foreground: 0 0% 100%; + --secondary: 220 14% 96%; + --secondary-foreground: 222 47% 11%; + --muted: 220 14% 96%; + --muted-foreground: 220 9% 46%; + --accent: 220 14% 96%; + --accent-foreground: 222 47% 11%; + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 100%; + --border: 220 13% 91%; + --input: 220 13% 91%; + --ring: 217 91% 60%; + --radius: 0.5rem; + } + + .dark { + --background: 222 47% 4%; + --foreground: 210 40% 98%; + --primary: 217 91% 60%; + --primary-foreground: 0 0% 100%; + --secondary: 217 33% 17%; + --secondary-foreground: 210 40% 98%; + --muted: 217 33% 17%; + --muted-foreground: 215 20% 65%; + --accent: 217 33% 17%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62% 30%; + --destructive-foreground: 0 0% 100%; + --border: 217 33% 17%; + --input: 217 33% 17%; + --ring: 217 91% 60%; + } +} +``` + +## Tailwind Config + +### tailwind.config.ts + +```typescript +import type { Config } from 'tailwindcss' + +const config: Config = { + darkMode: ['class'], + content: ['./src/**/*.{ts,tsx}'], + theme: { + extend: { + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + }, + }, + plugins: [], +} + +export default config +``` + +## HSL Format Benefits + +Using HSL without function allows opacity modifiers: + +```tsx +// With HSL format (space-separated) +
// 50% opacity +
// 80% opacity + +// CSS output +background-color: hsl(217 91% 60% / 0.5); +``` + +## Component Classes + +### Button Example + +```css +@layer components { + .btn { + @apply inline-flex items-center justify-center + rounded-md font-medium + transition-colors + focus-visible:outline-none focus-visible:ring-2 + focus-visible:ring-ring focus-visible:ring-offset-2 + disabled:pointer-events-none disabled:opacity-50; + } + + .btn-default { + @apply bg-primary text-primary-foreground + hover:bg-primary/90; + } + + .btn-secondary { + @apply bg-secondary text-secondary-foreground + hover:bg-secondary/80; + } + + .btn-outline { + @apply border border-input bg-background + hover:bg-accent hover:text-accent-foreground; + } + + .btn-ghost { + @apply hover:bg-accent hover:text-accent-foreground; + } + + .btn-destructive { + @apply bg-destructive text-destructive-foreground + hover:bg-destructive/90; + } + + /* Sizes */ + .btn-sm { @apply h-8 px-3 text-xs; } + .btn-md { @apply h-10 px-4 text-sm; } + .btn-lg { @apply h-12 px-6 text-base; } +} +``` + +## Spacing Integration + +```typescript +// tailwind.config.ts +theme: { + extend: { + spacing: { + // Map to CSS variables if needed + 'section': 'var(--spacing-section)', + 'component': 'var(--spacing-component)', + } + } +} +``` + +## Animation Tokens + +```typescript +// tailwind.config.ts +theme: { + extend: { + transitionDuration: { + fast: '150ms', + normal: '200ms', + slow: '300ms', + }, + keyframes: { + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + } +} +``` + +## Dark Mode Toggle + +```typescript +// Toggle dark mode +function toggleDarkMode() { + document.documentElement.classList.toggle('dark') +} + +// System preference +if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + document.documentElement.classList.add('dark') +} +``` + +## shadcn/ui Alignment + +This configuration aligns with shadcn/ui conventions: + +- Same CSS variable naming +- Same HSL format +- Same color scale structure +- Compatible with `npx shadcn@latest add` commands + +### Using with shadcn/ui + +```bash +# Initialize (uses same token structure) +npx shadcn@latest init + +# Add components (styled with these tokens) +npx shadcn@latest add button card input +``` + +Components will automatically use your design system tokens. diff --git a/.claude/skills/design-system/references/token-architecture.md b/.claude/skills/design-system/references/token-architecture.md new file mode 100644 index 0000000..e13ed2b --- /dev/null +++ b/.claude/skills/design-system/references/token-architecture.md @@ -0,0 +1,224 @@ +# Token Architecture + +Three-layer token system for scalable, themeable design systems. + +## Layer Overview + +``` +┌─────────────────────────────────────────┐ +│ Component Tokens │ Per-component overrides +│ --button-bg, --card-padding │ +├─────────────────────────────────────────┤ +│ Semantic Tokens │ Purpose-based aliases +│ --color-primary, --spacing-section │ +├─────────────────────────────────────────┤ +│ Primitive Tokens │ Raw design values +│ --color-blue-600, --space-4 │ +└─────────────────────────────────────────┘ +``` + +## Why Three Layers? + +| Layer | Purpose | When to Change | +|-------|---------|----------------| +| Primitive | Base values (colors, sizes) | Rarely - foundational | +| Semantic | Meaning assignment | Theme switching | +| Component | Component customization | Per-component needs | + +## Layer 1: Primitive Tokens + +Raw design values without semantic meaning. + +```css +:root { + /* Colors */ + --color-gray-50: #F9FAFB; + --color-gray-900: #111827; + --color-blue-500: #3B82F6; + --color-blue-600: #2563EB; + + /* Spacing (4px base) */ + --space-1: 0.25rem; /* 4px */ + --space-2: 0.5rem; /* 8px */ + --space-4: 1rem; /* 16px */ + --space-6: 1.5rem; /* 24px */ + + /* Typography */ + --font-size-sm: 0.875rem; + --font-size-base: 1rem; + --font-size-lg: 1.125rem; + + /* Radius */ + --radius-sm: 0.25rem; + --radius-default: 0.5rem; + --radius-lg: 0.75rem; + + /* Shadows */ + --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05); + --shadow-default: 0 1px 3px rgb(0 0 0 / 0.1); +} +``` + +## Layer 2: Semantic Tokens + +Purpose-based aliases that reference primitives. + +```css +:root { + /* Background */ + --color-background: var(--color-gray-50); + --color-foreground: var(--color-gray-900); + + /* Primary */ + --color-primary: var(--color-blue-600); + --color-primary-hover: var(--color-blue-700); + + /* Secondary */ + --color-secondary: var(--color-gray-100); + --color-secondary-foreground: var(--color-gray-900); + + /* Muted */ + --color-muted: var(--color-gray-100); + --color-muted-foreground: var(--color-gray-500); + + /* Destructive */ + --color-destructive: var(--color-red-600); + --color-destructive-foreground: white; + + /* Spacing */ + --spacing-component: var(--space-4); + --spacing-section: var(--space-6); +} +``` + +## Layer 3: Component Tokens + +Component-specific tokens referencing semantic layer. + +```css +:root { + /* Button */ + --button-bg: var(--color-primary); + --button-fg: white; + --button-hover-bg: var(--color-primary-hover); + --button-padding-x: var(--space-4); + --button-padding-y: var(--space-2); + --button-radius: var(--radius-default); + + /* Input */ + --input-bg: var(--color-background); + --input-border: var(--color-gray-300); + --input-focus-ring: var(--color-primary); + --input-padding: var(--space-2) var(--space-3); + + /* Card */ + --card-bg: var(--color-background); + --card-border: var(--color-gray-200); + --card-padding: var(--space-4); + --card-radius: var(--radius-lg); + --card-shadow: var(--shadow-default); +} +``` + +## Dark Mode + +Override semantic tokens for dark theme: + +```css +.dark { + --color-background: var(--color-gray-900); + --color-foreground: var(--color-gray-50); + --color-muted: var(--color-gray-800); + --color-muted-foreground: var(--color-gray-400); + --color-secondary: var(--color-gray-800); +} +``` + +## Naming Convention + +``` +--{category}-{item}-{variant}-{state} + +Examples: +--color-primary # category-item +--color-primary-hover # category-item-state +--button-bg-hover # component-property-state +--space-section-sm # category-semantic-variant +``` + +## Categories + +| Category | Examples | +|----------|----------| +| color | primary, secondary, muted, destructive | +| space | 1, 2, 4, 8, section, component | +| font-size | xs, sm, base, lg, xl | +| radius | sm, default, lg, full | +| shadow | sm, default, lg | +| duration | fast, normal, slow | + +## File Organization + +``` +tokens/ +├── primitives.css # Raw values +├── semantic.css # Purpose aliases +├── components.css # Component tokens +└── index.css # Imports all +``` + +Or single file with layer comments: + +```css +/* === PRIMITIVES === */ +:root { ... } + +/* === SEMANTIC === */ +:root { ... } + +/* === COMPONENTS === */ +:root { ... } + +/* === DARK MODE === */ +.dark { ... } +``` + +## Migration from Flat Tokens + +Before (flat): +```css +--button-primary-bg: #2563EB; +--button-secondary-bg: #F3F4F6; +``` + +After (three-layer): +```css +/* Primitive */ +--color-blue-600: #2563EB; +--color-gray-100: #F3F4F6; + +/* Semantic */ +--color-primary: var(--color-blue-600); +--color-secondary: var(--color-gray-100); + +/* Component */ +--button-bg: var(--color-primary); +--button-secondary-bg: var(--color-secondary); +``` + +## W3C DTCG Alignment + +Token JSON format (W3C Design Tokens Community Group): + +```json +{ + "color": { + "blue": { + "600": { + "$value": "#2563EB", + "$type": "color" + } + } + } +} +``` diff --git a/.claude/skills/design-system/scripts/embed-tokens.cjs b/.claude/skills/design-system/scripts/embed-tokens.cjs new file mode 100644 index 0000000..419c104 --- /dev/null +++ b/.claude/skills/design-system/scripts/embed-tokens.cjs @@ -0,0 +1,99 @@ +#!/usr/bin/env node +/** + * embed-tokens.cjs + * Reads design-tokens.css and outputs embeddable inline CSS. + * Use when generating standalone HTML files (infographics, slides, etc.) + * + * Usage: + * node embed-tokens.cjs # Output full CSS + * node embed-tokens.cjs --minimal # Output only commonly used tokens + * node embed-tokens.cjs --style # Wrap in `; + } else { + output = `/* Design Tokens (embedded for standalone HTML) */\n${output}`; + } + + console.log(output); +} catch (err) { + console.error(`Error reading tokens: ${err.message}`); + process.exit(1); +} diff --git a/.claude/skills/design-system/scripts/fetch-background.py b/.claude/skills/design-system/scripts/fetch-background.py new file mode 100644 index 0000000..bcbd357 --- /dev/null +++ b/.claude/skills/design-system/scripts/fetch-background.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python3 +""" +Background Image Fetcher +Fetches real images from Pexels for slide backgrounds. +Uses web scraping (no API key required) or WebFetch tool integration. +""" + +import json +import csv +import re +import sys +from pathlib import Path + +# Project root relative to this script +PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent +TOKENS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' +BACKGROUNDS_CSV = Path(__file__).parent.parent / 'data' / 'slide-backgrounds.csv' + + +def resolve_token_reference(ref: str, tokens: dict) -> str: + """Resolve token reference like {primitive.color.ocean-blue.500} to hex value.""" + if not ref or not ref.startswith('{') or not ref.endswith('}'): + return ref # Already a value, not a reference + + # Parse reference: {primitive.color.ocean-blue.500} + path = ref[1:-1].split('.') # ['primitive', 'color', 'ocean-blue', '500'] + current = tokens + for key in path: + if isinstance(current, dict): + current = current.get(key) + else: + return None # Invalid path + # Return $value if it's a token object + if isinstance(current, dict) and '$value' in current: + return current['$value'] + return current + + +def load_brand_colors(): + """Load colors from assets/design-tokens.json for overlay gradients. + + Resolves semantic token references to actual hex values. + """ + try: + with open(TOKENS_PATH) as f: + tokens = json.load(f) + + colors = tokens.get('primitive', {}).get('color', {}) + semantic = tokens.get('semantic', {}).get('color', {}) + + # Try semantic tokens first (preferred) - resolve references + if semantic: + primary_ref = semantic.get('primary', {}).get('$value') + secondary_ref = semantic.get('secondary', {}).get('$value') + accent_ref = semantic.get('accent', {}).get('$value') + background_ref = semantic.get('background', {}).get('$value') + + primary = resolve_token_reference(primary_ref, tokens) + secondary = resolve_token_reference(secondary_ref, tokens) + accent = resolve_token_reference(accent_ref, tokens) + background = resolve_token_reference(background_ref, tokens) + + if primary and secondary: + return { + 'primary': primary, + 'secondary': secondary, + 'accent': accent or primary, + 'background': background or '#0D0D0D', + } + + # Fallback: find first color palette with 500 value (primary) + primary_keys = ['ocean-blue', 'coral', 'blue', 'primary'] + secondary_keys = ['golden-amber', 'purple', 'amber', 'secondary'] + accent_keys = ['emerald', 'mint', 'green', 'accent'] + + primary_color = None + secondary_color = None + accent_color = None + + for key in primary_keys: + if key in colors and isinstance(colors[key], dict): + primary_color = colors[key].get('500', {}).get('$value') + if primary_color: + break + + for key in secondary_keys: + if key in colors and isinstance(colors[key], dict): + secondary_color = colors[key].get('500', {}).get('$value') + if secondary_color: + break + + for key in accent_keys: + if key in colors and isinstance(colors[key], dict): + accent_color = colors[key].get('500', {}).get('$value') + if accent_color: + break + + background = colors.get('dark', {}).get('800', {}).get('$value', '#0D0D0D') + + return { + 'primary': primary_color or '#3B82F6', + 'secondary': secondary_color or '#F59E0B', + 'accent': accent_color or '#10B981', + 'background': background, + } + except (FileNotFoundError, KeyError, TypeError): + # Fallback defaults + return { + 'primary': '#3B82F6', + 'secondary': '#F59E0B', + 'accent': '#10B981', + 'background': '#0D0D0D', + } + + +def load_backgrounds_config(): + """Load background configuration from CSV.""" + config = {} + try: + with open(BACKGROUNDS_CSV, newline='') as f: + reader = csv.DictReader(f) + for row in reader: + config[row['slide_type']] = row + except FileNotFoundError: + print(f"Warning: {BACKGROUNDS_CSV} not found") + return config + + +def get_overlay_css(style: str, brand_colors: dict) -> str: + """Generate overlay CSS using brand colors from design-tokens.json.""" + overlays = { + 'gradient-dark': f"linear-gradient(135deg, {brand_colors['background']}E6, {brand_colors['background']}B3)", + 'gradient-brand': f"linear-gradient(135deg, {brand_colors['primary']}CC, {brand_colors['secondary']}99)", + 'gradient-accent': f"linear-gradient(135deg, {brand_colors['accent']}99, transparent)", + 'blur-dark': f"rgba(13,13,13,0.8)", + 'desaturate-dark': f"rgba(13,13,13,0.7)", + } + return overlays.get(style, overlays['gradient-dark']) + + +# Curated high-quality images from Pexels (free to use, pre-selected for brand aesthetic) +CURATED_IMAGES = { + 'hero': [ + 'https://images.pexels.com/photos/3861969/pexels-photo-3861969.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/2582937/pexels-photo-2582937.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/1089438/pexels-photo-1089438.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'vision': [ + 'https://images.pexels.com/photos/3183150/pexels-photo-3183150.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3182812/pexels-photo-3182812.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3184291/pexels-photo-3184291.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'team': [ + 'https://images.pexels.com/photos/3184418/pexels-photo-3184418.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3184338/pexels-photo-3184338.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3182773/pexels-photo-3182773.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'testimonial': [ + 'https://images.pexels.com/photos/3184465/pexels-photo-3184465.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/1181622/pexels-photo-1181622.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'cta': [ + 'https://images.pexels.com/photos/3184339/pexels-photo-3184339.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3184298/pexels-photo-3184298.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'problem': [ + 'https://images.pexels.com/photos/3760529/pexels-photo-3760529.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/897817/pexels-photo-897817.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'solution': [ + 'https://images.pexels.com/photos/3184292/pexels-photo-3184292.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3184644/pexels-photo-3184644.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'hook': [ + 'https://images.pexels.com/photos/2582937/pexels-photo-2582937.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/1089438/pexels-photo-1089438.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'social': [ + 'https://images.pexels.com/photos/3184360/pexels-photo-3184360.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3184287/pexels-photo-3184287.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], + 'demo': [ + 'https://images.pexels.com/photos/1181675/pexels-photo-1181675.jpeg?auto=compress&cs=tinysrgb&w=1920', + 'https://images.pexels.com/photos/3861958/pexels-photo-3861958.jpeg?auto=compress&cs=tinysrgb&w=1920', + ], +} + + +def get_curated_images(slide_type: str) -> list: + """Get curated images for slide type.""" + return CURATED_IMAGES.get(slide_type, CURATED_IMAGES.get('hero', [])) + + +def get_pexels_search_url(keywords: str) -> str: + """Generate Pexels search URL for manual lookup.""" + import urllib.parse + return f"https://www.pexels.com/search/{urllib.parse.quote(keywords)}/" + + +def get_background_image(slide_type: str) -> dict: + """ + Get curated image matching slide type and brand aesthetic. + Uses pre-selected Pexels images (no API/scraping needed). + """ + brand_colors = load_brand_colors() + config = load_backgrounds_config() + + slide_config = config.get(slide_type) + overlay_style = 'gradient-dark' + keywords = slide_type + + if slide_config: + keywords = slide_config.get('search_keywords', slide_config.get('image_category', slide_type)) + overlay_style = slide_config.get('overlay_style', 'gradient-dark') + + # Get curated images + urls = get_curated_images(slide_type) + if urls: + return { + 'url': urls[0], + 'all_urls': urls, + 'overlay': get_overlay_css(overlay_style, brand_colors), + 'attribution': 'Photo from Pexels (free to use)', + 'source': 'pexels-curated', + 'search_url': get_pexels_search_url(keywords), + } + + # Fallback: provide search URL for manual selection + return { + 'url': None, + 'overlay': get_overlay_css(overlay_style, brand_colors), + 'keywords': keywords, + 'search_url': get_pexels_search_url(keywords), + 'available_types': list(CURATED_IMAGES.keys()), + } + + +def generate_css_for_background(result: dict, slide_class: str = '.slide-with-bg') -> str: + """Generate CSS for a background slide.""" + if not result.get('url'): + search_url = result.get('search_url', '') + return f"""/* No image scraped. Search manually: {search_url} */ +/* Overlay ready: {result.get('overlay', 'gradient-dark')} */ +""" + + return f"""{slide_class} {{ + background-image: url('{result['url']}'); + background-size: cover; + background-position: center; + position: relative; +}} + +{slide_class}::before {{ + content: ''; + position: absolute; + inset: 0; + background: {result['overlay']}; +}} + +{slide_class} .content {{ + position: relative; + z-index: 1; +}} + +/* {result.get('attribution', 'Pexels')} - {result.get('search_url', '')} */ +""" + + +def main(): + """CLI entry point.""" + import argparse + + parser = argparse.ArgumentParser(description='Get background images for slides') + parser.add_argument('slide_type', nargs='?', help='Slide type (hero, vision, team, etc.)') + parser.add_argument('--list', action='store_true', help='List available slide types') + parser.add_argument('--css', action='store_true', help='Output CSS for the background') + parser.add_argument('--json', action='store_true', help='Output JSON') + parser.add_argument('--colors', action='store_true', help='Show brand colors') + parser.add_argument('--all', action='store_true', help='Show all curated URLs') + + args = parser.parse_args() + + if args.colors: + colors = load_brand_colors() + print("\nBrand Colors (from design-tokens.json):") + for name, value in colors.items(): + print(f" {name}: {value}") + return + + if args.list: + print("\nAvailable slide types (curated images):") + for slide_type, urls in CURATED_IMAGES.items(): + print(f" {slide_type}: {len(urls)} images") + return + + if not args.slide_type: + parser.print_help() + return + + result = get_background_image(args.slide_type) + + if args.json: + print(json.dumps(result, indent=2)) + elif args.css: + print(generate_css_for_background(result)) + elif args.all: + print(f"\nAll images for '{args.slide_type}':") + for i, url in enumerate(result.get('all_urls', []), 1): + print(f" {i}. {url}") + else: + print(f"\nImage URL: {result['url']}") + print(f"Alternatives: {len(result.get('all_urls', []))} available (use --all)") + print(f"Overlay: {result['overlay']}") + + +if __name__ == '__main__': + main() diff --git a/.claude/skills/design-system/scripts/generate-slide.py b/.claude/skills/design-system/scripts/generate-slide.py new file mode 100644 index 0000000..228a50a --- /dev/null +++ b/.claude/skills/design-system/scripts/generate-slide.py @@ -0,0 +1,753 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Slide Generator - Generates HTML slides using design tokens +ALL styles MUST use CSS variables from design-tokens.css +NO hardcoded colors, fonts, or spacing allowed +""" + +import argparse +import json +from pathlib import Path +from datetime import datetime + +# Paths +SCRIPT_DIR = Path(__file__).parent +DATA_DIR = SCRIPT_DIR.parent / "data" +TOKENS_CSS = Path(__file__).resolve().parents[4] / "assets" / "design-tokens.css" +TOKENS_JSON = Path(__file__).resolve().parents[4] / "assets" / "design-tokens.json" +OUTPUT_DIR = Path(__file__).resolve().parents[4] / "assets" / "designs" / "slides" + +# ============ BRAND-COMPLIANT SLIDE TEMPLATE ============ +# ALL values reference CSS variables from design-tokens.css + +SLIDE_TEMPLATE = ''' + + + + + {title} + + + + + + + + + + + +
+ {slides_content} +
+ + +''' + + +# ============ SLIDE GENERATORS ============ + +def generate_title_slide(data): + """Title slide with gradient headline""" + return f''' +
+
{data.get('badge', 'Pitch Deck')}
+

{data.get('title', 'Your Title Here')}

+

{data.get('subtitle', 'Your compelling subtitle')}

+ + +
+ ''' + + +def generate_problem_slide(data): + """Problem statement slide using PAS formula""" + return f''' +
+
The Problem
+

{data.get('headline', 'The problem your audience faces')}

+
+
+
01
+

{data.get('pain_1_title', 'Pain Point 1')}

+

{data.get('pain_1_desc', 'Description of the first pain point')}

+
+
+
02
+

{data.get('pain_2_title', 'Pain Point 2')}

+

{data.get('pain_2_desc', 'Description of the second pain point')}

+
+
+
03
+

{data.get('pain_3_title', 'Pain Point 3')}

+

{data.get('pain_3_desc', 'Description of the third pain point')}

+
+
+ +
+ ''' + + +def generate_solution_slide(data): + """Solution slide with feature highlights""" + return f''' +
+
The Solution
+

{data.get('headline', 'How we solve this')}

+
+
+
+
+
+

{data.get('feature_1_title', 'Feature 1')}

+

{data.get('feature_1_desc', 'Description of feature 1')}

+
+
+
+
+
+

{data.get('feature_2_title', 'Feature 2')}

+

{data.get('feature_2_desc', 'Description of feature 2')}

+
+
+
+
+
+

{data.get('feature_3_title', 'Feature 3')}

+

{data.get('feature_3_desc', 'Description of feature 3')}

+
+
+
+
+
+
+

Product screenshot or demo

+
+
+
+ +
+ ''' + + +def generate_metrics_slide(data): + """Traction/metrics slide with large numbers""" + metrics = data.get('metrics', [ + {'value': '10K+', 'label': 'Active Users'}, + {'value': '95%', 'label': 'Retention Rate'}, + {'value': '3x', 'label': 'Revenue Growth'}, + {'value': '$2M', 'label': 'ARR'} + ]) + + metrics_html = ''.join([f''' +
+
{m['value']}
+
{m['label']}
+
+ ''' for m in metrics[:4]]) + + return f''' +
+
Traction
+

{data.get('headline', 'Our Growth')}

+
+ {metrics_html} +
+ +
+ ''' + + +def generate_chart_slide(data): + """Chart slide with CSS bar chart""" + bars = data.get('bars', [ + {'label': 'Q1', 'value': 40}, + {'label': 'Q2', 'value': 60}, + {'label': 'Q3', 'value': 80}, + {'label': 'Q4', 'value': 100} + ]) + + bars_html = ''.join([f''' +
+ {b.get('display', str(b['value']) + '%')} + {b['label']} +
+ ''' for b in bars]) + + return f''' +
+
{data.get('badge', 'Growth')}
+

{data.get('headline', 'Revenue Growth')}

+
+
{data.get('chart_title', 'Quarterly Revenue')}
+
+ {bars_html} +
+
+ +
+ ''' + + +def generate_testimonial_slide(data): + """Social proof slide""" + return f''' +
+
What They Say
+
+

"{data.get('quote', 'This product changed how we work. Incredible results.')}"

+

{data.get('author', 'Jane Doe')}

+

{data.get('role', 'CEO, Example Company')}

+
+ +
+ ''' + + +def generate_cta_slide(data): + """Closing CTA slide""" + return f''' +
+

{data.get('headline', 'Ready to get started?')}

+

{data.get('subheadline', 'Join thousands of teams already using our solution.')}

+ + +
+ ''' + + +# Slide type mapping +SLIDE_GENERATORS = { + 'title': generate_title_slide, + 'problem': generate_problem_slide, + 'solution': generate_solution_slide, + 'metrics': generate_metrics_slide, + 'traction': generate_metrics_slide, + 'chart': generate_chart_slide, + 'testimonial': generate_testimonial_slide, + 'cta': generate_cta_slide, + 'closing': generate_cta_slide +} + + +def generate_deck(slides_data, title="Pitch Deck"): + """Generate complete deck from slide data list""" + slides_html = "" + for slide in slides_data: + slide_type = slide.get('type', 'title') + generator = SLIDE_GENERATORS.get(slide_type) + if generator: + slides_html += generator(slide) + else: + print(f"Warning: Unknown slide type '{slide_type}'") + + # Calculate relative path to tokens CSS + tokens_rel_path = "../../../assets/design-tokens.css" + + return SLIDE_TEMPLATE.format( + title=title, + tokens_css_path=tokens_rel_path, + slides_content=slides_html + ) + + +def main(): + parser = argparse.ArgumentParser(description="Generate brand-compliant slides") + parser.add_argument("--json", "-j", help="JSON file with slide data") + parser.add_argument("--output", "-o", help="Output HTML file path") + parser.add_argument("--demo", action="store_true", help="Generate demo deck") + + args = parser.parse_args() + + if args.demo: + # Demo deck showcasing all slide types + demo_slides = [ + { + 'type': 'title', + 'badge': 'Investor Deck 2024', + 'title': 'ClaudeKit Marketing', + 'subtitle': 'Your AI marketing team. Always on.', + 'cta': 'Join Waitlist', + 'secondary_cta': 'See Demo', + 'company': 'ClaudeKit', + 'date': 'December 2024' + }, + { + 'type': 'problem', + 'headline': 'Marketing teams are drowning', + 'pain_1_title': 'Content Overload', + 'pain_1_desc': 'Need to produce 10x content with same headcount', + 'pain_2_title': 'Tool Fatigue', + 'pain_2_desc': '15+ tools that don\'t talk to each other', + 'pain_3_title': 'No Time to Think', + 'pain_3_desc': 'Strategy suffers when execution consumes all hours', + 'company': 'ClaudeKit', + 'page': '2' + }, + { + 'type': 'solution', + 'headline': 'AI agents that actually get marketing', + 'feature_1_title': 'Content Creation', + 'feature_1_desc': 'Blog posts, social, email - all on brand, all on time', + 'feature_2_title': 'Campaign Management', + 'feature_2_desc': 'Multi-channel orchestration with one command', + 'feature_3_title': 'Analytics & Insights', + 'feature_3_desc': 'Real-time optimization without the spreadsheets', + 'company': 'ClaudeKit', + 'page': '3' + }, + { + 'type': 'metrics', + 'headline': 'Early traction speaks volumes', + 'metrics': [ + {'value': '500+', 'label': 'Beta Users'}, + {'value': '85%', 'label': 'Weekly Active'}, + {'value': '4.9', 'label': 'NPS Score'}, + {'value': '50hrs', 'label': 'Saved/Week'} + ], + 'company': 'ClaudeKit', + 'page': '4' + }, + { + 'type': 'chart', + 'badge': 'Revenue', + 'headline': 'Growing month over month', + 'chart_title': 'MRR Growth ($K)', + 'bars': [ + {'label': 'Sep', 'value': 20, 'display': '$5K'}, + {'label': 'Oct', 'value': 40, 'display': '$12K'}, + {'label': 'Nov', 'value': 70, 'display': '$28K'}, + {'label': 'Dec', 'value': 100, 'display': '$45K'} + ], + 'company': 'ClaudeKit', + 'page': '5' + }, + { + 'type': 'testimonial', + 'quote': 'ClaudeKit replaced 3 tools and 2 contractors. Our content output tripled while costs dropped 60%.', + 'author': 'Sarah Chen', + 'role': 'Head of Marketing, TechStartup', + 'company': 'ClaudeKit', + 'page': '6' + }, + { + 'type': 'cta', + 'headline': 'Ship campaigns while you sleep', + 'subheadline': 'Early access available. Limited spots.', + 'cta': 'Join the Waitlist', + 'contact': 'hello@claudekit.ai', + 'website': 'claudekit.ai' + } + ] + + html = generate_deck(demo_slides, "ClaudeKit Marketing - Pitch Deck") + + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + output_path = OUTPUT_DIR / f"demo-pitch-{datetime.now().strftime('%y%m%d')}.html" + output_path.write_text(html, encoding='utf-8') + print(f"Demo deck generated: {output_path}") + + elif args.json: + with open(args.json, 'r') as f: + data = json.load(f) + + html = generate_deck(data.get('slides', []), data.get('title', 'Presentation')) + + output_path = Path(args.output) if args.output else OUTPUT_DIR / f"deck-{datetime.now().strftime('%y%m%d-%H%M')}.html" + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(html, encoding='utf-8') + print(f"Deck generated: {output_path}") + + else: + parser.print_help() + + +if __name__ == "__main__": + main() diff --git a/.claude/skills/design-system/scripts/generate-tokens.cjs b/.claude/skills/design-system/scripts/generate-tokens.cjs new file mode 100644 index 0000000..73cc7d7 --- /dev/null +++ b/.claude/skills/design-system/scripts/generate-tokens.cjs @@ -0,0 +1,205 @@ +#!/usr/bin/env node +/** + * Generate CSS variables from design tokens JSON + * + * Usage: + * node generate-tokens.cjs --config tokens.json -o tokens.css + * node generate-tokens.cjs --config tokens.json --format tailwind + */ + +const fs = require('fs'); +const path = require('path'); + +/** + * Parse command line arguments + */ +function parseArgs() { + const args = process.argv.slice(2); + const options = { + config: null, + output: null, + format: 'css' // css | tailwind + }; + + for (let i = 0; i < args.length; i++) { + if (args[i] === '--config' || args[i] === '-c') { + options.config = args[++i]; + } else if (args[i] === '--output' || args[i] === '-o') { + options.output = args[++i]; + } else if (args[i] === '--format' || args[i] === '-f') { + options.format = args[++i]; + } else if (args[i] === '--help' || args[i] === '-h') { + console.log(` +Usage: node generate-tokens.cjs [options] + +Options: + -c, --config Input JSON token file (required) + -o, --output Output file (default: stdout) + -f, --format Output format: css | tailwind (default: css) + -h, --help Show this help + `); + process.exit(0); + } + } + + return options; +} + +/** + * Resolve token references like {primitive.color.blue.600} + */ +function resolveReference(value, tokens) { + if (typeof value !== 'string' || !value.startsWith('{')) { + return value; + } + + const path = value.slice(1, -1).split('.'); + let result = tokens; + + for (const key of path) { + result = result?.[key]; + } + + if (result?.$value) { + return resolveReference(result.$value, tokens); + } + + return result || value; +} + +/** + * Convert token name to CSS variable name + */ +function toCssVarName(path) { + return '--' + path.join('-').replace(/\./g, '-'); +} + +/** + * Flatten tokens into CSS variables + */ +function flattenTokens(obj, tokens, prefix = [], result = {}) { + for (const [key, value] of Object.entries(obj)) { + const currentPath = [...prefix, key]; + + if (value && typeof value === 'object') { + if (value.$value !== undefined) { + // This is a token + const cssVar = toCssVarName(currentPath); + const resolvedValue = resolveReference(value.$value, tokens); + result[cssVar] = resolvedValue; + } else { + // Recurse into nested object + flattenTokens(value, tokens, currentPath, result); + } + } + } + + return result; +} + +/** + * Generate CSS output + */ +function generateCSS(tokens) { + const primitive = flattenTokens(tokens.primitive || {}, tokens, ['primitive']); + const semantic = flattenTokens(tokens.semantic || {}, tokens, []); + const component = flattenTokens(tokens.component || {}, tokens, []); + const darkSemantic = flattenTokens(tokens.dark?.semantic || {}, tokens, []); + + let css = `/* Design Tokens - Auto-generated */ +/* Do not edit directly - modify tokens.json instead */ + +/* === PRIMITIVES === */ +:root { +${Object.entries(primitive).map(([k, v]) => ` ${k}: ${v};`).join('\n')} +} + +/* === SEMANTIC === */ +:root { +${Object.entries(semantic).map(([k, v]) => ` ${k}: ${v};`).join('\n')} +} + +/* === COMPONENTS === */ +:root { +${Object.entries(component).map(([k, v]) => ` ${k}: ${v};`).join('\n')} +} +`; + + if (Object.keys(darkSemantic).length > 0) { + css += ` +/* === DARK MODE === */ +.dark { +${Object.entries(darkSemantic).map(([k, v]) => ` ${k}: ${v};`).join('\n')} +} +`; + } + + return css; +} + +/** + * Generate Tailwind config output + */ +function generateTailwind(tokens) { + const semantic = flattenTokens(tokens.semantic || {}, tokens, []); + + // Extract colors for Tailwind + const colors = {}; + for (const [key, value] of Object.entries(semantic)) { + if (key.includes('color')) { + const name = key.replace('--color-', '').replace(/-/g, '.'); + colors[name] = `var(${key})`; + } + } + + return `// Tailwind color config - Auto-generated +// Add to tailwind.config.ts theme.extend.colors + +module.exports = { + colors: ${JSON.stringify(colors, null, 2).replace(/"/g, "'")} +}; +`; +} + +/** + * Main + */ +function main() { + const options = parseArgs(); + + if (!options.config) { + console.error('Error: --config is required'); + process.exit(1); + } + + // Resolve config path + const configPath = path.resolve(process.cwd(), options.config); + + if (!fs.existsSync(configPath)) { + console.error(`Error: Config file not found: ${configPath}`); + process.exit(1); + } + + // Read and parse tokens + const tokens = JSON.parse(fs.readFileSync(configPath, 'utf-8')); + + // Generate output + let output; + if (options.format === 'tailwind') { + output = generateTailwind(tokens); + } else { + output = generateCSS(tokens); + } + + // Write output + if (options.output) { + const outputPath = path.resolve(process.cwd(), options.output); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, output); + console.log(`Generated: ${outputPath}`); + } else { + console.log(output); + } +} + +main(); diff --git a/.claude/skills/design-system/scripts/html-token-validator.py b/.claude/skills/design-system/scripts/html-token-validator.py new file mode 100644 index 0000000..a722498 --- /dev/null +++ b/.claude/skills/design-system/scripts/html-token-validator.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python3 +""" +HTML Design Token Validator +Ensures all HTML assets (slides, infographics, etc.) use design tokens. +Source of truth: assets/design-tokens.css + +Usage: + python html-token-validator.py # Validate all HTML assets + python html-token-validator.py --type slides # Validate only slides + python html-token-validator.py --type infographics # Validate only infographics + python html-token-validator.py path/to/file.html # Validate specific file + python html-token-validator.py --fix # Auto-fix issues (WIP) +""" + +import re +import json +import sys +from pathlib import Path +from typing import Dict, List, Tuple, Optional + +# Project root relative to this script +PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent +TOKENS_JSON_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.json' +TOKENS_CSS_PATH = PROJECT_ROOT / 'assets' / 'design-tokens.css' + +# Asset directories to validate +ASSET_DIRS = { + 'slides': PROJECT_ROOT / 'assets' / 'designs' / 'slides', + 'infographics': PROJECT_ROOT / 'assets' / 'infographics', +} + +# Patterns that indicate hardcoded values (should use tokens) +FORBIDDEN_PATTERNS = [ + (r'#[0-9A-Fa-f]{3,8}\b', 'hex color'), + (r'rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)', 'rgb color'), + (r'rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[\d.]+\s*\)', 'rgba color'), + (r'hsl\([^)]+\)', 'hsl color'), + (r"font-family:\s*'[^v][^a][^r][^']*',", 'hardcoded font'), # Exclude var() + (r'font-family:\s*"[^v][^a][^r][^"]*",', 'hardcoded font'), +] + +# Allowed rgba patterns (brand colors with transparency - CSS limitation) +# These are derived from brand tokens but need rgba for transparency +ALLOWED_RGBA_PATTERNS = [ + r'rgba\(\s*59\s*,\s*130\s*,\s*246', # --color-primary (#3B82F6) + r'rgba\(\s*245\s*,\s*158\s*,\s*11', # --color-secondary (#F59E0B) + r'rgba\(\s*16\s*,\s*185\s*,\s*129', # --color-accent (#10B981) + r'rgba\(\s*20\s*,\s*184\s*,\s*166', # --color-accent alt (#14B8A6) + r'rgba\(\s*0\s*,\s*0\s*,\s*0', # black transparency (common) + r'rgba\(\s*255\s*,\s*255\s*,\s*255', # white transparency (common) + r'rgba\(\s*15\s*,\s*23\s*,\s*42', # --color-surface (#0F172A) + r'rgba\(\s*7\s*,\s*11\s*,\s*20', # --color-background (#070B14) +] + +# Allowed exceptions (external images, etc.) +ALLOWED_EXCEPTIONS = [ + 'pexels.com', 'unsplash.com', 'youtube.com', 'ytimg.com', + 'googlefonts', 'fonts.googleapis.com', 'fonts.gstatic.com', +] + + +class ValidationResult: + """Validation result for a single file.""" + def __init__(self, file_path: Path): + self.file_path = file_path + self.errors: List[str] = [] + self.warnings: List[str] = [] + self.passed = True + + def add_error(self, msg: str): + self.errors.append(msg) + self.passed = False + + def add_warning(self, msg: str): + self.warnings.append(msg) + + +def load_css_variables() -> Dict[str, str]: + """Load CSS variables from design-tokens.css.""" + variables = {} + if TOKENS_CSS_PATH.exists(): + content = TOKENS_CSS_PATH.read_text() + # Extract --var-name: value patterns + for match in re.finditer(r'(--[\w-]+):\s*([^;]+);', content): + variables[match.group(1)] = match.group(2).strip() + return variables + + +def is_inside_block(content: str, match_pos: int, open_tag: str, close_tag: str) -> bool: + """Check if position is inside a specific HTML block.""" + pre = content[:match_pos] + tag_open = pre.rfind(open_tag) + tag_close = pre.rfind(close_tag) + return tag_open > tag_close + + +def is_allowed_exception(context: str) -> bool: + """Check if the hardcoded value is in an allowed exception context.""" + context_lower = context.lower() + return any(exc in context_lower for exc in ALLOWED_EXCEPTIONS) + + +def is_allowed_rgba(match_text: str) -> bool: + """Check if rgba pattern uses brand colors (allowed for transparency).""" + return any(re.match(pattern, match_text) for pattern in ALLOWED_RGBA_PATTERNS) + + +def get_context(content: str, pos: int, chars: int = 100) -> str: + """Get surrounding context for a match position.""" + start = max(0, pos - chars) + end = min(len(content), pos + chars) + return content[start:end] + + +def validate_html(content: str, file_path: Path, verbose: bool = False) -> ValidationResult: + """ + Validate HTML content for design token compliance. + + Checks: + 1. design-tokens.css import present + 2. No hardcoded colors in CSS (except in '): + if verbose: + result.add_warning(f"Allowed in