LLH UI

Introduction

LLH 使用文档

LLH 是一个面向中后台的轻量 UI 框架:纯 CSS + 原生 JS,零依赖、零构建。 它解决三件事——统一视觉、直接可用的布局骨架、够用的组件。所有可调项都收敛成 CSS 变量, 不做封装、不做运行时,任何能写 HTML 的地方都能用。

路径说明
oak/assets/llh.css核心样式,包含令牌、基础排版、全部组件
oak/assets/llh.js交互脚本,事件委托,无初始化代码
oak/index.html框架首页与快速开始
oak/dashboard.html后台布局演示(侧栏 + 顶栏 + 内容)
oak/components.html组件总览
oak/docs.html本页
oak/assets/demo.css演示页专用样式,接入项目时不必拷贝

引入

把 assets/ 整个目录放进项目,在页面里引两个文件即可。CSS 放 head,JS 放 body 末尾。

<link rel="stylesheet" href="/assets/llh.css">
<script src="/assets/llh.js"></script>

如果是 PHP、JSP 这类会缓存的模板,建议给样式加时间戳,改完立即生效:

<link rel="stylesheet" href="/assets/llh.css?v=<?= @filemtime('assets/llh.css') ?: '1' ?>">
在 file:// 下直接双击打开演示页也能正常工作,不需要起服务。

布局骨架

一个页面只需要四层:.llh-layout → .llh-sidebar + .llh-main → .llh-topbar + .llh-content。 侧边栏 232px,折叠后 76px;窗口 ≤900px 自动变成抽屉。

<div class="llh-layout" data-llh-layout>
    <aside class="llh-sidebar">
        <a class="llh-sidebar__brand" href="/">
            <span class="llh-sidebar__logo">LLH</span>
            <span class="llh-sidebar__name">控制台</span>
        </a>

        <nav class="llh-sidebar__nav">
            <div class="llh-sidebar__group">工作台</div>
            <a class="llh-sidebar__link is-active" href="/">
                <svg class="llh-ico"><use href="#i-home"></use></svg>
                <span>仪表盘</span>
            </a>
        </nav>

        <div class="llh-sidebar__footer">
            <div class="llh-sidebar__user">
                <span class="llh-avatar llh-avatar--sm">杨</span>
                <div class="llh-sidebar__user-meta">
                    <div class="llh-sidebar__user-name">杨东旭</div>
                    <div class="llh-sidebar__user-role">超级管理员</div>
                </div>
            </div>
        </div>
    </aside>

    <div class="llh-main">
        <header class="llh-topbar">
            <div class="llh-topbar__left">
                <button class="llh-ico-btn" data-llh-toggle="sidebar">菜单</button>
                <span class="llh-topbar__title">仪表盘</span>
            </div>
            <div class="llh-topbar__right">...</div>
        </header>

        <main class="llh-content">页面内容</main>
    </div>
</div>

要点

结构行为
.llh-layout[data-llh-layout]折叠 / 抽屉逻辑的容器,缺这个属性折叠按钮不生效
.llh-sidebar桌面端 sticky 满高,移动端 fixed 抽屉并带遮罩式关闭
.llh-sidebar__link span折叠时靠它淡出,图标必须和文字分开写,不要塞进同一个 span
data-llh-toggle="sidebar"桌面端切换折叠,≤900px 时改为打开抽屉
.llh-content内容区自带 16px 内边距,移动端降为 12px
.llh-sticky-bar页内 TAB 吸顶用,配合 --llh-sticky-top: 57px(顶栏高 56 + 1px 边框)

设计令牌

全部挂在 :root 上,深色主题只覆盖同名变量,组件样式一行不改。

