[teamai] Push 87 resource(s) from XingfenD

This commit is contained in:
2026-09-10 16:10:45 +08:00
parent 425c9c078a
commit 65c04def51
1314 changed files with 211681 additions and 0 deletions
@@ -0,0 +1,521 @@
# Advanced Reference: Component Patterns & Code Templates
This file contains advanced patterns and code templates to reference when implementing specific tasks.
## Table of Contents
1. [Responsive Slide Engine](#responsive-slide-engine)
2. [Device Simulation Frames](#device-simulation-frames)
3. [Tweaks Panel Implementation](#tweaks-panel-implementation)
4. [Animation Timeline Engine](#animation-timeline-engine)
5. [Design Canvas (Multi-option Comparison)](#design-canvas)
6. [Dark Mode Toggle](#dark-mode-toggle)
7. [Data Visualization Templates](#data-visualization-templates)
---
## Responsive Slide Engine
For building fixed-size presentations that auto-fit to any viewport.
**Key conventions**:
- Internal arrays use 0-indexed, **but numbers displayed to the user are always 1-indexed**
- Each `<section class="slide">` gets `data-screen-label="01 Title"`, `data-screen-label="02 Agenda"`, etc. for easy reference
- Control buttons go **outside** the `.stage` scaled container to ensure usability on small screens
```html
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #000;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
font-family: system-ui, sans-serif;
}
.stage {
width: 1920px;
height: 1080px;
position: relative;
transform-origin: center center;
}
.slide {
position: absolute;
inset: 0;
display: none;
padding: 80px;
}
.slide.active { display: flex; }
.controls {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 12px;
z-index: 1000;
}
.controls button {
padding: 8px 16px;
border: none;
border-radius: 6px;
background: rgba(255,255,255,0.15);
color: white;
cursor: pointer;
font-size: 14px;
}
.slide-counter {
position: fixed;
bottom: 20px;
right: 20px;
color: rgba(255,255,255,0.6);
font-size: 14px;
}
</style>
<script>
// Auto-fit scaling
function scaleStage() {
const stage = document.querySelector('.stage');
const scaleX = window.innerWidth / 1920;
const scaleY = window.innerHeight / 1080;
const scale = Math.min(scaleX, scaleY);
stage.style.transform = `scale(${scale})`;
}
window.addEventListener('resize', scaleStage);
scaleStage();
// Slide navigation
let current = parseInt(localStorage.getItem('slideIndex') || '0');
const slides = document.querySelectorAll('.slide');
function showSlide(n) {
current = Math.max(0, Math.min(n, slides.length - 1));
slides.forEach((s, i) => s.classList.toggle('active', i === current));
localStorage.setItem('slideIndex', current);
// Display 1-indexed to user, store 0-indexed internally
document.querySelector('.slide-counter').textContent = `${current + 1} / ${slides.length}`;
}
document.addEventListener('keydown', e => {
if (e.key === 'ArrowRight' || e.key === ' ') showSlide(current + 1);
if (e.key === 'ArrowLeft') showSlide(current - 1);
});
showSlide(current);
</script>
```
---
## Device Simulation Frames
### iPhone Frame
```jsx
const IPhoneFrame = ({ children, title = "App" }) => (
<div style={{
width: 390,
height: 844,
borderRadius: 48,
border: '12px solid #1a1a1a',
overflow: 'hidden',
position: 'relative',
boxShadow: '0 25px 50px -12px rgba(0,0,0,0.25)',
background: '#fff'
}}>
{/* Status bar */}
<div style={{
height: 54,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 24px',
fontSize: 14,
fontWeight: 600
}}>
<span>9:41</span>
<div style={{
width: 126,
height: 34,
background: '#1a1a1a',
borderRadius: 20,
position: 'absolute',
left: '50%',
transform: 'translateX(-50%)',
top: 8
}} />
<span>⚡ 📶</span>
</div>
{/* Content */}
<div style={{ height: 'calc(100% - 54px)', overflow: 'auto' }}>
{children}
</div>
{/* Home indicator */}
<div style={{
position: 'absolute',
bottom: 8,
left: '50%',
transform: 'translateX(-50%)',
width: 134,
height: 5,
background: '#1a1a1a',
borderRadius: 3
}} />
</div>
);
```
### Browser Window Frame
```jsx
const BrowserFrame = ({ children, url = "https://example.com", title = "Page" }) => (
<div style={{
borderRadius: 12,
overflow: 'hidden',
boxShadow: '0 25px 50px -12px rgba(0,0,0,0.25)',
border: '1px solid #e5e5e5'
}}>
{/* Title bar */}
<div style={{
background: '#f5f5f5',
padding: '12px 16px',
display: 'flex',
alignItems: 'center',
gap: 12,
borderBottom: '1px solid #e5e5e5'
}}>
<div style={{ display: 'flex', gap: 8 }}>
<div style={{ width: 12, height: 12, borderRadius: '50%', background: '#ff5f57' }} />
<div style={{ width: 12, height: 12, borderRadius: '50%', background: '#febc2e' }} />
<div style={{ width: 12, height: 12, borderRadius: '50%', background: '#28c840' }} />
</div>
<div style={{
flex: 1,
background: '#fff',
borderRadius: 6,
padding: '6px 12px',
fontSize: 13,
color: '#666',
border: '1px solid #e0e0e0'
}}>
{url}
</div>
</div>
{/* Content */}
<div style={{ background: '#fff' }}>
{children}
</div>
</div>
);
```
---
## Tweaks Panel Implementation
```jsx
const TweaksPanel = ({ config, onChange, visible }) => {
if (!visible) return null;
return (
<div style={{
position: 'fixed',
bottom: 20,
right: 20,
width: 280,
background: 'rgba(24, 24, 27, 0.95)',
backdropFilter: 'blur(12px)',
borderRadius: 12,
padding: 16,
color: '#fff',
fontSize: 13,
zIndex: 9999,
boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
border: '1px solid rgba(255,255,255,0.1)'
}}>
<div style={{ fontWeight: 600, marginBottom: 12, fontSize: 14 }}>Tweaks</div>
{Object.entries(config).map(([key, value]) => (
<div key={key} style={{ marginBottom: 12 }}>
<label style={{ display: 'block', marginBottom: 4, opacity: 0.7 }}>
{key}
</label>
{typeof value === 'boolean' ? (
<input
type="checkbox"
checked={value}
onChange={e => onChange({ ...config, [key]: e.target.checked })}
/>
) : typeof value === 'number' ? (
<input
type="range"
min="0"
max="100"
value={value}
onChange={e => onChange({ ...config, [key]: Number(e.target.value) })}
style={{ width: '100%' }}
/>
) : value.startsWith('#') ? (
<input
type="color"
value={value}
onChange={e => onChange({ ...config, [key]: e.target.value })}
/>
) : (
<input
type="text"
value={value}
onChange={e => onChange({ ...config, [key]: e.target.value })}
style={{
width: '100%',
background: 'rgba(255,255,255,0.1)',
border: '1px solid rgba(255,255,255,0.2)',
borderRadius: 4,
padding: '4px 8px',
color: '#fff'
}}
/>
)}
</div>
))}
</div>
);
};
```
---
## Animation Timeline Engine
```jsx
const useTime = (duration = 5000) => {
const [time, setTime] = React.useState(0);
const [playing, setPlaying] = React.useState(true);
const frameRef = React.useRef();
const startRef = React.useRef();
React.useEffect(() => {
if (!playing) return;
const animate = (timestamp) => {
if (!startRef.current) startRef.current = timestamp;
const elapsed = (timestamp - startRef.current) % duration;
setTime(elapsed / duration); // 0 to 1
frameRef.current = requestAnimationFrame(animate);
};
frameRef.current = requestAnimationFrame(animate);
return () => cancelAnimationFrame(frameRef.current);
}, [playing, duration]);
return { time, playing, setPlaying };
};
const Easing = {
linear: t => t,
easeInOut: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
easeOut: t => 1 - Math.pow(1 - t, 3),
easeIn: t => t * t * t,
spring: t => 1 - Math.pow(Math.E, -6 * t) * Math.cos(8 * t)
};
const interpolate = (t, from, to, easing = Easing.easeInOut) => {
const progress = easing(Math.max(0, Math.min(1, t)));
return from + (to - from) * progress;
};
// Usage example:
// const { time } = useTime(3000);
// const opacity = interpolate(time, 0, 1);
// const x = interpolate(time, -100, 0, Easing.spring);
```
---
## Design Canvas
For displaying multiple design options side by side for comparison:
```jsx
const DesignCanvas = ({ options, columns = 3 }) => (
<div style={{
display: 'grid',
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gap: 24,
padding: 40,
background: '#f8f9fa',
minHeight: '100vh'
}}>
{options.map((option, i) => (
<div key={i} style={{
background: '#fff',
borderRadius: 12,
overflow: 'hidden',
boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
}}>
<div style={{
padding: '12px 16px',
borderBottom: '1px solid #eee',
fontSize: 13,
fontWeight: 600,
color: '#666'
}}>
Option {String.fromCharCode(65 + i)}: {option.label}
</div>
<div style={{ padding: 16 }}>
{option.content}
</div>
</div>
))}
</div>
);
```
---
## Dark Mode Toggle
```jsx
const ThemeProvider = ({ children }) => {
const [dark, setDark] = React.useState(
window.matchMedia('(prefers-color-scheme: dark)').matches
);
const theme = dark ? {
bg: '#0a0a0b',
surface: '#18181b',
border: '#27272a',
text: '#fafafa',
textMuted: '#a1a1aa',
primary: '#3b82f6'
} : {
bg: '#ffffff',
surface: '#f4f4f5',
border: '#e4e4e7',
text: '#18181b',
textMuted: '#71717a',
primary: '#2563eb'
};
return (
<ThemeContext.Provider value={{ theme, dark, setDark }}>
<div style={{ background: theme.bg, color: theme.text, minHeight: '100vh' }}>
{children}
</div>
</ThemeContext.Provider>
);
};
```
---
## Data Visualization Templates
### Chart.js Quick Start
```html
<canvas id="myChart" width="800" height="400"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'line', // bar, pie, doughnut, radar, etc.
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Revenue',
data: [12, 19, 3, 5, 2, 3],
borderColor: '#3b82f6',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false }
},
scales: {
y: { beginAtZero: true, grid: { color: '#f0f0f0' } },
x: { grid: { display: false } }
}
}
});
</script>
```
---
## Color System Best Practices
Use oklch to define a harmonious color system:
```css
:root {
/* oklch-based color system */
--primary-h: 250; /* hue */
--primary: oklch(0.55 0.25 var(--primary-h));
--primary-light: oklch(0.75 0.15 var(--primary-h));
--primary-dark: oklch(0.35 0.2 var(--primary-h));
/* Neutrals */
--gray-50: oklch(0.98 0.002 250);
--gray-100: oklch(0.96 0.004 250);
--gray-200: oklch(0.92 0.006 250);
--gray-300: oklch(0.87 0.008 250);
--gray-400: oklch(0.71 0.01 250);
--gray-500: oklch(0.55 0.014 250);
--gray-600: oklch(0.45 0.014 250);
--gray-700: oklch(0.37 0.014 250);
--gray-800: oklch(0.27 0.014 250);
--gray-900: oklch(0.21 0.014 250);
}
```
---
## Font Recommendations (Non-default Choices)
> ⚠️ **These are experience-based suggestions, not hard rules.**
> - Always prefer fonts already specified by the brand or design system; only refer to this table when the user hasn't provided any font scheme.
> - The only hard rule: **Avoid Inter / Roboto / Arial / Fraunces / system-ui — fonts overused by AI-generated content** that instantly signal "this was assembled by AI."
> - When choosing fonts, focus on "personality fit" rather than "what's trendy." The table below lists common high-quality choices, not an exhaustive list.
| Use Case | Recommendation | Google Fonts Name |
|------|------|------------------|
| Modern headings | Plus Jakarta Sans | Plus+Jakarta+Sans |
| Elegant body text | Outfit | Outfit |
| Technical feel | Space Grotesk | Space+Grotesk |
| Premium brand | Sora | Sora |
| Editorial feel | Newsreader | Newsreader |
| Handwritten style | Caveat | Caveat |
| Monospace / code | JetBrains Mono | JetBrains+Mono |
```html
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
```
---
## Color × Font Pairing Reference
> ⚠️ **These are experience-based pairing suggestions, not hard rules.** When you have **absolutely no design context**, pick one as a starting point — it's far better than starting from Inter + #3b82f6.
> Once the user provides a brand / design system / reference site, drop this table immediately and follow their materials.
For quickly establishing a visual system with personality:
| Style | Primary Color (oklch) | Font Pairing | Best For |
|---|---|---|---|
| Modern tech | `oklch(0.55 0.25 250)` blue-violet | Space Grotesk + Inter | SaaS, dev tools, AI products |
| Elegant editorial | `oklch(0.35 0.10 30)` warm brown | Newsreader + Outfit | Content platforms, blogs, editorial |
| Premium brand | `oklch(0.20 0.02 250)` near-black | Sora + Plus Jakarta Sans | Luxury, consulting, finance |
| Lively consumer | `oklch(0.70 0.20 30)` coral | Plus Jakarta Sans + Outfit | E-commerce, lifestyle, social |
| Minimal professional | `oklch(0.50 0.15 200)` teal-blue | Outfit + Space Grotesk | Data products, dashboards, B2B |
| Artisan warmth | `oklch(0.55 0.15 80)` caramel | Caveat (decorative) + Newsreader | Food & beverage, education, creative |
Avoid these combos:
- ❌ Inter + Roboto + blue buttons (peak AI aesthetic)
- ❌ Fraunces + purple-pink gradients (overused)
- ❌ More than three font families (visual chaos)