// 小红书风格小组件库 v1.0 // 每个widget = 核心HTML模板 + 3~5个随机变体(variant) + CSS变量可覆写 // 国作登字-2026-A-00037559 // 设计原则: 每个widget是独立片段的HTML,可随机组合不打架 export const WIDGETS = { // ═══════════════════════════════════════ // 装饰类 Widgets // ═══════════════════════════════════════ 'dot-grid': { name: '圆点矩阵', category: 'decor', variants: [ { id: 'warm', css: { size: '6px', color: '#E07B39', opacity: '0.25', gap: '18px', cols: 8, rows: 3 } }, { id: 'cool', css: { size: '4px', color: '#4A90A4', opacity: '0.2', gap: '22px', cols: 10, rows: 2 } }, { id: 'mixed', css: { size: '5px', colors: ['#E07B39','#4A90A4','#5B8C5A'], opacity: '0.3', gap: '16px', cols: 6, rows: 4 } }, { id: 'large', css: { size: '8px', color: '#2D5016', opacity: '0.12', gap: '24px', cols: 5, rows: 2 } }, ], render(v) { const c = v.css, colors = c.colors || [c.color] let dots = '' const total = (c.cols || 6) * (c.rows || 3) for (let i = 0; i < total; i++) { const clr = colors[i % colors.length] const r = Math.floor(i / c.cols), col = i % c.cols const ox = (Math.random() - 0.5) * 4, oy = (Math.random() - 0.5) * 4 dots += `` } return `${dots}` } }, 'gradient-stripe': { name: '渐变装饰条', category: 'decor', variants: [ { id: 'warm-sunset', css: { colors: ['#E07B39','#F5A623','#FFD700'], height: '6px', width: '80%', radius: '3px' } }, { id: 'cool-ocean', css: { colors: ['#4A90A4','#5B8C5A','#7BC47B'], height: '4px', width: '60%', radius: '2px' } }, { id: 'rose-gold', css: { colors: ['#E8435E','#C9A96E','#F5D0C5'], height: '5px', width: '70%', radius: '3px' } }, { id: 'forest', css: { colors: ['#2D5016','#5B8C5A','#A8D5A2'], height: '8px', width: '50%', radius: '4px' } }, ], render(v) { const c = v.css const grad = c.colors.map((clr, i) => `${clr} ${(i / (c.colors.length - 1)) * 100}%`).join(', ') return `
` } }, 'sticker-badge': { name: '贴纸角标', category: 'decor', variants: [ { id: 'hot', text: 'HOT', bg: '#E8435E', color: '#FFF', rotate: '-8deg' }, { id: 'new', text: 'NEW', bg: '#E07B39', color: '#FFF', rotate: '5deg' }, { id: 'free', text: 'FREE', bg: '#5B8C5A', color: '#FFF', rotate: '-3deg' }, { id: 'top', text: 'TOP', bg: '#4A90A4', color: '#FFF', rotate: '10deg' }, { id: 'hand', text: '手写', bg: '#F5E6C8', color: '#8B4513', rotate: '-12deg' }, ], render(v) { return `
${v.text}
` } }, 'hand-drawn-circle': { name: '手绘圈线', category: 'decor', variants: [ { id: 'orange', stroke: '#E07B39', width: '3px', dash: 'none', radius: '60px' }, { id: 'green', stroke: '#5B8C5A', width: '2px', dash: '8,4', radius: '50px' }, { id: 'blue', stroke: '#4A90A4', width: '2.5px', dash: '12,6', radius: '45px' }, { id: 'pink', stroke: '#E8435E', width: '3px', dash: '6,3', radius: '55px' }, ], render(v) { const d = parseInt(v.radius) * 2 return `` } }, // ═══════════════════════════════════════ // 信息类 Widgets // ═══════════════════════════════════════ 'photo-frame': { name: '照片框', category: 'info', description: '带白边/阴影的照片展示框,支持随机占位色块', variants: [ { id: 'polaroid', style: 'polaroid', borderColor: '#FFF', borderWidth: '12px 12px 40px 12px', rotate: '-2deg' }, { id: 'rounded', style: 'rounded', borderColor: '#FFF', borderRadius: '16px', shadow: true }, { id: 'film', style: 'film', borderColor: '#1A1A1A', borderWidth: '6px 6px 24px 6px', rotate: '1deg' }, { id: 'clean', style: 'clean', borderColor: 'transparent', borderRadius: '20px' }, ], render(v) { const w = v.width || 280, h = v.height || 210 const placeholderColors = ['#F5D0C5','#A8D5A2','#B5D4F4','#F5E6C8','#F4C0D1'] const bg = placeholderColors[Math.floor(Math.random() * placeholderColors.length)] let style = `width:${w}px;height:${h}px;background:${bg}` if (v.style === 'polaroid') style += `;border:${v.borderWidth} solid ${v.borderColor};transform:rotate(${v.rotate})` else if (v.style === 'film') style += `;border:${v.borderWidth} solid ${v.borderColor};transform:rotate(${v.rotate})` else if (v.style === 'rounded') style += `;border:4px solid ${v.borderColor};border-radius:${v.borderRadius}` else style += `;border-radius:${v.borderRadius};overflow:hidden` if (v.shadow) style += ';box-shadow:0 8px 30px rgba(0,0,0,.1)' if (v.caption) { return `
${v.emoji ? `${v.emoji}` : ''}
${v.caption}
` } return `
${v.emoji ? `${v.emoji}` : ''}
` } }, 'info-tag-row': { name: '信息标签行', category: 'info', description: '一排小红书风格的信息标签:价格/难度/时长/评分等', variants: [ { id: 'price', items: [{ icon: '💰', text: '均价', value: '¥28' }, { icon: '⭐', text: '评分', value: '4.8' }, { icon: '⏱', text: '耗时', value: '30min' }] }, { id: 'recipe', items: [{ icon: '👨‍🍳', text: '难度', value: '简单' }, { icon: '⏱', text: '时间', value: '20min' }, { icon: '🍽', text: '份量', value: '2人' }] }, { id: 'travel', items: [{ icon: '📍', text: '目的地', value: '杭州' }, { icon: '💰', text: '预算', value: '¥500' }, { icon: '📅', text: '天数', value: '3天' }] }, { id: 'study', items: [{ icon: '📚', text: '方法', value: '番茄钟' }, { icon: '⏱', text: '时长', value: '25min' }, { icon: '📈', text: '效果', value: '显著' }] }, ], render(v) { const items = (v.items || v.variant?.items || []).map(i => `
${i.icon} ${i.text}
${i.value}
` ).join('') return `
${items}
` } }, 'bullet-list': { name: '要点清单', category: 'info', description: '小红书风格的bullet point列表,支持emoji前缀', variants: [ { id: 'check', bullet: '✅', color: '#5B8C5A', items: ['要点一','要点二','要点三'] }, { id: 'star', bullet: '✦', color: '#E07B39', items: ['关键点A','关键点B','关键点C'] }, { id: 'number', bullet: 'N', color: '#4A90A4', numbered: true, items: ['第一步','第二步','第三步'] }, { id: 'heart', bullet: '♡', color: '#E8435E', items: ['喜欢的原因','喜欢的原因','喜欢的原因'] }, ], render(v) { const items = (v.items || v.variant?.items || []).map((item, i) => { const prefix = v.numbered ? `${String(i + 1).padStart(2, '0')}.` : v.bullet return `
${prefix} ${item}
` }).join('') return `
${items}
` } }, // ═══════════════════════════════════════ // 排版类 Widgets // ═══════════════════════════════════════ 'split-layout': { name: '左右分栏', category: 'layout', description: '左图右文或左文右图分栏,小红书经典排版', variants: [ { id: 'image-left', direction: 'row', ratio: '45:55', gap: '16px' }, { id: 'image-right', direction: 'row-reverse', ratio: '45:55', gap: '16px' }, { id: 'equal', direction: 'row', ratio: '50:50', gap: '20px' }, { id: 'stack-top', direction: 'column', ratio: 'auto' }, ], render(v) { const [l, r] = (v.ratio || '50:50').split(':').map(Number) const dir = v.direction === 'column' ? 'column' : 'row' return { wrapper: true, style: `display:flex;flex-direction:${dir};gap:${v.gap || '16px'};align-items:center`, leftStyle: dir === 'column' ? 'width:100%' : `flex:0 0 ${l}%`, rightStyle: dir === 'column' ? 'width:100%' : `flex:1`, } } }, 'card-grid': { name: '卡片网格', category: 'layout', description: '小红书同款2x2或3列卡片网格', variants: [ { id: '2x2', cols: 2, gap: '12px', cardHeight: '100px' }, { id: '1x3', cols: 3, gap: '10px', cardHeight: '140px' }, { id: '1x2-wide', cols: 2, gap: '16px', cardHeight: '180px' }, ], render(v) { return { wrapper: true, style: `display:grid;grid-template-columns:repeat(${v.cols},1fr);gap:${v.gap}`, cardStyle: `height:${v.cardHeight};background:#FDF8F3;border-radius:16px;display:flex;align-items:center;justify-content:center`, } } }, // ═══════════════════════════════════════ // 情感类 Widgets // ═══════════════════════════════════════ 'sticky-note': { name: '便签纸', category: 'emotion', variants: [ { id: 'yellow', bg: '#FFF9C4', shadow: '#E6D88A', rotate: '-3deg', pin: '#F5A623' }, { id: 'pink', bg: '#FCE4EC', shadow: '#F0D0D8', rotate: '2deg', pin: '#E8435E' }, { id: 'green', bg: '#E8F5E9', shadow: '#C8E6C9', rotate: '-1deg', pin: '#5B8C5A' }, { id: 'blue', bg: '#E3F2FD', shadow: '#BBDEFB', rotate: '4deg', pin: '#4A90A4' }, ], render(v) { return `
${v.text || '便签内容'}
` } }, 'highlight-box': { name: '高亮强调框', category: 'emotion', description: '小红书金句高亮框,荧光笔标注风格', variants: [ { id: 'yellow-marker', bg: '#FFF3CD', border: '#FFE69C', text: '#856404', icon: '💡' }, { id: 'pink-marker', bg: '#FCE4EC', border: '#F48FB1', text: '#880E4F', icon: '❤️' }, { id: 'green-marker', bg: '#E8F5E9', border: '#A5D6A7', text: '#1B5E20', icon: '🌟' }, { id: 'blue-marker', bg: '#E3F2FD', border: '#90CAF9', text: '#0D47A1', icon: '📌' }, ], render(v) { return `
${v.icon ? v.icon + ' ' : ''}${v.text || '金句内容'}
` } }, 'mood-indicator': { name: '心情标识', category: 'emotion', variants: [ { id: 'happy', mood: '😊 开心', bg: '#FFF9C4', color: '#F5A623' }, { id: 'calm', mood: '😌 平静', bg: '#E8F5E9', color: '#5B8C5A' }, { id: 'excited', mood: '🤩 激动', bg: '#FCE4EC', color: '#E8435E' }, { id: 'focused', mood: '🧘 专注', bg: '#E3F2FD', color: '#4A90A4' }, ], render(v) { return `
${v.mood}
` } }, // ═══════════════════════════════════════ // 特殊效果 Widgets // ═══════════════════════════════════════ 'scribble-underline': { name: '手写划线下划线', category: 'decor', variants: [ { id: 'orange-wavy', color: '#E07B39', style: 'wavy', offset: '4px', width: '3px' }, { id: 'green-straight', color: '#5B8C5A', style: 'solid', offset: '2px', width: '4px' }, { id: 'pink-dashed', color: '#E8435E', style: 'dashed', offset: '6px', width: '2px' }, ], render(v) { return { cssClass: `text-decoration:underline;text-decoration-color:${v.color};text-decoration-thickness:${v.width};text-underline-offset:${v.offset};${v.style !== 'solid' ? `text-decoration-style:${v.style}` : ''}`, inline: true, } } }, 'corner-fold': { name: '折角效果', category: 'decor', variants: [ { id: 'top-right', corner: 'top-right', color: '#FDF8F3', size: '30px' }, { id: 'top-left', corner: 'top-left', color: '#E8F0E0', size: '24px' }, ], render(v) { return `
` } }, } // 随机选择一个widget的变体 export function randomVariant(widgetId) { const w = WIDGETS[widgetId] if (!w || !w.variants) return null const idx = Math.floor(Math.random() * w.variants.length) return w.variants[idx] } // 列举所有widget export function listWidgets() { return Object.entries(WIDGETS).map(([id, w]) => ({ id, name: w.name, category: w.category, description: w.description || '', variantCount: (w.variants || []).length, })) } // 按类别筛选 export function widgetsByCategory(category) { return listWidgets().filter(w => w.category === category) }