变量默认值用途
--llh-primary#0056b3主色:按钮、选中态、链接、焦点环
--llh-primary-hover#004a99主色悬停
--llh-primary-active#003f80主色按下
--llh-primary-softrgba(0,86,179,.10)主色淡底,用于选中背景、标签底
--llh-ink#101828标题与正文主色
--llh-ink-2#475467次要正文
--llh-ink-3#667085说明文字、表头
--llh-ink-4#98a2b3占位符、禁用
--llh-line / -line-2#eef0f4 / #dfe3ea分割线 / 控件描边
--llh-bg#f5f7fa页面底色、表头底色
--llh-card#ffffff卡片、侧栏、顶栏底色
--llh-success / -soft#067647 / #ecfdf3成功态
--llh-warn / -soft#b54708 / #fffaeb警告态
--llh-danger / -soft#b42318 / #fef3f2危险态
--llh-radius-sm/md/lg/xl/pill8/12/14/16/999圆角梯度
--llh-shadow-1/2/3—卡片 / 悬停 / 浮层投影
--llh-fontMiSans, system-ui…全站字体栈
--llh-nav-w / -nav-w-min232px / 76px侧栏宽度、折叠宽度
--llh-topbar-h56px顶栏高度
--llh-speed.18s全局过渡时长

换主色

:root {
    --llh-primary: #0f766e;
    --llh-primary-hover: #0d6a62;
    --llh-primary-active: #0b5a53;
    --llh-primary-soft: rgba(15, 118, 110, .10);
}

类名速查

命名规则:llh-块 + __元素 + --修饰,与 BEM 一致,好猜也好搜。

类名说明
.llh-layout / .llh-sidebar __brand/__logo/__name/__nav/__group/__link/__icon/__footer/__user*后台外壳与侧栏,折叠态由 html.is-collapsed 控制
.llh-main / .llh-topbar __left/__right/__title / .llh-content __header右侧主区、顶栏与页体
.llh-sticky-bar / .llh-page / .llh-body-reset / .llh-switch-page页内吸顶条 / 整页底色容器 / 样式重置 / 右侧浮动页面切换
.llh-container居中容器 1320px,--narrow 960 / --wide 1480
.llh-btn --primary/--soft/--ghost/--success/--danger/--danger-soft按钮类型
.llh-btn--sm/--lg/--block/--icon按钮尺寸与形态
.llh-btn-group按钮拼接组
.llh-card __header/__body/__footer卡片三段结构,--hover 加悬停上浮
.llh-stat __label/__value/__trend指标块,趋势加 --up/--down;.llh-stat--cn 切换为国内涨红跌绿
.llh-table表格,修饰 --striped/--compact,数字列 is-num
.llh-table-wrap表格外框,负责圆角与横向滚动
.llh-table__empty无数据占位行
.llh-badge --primary/--success/--warn/--danger/--solid/--dot标签与徽章
.llh-avatar --sm/--lg、.llh-avatar-group头像与头像堆叠
.llh-field / .llh-label / .llh-help / .llh-error-text表单字段结构
.llh-input .llh-select .llh-textarea输入控件,尺寸 --sm/--lg,错误态 is-error
.llh-input-group __addon带前后缀的输入组合
.llh-check / .llh-switch __track/__text勾选与开关,开关尺寸 --sm
.llh-alert --info/--success/--warn/--danger __title/__close页面内提示
.llh-toasts / .llh-toast __body/__title/__desc/__close --success/--warn/--danger全局提示(由 JS 生成,不用手写)
.llh-modal __backdrop/__dialog/__header/__title/__close/__body/__footer弹窗,尺寸 --sm/--lg
.llh-dropdown __menu/__item/__divider下拉菜单,右对齐加 --right
.llh-tabs / .llh-tab / .llh-pane标签页,线性风格 .llh-tabs--line
.llh-carousel __track/__slide/__content/__title/__desc/__dots/__dot轮播,图片背景加 __slide--image
.llh-steps / .llh-step is-done|is-active步骤条
.llh-timeline __item/__title/__desc/__time时间线
.llh-progress __bar --success/--warn/--danger进度条
.llh-pagination / .llh-page-item is-active|is-disabled分页
.llh-breadcrumb __sep/__current面包屑
.llh-empty __icon/__title/__desc空状态
.llh-skeleton --avatar/--title/--text骨架屏
.llh-divider --vertical分隔线
.llh-segmented分段控制器,radio + label 纯 CSS 切换
.llh-accordion __body折叠面板,基于原生 details/summary
.llh-drawer __panel/__header/__title/__body/__footer抽屉,左侧加 --left,开关与弹窗同一套 data 属性
.llh-popover __panel气泡卡片,hover 或 focus-within 展开
.llh-tip + data-llh-tip文字提示气泡,纯 CSS,无需 JS
.llh-desc --2描述列表,dl/dt/dd 两列或四列
.llh-result __icon/__title/__desc/__actions结果页,状态色 --warn/--danger/--info
.llh-upload __text/__hint、.llh-upload-item上传框与文件行
.llh-rate / .llh-range评分(radio 反序)与滑块(range)
.llh-tree __node/__count树形列表,嵌套 ul 自动缩进
.llh-notice __dot/__body/__title/__desc/__time通知列表
.llh-spinner / .llh-loading-mask / .llh-loading加载指示、容器遮罩与行内加载文案,父级加 .llh-loading-wrap
.llh-backtop + data-llh-backtop返回顶部,滚动超过 300px 自动显示
.llh-media __body媒体对象,左图右文自适应
.llh-ico --sm/--lg、.llh-ico-btn图标尺寸与图标按钮
.llh-ellipsis / .llh-nowrap / .llh-sr-only单行省略 / 不换行 / 仅读屏可见

