Files
AOS/skills/design/horizontal-craft/technique-library.md
T
Z User e45b68bb8b feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library:
- Connects to IMAP server (IMAPFlow, TLS on port 993)
- Fetches recent messages (configurable limit, default 20)
- Extracts: from, fromEmail, subject, date, snippet (200 chars)
- Thread-to-contact matching (PRD §6.1):
  1. Match sender email against persons table
  2. If no person match, extract domain and match against company.website
  3. Returns matchedCompany + matchedPerson for each email
- Audit-logged (action: crm.mailsync:imap)
- Zod validated (host, port, user, password, folder, limit)
- 502 on IMAP connection failure with detail
- 400 on validation failure

Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
2026-10-27 22:50:50 +00:00

12 KiB
Executable File

name, description, mode, type, applies_to, avoid_for
name description mode type applies_to avoid_for
technique-library Advanced front-end technique selection reference for motion, 3D, shaders, data visualization, and high-cost visual effects. This is a horizontal craft reference, not a routing target. reference horizontal-craft
landing-page
prototype
portfolio
info-interactive
deck
web-tool
static export cards unless explicitly animated
text-heavy editorial reading experiences

Technique Library

A reference of advanced techniques that can make a web artifact more effective, and when each is worth using. Artifact skills consult this when an effect would genuinely improve the result. It is not a scenario and not a routing target — a user asks for "a landing page with rich scroll storytelling", not "a GSAP". The artifact skill (e.g. landing-page.md) decides the form and intent, then reaches here for the implementation technique.

How to use this

  • Only reach for a technique when it serves comprehension, emotion, or the product goal. Default to simple CSS; escalate to a library only when the effect earns its weight.
  • Never add a technique for novelty. Motion/3D that doesn't help the message is noise — and a cost in load, accessibility, and maintenance.
  • Always pair an advanced technique with prefers-reduced-motion handling and a sensible no-JS / fallback state.
  • Prefer CDN includes consistent with the artifact environment; keep dependencies minimal.

Not every scenario should use this — match technique to intent

Scenario What it should pull (if anything)
landing-page full range — motion/3D/shader serve conviction and impact
prototype motion/transitions for realistic interaction; 3D/device mockups when relevant
portfolio optional motion/3D when craft is the message; restrained for résumé/text-led profiles
info-interactive interactive data-visualization is the point — use diagram/chart interaction that aids comprehension (linked highlighting, filter/recompare, draggable/zoomable graphs, slider-driven models); avoid only decorative motion/3D that doesn't explain
content-page none — reading wants calm; at most subtle CSS reveals
social-card none — static export graphics
web-tool none beyond simple CSS — single-task utility, keep it light
deck none beyond restrained CSS — decks favor clarity over spectacle

The rule of thumb: the more a scenario is about impact/craft (landing, creative portfolio), the more these techniques help; the more it is about reading or understanding (content-page, info-interactive), the more decorative effects hurt — there, only data viz qualifies.


Motion & scroll

CSS transitions / animations (default)

First choice for hovers, state changes, simple reveals, micro-interactions. No library needed. Reach further only when CSS genuinely can't express the effect.

GSAP (GreenSock)

For complex, sequenced, or scroll-driven motion that CSS handles poorly.

  • GSAP core + Timeline — choreographed multi-step sequences, precise easing, coordinated animations.
  • ScrollTrigger — scroll-linked storytelling: pin sections, scrub animations to scroll, reveal-on-enter, parallax. The main reason to choose GSAP for landing pages and explainers.
  • Use when: a landing page needs narrative scroll, a product reveal, or staged emphasis.
  • Avoid when: a simple fade/slide on scroll (use CSS + IntersectionObserver instead).

Lottie

For designer-authored vector animations (exported from After Effects) — mascots, rich loaders, illustrative loops. Use when you have/need polished illustrative motion; avoid for simple UI motion.


3D & shaders

Three.js

