官网和应用的分水岭在于:应用强调密度,官网强调呼吸。把行高放宽、把标题做成细字重展示体,界面的气质立刻就不一样了。
一句话说不清的,不配做组件
粒度过细会让组合成本爆炸,过粗又失去灵活性。我们给自己立的规矩是:能被一句话描述清楚的,才配成为一个组件。
安静优雅不是没有设计,而是设计完成之后,退到了内容的后面。
剩下的部分,都是在这条原则之下的反复权衡——这也是这篇文章想讲清楚的事。
内容站的结构比官网更简单:一栏文章流 + 一点关于作者的信息,剩下的交给排版。 本案例演示分类筛选的文章列表、热榜轮播、作者名片与订阅框如何拼成一个完整的博客首页。 品牌与文章均为虚构,源码就在本页,可以直接拷走改内容。
用到的组件:EvNavbar · EvTabs · EvArticleCard · EvSection · EvCarousel · EvQuote · EvProfileCard · EvTimeline · EvNewsletter · EvFooter
写下来,才算想清楚。
关于前端、设计与慢生活的个人札记,每周更新一至两篇。
组件粒度、插槽设计与令牌边界——一次开源组件库 API 设计的完整复盘。
一个滚动浮现指令,值得写一篇长文:观察器复用、减弱动效与 SSR 兜底。
CSS 变量重映射 + html.dark 一键切换,一篇讲透双主题的落地方式。
排版节奏、字重层级与 8px 网格,在长文页面里的具体用法。
藏青、粉彩与雾面质感如何共处,以及为什么语义色只需要五种。
在山里住满三十天之后,我对「效率」这个词有了新的理解。
从离线编辑器到噪音耳机,十件让我在咖啡馆写完这本书的东西。
组件设计
组件粒度、插槽设计与令牌边界——一次开源组件库 API 设计的完整复盘。
官网和应用的分水岭在于:应用强调密度,官网强调呼吸。把行高放宽、把标题做成细字重展示体,界面的气质立刻就不一样了。
粒度过细会让组合成本爆炸,过粗又失去灵活性。我们给自己立的规矩是:能被一句话描述清楚的,才配成为一个组件。
安静优雅不是没有设计,而是设计完成之后,退到了内容的后面。
剩下的部分,都是在这条原则之下的反复权衡——这也是这篇文章想讲清楚的事。
组件文档写得像散文一样克制,抄完首页我就把公司的官网重构了。
「留白是内容的一部分」这篇转给了整个设计组,本周例会的议题。
每篇都短,但每篇都有可以立刻用上的东西。
独立开发者 · 前端工程
正在写一本关于组件设计的小书。相信好的工具应该安静,好的文字应该诚实。
最近更新
每周精选,直接进邮箱
EvSection 的 gap="0" 让标题区与文章流贴在一起。EvTabs + computed:Tab 只负责切换状态,列表交给 computed 过滤;右侧的「N 篇」计数让筛选结果可预期。EvArticleCard 用 tag="a" 让整卡可点:封面缺省时用图标占位底,不用为占位图发愁;hoverable 默认开启上浮。EvQuote 塞进 EvCarousel:读者原话比阅读量数字更有说服力,autoplay 挂 5 秒并自动悬停暂停。EvProfileCard 讲人,EvTimeline + EvNewsletter 讲更新与订阅,一张 soft 底卡收拢。筛选逻辑全部在一个 computed 里,模板只负责渲染:
<script setup>
import { ref, computed } from 'vue'
const categories = ['全部', '前端', '设计', '随想']
const active = ref('全部')
const filtered = computed(() =>
active.value === '全部'
? posts
: posts.filter((p) => p.category === active.value)
)
</script>
<template>
<EvTabs v-model="active" :items="categories.map((c) => ({ label: c, value: c }))" />
<EvArticleCard
v-for="p in filtered"
:key="p.title"
tag="a"
:title="p.title"
:excerpt="p.excerpt"
:date="p.date"
:tags="p.tags"
:icon="p.icon"
/>
</template>热榜轮播:items 是普通数组,幻灯内容交给作用域插槽:
<EvCarousel :items="hot" :autoplay="5000">
<template #item="{ item }">
<EvQuote :quote="item.quote" :author="item.author" :role="item.role" />
</template>
</EvCarousel>