状态类

组件状态统一用 is-* 表达,不加 llh- 前缀,跨组件同名同义。

状态类作用对象与含义
.is-active当前项:TAB、侧栏导航、分页、轮播圆点、树节点、步骤条
.is-open弹窗、抽屉、下拉菜单已展开
.is-collapsed加在 html 上,侧栏收至 76px
.is-disabled分页项、勾选项不可用
.is-done步骤条已完成的节点
.is-error输入控件校验失败,配合 .llh-error-text 使用
.is-loading作用于 .llh-btn,用伪元素显示转圈并屏蔽点击
.is-leavingToast 退出动画,由 JS 自动添加与移除
.is-show返回顶部按钮滚动超过 300px 后显示
.is-num表格数字列右对齐并启用等宽数字
.is-primary时间线条目的强调节点

工具类

布局:.llh-flex -col -wrap .llh-items-center .llh-justify-between .llh-gap-4/8/12/16 .llh-grow .llh-shrink-0
栅格:.llh-grid 配 --2/--3/--4/--auto,或 .llh-row + .llh-col
间距:.llh-mt-* .llh-mb-*(4/8/12/16/24)
其他:.llh-text-* .llh-w-full .llh-hidden .llh-hide-sm

JS API

脚本会自动初始化,window.LLH 上只暴露少量方法,够用即可。

LLH.modal.open('#myModal');      // 打开
LLH.modal.close('#myModal');     // 关闭指定
LLH.modal.closeAll();            // 关闭全部

LLH.dropdown.closeAll();         // 收起所有下拉

LLH.toast('文本', {
    type: 'success',   // info | success | warn | danger
    title: '操作成功',  // 可选标题
    duration: 3200     // 毫秒,0 表示不自动关闭
});

LLH.theme.set('dark');           // 'light' | 'dark',写入 localStorage
LLH.theme.get();                 // 当前主题
LLH.theme.toggle();              // 切换

LLH.init(scope);                 // 给动态插入的 DOM 重新绑定标签页与轮播
LLH.toast() 返回 { close, el },需要提前关掉时调用返回值上的 close()。

data 属性

所有交互都以事件委托实现,写在 HTML 上就能用,不需要为每个组件写初始化。

属性作用
data-llh-toggle="modal"打开弹窗或抽屉,配合 data-llh-target="#id"
data-llh-toggle="modal-close"关闭当前弹窗 / 抽屉
data-llh-toggle="dropdown"切换所在下拉菜单,点外部自动收起
data-llh-toggle="theme"切换深浅主题
data-llh-toggle="sidebar"折叠侧栏 / 移动端抽屉
data-llh-dismiss="alert"关闭所在 Alert
data-llh-tip="文案"提示气泡内容,纯 CSS 显示
data-llh-backtop标记返回顶部按钮,自动监听滚动
data-llh-tabs标签页容器,内部按钮用 data-llh-target="#pane"
data-llh-panes面板容器,切换时给对应 .llh-pane 加 .is-active
data-llh-layout标记布局根节点,启用侧栏逻辑
data-llh-interval="5000"轮播自动播放间隔(毫秒)
data-llh-autofocus弹窗打开后优先聚焦该元素
data-llh-static加在弹窗上,点遮罩不再关闭