For real 3D: product showcases in 3D, spatial scenes, interactive 3D objects.

  • Use when: a hero needs a rotatable 3D product, a spatial concept, or depth that 2D can't convey.
  • Avoid when: a flat illustration or a CSS 3D transform would do. 3D is heavy — justify it.
  • Note: in constrained runtimes some Three.js addons/geometries may be unavailable; prefer core geometries and verify availability.

Shaders (GLSL / WebGL)

For custom visual effects: generative backgrounds, fluid/gradient fields, particle systems.

  • Use when: a distinctive atmospheric background or effect is core to the design direction.
  • Avoid when: a CSS gradient or static image achieves the same impression. High cost, niche payoff.

Data visualization

For quantitative, relational, or temporal information. This overlaps with info-interactive.md — when the deliverable's purpose is expressing information (report, data story, explainer), route to info-interactive.md and use these techniques inside it. Use them here when a scenario (e.g. landing page, dashboard) needs a chart as a supporting element.

Inline SVG (default for simple charts)

Hand-built bar/line/donut charts as inline SVG. First choice for a few simple, static charts — no dependency, full control, crisp export.

D3.js

For complex, custom, or data-driven visualizations: custom scales, force/network graphs, hierarchical layouts, bespoke interactive charts that off-the-shelf libraries can't express.

  • Use when: the visualization is interactive or custom beyond standard chart types — this is the right tool for the force/relationship graphs, zoomable hierarchies, and bespoke explorable charts in the Interactive diagrams section below. Don't shy away from it when the diagram genuinely needs force layout, hierarchical layout, or custom scales.
  • Avoid when: a standard bar/line/pie suffices (inline SVG or a chart library is lighter), or when hand-written SVG + vanilla JS already covers the interaction (see skeletons below — many linked-highlight / slider-model diagrams don't need D3 at all).

Chart libraries (Chart.js / Recharts, when supported)

For standard chart types quickly. Use when standard charts are enough and a library is available in the environment; otherwise fall back to inline SVG.

Visualization integrity (always): correct axes/units/scales; no misleading truncation; readable labels; the title states the insight; never invent data.

Interactive diagrams — go past "expand / click-to-show"

For info-interactive (and any explainer), "click to expand" is the lowest tier of interaction. When the information is relational, comparative, sequential, or model-like, reach for a real interactive diagram. The point is comprehension, not spectacle — but a static list with a toggle is usually under-delivering for these shapes. Tiers:

L1 (baseline)  expand / collapse / click-to-reveal      ← don't stop here for explore-class info
L2             linked highlighting, filter-and-recompose, hover-to-trace
L3             draggable / zoomable / slider-driven models that recompute live

Match shape → interaction (and grab a skeleton below):

Information shape Interactive move
relationship / network hover a node → highlight its neighbours + dim the rest; click → detail
architecture / dependency click a layer/component → trace and highlight its dependency path
sequence / process scrub or step through; the active step explains itself
comparison toggle a dimension → matrix/chart re-sorts or re-weights live
model / sensitivity slider/input → numbers, chart, and the written takeaway update together
time / evolution brush/zoom a range → detail and summary update for that window

Paste-ready skeletons (vanilla JS + SVG/HTML; reduced-motion safe; escalate to D3 only when the layout maths genuinely needs it):

1. Linked-highlight relationship graph (hover highlights neighbours)

<svg id="graph" viewBox="0 0 400 300" role="img" aria-label="Relationship graph"></svg>
<script>
const nodes=[{id:'a',x:80,y:80,label:'Auth'},{id:'b',x:220,y:60,label:'API'},
  {id:'c',x:300,y:180,label:'DB'},{id:'d',x:120,y:210,label:'Cache'}];
const edges=[['a','b'],['b','c'],['b','d']];
const svg=document.getElementById('graph'), adj={};
edges.forEach(([s,t])=>{(adj[s]=adj[s]||[]).push(t);(adj[t]=adj[t]||[]).push(s);});
edges.forEach(([s,t])=>{const a=nodes.find(n=>n.id===s),b=nodes.find(n=>n.id===t);
  const l=document.createElementNS(svg.namespaceURI,'line');
  l.setAttribute('x1',a.x);l.setAttribute('y1',a.y);l.setAttribute('x2',b.x);l.setAttribute('y2',b.y);
  l.setAttribute('stroke','#bbb');l.dataset.edge=s+'-'+t;svg.appendChild(l);});
nodes.forEach(n=>{const g=document.createElementNS(svg.namespaceURI,'g');g.tabIndex=0;
  g.innerHTML=`<circle cx="${n.x}" cy="${n.y}" r="20" fill="#3b82f6"/>
    <text x="${n.x}" y="${n.y+4}" text-anchor="middle" font-size="10" fill="#fff">${n.label}</text>`;
  const focus=()=>{const near=new Set([n.id,...(adj[n.id]||[])]);
    svg.querySelectorAll('circle').forEach((c,i)=>c.style.opacity=near.has(nodes[i].id)?1:.25);
    svg.querySelectorAll('line').forEach(l=>{const[s,t]=l.dataset.edge.split('-');
      l.setAttribute('stroke',s===n.id||t===n.id?'#3b82f6':'#eee');});};
  const reset=()=>{svg.querySelectorAll('circle').forEach(c=>c.style.opacity=1);
    svg.querySelectorAll('line').forEach(l=>l.setAttribute('stroke','#bbb'));};
  g.addEventListener('mouseenter',focus);g.addEventListener('focus',focus);
  g.addEventListener('mouseleave',reset);g.addEventListener('blur',reset);svg.appendChild(g);});
</script>

2. Slider-driven explorable model (input → chart + takeaway recompute)

<label>Monthly users <input id="u" type="range" min="100" max="10000" value="2000" step="100"></label>
<p id="out"></p><svg id="bars" viewBox="0 0 320 120"></svg>
<script>
const u=document.getElementById('u'),out=document.getElementById('out'),bars=document.getElementById('bars');
function render(){const users=+u.value, revenue=users*12, cost=2000+users*3, profit=revenue-cost;
  out.textContent=`At ${users.toLocaleString()} users: revenue ¥${revenue.toLocaleString()}, `
    +`profit ¥${profit.toLocaleString()} — ${profit>0?'profitable':'below break-even'}.`;
  const data=[['Revenue',revenue],['Cost',cost],['Profit',Math.max(profit,0)]],max=Math.max(...data.map(d=>d[1]),1);
  bars.innerHTML=data.map((d,i)=>{const h=d[1]/max*80;return `
    <rect x="${20+i*100}" y="${100-h}" width="60" height="${h}" fill="${['#3b82f6','#ef4444','#22c55e'][i]}"/>
    <text x="${50+i*100}" y="115" text-anchor="middle" font-size="9">${d[0]}</text>`;}).join('');}
u.addEventListener('input',render);render();
</script>

3. Step-through process (active step explains itself) and 4. Toggle-recompose comparison follow the same pattern: state in a variable, one render() that redraws SVG/HTML from state, and controls that mutate state then call render(). Keep state in the DOM (classes/attributes) so it stays editable and inspectable.

Rules for all of the above: every control has a visible output change and keyboard access; provide a static-readable fallback (the default render must already make sense); honor prefers-reduced-motion; never fake a control. Use D3 only when force layout, hierarchical layout, or custom scales exceed what hand-written SVG can manage.


Selection summary

Need Default Escalate to
hover / state / simple reveal CSS —
scroll-driven storytelling CSS + IntersectionObserver GSAP ScrollTrigger
choreographed multi-step motion CSS GSAP Timeline
illustrative vector animation — Lottie
real 3D / spatial CSS 3D transforms Three.js
custom atmospheric effect CSS gradient/image Shaders (WebGL)
a few simple charts inline SVG —
standard charts, fast inline SVG Chart.js / Recharts
custom / interactive data viz inline SVG D3.js

Every escalation must (1) serve the goal, (2) ship reduced-motion handling, (3) justify its weight.