Odyssey UI v1.0.0

Components

Feedback

向用户传达状态、进度与结果的一组组件:内联 Alert、命令式 Toast、遮罩层 Modal / DrawerEmpty stateSpinner、页头 Banner 与轻量 Popover。交互组件按 data-ody-* 属性或 window.Odyssey API 驱动,亮/暗两色均走语义 token。

Alert

内联状态条 .ody-alert:左侧色条 + 图标 + .ody-alert__body(含可选 .ody-alert__title)。语义色修饰符 --ok / --warn / --down / --info;不加修饰符即中性提示。

中性提示:这是一条不带语义色的说明信息。
保存成功
你的更改已同步到所有设备。
存储空间不足
剩余容量低于 10%,建议尽快清理。
部署失败
无法连接到构建节点,请检查网络后重试。
新版本 v1.0.0 已发布,查看更新日志了解详情。
<div class="ody-alert ody-alert--ok">
  <svg class="ody-alert__ico" viewBox="0 0 24 24" ...></svg>
  <div class="ody-alert__body">
    <div class="ody-alert__title">保存成功</div>
    你的更改已同步到所有设备。
  </div>
</div>

<!-- 语义色:--ok / --warn / --down / --info,省略即中性 -->
<div class="ody-alert ody-alert--warn">…</div>
<div class="ody-alert ody-alert--down">…</div>
<div class="ody-alert ody-alert--info">…</div>

可关闭 Alert

加入 .ody-alert__close 按钮即可提供关闭入口。库脚本的 data-ody-dismiss 仅接管 Modal / Drawer,Alert 的移除由宿主自行处理 —— 给关闭按钮一个 handler 切换 .is-dismissed(或 hidden)即可,CSS 已内置隐藏规则。

同步进行中
点击右侧 × 可关闭这条提示。
<div class="ody-alert ody-alert--info">
  <svg class="ody-alert__ico" ...></svg>
  <div class="ody-alert__body">
    <div class="ody-alert__title">同步进行中</div>
    点击右侧 × 可关闭这条提示。
  </div>
  <button class="ody-alert__close" type="button" aria-label="关闭提示"
          onclick="this.closest('.ody-alert').classList.add('is-dismissed')">
    <svg viewBox="0 0 24 24" ...><path d="M6 6l12 12M18 6L6 18"/></svg>
  </button>
</div>

Toast

短暂的浮层通知,通过命令式 API 触发:Odyssey.toast({title, message, tone, timeout})。脚本会自动创建固定于右下角的 .ody-toast-region 并堆叠 Toast,超时后自动消失,也可点 × 手动关闭。toneok / warn / down / infotimeout 单位毫秒(默认 4200,传 0 则常驻)。

<button class="ody-btn ody-btn--secondary"
        onclick="Odyssey.toast({ title:'保存成功', message:'更改已同步。', tone:'ok' })">
  成功 Toast
</button>

<script>
  // 语义色:tone = 'ok' | 'warn' | 'down' | 'info'
  Odyssey.toast({ title:'有新消息', message:'你有 3 条未读通知。', tone:'info' });

  // timeout 单位毫秒;传 0 则常驻,需手动关闭
  Odyssey.toast({ title:'常驻通知', message:'不会自动消失。', tone:'info', timeout:0 });
</script>

居中对话框。触发器加 data-ody-open="#id" 打开对应的 .ody-modal 容器;关闭方式包括容器内的 data-ody-dismiss 按钮、点击 .ody-modal__backdrop 遮罩、或按 Esc。打开时锁定 body 滚动并做焦点捕获。尺寸修饰符 --sm / --lg

<button class="ody-btn ody-btn--primary" data-ody-open="#demoModal">打开对话框</button>

<div class="ody-modal" id="demoModal" hidden>
  <div class="ody-modal__backdrop"></div>
  <div class="ody-modal__panel">
    <div class="ody-modal__head">
      <h2 class="ody-modal__title">删除工作区?</h2>
      <button class="ody-iconbtn ody-iconbtn--sm" data-ody-dismiss aria-label="关闭">
        <svg viewBox="0 0 24 24" ...><path d="M6 6l12 12M18 6L6 18"/></svg>
      </button>
    </div>
    <div class="ody-modal__body">
      <p>此操作不可撤销,工作区内的全部数据将被永久删除。</p>
    </div>
    <div class="ody-modal__foot">
      <button class="ody-btn ody-btn--subtle" data-ody-dismiss>取消</button>
      <button class="ody-btn ody-btn--danger" data-ody-dismiss>删除</button>
    </div>
  </div>