事件

标签页切换后会派发 llh:tab,需要联动其他区域时监听它即可。

document.querySelector('[data-llh-tabs]').addEventListener('llh:tab', function (e) {
    console.log(e.detail.target); // 被激活面板的选择器
});

主题切换

深色主题实现方式是:在 <html> 上写 data-llh-theme="dark", 只覆盖同名 CSS 变量。所以自定义组件只要用变量取色,就自动支持深色。

<!-- 手动指定 -->
<html data-llh-theme="dark">

<!-- 避免刷新闪烁:在 head 里先读记忆 -->
<script>
(function () {
    try {
        if (localStorage.getItem('llh-theme') === 'dark') {
            document.documentElement.setAttribute('data-llh-theme', 'dark');
        }
    } catch (err) {}
})();
</script>
顺序很关键:样式表要在脚本之前引入,脚本放在 <head> 内、body 之前, 浏览器首帧就已经是正确主题,不会出现“先白后黑”。

侧栏折叠防闪烁

侧栏有折叠记忆,如果等 JS 加载完再加 is-collapsed,用户会看到“先展开、再收起”的一下抖动。 LLH 的做法是:head 内先把状态写到 <html data-llh-sidebar="collapsed">, CSS 里对应该属性直接渲染折叠态,脚本随后接管并保持同步。

<head>
<link rel="stylesheet" href="assets/llh.css">
<script>
(function () {
    try {
        if (localStorage.getItem('llh-sidebar-collapsed') === '1') {
            document.documentElement.setAttribute('data-llh-sidebar', 'collapsed');
        }
    } catch (err) {}
})();
</script>
</head>

llh.js 在初始化与每次点击时都会同步这个属性,因此两种状态永远一致,不需要你手动维护。 折叠态只在 min-width: 901px 下生效,移动端仍是完整抽屉。

常见问题

1. 只想用样式,不用 JS 行不行?

可以。CSS 里的组件不依赖 JS,按钮、表单、表格、卡片全部纯样式;只有弹窗、下拉、Toast、标签页、轮播、折叠需要脚本。

2. 怎么接进已有的 PHP / JSP 项目?

把 assets/llh.css 与 llh.js 放进静态目录,在公共 header 里引一次, 然后把老的 class 逐步替换成 llh-*。两者可以共存,不需要一次性重写。

3. 表格列太多撑破了怎么办?

用 .llh-table-wrap 包住表格,它负责横向滚动与圆角裁切。注意别在祖先元素上加 overflow: hidden,否则里面的下拉菜单会被裁掉。

4. 页内 TAB 想吸顶怎么做?

<div class="llh-tabs llh-sticky-bar" style="--llh-sticky-top:57px">...</div>

57px = 顶栏 56px + 1px 边框。改了 --llh-topbar-h 记得同步这个值。

5. 弹窗里的下拉被裁切?

下拉菜单是绝对定位,父级若有 overflow: hidden 就会裁。给弹窗内容区保留足够空间, 或把下拉挂到 body 上用 fixed 定位。

6. 为什么图标要自己提供?

框架不绑定图标库,避免多一份字体文件和版本冲突。演示页用内联 SVG sprite, 你接入 Bootstrap Icons、Remix Icon 或自己的 iconfont 都可以,只要保证图标元素有 .llh-ico 控制尺寸。

7. 浏览器支持范围?

面向现代浏览器:Chrome / Edge 90+、Firefox 90+、Safari 15+。不兼容 IE。 使用到的现代特性主要是 CSS 变量、flex/grid、position: sticky、:focus-visible。

8. 想改成自己的品牌怎么最快?

新建一个 brand.css,在 llh.css 之后引入,里面只写 :root 覆盖主色、字体、圆角即可, 不要直接改 llh.css,方便以后升级。

下一步
去 后台演示 看真实组合效果,或到 组件总览 直接复制代码。