大家平时在写一些简单的网页,或者搞个小的开源项目 Demo 时,最头疼的是什么?我相信很多人都会脱口而出:写 CSS 样式。
既要考虑布局,又要设计配色,还得保证响应式,一套写下来常常让人筋疲力尽。最近我在 GitHub 上发现了一个开源项目 Oat UI,它让我重新找回了用原生 HTML 写页面的快乐。
这是一个超级轻量级的前端 UI 库,目前已经在 GitHub 上收获了 4.5k+ Star。它的理念简单直接:你只需要写标准的原生 HTML,引入它的库,页面就能自动拥有统一、美观的样式,完全不需要复杂的工程化配置。

Oat UI 是什么?
Oat UI 是一个 “无类”(Classless)CSS 框架,或者说是一个超轻量级的语义化 HTML + CSS + JS UI 组件库。
这是什么意思呢?回想一下,传统的 UI 库(如 Bootstrap、Element UI)通常需要你在 HTML 标签上添加各种类名,比如 class=”btn btn-primary”。而 Oat UI 则反其道而行之,它通过 CSS 的标签选择器和属性选择器,直接对原生 HTML 元素进行美化。
你写的 <button>、<table>、<form>,只要引入了 Oat UI,就会自动拥有统一、现代的样式。它不需要任何第三方库依赖,纯原生实现,直接引入 CDN 文件即可,无需构建工具。
来看下示例效果
下面是一个完全没有任何 class 的、最原生的表单和表格 HTML 代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Oat UI 演示</title>
</head>
<style>
body {
width: 800px;
margin: 0 auto;
}
</style>
<body>
<h1>用户注册</h1>
<p>这是一个没有任何 class 的表单示例。</p>
<form>
<div>
<label for="username">用户名</label>
<input type="text" id="username" placeholder="请输入用户名">
</div>
<div>
<label for="email">邮箱</label>
<input type="email" id="email" placeholder="请输入邮箱">
</div>
<div>
<label for="password">密码</label>
<input type="password" id="password" placeholder="请输入密码">
</div>
<div>
<label for="role">角色</label>
<select id="role">
<option>开发者</option>
<option>设计师</option>
<option>产品经理</option>
</select>
</div>
<button type="submit">立即注册</button>
</form>
<hr>
<h2>数据报表</h2>
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>职业</th>
</tr>
</thead>
<tbody>
<tr>
<td>张三</td>
<td>28</td>
<td>前端工程师</td>
</tr>
<tr>
<td>李四</td>
<td>32</td>
<td>后端架构师</td>
</tr>
</tbody>
</table>
</body>
</html>
如果不引入任何样式,它的界面会非常“原始”,就是浏览器默认的样式。
而使用 Oat UI 只需要在 HTML 的 <head> 标签中引入它的 CSS 和 JS 文件:
<link rel="stylesheet" href="https://unpkg.com/@knadh/oat/oat.min.css">
<script src="https://unpkg.com/@knadh/oat/oat.min.js" defer></script>
引入之后,无需修改上面 HTML 代码的任何一行,整个页面的表单、按钮、表格就会立刻变得美观、整洁,拥有统一的间距、边框、颜色和交互反馈。你可以清晰地看到,从“原始”到“美观”的转变,仅需两行引入代码。