</div>

<!-- 尺寸:.ody-modal--sm / .ody-modal--lg -->

Drawer

侧边抽屉,与 Modal 共用开合引擎:data-ody-open="#id" 打开 .ody-drawer,遮罩点击、data-ody-dismissEsc 关闭。默认从右侧滑入,加 --left 改为左侧。

<button class="ody-btn ody-btn--primary" data-ody-open="#demoDrawer">右侧抽屉</button>

<div class="ody-drawer" id="demoDrawer" hidden>
  <div class="ody-drawer__backdrop"></div>
  <div class="ody-drawer__panel">
    <div class="ody-drawer__head">
      <h2 class="ody-drawer__title">筛选</h2>
      <button class="ody-iconbtn ody-iconbtn--sm" data-ody-dismiss aria-label="关闭">
        <svg viewBox="0 0 24 24" ...><path d="M6 6l12 12M18 6L6 18"/></svg>
      </button>
    </div>
    <div class="ody-drawer__body">抽屉内容……</div>
    <div class="ody-drawer__foot">
      <button class="ody-btn ody-btn--subtle" data-ody-dismiss>重置</button>
      <button class="ody-btn ody-btn--primary" data-ody-dismiss>应用</button>
    </div>
  </div>
</div>

<!-- 从左侧滑入:.ody-drawer--left -->

Empty state

空数据占位 .ody-empty__ico 图标 + __title 标题 + __text 说明,通常附带一个引导操作。适用于列表为空、搜索无结果等场景。

还没有任何项目

创建你的第一个项目,开始协作与追踪进度。

<div class="ody-empty">
  <div class="ody-empty__ico">
    <svg viewBox="0 0 24 24" ...></svg>
  </div>
  <h3 class="ody-empty__title">还没有任何项目</h3>
  <p class="ody-empty__text">创建你的第一个项目,开始协作与追踪进度。</p>
  <button class="ody-btn ody-btn--primary">新建项目</button>
</div>

Spinner

加载指示器 .ody-spinner,尺寸修饰符 --sm / --lg。按钮的加载态直接用 .is-loading —— 它会隐藏文字并内嵌自旋圈,同时禁用点击。

<span class="ody-spinner ody-spinner--sm" role="status" aria-label="加载中"></span>
<span class="ody-spinner" role="status" aria-label="加载中"></span>
<span class="ody-spinner ody-spinner--lg" role="status" aria-label="加载中"></span>

<!-- 按钮加载态 -->
<button class="ody-btn ody-btn--primary is-loading">保存中</button>

横跨容器宽度的通栏提示 .ody-banner,默认强调色,修饰符 --warn / --down。用 .ody-banner__spacer 把操作推到右侧;.ody-banner[hidden] 内置隐藏,可作可关闭横幅。

新版本 v1.0.0 已发布 —— 查看更新内容。 查看
计划维护将于本周日 02:00 开始,服务可能短暂中断。
连接已断开,正在尝试重新连接……
<div class="ody-banner">
  <svg width="18" height="18" ...></svg>
  <span>新版本 v1.0.0 已发布 —— 查看更新内容。</span>
  <span class="ody-banner__spacer"></span>
  <a class="ody-btn ody-btn--sm ody-btn--secondary" href="#">查看</a>
  <button class="ody-iconbtn ody-iconbtn--sm" aria-label="关闭横幅"
          onclick="this.closest('.ody-banner').hidden=true">
    <svg viewBox="0 0 24 24" ...><path d="M6 6l12 12M18 6L6 18"/></svg>
  </button>
</div>

<!-- 语义色:.ody-banner--warn / .ody-banner--down -->

Popover

点击触发的轻量浮层。触发器加 data-ody-popover="#id",目标 .ody-popover(可含 __title)显隐由脚本切换 .is-open;点击外部或按 Esc 关闭。把触发器与浮层放进同一个相对定位容器,即可让浮层贴着触发器展开。

<span style="position:relative;display:inline-flex">
  <button class="ody-btn ody-btn--secondary" data-ody-popover="#demoPop">显示详情</button>
  <div class="ody-popover" id="demoPop" style="top:calc(100% + 8px);left:0" hidden>
    <p class="ody-popover__title">构建 #482</p>
    <p>在 main 分支上耗时 1m 12s 通过全部 128 项检查。</p>
  </div>
</span>