Oat UI 的作者 Kailash Nadh 创建这个项目的初衷,正是因为他受够了 JavaScript 生态系统中过度设计的臃肿和“依赖地狱”。他想要一个回归初心的方案。整个 UI 库只有 6KB 的 CSS + 2.2KB 的 JS(压缩后),轻得像一片燕麦,这也是它名字的由来。
虽然轻量,但 Oat UI 覆盖的组件种类非常齐全:
| 类别 |
组件 |
| 布局 |
Grid 网格、Card 卡片、Sidebar 侧边栏 |
| 表单 |
Button 按钮、Switch 开关、Form 表单元素 |
| 导航 |
Tabs 标签页、Breadcrumb 面包屑、Pagination 分页 |
| 反馈 |
Alert 警告框、Toast 通知、Progress 进度条、Spinner 加载 |
| 数据展示 |
Table 表格、Badge 徽章、Avatar 头像 |
| 交互 |
Accordion 折叠面板、Dropdown 下拉菜单、Tooltip 提示、Dialog 对话框 |
快速上手指南
方法一:CDN 引入(推荐,最快)
对于快速原型、演示或简单页面,CDN 是最方便的方式。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Oat UI 示例</title>
<!-- 引入 Oat UI CSS -->
<link rel="stylesheet" href="https://unpkg.com/@knadh/oat/oat.min.css">
</head>
<body>
<!-- 在这里写你的页面内容 -->
<!-- 引入 Oat UI JS(部分交互组件需要) -->
<script src="https://unpkg.com/@knadh/oat/oat.min.js" defer></script>
</body>
</html>
方法二:npm 安装
如果你的项目使用构建工具,可以通过 npm 安装。
npm install @knadh/oat
然后在你的项目中导入:
// 方式一:导入全部
import '@knadh/oat/oat.min.css';
import '@knadh/oat/oat.min.js';
// 方式二:按需导入(如果你使用构建工具)
import '@knadh/oat/css/oat.min.css';
import '@knadh/oat/js/oat.min.js';
详细代码示例
1. 按钮组件
Oat UI 为 <button> 标签提供了多种样式变体,通过 data-variant 属性或 class 控制。
<!-- 基础按钮 -->
<button>默认按钮</button>
<button data-variant="secondary">次要按钮</button>
<button data-variant="danger">危险操作</button>
<!-- 样式变体 -->
<button class="outline">轮廓按钮</button>
<button data-variant="danger" class="outline">危险轮廓</button>
<button class="ghost">幽灵按钮</button>
<button disabled>禁用状态</button>
<!-- 尺寸 -->
<button class="small">小按钮</button>
<button>中按钮</button>
<button class="large">大按钮</button>
<!-- 按钮组 -->
<menu class="buttons">
<li><button class="outline">左</button></li>
<li><button class="outline">中</button></li>
<li><button class="outline">右</button></li>
</menu>
2. 表单组件
所有原生表单元素 <input>, <select>, <textarea>, <radio>, <checkbox> 都会被自动美化。使用 data-field 属性可以创建带标签的输入框组合。
<form>
<!-- 文本输入 -->
<label data-field>
用户名
<input type="text" placeholder="请输入用户名" />
</label>
<!-- 邮箱输入 -->
<label data-field>
邮箱
<input type="email" placeholder="you@example.com" />
</label>
<!-- 密码输入 -->
<label data-field>
密码
<input type="password" placeholder="请输入密码" aria-describedby="pwd-hint" />
<small id="pwd-hint">密码至少8位</small>
</label>
<!-- 下拉选择 -->
<div data-field>
<label>角色</label>
<select>
<option value="">请选择</option>
<option value="admin">管理员</option>
<option value="user">普通用户</option>
</select>
</div>
<!-- 多行文本 -->
<label data-field>
简介
<textarea placeholder="请输入简介..."></textarea>
</label>
<!-- 单选 -->
<fieldset class="hstack">
<legend>性别</legend>
<label><input type="radio" name="gender"> 男</label>
<label><input type="radio" name="gender"> 女</label>
</fieldset>
<!-- 多选 -->
<label data-field>
<input type="checkbox" /> 同意用户协议
</label>
<!-- 开关 -->
<label>
<input type="checkbox" role="switch" /> 接收推送通知
</label>
<!-- 提交按钮 -->
<button type="submit">提交</button>
</form>
3. 卡片组件
使用 <article class=”card”> 快速创建卡片容器。
<article class="card">
<header>
<h3>文章标题</h3>
<p>这是一段描述文字</p>
</header>
<p>卡片的主要内容区域,可以包含任意 HTML 元素。</p>
<footer class="hstack">
<button class="outline">取消</button>
<button>确定</button>
</footer>
</article>
4. 警告框(Alert)
使用 role=”alert” 和 data-variant 属性创建不同状态的提示信息。
<div role="alert" data-variant="success">
<strong>✅ 成功!</strong> 您的操作已成功完成。
</div>
<div role="alert" data-variant="warning">
<strong>⚠️ 警告!</strong> 请在继续之前检查所有信息。
</div>
<div role="alert" data-variant="error">
<strong>❌ 错误!</strong> 发生了未知错误,请重试。
</div>
<div role="alert">
<strong>💡 提示</strong> 这是一条普通的消息通知。
</div>
5. 表格组件
原生 <table> 会被自动美化。在外层添加 <div class=”table”> 可以使其支持水平滚动。
<div class="table">
<table>
<thead>
<tr>
<th>姓名</th>
<th>邮箱</th>
<th>角色</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr>
<td>张三</td>
<td>zhangsan@example.com</td>
<td>管理员</td>
<td><span class="badge success">活跃</span></td>
</tr>
<tr>
<td>李四</td>
<td>lisi@example.com</td>
<td>编辑</td>
<td><span class="badge">活跃</span></td>
</tr>
<tr>
<td>王五</td>
<td>wangwu@example.com</td>
<td>访客</td>
<td><span class="badge secondary">待审核</span></td>
</tr>
</tbody>
</table>
</div>
6. 标签页(Tabs)
Oat UI 使用 Web Components 技术实现了 <ot-tabs> 组件,用于创建可访问的标签页。
<ot-tabs>
<div role="tablist">
<button role="tab" aria-selected="true">账户</button>
<button role="tab">安全</button>
<button role="tab">通知</button>
</div>
<div role="tabpanel">
<h3>账户设置</h3>
<p>在这里管理您的账户信息...</p>
</div>
<div role="tabpanel" hidden>
<h3>安全设置</h3>
<p>修改密码和安全设置...</p>
</div>
<div role="tabpanel" hidden>
<h3>通知设置</h3>
<p>配置您的通知偏好...</p>
</div>
</ot-tabs>
7. 对话框(Dialog)
基于原生 <dialog> 元素增强,使用 commandfor 和 command 属性控制开关。
<!-- 打开对话框的按钮 -->
<button commandfor="my-dialog" command="show-modal">打开对话框</button>
<!-- 对话框 -->
<dialog id="my-dialog" closedby="any">
<form method="dialog">
<header>
<h3>编辑资料</h3>
<p>修改您的个人信息</p>
</header>
<div class="vstack">
<label>姓名 <input name="name" required></label>
<label>邮箱 <input name="email" type="email"></label>
</div>
<footer>
<button type="button" commandfor="my-dialog" command="close" class="outline">取消</button>
<button type="submit" value="save">保存</button>
</footer>
</form>
</dialog>
<script>
// 处理对话框返回值
const dialog = document.querySelector("#my-dialog");
dialog.addEventListener('close', (e) => {
if (dialog.returnValue === 'save') {
console.log('用户点击了保存');
}
});
</script>
8. Toast 通知
通过全局 ot 对象提供的 toast 方法显示临时通知。
<button onclick="showSuccess()">显示成功通知</button>
<button onclick="showError()">显示错误通知</button>
<script>
// 成功通知
function showSuccess() {
ot.toast('操作已完成', '成功', { variant: 'success' });
}
// 错误通知
function showError() {
ot.toast('发生了错误', '失败', { variant: 'danger', placement: 'top-center' });
}
// 自定义通知配置
ot.toast('新消息', '通知中心', {
variant: 'warning', // success | danger | warning
placement: 'bottom-right', // top-left | top-center | top-right | bottom-left | bottom-center | bottom-right
duration: 5000 // 显示时间(毫秒),0 为永久
});
</script>
9. 进度条和加载指示器
使用原生 <progress> 标签创建进度条,使用 aria-busy 和 data-spinner 属性创建加载动画。
<!-- 进度条 -->
<progress value="60" max="100"></progress>
<progress value="30" max="100"></progress>
<progress value="90" max="100"></progress>
<!-- 加载指示器 -->
<div class="hstack" style="gap: 2rem;">
<div aria-busy="true" data-spinner="small"></div>
<div aria-busy="true"></div>
<div aria-busy="true" data-spinner="large"></div>
</div>
<!-- 带遮罩的加载状态 -->
<article class="card" aria-busy="true" data-spinner="large overlay">
<header>
<h3>卡片标题</h3>
<p>卡片描述</p>
</header>
<p>内容区域会被遮罩</p>
</article>
10. 网格布局
Oat UI 提供了一个简单的 12 列网格系统,类名类似于传统框架。
<div class="container">
<div class="row">
<div class="col-4">col-4</div>
<div class="col-4">col-4</div>
<div class="col-4">col-4</div>
</div>
<div class="row">
<div class="col-6">col-6</div>
<div class="col-6">col-6</div>
</div>
<div class="row">
<div class="col-3">col-3</div>
<div class="col-6">col-6</div>
<div class="col-3">col-3</div>
</div>
<!-- 带偏移的布局 -->
<div class="row">
<div class="col-4 offset-2">col-4 offset-2</div>
<div class="col-4">col-4</div>
</div>
</div>
适合哪些场景?
Oat UI 并非万能的银弹。如果你要开发复杂交互的单页面应用(SPA),拥有大量动态状态,那么 React、Vue 等现代框架依然是更好的选择。
但在以下这些场景中,Oat UI 的轻量与便捷优势会非常突出:
- 内部工具/管理后台:为公司或团队快速搭建一个数据查询、内容管理的页面,没必要启动一个完整的前端项目。
- 产品原型/MVP(最小可行产品):当你有一个新想法,需要在极短时间内做出一个可演示的界面给客户或团队看时,它让你专注于功能逻辑而非样式细节。
- 静态内容网站/博客:个人博客、产品介绍页、活动落地页等,追求极致的加载速度与简单的维护方式。
- 学习与教学:对于刚接触后端开发或数据分析的同学,想快速做一个带界面的 Demo 来展示结果,而不想深入前端框架。
如何定制主题?
Oat UI 的样式完全基于 CSS 变量(Custom Properties)构建,因此定制主题非常简单。你只需要在你的样式表中覆盖这些变量即可。
:root {
--primary: #3498db; /* 主色调 */
--background: #ffffff; /* 背景色 */
--text: #2c3e50; /* 文字颜色 */
--border: #e0e0e0; /* 边框颜色 */
--radius: 0.5rem; /* 圆角大小 */
}
库本身支持暗色主题,会根据用户的系统偏好(prefers-color-scheme: dark)自动切换。你也可以通过为 <html> 标签添加 data-theme=”dark” 属性来强制启用暗色模式。
写在最后
在如今前端工具链日益复杂、项目动辄需要安装上百个依赖的背景下,Oat UI 像一股清流,让我们重温了 Web 开发的初心:HTML 文档本身就是内容的载体,应该清晰可读。
它可能不是构建大型应用的首选,但在追求“简单快速出活”的场景下,这种回归原生的轻量方案,往往能带来意想不到的高效率。它减少了在类名、构建配置和依赖管理上的心智负担,让你能更专注于内容和功能本身。
如果你也在为某个小项目简单而美观的界面发愁,不妨试试 Oat UI,或许它会给你带来惊喜。更多前沿的技术工具和实践分享,欢迎到 云栈社区 交流探讨。
项目地址:https://github.com/knadh/oat